diff --git a/cabal.project b/cabal.project index b6b8922..a5ed372 100644 --- a/cabal.project +++ b/cabal.project @@ -5,3 +5,4 @@ packages: ./paymentFlow ./api-contract ./dc + ./endpoints diff --git a/dc/dc.cabal b/dc/dc.cabal index 77462d1..6f2d8d5 100644 --- a/dc/dc.cabal +++ b/dc/dc.cabal @@ -102,5 +102,5 @@ test-suite dc-test -- Test dependencies. build-depends: - base ^>=4.16.4.0, + base , dc diff --git a/endpoints/CHANGELOG.md b/endpoints/CHANGELOG.md new file mode 100644 index 0000000..2bbb352 --- /dev/null +++ b/endpoints/CHANGELOG.md @@ -0,0 +1,5 @@ +# Revision history for endpoints + +## 0.1.0.0 -- YYYY-mm-dd + +* First version. Released on an unsuspecting world. diff --git a/endpoints/LICENSE b/endpoints/LICENSE new file mode 100644 index 0000000..0a9095c --- /dev/null +++ b/endpoints/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2024 eswar2001 + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/endpoints/README b/endpoints/README new file mode 100644 index 0000000..2c7c91c --- /dev/null +++ b/endpoints/README @@ -0,0 +1,127 @@ +# Servant API Parser Plugin + +A GHC plugin that automatically parses Servant API types and generates API specifications. This plugin analyzes both client and server-side Servant type declarations and converts them into a structured API specification format. + +## Features + +- Automatically extracts API endpoints from Servant type declarations +- Supports full REST API operations (GET, POST, PUT, DELETE) +- Parses path parameters, query parameters, and headers +- Handles request and response bodies with content type information +- Generates JSON output compatible with API documentation tools + +## Output Format + +The plugin generates a JSON structure that describes each endpoint with the following information: + +```typescript +interface Endpoint { + method: string; // HTTP method + path: string[]; // URL path segments + queryParams: string[]; // Query parameters + headers: string[]; // Required headers + responseStatus: string; // HTTP response status + responseContentType: string | null; // Response content type + responseBody: string | null; // Response body type + requestBody: string | null; // Request body type + requestContentType: string | null; // Request content type +} +``` + +### Example Output + +```json +{ + "API": [ + { + "headers": [], + "method": "'GET", + "path": ["v1", "api", "users"], + "queryParams": [], + "requestBody": null, + "requestContentType": null, + "responseBody": null, + "responseContentType": "'[JSON]", + "responseStatus": "200" + }, + // ... more endpoints + ] +} +``` + +## Implementation Details + +The plugin parses Servant types using GHC's Type system and supports the following Servant combinators: + +- `:>` - Path concatenation +- `:<|>` - API alternatives +- `Capture'` - URL path parameters +- `QueryParam'` - URL query parameters +- `Header'` - HTTP headers +- `ReqBody'` - Request body +- `Verb` - HTTP methods +- `AddArgs` - Additional arguments +- `Tag` - API grouping tags + +### Core Data Types + +```haskell +data Endpoint = Endpoint + { method :: String + , path :: [String] + , queryParams :: [String] + , headers :: [String] + , responseStatus :: String + , responseContentType :: Maybe String + , responseBody :: Maybe String + , requestBody :: Maybe String + , requestContentType :: Maybe String + } deriving (Generic, Show) + +data ApiComponent + = Path String + | Capture String + | QueryParam String + | Header String + | AddArgs String + | ReqBody String String + | Verb String String (Maybe String) (Maybe String) + | Group String [ApiComponent] + | Alternative [ApiComponent] + | Tag String + deriving (Generic, Show, Data, ToJSON) +``` + +## Usage + +1. Add the plugin to your project dependencies +2. Enable the plugin in your Cabal file: + ```yaml + ghc-options: -fplugin=ServantApiParser + ``` +3. The plugin will automatically process your Servant API types during compilation +4. Access the generated API specification through the plugin's output + +## Example API Definition + +```haskell +type UserAPI = + "v1" :> "api" :> "users" :> Get '[JSON] [User] + :<|> "v1" :> "api" :> "users" :> Capture "userId" UserId :> Get '[JSON] User + :<|> "v1" :> "api" :> "users" :> ReqBody '[JSON] User :> Post '[JSON] User +``` + +## Supported Servant Components + +| Component | Description | Example | +|-----------|-------------|---------| +| Path | Static path segments | `"v1" :> "api"` | +| Capture | URL parameters | `Capture "userId" UserId` | +| QueryParam | URL query parameters | `QueryParam "sort" SortOrder` | +| Header | HTTP headers | `Header "Authorization" Token` | +| ReqBody | Request body | `ReqBody '[JSON] User` | +| Verb | HTTP methods | `Get '[JSON] User` | + +## Contributing + +Feel free to open issues or submit pull requests if you find bugs or have suggestions for improvements. The plugin is designed to be extensible for supporting additional Servant combinators and output formats. \ No newline at end of file diff --git a/endpoints/endpoints.cabal b/endpoints/endpoints.cabal new file mode 100644 index 0000000..be2b360 --- /dev/null +++ b/endpoints/endpoints.cabal @@ -0,0 +1,109 @@ +cabal-version: 3.0 +-- The cabal-version field refers to the version of the .cabal specification, +-- and can be different from the cabal-install (the tool) version and the +-- Cabal (the library) version you are using. As such, the Cabal (the library) +-- version used must be equal or greater than the version stated in this field. +-- Starting from the specification version 2.2, the cabal-version field must be +-- the first thing in the cabal file. + +-- Initial package description 'endpoints' generated by +-- 'cabal init'. For further documentation, see: +-- http://haskell.org/cabal/users-guide/ +-- +-- The name of the package. +name: endpoints + +-- The package version. +-- See the Haskell package versioning policy (PVP) for standards +-- guiding when and how versions should be incremented. +-- https://pvp.haskell.org +-- PVP summary: +-+------- breaking API changes +-- | | +----- non-breaking API additions +-- | | | +--- code changes with no API change +version: 0.1.0.0 + +-- A short (one-line) description of the package. +-- synopsis: + +-- A longer description of the package. +-- description: + +-- The license under which the package is released. +license: MIT + +-- The file containing the license text. +license-file: LICENSE + +-- The package author(s). +author: eswar2001 + +-- An email address to which users can send suggestions, bug reports, and patches. +maintainer: eswar.tadiparth@juspay.in + +-- A copyright notice. +-- copyright: +category: Development +build-type: Simple + +-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README. +extra-doc-files: CHANGELOG.md + +-- Extra source files to be distributed with the package, such as examples, or a tutorial module. +-- extra-source-files: + +common warnings + ghc-options: -Wall + +library + -- Import common warning flags. + import: warnings + + -- Modules exported by the library. + exposed-modules: Endpoints.Plugin + default-extensions: OverloadedStrings + + -- Modules included in this library but not exported. + -- other-modules: + + -- LANGUAGE extensions used by modules in this package. + -- other-extensions: + + -- Other library packages from which modules are imported. + build-depends: servant,containers,base,text,directory, bytestring, aeson, extra, aeson-pretty, unordered-containers, uniplate, ghc, references, yaml + + + -- Directories containing source files. + hs-source-dirs: src + + -- Base language which the package is written in. + default-language: Haskell2010 + +test-suite endpoints-test + -- Import common warning flags. + import: warnings + + -- Base language which the package is written in. + default-language: Haskell2010 + ghc-options: -fplugin=Endpoints.Plugin + -- Modules included in this executable, other than Main. + -- other-modules: + + -- LANGUAGE extensions used by modules in this package. + -- other-extensions: + + -- The interface type and version of the test suite. + type: exitcode-stdio-1.0 + + -- Directories containing source files. + hs-source-dirs: test + + -- The entrypoint to the test suite. + main-is: Main.hs + + -- Test dependencies. + build-depends: + base ^>=4.16.4.0 + , endpoints + , servant + , servant-server + , text diff --git a/endpoints/src/Endpoints/Plugin.hs b/endpoints/src/Endpoints/Plugin.hs new file mode 100644 index 0000000..3f06aee --- /dev/null +++ b/endpoints/src/Endpoints/Plugin.hs @@ -0,0 +1,349 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE StandaloneDeriving #-} + +module Endpoints.Plugin where + +#if __GLASGOW_HASKELL__ >= 900 +import GHC +import GHC.Data.FastString (unpackFS) +import qualified Data.Text as T +import GHC.Types.TypeEnv +import Data.Data ( Data(toConstr) ) +import Data.List.Extra (intercalate, isSuffixOf, replace, splitOn,groupBy) +import GHC.Driver.Plugins (Plugin(..),CommandLineOption,defaultPlugin,PluginRecompile(..)) +import GHC.Utils.Outputable (showSDocUnsafe,ppr,SDoc,Outputable) +import GHC.Data.Bag +import System.Directory ( createDirectoryIfMissing ) +import Control.Monad.IO.Class (liftIO) +import GHC.Types.Name hiding (varName) +import GHC.Types.Var +import GHC.Driver.Env +import GHC.Tc.Types +import GHC.Unit.Module.ModSummary +import GHC.Utils.Outputable (showSDocUnsafe,ppr) +import GHC.Data.Bag (bagToList) +import qualified Data.Aeson.KeyMap as HM +import GHC.Hs.Expr +import Language.Haskell.Syntax.Pat +import GHC as GhcPlugins +import GHC.Generics (Generic) +import Data.Aeson +import GHC.Core.DataCon as GhcPlugins +import GHC.Core.TyCon as GhcPlugins +import GHC.Core.TyCo.Rep +import GHC.Driver.Session +import qualified Data.ByteString as DBS +import qualified Data.Map as Map +import Data.Maybe +import Data.List (intercalate,isInfixOf,foldl') +import GHC.Core.Type +import Control.Applicative ((<|>)) +import Debug.Trace (traceShowId,trace) +import Control.Monad (when) +import Control.Concurrent (MVar,putMVar,takeMVar,readMVar,newMVar) +import GHC.IO (unsafePerformIO) +import qualified Data.Aeson.Key as Key +import Control.Reference (biplateRef, (^?)) +import Data.Generics.Uniplate.Data () +#endif + +plugin :: Plugin +plugin = + defaultPlugin + { + pluginRecompile = (\_ -> return NoForceRecompile) + , typeCheckResultAction = collectTypesTC + } + +-- cachedTypes :: MVar (HM.KeyMap Type) +-- cachedTypes = unsafePerformIO (newMVar mempty) + +collectTypesTC :: [CommandLineOption] -> ModSummary -> TcGblEnv -> TcM TcGblEnv +collectTypesTC opts modSummary tcg = do + dflags <- getDynFlags + _ <- liftIO $ + do + let prefixPath = case opts of + [] -> "/tmp/endpoints/" + local : _ -> local + moduleName' = moduleNameString $ GhcPlugins.moduleName $ ms_mod modSummary + modulePath = prefixPath <> msHsFilePath modSummary + typeEnv = tcg_type_env tcg + path = (intercalate "/" . init . splitOn "/") modulePath + servantAPIs <- mapM (\x -> + case x of + ATyCon tyCon -> getAPITypes tyCon + _ -> pure mempty + ) (typeEnvElts typeEnv) + -- hm <- takeMVar cachedTypes + -- putMVar cachedTypes $ (foldl' (\hm (name,ty) -> HM.insert (Key.fromText $ T.pack $ showSDocUnsafe $ ppr name) ty hm) hm (concat servantAPIs)) + parsedEndpoints <- processServantApis $ concat servantAPIs + createDirectoryIfMissing True path + DBS.writeFile (modulePath <> ".api-spec.json") (DBS.toStrict $ encode $ parsedEndpoints) + return tcg + +mergeEndpoints x = x + +isServantAPI :: Type -> Bool +isServantAPI ty = go ty + where + go :: Type -> Bool + go t = case splitTyConApp_maybe t of + Just (tyCon', args) -> + let tyConName' = getOccString (GhcPlugins.tyConName tyCon') + in isServantCombinator tyConName' || any go args + Nothing -> False + + isServantCombinator :: String -> Bool + isServantCombinator name = name `elem` [ + ":>", ":<|>", "Get", "Post", "Put", "Delete", "Patch", + "Capture", "QueryParam", "QueryParams", "QueryFlag", + "Header", "ReqBody", "StreamBody", "Raw", "EmptyAPI" + ] + +getAPITypes :: GhcPlugins.TyCon -> IO [(Name,Type)] +getAPITypes tyCon' = do + let name = GhcPlugins.tyConName tyCon' + tyConStr = showSDocUnsafe (ppr name) + tyConKind' = tyConKind tyCon' + kindStr = showSDocUnsafe (ppr tyConKind') + dataCons = tyConDataCons tyCon' + case synTyConRhs_maybe tyCon' of + Just rhs -> + if isServantAPI rhs + then do + pure $ [(name, rhs)] + else pure $ mempty + Nothing -> pure mempty + +data HttpMethod = Get | Post | Put | Delete | Patch + deriving (Show, Eq,Data) + +-- data ResponseType = JSON | Text | CUSTOM Text deriving (Show) + +data Endpoint = Endpoint + { method :: String + , path :: [String] + , queryParams :: [String] + , headers :: [String] + , responseStatus :: String + , responseContentType :: Maybe String + , responseBody :: Maybe String + , requestBody :: Maybe String + , requestContentType :: Maybe String + } deriving (Generic,Show) + +deriving instance ToJSON Endpoint + +-- processServantApis :: [(Name,Type)] -> IO [(String,[Endpoint])] +processServantApis ts = do + res <- mapM (\(name',type') -> do + res <- parseApiType type' + endpoints <- (maybe mempty parseApiDefinition) res + pure (showSDocUnsafe $ ppr name',endpoints) + ) ts + pure $ Map.fromList res + +mergeAllURLOptions :: [ApiComponent] -> [Endpoint] +mergeAllURLOptions list = + let (headers,queryParams,verbs,captures,reqBodyList,contentTypeList) = processList list + in concatMap (\x -> + case x of + (Verb method status responseType responseBody) -> [(Endpoint method captures queryParams headers status responseType responseBody (if null $ concat reqBodyList then Nothing else Just $ concat reqBodyList) (if null $ concat contentTypeList then Nothing else Just $ concat contentTypeList))] + _ -> mempty + ) verbs + where + processList list = + foldl' (\(rh,qp,verb,capture,reqBodyList,contentTypeList) x -> + case x of + (QueryParam p) -> (rh,qp <> [p],verb,capture,reqBodyList,contentTypeList) + (Header p) -> (rh <> [p], qp,verb,capture,reqBodyList,contentTypeList) + (Verb method status responseType responseBody) -> (rh,qp,verb <> [x],capture,reqBodyList,contentTypeList) + (ReqBody contentType reqBody) -> (rh,qp,verb,capture,(reqBodyList <> [reqBody]),contentTypeList <> [contentType]) + (Group "" l) -> + let (a,b,c,cc,reqBodyL,contentTypeL) = processList l + in (rh <> a,qp <> b,verb <> c,capture <>cc,(reqBodyList <> reqBodyL),(contentTypeList <> contentTypeL)) + (Group p []) -> (rh,qp,verb,capture <> [p],reqBodyList,contentTypeList) + (Group x l) -> + let (a,b,c,cc,reqBodyL,contentTypeL) = processList l + in (rh <> a,qp <> b,verb <> c,capture <> cc <> [x],(reqBodyList <> reqBodyL),contentTypeList<>contentTypeL) + (Tag p) -> (rh,qp,verb,capture,reqBodyList,contentTypeList) + _ -> (rh,qp,verb,capture,reqBodyList,contentTypeList) + ) ([],[],[],([] :: [String]),[],[]) list + +parseApiDefinition :: ApiComponent -> IO [Endpoint] +parseApiDefinition (Path p) = pure [Endpoint mempty [p] [] mempty "" Nothing Nothing Nothing Nothing] +parseApiDefinition (Verb method status responseType _) = pure [Endpoint (method) [] [] mempty status (responseType) Nothing Nothing Nothing] +parseApiDefinition (Group p []) = pure [Endpoint mempty [p] [] mempty mempty Nothing Nothing Nothing Nothing] +parseApiDefinition (Group "" [(Verb method status responseType responseBody)]) = pure [Endpoint (method) [] [] mempty status (responseType) responseBody Nothing Nothing] +parseApiDefinition (Group "" [(ReqBody contentType reqBody),(Verb method status responseType responseBody)]) = pure [Endpoint (method) [] [] mempty status (responseType) responseBody (Just reqBody) (Just contentType)] +-- parseApiDefinition (Group "" [(Verb method status responseType responseBody)]) = pure [Endpoint (method) [] [] mempty status (responseType) responseBody] +parseApiDefinition (Group "" (x:xs)) = do + pure $ mergeAllURLOptions (x:xs) +parseApiDefinition (Group p [(ReqBody contentType reqBody),(Verb method status responseType responseBody)]) = pure [Endpoint (method) [p] [] mempty status (responseType) responseBody (Just reqBody) (Just contentType)] +parseApiDefinition (Group p [(Verb method status responseType responseBody)]) = pure [Endpoint (method) [p] [] mempty status (responseType) responseBody Nothing Nothing] +parseApiDefinition x@(Group p comps) = do + endpointsList <- mapM parseApiDefinition comps + let filteredList = concat $ filter (not . null) endpointsList + case filteredList of + [] -> do + pure [Endpoint mempty [p] [] mempty mempty Nothing Nothing Nothing Nothing] + [(endpoint)] -> pure [(\(Endpoint method path qp h rs rt rb reqBody contentType) -> Endpoint method ([p] <> path) qp h rs rt rb reqBody contentType) endpoint] + _ -> pure $ (map (\endpoint -> endpoint { path = [p] <> (path endpoint) })) filteredList +parseApiDefinition (Alternative comps) = concat <$> mapM parseApiDefinition comps +parseApiDefinition _ = pure [] + +-- parseApiDefinition :: ApiComponent -> IO [[String]] +-- parseApiDefinition (Path p) = pure [[p]] +-- parseApiDefinition (Capture p) = pure [["{" ++ p ++ "}"]] +-- parseApiDefinition (QueryParam p) = pure mempty--[["?" ++ p ++ "={" ++ p ++ "}"]] +-- parseApiDefinition (Header p) = pure mempty--pure [["@" ++ p]] +-- parseApiDefinition ReqBody = pure mempty +-- parseApiDefinition (Verb method status responseType responseBody) = pure [[("->" ++ method ++ "->" ++ status ++ "->" ++ responseType ++ "->" ++ responseBody)]] +-- parseApiDefinition (Group (p) []) = pure [[p]] +-- parseApiDefinition (Group ("") [(Verb method status responseType responseBody)]) = do +-- pure [[("->" ++ method ++ "->" ++ status ++ "->" ++ responseType ++ "->" ++ responseBody)]] +-- parseApiDefinition (Group (p) [(Verb method status responseType responseBody)]) = do +-- pure [[p ++ "/" ++ ("->" ++ method ++ "->" ++ status ++ "->" ++ responseType ++ "->" ++ responseBody)]] +-- parseApiDefinition (Group (p) comps) = do +-- pathsList <- mapM parseApiDefinition comps +-- let filteredList = filter (\x -> +-- case x of +-- [] -> False +-- [[]] -> False +-- _ -> True +-- ) pathsList +-- print (toConstr $ head comps,filteredList,pathsList) +-- case pathsList of +-- [] -> +-- pure $ [[p]] +-- [[[x]]] -> do +-- case p of +-- "" -> pure [[x]] +-- _ -> pure [[p <> "/" <> x]] +-- (x:xs) -> +-- case p of +-- "" -> pure $ concat filteredList +-- _ -> pure $ concatMap (map (map ((p <> "/") <>))) filteredList +-- parseApiDefinition (Alternative comps) = concat <$> mapM parseApiDefinition comps +-- parseApiDefinition _ = pure mempty + +data ApiComponent + = Path String + | Capture String + | QueryParam String + | Header String + | AddArgs String + | ReqBody String String + | Verb String String (Maybe String) (Maybe String) + | Group String [ApiComponent] + | Alternative [ApiComponent] + | Tag String + deriving (Generic,Show,Data,ToJSON) + +headMaybe :: [a] -> Maybe a +headMaybe [] = Nothing +headMaybe (x:xs) = Just x + +tailMaybe :: [a] -> Maybe [a] +tailMaybe [] = Nothing +tailMaybe [x] = Nothing +tailMaybe (x:xs) = Just xs + +-- extractResponseTypeToLink :: Type -> String -> IO (Maybe ResponseType) +-- extractResponseTypeToLink (TyConApp tyCon args) rawStr = +-- case showSDocUnsafe $ ppr $ (tyCon) of +-- "Headers" -> +-- case args of +-- [(TyConApp tyConh headers),resp] -> do +-- pure $ Just $ ResponseType (map (showSDocUnsafe . ppr) headers) [((showSDocUnsafe . ppr) resp)] rawStr +-- _ -> pure $ Just $ ResponseType mempty mempty rawStr +-- _ -> pure $ Just $ ResponseType mempty mempty rawStr + +-- extractRequestTypeToLink x@(TyConApp tyCon args) raw = pure $ Just $ RequestType [showSDocUnsafe $ ppr x] (raw) + +parseApiType :: Type -> IO (Maybe ApiComponent) +parseApiType ty = do + case splitTyConApp_maybe ty of + Just (tyCon, args) -> do + let tyConName' = getOccString (tyConName tyCon) + case tyConName' of + ":>" -> do + res <- catMaybes <$> mapM parseApiType args + case res of + (Path p : rest) -> pure $ Just $ Group p rest + (Capture p : rest) -> pure $ Just $ Group ("{" ++ p ++ "}") rest + _ -> do + if null res + then pure Nothing + else pure $ Just $ Group "" res + ":<|>" -> do + res <- mapM parseApiType args + pure $ Just $ Alternative (catMaybes res) + "Header'" -> do + res <- mapM parseApiType args + case catMaybes res of + [Path p] -> pure $ Just $ Header p + _ -> pure Nothing + "Capture'" -> do + res <- mapM parseApiType args + -- print $ (tyConName',showSDocUnsafe $ ppr $ ty,map (\x -> (showSDocUnsafe $ ppr x,toConstr x)) args,res) + case catMaybes res of + [Path p] -> pure $ Just $ Group ("{" ++ p ++ "}") mempty + _ -> pure Nothing + "QueryParam'" -> do + res <- mapM parseApiType args + case catMaybes res of + [Path p] -> pure $ Just $ QueryParam p + _ -> pure Nothing + "QueryFlag" -> do + res <- mapM parseApiType args + case catMaybes res of + [Path p] -> pure $ Just $ QueryParam p + _ -> pure Nothing + "AddArgs" -> do + res <- mapM parseApiType args + -- print ("----AddArgs",null (catMaybes res),catMaybes res,map (\x -> (showSDocUnsafe $ ppr x,toConstr x)) args) + if null (catMaybes res) + then pure Nothing + else pure $ Just $ Group "" $ catMaybes res + "Verb" -> do + res <- mapM (\x -> pure $ (showSDocUnsafe $ ppr x)) args + case res of + ["StdMethod",method,statusCode,encodeType,responseTypeName] -> pure $ Just $ Verb method statusCode (Just encodeType) (Just $ showSDocUnsafe $ ppr $ last $ args) + _ -> pure Nothing + "ReqBody'" -> do + res <- mapM (\x -> pure $ (showSDocUnsafe $ ppr x)) args + case res of + [("'[Required, Strict]"),reqContentType,reqBody] -> pure $ Just $ ReqBody (reqContentType) reqBody + _ -> pure Nothing + "Tag" -> pure $ Just $ Tag (showSDocUnsafe $ ppr args) + "TYPE" -> do + res <- mapM parseApiType args + if null (catMaybes res) + then pure Nothing + else pure $ Just $ Group "" $ catMaybes res + ":" -> do + res <- mapM parseApiType args + if null (catMaybes res) + then pure Nothing + else pure $ Just $ Group "" $ catMaybes res + _ -> do + -- print ("------",tyConName',showSDocUnsafe $ ppr tyCon,map (\x -> (showSDocUnsafe $ ppr x,toConstr x)) args) + pure Nothing + Nothing -> do + case isStrLitTy ty of + Just fs -> pure $ Just $ Path (unpackFS fs) + Nothing -> pure Nothing + +isTyConApp :: Type -> Bool +isTyConApp (TyConApp _ _) = True +isTyConApp _ = False \ No newline at end of file diff --git a/endpoints/test/Main.hs b/endpoints/test/Main.hs new file mode 100644 index 0000000..6319296 --- /dev/null +++ b/endpoints/test/Main.hs @@ -0,0 +1,53 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} + +module Main (main) where + +import Data.Text +import Servant + +main :: IO () +main = putStrLn "Test suite not yet implemented." + +-- Sample data types +data User = User + { userId :: Int + , userName :: Text + } + +data Product = Product + { productId :: Int + , productName :: Text + , productPrice :: Double + } + +-- API definition +type SampleAPI = "api" :> ( + "users" :> UsersAPI + :<|> "products" :> ProductsAPI + :<|> "health" :> Get '[JSON] Text + ) + +type UsersAPI = + Get '[JSON] [User] + :<|> Capture "userId" Int :> Get '[JSON] User + :<|> ReqBody '[JSON] User :> Post '[JSON] User + :<|> Capture "userId" Int :> ReqBody '[JSON] User :> Put '[JSON] User + :<|> Capture "userId" Int :> Delete '[JSON] NoContent + +type ProductsAPI = + Get '[JSON] [Product] + :<|> Capture "productId" Int :> Get '[JSON] Product + :<|> ReqBody '[JSON] Product :> Post '[JSON] Product + :<|> Capture "productId" Int :> ReqBody '[JSON] Product :> Put '[JSON] Product + :<|> Capture "productId" Int :> Delete '[JSON] NoContent + :<|> "search" :> QueryParam "query" Text :> Get '[JSON] [Product] + +-- The main API type that your plugin will analyze +type EulerAPI = "v1" :> SampleAPI \ No newline at end of file diff --git a/fdep/fdep.cabal b/fdep/fdep.cabal index 1bb8f1f..c9161a7 100644 --- a/fdep/fdep.cabal +++ b/fdep/fdep.cabal @@ -59,6 +59,7 @@ library , websockets , network , primitive + , sqlite-simple hs-source-dirs: src default-language: Haskell2010 diff --git a/fdep/src/Fdep/Plugin.hs b/fdep/src/Fdep/Plugin.hs index dff78f8..9e90a43 100644 --- a/fdep/src/Fdep/Plugin.hs +++ b/fdep/src/Fdep/Plugin.hs @@ -30,7 +30,7 @@ import Fdep.Types ( PFunction(PFunction), FunctionInfo(FunctionInfo) ) import Text.Read (readMaybe) -import Prelude hiding (id, mapM, mapM_, writeFile) +import Prelude hiding (id, writeFile) import qualified Prelude as P import qualified Data.List.Extra as Data.List import Network.Socket (withSocketsDo) @@ -39,7 +39,6 @@ import System.Directory ( createDirectoryIfMissing ) import System.Environment (lookupEnv) import GHC.IO (unsafePerformIO) #if __GLASGOW_HASKELL__ >= 900 -import Streamly.Internal.Data.Stream (fromList,mapM_,mapM,toList) import GHC import GHC.Driver.Plugins (Plugin(..),CommandLineOption,defaultPlugin,PluginRecompile(..)) import GHC.Driver.Env @@ -51,8 +50,8 @@ import GHC.Types.Name hiding (varName) import GHC.Types.Var import qualified Data.Aeson.KeyMap as HM import GHC.Hs.Expr +import Language.Haskell.Syntax.Pat #else -import Streamly.Internal.Data.Stream (fromList, mapM, mapM_, toList) import qualified Data.HashMap.Strict as HM import Bag (bagToList) import DynFlags () @@ -155,7 +154,7 @@ collectDecls opts modSummary hsParsedModule = do path = (Data.List.intercalate "/" . reverse . tail . reverse . splitOn "/") modulePath declsList = hsmodDecls $ unLoc $ hpm_module hsParsedModule createDirectoryIfMissing True path - functionsVsCodeString <- toList $ mapM getDecls $ fromList declsList + functionsVsCodeString <- mapM getDecls declsList writeFile (modulePath <> ".function_code.json") (encodePretty $ Map.fromList $ concat functionsVsCodeString) pure hsParsedModule @@ -206,7 +205,7 @@ shouldLog = readBool $ unsafePerformIO $ lookupEnv "ENABLE_LOGS" readBool _ = False websocketPort :: Int -websocketPort = maybe 8000 (fromMaybe 8000 . readMaybe) $ unsafePerformIO $ lookupEnv "SERVER_PORT" +websocketPort = maybe 9898 (fromMaybe 9898 . readMaybe) $ unsafePerformIO $ lookupEnv "SERVER_PORT" websocketHost :: String websocketHost = fromMaybe "localhost" $ unsafePerformIO $ lookupEnv "SERVER_HOST" @@ -221,38 +220,6 @@ decodeBlacklistedFunctions = do _ -> filterList _ -> filterList -fDep :: [CommandLineOption] -> ModSummary -> TcGblEnv -> TcM TcGblEnv -fDep opts modSummary tcEnv = do - when (shouldGenerateFdep) $ - liftIO $ - bool P.id (void . forkIO) shouldForkPerFile $ do - let prefixPath = case opts of - [] -> "/tmp/fdep/" - local : _ -> local - moduleName' = moduleNameString $ moduleName $ ms_mod modSummary - modulePath = prefixPath <> msHsFilePath modSummary - let path = (Data.List.intercalate "/" . reverse . tail . reverse . splitOn "/") modulePath - when shouldLog $ print ("generating dependancy for module: " <> moduleName' <> " at path: " <> path) - createDirectoryIfMissing True path - let binds = bagToList $ tcg_binds tcEnv - t1 <- getCurrentTime - withSocketsDo $ do - eres <- try $ WS.runClient websocketHost websocketPort ("/" <> modulePath <> ".json") (\conn -> do mapM_ (loopOverLHsBindLR (Just conn) Nothing (T.pack ("/" <> modulePath <> ".json"))) (fromList binds)) - case eres of - Left (err :: SomeException) -> when shouldLog $ print err - Right _ -> pure () - t2 <- getCurrentTime - when shouldLog $ print ("generated dependancy for module: " <> moduleName' <> " at path: " <> path <> " total-timetaken: " <> show (diffUTCTime t2 t1)) - return tcEnv - -transformFromNameStableString :: (Maybe Text, Maybe Text, Maybe Text, [Text]) -> Maybe FunctionInfo -transformFromNameStableString (Just str, Just loc, _type, args) = - let parts = filter (\x -> x /= "") $ T.splitOn ("$") str - in Just $ if length parts == 2 then FunctionInfo "" (parts !! 0) (parts !! 1) (fromMaybe "" _type) loc args else FunctionInfo (parts !! 0) (parts !! 1) (parts !! 2) (fromMaybe "" _type) loc args -transformFromNameStableString (Just str, Nothing, _type, args) = - let parts = filter (\x -> x /= "") $ T.splitOn ("$") str - in Just $ if length parts == 2 then FunctionInfo "" (parts !! 0) (parts !! 1) (fromMaybe "" _type) "" args else FunctionInfo (parts !! 0) (parts !! 1) (parts !! 2) (fromMaybe "" _type) "" args - sendTextData' :: WS.Connection -> Text -> Text -> IO () sendTextData' conn path data_ = do t1 <- getCurrentTime @@ -265,316 +232,510 @@ sendTextData' conn path data_ = do t2 <- getCurrentTime when (shouldLog) $ print ("websocket call timetaken: " <> (T.pack $ show $ diffUTCTime t2 t1)) -loopOverLHsBindLR :: Maybe WS.Connection -> (Maybe Text) -> Text -> LHsBindLR GhcTc GhcTc -> IO () -#if __GLASGOW_HASKELL__ >= 900 -loopOverLHsBindLR mConn mParentName path (L _ x@(FunBind fun_ext id matches _)) = do -#else -loopOverLHsBindLR mConn mParentName path (L _ x@(FunBind fun_ext id matches _ _)) = do -#endif - funName <- evaluate $ force $ T.pack $ getOccString $ unLoc id - fName <- evaluate $ force $ T.pack $ nameStableString $ getName id - let matchList = mg_alts matches - if funName `elem` (unsafePerformIO $ decodeBlacklistedFunctions) || ("$_in$$" `T.isPrefixOf` fName) - then pure mempty - else do - when (shouldLog) $ print ("processing function: " <> fName) -#if __GLASGOW_HASKELL__ >= 900 - name <- evaluate $ force (fName <> "**" <> (T.pack (getLoc' id))) -#else - name <- evaluate $ force (fName <> "**" <> (T.pack ((showSDocUnsafe . ppr . getLoc) id))) -#endif - typeSignature <- evaluate $ force $ (T.pack $ showSDocUnsafe (ppr (varType (unLoc id)))) - nestedNameWithParent <- evaluate $ force $ (maybe (name) (\x -> x <> "::" <> name) mParentName) - data_ <- evaluate $ force $ (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String nestedNameWithParent), ("typeSignature", String typeSignature)]) +fDep :: [CommandLineOption] -> ModSummary -> TcGblEnv -> TcM TcGblEnv +fDep opts modSummary tcEnv = do + when (shouldGenerateFdep) $ + liftIO $ bool P.id (void . forkIO) shouldForkPerFile $ do + let prefixPath = case opts of + [] -> "/tmp/fdep/" + local : _ -> local + moduleName' = moduleNameString $ moduleName $ ms_mod modSummary + modulePath = prefixPath <> msHsFilePath modSummary + let path = (Data.List.intercalate "/" . reverse . tail . reverse . splitOn "/") modulePath + when shouldLog $ print ("generating dependancy for module: " <> moduleName' <> " at path: " <> path) + createDirectoryIfMissing True path t1 <- getCurrentTime - if isJust mConn - then do - sendTextData' (fromJust mConn) path data_ - mapM_ (processMatch (fromJust mConn) nestedNameWithParent path) (fromList $ unLoc matchList) - else - withSocketsDo $ - WS.runClient websocketHost websocketPort (T.unpack path) ( \conn -> do - sendTextData' (conn) path data_ - mapM_ (processMatch conn (nestedNameWithParent) path) (fromList $ unLoc matchList) - ) + withSocketsDo $ do + eres <- try $ WS.runClient websocketHost websocketPort ("/" <> modulePath <> ".json") (\conn -> do mapM_ (loopOverLHsBindLR conn Nothing (T.pack ("/" <> modulePath <> ".json"))) ((bagToList $ tcg_binds tcEnv))) + case eres of + Left (err :: SomeException) -> when shouldLog $ print err + Right _ -> pure () t2 <- getCurrentTime - when (shouldLog) $ print $ "processed function: " <> fName <> " timetaken: " <> (T.pack $ show $ diffUTCTime t2 t1) -loopOverLHsBindLR mConn mParentName path (L _ AbsBinds{abs_binds = binds}) = - mapM_ (loopOverLHsBindLR mConn mParentName path) $ fromList $ bagToList binds -loopOverLHsBindLR _ _ _ (L _ VarBind{var_rhs = rhs}) = pure mempty -loopOverLHsBindLR _ _ _ (L _ (PatSynBind _ PSB{psb_def = def})) = pure mempty -loopOverLHsBindLR _ _ _ (L _ (PatSynBind _ (XPatSynBind _))) = pure mempty -loopOverLHsBindLR _ _ _ (L _ (XHsBindsLR _)) = pure mempty -loopOverLHsBindLR _ _ _ (L _ (PatBind _ _ pat_rhs _)) = pure mempty - -processMatch :: WS.Connection -> Text -> Text -> LMatch GhcTc (LHsExpr GhcTc) -> IO () -processMatch con keyFunction path (L _ match) = do -#if __GLASGOW_HASKELL__ >= 900 - whereClause <- (evaluate . force) =<< (processHsLocalBinds con keyFunction path $ grhssLocalBinds (m_grhss match)) -#else - whereClause <- (evaluate . force) =<< (processHsLocalBinds con keyFunction path $ unLoc $ grhssLocalBinds (m_grhss match)) -#endif - mapM_ (processGRHS con keyFunction path) $ fromList $ grhssGRHSs (m_grhss match) - pure mempty - -processGRHS :: WS.Connection -> Text -> Text -> LGRHS GhcTc (LHsExpr GhcTc) -> IO () -processGRHS con keyFunction path (L _ (GRHS _ _ body)) = processExpr con keyFunction path body -processGRHS _ _ _ _ = pure mempty - -processHsLocalBinds :: WS.Connection -> Text -> Text -> HsLocalBindsLR GhcTc GhcTc -> IO () -processHsLocalBinds con keyFunction path (HsValBinds _ (ValBinds _ x y)) = do - mapM_ (loopOverLHsBindLR (Just con) (Just keyFunction) path) $ fromList $ bagToList $ x -processHsLocalBinds con keyFunction path (HsValBinds _ (XValBindsLR (NValBinds x y))) = do - mapM_ (\(recFlag, binds) -> mapM_ (loopOverLHsBindLR (Just con) (Just keyFunction) path) $ fromList $ bagToList binds) (fromList x) -processHsLocalBinds _ _ _ _ = pure mempty + when shouldLog $ print ("generated dependancy for module: " <> moduleName' <> " at path: " <> path <> " total-timetaken: " <> show (diffUTCTime t2 t1)) + return tcEnv -grhsExpr :: LGRHS GhcTc (LHsExpr GhcTc) -> LHsExpr GhcTc -grhsExpr (L _ (GRHS _ _ body)) = body +transformFromNameStableString :: (Maybe Text, Maybe Text, Maybe Text, [Text]) -> Maybe FunctionInfo +transformFromNameStableString (Just str, Just loc, _type, args) = + let parts = filter (\x -> x /= "") $ T.splitOn ("$") str + in Just $ if length parts == 2 then FunctionInfo "" (parts !! 0) (parts !! 1) (fromMaybe "" _type) loc args else FunctionInfo (parts !! 0) (parts !! 1) (parts !! 2) (fromMaybe "" _type) loc args +transformFromNameStableString (Just str, Nothing, _type, args) = + let parts = filter (\x -> x /= "") $ T.splitOn ("$") str + in Just $ if length parts == 2 then FunctionInfo "" (parts !! 0) (parts !! 1) (fromMaybe "" _type) "" args else FunctionInfo (parts !! 0) (parts !! 1) (parts !! 2) (fromMaybe "" _type) "" args -hsStmtsExpr :: WS.Connection -> Text -> Text -> [LStmt GhcTc (LHsExpr GhcTc)] -> IO () -hsStmtsExpr con keyFunction path stmts = mapM_ (stmtExpr con keyFunction path) $ fromList stmts +headMaybe :: [a] -> Maybe a +headMaybe [] = Nothing +headMaybe (x:xs) = Just x -stmtExpr :: WS.Connection -> Text -> Text -> LStmt GhcTc (LHsExpr GhcTc) -> IO () -stmtExpr con keyFunction path (L _ stmt) = case stmt of +loopOverLHsBindLR :: WS.Connection -> (Maybe Text) -> Text -> LHsBindLR GhcTc GhcTc -> IO () +loopOverLHsBindLR con mParentName path (L _ AbsBinds{abs_binds = binds}) = + mapM_ (loopOverLHsBindLR con mParentName path) $ bagToList binds +loopOverLHsBindLR con mParentName path (L location bind) = do + case bind of #if __GLASGOW_HASKELL__ >= 900 - BindStmt _ pat expr -> processExpr con keyFunction path expr + x@(FunBind fun_ext id matches _) -> do #else - BindStmt _ pat expr _ _-> processExpr con keyFunction path expr + x@(FunBind fun_ext id matches _ _) -> do #endif - BodyStmt _ expr _ _ -> processExpr con keyFunction path expr - LastStmt _ expr _ _ -> processExpr con keyFunction path expr - ParStmt _ stmtBlocks _ _ -> mapM_ blockExprs (fromList stmtBlocks) - TransStmt{..} -> do - hsStmtsExpr con keyFunction path $ trS_stmts - processExpr con keyFunction path trS_using - maybe (pure ()) (processExpr con keyFunction path) trS_by - ApplicativeStmt _ args _ -> mapM_ (extractApplicativeArg con keyFunction path . snd) (fromList args) + funName <- pure $ T.pack $ getOccString $ unLoc id + fName <- pure $ T.pack $ nameStableString $ getName id + let matchList = mg_alts matches + if funName `elem` (unsafePerformIO $ decodeBlacklistedFunctions) || ("$_in$$" `T.isPrefixOf` fName) + then pure mempty + else do + when (shouldLog) $ print ("processing function: " <> fName) #if __GLASGOW_HASKELL__ >= 900 - RecStmt{..} -> mapM_ (stmtExpr con keyFunction path) (fromList $ unXRec @(GhcTc) recS_stmts) - LetStmt _ binds -> processHsLocalBinds con keyFunction path binds + name <- pure (fName <> "**" <> (T.pack (getLoc' id))) #else - RecStmt{..} -> mapM_ (stmtExpr con keyFunction path) (fromList recS_stmts) - LetStmt _ binds -> processHsLocalBinds con keyFunction path (unLoc binds) + name <- pure (fName <> "**" <> (T.pack ((showSDocUnsafe . ppr . getLoc) id))) #endif - XStmtLR{} -> pure () - where - blockExprs :: ParStmtBlock GhcTc GhcTc -> IO () - blockExprs (ParStmtBlock _ stmts _ _) = mapM_ (stmtExpr con keyFunction path) (fromList stmts) + typeSignature <- pure $ (T.pack $ showSDocUnsafe (ppr (varType (unLoc id)))) + nestedNameWithParent <- pure $ (maybe (name) (\x -> x <> "::" <> name) mParentName) + data_ <- pure (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String nestedNameWithParent), ("typeSignature", String typeSignature)]) + t1 <- getCurrentTime + sendTextData' con path data_ + mapM_ (processMatch (nestedNameWithParent) path) (unLoc matchList) + t2 <- getCurrentTime + when (shouldLog) $ print $ "processed function: " <> fName <> " timetaken: " <> (T.pack $ show $ diffUTCTime t2 t1) + (VarBind{var_id = var, var_rhs = expr}) -> do + let stmts = (expr ^? biplateRef :: [LHsExpr GhcTc]) + fName = T.pack $ nameStableString $ getName var #if __GLASGOW_HASKELL__ >= 900 - extractApplicativeArg con keyFunction path (ApplicativeArgOne _ _ arg_expr _) = processExpr con keyFunction path arg_expr - extractApplicativeArg con keyFunction path (ApplicativeArgMany _ app_stmts final_expr _ _) = do - hsStmtsExpr con keyFunction path app_stmts - processExpr con keyFunction path $ wrapXRec @(GhcTc) final_expr + name <- pure (fName <> "**" <> (T.pack ((showSDocUnsafe . ppr) $ locA location))) #else - extractApplicativeArg con keyFunction path (ApplicativeArgOne _ _ arg_expr _ _) = processExpr con keyFunction path arg_expr - extractApplicativeArg con keyFunction path (ApplicativeArgMany _ app_stmts final_expr _) = do - hsStmtsExpr con keyFunction path app_stmts - processExpr con keyFunction path (noLoc final_expr) + name <- pure (fName <> "**" <> (T.pack ((showSDocUnsafe . ppr) location))) #endif - extractApplicativeArg _ _ _ _ = pure () - -processExpr :: WS.Connection -> Text -> Text -> LHsExpr GhcTc -> IO () -processExpr con keyFunction path x@(L _ (HsVar _ (L _ var))) = do - let name = T.pack $ nameStableString $ varName var - _type = T.pack $ showSDocUnsafe $ ppr $ varType var - expr <- evaluate $ force $ transformFromNameStableString (Just name, Just $ T.pack $ getLocTC' $ x, Just _type, mempty) - sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) -processExpr _ _ _ (L _ (HsUnboundVar _ _)) = pure mempty -processExpr con keyFunction path (L _ (HsApp _ funl funr)) = do - processExpr con keyFunction path funl - processExpr con keyFunction path funr -processExpr con keyFunction path (L _ (OpApp _ funl funm funr)) = do - processExpr con keyFunction path funl - processExpr con keyFunction path funm - processExpr con keyFunction path funr -processExpr con keyFunction path (L _ (NegApp _ funl _)) = - processExpr con keyFunction path funl -processExpr con keyFunction path (L _ (HsTick _ _ fun)) = - processExpr con keyFunction path fun -processExpr con keyFunction path (L _ (HsStatic _ fun)) = - processExpr con keyFunction path fun -processExpr con keyFunction path (L _ (HsBinTick _ _ _ fun)) = - processExpr con keyFunction path fun -#if __GLASGOW_HASKELL__ < 900 -processExpr con keyFunction path (L _ (ExplicitList _ _ funList)) = - mapM_ (processExpr con keyFunction path) (fromList funList) -processExpr con keyFunction path (L _ (HsTickPragma _ _ _ _ fun)) = - processExpr con keyFunction path fun -processExpr con keyFunction path (L _ (HsSCC _ _ _ fun)) = - processExpr con keyFunction path fun -processExpr con keyFunction path (L _ (HsCoreAnn _ _ _ fun)) = - processExpr con keyFunction path fun -processExpr con keyFunction path (L _ x@(HsWrap _ _ fun)) = - processExpr con keyFunction path (noLoc fun) -processExpr con keyFunction path (L _ (HsIf _ exprLStmt funl funm funr)) = - let stmts = (exprLStmt ^? biplateRef :: [LHsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) $ fromList $ [funl, funm, funr] <> stmts -processExpr con keyFunction path (L _ (HsTcBracketOut b exprLStmtL exprLStmtR)) = - let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) - stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmtsL <> stmtsR) -processExpr con keyFunction path (L _ (ArithSeq _ Nothing exprLStmtR)) = - let stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) - stmtsRNoLoc = (exprLStmtR ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmtsR <> ((map noLoc) $ stmtsRNoLoc)) -processExpr con keyFunction path (L _ (HsRecFld _ exprLStmt)) = - let stmts = (exprLStmt ^? biplateRef :: [LHsExpr GhcTc]) - stmtsNoLoc = (exprLStmt ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList (stmts <> (map noLoc) stmtsNoLoc)) -processExpr con keyFunction path (L _ (HsRnBracketOut _ exprLStmtL exprLStmtR)) = - let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) - stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) - stmtsLNoLoc = (exprLStmtL ^? biplateRef :: [HsExpr GhcTc]) - stmtsRNoLoc = (exprLStmtR ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmtsL <> stmtsR <> (map noLoc $ (stmtsLNoLoc <> stmtsRNoLoc))) -processExpr con keyFunction path (L _ x@(RecordCon expr (L _ (iD)) rcon_flds)) = do - let stmts = (rcon_flds ^? biplateRef :: [LHsExpr GhcTc]) - stmtsNoLoc = (rcon_flds ^? biplateRef :: [HsExpr GhcTc]) - stmtsNoLocexpr = (expr ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmts <> (map noLoc) (stmtsNoLoc <> stmtsNoLocexpr)) -processExpr con keyFunction path (L _ (RecordUpd _ rupd_expr rupd_flds)) = - let stmts = (rupd_flds ^? biplateRef :: [LHsExpr GhcTc]) - stmtsNoLoc = (rupd_flds ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmts <> (map noLoc) stmtsNoLoc) + mapM_ (processExpr (name) path) (stmts) + (PatBind{pat_lhs = pat, pat_rhs = expr}) -> do + let stmts = (expr ^? biplateRef :: [LHsExpr GhcTc]) + ids = (pat ^? biplateRef :: [LIdP GhcTc]) + fName = (maybe (T.pack "::") (T.pack . nameStableString . getName) $ (headMaybe ids)) +#if __GLASGOW_HASKELL__ >= 900 + name <- pure (fName <> "**" <> (T.pack ((showSDocUnsafe . ppr) $ locA location))) #else -processExpr con keyFunction path (L _ (HsGetField _ exprLStmt _)) = - let stmts = exprLStmt ^? biplateRef :: [LHsExpr GhcTc] - stmtsNoLoc = exprLStmt ^? biplateRef :: [HsExpr GhcTc] - in mapM_ (processExpr con keyFunction path) (fromList (stmts <> (map (wrapXRec @(GhcTc)) stmtsNoLoc))) -processExpr con keyFunction path (L _ (ExplicitList _ funList)) = - mapM_ (processExpr con keyFunction path) (fromList funList) -processExpr con keyFunction path (L _ (HsPragE _ _ fun)) = - processExpr con keyFunction path fun -processExpr con keyFunction path (L _ (HsProc _ lPat fun)) = do - let stmts = lPat ^? biplateRef :: [LHsExpr GhcTc] - stmts' = fun ^? biplateRef :: [LHsExpr GhcTc] - mapM_ (processExpr con keyFunction path) (fromList (stmts <> stmts')) -processExpr con keyFunction path (L _ (HsIf exprLStmt funl funm funr)) = - let stmts = (exprLStmt ^? biplateRef :: [LHsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) $ fromList $ [funl, funm, funr] <> stmts -processExpr con keyFunction path (L _ (HsTcBracketOut b mQW exprLStmtL exprLStmtR)) = - let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) - stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmtsL <> stmtsR) -processExpr con keyFunction path (L _ (ArithSeq _ Nothing exprLStmtR)) = - let stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) - stmtsRNoLoc = (exprLStmtR ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmtsR <> ((map (wrapXRec @(GhcTc)) $ stmtsRNoLoc))) -processExpr con keyFunction path (L _ (HsRecFld _ exprLStmt)) = - let stmts = (exprLStmt ^? biplateRef :: [LHsExpr GhcTc]) - stmtsNoLoc = (exprLStmt ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList (stmts <> (map (wrapXRec @(GhcTc)) stmtsNoLoc))) -processExpr con keyFunction path (L _ (HsRnBracketOut _ exprLStmtL exprLStmtR)) = - let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) - stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) - stmtsLNoLoc = (exprLStmtL ^? biplateRef :: [HsExpr GhcTc]) - stmtsRNoLoc = (exprLStmtR ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmtsL <> stmtsR <> (map (wrapXRec @(GhcTc)) $ (stmtsLNoLoc <> stmtsRNoLoc))) -processExpr con keyFunction path (L _ x@(RecordCon expr (L _ (iD)) rcon_flds)) = do - let stmts = (rcon_flds ^? biplateRef :: [LHsExpr GhcTc]) - stmtsNoLoc = (rcon_flds ^? biplateRef :: [HsExpr GhcTc]) - stmtsNoLocexpr = (expr ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmts <> (map (wrapXRec @(GhcTc)) (stmtsNoLoc <> stmtsNoLocexpr))) -processExpr con keyFunction path (L _ (RecordUpd _ rupd_expr rupd_flds)) = - let stmts = (rupd_flds ^? biplateRef :: [LHsExpr GhcTc]) - stmtsNoLoc = (rupd_flds ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmts <> (map (wrapXRec @(GhcTc)) stmtsNoLoc)) + name <- pure (fName <> "**" <> (T.pack ((showSDocUnsafe . ppr) location))) #endif -processExpr con keyFunction path (L _ (ExprWithTySig _ fun _)) = - processExpr con keyFunction path fun -processExpr con keyFunction path (L _ (HsDo _ smtContext exprLStmt)) = - let stmts = (smtContext ^? biplateRef :: [LHsExpr GhcTc]) <> (exprLStmt ^? biplateRef :: [LHsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList stmts) -processExpr con keyFunction path (L _ (HsLet _ exprLStmt func)) = do + mapM_ (processExpr name path) (stmts <> map (\v -> wrapXRec @(GhcTc) $ HsVar noExtField v) (tail ids)) + where + processMatch :: Text -> Text -> LMatch GhcTc (LHsExpr GhcTc) -> IO () + processMatch keyFunction path (L _ match) = do #if __GLASGOW_HASKELL__ >= 900 - processHsLocalBinds con keyFunction path exprLStmt + whereClause <- processHsLocalBinds keyFunction path $ grhssLocalBinds (m_grhss match) #else - processHsLocalBinds con keyFunction path (unLoc exprLStmt) + whereClause <- processHsLocalBinds keyFunction path $ unLoc $ grhssLocalBinds (m_grhss match) #endif - processExpr con keyFunction path func -processExpr con keyFunction path (L _ (HsMultiIf _ exprLStmt)) = - let stmts = exprLStmt ^? biplateRef :: [LHsExpr GhcTc] - in mapM_ (processExpr con keyFunction path) (fromList stmts) -processExpr con keyFunction path (L _ (HsCase _ funl exprLStmt)) = do - processExpr con keyFunction path funl - mapM_ (processMatch con keyFunction path) (fromList $ unLoc $ mg_alts exprLStmt) -processExpr con keyFunction path (L _ (ExplicitSum _ _ _ fun)) = processExpr con keyFunction path fun -processExpr con keyFunction path (L _ (SectionR _ funl funr)) = processExpr con keyFunction path funl <> processExpr con keyFunction path funr -processExpr con keyFunction path (L _ (ExplicitTuple _ exprLStmt _)) = + mapM_ (processGRHS keyFunction path) $ grhssGRHSs (m_grhss match) + + processGRHS :: Text -> Text -> LGRHS GhcTc (LHsExpr GhcTc) -> IO () + processGRHS keyFunction path (L _ (GRHS _ _ body)) = processExpr keyFunction path body + processGRHS _ _ _ = pure mempty + + processHsLocalBinds :: Text -> Text -> HsLocalBindsLR GhcTc GhcTc -> IO () + processHsLocalBinds keyFunction path (HsValBinds _ (ValBinds _ x y)) = do + void $ mapM (loopOverLHsBindLR con (Just keyFunction) path) $ bagToList $ x + processHsLocalBinds keyFunction path (HsValBinds _ (XValBindsLR (NValBinds x y))) = do + void $ mapM (\(recFlag, binds) -> void $ mapM (loopOverLHsBindLR con (Just keyFunction) path) $ bagToList binds) ( x) + processHsLocalBinds _ _ _ = pure mempty + + processExpr :: Text -> Text -> LHsExpr GhcTc -> IO () + processExpr keyFunction path x@(L _ (HsVar _ (L _ var))) = do + let name = T.pack $ nameStableString $ varName var + _type = T.pack $ showSDocUnsafe $ ppr $ varType var + expr <- pure $ transformFromNameStableString (Just name, Just $ T.pack $ getLocTC' $ x, Just _type, mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + processExpr _ _ (L _ (HsUnboundVar _ _)) = pure mempty + processExpr keyFunction path (L _ (HsApp _ funl funr)) = do + processExpr keyFunction path funl + processExpr keyFunction path funr + processExpr keyFunction path (L _ (OpApp _ funl funm funr)) = do + processExpr keyFunction path funl + processExpr keyFunction path funm + processExpr keyFunction path funr + processExpr keyFunction path (L _ (NegApp _ funl _)) = + processExpr keyFunction path funl + processExpr keyFunction path (L _ (HsTick _ _ fun)) = + processExpr keyFunction path fun + processExpr keyFunction path (L _ (HsStatic _ fun)) = + processExpr keyFunction path fun + processExpr keyFunction path (L _ (HsBinTick _ _ _ fun)) = + processExpr keyFunction path fun + processExpr keyFunction path (L _ (ExprWithTySig _ fun _)) = + processExpr keyFunction path fun + processExpr keyFunction path (L _ (HsLet _ exprLStmt func)) = do #if __GLASGOW_HASKELL__ >= 900 - let l = (fromList exprLStmt) + processHsLocalBinds keyFunction path exprLStmt #else - let l = fromList (unLoc <$> exprLStmt) + processHsLocalBinds keyFunction path (unLoc exprLStmt) #endif - in mapM_ (\x -> - case x of - (Present _ exprs) -> processExpr con keyFunction path exprs - _ -> pure ()) l -processExpr con keyFunction path (L _ (HsPar _ fun)) = - processExpr con keyFunction path fun -processExpr con keyFunction path (L _ (HsAppType _ fun _)) = processExpr con keyFunction path fun -processExpr con keyFunction path (L _ x@(HsLamCase _ exprLStmt)) = - mapM_ (processMatch con keyFunction path) (fromList $ unLoc $ mg_alts exprLStmt) -processExpr con keyFunction path (L _ x@(HsLam _ exprLStmt)) = - mapM_ (processMatch con keyFunction path) (fromList $ unLoc $ mg_alts exprLStmt) -processExpr con keyFunction path y@(L _ x@(HsLit _ hsLit)) = do - expr <- evaluate $ force $ transformFromNameStableString (Just $ ("$_lit$" <> (T.pack $ showSDocUnsafe $ ppr hsLit)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr hsLit), mempty) - sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) -processExpr con keyFunction path y@(L _ x@(HsOverLit _ overLitVal)) = do - expr <- evaluate $ force $ transformFromNameStableString (Just $ ("$_lit$" <> (T.pack $ showSDocUnsafe $ ppr overLitVal)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr overLitVal), mempty) - sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) -processExpr con keyFunction path (L _ (HsSpliceE exprLStmtL exprLStmtR)) = - let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) - stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmtsL <> stmtsR) -processExpr con keyFunction path (L _ (ArithSeq _ (Just exprLStmtL) exprLStmtR)) = - let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) - stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList $ stmtsL <> stmtsR) -processExpr con keyFunction path y@(L _ x@(HsConLikeOut _ hsType)) = do - expr <- evaluate $ force $ transformFromNameStableString (Just $ ("$_type$" <> (T.pack $ showSDocUnsafe $ ppr hsType)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr hsType), mempty) - sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) -processExpr con keyFunction path y@(L _ x@(HsIPVar _ implicit)) = do - expr <- evaluate $ force $ transformFromNameStableString (Just $ ("$_implicit$" <> T.pack (showSDocUnsafe $ ppr implicit)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr x), mempty) - sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) -processExpr con keyFunction path (L _ (SectionL _ funl funr)) = processExpr con keyFunction path funl <> processExpr con keyFunction path funr + processExpr keyFunction path func + processExpr keyFunction path (L _ (HsMultiIf _ exprLStmt)) = + mapM_ (processGRHS keyFunction path) exprLStmt + processExpr keyFunction path (L _ (HsCase _ funl exprLStmt)) = do + processExpr keyFunction path funl + void $ mapM (processMatch keyFunction path) (unLoc $ mg_alts exprLStmt) + processExpr keyFunction path (L _ (ExplicitSum _ _ _ fun)) = processExpr keyFunction path fun + processExpr keyFunction path (L _ (SectionR _ funl funr)) = processExpr keyFunction path funl <> processExpr keyFunction path funr + processExpr keyFunction path (L _ (HsPar _ fun)) = + processExpr keyFunction path fun + processExpr keyFunction path (L _ (HsAppType _ fun _)) = processExpr keyFunction path fun + processExpr keyFunction path (L _ x@(HsLamCase _ exprLStmt)) = + void $ mapM (processMatch keyFunction path) (unLoc $ mg_alts exprLStmt) + processExpr keyFunction path (L _ x@(HsLam _ exprLStmt)) = + void $ mapM (processMatch keyFunction path) (unLoc $ mg_alts exprLStmt) + processExpr keyFunction path y@(L _ x@(HsLit _ hsLit)) = do + expr <- pure $ transformFromNameStableString (Just $ ("$_lit$" <> (T.pack $ showSDocUnsafe $ ppr hsLit)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr hsLit), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + processExpr keyFunction path y@(L _ x@(HsOverLit _ overLitVal)) = do + expr <- pure $ transformFromNameStableString (Just $ ("$_lit$" <> (T.pack $ showSDocUnsafe $ ppr overLitVal)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr overLitVal), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + processExpr keyFunction path (L _ (HsSpliceE exprLStmtL exprLStmtR)) = + let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) + stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmtsL <> stmtsR) + processExpr keyFunction path y@(L _ x@(HsConLikeOut _ hsType)) = do + expr <- pure $ transformFromNameStableString (Just $ ("$_type$" <> (T.pack $ showSDocUnsafe $ ppr hsType)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr hsType), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + processExpr keyFunction path y@(L _ x@(HsIPVar _ implicit)) = do + expr <- pure $ transformFromNameStableString (Just $ ("$_implicit$" <> T.pack (showSDocUnsafe $ ppr implicit)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr x), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + processExpr keyFunction path (L _ (SectionL _ funl funr)) = do + processExpr keyFunction path funl + processExpr keyFunction path funr #if __GLASGOW_HASKELL__ > 900 -processExpr con keyFunction path y@(L _ (XExpr overLitVal)) = do - processXXExpr con keyFunction path overLitVal -processExpr con keyFunction path y@(L _ x@(HsOverLabel _ fs)) = do - expr <- evaluate $ force $ transformFromNameStableString (Just $ ("$_overLabel$" <> (T.pack $ showSDocUnsafe $ ppr fs)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr x), mempty) - sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) -processExpr con keyFunction path (L _ x) = - let stmts = (x ^? biplateRef :: [LHsExpr GhcTc]) - stmtsNoLoc = (x ^? biplateRef :: [HsExpr GhcTc]) - -- ids = (x ^? biplateRef :: [LIdP GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList (stmts <> (map (wrapXRec @(GhcTc)) stmtsNoLoc))) + processExpr keyFunction path (L _ (HsDo _ smtContext ((L _ stmt)))) = + mapM_ (\(L _ x ) -> extractExprsFromStmtLRHsExpr keyFunction path x) $ stmt + processExpr keyFunction path (L _ (HsGetField _ exprLStmt _)) = + processExpr keyFunction path exprLStmt + processExpr keyFunction path (L _ (ExplicitList _ funList)) = + void $ mapM (processExpr keyFunction path) funList + processExpr keyFunction path (L _ (HsPragE _ _ fun)) = + processExpr keyFunction path fun + processExpr keyFunction path (L _ (HsProc _ lPat fun)) = do + extractExprsFromPat keyFunction path lPat + (extractExprsFromLHsCmdTop keyFunction path fun) + processExpr keyFunction path (L _ (HsIf _ funl funm funr)) = + void $ mapM (processExpr keyFunction path) $ [funl, funm, funr] + processExpr keyFunction path (L _ (ArithSeq hsexpr exprLStmtL exprLStmtR)) = do + processExpr keyFunction path $ wrapXRec @(GhcTc) hsexpr + case exprLStmtL of + Just epr -> processExpr keyFunction path $ wrapXRec @GhcTc $ syn_expr epr + Nothing -> pure () + case exprLStmtR of + From l -> processExpr keyFunction path l + FromThen l r -> do + processExpr keyFunction path l + processExpr keyFunction path r + FromTo l r -> do + processExpr keyFunction path l + processExpr keyFunction path r + FromThenTo l m r -> do + processExpr keyFunction path l + processExpr keyFunction path m + processExpr keyFunction path r + processExpr keyFunction path (L _ (HsRnBracketOut _ exprLStmtL exprLStmtR)) = + let stmtsLNoLoc = (exprLStmtL ^? biplateRef :: [HsExpr GhcTc]) + stmtsRNoLoc = (exprLStmtR ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (map (wrapXRec @(GhcTc)) $ (stmtsLNoLoc <> stmtsRNoLoc)) + processExpr keyFunction path (L _ (HsRecFld _ exprLStmt)) = + let stmts = (exprLStmt ^? biplateRef :: [LHsExpr GhcTc]) + stmtsNoLoc = (exprLStmt ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmts <> (map (wrapXRec @(GhcTc)) stmtsNoLoc)) + processExpr keyFunction path (L _ x@(RecordCon expr (L _ (iD)) rcon_flds)) = + let stmts = (rcon_flds ^? biplateRef :: [LHsExpr GhcTc]) + stmtsNoLoc = (rcon_flds ^? biplateRef :: [HsExpr GhcTc]) + stmtsNoLocexpr = (expr ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmts <> (map (wrapXRec @(GhcTc)) (stmtsNoLoc <> stmtsNoLocexpr))) + processExpr keyFunction path (L _ (RecordUpd _ rupd_expr rupd_flds)) = + let stmts = (rupd_flds ^? biplateRef :: [LHsExpr GhcTc]) + stmtsNoLoc = (rupd_flds ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmts <> (map (wrapXRec @(GhcTc)) stmtsNoLoc)) + processExpr keyFunction path (L _ (ExplicitTuple _ exprLStmt _)) = + let l = (exprLStmt) + in mapM_ (\x -> + case x of + (Present _ exprs) -> processExpr keyFunction path exprs + _ -> pure ()) l + processExpr keyFunction path y@(L _ (XExpr overLitVal)) = do + processXXExpr keyFunction path overLitVal + processExpr keyFunction path y@(L _ x@(HsOverLabel _ fs)) = do + expr <- pure $ transformFromNameStableString (Just $ ("$_overLabel$" <> (T.pack $ showSDocUnsafe $ ppr fs)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr x), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + processExpr keyFunction path (L _ (HsTcBracketOut b mQW exprLStmtL exprLStmtR)) = + let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) + stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmtsL <> stmtsR) + processExpr keyFunction path (L _ x) = + let stmts = (x ^? biplateRef :: [LHsExpr GhcTc]) + stmtsNoLoc = (x ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) ( (stmts <> (map (wrapXRec @(GhcTc)) stmtsNoLoc))) #else -processExpr con keyFunction path y@(L _ (XExpr overLitVal)) = - let stmts = (overLitVal ^? biplateRef :: [LHsExpr GhcTc]) - stmtsNoLoc = (overLitVal ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList (stmts <> (map (noLoc) stmtsNoLoc))) -processExpr con keyFunction path y@(L _ x@(HsOverLabel _ mIdp fs)) = do - print $ showSDocUnsafe $ ppr mIdp - expr <- evaluate $ force $ transformFromNameStableString (Just $ ("$_overLabel$" <> (T.pack $ showSDocUnsafe $ ppr fs)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr x), mempty) - sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) -processExpr con keyFunction path (L _ x) = - let stmts = (x ^? biplateRef :: [LHsExpr GhcTc]) - stmtsNoLoc = (x ^? biplateRef :: [HsExpr GhcTc]) - -- ids = (x ^? biplateRef :: [LIdP GhcTc]) - in mapM_ (processExpr con keyFunction path) (fromList (stmts <> (map (noLoc) stmtsNoLoc))) + processExpr keyFunction path (L _ (ExplicitTuple _ exprLStmt _)) = + let l = (unLoc <$> exprLStmt) + in void $ mapM (\x -> + case x of + (Present _ exprs) -> processExpr keyFunction path exprs + _ -> pure ()) l + processExpr keyFunction path (L _ (ExplicitList _ _ funList)) = + void $ mapM (processExpr keyFunction path) ( funList) + processExpr keyFunction path (L _ (HsTickPragma _ _ _ _ fun)) = + processExpr keyFunction path fun + processExpr keyFunction path (L _ (HsSCC _ _ _ fun)) = + processExpr keyFunction path fun + processExpr keyFunction path (L _ (HsCoreAnn _ _ _ fun)) = + processExpr keyFunction path fun + processExpr keyFunction path (L _ x@(HsWrap _ _ fun)) = + processExpr keyFunction path (noLoc fun) + processExpr keyFunction path (L _ (HsIf _ exprLStmt funl funm funr)) = + mapM_ (processExpr keyFunction path) $ [funl, funm, funr] + processExpr keyFunction path (L _ (HsTcBracketOut b exprLStmtL exprLStmtR)) = + let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) + stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmtsL <> stmtsR) + processExpr keyFunction path (L _ (ArithSeq _ Nothing exprLStmtR)) = + let stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) + stmtsRNoLoc = (exprLStmtR ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmtsR <> ((map noLoc) $ stmtsRNoLoc)) + processExpr keyFunction path (L _ (HsRecFld _ exprLStmt)) = + let stmts = (exprLStmt ^? biplateRef :: [LHsExpr GhcTc]) + stmtsNoLoc = (exprLStmt ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) ( (stmts <> (map noLoc) stmtsNoLoc)) + processExpr keyFunction path (L _ (HsRnBracketOut _ exprLStmtL exprLStmtR)) = + let stmtsL = (exprLStmtL ^? biplateRef :: [LHsExpr GhcTc]) + stmtsR = (exprLStmtR ^? biplateRef :: [LHsExpr GhcTc]) + stmtsLNoLoc = (exprLStmtL ^? biplateRef :: [HsExpr GhcTc]) + stmtsRNoLoc = (exprLStmtR ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmtsL <> stmtsR <> (map noLoc $ (stmtsLNoLoc <> stmtsRNoLoc))) + processExpr keyFunction path (L _ x@(RecordCon expr (L _ (iD)) rcon_flds)) = + let stmts = (rcon_flds ^? biplateRef :: [LHsExpr GhcTc]) + stmtsNoLoc = (rcon_flds ^? biplateRef :: [HsExpr GhcTc]) + stmtsNoLocexpr = (expr ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmts <> (map noLoc) (stmtsNoLoc <> stmtsNoLocexpr)) + processExpr keyFunction path (L _ (RecordUpd _ rupd_expr rupd_flds)) = + let stmts = (rupd_flds ^? biplateRef :: [LHsExpr GhcTc]) + stmtsNoLoc = (rupd_flds ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) (stmts <> (map noLoc) stmtsNoLoc) + processExpr keyFunction path y@(L _ (XExpr overLitVal)) = + let stmts = (overLitVal ^? biplateRef :: [LHsExpr GhcTc]) + stmtsNoLoc = (overLitVal ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) ( (stmts <> (map (noLoc) stmtsNoLoc))) + processExpr keyFunction path y@(L _ x@(HsOverLabel _ mIdp fs)) = do + expr <- evaluate $ force $ transformFromNameStableString (Just $ ("$_overLabel$" <> (T.pack $ showSDocUnsafe $ ppr fs)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr x), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + processExpr keyFunction path (L _ x) = + let stmts = (x ^? biplateRef :: [LHsExpr GhcTc]) + stmtsNoLoc = (x ^? biplateRef :: [HsExpr GhcTc]) + in void $ mapM (processExpr keyFunction path) ( (stmts <> (map (noLoc) stmtsNoLoc))) #endif --- processExpr _ _ _ (L _ (HsBracket _ _)) = pure mempty --- processExpr _ _ _ (L _ (HsProjection _ _)) = pure mempty - #if __GLASGOW_HASKELL__ > 900 -processXXExpr :: WS.Connection -> Text -> Text -> XXExprGhcTc -> IO () -processXXExpr con keyFunction path (WrapExpr (HsWrap hsWrapper hsExpr)) = do - -- print $ (showSDocUnsafe $ ppr $ hsExpr,toConstr $ hsExpr) - processExpr con keyFunction path (wrapXRec @(GhcTc) hsExpr) -processXXExpr con keyFunction path x = - let stmtsL = (x ^? biplateRef :: [HsExpr GhcTc]) - in mapM_ (processExpr con keyFunction path . (wrapXRec @(GhcTc))) (fromList stmtsL) + + extractExprsFromLHsCmdTop :: Text -> Text -> LHsCmdTop GhcTc -> IO () + extractExprsFromLHsCmdTop keyFunction path (L _ cmdTop) = + case cmdTop of + HsCmdTop _ cmd -> extractExprsFromLHsCmd keyFunction path cmd + XCmdTop _ -> pure () + + extractExprsFromLHsCmd :: Text -> Text -> LHsCmd GhcTc -> IO () + extractExprsFromLHsCmd keyFunction path (L _ cmd) = extractExprsFromHsCmd keyFunction path cmd + + extractExprsFromCmdLStmt :: Text -> Text -> CmdLStmt GhcTc -> IO () + extractExprsFromCmdLStmt keyFunction path (L _ stmt) = extractExprsFromStmtLR keyFunction path stmt + + extractExprsFromMatchGroup :: Text -> Text -> MatchGroup GhcTc (LHsCmd GhcTc) -> IO () + extractExprsFromMatchGroup keyFunction path (MG _ (L _ matches) _) = mapM_ (extractExprsFromMatch keyFunction path) matches + + extractExprsFromMatch :: Text -> Text -> LMatch GhcTc (LHsCmd GhcTc) -> IO () + extractExprsFromMatch keyFunction path (L _ (Match _ _ _ grhs)) = extractExprsFromGRHSs keyFunction path grhs + + extractExprsFromGRHSs :: Text -> Text -> GRHSs GhcTc (LHsCmd GhcTc) -> IO () + extractExprsFromGRHSs keyFunction path (GRHSs _ grhss _) = mapM_ (extractExprsFromGRHS keyFunction path) grhss + extractExprsFromGRHSs keyFunction path (XGRHSs _) = pure () + + extractExprsFromGRHS :: Text -> Text -> LGRHS GhcTc (LHsCmd GhcTc) -> IO () + extractExprsFromGRHS keyFunction path (L _ (GRHS _ _ body)) = extractExprsFromLHsCmd keyFunction path body + extractExprsFromGRHS keyFunction path (L _ (XGRHS _)) = pure () + + extractExprsFromStmtLR :: Text -> Text -> StmtLR GhcTc GhcTc (LHsCmd GhcTc) -> IO () + extractExprsFromStmtLR keyFunction path stmt = case stmt of + LastStmt _ body _ retExpr -> do + extractExprsFromLHsCmd keyFunction path body + processSynExpr keyFunction path retExpr + BindStmt _ pat body -> do + extractExprsFromPat keyFunction path pat + extractExprsFromLHsCmd keyFunction path body + ApplicativeStmt _ args mJoin -> do + mapM_ (\(op, arg) -> do + processSynExpr keyFunction path op + extractExprFromApplicativeArg keyFunction path arg) args + case mJoin of + Just m -> processSynExpr keyFunction path m + _ -> pure () + BodyStmt _ body _ guardOp -> do + extractExprsFromLHsCmd keyFunction path body + processSynExpr keyFunction path guardOp + LetStmt _ binds -> + processHsLocalBinds keyFunction path binds + ParStmt _ blocks _ bindOp -> do + mapM_ (extractExprsFromParStmtBlock keyFunction path) blocks + processSynExpr keyFunction path bindOp + TransStmt{..} -> do + mapM_ (extractExprsFromStmtLRHsExpr keyFunction path . unLoc) (trS_stmts) + processExpr keyFunction path trS_using + mapM_ (processExpr keyFunction path) (trS_by) + processSynExpr keyFunction path trS_ret + processSynExpr keyFunction path trS_bind + processExpr keyFunction path (wrapXRec @GhcTc trS_fmap) + RecStmt{..} -> do + mapM_ (extractExprsFromStmtLR keyFunction path . unLoc) (unLoc recS_stmts) + processSynExpr keyFunction path recS_bind_fn + processSynExpr keyFunction path recS_ret_fn + processSynExpr keyFunction path recS_mfix_fn + XStmtLR _ -> pure () + + extractExprsFromParStmtBlock :: Text -> Text -> ParStmtBlock GhcTc GhcTc -> IO () + extractExprsFromParStmtBlock keyFunction path (ParStmtBlock _ stmts _ _) = + mapM_ (extractExprsFromStmtLRHsExpr keyFunction path . unLoc) stmts + + processSynExpr keyFunction path (SyntaxExprTc { syn_expr = expr}) = processExpr keyFunction path (wrapXRec @GhcTc $ expr) + processSynExpr _ _ _ = pure () + + extractExprsFromStmtLRHsExpr :: Text -> Text -> StmtLR GhcTc GhcTc (LHsExpr GhcTc) -> IO () + extractExprsFromStmtLRHsExpr keyFunction path stmt = case stmt of + LastStmt _ body _ retExpr -> do + processExpr keyFunction path body + processSynExpr keyFunction path retExpr + BindStmt _ pat body -> do + extractExprsFromPat keyFunction path pat + processExpr keyFunction path body + ApplicativeStmt _ args mJoin -> do + mapM_ (\(op, arg) -> do + processSynExpr keyFunction path op + extractExprFromApplicativeArg keyFunction path arg) args + case mJoin of + Just m -> processSynExpr keyFunction path m + _ -> pure () + BodyStmt _ body _ guardOp -> do + processExpr keyFunction path body + processSynExpr keyFunction path guardOp + LetStmt _ binds -> + processHsLocalBinds keyFunction path binds + ParStmt _ blocks _ bindOp -> do + mapM_ (extractExprsFromParStmtBlock keyFunction path) blocks + processSynExpr keyFunction path bindOp + TransStmt{..} -> do + mapM_ (extractExprsFromStmtLRHsExpr keyFunction path . unLoc) trS_stmts + processExpr keyFunction path trS_using + mapM_ (processExpr keyFunction path) (trS_by) + processSynExpr keyFunction path trS_ret + processSynExpr keyFunction path trS_bind + processExpr keyFunction path (wrapXRec @GhcTc trS_fmap) + RecStmt{..} -> do + mapM_ (extractExprsFromStmtLRHsExpr keyFunction path . unLoc) (unXRec @GhcTc $ recS_stmts) + processSynExpr keyFunction path recS_bind_fn + processSynExpr keyFunction path recS_ret_fn + processSynExpr keyFunction path recS_mfix_fn + XStmtLR _ -> pure () + + extractExprsFromHsCmd :: Text -> Text -> HsCmd GhcTc -> IO () + extractExprsFromHsCmd keyFunction path cmd = case cmd of + HsCmdArrApp _ f arg _ _ -> + void $ mapM (processExpr keyFunction path) [f, arg] + HsCmdArrForm _ e _ _ cmdTops -> do + mapM_ (extractExprsFromLHsCmdTop keyFunction path) cmdTops + processExpr keyFunction path e + HsCmdApp _ cmd' e -> do + extractExprsFromLHsCmd keyFunction path cmd' + processExpr keyFunction path e + HsCmdLam _ mg -> extractExprsFromMatchGroup keyFunction path mg + HsCmdPar _ cmd' -> + extractExprsFromLHsCmd keyFunction path cmd' + HsCmdCase _ e mg -> do + extractExprsFromMatchGroup keyFunction path mg + processExpr keyFunction path e + HsCmdLamCase _ mg -> + extractExprsFromMatchGroup keyFunction path mg + HsCmdIf _ _ predExpr thenCmd elseCmd -> do + extractExprsFromLHsCmd keyFunction path elseCmd + extractExprsFromLHsCmd keyFunction path thenCmd + processExpr keyFunction path predExpr + HsCmdLet _ binds cmd' -> do + processHsLocalBinds keyFunction path binds + extractExprsFromLHsCmd keyFunction path cmd' + HsCmdDo _ stmts -> + mapM_ (extractExprsFromCmdLStmt keyFunction path )(unLoc stmts) + XCmd _ -> pure () + + extractExprsFromPat :: Text -> Text -> LPat GhcTc -> IO () + extractExprsFromPat keyFunction path y@(L _ pat) = + case pat of + WildPat hsType -> do + expr <- pure $ transformFromNameStableString (Just $ ("$_type$" <> (T.pack $ showSDocUnsafe $ ppr hsType)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr hsType), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + VarPat _ var -> processExpr keyFunction path ((wrapXRec @(GhcTc)) (HsVar noExtField (var))) + LazyPat _ p -> (extractExprsFromPat keyFunction path) p + AsPat _ var p -> do + processExpr keyFunction path ((wrapXRec @(GhcTc)) (HsVar noExtField (var))) + (extractExprsFromPat keyFunction path) p + ParPat _ p -> (extractExprsFromPat keyFunction path) p + BangPat _ p -> (extractExprsFromPat keyFunction path) p + ListPat _ ps -> mapM_ (extractExprsFromPat keyFunction path) ps + TuplePat _ ps _ -> mapM_ (extractExprsFromPat keyFunction path) ps + SumPat hsTypes p _ _ -> do + mapM_ (\hsType -> do + expr <- pure $ transformFromNameStableString (Just $ ("$_type$" <> (T.pack $ showSDocUnsafe $ ppr hsType)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr hsType), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + ) hsTypes + (extractExprsFromPat keyFunction path) p + ConPat {pat_args = args} -> (extractExprsFromHsConPatDetails args) + ViewPat hsType expr p -> do + expr' <- pure $ transformFromNameStableString (Just $ ("$_type$" <> (T.pack $ showSDocUnsafe $ ppr hsType)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr hsType), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr')]) + processExpr keyFunction path expr + (extractExprsFromPat keyFunction path) p + SplicePat _ splice -> mapM_ (processExpr keyFunction path) $ extractExprsFromSplice splice + LitPat _ hsLit -> do + expr <- pure $ transformFromNameStableString (Just $ ("$_lit$" <> (T.pack $ showSDocUnsafe $ ppr hsLit)), (Just $ T.pack $ getLocTC' $ y), (Just $ T.pack $ show $ toConstr hsLit), mempty) + sendTextData' con path (decodeUtf8 $ toStrict $ Data.Aeson.encode $ Object $ HM.fromList [("key", String keyFunction), ("expr", toJSON expr)]) + NPat _ (L _ overLit) _ _ -> do + extractExprsFromOverLit overLit + NPlusKPat _ _ (L _ overLit) _ _ _ -> + extractExprsFromOverLit overLit + SigPat _ p _ -> (extractExprsFromPat keyFunction path) p + XPat _ -> pure () + where + extractExprsFromOverLit :: HsOverLit GhcTc -> IO () + extractExprsFromOverLit (OverLit _ _ e) = processExpr keyFunction path $ wrapXRec @(GhcTc) e + + extractExprsFromHsConPatDetails :: HsConPatDetails GhcTc -> IO () + extractExprsFromHsConPatDetails (PrefixCon _ args) = mapM_ (extractExprsFromPat keyFunction path) args + extractExprsFromHsConPatDetails x@(RecCon (HsRecFields {rec_flds = flds})) = + let stmtsLNoLoc = (x ^? biplateRef :: [HsExpr GhcTc]) + in mapM_ (processExpr keyFunction path) $ map (wrapXRec @(GhcTc)) stmtsLNoLoc + extractExprsFromHsConPatDetails (InfixCon p1 p2) = do + (extractExprsFromPat keyFunction path) p1 + (extractExprsFromPat keyFunction path) p2 + + extractExprFromApplicativeArg :: Text -> Text -> ApplicativeArg GhcTc -> IO () + extractExprFromApplicativeArg keyFunction path (ApplicativeArgOne _ lpat expr _) = do + processExpr keyFunction path expr + extractExprsFromPat keyFunction path lpat + extractExprFromApplicativeArg keyFunction path (ApplicativeArgMany _ exprLStmt stmts lpat _) = do + processExpr keyFunction path (wrapXRec @(GhcTc) stmts) + mapM_ (extractExprsFromStmtLRHsExpr keyFunction path) (map (unLoc) exprLStmt) + extractExprsFromPat keyFunction path lpat + + extractLStmt :: Text -> Text -> GenLocated l (StmtLR GhcTc GhcTc (GenLocated SrcSpanAnnA (HsCmd GhcTc))) -> IO () + extractLStmt keyFunction path (L _ smtlr) = extractExprsFromStmtLR keyFunction path smtlr + + extractExprsFromSplice :: HsSplice GhcTc -> [LHsExpr GhcTc] + extractExprsFromSplice (HsTypedSplice _ _ _ e) = [e] + extractExprsFromSplice (HsUntypedSplice _ _ _ e) = [e] + extractExprsFromSplice (HsQuasiQuote _ _ _ _ _) = [] + extractExprsFromSplice (HsSpliced _ _ _) = [] + + processXXExpr :: Text -> Text -> XXExprGhcTc -> IO () + processXXExpr keyFunction path (WrapExpr (HsWrap hsWrapper hsExpr)) = + processExpr keyFunction path (wrapXRec @(GhcTc) hsExpr) + processXXExpr keyFunction path (ExpansionExpr (HsExpanded _ expansionExpr)) = + mapM_ (processExpr keyFunction path . (wrapXRec @(GhcTc))) [expansionExpr] getLocTC' = (showSDocUnsafe . ppr . la2r . getLoc) getLoc' = (showSDocUnsafe . ppr . la2r . getLoc) diff --git a/fieldInspector/fieldInspector.cabal b/fieldInspector/fieldInspector.cabal index c2623a2..cb20098 100644 --- a/fieldInspector/fieldInspector.cabal +++ b/fieldInspector/fieldInspector.cabal @@ -53,9 +53,16 @@ flag enable-lr-plugins default: False manual: True +flag enable-api-contract-plugins + description: set this flag to disable these plugins api contract + default: True + manual: False + common common-options if flag(enable-lr-plugins) cpp-options: -DENABLE_LR_PLUGINS + if flag(enable-api-contract-plugins) + cpp-options: -DENABLE_API_CONTRACT_PLUGINS ghc-options: -Wall -Wincomplete-uni-patterns @@ -150,37 +157,37 @@ executable fieldInspector -- Base language which the package is written in. default-language: Haskell2010 -test-suite fieldInspector-test - -- Import common warning flags. - import: common-options - ghc-options: -fplugin=ApiContract.Plugin -fplugin=FieldInspector.PluginFields -fplugin-opt FieldInspector.PluginFields:./tmp/fieldInspector/ -fplugin=FieldInspector.PluginTypes -fplugin-opt FieldInspector.PluginTypes:./tmp/fieldInspector/ - -- Base language which the package is written in. - default-language: Haskell2010 - - -- Modules included in this executable, other than Main. - -- other-modules: - - -- LANGUAGE extensions used by modules in this package. - -- other-extensions: - - -- The interface type and version of the test suite. - type: exitcode-stdio-1.0 - - -- Directories containing source files. - hs-source-dirs: test - - -- The entrypoint to the test suite. - main-is: Main.hs - - -- Test dependencies. - build-depends: - base - , fieldInspector - , large-records - , large-generics - , large-anon - , record-dot-preprocessor - , ghc-tcplugin-api - , typelet - , record-hasfield - , scientific +-- test-suite fieldInspector-test +-- -- Import common warning flags. +-- import: common-options +-- ghc-options: -fplugin=ApiContract.Plugin -fplugin=FieldInspector.PluginFields -fplugin-opt FieldInspector.PluginFields:/tmp/fieldInspector/ -fplugin=FieldInspector.PluginTypes -fplugin-opt FieldInspector.PluginTypes:/tmp/fieldInspector/ +-- -- Base language which the package is written in. +-- default-language: Haskell2010 + +-- -- Modules included in this executable, other than Main. +-- -- other-modules: + +-- -- LANGUAGE extensions used by modules in this package. +-- -- other-extensions: + +-- -- The interface type and version of the test suite. +-- type: exitcode-stdio-1.0 + +-- -- Directories containing source files. +-- hs-source-dirs: test + +-- -- The entrypoint to the test suite. +-- main-is: Main.hs + +-- -- Test dependencies. +-- build-depends: +-- base +-- , fieldInspector +-- , large-records +-- , large-generics +-- , large-anon +-- , record-dot-preprocessor +-- , ghc-tcplugin-api +-- , typelet +-- , record-hasfield +-- , scientific diff --git a/fieldInspector/src/FieldInspector/PluginFields.hs b/fieldInspector/src/FieldInspector/PluginFields.hs index 01d5100..195fd8a 100644 --- a/fieldInspector/src/FieldInspector/PluginFields.hs +++ b/fieldInspector/src/FieldInspector/PluginFields.hs @@ -14,8 +14,8 @@ import GHC import GHC.Driver.Plugins (Plugin(..),CommandLineOption,defaultPlugin,PluginRecompile(..)) import GHC as GhcPlugins import GHC.Core.DataCon as GhcPlugins -import GHC.Core.TyCo.Rep import GHC.Core.TyCon as GhcPlugins +import GHC.Core.TyCo.Rep import GHC.Driver.Env import GHC.Tc.Types import GHC.Unit.Module.ModSummary @@ -285,7 +285,9 @@ buildCfgPass opts guts = do createDirectoryIfMissing True ((intercalate "/" . init . splitOn "/") moduleLoc) removeIfExists (moduleLoc Prelude.<> ".fieldUsage.json") l <- toList $ mapM (liftIO . toLBind) (fromList binds) - DBS.writeFile (moduleLoc Prelude.<> ".fieldUsage.json") =<< (evaluate $ toStrict $ encodePretty $ Map.fromList $ groupByFunction $ Prelude.concat l) + res <- pure $ Prelude.filter (\(x,y) -> (Prelude.not $ Prelude.null y) && (Prelude.not $ ("$$" :: Text) `T.isInfixOf` x)) $ groupByFunction $ Prelude.concat l + when (Prelude.not $ Prelude.null res) $ + DBS.writeFile (moduleLoc Prelude.<> ".fieldUsage.json") =<< (evaluate $ toStrict $ encodePretty $ Map.fromList $ res) return guts getAllTypeManipulations :: [LHsBindLR GhcTc GhcTc] -> IO [DataTypeUC] @@ -424,38 +426,38 @@ processHasField functionName (Var x) (Var hasField) = do let y = map (\(zz) -> (pack $ showSDocUnsafe $ ppr zz, pack $ extractVarFromType zz)) z if length y == 4 then - pure $ - res - <> [ - ( functionName - , - [ FieldUsage - (T.strip $ fst $ y Prelude.!! 2) - (T.strip $ fst $ y Prelude.!! 1) - (T.strip $ fst $ y Prelude.!! 3) - (T.strip $ snd $ y Prelude.!! 2) - lensString - ] - ) - ] - else - if length y == 3 - then pure $ res <> [ - ( functionName - , - [ FieldUsage - (T.strip $ fst $ y Prelude.!! 1) - (T.strip $ fst $ y Prelude.!! 0) + ( functionName + , + [ FieldUsage (T.strip $ fst $ y Prelude.!! 2) - (T.strip $ snd $ y Prelude.!! 1) + (T.strip $ fst $ y Prelude.!! 1) + (T.strip $ fst $ y Prelude.!! 3) + (T.strip $ snd $ y Prelude.!! 2) lensString - ] - ) - ] - else do + ] + ) + ] + else + if length y == 3 + then + pure $ + res + <> [ + ( functionName + , + [ FieldUsage + (T.strip $ fst $ y Prelude.!! 1) + (T.strip $ fst $ y Prelude.!! 0) + (T.strip $ fst $ y Prelude.!! 2) + (T.strip $ snd $ y Prelude.!! 1) + lensString + ] + ) + ] + else do pure res #if __GLASGOW_HASKELL__ >= 900 (FunTy _ _ a _) -> do @@ -609,15 +611,26 @@ processHasField functionName x (Var hasField) = do pure res groupByFunction :: [(Text, [FieldUsage])] -> [(Text, [FieldUsage])] -groupByFunction = map mergeGroups . groupBy ((==) `on` fst) . sortBy (compare `on` fst) +groupByFunction = filter' . map mergeGroups . groupBy ((==) `on` fst) . sortBy (compare `on` fst) where mergeGroups :: [(Text, [FieldUsage])] -> (Text, [FieldUsage]) mergeGroups xs = (fst (Prelude.head xs), concatMap snd xs) +primitivePackages = ["aeson","aeson-better-errors","aeson-casing","aeson-diff","aeson-pretty","amazonka","amazonka-kms","async","attoparsec","authenticate-oauth","base","base16","base16-bytestring","base64-bytestring","basement","beam-core","beam-mysql","binary","blaze-builder","byteable","bytestring","case-insensitive","cassava","cipher-aes","containers","country","cryptohash","cryptonite","cryptonite-openssl","cryptostore","currency-codes","data-default","digest","dns","double-conversion","errors","extra","generic-arbitrary","generic-random","hedis","HsOpenSSL","HTTP","http-api-data","http-client","http-client-tls","http-media","http-types","iso8601-time","jose-jwt","json","jwt","lucid","memory","mtl","mysql","neat-interpolation","network-uri","newtype-generics","optics-core","QuickCheck","quickcheck-text","random","record-dot-preprocessor","record-hasfield","reflection","regex-compat","regex-pcre","regex-pcre","relude","resource-pool","RSA","safe","safe-exceptions","scientific","sequelize","servant","servant-client","servant-client-core","servant-server","split","streamly-core","streamly-serialize-instances","string-conversions","tagsoup","text","time","transformers","unix","unix-time","unordered-containers","utf8-string","uuid","vector","wai","wai-extra","warp","x509","x509-store","xeno","xml-conduit","xmlbf","xmlbf-xeno","ghc-prim","reflection","time","base","servant-client-core","reflection","servant-server","http-types","containers","unordered-containers"] + +filter' :: [(Text, [FieldUsage])] -> [(Text, [FieldUsage])] +filter' [] = [] +filter' li = + map (\(funName,fieldsList) -> + (funName,Prelude.filter (\(FieldUsage typeName fieldName fieldType typeSrcLoc beautifiedCode) -> + (Prelude.not (Prelude.any (\x -> x `T.isInfixOf` typeSrcLoc) primitivePackages) && (typeName /= fieldName) && (fieldName /= fieldType)) + ) fieldsList) + ) li + toLBind :: CoreBind -> IO [(Text, [FieldUsage])] toLBind (NonRec binder expr) = do res <- toLexpr (pack $ nameStableString $ idName binder) expr - pure $ groupByFunction res + pure $ filter' $ groupByFunction res toLBind (Rec binds) = do r <- toList $ @@ -626,7 +639,7 @@ toLBind (Rec binds) = do toLexpr (pack $ nameStableString (idName b)) e ) (fromList binds) - pure $ groupByFunction $ Prelude.concat r + pure $ filter' $ groupByFunction $ Prelude.concat r processFieldExtraction :: Text -> Var -> Var -> Text -> IO [(Text, [FieldUsage])] processFieldExtraction functionName _field _type b = do diff --git a/fieldInspector/src/FieldInspector/PluginTypes.hs b/fieldInspector/src/FieldInspector/PluginTypes.hs index 084f785..61e6d8c 100644 --- a/fieldInspector/src/FieldInspector/PluginTypes.hs +++ b/fieldInspector/src/FieldInspector/PluginTypes.hs @@ -122,7 +122,9 @@ plugin = (defaultPlugin{ pluginRecompile = (\_ -> return NoForceRecompile) , parsedResultAction = collectTypeInfoParser }) +#if defined(ENABLE_API_CONTRACT_PLUGINS) <> ApiContract.plugin +#endif #if defined(ENABLE_LR_PLUGINS) <> DRP.plugin <> DRAP.plugin diff --git a/flake.lock b/flake.lock index a7b5b0b..84c2732 100644 --- a/flake.lock +++ b/flake.lock @@ -393,17 +393,17 @@ "systems": "systems_3" }, "locked": { - "lastModified": 1721562622, - "narHash": "sha256-4XivoIvlVl7UyVCyZneeLIvyKBbRIvDEOEnJBxnZp+c=", + "lastModified": 1728704762, + "narHash": "sha256-Y3Zs8nGfFQLqpkRt8l2M+wkrp8fHkblu4/qMRP1ryu8=", "owner": "eswar2001", "repo": "large-records", - "rev": "b60bcb312c7d55f1d638aa1a5143696e6586e76d", + "rev": "f7fbffd022c792cd7686bca058a51b08722091dc", "type": "github" }, "original": { "owner": "eswar2001", + "ref": "ghc928-qualified-prelude", "repo": "large-records", - "rev": "b60bcb312c7d55f1d638aa1a5143696e6586e76d", "type": "github" } }, diff --git a/flake.nix b/flake.nix index 7495fa1..ae43376 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,7 @@ references.url = "github:eswar2001/references/120ae7826a7af01a527817952ad0c3f5ef08efd0"; beam.url = "github:juspay/beam/c4f86057db76640245c3d1fde040176c53e9b9a3"; beam.flake = false; - large-records.url = "github:eswar2001/large-records/b60bcb312c7d55f1d638aa1a5143696e6586e76d"; + large-records.url = "github:eswar2001/large-records/ghc928-qualified-prelude"; large-records.inputs.beam.follows = "beam"; # ghc 8.10.7 packages @@ -98,6 +98,8 @@ streamly-core.source = inputs.streamly + /core; }; settings = { + servant.jailbreak = true; + servant-server.jailbreak = true; # aeson = { # check = false; # }; @@ -110,6 +112,7 @@ # jailbreak = true; # }; sheriff.check = false; + http2.check = false; }; devShell = {