diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/HaskellCodeExplorer/AST/RenamedSource.hs | 498 | ||||
| -rw-r--r-- | src/HaskellCodeExplorer/AST/TypecheckedSource.hs | 1231 | ||||
| -rw-r--r-- | src/HaskellCodeExplorer/GhcUtils.hs | 1122 | ||||
| -rw-r--r-- | src/HaskellCodeExplorer/ModuleInfo.hs | 811 | ||||
| -rw-r--r-- | src/HaskellCodeExplorer/PackageInfo.hs | 595 | ||||
| -rw-r--r-- | src/HaskellCodeExplorer/Preprocessor.hs | 159 | ||||
| -rw-r--r-- | src/HaskellCodeExplorer/Types.hs | 880 | 
7 files changed, 5296 insertions, 0 deletions
diff --git a/src/HaskellCodeExplorer/AST/RenamedSource.hs b/src/HaskellCodeExplorer/AST/RenamedSource.hs new file mode 100644 index 0000000..c1bf463 --- /dev/null +++ b/src/HaskellCodeExplorer/AST/RenamedSource.hs @@ -0,0 +1,498 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE StrictData #-} + +module HaskellCodeExplorer.AST.RenamedSource +  ( NameOccurrence(..) +  , namesFromRenamedSource +  ) where + +import BasicTypes (TupleSort(..)) +import BooleanFormula (BooleanFormula(..)) +import Data.Generics (Data, everything, extQ, mkQ) +import Data.Maybe (Maybe(..), mapMaybe) +import qualified Data.Text as T (Text) +import GHC +  ( AmbiguousFieldOcc(..) +  , ConDecl(..) +  , ConDeclField(..) +  , DataFamInstDecl(..) +  , FamilyDecl(..) +  , FieldOcc(..) +  , FixitySig(..) +  , ForeignDecl(..) +  , GenLocated(..) +  , HsBindLR(..) +  , HsExpr(..) +  , HsPatSynDetails(..) +  , HsRecField'(..) +  , HsTupleSort(..) +  , HsTyLit(..) +  , HsTyPats +  , HsTyVarBndr(..) +  , HsType(..) +  , IE(..) +  , LHsBindLR +  , LHsExpr +  , LHsQTyVars(..) +  , LHsType +  , LPat +  , LSig +  , LTyClDecl +  , Located +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +  , HsMatchContext(..) +  , Match(..) +#else +  , m_fixity +  , MatchFixity(..) +#endif +  , MatchGroup(..) +  , Name +  , Pat(..) +  , PatSynBind(..) +  , Sig(..) +  , TyClDecl(..) +  , TyFamEqn(..) +  , Type +  , unLoc +  ) +import HaskellCodeExplorer.GhcUtils (hsPatSynDetails, ieLocNames) +import Prelude hiding (span) +import TysWiredIn +  ( nilDataConName   +  , tupleTyConName +  , typeNatKind +  , typeSymbolKind +  ) +data NameOccurrence +  = NameOccurrence { locatedName :: Located (Maybe Name) +                   , description :: T.Text +                   , isBinder :: Bool } +  | TyLitOccurrence { locatedName :: Located (Maybe Name) +                    , description :: T.Text +                    , kind :: Type } + +-- | Here we are only interested in a small subset of all AST nodes, so it is +-- convenient to use generic functions +namesFromRenamedSource :: (Data a) => a -> [NameOccurrence] +namesFromRenamedSource = +  everything +    (++) +    ([] `mkQ` hsExprNames `extQ` matchGroupNames `extQ` bindNames `extQ` +     patNames `extQ` +     sigNames `extQ` +     hsTypeNames `extQ` +     tyClDeclNames `extQ` +     familyDeclNames `extQ` +     tyFamilyEqNames `extQ` +     tyFamilyDefEqNames `extQ` +     dataFamInstDeclNames `extQ` +     conDeclNames `extQ` +     importNames `extQ` +     hsTyVarBndrNames `extQ` +     hsPatSynDetailsNames `extQ` +     conDeclFieldNames `extQ` +     hsRecFieldExprNames `extQ` +     hsRecAmbFieldExprNames `extQ` +     hsRecFieldPatNames `extQ` +     foreignDeclNames) + +fieldOccName :: Bool -> FieldOcc Name -> NameOccurrence +fieldOccName isBinder (FieldOcc (L span _) name) = +  NameOccurrence +    { locatedName = L span (Just name) +    , description = "FieldOcc" +    , isBinder = isBinder +    } + +conDeclFieldNames :: ConDeclField Name -> [NameOccurrence] +conDeclFieldNames ConDeclField {..} = +  map (fieldOccName True . unLoc) cd_fld_names + +hsRecFieldExprNames :: +     HsRecField' (FieldOcc Name) (LHsExpr Name) -> [NameOccurrence] +hsRecFieldExprNames HsRecField {..} = [fieldOccName False $ unLoc hsRecFieldLbl] + +hsRecAmbFieldExprNames :: +     HsRecField' (AmbiguousFieldOcc Name) (LHsExpr Name) -> [NameOccurrence] +hsRecAmbFieldExprNames HsRecField {..} = +  let (L span recField) = hsRecFieldLbl +      mbName = +        case recField of +          Ambiguous _ _ -> Nothing +          Unambiguous _ name -> Just name +   in [ NameOccurrence +          { locatedName = L span mbName +          , description = "AmbiguousFieldOcc" +          , isBinder = False +          } +      ] + +hsRecFieldPatNames :: +     HsRecField' (FieldOcc Name) (LPat Name) -> [NameOccurrence] +hsRecFieldPatNames HsRecField {..} = [fieldOccName False $ unLoc hsRecFieldLbl] + +hsExprNames :: LHsExpr Name -> [NameOccurrence] +hsExprNames (L _span (HsVar name)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "HsVar" +    , isBinder = False +    } +  ] +hsExprNames (L span (ExplicitList _ _ exprs)) +  | null exprs = +    [ NameOccurrence +      { locatedName = L span $ Just nilDataConName +      , description = "ExplicitList" +      , isBinder = False +      } +    ] +  | otherwise = [] +hsExprNames (L _span (RecordCon name _conLike _instFun _binds)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "RecordCon" +    , isBinder = False +    } +  ] +hsExprNames (L _span (HsRecFld (Unambiguous (L span _) name))) = +  [ NameOccurrence +    { locatedName = L span (Just name) +    , description = "HsRecFld" +    , isBinder = False +    } +  ] +hsExprNames (L _span (HsRecFld (Ambiguous (L span _) _name))) = +  [ NameOccurrence +    { locatedName = L span Nothing +    , description = "HsRecFld" +    , isBinder = False +    } +  ] +hsExprNames _ = [] + +matchGroupNames :: MatchGroup Name (LHsExpr Name) -> [NameOccurrence] +matchGroupNames = +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)       +  mapMaybe (fmap toNameOcc . matchContextName . m_ctxt . unLoc) . +#else +  mapMaybe (fmap toNameOcc . matchFixityName . m_fixity . unLoc) . +#endif +  unLoc . mg_alts +  where +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)     +    matchContextName :: HsMatchContext Name -> Maybe (Located Name) +    matchContextName (FunRhs name _ _bool) = Just name +    matchContextName _ = Nothing    +#else +    matchFixityName :: MatchFixity Name -> Maybe (Located Name) +    matchFixityName NonFunBindMatch = Nothing +    matchFixityName (FunBindMatch name _bool) = Just name +#endif +    toNameOcc :: Located Name -> NameOccurrence +    toNameOcc n = +      NameOccurrence +        {locatedName = Just <$> n, description = "Match", isBinder = True} + +bindNames :: LHsBindLR Name Name -> [NameOccurrence] +bindNames (L _span (PatSynBind PSB {..})) = +  [ NameOccurrence +      { locatedName = Just <$> psb_id +      , description = "PatSynBind" +      , isBinder = True +      } +  ] +bindNames _ = [] + +hsPatSynDetailsNames :: HsPatSynDetails (Located Name) -> [NameOccurrence] +hsPatSynDetailsNames = +  map +    (\name -> +       NameOccurrence +         { locatedName = Just <$> name +         , description = "HsPatSynDetails" +         , isBinder = True +         }) . +  hsPatSynDetails + +importNames :: IE Name -> [NameOccurrence] +importNames = +  map +    (\name -> +        NameOccurrence +        { locatedName = Just <$> name +        , description = "IE" +        , isBinder = False +        }) . +  ieLocNames + +patNames :: LPat Name -> [NameOccurrence] +patNames (L _span (VarPat name)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "VarPat" +    , isBinder = True +    } +  ] +patNames (L _span (ConPatIn name _)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "ConPatIn" +    , isBinder = False +    } +  ] +patNames (L _span (AsPat name _)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "AsPat" +    , isBinder = True +    } +  ] +patNames (L _span (NPlusKPat name _ _ _ _ _)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "NPlusKPat" +    , isBinder = True +    } +  ] +patNames _ = [] + +sigNames :: LSig Name -> [NameOccurrence] +sigNames (L _span (TypeSig names _)) = +  map +    (\n -> +        NameOccurrence +        { locatedName = Just <$> n +        , description = "TypeSig" +        , isBinder = False +        }) +    names +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)     +sigNames (L _span (PatSynSig names _)) = +  map (\name -> NameOccurrence (Just <$> name) "PatSynSig" False) names +#else +sigNames (L _span (PatSynSig name _)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "PatSynSig" +    , isBinder = False +    } +  ] +#endif +sigNames (L _span (ClassOpSig _ names _)) = +  map +    (\n -> +        NameOccurrence +        { locatedName = Just <$> n +        , description = "ClassOpSig" +        , isBinder = True +        }) +    names +sigNames (L _span (FixSig (FixitySig names _))) = +  map +    (\n -> +        NameOccurrence +        { locatedName = Just <$> n +        , description = "FixitySig" +        , isBinder = False +        }) +    names +sigNames (L _span (InlineSig name _)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "InlineSig" +    , isBinder = False +    } +  ] +sigNames (L _span (SpecSig name _ _)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "SpecSig" +    , isBinder = False +    } +  ] +sigNames (L _span (MinimalSig _ (L _ boolFormula))) = +  map +    (\n -> +        NameOccurrence +        { locatedName = Just <$> n +        , description = "MinimalSig" +        , isBinder = False +        }) . +  boolFormulaNames $ +  boolFormula +  where +    boolFormulaNames :: BooleanFormula name -> [name] +    boolFormulaNames (Var a) = [a] +    boolFormulaNames (And fs) = concatMap (boolFormulaNames . unLoc) fs +    boolFormulaNames (Or fs) = concatMap (boolFormulaNames . unLoc) fs +    boolFormulaNames (Parens (L _ f)) = boolFormulaNames f +sigNames (L _ _) = [] + +hsTypeNames :: LHsType Name -> [NameOccurrence] +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +hsTypeNames (L _span (HsTyVar _promoted name)) = +#else +hsTypeNames (L _span (HsTyVar name)) = +#endif +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "HsTyVar" +    , isBinder = False +    } +  ] +hsTypeNames (L span (HsTyLit lit)) = +  let kind = +        case lit of +          HsNumTy _ _ -> typeNatKind +          HsStrTy _ _ -> typeSymbolKind +  in [ TyLitOccurrence +       { locatedName = L span Nothing +       , description = "HsTyLit" +       , kind = kind +       } +     ] +hsTypeNames (L _span (HsOpTy _ name _)) = +  [ NameOccurrence +    { locatedName = Just <$> name +    , description = "HsOpTy" +    , isBinder = False +    } +  ] +hsTypeNames (L span (HsTupleTy tupleSort types)) +  | null types = +    let sort = +          case tupleSort of +            HsUnboxedTuple -> UnboxedTuple +            HsBoxedTuple -> BoxedTuple +            HsConstraintTuple -> ConstraintTuple +            HsBoxedOrConstraintTuple -> BoxedTuple +    in [ NameOccurrence +         { locatedName = L span (Just $ tupleTyConName sort 0) +         , description = "HsTupleTy" +         , isBinder = False +         } +       ] +  | otherwise = [] +--https://ghc.haskell.org/trac/ghc/ticket/13737     +--hsTypeNames (L span (HsExplicitListTy _kind types)) = ... +--hsTypeNames (L span (HsExplicitTupleTy _kind types)) = ... +hsTypeNames _ = [] + +hsTyVarBndrNames :: HsTyVarBndr Name -> [NameOccurrence] +hsTyVarBndrNames (UserTyVar n) = +  [ NameOccurrence +    { locatedName = Just <$> n +    , description = "UserTyVar" +    , isBinder = True +    } +  ] +hsTyVarBndrNames (KindedTyVar n _) = +  [ NameOccurrence +    { locatedName = Just <$> n +    , description = "KindedTyVar" +    , isBinder = True +    } +  ] + +tyClDeclNames :: LTyClDecl Name -> [NameOccurrence] +tyClDeclNames (L _span DataDecl {..}) = +  [ NameOccurrence +    { locatedName = Just <$> tcdLName +    , description = "DataDecl" +    , isBinder = True +    } +  ] +tyClDeclNames (L _span SynDecl {..}) = +  [ NameOccurrence +    { locatedName = Just <$> tcdLName +    , description = "SynDecl" +    , isBinder = True +    } +  ] +tyClDeclNames (L _span ClassDecl {..}) = +  NameOccurrence +  { locatedName = Just <$> tcdLName +  , description = "ClassDecl" +  , isBinder = True +  } : +  concatMap +    ((\(names1, names2) -> map toNameOcc names1 ++ map toNameOcc names2) . unLoc) +    tcdFDs +  where +    toNameOcc :: Located Name -> NameOccurrence +    toNameOcc n = +      NameOccurrence +      { locatedName = Just <$> n +      , description = "FunDep" +      , isBinder = False +      } +tyClDeclNames _ = [] + +familyDeclNames :: FamilyDecl Name -> [NameOccurrence] +familyDeclNames FamilyDecl {..} = +  [ NameOccurrence +    { locatedName = Just <$> fdLName +    , description = "FamilyDecl" +    , isBinder = True +    } +  ] + +tyFamilyEqNames :: TyFamEqn Name (HsTyPats Name) -> [NameOccurrence] +tyFamilyEqNames TyFamEqn {tfe_tycon = tyCon} = +  [ NameOccurrence +    { locatedName = Just <$> tyCon +    , description = "TyFamEqn" +    , isBinder = False +    } +  ] + +tyFamilyDefEqNames :: TyFamEqn Name (LHsQTyVars Name) -> [NameOccurrence] +tyFamilyDefEqNames TyFamEqn {tfe_tycon = tyCon} = +  [ NameOccurrence +    { locatedName = Just <$> tyCon +    , description = "TyFamEqn" +    , isBinder = False +    } +  ] + +dataFamInstDeclNames :: DataFamInstDecl Name -> [NameOccurrence] +dataFamInstDeclNames DataFamInstDecl {dfid_tycon = tyCon} = +  [ NameOccurrence +    { locatedName = Just <$> tyCon +    , description = "DataFamInstDecl" +    , isBinder = False +    } +  ] + +conDeclNames :: ConDecl Name -> [NameOccurrence] +conDeclNames con = +  case con of +    ConDeclGADT {con_names = names} -> +      map +        (\n -> +            NameOccurrence +            { locatedName = Just <$> n +            , description = "ConDeclGADT" +            , isBinder = True +            }) +        names +    ConDeclH98 {con_name = name} -> +      [ NameOccurrence +        { locatedName = Just <$> name +        , description = "ConDeclH98" +        , isBinder = True +        } +      ] + +foreignDeclNames :: ForeignDecl Name -> [NameOccurrence] +foreignDeclNames decl = +  [ NameOccurrence +    { locatedName = Just <$> fd_name decl +    , description = "ForeignDecl" +    , isBinder = True +    } +  ] diff --git a/src/HaskellCodeExplorer/AST/TypecheckedSource.hs b/src/HaskellCodeExplorer/AST/TypecheckedSource.hs new file mode 100644 index 0000000..f97c33b --- /dev/null +++ b/src/HaskellCodeExplorer/AST/TypecheckedSource.hs @@ -0,0 +1,1231 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE StrictData #-} + +module HaskellCodeExplorer.AST.TypecheckedSource +  ( ASTState(..) +  , Environment(..) +  , TypeError(..) +  , foldTypecheckedSource +  , mkIdentifierInfo +  , mkIdentifierOccurrence +  , mkType +  , removeOverlappingInterval +  ) where + +import Bag (bagToList) +import BasicTypes (Origin(..)) +import Class (Class, classTyVars) +import ConLike (ConLike(..),conLikeWrapId_maybe) +import Control.Monad (return, unless, void) +import Control.Monad.State.Strict (State, get, modify') +import qualified Data.HashMap.Strict as HM +import qualified Data.IntMap.Strict as IM +import qualified Data.IntervalMap.Strict as IVM +import qualified Data.Map.Strict as M +import Data.Maybe (Maybe, fromMaybe, mapMaybe) +import qualified Data.Set as S +import qualified Data.Text as T +import DataCon (dataConWorkId) +import DynFlags (DynFlags) +import FastString (mkFastString) +import HaskellCodeExplorer.GhcUtils +import qualified HaskellCodeExplorer.Types as HCE +import HsBinds (HsPatSynDetails(..), RecordPatSynField(..)) +import HsSyn +  ( ABExport(..) +  , ApplicativeArg(..) +  , ArithSeqInfo(..) +  , FieldOcc(..) +  , GRHS(..) +  , GRHSs(..) +  , HsBindLR(..) +  , HsCmd(..) +  , HsCmdTop(..) +  , HsConDetails(..) +  , HsConPatDetails +  , HsExpr(..) +  , HsLocalBindsLR(..) +  , HsOverLit(..) +  , HsRecField'(..) +  , HsRecFields(..) +  , HsTupArg(..) +  , HsValBindsLR(..) +  , HsValBindsLR(..) +  , LGRHS +  , LHsBindLR +  , LHsBinds +  , LHsCmd +  , LHsCmd +  , LHsCmdTop +  , LHsExpr +  , LHsRecField +  , LHsRecUpdField +  , LHsTupArg +  , LMatch +  , LPat +  , LStmtLR +  , Match(..) +  , Match(..) +  , MatchGroup(..) +  , ParStmtBlock(..) +  , Pat(..) +  , PatSynBind(..) +  , StmtLR(..) +  , selectorAmbiguousFieldOcc +  ) +import HscTypes (TypeEnv, lookupTypeEnv) +import Id (idType) +import IdInfo (IdDetails(..)) +import InstEnv +  ( ClsInst(..) +  , InstEnvs +  , instanceSig +  , is_dfun +  , lookupUniqueInstEnv +  ) +import Name (Name, nameOccName, nameUnique) +import Prelude hiding (span) +import SrcLoc (GenLocated(..), SrcSpan(..), isGoodSrcSpan, isOneLineSpan, unLoc) +import TcEvidence (HsWrapper(..)) +import TcHsSyn (conLikeResTy, hsLitType) +import Type +  ( TyThing(..) +  , Type +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +  , nonDetCmpTypes +#else +  , cmpTypes   +#endif +  , eqTypes +  , eqType +  , getClassPredTys_maybe +  , mkFunTy +  , mkFunTys +  , splitForAllTys +  , splitFunTy_maybe +  , splitFunTys +  , substTys +  , tidyOpenType +  , zipTvSubst +  ) +import TysWiredIn (mkListTy, mkTupleTy) +import Unique (getKey) +import Var (Id, Var, idDetails, isId, setVarName, setVarType, varName, varType) +import VarEnv (TidyEnv) + +data ASTState = ASTState +  { astStateExprInfoMap :: !HCE.ExpressionInfoMap +  -- ^ Type of each expression +  , astStateIdOccMap :: !HCE.IdentifierOccurrenceMap +  -- ^ Each occurrence of an identifier in a source code +  , astStateIdSrcSpanMap :: !(M.Map SrcSpan (Var, Maybe (Type, [Type]))) +  -- ^ Intermediate data structure that is used to populate 'IdentifierOccurrenceMap' +  -- and 'IdentifierInfoMap'. +  -- 'SrcSpan' - location of an identifier in a source code +  -- 'Type' - 'expected' type of an identifier +  -- '[Type]' - types at which type variables are instantiated +  , astStateTidyEnv :: !TidyEnv +  -- ^ 'TidyEnv' is used to prevent name clashes of free type variables. +  -- ('TidyEnv' contains all free type variables in scope) +  , astStateHsWrapper :: !(Maybe HsWrapper) +  -- ^ HsWrapper comes from 'HsWrap' constructor of 'HsExpr' datatype. +  , astStateEnv :: !Environment +  -- ^ 'Environment' doesn't change +  , astStateTypeErrors :: [TypeError] +  -- ^ Non-empty list of TypeError's indicates that most likely there is a bug in +  -- a fold_something function in this module. +  } + +-- | A 'TypeError' means that an assumption about a type of an AST node is incorrect. +data TypeError = TypeError +  { typeErrorSrcSpan :: SrcSpan +  , typeErrorMessage :: T.Text +  , typeErrorASTNodeName :: T.Text +  } deriving (Show, Eq) + +data Environment = Environment +  { envDynFlags :: DynFlags +  , envTypeEnv :: TypeEnv +  , envInstEnv :: InstEnvs +  , envTransformation :: HCE.SourceCodeTransformation +  , envPackageId :: HCE.PackageId +  , envCurrentModuleDefSites :: HCE.DefinitionSiteMap +  , envFileMap :: HM.HashMap HCE.HaskellFilePath HCE.HaskellModulePath +  , envDefSiteMap :: HM.HashMap HCE.HaskellModulePath HCE.DefinitionSiteMap +  , envModuleNameMap :: HM.HashMap HCE.HaskellModuleName (HM.HashMap HCE.ComponentId HCE.HaskellModulePath) +  , envExportedNames :: S.Set Name +  , envComponentId :: HCE.ComponentId +  } + +-- | Indicates whether an expression consists of more than one token. +-- Simple expression : wildcard, literal +-- Composite expressin : applcation, lambda abstraction,... +data ExprSort +  = Simple +  | Composite +  deriving (Show, Eq) + +exprSort :: HsExpr a -> ExprSort +exprSort (HsVar _) = Simple +exprSort (HsIPVar _) = Simple +exprSort (HsOverLit _) = Simple +exprSort (HsLit _) = Simple +exprSort (ExplicitTuple args _) +  | null args = Simple +  | otherwise = Composite    +exprSort (ExplicitList _ _ args)  +  | null args = Simple +  | otherwise = Composite      +exprSort _ = Composite + +patSort :: Pat a -> ExprSort +patSort (WildPat _typ) = Simple +patSort (LitPat _lit) = Simple +patSort NPat {} = Simple +patSort (ListPat pats _ _)  +  | null pats = Simple +  | otherwise = Composite        +patSort (TuplePat pats _ _) +  | null pats = Simple +  | otherwise = Composite +patSort _ = Composite + +-- | Splits a type of a function, adds 'TypeError' to 'ASTState' +-- in case of failure. +splitFunTySafe :: +     SrcSpan -> T.Text -> Type -> State ASTState (Maybe (Type, Type)) +splitFunTySafe srcSpan astNode typ = +  case splitFunTy_maybe typ of +    Just (ty1, ty2) -> return $ Just (ty1, ty2) +    Nothing -> do +      flags <- envDynFlags . astStateEnv <$> get +      let typeError = +            TypeError +              { typeErrorSrcSpan = srcSpan +              , typeErrorMessage = T.append "splitFunTy : " $ toText flags typ +              , typeErrorASTNodeName = astNode +              } +      modify' +        (\st -> st {astStateTypeErrors = typeError : astStateTypeErrors st}) +      return Nothing +       +-- | Splits a type of a function of two arguments, adds +-- 'TypeError' to 'ASTState' in case of a failure. +splitFunTy2Safe :: +     SrcSpan -> T.Text -> Type -> State ASTState (Maybe (Type, Type, Type)) +splitFunTy2Safe srcSpan astNode typ = do +  tys <- splitFunTySafe srcSpan astNode typ +  case tys of +    Just (arg1, ty1) -> do +      res <- splitFunTySafe srcSpan astNode ty1 +      case res of +        Just (arg2, ty2) -> return $ Just (arg1, arg2, ty2) +        Nothing -> return Nothing +    Nothing -> return Nothing +  +-- | Returns result type of a function, adds 'TypeError' to +-- 'ASTState' in case of a failure. +funResultTySafe :: SrcSpan -> T.Text -> Type -> State ASTState (Maybe Type) +funResultTySafe srcSpan astNode typ = +  fmap snd <$> splitFunTySafe srcSpan astNode typ + +-- | Returns result type of a function of two arguments, +-- adds 'TypeError' to 'ASTState' in case of a failure. +funResultTy2Safe :: SrcSpan -> T.Text -> Type -> State ASTState (Maybe Type) +funResultTy2Safe srcSpan astNode typ = do +  mbResTy1 <- funResultTySafe srcSpan astNode typ +  case mbResTy1 of +    Just resTy1 -> funResultTySafe srcSpan astNode resTy1 +    Nothing -> return Nothing + +addIdentifierToIdSrcSpanMap :: +     SrcSpan -> Id -> Maybe (Type, [Type]) -> State ASTState () +addIdentifierToIdSrcSpanMap span identifier mbTypes +  | isGoodSrcSpan span = +    modify' $ \astState@ASTState {astStateIdSrcSpanMap = ids} -> +      let ids' = M.insert span (identifier, mbTypes) ids +       in astState {astStateIdSrcSpanMap = ids'} +addIdentifierToIdSrcSpanMap _ _ _ = return () + +-- | Updates 'ExpressionInfoMap' or 'IdentifierOccurrenceMap' depending +-- on 'ExprSort'. +addExprInfo :: SrcSpan -> Maybe Type -> T.Text -> ExprSort -> State ASTState () +addExprInfo span mbType descr sort = do +  transformation <- envTransformation . astStateEnv <$> get +  case srcSpanToLineAndColNumbers transformation span of +    Just (_file,(startLine, startCol), (endLine, endCol)) -> do +      flags <- envDynFlags . astStateEnv <$> get +      mbHsWrapper <- astStateHsWrapper <$> get +      modify' $ \astState@ASTState {astStateExprInfoMap = exprInfoMap} -> +        case sort of +          Composite -> +            let exprInfo = +                  HCE.ExpressionInfo +                    {exprType = mkType flags <$> mbType, description = descr} +                interval = +                  IVM.OpenInterval (startLine, startCol) (endLine, endCol) +                exprInfoMap' = IVM.insert interval exprInfo exprInfoMap +             in astState {astStateExprInfoMap = exprInfoMap'} +          Simple -> +            let idOcc = +                  HCE.IdentifierOccurrence +                    { internalId = Nothing +                    , internalIdFromRenamedSource = Nothing +                    , isBinder = False +                    , instanceResolution = Nothing +                    , idOccType = +                        case mbHsWrapper of                           +                          Just w -> mkType flags <$> (applyWrapper w <$> mbType) +                          Nothing -> mkType flags <$> mbType +                    , typeArguments = Nothing +                    , description = descr +                    , sort = HCE.ValueId +                    } +                idOccMap = +                  IM.insertWith +                    removeOverlappingInterval +                    startLine +                    [((startCol, endCol), idOcc)] +                    (astStateIdOccMap astState) +             in astState {astStateIdOccMap = idOccMap} +    Nothing -> return () + +-- | Finds the first interval that overlaps with a new interval +-- and adds the smaller one of the two to the list. If there are no overlapping +-- intervals then this function adds a new interval to the list. +removeOverlappingInterval :: +     forall a. [((Int, Int), a)] -> [((Int, Int), a)] -> [((Int, Int), a)] +removeOverlappingInterval [newInterval@((newStart, newEnd), _newVal)] intervals = +  go intervals False +  where +    go :: +         [((Int, Int), a)] +      -> Bool -- If an overlapping interval is found +      -> [((Int, Int), a)] +    go (i:is) True = i : go is True +    -- Current interval is inside new interval +    go (interval@((s, e), _val):is) False +      | newStart <= s && newEnd >= e = interval : go is True +    -- New interval is inside current interval +    go (((s, e), _val):is) False +      | newStart >= s && newEnd <= e = newInterval : go is True +    -- Intervals partially overlap +    go (interval@((s, e), _val):is) False +      | newStart >= s && newEnd >= e && newStart < e = +        (if e - s >= newEnd - newStart +           then newInterval +           else interval) : +        go is True +    -- Intervals partially overlap +    go (interval@((s, e), _val):is) False +      | newStart <= s && newEnd <= e && newEnd > s = +        (if e - s >= newEnd - newStart +           then newInterval +           else interval) : +        go is True +    -- Intervals don't overlap +    go (interval:is) False = interval : go is False +    go [] True = [] +    go [] False = [newInterval] +removeOverlappingInterval _ intervals = intervals + +newtype InstTypes = InstTypes [Type] + +instance Eq InstTypes where +  (==) (InstTypes ts1) (InstTypes ts2) = eqTypes ts1 ts2 + +instance Ord InstTypes where +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +  compare (InstTypes ts1) (InstTypes ts2) = nonDetCmpTypes ts1 ts2 +#else +  compare (InstTypes ts1) (InstTypes ts2) = cmpTypes ts1 ts2 +#endif + +-- | Creates an instance resolution tree  +traceInstanceResolution :: +     Environment +  -> Class +  -> [Type] -- ^ Types at which type variables of a class are instantated +  -> HCE.InstanceResolution +traceInstanceResolution environment c ts = go c ts S.empty +  where +    flags = envDynFlags environment +    go :: Class -> [Type] -> S.Set (Name, InstTypes) -> HCE.InstanceResolution +    go cls types seenInstances = +      let clsTyVarCount = length $ classTyVars cls +       in case lookupUniqueInstEnv +                 (envInstEnv environment) +                 cls +                 (take clsTyVarCount types) of +            Right (inst, instTypes) -> +              -- A successful match is a ClsInst, together with the types at which +              -- the dfun_id in the ClsInst should be instantiated               +              let instWithTypes = (is_dfun_name inst, InstTypes instTypes) +               in if not $ S.member instWithTypes seenInstances +                    then let (typeVars, predTypes, _class, _types) = +                               instanceSig inst +                             subst = zipTvSubst typeVars instTypes +                             constraints = +                               mapMaybe getClassPredTys_maybe . substTys subst $ +                               predTypes +                          in HCE.Instance +                               (instanceToText flags inst) +                               (mkType flags . idType $ is_dfun inst) +                               (map (mkType flags) instTypes) +                               (nameLocationInfo +                                  flags +                                  (envPackageId environment) +                                  (envComponentId environment) +                                  (envTransformation environment) +                                  (envFileMap environment) +                                  (envDefSiteMap environment) +                                  (Just . instanceToText flags $ inst) +                                  Nothing +                                  (varName . is_dfun $ inst)) +                               (map +                                  (\(cl, tys) -> +                                     go +                                       cl +                                       tys +                                       (S.insert instWithTypes seenInstances)) +                                  constraints) +                    else HCE.Stop +            Left _ -> HCE.Stop + +mkIdentifierInfo :: Environment -> Id -> Maybe Name -> HCE.IdentifierInfo +mkIdentifierInfo environment identifier mbNameFromRenamedSource = +  let name = fromMaybe (varName identifier) mbNameFromRenamedSource +      sort = nameSort name +      nameSpace = occNameNameSpace . nameOccName $ name +      flags = envDynFlags environment +      currentPackageId = envPackageId environment +      compId = envComponentId environment +      transformation = envTransformation environment +      fileMap = envFileMap environment +      defSiteMap = envDefSiteMap environment +      locationInfo = +        nameLocationInfo +          flags +          currentPackageId +          compId +          transformation +          fileMap +          defSiteMap +          Nothing +          Nothing +          name +   in HCE.IdentifierInfo +        { sort = sort +        , occName = HCE.OccName $ nameToText name +        , demangledOccName = demangleOccName name +        , nameSpace = nameSpace +        , idType = mkType flags $ varType identifier +        , locationInfo = locationInfo +        , details = mbIdDetails identifier +        , doc = +            nameDocumentation +              transformation +              fileMap +              defSiteMap +              (envCurrentModuleDefSites environment) +              name +        , internalId = HCE.InternalId $ identifierKey flags identifier +        , externalId = +            case sort of +              HCE.External -> +                case locationInfo of +                  HCE.ExactLocation {..} -> +                    Just $ +                    HCE.ExternalId $ +                    T.intercalate +                      "|" +                      [ HCE.packageIdToText currentPackageId +                      , HCE.getHaskellModuleName moduleName +                      , case nameSpace of +                          HCE.VarName -> T.pack $ show HCE.Val   +                          HCE.DataName -> T.pack $ show HCE.Val   +                          _ -> T.pack $ show HCE.Typ +                      , nameToText name +                      ] +                  HCE.ApproximateLocation {name = n, ..} -> +                    Just $ +                    HCE.ExternalId $ +                    T.intercalate +                      "|" +                      [ HCE.packageIdToText packageId +                      , HCE.getHaskellModuleName moduleName +                      , T.pack $ show entity   +                      , n +                      ] +                  _ -> Nothing +              _ -> Nothing +        , isExported = S.member name $ envExportedNames environment +        } + +mkIdentifierOccurrence :: +     Environment +  -> Id +  -> Name +  -> Maybe (Type, [Type]) +  -> Bool +  -> T.Text +  -> HCE.IdentifierOccurrence +mkIdentifierOccurrence environment identifier nameFromRenamedSource mbInstTypes isBinder descr = +  let flags = envDynFlags environment +      mbClass +        | isId identifier = +          case idDetails identifier of +            ClassOpId cls -> Just cls +            _ -> Nothing +        | otherwise = Nothing +      mbInstanceResolution = +        case (mbClass, mbInstTypes) of +          (Just cls, Just (_, ts)) -> +            Just $ traceInstanceResolution environment cls ts +          _ -> Nothing +   in HCE.IdentifierOccurrence +        (Just . HCE.InternalId . identifierKey flags $ identifier) +        (Just . HCE.InternalId . T.pack . show . getKey . nameUnique $ nameFromRenamedSource) +        isBinder +        mbInstanceResolution +        (mkType flags . fst <$> mbInstTypes) +        (map (mkType flags) . snd <$> mbInstTypes) +        descr +        (if isId identifier +           then HCE.ValueId +           else HCE.TypeId) + +restoreTidyEnv :: (State ASTState) a -> (State ASTState) a +restoreTidyEnv action = do +  tidyEnv <- astStateTidyEnv <$> get +  res <- action +  modify' $ \s -> s {astStateTidyEnv = tidyEnv} +  return res + +restoreHsWrapper :: (State ASTState) a -> (State ASTState) a +restoreHsWrapper action = do +  wrapper <- astStateHsWrapper <$> get +  res <- action +  modify' $ \s -> s {astStateHsWrapper = wrapper} +  return res   + +tidyIdentifier :: Id -> State ASTState (Id, Maybe (Type, [Type])) +tidyIdentifier identifier = do +  tidyEnv <- astStateTidyEnv <$> get +  mbHsWrapper <- astStateHsWrapper <$> get +  let (tidyEnv', identifier') = tidyIdentifierType tidyEnv identifier +      identifierType = varType identifier' +      (mbTypes, updatedEnv) = +        case mbHsWrapper of +          Just wrapper -> +            let expectedType = applyWrapper wrapper identifierType +                (tidyEnv'', expectedType') = tidyOpenType tidyEnv' expectedType +                wrapperTys = +                  map (snd . tidyOpenType tidyEnv'') (wrapperTypes wrapper) +             in if not $ eqType expectedType identifierType +                  then (Just (expectedType', wrapperTys), tidyEnv'') +                  else (Nothing, tidyEnv') +          Nothing -> (Nothing, tidyEnv') +  modify' (\s -> s {astStateTidyEnv = updatedEnv}) +  return (identifier', mbTypes) + +tidyType :: Type -> State ASTState Type +tidyType typ = do +  tidyEnv <- astStateTidyEnv <$> get +  let (tidyEnv', typ') = tidyOpenType tidyEnv typ +  modify' (\s -> s {astStateTidyEnv = tidyEnv'}) +  return typ' +   +foldTypecheckedSource :: LHsBinds Id -> State ASTState () +foldTypecheckedSource = foldLHsBindsLR + +foldLHsExpr :: LHsExpr Var -> State ASTState (Maybe Type) +foldLHsExpr (L span (HsVar (L _ identifier))) = +  restoreTidyEnv $ do +    (identifier', mbTypes) <- tidyIdentifier identifier +    addIdentifierToIdSrcSpanMap span identifier' mbTypes +    return . Just . varType $ identifier' +foldLHsExpr (L _ (HsUnboundVar _)) = return Nothing +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +foldLHsExpr (L _ (HsConLikeOut conLike)) = +  restoreTidyEnv $ do     +    let mbType = varType <$> conLikeWrapId_maybe conLike     +    mbType' <- maybe (return Nothing) (fmap Just . tidyType) mbType     +    return mbType' +#endif +foldLHsExpr (L _ (HsRecFld _)) = return Nothing +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +foldLHsExpr (L _ (HsOverLabel _ _)) = return Nothing +#else +foldLHsExpr (L _ (HsOverLabel _)) = return Nothing +#endif +foldLHsExpr (L span expr@(HsIPVar _)) = do +  addExprInfo span Nothing "HsIPVar" (exprSort expr) +  return Nothing +foldLHsExpr (L span (HsOverLit OverLit {ol_type})) = +  restoreTidyEnv $ do +    typ <- tidyType ol_type +    addExprInfo +      span +      (Just typ) +      "HsOverLit" +      (if isOneLineSpan span +         then Simple +         else Composite) +    return $ Just typ +foldLHsExpr (L span (HsLit lit)) = +  restoreTidyEnv $ do +    typ <- tidyType $ hsLitType lit +    addExprInfo +      span +      (Just typ) +      "HsLit" +      (if isOneLineSpan span +         then Simple +         else Composite) +    return $ Just typ +foldLHsExpr (L span expr@(HsLam MG {..})) = +  restoreTidyEnv $ do +    typ <- tidyType $ mkFunTys mg_arg_tys mg_res_ty +    addExprInfo span (Just typ) "HsLam" (exprSort expr) +    mapM_ foldLMatch $ unLoc mg_alts +    return $ Just typ +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)     +foldLHsExpr (L span expr@(HsLamCase MG {..})) = +#else +foldLHsExpr (L span expr@(HsLamCase _typ MG {..})) = +#endif +  restoreTidyEnv $ do +    typ <- tidyType $ mkFunTys mg_arg_tys mg_res_ty +    addExprInfo span (Just typ) "HsLamCase" (exprSort expr) +    mapM_ foldLMatch $ unLoc mg_alts +    return $ Just typ +foldLHsExpr (L span expr@(HsApp fun arg)) = do +  funTy <- foldLHsExpr fun +  _argTy <- foldLHsExpr arg +  typ <- fromMaybe (return Nothing) (funResultTySafe span "HsApp" <$> funTy) +  addExprInfo span typ "HsApp" (exprSort expr) +  return typ +foldLHsExpr (L _ (HsAppType _ _)) = return Nothing +foldLHsExpr (L span ex@(HsAppTypeOut expr _)) = do +  typ <- foldLHsExpr expr +  addExprInfo span typ "HsAppTypeOut" (exprSort ex) +  return typ   +foldLHsExpr (L span expr@(OpApp left op _fixity right)) = do +  opTyp <- foldLHsExpr op +  typ <- fromMaybe (return Nothing) (funResultTy2Safe span "HsApp" <$> opTyp) +  _ <- foldLHsExpr left +  _ <- foldLHsExpr right +  addExprInfo span typ "OpApp" (exprSort expr) +  return typ +foldLHsExpr (L span e@(NegApp expr _syntaxExp)) = do +  typ <- foldLHsExpr expr +  addExprInfo span typ "NegApp" (exprSort e) +  return typ +foldLHsExpr (L _span (HsPar expr)) = foldLHsExpr expr +foldLHsExpr (L span expr@(SectionL operand operator)) = do +  opType <- foldLHsExpr operator +  _ <- foldLHsExpr operand +  mbTypes <- +    fromMaybe (return Nothing) (splitFunTy2Safe span "SectionL" <$> opType) +  let typ = +        case mbTypes of +          Just (_arg1, arg2, res) -> Just $ mkFunTy arg2 res +          Nothing -> Nothing +  addExprInfo span typ "SectionL" (exprSort expr) +  return typ +foldLHsExpr (L span e@(SectionR operator operand)) = do +  opType <- foldLHsExpr operator +  _ <- foldLHsExpr operand +  mbTypes <- +    fromMaybe (return Nothing) (splitFunTy2Safe span "SectionR" <$> opType) +  let typ = +        case mbTypes of +          Just (arg1, _arg2, res) -> Just $ mkFunTy arg1 res +          Nothing -> Nothing +  addExprInfo span typ "SectionR" (exprSort e) +  return typ +foldLHsExpr (L span e@(ExplicitTuple tupArgs boxity)) = do +  tupleArgs <- mapM foldLHsTupArg tupArgs +  let tupleSectionArgTys = +        mapM fst . filter ((== TupArgMissing) . snd) $ tupleArgs +      tupleArgTys = mapM fst tupleArgs +      resultType = +        mkFunTys <$> tupleSectionArgTys <*> (mkTupleTy boxity <$> tupleArgTys) +  tidyEnv <- astStateTidyEnv <$> get +  addExprInfo +    span +    ((snd . tidyOpenType tidyEnv) <$> resultType) +    "ExplicitTuple" +    (exprSort e) +  return resultType +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +foldLHsExpr (L _span (ExplicitSum _ _ expr _types)) = do +  -- TODO +  _ <- foldLHsExpr expr +  return Nothing     +#endif     +foldLHsExpr (L span e@(HsCase expr MG {..})) = +  restoreTidyEnv $ do +    typ <- tidyType mg_res_ty +    _ <- foldLHsExpr expr +    mapM_ foldLMatch (unLoc mg_alts) +    addExprInfo span (Just typ) "HsCase" (exprSort e) +    return $ Just typ +foldLHsExpr (L span e@(HsIf _mbSynExpr condExpr thenExpr elseExpr)) = do +  _ <- foldLHsExpr condExpr +  typ <- foldLHsExpr thenExpr +  _ <- foldLHsExpr elseExpr +  addExprInfo span typ "HsIf" (exprSort e) +  return typ +foldLHsExpr (L span e@(HsMultiIf typ grhss)) = +  restoreTidyEnv $ do +    typ' <- tidyType typ +    addExprInfo span (Just typ') "HsMultiIf" (exprSort e) +    mapM_ foldLGRHS grhss +    return $ Just typ' +foldLHsExpr (L span e@(HsLet (L _ binds) expr)) = do +  _ <- foldHsLocalBindsLR binds +  typ <- foldLHsExpr expr +  addExprInfo span typ "HsLet" (exprSort e) +  return typ +foldLHsExpr (L span expr@(HsDo _context (L _ stmts) typ)) = +  restoreTidyEnv $ do +    typ' <- tidyType typ +    addExprInfo span (Just typ') "HsDo" (exprSort expr) +    mapM_ foldLStmtLR stmts +    return $ Just typ' +foldLHsExpr (L span (ExplicitList typ _syntaxExpr exprs)) = +  restoreTidyEnv $ do +    typ' <- mkListTy <$> tidyType typ +    unless (null exprs) $ addExprInfo span (Just typ') "ExplicitList" Composite +    mapM_ foldLHsExpr exprs +    return $ Just typ' +foldLHsExpr (L span e@(ExplicitPArr typ exprs)) = +  restoreTidyEnv $ do +    typ' <- tidyType typ +    addExprInfo span (Just typ') "ExplicitPArr" (exprSort e) +    mapM_ foldLHsExpr exprs +    return $ Just typ' +foldLHsExpr (L span e@(RecordCon (L _ _) _conLike conExpr binds)) = do +    mbConType <- +      fmap (snd . splitFunTys) <$> +      foldLHsExpr (L (UnhelpfulSpan $ mkFastString "RecordCon") conExpr) +    addExprInfo span mbConType "RecordCon" (exprSort e) +    _ <- foldHsRecFields binds +    return mbConType +foldLHsExpr (L span e@(RecordUpd expr binds cons _inputTys outTys _wrapper)) = +  restoreTidyEnv $ do    +    -- cons is a non-empty list of DataCons that have  all the upd'd fields +    let typ = conLikeResTy (head cons) outTys +    typ' <- tidyType typ +    addExprInfo span (Just typ') "RecordUpd" (exprSort e) +    _ <- foldLHsExpr expr +    mapM_ foldLHsRecUpdField binds +    return $ Just typ' +foldLHsExpr (L _span (ExprWithTySig _expr _type)) = return Nothing +foldLHsExpr (L span e@(ExprWithTySigOut expr _type)) = do +  typ <- foldLHsExpr expr +  addExprInfo span typ "ExprWithTySigOut" (exprSort e) +  return typ +foldLHsExpr (L span e@(ArithSeq postTcExpr _mbSyntaxExpr seqInfo)) = do +  typ <- +    fmap (snd . splitFunTys . snd . splitForAllTys) <$> +    foldLHsExpr (L (UnhelpfulSpan $ mkFastString "ArithSeq") postTcExpr) +  _ <- +    case seqInfo of +      From expr -> foldLHsExpr expr +      FromThen expr1 expr2 -> foldLHsExpr expr1 >> foldLHsExpr expr2 +      FromTo expr1 expr2 -> foldLHsExpr expr1 >> foldLHsExpr expr2 +      FromThenTo expr1 expr2 expr3 -> +        foldLHsExpr expr1 >> foldLHsExpr expr2 >> foldLHsExpr expr3 +  addExprInfo span typ "ArithSeq" (exprSort e) +  return typ +foldLHsExpr (L span e@(PArrSeq postTcExpr _seqInfo)) = do +  typ <- foldLHsExpr (L (UnhelpfulSpan $ mkFastString "PArrSeq") postTcExpr) +  addExprInfo span typ "ArithSeq" (exprSort e) +  return typ +foldLHsExpr (L span e@(HsSCC _sourceText _fastString expr)) = do +  typ <- foldLHsExpr expr +  addExprInfo span typ "HsSCC" (exprSort e) +  return typ +foldLHsExpr (L span e@(HsCoreAnn _sourceText _fastString expr)) = do +  typ <- foldLHsExpr expr +  addExprInfo span typ "HsCoreAnn" (exprSort e) +  return typ +foldLHsExpr (L _span (HsBracket _bracket)) = return Nothing +foldLHsExpr (L _span (HsRnBracketOut _ _)) = return Nothing +foldLHsExpr (L _span (HsTcBracketOut _bracket _splice)) = return Nothing +foldLHsExpr (L _span (HsSpliceE _)) = return Nothing +foldLHsExpr (L span expr@(HsProc pat cmd)) = do +  _ <- foldLPat pat +  _ <- foldLHsCmdTop cmd +  addExprInfo span Nothing "HsProc" (exprSort expr) +  return Nothing +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)   +foldLHsExpr (L span e@(HsStatic _ expr)) = do +#else +foldLHsExpr (L span e@(HsStatic expr)) = do +#endif +  typ <- foldLHsExpr expr +  addExprInfo span typ "HsStatic" (exprSort e) +  return typ +foldLHsExpr (L _ HsArrForm {}) = return Nothing +foldLHsExpr (L _ HsArrApp {}) = return Nothing +foldLHsExpr (L span e@(HsTick _ expr)) = do +  typ <- foldLHsExpr expr +  addExprInfo span typ "HsTick" (exprSort e) +  return typ +foldLHsExpr (L span e@(HsBinTick _ _ expr)) = do +  typ <- foldLHsExpr expr +  addExprInfo span typ "HsBinTick" (exprSort e) +  return typ +foldLHsExpr (L span e@(HsTickPragma _ _ _ expr)) = do +  typ <- foldLHsExpr expr +  addExprInfo span typ "HsTickPragma" (exprSort e) +  return typ +foldLHsExpr (L _span EWildPat) = return Nothing +foldLHsExpr (L _span (EAsPat _ _)) = return Nothing +foldLHsExpr (L _span (EViewPat _ _)) = return Nothing +foldLHsExpr (L _span (ELazyPat _)) = return Nothing +foldLHsExpr (L span (HsWrap wrapper expr)) = +  restoreHsWrapper $ do +    case exprSort expr of +      Simple -> modify' (\s -> s {astStateHsWrapper = Just wrapper}) +      Composite -> return () -- Not sure if it is possible +    typ <- foldLHsExpr (L span expr) +    return $ applyWrapper wrapper <$> typ +   +foldHsRecFields :: HsRecFields Id (LHsExpr Id) -> State ASTState (Maybe Type) +foldHsRecFields HsRecFields {..} = do +  let userWritten = +        case rec_dotdot of +          Just i -> take i +          Nothing -> id +  mapM_ foldLHsRecField $ userWritten rec_flds +  return Nothing +   +foldLHsRecField :: LHsRecField Id (LHsExpr Id) -> State ASTState (Maybe Type) +foldLHsRecField (L span (HsRecField (L idSpan (FieldOcc _ identifier)) arg pun)) = +  restoreTidyEnv $ do +    (identifier', mbTypes) <- tidyIdentifier identifier +    addIdentifierToIdSrcSpanMap idSpan identifier' mbTypes +    addExprInfo span (Just . varType $ identifier') "HsRecField" Composite +    unless pun $ void (foldLHsExpr arg) +    return . Just . varType $ identifier' + +foldLHsRecUpdField :: LHsRecUpdField Id -> State ASTState (Maybe Type) +foldLHsRecUpdField (L span (HsRecField (L idSpan recField) arg pun)) = +  restoreTidyEnv $ do +    let selectorId = selectorAmbiguousFieldOcc recField +    (identifier', mbTypes) <- tidyIdentifier selectorId +     -- Name of the selectorId is not 'correct' (Internal instead of External) : +     -- https://github.com/ghc/ghc/blob/321b420f4582d103ca7b304867b916a749712e9f/compiler/typecheck/TcExpr.hs#L2424 +    typeEnv <- envTypeEnv . astStateEnv <$> get +    let selName = varName selectorId +        originalName = +          case lookupTypeEnv typeEnv selName of +            Just (AnId originalSelId) -> varName originalSelId +            _ -> selName +    let identifier'' = setVarName identifier' originalName +    addIdentifierToIdSrcSpanMap idSpan identifier'' mbTypes +    addExprInfo span (Just . varType $ identifier'') "HsRecUpdField" Composite +    unless pun $ void (foldLHsExpr arg) +    return . Just . varType $ identifier' + +data TupArg +  = TupArgPresent +  | TupArgMissing +  deriving (Show, Eq) + +foldLHsTupArg :: LHsTupArg Id -> State ASTState (Maybe Type, TupArg) +foldLHsTupArg (L _span (Present expr)) = +  restoreTidyEnv $ do +    typ <- foldLHsExpr expr +    typ' <- +      case typ of +        Just t -> Just <$> tidyType t +        Nothing -> return Nothing +    return (typ', TupArgPresent) +foldLHsTupArg (L _ (Missing typ)) = +  restoreTidyEnv $ do +    typ' <- tidyType typ +    return (Just typ', TupArgMissing) + +foldLMatch :: LMatch Id (LHsExpr Var) -> State ASTState (Maybe Type) +foldLMatch (L _span Match {..}) = do +  mapM_ foldLPat m_pats +  _ <- foldGRHSs m_grhss +  return Nothing     +        +foldLMatchCmd :: LMatch Id (LHsCmd Var) -> State ASTState (Maybe Type) +foldLMatchCmd (L _span Match {..}) = do +  mapM_ foldLPat m_pats +  _ <- foldGRHSsCmd m_grhss +  return Nothing + +foldGRHSsCmd :: GRHSs Id (LHsCmd Id) -> State ASTState (Maybe Type) +foldGRHSsCmd GRHSs {..} = do +  mapM_ foldLGRHSCmd grhssGRHSs +  _ <- foldHsLocalBindsLR (unLoc grhssLocalBinds) +  return Nothing + +foldGRHSs :: GRHSs Id (LHsExpr Var) -> State ASTState (Maybe Type) +foldGRHSs GRHSs {..} = do +  mapM_ foldLGRHS grhssGRHSs +  _ <- foldHsLocalBindsLR (unLoc grhssLocalBinds) +  return Nothing + +foldLStmtLR :: LStmtLR Id Id (LHsExpr Var) -> State ASTState (Maybe Type) +foldLStmtLR (L span (LastStmt body _ _)) =   +  do typ <- foldLHsExpr body +     addExprInfo span typ "LastStmt" Composite +     return typ +foldLStmtLR (L _span (BindStmt pat body _ _ _)) = do +  _ <- foldLPat pat +  _ <- foldLHsExpr body +  return Nothing +foldLStmtLR (L span (BodyStmt body _ _ _)) = do +  mbTyp <- foldLHsExpr body +  addExprInfo span mbTyp "BodyStmt" Composite +  return mbTyp +foldLStmtLR (L _ (LetStmt (L _ binds))) = do +  _ <- foldHsLocalBindsLR binds +  return Nothing +foldLStmtLR (L _ (ParStmt blocks _ _ _)) = do +  mapM_ foldParStmtBlock blocks +  return Nothing +foldLStmtLR (L _ TransStmt {..}) = do +  mapM_ foldLStmtLR trS_stmts   +  _ <- maybe (return Nothing) foldLHsExpr trS_by +  _ <- foldLHsExpr trS_using +  return Nothing +foldLStmtLR (L _span RecStmt {..}) = do +  mapM_ foldLStmtLR recS_stmts +  return Nothing +foldLStmtLR (L span (ApplicativeStmt args _ typ)) = +  restoreTidyEnv $ do +    typ' <- tidyType typ +    mapM_ (foldApplicativeArg . snd) args +    addExprInfo span (Just typ') "ApplicativeStmt" Composite +    return Nothing + +foldApplicativeArg :: ApplicativeArg Id Id -> State ASTState (Maybe Type) +foldApplicativeArg appArg = +  case appArg of +    ApplicativeArgOne pat expr -> do +      _ <- foldLPat pat +      _ <- foldLHsExpr expr +      return Nothing +    ApplicativeArgMany exprStmts _ pat -> do +      _ <- mapM_ foldLStmtLR exprStmts +      _ <- foldLPat pat +      return Nothing   + +foldLStmtLRCmd :: LStmtLR Id Id (LHsCmd Var)  +               -> State ASTState (Maybe Type) +foldLStmtLRCmd (L span (LastStmt body _syntaxExpr _)) = do +  typ <- foldLHsCmd body +  addExprInfo span typ "LastStmt Cmd" Composite +  return typ +foldLStmtLRCmd (L _ (BindStmt pat body _ _ _)) = do +  _ <- foldLPat pat +  _ <- foldLHsCmd body +  return Nothing +foldLStmtLRCmd (L span (BodyStmt body _ _ _)) = do +  typ <- foldLHsCmd body +  addExprInfo span typ "BodyStmt Cmd" Composite +  return typ +foldLStmtLRCmd (L _ (LetStmt (L _ binds))) = do +  _ <- foldHsLocalBindsLR binds +  return Nothing +foldLStmtLRCmd (L _ (ParStmt blocks _ _ _)) = do +  mapM_ foldParStmtBlock blocks +  return Nothing +foldLStmtLRCmd (L _ TransStmt {..}) = do +  mapM_ foldLStmtLR trS_stmts +  _ <- foldLHsExpr trS_using +  _ <- maybe (return Nothing) foldLHsExpr trS_by +  return Nothing +foldLStmtLRCmd (L _ RecStmt {..}) = do +  mapM_ foldLStmtLRCmd recS_stmts +  return Nothing +foldLStmtLRCmd (L span (ApplicativeStmt args _ typ)) = +  restoreTidyEnv $ do +    typ' <- tidyType typ +    mapM_ (foldApplicativeArg . snd) args +    addExprInfo span (Just typ') "ApplicativeStmt Cmd" Composite +    return Nothing   +   +foldLGRHS :: LGRHS Id (LHsExpr Id) -> State ASTState (Maybe Type) +foldLGRHS (L _span (GRHS guards body)) = do +  typ <- foldLHsExpr body +  mapM_ foldLStmtLR guards +  return typ +   +foldLGRHSCmd :: LGRHS Id (LHsCmd Var) -> State ASTState (Maybe Type) +foldLGRHSCmd (L _span (GRHS guards body)) = do +  typ <- foldLHsCmd body +  mapM_ foldLStmtLR guards +  return typ    + +foldParStmtBlock :: ParStmtBlock Id Id -> State ASTState (Maybe Type) +foldParStmtBlock (ParStmtBlock exprStmts _ids _syntaxExpr) = do +  mapM_ foldLStmtLR exprStmts +  return Nothing + +foldHsLocalBindsLR :: HsLocalBindsLR Id Id -> State ASTState (Maybe Type) +foldHsLocalBindsLR (HsValBinds binds) = do +  _ <- foldHsValBindsLR binds +  return Nothing +foldHsLocalBindsLR (HsIPBinds _binds) = return Nothing +foldHsLocalBindsLR EmptyLocalBinds = return Nothing + +foldHsValBindsLR :: HsValBindsLR Id Var -> State ASTState (Maybe Type) +foldHsValBindsLR (ValBindsIn _ _) = return Nothing +foldHsValBindsLR (ValBindsOut binds _) = do +  _ <- mapM_ (foldLHsBindsLR . snd) binds +  return Nothing + +foldLHsBindsLR :: LHsBinds Id -> State ASTState () +foldLHsBindsLR = mapM_ (`foldLHsBindLR` Nothing) . bagToList + +foldLHsBindLR :: LHsBindLR Id Var +              -> Maybe Id -- ^ Polymorphic id +              -> State ASTState (Maybe Type) +foldLHsBindLR (L _span FunBind {..}) mbPolyId +  | mg_origin fun_matches == FromSource = +    restoreTidyEnv $ do +      let (L idSpan identifier) = fun_id -- monotype +          typ = +            case mbPolyId of +              Just polyId -> varType polyId +              Nothing -> varType identifier +          name = maybe (varName identifier) varName mbPolyId +          identifier' = setVarType (setVarName identifier name) typ +      (identifier'', _) <- tidyIdentifier identifier' +      addIdentifierToIdSrcSpanMap idSpan identifier'' Nothing +      mapM_ foldLMatch (unLoc (mg_alts fun_matches)) +      return Nothing +  | otherwise = return Nothing +foldLHsBindLR (L _ PatBind {..}) _ = do +  _ <- foldLPat pat_lhs +  _ <- foldGRHSs pat_rhs +  return Nothing +foldLHsBindLR (L _ VarBind {..}) _ = return Nothing +foldLHsBindLR (L _ AbsBinds {..}) _ = do +  _ <- +    mapM_ (\(bind, typ) -> foldLHsBindLR bind (Just typ)) $ +    zip (bagToList abs_binds) (map abe_poly abs_exports) +  return Nothing +foldLHsBindLR (L _ AbsBindsSig {..}) _ = do +  _ <- foldLHsBindLR abs_sig_bind (Just abs_sig_export) +  return Nothing +foldLHsBindLR (L _ (PatSynBind PSB {..})) _ = +  restoreTidyEnv $ do +    _ <- foldLPat psb_def +    _ <- +      let addId :: GenLocated SrcSpan Id -> State ASTState () +          addId (L span i) = do +            (i', _) <- tidyIdentifier i +            addIdentifierToIdSrcSpanMap span i' Nothing +       in case psb_args of +            InfixPatSyn id1 id2 -> addId id1 >> addId id2 +            PrefixPatSyn ids -> mapM_ addId ids +            RecordPatSyn recs -> +              mapM_ +                (\(RecordPatSynField selId patVar) -> +                   addId selId >> addId patVar) +                recs +    return Nothing + +foldLPat :: LPat Id -> State ASTState (Maybe Type) +foldLPat (L span (VarPat (L _ identifier))) = do +  (identifier', _) <- tidyIdentifier identifier +  addIdentifierToIdSrcSpanMap span identifier' Nothing +  return . Just . varType $ identifier' +foldLPat (L span pat@(WildPat typ)) = do +  typ' <- tidyType typ +  addExprInfo span (Just typ') "WildPat" (patSort pat) +  return $ Just typ' +foldLPat (L span p@(LazyPat pat)) = do +  mbType <- foldLPat pat +  addExprInfo span mbType "LazyPat" (patSort p) +  return mbType +foldLPat (L span p@(AsPat (L idSpan identifier) pat)) = do +  (identifier', _) <- tidyIdentifier identifier +  addIdentifierToIdSrcSpanMap idSpan identifier' Nothing +  addExprInfo span (Just . varType $ identifier') "AsPat" (patSort p) +  _ <- foldLPat pat +  return . Just . varType $ identifier' +foldLPat (L _span (ParPat pat)) = foldLPat pat   +foldLPat (L span p@(BangPat pat)) = do +  typ <- foldLPat pat +  addExprInfo span typ "BangPat" (patSort p) +  return typ +foldLPat (L span p@(ListPat pats typ _)) = do +  typ' <- tidyType typ +  let listType = mkListTy typ' +  addExprInfo span (Just listType) "ListPat" (patSort p) +  _ <- mapM_ foldLPat pats +  return $ Just listType +foldLPat (L span pat@(TuplePat pats boxity types)) = do +  typ' <- tidyType $ mkTupleTy boxity types +  addExprInfo span (Just typ') "TuplePat" (patSort pat) +  _ <- mapM_ foldLPat pats +  return $ Just typ' +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +foldLPat (L _span (SumPat pat _ _ _types)) = do +  -- TODO +  _ <- foldLPat pat +  return Nothing   +#endif +foldLPat (L span pat@(PArrPat pats typ)) = do +  typ' <- tidyType typ +  addExprInfo span (Just typ') "PArrPat" (patSort pat) +  _ <- mapM_ foldLPat pats +  return $ Just typ' +foldLPat (L _span (ConPatIn _ _)) = return Nothing +foldLPat (L span pat@ConPatOut {..}) = do +  let (L idSpan conLike) = pat_con  +      conId = +        case conLike of +          RealDataCon dc -> dataConWorkId dc +          PatSynCon ps -> patSynId ps +      typ = conLikeResTy (unLoc pat_con) pat_arg_tys +  (identifier', mbTypes) <- tidyIdentifier conId +  addIdentifierToIdSrcSpanMap idSpan identifier' mbTypes +  typ' <- tidyType typ +  addExprInfo span (Just typ') "ConPatOut" (patSort pat) +  _ <- foldHsConPatDetails pat_args +  return . Just . varType $ identifier' +foldLPat (L span p@(ViewPat expr pat typ)) = do +  typ' <- tidyType typ +  addExprInfo span (Just typ') "ViewPat" (patSort p) +  _ <- foldLPat pat +  _ <- foldLHsExpr expr +  return $ Just typ' +foldLPat (L _ (SplicePat _)) = return Nothing +foldLPat (L span (LitPat hsLit)) = do +  typ' <- tidyType $ hsLitType hsLit +  addExprInfo +    span +    (Just typ') +    "LitPat" +    (if isOneLineSpan span +       then Simple +       else Composite) +  return $ Just typ' +foldLPat (L span pat@(NPat (L _spanLit OverLit {ol_type}) _ _ _)) = do +  typ' <- tidyType ol_type +  addExprInfo span (Just typ') "NPat" (patSort pat) +  return $ Just ol_type +foldLPat (L span pat@(NPlusKPat (L idSpan identifier) (L litSpan OverLit {ol_type}) _ _ _ typ)) = do +  (identifier', _) <- tidyIdentifier identifier +  addIdentifierToIdSrcSpanMap idSpan identifier' Nothing +  typ' <- tidyType typ +  addExprInfo span (Just typ') "NPlusKPat" (patSort pat) +  olType' <- tidyType ol_type +  addExprInfo +    litSpan +    (Just olType') +    "NPlusKPat" +    (if isOneLineSpan span +       then Simple +       else Composite) +  return $ Just typ' +foldLPat (L _span (SigPatIn _ _)) = return Nothing   +foldLPat (L _span (SigPatOut pat typ)) = do +  typ' <- tidyType typ +  _ <- foldLPat pat +  return $ Just typ' +foldLPat (L span p@(CoPat _ pat typ)) = do +  typ' <- tidyType typ +  addExprInfo span (Just typ') "CoPat" (patSort p) +  _ <- foldLPat (L span pat) +  return Nothing  + +foldHsConPatDetails +  :: HsConPatDetails Id +  -> State ASTState (Maybe Type) +foldHsConPatDetails (PrefixCon args) = do +  _ <- mapM_ foldLPat args +  return Nothing +foldHsConPatDetails (RecCon rec) = do +  _ <- foldHsRecFieldsPat rec +  return Nothing +foldHsConPatDetails (InfixCon arg1 arg2) = do +  _ <- foldLPat arg1 +  _ <- foldLPat arg2 +  return Nothing + +foldHsRecFieldsPat :: HsRecFields Id (LPat Id) -> State ASTState (Maybe Type) +foldHsRecFieldsPat HsRecFields {..} = do +  let onlyUserWritten = +        case rec_dotdot of +          Just i -> take i +          Nothing -> id +  _ <- mapM_ foldLHsRecFieldPat $ onlyUserWritten rec_flds +  return Nothing + +foldLHsRecFieldPat :: LHsRecField Id (LPat Id) -> State ASTState (Maybe Type) +foldLHsRecFieldPat (L _ (HsRecField (L idSpan (FieldOcc _ identifier)) arg pun)) = do +  (identifier', mbTypes) <- tidyIdentifier identifier +  addIdentifierToIdSrcSpanMap idSpan identifier' mbTypes +  unless pun $ void $ foldLPat arg +  return . Just . varType $ identifier' + +foldLHsCmdTop :: LHsCmdTop Id -> State ASTState (Maybe Type) +foldLHsCmdTop (L span (HsCmdTop cmd _ _ _)) = do +  mbTyp <- foldLHsCmd cmd +  addExprInfo span mbTyp "HsCmdTop" Composite +  return mbTyp + +foldLHsCmd :: LHsCmd Id -> State ASTState (Maybe Type) +foldLHsCmd (L _ (HsCmdArrApp expr1 expr2 _ _ _)) = do +  _ <- foldLHsExpr expr1 +  _ <- foldLHsExpr expr2 +  return Nothing +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)   +foldLHsCmd (L _ (HsCmdArrForm expr _  _ topCmds)) = do +#else +foldLHsCmd (L _ (HsCmdArrForm expr _ topCmds)) = do +#endif +  _ <- foldLHsExpr expr +  _ <- mapM_ foldLHsCmdTop topCmds +  return Nothing   +foldLHsCmd (L _ (HsCmdApp cmd expr)) = do +  _ <- foldLHsCmd cmd +  _ <- foldLHsExpr expr +  return Nothing +foldLHsCmd (L _ (HsCmdLam MG {..})) = do +  mapM_ foldLMatchCmd $ unLoc mg_alts +  return Nothing +foldLHsCmd (L _ (HsCmdCase expr MG {..})) = do +  _ <- foldLHsExpr expr +  mapM_ foldLMatchCmd $ unLoc mg_alts +  return Nothing    +foldLHsCmd (L _ (HsCmdPar cmd)) = do +  _ <- foldLHsCmd cmd +  return Nothing +foldLHsCmd (L _ (HsCmdIf _ expr cmd1 cmd2)) = do +  _ <- foldLHsCmd cmd1 +  _ <- foldLHsCmd cmd2 +  _ <- foldLHsExpr expr +  return Nothing +foldLHsCmd (L _ (HsCmdLet (L _ binds) cmd)) = do +  _ <- foldLHsCmd cmd +  _ <- foldHsLocalBindsLR binds +  return Nothing +foldLHsCmd (L _ (HsCmdDo stmts _)) = do +  mapM_ foldLStmtLRCmd $ unLoc stmts +  return Nothing +foldLHsCmd (L span (HsCmdWrap _ cmd)) = do +  _ <- foldLHsCmd (L span cmd) +  return Nothing diff --git a/src/HaskellCodeExplorer/GhcUtils.hs b/src/HaskellCodeExplorer/GhcUtils.hs new file mode 100644 index 0000000..714e429 --- /dev/null +++ b/src/HaskellCodeExplorer/GhcUtils.hs @@ -0,0 +1,1122 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE Rank2Types #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE RecordWildCards #-} + +module HaskellCodeExplorer.GhcUtils +  ( -- * Pretty-printing +    toText +  , instanceToText +  , instanceDeclToText +  , nameToText +  , tyClDeclPrefix +  , demangleOccName +  , stringBufferToByteString +  , nameSort +  , occNameNameSpace +  , identifierKey +  , nameKey +  , mbIdDetails   +    -- * Syntax manipulation +  , hsGroupVals +  , hsPatSynDetails +  , ieLocNames +    -- * Lookups +  , lookupIdInTypeEnv +  , lookupNameModuleAndPackage +    -- * Location info +  , isHsBoot +  , moduleLocationInfo +  , nameLocationInfo +  , occNameLocationInfo   +  , nameDocumentation   +  , srcSpanToLineAndColNumbers +    -- * Type-related functions +  , tyThingToId   +  , tidyIdentifierType +  , patSynId +  , applyWrapper +  , wrapperTypes   +  , tyVarsOfType   +  , tyConsOfType +  , updateOccNames   +  , mkType +    -- * Documentation processing +  , collectDocs +  , ungroup +  , mkDecls +  , getMainDeclBinder +  , classDeclDocs +  , sigNameNoLoc +  , clsInstDeclSrcSpan +  , hsDocsToDocH +  , subordinateNamesWithDocs +  ) where +import Bag (bagToList) +import ConLike (ConLike(..)) +import qualified Data.ByteString as BS +import Data.Hashable (Hashable,hash) +import qualified Data.ByteString.Internal as BSI +import Data.Char (isAlpha, isAlphaNum, isAscii, ord) +import Data.Either (either) +import Data.Generics (Data) +import Data.Generics.SYB (everything, everywhere, mkQ, mkT) +import qualified Data.Generics.Uniplate.Data() +import qualified Data.HashMap.Strict as HM +import qualified Data.List as L +import Data.Maybe (fromMaybe, isJust, mapMaybe) +import Data.Ord (comparing) +import qualified Data.Text as T +import DataCon (dataConWorkId, flSelector) +import Documentation.Haddock.Parser (overIdentifier, parseParas) +import Documentation.Haddock.Types (DocH(..), Header(..), _doc) +import DynFlags () +import FastString (mkFastString, unpackFS) +import GHC +  ( DynFlags +  , HsDocString(..) +  , InstDecl(..) +  , ModuleName +  , Name +  , SrcSpan(..) +  , RealSrcSpan(..) +  , ClsInstDecl(..) +  , TyClDecl(..)   +  , HsDataDefn(..)   +  , NewOrData(..) +  , Id +  , HsGroup(..)   +  , HsBindLR(..)   +  , HsValBindsLR(..) +  , HsPatSynDetails(..) +  , Located +  , IE(..)   +  , TyThing(..) +  , LHsDecl   +  , HsDecl(..)   +  , DocDecl(..)   +  , ConDecl(..) +  , PostRn   +  , HsConDetails(..)   +  , ConDeclField(..)   +  , DataFamInstDecl(..) +  , LSig   +  , Sig(..)   +  , ForeignDecl(..)  +  , FixitySig(..) +  , tcdName +  , collectHsBindBinders +  , getLoc +  , hsSigType +  , getConNames +  , getConDetails +  , selectorFieldOcc +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)     +  , tyClGroupTyClDecls +  , LIEWrappedName +  , hsGroupInstDecls +  , ieLWrappedName +#else +  , tyClGroupConcat +#endif +  , tyConKind +  , nameSrcSpan +  , srcSpanFile +  , srcSpanStartLine +  , srcSpanEndLine +  , srcSpanStartCol +  , srcSpanEndCol +  , isExternalName +  , moduleNameString +  , recordPatSynSelectorId +  , recordPatSynPatVar +  , isGoodSrcSpan +  , isLocalId +  , isDataFamilyDecl +  , tyFamInstDeclName  +  , idType +  , hsib_body +  , tfe_pats +  , tfid_eqn +  ) +import qualified HaskellCodeExplorer.Types as HCE +import HscTypes (TypeEnv, lookupTypeEnv) +import IdInfo (IdDetails(..)) +import InstEnv (ClsInst(..)) +import Lexer (ParseResult(POk), mkPState, unP) +import Module (Module(..)) +import Name +  ( isDataConNameSpace +  , isDerivedOccName +  , isInternalName +  , isSystemName +  , isTvNameSpace +  , isTyConName +  , isVarNameSpace +  , isWiredInName +  , mkInternalName +  , mkOccName +  , nameModule_maybe +  , nameOccName +  , nameUnique +  , occNameFS +  , occNameSpace +  , occNameString +  , wiredInNameTyThing_maybe +  ) +import OccName (OccName) +import Outputable (Outputable, ppr, showPpr, showSDoc) +import PackageConfig (packageVersion) +import Packages +  ( LookupResult(..) +  , lookupModuleWithSuggestions +  , lookupPackage +  , packageNameString +  ) +import Pair (pSnd) +import Parser (parseIdentifier) +import PatSyn (PatSyn, patSynMatcher, patSynSig) +import Prelude hiding (id, span) +import RdrName (GlobalRdrEnv, RdrName(..), gre_name, lookupGRE_RdrName) +import RnEnv (dataTcOccs) +import SrcLoc (GenLocated(..), mkRealSrcLoc, unLoc) +import StringBuffer (StringBuffer(..), stringToStringBuffer) +import System.FilePath (normalise) +import TcEvidence (HsWrapper(..), tcCoercionKind) +import TcType (evVarPred) +import TyCoRep (Type(..), +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +  ArgFlag(..) +#else +  VisibilityFlag(..) +#endif + ) +import TyCon (tyConName) +import Type +  ( coreView +  , expandTypeSynonyms +  , mkForAllTy +  , mkFunTy +  , mkFunTys +  , mkInvForAllTys +#if !MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +  , mkNamedBinder +#endif   +  , piResultTy   +  , pprSigmaType +  , splitFunTy_maybe +  , tidyOpenType +  ) +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +import ToIface +import IfaceType +#endif +import TysWiredIn (unitTy) +import UniqSet (emptyUniqSet, unionUniqSets, +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +   nonDetEltsUniqSet +#else +   uniqSetToList +#endif + ) +import Unique (getKey) +import Var +  ( idDetails +  , isId +  , mkTyVar +  , setVarType +  , varName +  , varType +  , varUnique +  ) +import VarEnv (TidyEnv) +import VarSet (VarSet, emptyVarSet, unionVarSet, unitVarSet +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +#else + ,varSetElems +#endif + ) + +-------------------------------------------------------------------------------- +-- Pretty-printing +-------------------------------------------------------------------------------- + +toText :: (Outputable a) => DynFlags -> a -> T.Text +toText flags = T.pack . showSDoc flags . ppr + +instanceToText :: DynFlags -> ClsInst -> T.Text +instanceToText flags ClsInst {..} = +  T.append "instance " $ T.pack . showSDoc flags $ pprSigmaType (idType is_dfun) + +instanceDeclToText :: DynFlags -> InstDecl Name -> T.Text +instanceDeclToText flags decl = +  case decl of +    ClsInstD ClsInstDecl {..} -> T.append "instance " (toText flags cid_poly_ty) +    DataFamInstD di -> +      let args = +            T.intercalate " " . map (toText flags) . hsib_body $ dfid_pats di +       in T.concat +            ["data instance ", toText flags (unLoc $ dfid_tycon di), " ", args] +    TyFamInstD ti -> +      let args = +            T.intercalate " " . +            map (toText flags) . hsib_body . tfe_pats . unLoc . tfid_eqn $ +            ti +       in T.concat +            ["type instance ", toText flags $ tyFamInstDeclName ti, " ", args] + +nameToText :: Name -> T.Text +nameToText = T.pack . unpackFS . occNameFS . nameOccName + +tyClDeclPrefix :: TyClDecl a -> T.Text +tyClDeclPrefix tyClDecl = +  let isNewTy :: TyClDecl a -> Bool +      isNewTy DataDecl {tcdDataDefn = HsDataDefn {dd_ND = NewType}} = True +      isNewTy _ = False +   in case tyClDecl of +        FamDecl _ +          | isDataFamilyDecl tyClDecl -> "data family " +          | otherwise -> "type family " +        SynDecl {} -> "type " +        DataDecl {} +          | isNewTy tyClDecl -> "newtype " +          | otherwise -> "data " +        ClassDecl {} -> "class " +     +demangleOccName :: Name -> T.Text +demangleOccName name +  | isDerivedOccName (nameOccName name) = +    let removePrefix :: T.Text -> T.Text +        removePrefix occName +          | T.isPrefixOf "$sel:" occName = +            fst $ T.breakOn ":" (T.drop 5 occName) +          | T.isPrefixOf "$W" occName = T.drop 2 occName +          | T.isPrefixOf "$w" occName = T.drop 2 occName +          | T.isPrefixOf "$m" occName = T.drop 2 occName +          | T.isPrefixOf "$b" occName = T.drop 2 occName +          | T.isPrefixOf "$dm" occName = T.drop 3 occName +          | T.isPrefixOf "$c" occName = T.drop 2 occName +          | T.isPrefixOf "$d" occName = T.drop 2 occName +          | T.isPrefixOf "$i" occName = T.drop 2 occName +          | T.isPrefixOf "$s" occName = T.drop 2 occName +          | T.isPrefixOf "$f" occName = T.drop 2 occName +          | T.isPrefixOf "$r" occName = T.drop 2 occName +          | T.isPrefixOf "C:" occName = T.drop 2 occName +          | T.isPrefixOf "N:" occName = T.drop 2 occName +          | T.isPrefixOf "D:" occName = T.drop 2 occName +          | T.isPrefixOf "$co" occName = T.drop 3 occName +          | otherwise = occName +     in removePrefix $ nameToText name +  | otherwise = nameToText name +     +stringBufferToByteString :: StringBuffer -> BS.ByteString +stringBufferToByteString (StringBuffer buf len cur) = +  BSI.fromForeignPtr buf cur len + +nameSort :: Name -> HCE.NameSort +nameSort n = +  if isExternalName n +    then HCE.External +    else HCE.Internal +   +occNameNameSpace :: OccName -> HCE.NameSpace +occNameNameSpace n +  | isVarNameSpace (occNameSpace n) = HCE.VarName +  | isDataConNameSpace (occNameSpace n) = HCE.DataName +  | isTvNameSpace (occNameSpace n) = HCE.TvName +  | otherwise = HCE.TcClsName + +-- Two 'Id''s may have different types even though they have the same 'Unique'. +identifierKey :: DynFlags -> Id -> T.Text +identifierKey flags id +  | isLocalId id = +    T.concat +      [ T.pack . show . getKey . varUnique $ id +      , "_" +      , T.pack . show . hash . showSDoc flags . ppr . varType $ id +      ] +identifierKey _ id = T.pack . show . getKey . varUnique $ id + +nameKey :: Name -> T.Text +nameKey = T.pack . show . getKey . nameUnique + +mbIdDetails :: Id -> Maybe HCE.IdDetails +mbIdDetails v +  | isId v = +    case idDetails v of +      VanillaId -> Just HCE.VanillaId +      RecSelId {sel_naughty = False} -> Just HCE.RecSelId +      RecSelId {sel_naughty = True} -> Just HCE.RecSelIdNaughty +      DataConWorkId _ -> Just HCE.DataConWorkId +      DataConWrapId _ -> Just HCE.DataConWrapId +      ClassOpId _ -> Just HCE.ClassOpId +      PrimOpId _ -> Just HCE.PrimOpId +      FCallId _ -> Just HCE.FCallId +      TickBoxOpId _ -> Just HCE.TickBoxOpId +      DFunId _ -> Just HCE.DFunId +      CoVarId -> Just HCE.CoVarId +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +      JoinId _ -> Just HCE.JoinId +#endif         +mbIdDetails _ = Nothing + +-------------------------------------------------------------------------------- +--  Syntax transformation +-------------------------------------------------------------------------------- + +hsGroupVals :: HsGroup Name -> [GenLocated SrcSpan (HsBindLR Name Name)] +hsGroupVals hsGroup = +  filter (isGoodSrcSpan . getLoc) $ +  case hs_valds hsGroup of +    ValBindsOut binds _ -> concatMap (bagToList . snd) binds +    _ -> [] + +hsPatSynDetails :: HsPatSynDetails a -> [a] +hsPatSynDetails patDetails = +  case patDetails of +    InfixPatSyn name1 name2 -> [name1, name2] +    PrefixPatSyn name -> name +    RecordPatSyn fields -> +      concatMap +        (\field -> [recordPatSynSelectorId field, recordPatSynPatVar field]) +        fields + +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +unwrapName :: LIEWrappedName Name -> Located Name +unwrapName = ieLWrappedName +#else +unwrapName :: Located Name -> Located Name +unwrapName n = n +#endif + +ieLocNames :: IE Name -> [Located Name] +ieLocNames (IEVar n) = [unwrapName n] +ieLocNames (IEThingAbs n) = [unwrapName n] +ieLocNames (IEThingAll n) = [unwrapName n] +ieLocNames (IEThingWith n _ ns labels) = +  unwrapName n : (map unwrapName ns ++ map (fmap flSelector) labels) +ieLocNames (IEModuleContents (L _ _)) = [] +ieLocNames (IEGroup _ _) = [] +ieLocNames (IEDoc _) = [] +ieLocNames (IEDocNamed _) = [] + +-------------------------------------------------------------------------------- +-- Lookups +-------------------------------------------------------------------------------- + +lookupIdInTypeEnv :: TypeEnv -> Name -> Maybe Id +lookupIdInTypeEnv typeEnv name = do +  let mbTyThing +        | isInternalName name = Nothing +        | isSystemName name = Nothing +        | isWiredInName name = wiredInNameTyThing_maybe name +        | isExternalName name = lookupTypeEnv typeEnv name +        | otherwise = Nothing +  case mbTyThing of +    Just tyThing -> tyThingToId tyThing     +    _ -> Nothing + +lookupNameModuleAndPackage :: +     DynFlags +  -> HCE.PackageId +  -> Name +  -> Either T.Text (HCE.HaskellModuleName, HCE.PackageId) +lookupNameModuleAndPackage flags currentPackageId name = +  case nameModule_maybe name of +    Just Module {..} -> +      case lookupPackage flags moduleUnitId of +        Just packageConfig -> +          let packageId = +                if (T.pack . packageNameString $ packageConfig) == +                   HCE.name (currentPackageId :: HCE.PackageId) +                  then currentPackageId +                  else HCE.PackageId +                         (T.pack $ packageNameString packageConfig) +                         (PackageConfig.packageVersion packageConfig) +           in Right +                ( HCE.HaskellModuleName . T.pack . moduleNameString $ moduleName +                , packageId) +        Nothing -> +          Right +            ( HCE.HaskellModuleName . T.pack . moduleNameString $ moduleName +            , currentPackageId) +    Nothing -> +      Left $ T.concat ["nameModule_maybe ", nameToText name, " is Nothing"] + +-------------------------------------------------------------------------------- +-- Location info +-------------------------------------------------------------------------------- + +isHsBoot :: HCE.HaskellModulePath -> Bool +isHsBoot = T.isSuffixOf "-boot" . HCE.getHaskellModulePath  + +moduleLocationInfo :: +     DynFlags +  -> HM.HashMap HCE.HaskellModuleName (HM.HashMap HCE.ComponentId HCE.HaskellModulePath) +  -> HCE.PackageId +  -> HCE.ComponentId +  -> ModuleName +  -> HCE.LocationInfo +moduleLocationInfo flags moduleNameMap currentPackageId compId moduleName = +  let moduleNameText = T.pack . moduleNameString $ moduleName +      currentPackageLocation = +        HCE.ApproximateLocation +          currentPackageId +          (HCE.HaskellModuleName . T.pack . moduleNameString $ moduleName) +          HCE.Mod +          moduleNameText +          Nothing +          compId +   in case HM.lookup (HCE.HaskellModuleName moduleNameText) moduleNameMap of +        Just modulePathMap +          | Just modulePath <- HM.lookup compId modulePathMap -> +            HCE.ExactLocation +              currentPackageId +              modulePath +              (HCE.HaskellModuleName moduleNameText) +              1 +              1 +              1 +              1 +        _ -> +          case lookupModuleWithSuggestions flags moduleName Nothing of +            LookupFound Module {moduleUnitId = unitId} _ -> +              case lookupPackage flags unitId of +                Just packInfo -> +                  let packageId = +                        HCE.PackageId +                          (T.pack $ packageNameString packInfo) +                          (PackageConfig.packageVersion packInfo) +                   in HCE.ApproximateLocation +                        packageId +                        (HCE.HaskellModuleName . T.pack . moduleNameString $ +                         moduleName) +                        HCE.Mod +                        moduleNameText +                        Nothing +                        (if packageId == currentPackageId +                           then compId +                           else HCE.ComponentId "lib") +                Nothing -> currentPackageLocation +            _ -> currentPackageLocation +              +isDefinedInCurrentModule :: +     HCE.SourceCodeTransformation -> HCE.HaskellFilePath -> Bool +isDefinedInCurrentModule transformation file = +  let includedFiles = HM.keys $ HCE.fileIndex transformation +      modPath = +        HCE.getHaskellModulePath $ +        HCE.filePath (transformation :: HCE.SourceCodeTransformation) +   in HCE.getHaskellFilePath file == modPath || (file `elem` includedFiles) +       +nameLocationInfo :: +     DynFlags +  -> HCE.PackageId +  -> HCE.ComponentId +  -> HCE.SourceCodeTransformation +  -> HM.HashMap HCE.HaskellFilePath HCE.HaskellModulePath +  -> HM.HashMap HCE.HaskellModulePath HCE.DefinitionSiteMap +  -> Maybe T.Text -- ^ Instance head (when name is a dictionary function) +  -> Maybe SrcSpan -- ^ Only for wired-in names +  -> Name +  -> HCE.LocationInfo +nameLocationInfo flags currentPackageId compId transformation fileMap defSiteMap mbInstanceHead mbSrcSpan name +  | Just srcSpan <- realSrcSpan name mbSrcSpan = +    let filePath = +          HCE.HaskellFilePath . T.pack . normalise . unpackFS . srcSpanFile $ +          srcSpan +        approximateLocation = +          mkApproximateLocation +            flags +            currentPackageId +            compId +            mbInstanceHead +            name +     in if isDefinedInCurrentModule transformation filePath +          then let eitherStart = +                     HCE.fromOriginalLineNumber +                       transformation +                       (filePath, srcSpanStartLine srcSpan) +                   eitherEnd = +                     HCE.fromOriginalLineNumber +                       transformation +                       (filePath, srcSpanEndLine srcSpan)                       +                in case (,) eitherStart eitherEnd of +                     (Right startLine,Right endLine) -> +                         let  modulePath = HCE.filePath (transformation :: HCE.SourceCodeTransformation) +                              moduleName = +                                  either +                                    (const $ HCE.HaskellModuleName "") +                                    fst +                                    (lookupNameModuleAndPackage flags currentPackageId name) +                         in HCE.ExactLocation +                              { packageId = currentPackageId +                              , modulePath = modulePath +                              , moduleName = moduleName +                              , startLine = startLine +                              , endLine = endLine +                              , startColumn = srcSpanStartCol srcSpan +                              , endColumn = srcSpanEndCol srcSpan +                              } +                     _ -> approximateLocation +          else case HM.lookup filePath fileMap of +                 Just haskellModulePath -> +                   case HM.lookup haskellModulePath defSiteMap of +                     Just defSites -> +                       let key = fromMaybe (nameToText name) mbInstanceHead +                        in lookupEntityLocation +                             defSites +                             (mkLocatableEntity name mbInstanceHead) +                             key +                     Nothing -> approximateLocation +                 Nothing -> approximateLocation +  where +    realSrcSpan :: Name -> Maybe SrcSpan -> Maybe RealSrcSpan +    realSrcSpan name mbSrcSpan = +      case nameSrcSpan name of +        RealSrcSpan span -> Just span +        _ +          | isWiredInName name -> +            case mbSrcSpan of +              Just span -> +                case span of +                  RealSrcSpan s -> Just s +                  _ -> Nothing +              _ -> Nothing +        _ -> Nothing +nameLocationInfo flags currentPackageId compId _transformation _fileMap _defSiteMap mbInstanceHead _mbSrcSpan name = +  mkApproximateLocation flags currentPackageId compId mbInstanceHead name + +mkApproximateLocation :: +     DynFlags +  -> HCE.PackageId +  -> HCE.ComponentId +  -> Maybe T.Text +  -> Name +  -> HCE.LocationInfo +mkApproximateLocation flags currentPackageId compId mbInstanceHead name = +  let haddockAnchor = +        Just . T.pack . makeAnchorId . T.unpack . nameToText $ name +   in case lookupNameModuleAndPackage flags currentPackageId name of +        Right (moduleName, packageId) -> +          HCE.ApproximateLocation +            { moduleName = moduleName +            , packageId = packageId +            , componentId = +                if packageId == currentPackageId +                  then compId +                  else HCE.ComponentId "lib" +            , entity = mkLocatableEntity name mbInstanceHead +            , haddockAnchorId = haddockAnchor +            , name = fromMaybe (nameToText name) mbInstanceHead +            } +        Left errorMessage -> HCE.UnknownLocation errorMessage + +mkLocatableEntity :: Name -> Maybe a -> HCE.LocatableEntity +mkLocatableEntity name mbInstanceHead +  | isJust mbInstanceHead = HCE.Inst +  | otherwise = +    case occNameNameSpace . nameOccName $ name of +      HCE.VarName -> HCE.Val +      HCE.DataName -> HCE.Val +      _ -> HCE.Typ         + +occNameLocationInfo :: +     DynFlags +  -> HCE.PackageId +  -> HCE.ComponentId +  -> (ModuleName, OccName) +  -> HCE.LocationInfo +occNameLocationInfo flags packageId componentId (modName, occName) = +  HCE.ApproximateLocation +    { packageId = packageId +    , moduleName = HCE.HaskellModuleName $ toText flags modName +    , entity = +        case occNameNameSpace occName of +          HCE.VarName -> HCE.Val +          HCE.DataName -> HCE.Val +          _ -> HCE.Typ +    , name = toText flags occName +    , haddockAnchorId = +        Just . T.pack . makeAnchorId . T.unpack $ toText flags occName +    , componentId = componentId +    } + +lookupEntityLocation :: +     HCE.DefinitionSiteMap -> HCE.LocatableEntity -> T.Text -> HCE.LocationInfo +lookupEntityLocation defSiteMap locatableEntity text = +  let errorMessage = +        T.concat +          [ "Cannot find location of " +          , T.pack . show $ locatableEntity +          , " " +          , text +          ] +      defSiteLocation = HCE.location :: HCE.DefinitionSite -> HCE.LocationInfo +      lookupLocation :: +           (Eq a, Hashable a) +        => (HCE.DefinitionSiteMap -> HM.HashMap a HCE.DefinitionSite) +        -> (T.Text -> a) +        -> HCE.LocationInfo +      lookupLocation selector toKey = +        maybe (HCE.UnknownLocation errorMessage) defSiteLocation $ +        HM.lookup (toKey text) (selector defSiteMap) +   in case locatableEntity of +        HCE.Val -> lookupLocation HCE.values HCE.OccName +        HCE.Typ -> lookupLocation HCE.types HCE.OccName +        HCE.Inst -> lookupLocation HCE.instances (\t -> t) +        HCE.Mod -> HCE.UnknownLocation errorMessage + +nameDocumentation :: +     HCE.SourceCodeTransformation +  -> HM.HashMap HCE.HaskellFilePath HCE.HaskellModulePath +  -> HM.HashMap HCE.HaskellModulePath HCE.DefinitionSiteMap +  -> HCE.DefinitionSiteMap +  -> Name +  -> Maybe T.Text +nameDocumentation transformation fileMap defSiteMap currentModuleDefSiteMap name +  | isExternalName name || isWiredInName name +  , Just file <- srcSpanToFilePath . nameSrcSpan $ name = +    if isDefinedInCurrentModule transformation file +      then lookupNameDocumentation name currentModuleDefSiteMap +      else case HM.lookup file fileMap of +             Just haskellModulePath -> +               case HM.lookup haskellModulePath defSiteMap of +                 Just defSites -> lookupNameDocumentation name defSites +                 Nothing -> Nothing +             Nothing -> Nothing +nameDocumentation _ _ _ _ _ = Nothing + +lookupNameDocumentation :: Name -> HCE.DefinitionSiteMap -> Maybe T.Text +lookupNameDocumentation name defSiteMap = +  let key = HCE.OccName $ nameToText name +      lookupDoc :: +           (HCE.DefinitionSiteMap -> HM.HashMap HCE.OccName HCE.DefinitionSite) +        -> Maybe T.Text +      lookupDoc selector = +        maybe Nothing HCE.documentation $ +        HM.lookup key (selector (defSiteMap :: HCE.DefinitionSiteMap)) +   in case occNameNameSpace . nameOccName $ name of +        HCE.VarName -> lookupDoc HCE.values +        HCE.DataName -> lookupDoc HCE.values +        _ -> lookupDoc HCE.types + +srcSpanToFilePath :: SrcSpan -> Maybe HCE.HaskellFilePath +srcSpanToFilePath (RealSrcSpan s) = +  Just . HCE.HaskellFilePath . T.pack . normalise . unpackFS . srcSpanFile $ s    +srcSpanToFilePath (UnhelpfulSpan _) = Nothing +    +srcSpanToLineAndColNumbers :: +     HCE.SourceCodeTransformation +  -> SrcSpan +  -> Maybe (HCE.HaskellFilePath, (Int, Int), (Int, Int)) +srcSpanToLineAndColNumbers transformation (RealSrcSpan s) = +  let filePath = +        HCE.HaskellFilePath . T.pack . normalise . unpackFS . srcSpanFile $ s +      eitherStart = +        HCE.fromOriginalLineNumber transformation (filePath, srcSpanStartLine s) +      eitherEnd = +        HCE.fromOriginalLineNumber transformation (filePath, srcSpanEndLine s) +   in case (,) eitherStart eitherEnd of +        (Right startLine, Right endLine) -> +          Just +            ( filePath +            , (startLine, srcSpanStartCol s) +            , (endLine, srcSpanEndCol s)) +        _ -> Nothing +srcSpanToLineAndColNumbers _ _ = Nothing + +-------------------------------------------------------------------------------- +-- Type-related functions +-------------------------------------------------------------------------------- + +tyThingToId :: TyThing -> Maybe Id +tyThingToId tyThing = +  case tyThing of +    AnId id -> Just id +    ATyCon tc -> Just $ mkTyVar (tyConName tc) (tyConKind tc) +    AConLike con -> +      case con of +        RealDataCon dataCon -> Just $ dataConWorkId dataCon +        PatSynCon ps -> Just $ patSynId ps +    ACoAxiom _ -> Nothing + +tidyIdentifierType :: TidyEnv -> Id -> (TidyEnv, Id) +tidyIdentifierType tidyEnv identifier = +  let (tidyEnv', typ') = tidyOpenType tidyEnv (varType identifier) +   in (tidyEnv', setVarType identifier typ') + +patSynId :: PatSyn -> Id +patSynId patSyn = +  let (univTvs, reqTheta, exTvs, provTheta, argTys, resTy) = patSynSig patSyn +      reqTheta' +        | null reqTheta && not (null provTheta && null exTvs) = [unitTy] +        | otherwise = reqTheta +      --  required => provided => arg_1 -> ... -> arg_n -> res +      patSynTy = +        mkInvForAllTys univTvs $ +        mkFunTys reqTheta' $ +        mkInvForAllTys exTvs $ mkFunTys provTheta $ mkFunTys argTys resTy +   in flip setVarType patSynTy . fst . patSynMatcher $ patSyn + +applyWrapper :: HsWrapper -> Type -> Type +applyWrapper wp ty +  | Just ty' <- coreView ty = applyWrapper wp ty' +applyWrapper WpHole t = t +applyWrapper (WpCompose w1 w2) t = applyWrapper w1 . applyWrapper w2 $ t +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +applyWrapper (WpFun w1 w2 t1 _doc) t = mkFunTy t1 (applyWrapper w2 $ piResultTy t (applyWrapper w1 t1)) +#else +applyWrapper (WpFun w1 w2 t1) t = mkFunTy t1 (applyWrapper w2 $ piResultTy t (applyWrapper w1 t1)) +#endif +applyWrapper (WpCast coercion) _t = pSnd $ tcCoercionKind coercion +applyWrapper (WpEvLam v) t = mkFunTy (evVarPred v) t +applyWrapper (WpEvApp _ev) t = case splitFunTy_maybe t of +  Just (_arg,res) -> res +  Nothing -> t +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)   +applyWrapper (WpTyLam v) t = mkForAllTy v Required t +#else +applyWrapper (WpTyLam v) t = mkForAllTy (mkNamedBinder Invisible v) t +#endif +applyWrapper (WpTyApp t') t = piResultTy t t' +applyWrapper (WpLet _) t = t + +wrapperTypes :: HsWrapper -> [Type] +wrapperTypes WpHole  = [] +wrapperTypes (WpCompose w1 w2) = wrapperTypes w2 ++ wrapperTypes w1 +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +wrapperTypes (WpFun w1 w2 _ _) = wrapperTypes w2 ++ wrapperTypes w1 +#else +wrapperTypes (WpFun w1 w2 _) = wrapperTypes w2 ++ wrapperTypes w1 +#endif +wrapperTypes (WpCast _)  = [] +wrapperTypes (WpEvLam _) = [] +wrapperTypes (WpEvApp _) = [] +wrapperTypes (WpTyLam _) = [] +wrapperTypes (WpTyApp t) = [t] +wrapperTypes (WpLet _) = [] + +mkType :: DynFlags -> Type -> HCE.Type +mkType flags typ = +  let typeExpanded = expandTypeSynonyms typ +      typeComponents = toTypeComponents flags typ +      typeComponentsExpanded = toTypeComponents flags typeExpanded +   in HCE.Type +        typeComponents +        (if typeComponents /= typeComponentsExpanded +           then Just typeComponentsExpanded +           else Nothing) + +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +typeToText :: DynFlags -> Type -> T.Text     +typeToText flags = T.pack . showSDoc flags . pprIfaceType . toIfaceType +#else +typeToText :: DynFlags -> Type -> T.Text     +typeToText = toText +#endif + +toTypeComponents :: DynFlags -> Type -> [HCE.TypeComponent] +toTypeComponents flags typ = +  let signature = +        typeToText flags $ +        updateOccNames (\_unique occName -> ";" ++ drop 2 occName ++ ";") typ +      -- Signature with OccNames and uniques +      signatureWithUniques = +        typeToText flags $ +        updateOccNames +          (\unique occName -> ";," ++ occName ++ "," ++ unique ++ ";") +          typ +      -- Dirty but simple way to extract a list of TypeComponent from a type signature.       +      -- Assumptions : +      -- 1. Character ';' cannot appear anywhere in a type signature +      -- 2. Character ',' cannot appear in an 'OccName' +      -- 3. length (T.splitOn ";" signature) == length (T.splitOn ";" signatureWithUniques) +      components = +        L.zip (T.splitOn ";" signature) (T.splitOn ";" signatureWithUniques) +   in mapMaybe +        (\(text1, text2) -> +           if T.isPrefixOf "," text2 +             then case T.splitOn "," text2 of +                    ["", name, id] -> +                      Just HCE.TyCon {name = name, internalId = HCE.InternalId id} +                    _ -> Just $ HCE.Text text1 +             else if T.null text1 +                    then Nothing +                    else Just $ HCE.Text text1) +        components + +-- | Replaces 'OccName' of each type variable and type constructor in a type. +updateOccNames :: (String -> String -> String) -> Type -> Type +updateOccNames update = everywhere (mkT updateType) +  where +    updateType :: Type -> Type +    updateType (TyVarTy var) = TyVarTy var {varName = updateName (varName var)} +    updateType (TyConApp con args) = +      TyConApp (con {tyConName = updateName (tyConName con)}) args +    updateType other = other +    updateName :: Name -> Name +    updateName oldName = +      let oldOccName = nameOccName oldName +          unique = T.unpack $ nameKey oldName +          newOccName = +            mkOccName +              (occNameSpace oldOccName) +              (update unique (occNameString oldOccName)) +       in mkInternalName (nameUnique oldName) newOccName (nameSrcSpan oldName) +           +-- | This function doesn't look through type synonyms +tyConsOfType :: Type -> [Id] +tyConsOfType = +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)   +  nonDetEltsUniqSet . everything unionUniqSets (emptyVarSet `mkQ` tyCon) +#else +  uniqSetToList . everything unionUniqSets (emptyVarSet `mkQ` tyCon) +#endif +  where +    tyCon :: Type -> VarSet +    tyCon (TyConApp tc _) = unitVarSet $ mkTyVar (tyConName tc) (tyConKind tc) +    tyCon _ = emptyUniqSet + +tyVarsOfType :: (Data a) => a -> [Id] +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)   +tyVarsOfType = nonDetEltsUniqSet . everything unionVarSet (emptyVarSet `mkQ` tyVar) +#else +tyVarsOfType = varSetElems . everything unionVarSet (emptyVarSet `mkQ` tyVar) +#endif +  where +    tyVar :: Type -> VarSet +    tyVar (TyVarTy ty) = unitVarSet ty +    tyVar _ = emptyVarSet + +-------------------------------------------------------------------------------- +-- Documentation processing +-- Some functions are copied from haddock-api package +-------------------------------------------------------------------------------- + +collectDocs :: [LHsDecl a] -> [(LHsDecl a, [HsDocString])] +collectDocs = go Nothing [] +  where +    go Nothing _ [] = [] +    go (Just prev) docs [] = finished prev docs [] +    go prev docs (L _ (DocD (DocCommentNext str)):ds) +      | Nothing <- prev = go Nothing (str : docs) ds +      | Just decl <- prev = finished decl docs (go Nothing [str] ds) +    go prev docs (L _ (DocD (DocCommentPrev str)):ds) = go prev (str : docs) ds +    go Nothing docs (d:ds) = go (Just d) docs ds +    go (Just prev) docs (d:ds) = finished prev docs (go (Just d) [] ds) +    finished decl docs rest = (decl, reverse docs) : rest + +ungroup :: HsGroup Name -> [LHsDecl Name] +ungroup group_ = +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +  mkDecls (tyClGroupTyClDecls . hs_tyclds) TyClD group_ ++   +#else +  mkDecls (tyClGroupConcat . hs_tyclds) TyClD group_ ++ +#endif +  mkDecls hs_derivds DerivD group_ ++ +  mkDecls hs_defds DefD group_ ++ +  mkDecls hs_fords ForD group_ ++ +  mkDecls hs_docs DocD group_ ++ +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)   +  mkDecls hsGroupInstDecls InstD group_ ++ +#else +  mkDecls hs_instds InstD group_ ++ +#endif +  mkDecls (typesigs . hs_valds) SigD group_ ++ +  mkDecls (valbinds . hs_valds) ValD group_ +  where +    typesigs (ValBindsOut _ sigs) = filter isUserLSig sigs +    typesigs _ = [] +    valbinds (ValBindsOut binds _) = concatMap bagToList . snd . unzip $ binds +    valbinds _ = [] + +mkDecls :: (a -> [Located b]) -> (b -> c) -> a -> [Located c] +mkDecls field con struct = [L loc (con decl) | L loc decl <- field struct] + +sortByLoc :: [Located a] -> [Located a] +sortByLoc = L.sortBy (comparing getLoc) + +classDeclDocs :: TyClDecl Name -> [(LHsDecl Name, [HsDocString])] +classDeclDocs class_ = collectDocs . sortByLoc $ decls +  where +    decls = docs ++ defs ++ sigs ++ ats +    docs = mkDecls tcdDocs DocD class_ +    defs = mkDecls (bagToList . tcdMeths) ValD class_ +    sigs = mkDecls tcdSigs SigD class_ +    ats = mkDecls tcdATs (TyClD . FamDecl) class_ + +conDeclDocs :: ConDecl Name -> [(Name, [HsDocString], SrcSpan)] +conDeclDocs conDecl = +  map (\(L span n) -> (n, maybe [] ((: []) . unLoc) $ con_doc conDecl, span)) . +  getConNames $ +  conDecl + +selectorDocs :: ConDecl name -> [(PostRn name name, [HsDocString], SrcSpan)] +selectorDocs con = +  case getConDetails con of +    RecCon (L _ flds) -> +      concatMap +        (\(L _ (ConDeclField fieldOccs _ mbDoc)) -> +           map +             (\(L span f) -> +                (selectorFieldOcc f, maybe [] ((: []) . unLoc) mbDoc, span)) +             fieldOccs) +        flds +    _ -> [] + +subordinateNamesWithDocs :: +     [GenLocated SrcSpan (HsDecl Name)] -> [(Name, [HsDocString], SrcSpan)] +subordinateNamesWithDocs = +  concatMap +    (\(L span tyClDecl) -> +       case tyClDecl of +         TyClD classDecl@ClassDecl {..} -> +           concatMap +             (\(L _ decl, docs) -> map (, docs, span) $ getMainDeclBinder decl) $ +           classDeclDocs classDecl +         TyClD DataDecl {..} -> +           concatMap (\(L _ con) -> conDeclDocs con ++ selectorDocs con) $ +           dd_cons tcdDataDefn +         InstD (DataFamInstD DataFamInstDecl {..}) -> +           concatMap (conDeclDocs . unLoc) . dd_cons $ dfid_defn +         _ -> []) +     +isUserLSig :: LSig name -> Bool +isUserLSig (L _ TypeSig {})    = True +isUserLSig (L _ ClassOpSig {}) = True +isUserLSig _ = False + +getMainDeclBinder :: HsDecl name -> [name] +getMainDeclBinder (TyClD d) = [tcdName d] +getMainDeclBinder (ValD d) = +  case collectHsBindBinders d of +    [] -> [] +    (name:_) -> [name] +getMainDeclBinder (SigD d) = sigNameNoLoc d +getMainDeclBinder (ForD (ForeignImport name _ _ _)) = [unLoc name] +getMainDeclBinder (ForD ForeignExport {}) = [] +getMainDeclBinder _ = [] + +sigNameNoLoc :: Sig name -> [name] +sigNameNoLoc (TypeSig      ns _)       = map unLoc ns +sigNameNoLoc (ClassOpSig _ ns _)       = map unLoc ns +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +sigNameNoLoc (PatSynSig    ns _)        = map unLoc ns +#else +sigNameNoLoc (PatSynSig    n _)        = [unLoc n] +#endif +sigNameNoLoc (SpecSig      n _ _)      = [unLoc n] +sigNameNoLoc (InlineSig    n _)        = [unLoc n] +sigNameNoLoc (FixSig (FixitySig ns _)) = map unLoc ns +sigNameNoLoc _                         = [] + +clsInstDeclSrcSpan :: ClsInstDecl name -> SrcSpan +clsInstDeclSrcSpan ClsInstDecl {cid_poly_ty = ty} = getLoc (hsSigType ty) + +hsDocsToDocH :: DynFlags -> GlobalRdrEnv -> [HsDocString] -> Doc Name +hsDocsToDocH flags rdrEnv = +  rename flags rdrEnv . +  overIdentifier (parseIdent flags) . +  _doc . parseParas . concatMap (unpackFS . (\(HsDocString s) -> s)) + +parseIdent :: DynFlags -> String -> Maybe RdrName +parseIdent dflags str0 = +  let buffer = stringToStringBuffer str0 +      realSrcLc = mkRealSrcLoc (mkFastString "<unknown file>") 0 0 +      pstate = mkPState dflags buffer realSrcLc +  in case unP parseIdentifier pstate of +    POk _ name -> Just (unLoc name) +    _ -> Nothing + +type Doc id = DocH (ModuleName, OccName) id + +rename :: DynFlags -> GlobalRdrEnv -> Doc RdrName -> Doc Name +rename dflags gre = rn +  where +    rn d = case d of +      DocAppend a b -> DocAppend (rn a) (rn b) +      DocParagraph doc -> DocParagraph (rn doc) +      DocIdentifier x -> do +        -- Generate the choices for the possible kind of thing this +        -- is. +        let choices = dataTcOccs x +        -- Try to look up all the names in the GlobalRdrEnv that match +        -- the names. +        let names = concatMap (\c -> map gre_name (lookupGRE_RdrName c gre)) choices + +        case names of +          -- We found no names in the env so we start guessing. +          [] -> +            case choices of +              [] -> DocMonospaced (DocString (showPpr dflags x)) +              -- There was nothing in the environment so we need to +              -- pick some default from what's available to us. We +              -- diverge here from the old way where we would default +              -- to type constructors as we're much more likely to +              -- actually want anchors to regular definitions than +              -- type constructor names (such as in #253). So now we +              -- only get type constructor links if they are actually +              -- in scope. +              a:_ -> outOfScope dflags a + +          -- There is only one name in the environment that matches so +          -- use it. +          [a] -> DocIdentifier a +          -- But when there are multiple names available, default to +          -- type constructors: somewhat awfully GHC returns the +          -- values in the list positionally. +          a:b:_ | isTyConName a -> DocIdentifier a +                | otherwise -> DocIdentifier b + +      DocWarning doc -> DocWarning (rn doc) +      DocEmphasis doc -> DocEmphasis (rn doc) +      DocBold doc -> DocBold (rn doc) +      DocMonospaced doc -> DocMonospaced (rn doc) +      DocUnorderedList docs -> DocUnorderedList (map rn docs) +      DocOrderedList docs -> DocOrderedList (map rn docs) +      DocDefList list -> DocDefList [ (rn a, rn b) | (a, b) <- list ] +      DocCodeBlock doc -> DocCodeBlock (rn doc) +      DocIdentifierUnchecked x -> DocIdentifierUnchecked x +      DocModule str -> DocModule str +      DocHyperlink l -> DocHyperlink l +      DocPic str -> DocPic str +      DocMathInline str -> DocMathInline str +      DocMathDisplay str -> DocMathDisplay str +      DocAName str -> DocAName str +      DocProperty p -> DocProperty p +      DocExamples e -> DocExamples e +      DocEmpty -> DocEmpty +      DocString str -> DocString str +      DocHeader (Header l t) -> DocHeader $ Header l (rn t) + +-- | Wrap an identifier that's out of scope (i.e. wasn't found in +-- 'GlobalReaderEnv' during 'rename') in an appropriate doc. Currently +-- we simply monospace the identifier in most cases except when the +-- identifier is qualified: if the identifier is qualified then we can +-- still try to guess and generate anchors accross modules but the +-- users shouldn't rely on this doing the right thing. See tickets +-- #253 and #375 on the confusion this causes depending on which +-- default we pick in 'rename'. +outOfScope :: DynFlags -> RdrName -> Doc a +outOfScope dflags x = +  case x of +    Unqual occ -> monospaced occ +    Qual mdl occ -> DocIdentifierUnchecked (mdl, occ) +    Orig _ occ -> monospaced occ +    Exact name -> monospaced name -- Shouldn't happen since x is out of scope +  where +    monospaced a = DocMonospaced (DocString (showPpr dflags a)) + +makeAnchorId :: String -> String +makeAnchorId [] = [] +makeAnchorId (f:r) = escape isAlpha f ++ concatMap (escape isLegal) r +  where +    escape p c | p c = [c] +               | otherwise = '-' : show (ord c) ++ "-" +    isLegal ':' = True +    isLegal '_' = True +    isLegal '.' = True +    isLegal c = isAscii c && isAlphaNum c     diff --git a/src/HaskellCodeExplorer/ModuleInfo.hs b/src/HaskellCodeExplorer/ModuleInfo.hs new file mode 100644 index 0000000..cc81a36 --- /dev/null +++ b/src/HaskellCodeExplorer/ModuleInfo.hs @@ -0,0 +1,811 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE StrictData #-} + +module HaskellCodeExplorer.ModuleInfo +  ( createModuleInfo +  , ModuleDependencies +  ) where + +import qualified Data.Generics.Uniplate.Data as U +import Control.Monad.State.Strict (execState,evalState,get,put,State) +import qualified Data.Aeson as Aeson +import Data.Aeson.Text(encodeToLazyText) +import qualified Data.Vector as V +import qualified Data.HashMap.Strict as HM +import qualified Data.Map.Strict as M +import qualified Data.IntMap.Strict as IM +import qualified Data.IntervalMap.Strict as IVM +import qualified Data.List as L hiding (span) +import Data.Maybe(fromMaybe,mapMaybe) +import Data.Ord(comparing) +import qualified Data.Set as S +import qualified Data.Text as T +import qualified Data.Text.Encoding as TE +import Data.Text.Lazy (toStrict) +import Documentation.Haddock.Types (DocH) +import DynFlags(DynFlags) +import GHC +  ( GenLocated(..) +  , ModSummary +  , ModuleInfo +  , ModuleName +  , SrcSpan +  , TyThing(..) +  , Type +  , TypecheckedModule +  , getLoc +  , isGoodSrcSpan +  , modInfoExportsWithSelectors +  , modInfoInstances +  , moduleInfo +  , moduleNameString +  , ms_hspp_buf +  , ms_mod +  , renamedSource +  , tm_internals_ +  , tm_typechecked_source +  , unLoc +  ) +import Type(expandTypeSynonyms) +import TyCon (isFamInstTyCon,tyConName) +import HaskellCodeExplorer.AST.RenamedSource +import HaskellCodeExplorer.AST.TypecheckedSource +import HaskellCodeExplorer.GhcUtils +import HaskellCodeExplorer.Preprocessor (createSourceCodeTransformation) +import qualified HaskellCodeExplorer.Types as HCE +import HsBinds(HsBindLR) +import HsDecls +  ( ForeignDecl(..) +  , HsDecl(..) +  , HsGroup(..) +  , InstDecl +  , InstDecl(..) +  , TyClDecl +  , group_tyclds +  , tyClDeclLName +  , tcdName +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +  , hsGroupInstDecls +#endif +  ) +import HsDoc(HsDocString) +import HsImpExp (IE(..), ImportDecl(..)) +import HsUtils(collectHsBindBinders) +import HscTypes +  ( ExternalPackageState +  , HomePackageTable +  , TypeEnv +  , eps_PTE +  , eps_inst_env +  , hm_details +  , md_types +  , mkTypeEnv +  , typeEnvElts +  ) +import InstEnv (InstEnvs(..), is_dfun) +import Module(Module(..)) +import Name (Name, OccName, getSrcSpan, nameOccName, nameSrcSpan, nameUnique) +import Prelude hiding(id,span) +import RdrName(GlobalRdrEnv) +import SrcLoc (isOneLineSpan)   +import TcRnTypes (tcVisibleOrphanMods, tcg_inst_env, tcg_rdr_env, tcg_type_env) +import qualified Text.Blaze.Html5 as H  +import qualified Text.Blaze.Html5.Attributes as A +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0) +import UniqDFM (eltsUDFM) +#else +import UniqFM (eltsUFM) +#endif +import Unique (getKey) +import Var (varName, varType,Id) +import VarEnv (emptyTidyEnv) + +type ModuleDependencies +   = ( HM.HashMap HCE.HaskellFilePath HCE.HaskellModulePath +     , HM.HashMap HCE.HaskellModulePath HCE.DefinitionSiteMap +     , HM.HashMap HCE.HaskellModuleName (HM.HashMap HCE.ComponentId HCE.HaskellModulePath)) +   +type ModuleGhcData +   = ( DynFlags +     , TypecheckedModule +     , HomePackageTable +     , ExternalPackageState +     , ModSummary) +  +createModuleInfo :: +     ModuleDependencies -- ^ Modules that have already been indexed +  -> ModuleGhcData -- ^ Data types from GHC +  -> HCE.HaskellModulePath -- ^ Current module path +  -> HCE.PackageId -- ^ Current package id +  -> HCE.ComponentId -- ^ Current build component id +  -> (T.Text, HCE.SourceCodePreprocessing) -- ^ Source code +  -> (HCE.ModuleInfo, ModuleDependencies, [TypeError]) +createModuleInfo (fileMap, defSiteMap, moduleNameMap) (flags, typecheckedModule, homePackageTable, externalPackageState, modSum) modulePath currentPackageId compId (originalSourceCode, sourceCodePreprocessing) = +  let globalRdrEnv = tcg_rdr_env . fst . tm_internals_ $ typecheckedModule +      modInfo = moduleInfo typecheckedModule +      (Just (hsGroup, _, _, _)) = renamedSource typecheckedModule +      exportedNamesSet = S.fromList $ modInfoExportsWithSelectors modInfo +      -------------------------------------------------------------------------------- +      -- Preprocessed source +      -------------------------------------------------------------------------------- +      (transformation, sourceCode') = +        prepareSourceCode +          sourceCodePreprocessing +          originalSourceCode +          modSum +          modulePath +      includedFiles = HM.keys $ HCE.fileIndex transformation +      -------------------------------------------------------------------------------- +      -- Type environment +      -------------------------------------------------------------------------------- +      (tcGblEnv, _) = tm_internals_ typecheckedModule +      currentModuleTyThings = typeEnvElts $ tcg_type_env tcGblEnv +      homePackageTyThings = +        concatMap (typeEnvElts . md_types . hm_details) $ +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)         +        eltsUDFM homePackageTable +#else +        eltsUFM homePackageTable +#endif +      externalPackagesTyThings = typeEnvElts $ eps_PTE externalPackageState +      typeEnv = +        mkTypeEnv +          (currentModuleTyThings ++ +           homePackageTyThings ++ externalPackagesTyThings) +      -------------------------------------------------------------------------------- +      -- Exported entities +      -------------------------------------------------------------------------------- +      dataFamTyCons = +        mapMaybe +          (\case +             ATyCon tc +               | isFamInstTyCon tc -> Just $ tyConName tc +             _ -> Nothing) +          currentModuleTyThings +      (defSites, allNames) = +        createDefinitionSiteMap +          flags +          currentPackageId +          compId +          defSiteMap +          fileMap           +          globalRdrEnv +          transformation +          modInfo +          dataFamTyCons +          hsGroup +      -------------------------------------------------------------------------------- +      -- Instance environment +      -------------------------------------------------------------------------------- +      homeInstEnv = tcg_inst_env tcGblEnv +      visOrphanModules = tcVisibleOrphanMods tcGblEnv +      packageInstEnv = eps_inst_env externalPackageState +      instEnv = InstEnvs packageInstEnv homeInstEnv visOrphanModules +      -------------------------------------------------------------------------------- +      declarations = +        createDeclarations flags hsGroup typeEnv exportedNamesSet transformation +      environment = +        Environment +          { envDynFlags = flags +          , envInstEnv = instEnv +          , envTypeEnv = typeEnv +          , envTransformation = transformation +          , envCurrentModuleDefSites = defSites +          , envFileMap = fileMap +          , envDefSiteMap = defSiteMap +          , envModuleNameMap = moduleNameMap +          , envPackageId = currentPackageId +          , envComponentId = compId +          , envExportedNames = exportedNamesSet +          } +      externalIds = +        L.foldl' +          (\acc name -> +             maybe +               acc +               (\id -> (HCE.ExternalIdentifierInfo $ mkIdentifierInfo environment id (Just name)) : acc) +               (lookupIdInTypeEnv typeEnv name)) +          [] +          allNames +      currentModuleName = +        (\(Module _ name) -> +           HCE.HaskellModuleName . T.pack . moduleNameString $ name) . +        ms_mod $ +        modSum +      SourceInfo {..} = foldAST environment typecheckedModule +   in (tidyInternalIds HCE.ModuleInfo +          { id = modulePath +          , transformation = transformation +          , name = currentModuleName +          , declarations = declarations +          , exprInfoMap = sourceInfoExprMap +          , idInfoMap = sourceInfoIdMap +          , idOccMap = sourceInfoIdOccMap +          , definitionSiteMap = defSites +          , source = V.fromList . T.splitOn "\n" $ sourceCode' +          , externalIds = externalIds +          } +      , if not $ isHsBoot modulePath +          then  (HM.union +                   (HM.fromList . +                    (( HCE.HaskellFilePath $ HCE.getHaskellModulePath modulePath +                     , modulePath) :) . +                    map (\includedFile -> (includedFile, modulePath)) $ +                    includedFiles) +                   fileMap +               , HM.union (HM.singleton modulePath defSites) defSiteMap +               , HM.insertWith HM.union currentModuleName +                   (HM.singleton compId modulePath) moduleNameMap) +          else (fileMap, defSiteMap, moduleNameMap) +       , sourceInfoTypeErrors) + +data SourceInfo = SourceInfo +  { sourceInfoExprMap :: HCE.ExpressionInfoMap +  , sourceInfoIdMap :: HCE.IdentifierInfoMap +  , sourceInfoIdOccMap :: HCE.IdentifierOccurrenceMap +  , sourceInfoTypeErrors :: [TypeError] +  } deriving (Show, Eq) + +tidyInternalIds :: HCE.ModuleInfo -> HCE.ModuleInfo +tidyInternalIds modInfo = evalState (U.transformBiM tidy modInfo) (HM.empty, 0) +  where +    tidy :: +         HCE.InternalId -> State (HM.HashMap T.Text T.Text, Int) HCE.InternalId +    tidy (HCE.InternalId text) = do +      (hmap, number) <- get +      case HM.lookup text hmap of +        Just val -> return $ HCE.InternalId val +        Nothing -> do +          let nextInternalId = T.pack . show $ number +          put (HM.insert text nextInternalId hmap, number + 1) +          return $ HCE.InternalId nextInternalId + +prepareSourceCode :: +     HCE.SourceCodePreprocessing +  -> T.Text +  -> ModSummary +  -> HCE.HaskellModulePath +  -> (HCE.SourceCodeTransformation, T.Text) +prepareSourceCode sourceCodePreprocessing originalSourceCode modSum modulePath = +  let sourceCodeAfterPreprocessing = +        case TE.decodeUtf8' +               (fromMaybe (error "ms_hspp_buf is Nothing") $ +                stringBufferToByteString <$> ms_hspp_buf modSum) of +          Right text -> T.replace "\t" "        " text +          Left err -> +            error $ +            "decodeUtf8' : " ++ show err ++ " , file : " ++ show modulePath +   in case sourceCodePreprocessing of +        HCE.BeforePreprocessing -> +          let sourceCodeLines = T.splitOn "\n" originalSourceCode +           in ( HCE.SourceCodeTransformation +                  (length sourceCodeLines) +                  modulePath +                  S.empty +                  HM.empty +              , originalSourceCode) +        HCE.AfterPreprocessing -> +          createSourceCodeTransformation +            modulePath +            originalSourceCode +            sourceCodeAfterPreprocessing + +createDefinitionSiteMap :: +     DynFlags +  -> HCE.PackageId +  -> HCE.ComponentId +  -> HM.HashMap HCE.HaskellModulePath HCE.DefinitionSiteMap +  -> HM.HashMap HCE.HaskellFilePath HCE.HaskellModulePath +  -> GlobalRdrEnv +  -> HCE.SourceCodeTransformation +  -> ModuleInfo +  -> [Name] +  -> HsGroup Name +  -> (HCE.DefinitionSiteMap, [Name]) +createDefinitionSiteMap flags currentPackageId compId defSiteMap fileMap globalRdrEnv transformation modInfo dataFamTyCons hsGroup = +  let allDecls :: [GenLocated SrcSpan (HsDecl Name)] +      allDecls = L.sortBy (comparing getLoc) . ungroup $ hsGroup +      (instanceDeclsWithDocs, valueAndTypeDeclsWithDocs) = +        L.partition +          (\(L _ decl, _) -> +             case decl of +               InstD _ -> True +               _ -> False) $ +        collectDocs allDecls +      -------------------------------------------------------------------------------- +      -- Instances +      -------------------------------------------------------------------------------- +      -- No type instances or data instances here for now +      instanceDocMap :: M.Map SrcSpan [HsDocString] +      instanceDocMap = +        M.fromList . +        mapMaybe +          (\(L _n decl, docs) -> +             case decl of +               InstD (ClsInstD inst) -> Just (clsInstDeclSrcSpan inst, docs) +               _ -> Nothing) $ +        instanceDeclsWithDocs +      nameLocation :: Maybe SrcSpan -> Name -> HCE.LocationInfo +      nameLocation = +        nameLocationInfo +          flags +          currentPackageId +          compId +          transformation +          fileMap +          defSiteMap +          Nothing +      docHToHtml :: DocH (ModuleName, OccName) Name -> HCE.HTML +      docHToHtml = +        docWithNamesToHtml +          flags +          currentPackageId +          compId +          transformation +          fileMap +          defSiteMap +      instancesWithDocumentation = +        HM.fromList . +        map +          (\clsInst -> +             ( instanceToText flags clsInst +             , let location = +                     nameLocation Nothing (Var.varName . is_dfun $ clsInst) +                in case M.lookup (getSrcSpan clsInst) instanceDocMap of +                     Just hsDocString -> +                       HCE.DefinitionSite +                         location +                         (Just . docHToHtml . hsDocsToDocH flags globalRdrEnv $ +                          hsDocString) +                     Nothing -> HCE.DefinitionSite location Nothing)) $ +        modInfoInstances modInfo -- all instances (including derived) +      -------------------------------------------------------------------------------- +      -- Values and types +      -------------------------------------------------------------------------------- +      mainDeclNamesWithDocumentation = +        concatMap +          (\(L span decl, docs) -> map (, docs, span) $ getMainDeclBinder decl) +          valueAndTypeDeclsWithDocs +      dataFamTyConsWithoutDocs = +        map (\name -> (name, [], nameSrcSpan name)) dataFamTyCons +      allNamesWithDocumentation = +        mainDeclNamesWithDocumentation ++ +        subordinateNamesWithDocs allDecls ++ dataFamTyConsWithoutDocs +      (valuesWithDocumentation, typesWithDocumentation) = +        L.partition +          (\(name, _doc, _srcSpan) -> +             case occNameNameSpace . nameOccName $ name of +               HCE.VarName -> True +               HCE.DataName -> True +               _ -> False) +          allNamesWithDocumentation +      toHashMap :: +           [(Name, [HsDocString], SrcSpan)] +        -> HM.HashMap HCE.OccName HCE.DefinitionSite +      toHashMap = +        HM.fromListWith +          (\(HCE.DefinitionSite loc newDoc) (HCE.DefinitionSite _ oldDoc) -> +             (HCE.DefinitionSite loc $ mappend newDoc oldDoc)) . +        map +          (\(name, docs, srcSpan) -> +             let location = nameLocation (Just srcSpan) name +                 htmlDoc = +                   if not . null $ docs +                     then Just . docHToHtml . hsDocsToDocH flags globalRdrEnv $ +                          docs +                     else Nothing +              in (HCE.OccName $ toText flags name, HCE.DefinitionSite location htmlDoc)) +      -------------------------------------------------------------------------------- +   in ( HCE.DefinitionSiteMap +          { HCE.values = toHashMap valuesWithDocumentation +          , HCE.types = +              toHashMap $ typesWithDocumentation ++ dataFamTyConsWithoutDocs +          , HCE.instances = instancesWithDocumentation +          } +      , map (\(n, _, _) -> n) allNamesWithDocumentation) + +occNameToHtml :: +     DynFlags   +  -> HCE.PackageId +  -> HCE.ComponentId +  -> (ModuleName, OccName) +  -> H.Html +occNameToHtml flags packageId compId (modName, occName) = +  let location = +        H.textValue . toStrict . encodeToLazyText . Aeson.toJSON $ +        occNameLocationInfo flags packageId compId (modName, occName) +   in (H.span H.! H.dataAttribute "location" location) H.! A.class_ "link" $ +      H.toHtml (toText flags occName) + +nameToHtml :: +     DynFlags +  -> HCE.PackageId +  -> HCE.ComponentId +  -> HCE.SourceCodeTransformation +  -> HM.HashMap HCE.HaskellFilePath HCE.HaskellModulePath +  -> HM.HashMap HCE.HaskellModulePath HCE.DefinitionSiteMap +  -> Name +  -> H.Html +nameToHtml flags packageId compId transformation files defSiteMap name = +  let location = +        H.textValue . toStrict . encodeToLazyText . Aeson.toJSON $ +        nameLocationInfo +          flags +          packageId +          compId +          transformation +          files +          defSiteMap +          Nothing +          Nothing +          name +   in H.span H.! H.dataAttribute "location" location H.! A.class_ "link" $ +      H.toHtml (nameToText name) +   +docWithNamesToHtml :: +     DynFlags +  -> HCE.PackageId +  -> HCE.ComponentId +  -> HCE.SourceCodeTransformation +  -> HM.HashMap HCE.HaskellFilePath HCE.HaskellModulePath +  -> HM.HashMap HCE.HaskellModulePath HCE.DefinitionSiteMap +  -> DocH (ModuleName, OccName) Name +  -> HCE.HTML +docWithNamesToHtml flags packageId compId transformation fileMap defSiteMap = +  HCE.docToHtml +    (occNameToHtml flags packageId compId) +    (nameToHtml flags packageId compId transformation fileMap defSiteMap) + +createDeclarations :: +     DynFlags +  -> HsGroup Name +  -> TypeEnv +  -> S.Set Name +  -> HCE.SourceCodeTransformation +  -> [HCE.Declaration] +createDeclarations flags hsGroup typeEnv exportedSet transformation = +  let lineNumber :: SrcSpan -> Int +      lineNumber srcSpan = +        case srcSpanToLineAndColNumbers transformation srcSpan of +          Just (_file,(lineNum, _), (_, _)) -> lineNum +          Nothing -> 1 +      nameType :: Name -> Maybe HCE.Type +      nameType n = +        case lookupIdInTypeEnv typeEnv n of +          Just i -> Just . mkType flags . varType $ i +          Nothing -> Nothing +      -- | Top-level functions +      -------------------------------------------------------------------------------- +      valToDeclarations :: +           GenLocated SrcSpan (HsBindLR Name Name) -> [HCE.Declaration] +      valToDeclarations (L loc bind) = +        map +          (\name -> +             HCE.Declaration +               HCE.ValD +               (toText flags name) +               (nameType name) +               (S.member name exportedSet) +               (lineNumber loc)) $ +        collectHsBindBinders bind +      vals = concatMap valToDeclarations $ hsGroupVals hsGroup +      -- | Data, newtype, type, type family, data family or class declaration +      -------------------------------------------------------------------------------- +      tyClToDeclaration :: GenLocated SrcSpan (TyClDecl Name) -> HCE.Declaration +      tyClToDeclaration (L loc tyClDecl) = +        HCE.Declaration +          HCE.TyClD +          (T.append (tyClDeclPrefix tyClDecl) (toText flags $ tcdName tyClDecl)) +          (nameType $ tcdName tyClDecl) +          (S.member (unLoc $ tyClDeclLName tyClDecl) exportedSet) +          (lineNumber loc) +      tyclds = +        map tyClToDeclaration . +        filter (isGoodSrcSpan . getLoc) . concatMap group_tyclds . hs_tyclds $ +        hsGroup +      -- | Instances +      -------------------------------------------------------------------------------- +      instToDeclaration :: GenLocated SrcSpan (InstDecl Name) -> HCE.Declaration +      instToDeclaration (L loc inst) = +        HCE.Declaration +          HCE.InstD +          (instanceDeclToText flags inst) +          Nothing +          True +          (lineNumber loc) +      insts = +#if MIN_VERSION_GLASGOW_HASKELL(8,2,2,0)         +        map instToDeclaration . filter (isGoodSrcSpan . getLoc) . hsGroupInstDecls $ +#else +        map instToDeclaration . filter (isGoodSrcSpan . getLoc) . hs_instds $ +#endif +        hsGroup +      -- | Foreign functions +      -------------------------------------------------------------------------------- +      foreignFunToDeclaration :: +           GenLocated SrcSpan (ForeignDecl Name) -> HCE.Declaration +      foreignFunToDeclaration (L loc fd) = +        let name = unLoc $ fd_name fd +         in HCE.Declaration +              HCE.ForD +              (toText flags name) +              (nameType name) +              True +              (lineNumber loc) +      fords = map foreignFunToDeclaration $ hs_fords hsGroup +      -------------------------------------------------------------------------------- +   in L.sortBy (comparing HCE.lineNumber) $ vals ++ tyclds ++ insts ++ fords + +foldAST :: Environment -> TypecheckedModule -> SourceInfo +foldAST environment typecheckedModule = +  let (Just renamed@(_, importDecls, mbExported, _)) = +        renamedSource typecheckedModule +      emptyASTState = +        ASTState IVM.empty IM.empty M.empty emptyTidyEnv Nothing environment [] +      ASTState {..} = +        execState +          (foldTypecheckedSource $ tm_typechecked_source typecheckedModule) +          emptyASTState +      -- A few things that are not in the output of the typechecker: +      --     - the export list +      --     - the imports +      --     - type signatures +      --     - type/data/newtype declarations +      --     - class declarations +       +      -- Both typechecked source and renamed source are used to populate +      -- 'IdentifierInfoMap' and 'IdentifierOccurrenceMap' +      (idInfoMap, idOccMap) = +        L.foldl' +          (addIdentifierToMaps environment astStateIdSrcSpanMap) +          (HM.empty, astStateIdOccMap) +          (namesFromRenamedSource renamed)           +      flags = envDynFlags environment +      packageId = envPackageId environment +      compId = envComponentId environment +      importedModules = +        map +          ((\(L span modName) -> +              ( modName +              , span +              , moduleLocationInfo +                  flags +                  (envModuleNameMap environment) +                  packageId +                  compId +                  modName)) . +           ideclName . unLoc) . +        filter (not . ideclImplicit . unLoc) $ +        importDecls +      exportedModules = +        case mbExported of +          Just lieNames -> +            mapMaybe +              (\(L span ie) -> +                 case ie of +                   IEModuleContents (L _ modName) -> +                     Just +                       ( modName +                       , span +                       , moduleLocationInfo +                           flags +                           (envModuleNameMap environment) +                           packageId +                           compId +                           modName) +                   _ -> Nothing) +              lieNames +          Nothing -> [] +      addImportedAndExportedModulesToIdOccMap :: +           HCE.IdentifierOccurrenceMap -> HCE.IdentifierOccurrenceMap +      addImportedAndExportedModulesToIdOccMap = +        IM.map (L.sortBy $ comparing fst) . +        addModules +          (envTransformation environment) +          (importedModules ++ exportedModules) +   in SourceInfo +        { sourceInfoExprMap = astStateExprInfoMap +        , sourceInfoIdMap = idInfoMap +        , sourceInfoIdOccMap = addImportedAndExportedModulesToIdOccMap idOccMap +        , sourceInfoTypeErrors = astStateTypeErrors +        } + +-- | Updates 'IdentifierOccurrenceMap' and 'IdentifierInfoMap' using information +-- from typechecked source and renamed source +addIdentifierToMaps :: +     Environment +  -> M.Map SrcSpan (Id, Maybe (Type, [Type])) +  -> (HCE.IdentifierInfoMap, HCE.IdentifierOccurrenceMap) +  -> NameOccurrence +  -> (HCE.IdentifierInfoMap, HCE.IdentifierOccurrenceMap) +addIdentifierToMaps environment idSrcSpanMap idMaps@(idInfoMap, idOccMap) nameOcc +  | isGoodSrcSpan (getLoc $ locatedName nameOcc) && +      isOneLineSpan (getLoc $ locatedName nameOcc) +  , Just (_, (lineNumber, startCol), (_, endCol)) <- +     srcSpanToLineAndColNumbers (envTransformation environment) . +     getLoc . locatedName $ +     nameOcc = +    case nameOcc of +      TyLitOccurrence {kind = kind} -> +        addTypeToMaps +          environment +          idMaps +          (Just kind) +          Nothing +          (description nameOcc) +          lineNumber +          startCol +          endCol +      NameOccurrence {isBinder = isBinder} -> +        case lookupIdByNameOccurrence environment idSrcSpanMap nameOcc of +          Just (identifier, mbTypes) -> +            let name = +                  fromMaybe +                    (Var.varName identifier) +                    (unLoc $ locatedName nameOcc) +                identifierType = varType identifier +                identifierTypeExpanded = expandTypeSynonyms identifierType +                tyConsAndTyVars = +                  map +                    (, Nothing) +                    (tyConsOfType identifierType ++ +                     tyVarsOfType identifierType ++ +                     tyConsOfType identifierTypeExpanded ++ +                     tyVarsOfType identifierTypeExpanded ++ +                     maybe [] (tyConsOfType . fst) mbTypes ++ +                     maybe [] (tyVarsOfType . fst) mbTypes) +                idInfoMap' = +                  updateIdMap +                    environment +                    ((identifier, unLoc $ locatedName nameOcc) : tyConsAndTyVars) +                    idInfoMap +                idOcc = +                  mkIdentifierOccurrence +                    environment +                    identifier +                    name +                    mbTypes +                    isBinder +                    (description nameOcc) +                idOccMap' = +                  IM.insertWith +                    removeOverlappingInterval +                    lineNumber +                    [((startCol, endCol), idOcc)] +                    idOccMap +             in (idInfoMap', idOccMap') +          Nothing -- type variable +           -> +            case unLoc $ locatedName nameOcc of +              Just name -> +                addTypeToMaps +                  environment +                  idMaps +                  Nothing +                  (Just name) +                  (description nameOcc) +                  lineNumber +                  startCol +                  endCol +              Nothing -> idMaps              +addIdentifierToMaps _ _ idMaps _ = idMaps + +addTypeToMaps :: +     Environment +  -> (HCE.IdentifierInfoMap, HCE.IdentifierOccurrenceMap) +  -> Maybe Type +  -> Maybe Name +  -> T.Text +  -> Int +  -> Int +  -> Int +  -> (HCE.IdentifierInfoMap, HCE.IdentifierOccurrenceMap) +addTypeToMaps environment (idInfoMap, idOccMap) mbKind mbName descr lineNumber colStart colEnd = +  let flags = envDynFlags environment +      idInfoMap' = +        maybe +          idInfoMap +          (\kind -> +             updateIdMap +               environment +               (map (, Nothing) $ tyConsOfType kind ++ tyVarsOfType kind) +               idInfoMap) +          mbKind +      idOcc = +        HCE.IdentifierOccurrence +          { internalId = fmap (HCE.InternalId . nameKey) mbName +          , internalIdFromRenamedSource = +              HCE.InternalId . T.pack . show . getKey . nameUnique <$> mbName +          , isBinder = False +          , instanceResolution = Nothing +          , idOccType = mkType flags <$> mbKind +          , typeArguments = Nothing +          , description = descr +          , sort = HCE.TypeId +          } +      idOccMap' = +        IM.insertWith +          removeOverlappingInterval +          lineNumber +          [((colStart, colEnd), idOcc)] +          idOccMap +   in (idInfoMap', idOccMap') + +lookupIdByNameOccurrence :: +     Environment +  -> M.Map SrcSpan (Id, Maybe (Type, [Type])) +  -> NameOccurrence +  -> Maybe (Id, Maybe (Type, [Type])) +lookupIdByNameOccurrence environment idSrcSpanMap (NameOccurrence (L span mbName) _ _) = +  case M.lookup span idSrcSpanMap of     +    Just (identifier, mbTypes) -> Just (identifier, mbTypes) +    Nothing -> +      case mbName of +        Just name -> +          case M.lookup (nameSrcSpan name) idSrcSpanMap of +            -- LHS of a Match +            Just (identifier, mbTypes) -> Just (identifier, mbTypes) +            Nothing -> +              -- Things that are not in the typechecked source +              case lookupIdInTypeEnv (envTypeEnv environment) name of +                Just t -> Just (t, Nothing) +                Nothing -> Nothing +        Nothing -> Nothing +lookupIdByNameOccurrence _ _ TyLitOccurrence {..} = Nothing +       +updateIdMap :: +     Environment +  -> [(Id, Maybe Name)] +  -> HCE.IdentifierInfoMap +  -> HCE.IdentifierInfoMap +updateIdMap environment ids identifiersMap = +  let flags = envDynFlags environment +      update :: +           HCE.IdentifierInfoMap -> (Id, Maybe Name) -> HCE.IdentifierInfoMap +      update idMap (identifier, mbName) = +        let info = mkIdentifierInfo environment identifier mbName +         in HM.insertWith +              (flip const) +              (HCE.InternalId $ identifierKey flags identifier) +              info +              idMap +   in L.foldl' update identifiersMap ids + +addModules :: +     HCE.SourceCodeTransformation +  -> [(ModuleName, SrcSpan, HCE.LocationInfo)] +  -> HCE.IdentifierOccurrenceMap +  -> HCE.IdentifierOccurrenceMap +addModules transformation modules idMap = +  let update :: +           IM.IntMap [((Int, Int), HCE.IdentifierOccurrence)] +        -> (a, SrcSpan, HCE.LocationInfo) +        -> IM.IntMap [((Int, Int), HCE.IdentifierOccurrence)] +      update idOccMap (_modInfo, span, locInfo) +        | Just (_file,(lineNumber, colStart), (_, colEnd)) <- +           srcSpanToLineAndColNumbers transformation span = +          let idOcc = +                HCE.IdentifierOccurrence +                  { internalId = Nothing +                  , internalIdFromRenamedSource = Nothing +                  , isBinder = False +                  , instanceResolution = Nothing +                  , idOccType = Nothing +                  , typeArguments = Nothing +                  , description = "ImportDecl" +                  , sort = HCE.ModuleId locInfo +                  } +           in IM.insertWith +                removeOverlappingInterval +                lineNumber +                [((colStart, colEnd), idOcc)] +                idOccMap +      update idOccMap _ = idOccMap +   in L.foldl' update idMap modules diff --git a/src/HaskellCodeExplorer/PackageInfo.hs b/src/HaskellCodeExplorer/PackageInfo.hs new file mode 100644 index 0000000..f384a74 --- /dev/null +++ b/src/HaskellCodeExplorer/PackageInfo.hs @@ -0,0 +1,595 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE Rank2Types #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module HaskellCodeExplorer.PackageInfo +  ( createPackageInfo +  ) where + +import Control.DeepSeq(deepseq) +import Control.Exception +  ( IOException +  , SomeAsyncException +  , SomeException +  , fromException +  , throw +  , try +  ) +import Control.Monad (foldM, join, unless) +import Control.Monad.Extra (findM) +import Control.Monad.Logger +  ( LoggingT(..) +  , MonadLogger(..) +  , MonadLoggerIO(..) +  , logDebugN +  , logErrorN +  , logInfoN +  ) +import qualified Data.ByteString as BS +import qualified Data.HashMap.Strict as HM +import Data.IORef (readIORef) +import qualified Data.IntMap.Strict as IM +import qualified Data.List as L +import Data.Maybe (fromMaybe, isJust, maybeToList) +import qualified Data.Set as S +import qualified Data.Text as T +import qualified Data.Text.Encoding as TE +import Digraph (flattenSCCs) +import Distribution.Helper +  ( ChComponentName(..) +  , ChEntrypoint(..) +  , ChModuleName(..) +  , components +  , entrypoints +  , ghcOptions +  , mkQueryEnv +  , packageId +  , runQuery +  , sourceDirs +  ) +import DynFlags +  ( DynFlags(..) +  , GeneralFlag(..) +  , GhcMode(..) +  , WarnReason(..) +  , gopt_set +  , parseDynamicFlagsCmdLine +  ) +import Exception (ExceptionMonad(..), ghandle) +import GHC +  ( GhcLink(..) +  , HscTarget(..) +  , LoadHowMuch(..) +  , ModLocation(..) +  , ModSummary(..) +  , Severity +  , SrcSpan +  , getModuleGraph +  , getSession +  , getSessionDynFlags +  , guessTarget +  , load +  , noLoc +  , parseModule +  , runGhcT +  , setSessionDynFlags +  , setTargets +  , topSortModuleGraph +  , typecheckModule +  , moduleNameString +  , moduleName   +  ) +import GHC.Paths (libdir) +import GhcMonad (GhcT(..), liftIO) +import HaskellCodeExplorer.GhcUtils (isHsBoot,toText) +import HaskellCodeExplorer.ModuleInfo (ModuleDependencies, createModuleInfo) +import qualified HaskellCodeExplorer.Types as HCE +import HscTypes (hsc_EPS, hsc_HPT) +import Outputable (PprStyle, SDoc, neverQualify, showSDocForUser) +import Packages (initPackages) +import Prelude hiding (id) +import System.Directory +  ( doesFileExist +  , doesFileExist +  , findExecutable +  , setCurrentDirectory +  , getCurrentDirectory +  , makeAbsolute +  ) +import qualified System.Directory.Tree as DT +import System.FilePath +  ( (</>) +  , addTrailingPathSeparator +  , joinPath +  , normalise +  , replaceExtension +  , splitPath +  , takeExtension +  , takeBaseName +  , splitDirectories +  ) +import System.Process (readProcess) + +createPackageInfo :: +     FilePath -- ^ Path to a Cabal package +  -> Maybe FilePath -- ^ Relative path to a dist directory +  -> HCE.SourceCodePreprocessing -- ^ Before or after preprocessor +  -> [String] -- ^ Options for GHC +  -> [String] -- ^ Directories to ignore +  -> LoggingT IO (HCE.PackageInfo HCE.ModuleInfo) +createPackageInfo packageDirectoryPath mbDistDirRelativePath sourceCodePreprocessing additionalGhcOptions ignoreDirectories = do +  packageDirectoryAbsPath <- liftIO $ makeAbsolute packageDirectoryPath +  currentDirectory <- liftIO getCurrentDirectory +  liftIO $ setCurrentDirectory packageDirectoryAbsPath +  distDir <- +    case mbDistDirRelativePath of +      Just path -> return $ packageDirectoryAbsPath </> path +      Nothing -> findDistDirectory packageDirectoryAbsPath +  let cabalHelperQueryEnv = mkQueryEnv packageDirectoryAbsPath distDir +  ((packageName, packageVersion), compInfo) <- +    liftIO $ +    runQuery +      cabalHelperQueryEnv +      ((,) <$> packageId <*> +       (zip3 <$> components ((,) <$> ghcOptions) <*> +        components ((,) <$> entrypoints) <*> +        components ((,) <$> sourceDirs))) +  let currentPackageId = HCE.PackageId (T.pack packageName) packageVersion +  logInfoN $ T.append "Indexing " $ HCE.packageIdToText currentPackageId +  let buildComponents = +        L.map +          (\((options, compName), (entrypoint, _), (srcDirs, _)) -> +             ( chComponentNameToComponentId compName +             , options +             , chEntrypointsToModules entrypoint +             , srcDirs +             , chComponentNameToComponentType compName)) . +        L.sortBy +          (\((_, compName1), _, _) ((_, compName2), _, _) -> +             compare compName1 compName2) $ +        compInfo +      libSrcDirs = +        concatMap (\(_, _, _, srcDirs, _) -> srcDirs) . +        filter (\(_, _, _, _, compType) -> HCE.isLibrary compType) $ +        buildComponents +  (indexedModules, (_fileMapResult, _defSiteMapResult, modNameMapResult)) <- +    foldM +      (\(modules, (fileMap, defSiteMap, modNameMap)) (compId, options, (mbMain, moduleNames), srcDirs, _) -> do +         mbMainPath <- +           case mbMain of +             Just mainPath -> +               liftIO $ +               findM doesFileExist $ +               mainPath : +               map (\srcDir -> normalise $ srcDir </> mainPath) srcDirs +             Nothing -> return Nothing +         (modules', (fileMap', defSiteMap', modNameMap')) <- +           indexBuildComponent +             sourceCodePreprocessing +             currentPackageId +             compId +             (fileMap, defSiteMap, modNameMap) +             srcDirs +             libSrcDirs +             (options ++ additionalGhcOptions) +             (maybe moduleNames (: moduleNames) mbMainPath) +         return (modules ++ modules', (fileMap', defSiteMap', modNameMap'))) +      ([], (HM.empty, HM.empty, HM.empty)) +      buildComponents +  let modId = HCE.id :: HCE.ModuleInfo -> HCE.HaskellModulePath +      moduleMap = +        HM.fromList . map (\modInfo -> (modId modInfo, modInfo)) $ +        indexedModules +      references = L.foldl' addReferencesFromModule HM.empty indexedModules +      moduleId = HCE.id :: HCE.ModuleInfo -> HCE.HaskellModulePath +      topLevelIdentifiersTrie = +        L.foldl' addTopLevelIdentifiersFromModule HCE.emptyTrie . +        L.filter (not . isHsBoot . moduleId) $ +        indexedModules +  directoryTree <- +    liftIO $ +    buildDirectoryTree +      packageDirectoryAbsPath +      ignoreDirectories +      (\path -> HM.member (HCE.HaskellModulePath . T.pack $ path) moduleMap) +  liftIO $ setCurrentDirectory currentDirectory +  return +    HCE.PackageInfo +      { id = currentPackageId +      , moduleMap = moduleMap +      , moduleNameMap = modNameMapResult +      , directoryTree = directoryTree +      , externalIdOccMap = references +      , externalIdInfoMap = topLevelIdentifiersTrie +      } +  where +    chEntrypointsToModules :: ChEntrypoint -> (Maybe String, [String]) +    chEntrypointsToModules (ChLibEntrypoint modules otherModules signatures) = +      ( Nothing +      , L.map chModuleToString modules ++ +        L.map chModuleToString otherModules ++ L.map chModuleToString signatures) +    chEntrypointsToModules (ChExeEntrypoint mainModule _otherModules) = +      (Just mainModule, []) +    chEntrypointsToModules ChSetupEntrypoint = (Nothing, []) +    chModuleToString :: ChModuleName -> String +    chModuleToString (ChModuleName n) = n +    chComponentNameToComponentType :: ChComponentName -> HCE.ComponentType +    chComponentNameToComponentType ChSetupHsName = HCE.Setup +    chComponentNameToComponentType ChLibName = HCE.Lib +    chComponentNameToComponentType (ChSubLibName name) = +      HCE.SubLib $ T.pack name +    chComponentNameToComponentType (ChFLibName name) = HCE.FLib $ T.pack name +    chComponentNameToComponentType (ChExeName name) = HCE.Exe $ T.pack name +    chComponentNameToComponentType (ChTestName name) = HCE.Test $ T.pack name +    chComponentNameToComponentType (ChBenchName name) = HCE.Bench $ T.pack name +    chComponentNameToComponentId :: ChComponentName -> HCE.ComponentId +    chComponentNameToComponentId ChLibName = HCE.ComponentId "lib" +    chComponentNameToComponentId (ChSubLibName name) = +      HCE.ComponentId . T.append "sublib-" . T.pack $ name +    chComponentNameToComponentId (ChFLibName name) = +      HCE.ComponentId . T.append "flib-" . T.pack $ name +    chComponentNameToComponentId (ChExeName name) = +      HCE.ComponentId . T.append "exe-" . T.pack $ name +    chComponentNameToComponentId (ChTestName name) = +      HCE.ComponentId . T.append "test-" . T.pack $ name +    chComponentNameToComponentId (ChBenchName name) = +      HCE.ComponentId . T.append "bench-" . T.pack $ name +    chComponentNameToComponentId ChSetupHsName = HCE.ComponentId "setup" +      +buildDirectoryTree :: FilePath -> [FilePath] -> (FilePath -> Bool) -> IO HCE.DirTree +buildDirectoryTree path ignoreDirectories isHaskellModule = do +  (_dir DT.:/ tree) <- DT.readDirectoryWith (const . return $ ()) path +  -- Tuple up the complete file path with the file contents, by building up the path, +  -- trie-style, from the root. The filepath will be relative to "anchored" directory. +  let treeWithPaths = DT.zipPaths ("" DT.:/ DT.filterDir (not . ignore) tree) +  return $ toDirTree (removeTopDir . fst <$> treeWithPaths) +  where +    ignore :: DT.DirTree a -> Bool +    ignore (DT.Dir dirName _) +      | "." `L.isPrefixOf` dirName = True +      | dirName == "dist" = True +      | dirName == "dist-newstyle" = True +      | dirName == "tmp" = True +      | otherwise = dirName `elem` ignoreDirectories +    ignore (DT.Failed _ _) = True +    ignore _ = False +    removeTopDir :: FilePath -> FilePath +    removeTopDir p = +      case splitPath p of +        _x:xs -> joinPath xs +        [] -> "" +    toDirTree :: DT.DirTree FilePath -> HCE.DirTree +    toDirTree (DT.Dir name contents) = +      HCE.Dir name (map toDirTree . filter (not . DT.failed) $ contents) +    toDirTree (DT.File name filePath) = +      HCE.File name filePath (isHaskellModule filePath) +    toDirTree (DT.Failed name err) = +      HCE.File (name ++ " : " ++ show err) "" False +         +addTopLevelIdentifiersFromModule :: +     HCE.Trie Char HCE.ExternalIdentifierInfo +  -> HCE.ModuleInfo +  -> HCE.Trie Char HCE.ExternalIdentifierInfo +addTopLevelIdentifiersFromModule trieIdInfo HCE.ModuleInfo {..} = +  L.foldl' +    (\trie idInfo@(HCE.ExternalIdentifierInfo HCE.IdentifierInfo {..}) -> +       HCE.insertToTrie S.insert (T.unpack demangledOccName) idInfo trie) +    trieIdInfo +    externalIds +   +addReferencesFromModule ::      +     HM.HashMap HCE.ExternalId (S.Set HCE.IdentifierSrcSpan) +  -> HCE.ModuleInfo +  -> HM.HashMap HCE.ExternalId (S.Set HCE.IdentifierSrcSpan) +addReferencesFromModule references modInfo@HCE.ModuleInfo {..} = +  eachIdentifierOccurrence +    references +    modInfo +    (\occMap lineNumber startCol endCol occ -> +       let mbIdExternalId = +             join $ +             HCE.externalId <$> +             maybe +               Nothing +               (`HM.lookup` idInfoMap) +               (HCE.internalId (occ :: HCE.IdentifierOccurrence)) +           idSrcSpan = +             HCE.IdentifierSrcSpan +               { modulePath = id +               , line = lineNumber +               , startColumn = startCol +               , endColumn = endCol +               } +        in case mbIdExternalId of +             Just externalId -> +               HM.insertWith S.union externalId (S.singleton idSrcSpan) occMap +             Nothing -> occMap) + +findDistDirectory :: FilePath -> LoggingT IO FilePath +findDistDirectory packagePath = do +  hasStackYaml <- liftIO $ doesFileExist (packagePath </> "stack.yaml") +  mbStackExecutable <- liftIO $ findExecutable "stack" +  let defaultDistDir = packagePath </> "dist" +  case (hasStackYaml, mbStackExecutable) of +    (True, Just stack) -> do +      let removeEndOfLine str +            | null str = str +            | otherwise = init str +      eitherDistDir :: (Either IOException String) <- +        liftIO . +        try . fmap removeEndOfLine . readProcess stack ["path", "--dist-dir"] $ +        "" +      case eitherDistDir of +        Right distDir -> do +          logDebugN $ T.append "Stack dist directory : " $ T.pack distDir +          return distDir +        Left exception -> do +          logDebugN $ +            T.append +              "Error while executing \"stack path --dist-dir\" : " +              (T.pack . show $ exception) +          return defaultDistDir +    (False, _) -> do +      logDebugN +        "stack.yaml is not found in the package directory. Using default dist directory." +      return defaultDistDir +    (_, Nothing) -> do +      logDebugN "stack executable is not found. Using default dist directory." +      return defaultDistDir +       +eachIdentifierOccurrence :: +     forall a. +     a +  -> HCE.ModuleInfo +  -> (a -> IM.Key -> Int -> Int -> HCE.IdentifierOccurrence -> a) +  -> a +eachIdentifierOccurrence accumulator HCE.ModuleInfo {..} f = +  IM.foldlWithKey' +    (\acc lineNumber occurences -> +       L.foldl' +         (\a ((startCol, endCol), occ) -> f a lineNumber startCol endCol occ) +         acc +         occurences) +    accumulator +    idOccMap + +instance ExceptionMonad (LoggingT IO) where +  gcatch act h = +    LoggingT $ \logFn -> +      runLoggingT act logFn `gcatch` \e -> runLoggingT (h e) logFn +  gmask f = +    LoggingT $ \logFn -> +      gmask $ \io_restore -> +        let g_restore (LoggingT m) = LoggingT $ \lf -> io_restore (m lf) +         in runLoggingT (f g_restore) logFn + +instance MonadLoggerIO (GhcT (LoggingT IO)) where +  askLoggerIO = GhcT $ const askLoggerIO + +instance MonadLogger (GhcT (LoggingT IO)) where +  monadLoggerLog loc source level = +    GhcT . const . monadLoggerLog loc source level + +gtrySync :: (ExceptionMonad m) => m a -> m (Either SomeException a) +gtrySync action = ghandleSync (return . Left) (fmap Right action) + +ghandleSync :: (ExceptionMonad m) => (SomeException -> m a) -> m a -> m a +ghandleSync onError = +  ghandle +    (\ex -> +       case fromException ex of +         Just (asyncEx :: SomeAsyncException) -> throw asyncEx +         _ -> onError ex) +     +indexBuildComponent :: +     HCE.SourceCodePreprocessing -- ^ Before or after preprocessor +  -> HCE.PackageId -- ^ Current package id +  -> HCE.ComponentId -- ^ Current component id +  -> ModuleDependencies -- ^ Already indexed modules +  -> [FilePath] -- ^ Src dirs +  -> [FilePath] -- ^ Src dirs of libraries +  -> [String] -- ^ Command-line options for GHC +  -> [String] -- ^ Modules to compile +  -> LoggingT IO ([HCE.ModuleInfo],ModuleDependencies) +indexBuildComponent sourceCodePreprocessing currentPackageId componentId deps@(fileMap, defSiteMap, modNameMap) srcDirs libSrcDirs options modules = do +  let onError ex = do +        logErrorN $ +          T.concat +            [ "Error while indexing component " +            , HCE.getComponentId componentId +            , " : " +            , T.pack . show $ ex +            ] +        return ([], deps) +  ghandleSync onError $ +    runGhcT (Just libdir) $ do +      logDebugN (T.append "Component id : " $ HCE.getComponentId componentId) +      logDebugN (T.append "Modules : " $ T.pack $ show modules) +      logDebugN (T.append "GHC options : " $ T.pack $ show options) +      flags <- getSessionDynFlags +      (flags', _, _) <- parseDynamicFlagsCmdLine flags (L.map noLoc options) +      (flags'', _) <- liftIO $ initPackages flags' +      logFn <- askLoggerIO +      let logAction :: +               DynFlags +            -> WarnReason +            -> Severity +            -> SrcSpan +            -> Outputable.PprStyle +            -> SDoc +            -> IO () +          logAction fs _reason _severity srcSpan _stype msg = +            runLoggingT +              (logDebugN +                 (T.append "GHC message : " $ +                  T.pack $ +                  showSDocForUser fs neverQualify msg ++ +                  " , SrcSpan : " ++ show srcSpan)) +              logFn +          mbTmpDir = +            case hiDir flags'' of +              Just buildDir -> +                Just $ buildDir </> (takeBaseName buildDir ++ "-tmp") +              Nothing -> Nothing +      _ <- +        setSessionDynFlags $ +        L.foldl' +          gopt_set +          (flags'' +             { hscTarget = HscAsm +             , ghcLink = LinkInMemory +             , ghcMode = CompManager +             , log_action = logAction +             , importPaths = importPaths flags'' ++ maybeToList mbTmpDir +             }) +          [Opt_Haddock] +      targets <- mapM (`guessTarget` Nothing) modules +      setTargets targets +      _ <- load LoadAllTargets +      modGraph <- getModuleGraph +      let topSortMods = flattenSCCs (topSortModuleGraph False modGraph Nothing) +          buildDir = +            addTrailingPathSeparator . normalise . fromMaybe "" . hiDir $ +            flags'' +          pathsModuleName = +            "Paths_" ++ +            map +              (\c -> +                 if c == '-' +                   then '_' +                   else c) +              (T.unpack (HCE.name (currentPackageId :: HCE.PackageId)))       +      (modSumWithPath, modulesNotFound) <- +        (\(mods, notFound) -> +           ( L.reverse . +             L.foldl' +               (\acc (mbPath, modSum) -> +                  case mbPath of +                    Just path +                      | not $ HM.member path defSiteMap -> (path, modSum) : acc +                    _ -> acc) +               [] $ +             mods +           , map snd notFound)) . +        L.partition (\(mbPath, _) -> isJust mbPath) <$> +        mapM +          (\modSum -> +             liftIO $ +             (, modSum) <$> +             findHaskellModulePath buildDir (srcDirs ++ libSrcDirs) modSum) +          (filter +             (\modSum -> +                pathsModuleName /= +                (moduleNameString . moduleName $ ms_mod modSum)) +             topSortMods)       +      unless (null modulesNotFound) $ +        logErrorN $ +        T.append +          "Cannot find module path : " +          (toText flags'' $ map ms_mod modulesNotFound) +      foldM +        (\(indexedModules, (fileMap', defSiteMap', modNameMap')) (modulePath, modSum) -> do +           result <- +             indexModule +               sourceCodePreprocessing +               componentId +               currentPackageId +               flags'' +               (fileMap', defSiteMap', modNameMap') +               (modulePath, modSum) +           case result of +             Right (modInfo, (fileMap'', defSiteMap'', modNameMap'')) -> +               return +                 ( modInfo : indexedModules +                 , (fileMap'', defSiteMap'', modNameMap'')) +             Left exception -> do +               logErrorN $ +                 T.concat +                   [ "Error while indexing " +                   , T.pack . show $ modulePath +                   , " : " +                   , T.pack . show $ exception +                   ] +               return (indexedModules, (fileMap', defSiteMap', modNameMap'))) +        ([], (fileMap, defSiteMap, modNameMap)) +        modSumWithPath +           +findHaskellModulePath :: +     FilePath -> [FilePath] -> ModSummary -> IO (Maybe HCE.HaskellModulePath) +findHaskellModulePath buildDir srcDirs modSum = +  case normalise <$> (ml_hs_file . ms_location $ modSum) of +    Just modulePath -> +      let toHaskellModulePath = return . Just . HCE.HaskellModulePath . T.pack +          removeTmpDir path = +            case splitDirectories path of +              parent:rest -> +                if "-tmp" `L.isSuffixOf` parent +                  then joinPath rest +                  else path +              _ -> path +       in case removeTmpDir <$> L.stripPrefix buildDir modulePath of +            -- File is in the build directory         +            Just path +              | takeExtension path == ".hs-boot" -> do +                let possiblePaths = path : map (</> path) srcDirs +                mbFoundPath <- findM doesFileExist possiblePaths +                case mbFoundPath of +                  Just p -> toHaskellModulePath p +                  _ -> return Nothing +              | takeExtension path == ".hs" -> do +                let paths = +                      map +                        (replaceExtension path) +                        HCE.haskellPreprocessorExtensions +                    possiblePaths = +                      paths ++ +                      concatMap (\srcDir -> map (srcDir </>) paths) srcDirs +                mbFoundPath <- findM doesFileExist possiblePaths +                case mbFoundPath of +                  Just p -> toHaskellModulePath p +                  _ -> return Nothing +              | otherwise -> return Nothing +            Nothing -> toHaskellModulePath modulePath +    Nothing -> return Nothing + +indexModule :: +     HCE.SourceCodePreprocessing +  -> HCE.ComponentId +  -> HCE.PackageId +  -> DynFlags +  -> ModuleDependencies +  -> (HCE.HaskellModulePath, ModSummary) +  -> GhcT (LoggingT IO) (Either SomeException ( HCE.ModuleInfo +                                              , ModuleDependencies)) +indexModule sourceCodePreprocessing componentId currentPackageId flags deps (modulePath, modSum) = +  gtrySync $ do +    logDebugN (T.append "Indexing " $ HCE.getHaskellModulePath modulePath) +    parsedModule <- parseModule modSum +    typecheckedModule <- typecheckModule parsedModule +    hscEnv <- getSession +    externalPackageState <- liftIO . readIORef . hsc_EPS $ hscEnv +    originalSourceCode <- +      liftIO $ +      T.replace "\t" "        " . TE.decodeUtf8 <$> +      BS.readFile (T.unpack . HCE.getHaskellModulePath $ modulePath) +    let (modInfo, (fileMap', exportMap', moduleNameMap'), typeErrors) = +          createModuleInfo +            deps +            ( flags +            , typecheckedModule +            , hsc_HPT hscEnv +            , externalPackageState +            , modSum) +            modulePath +            currentPackageId +            componentId +            (originalSourceCode, sourceCodePreprocessing) +    unless (null typeErrors) $ +      logInfoN $ T.append "Type errors : " $ T.pack $ show typeErrors +    deepseq modInfo $ return (modInfo, (fileMap', exportMap', moduleNameMap')) diff --git a/src/HaskellCodeExplorer/Preprocessor.hs b/src/HaskellCodeExplorer/Preprocessor.hs new file mode 100644 index 0000000..6a90ff9 --- /dev/null +++ b/src/HaskellCodeExplorer/Preprocessor.hs @@ -0,0 +1,159 @@ +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE OverloadedStrings #-} + +module HaskellCodeExplorer.Preprocessor +  ( createSourceCodeTransformation +  ) where + +import Control.Applicative ((<|>)) +import qualified Data.Attoparsec.Text as AT +import Data.Foldable (foldl') +import qualified Data.HashMap.Strict as HM +import qualified Data.List as L +import qualified Data.Set as S +import qualified Data.Text as T +import HaskellCodeExplorer.Types +  ( FileLocation(..) +  , HaskellFilePath(..) +  , HaskellModulePath(..) +  , LinePragma(..) +  , SourceCodeTransformation(..) +  , haskellPreprocessorExtensions   +  ) +import System.FilePath (normalise,takeExtension,takeFileName) + + +-- | Finds locations of line pragmas and creates an index +createSourceCodeTransformation :: +     HaskellModulePath -> T.Text -> T.Text -> (SourceCodeTransformation, T.Text) +createSourceCodeTransformation currentModulePath originalSourceCode sourceCodeAfterPreprocessing = +  let sourceCodeLines = T.splitOn "\n" sourceCodeAfterPreprocessing +      numberedLines = zip [1 :: Int ..] sourceCodeLines +      currentFilePath = +        HaskellFilePath . getHaskellModulePath $ currentModulePath +      addPragma :: [LinePragma] -> (Int, T.Text) -> [LinePragma] +      addPragma acc (lineNumber, line) = +        case AT.parseOnly linePragmaParser line of +          Right (originalLineNumber, mbFileName) -> +            LinePragma +              (maybe +                 currentFilePath +                 (HaskellFilePath . T.pack . normalise . T.unpack) +                 mbFileName) +              lineNumber +              originalLineNumber : +            acc +          Left _ -> acc +      totalLines = length numberedLines +      pragmas = L.reverse . L.foldl' addPragma [] $ numberedLines +      pragmaPath = filePath :: LinePragma -> HaskellFilePath +      currentFileExtension = +        takeExtension . T.unpack . getHaskellFilePath $ currentFilePath +      standardHeaderFiles = ["stdc-predef.h", "cabal_macros.h", "ghcversion.h"]       +      hasIncludedFiles = +        L.any +          ((\path -> +              let fileName = takeFileName . T.unpack . getHaskellFilePath $ path +               in (path /= currentFilePath) && +                  (path /= HaskellFilePath "<built-in>") && +                  (path /= HaskellFilePath "<command-line>") && +                  not ("ghc_" `L.isPrefixOf` fileName) && +                  (fileName `notElem` standardHeaderFiles)) . +           pragmaPath) +          pragmas +   in if hasIncludedFiles || +         currentFileExtension `elem` haskellPreprocessorExtensions +        then ( SourceCodeTransformation +                 totalLines +                 currentModulePath +                 (S.fromList pragmas) +                 (indexLocations totalLines currentFilePath pragmas) +             , sourceCodeAfterPreprocessing) +        else ( SourceCodeTransformation +                 (length $ T.splitOn "\n" originalSourceCode) +                 currentModulePath +                 S.empty +                 HM.empty +             , originalSourceCode) + +-- | Parses line pragma +linePragmaParser :: AT.Parser (Int, Maybe T.Text) +linePragmaParser = pragma1 <|> pragma2 +  where    +    pragma1 :: AT.Parser (Int, Maybe T.Text) +    pragma1 = parser "#" "line" +     +    pragma2 :: AT.Parser (Int, Maybe T.Text) +    pragma2 = parser "{-#" "LINE" +     +    parser :: T.Text -> T.Text -> AT.Parser (Int, Maybe T.Text) +    parser start line = do +      _ <- AT.string start +      _ <- AT.takeWhile (== ' ') +      _ <- AT.string line <|> return "" +      _ <- AT.takeWhile (== ' ') +      num <- AT.decimal +      _ <- AT.takeWhile (== ' ') +      mbName <- (Just <$> fileName) <|> return Nothing +      return (num, mbName) +       +    fileName :: AT.Parser T.Text +    fileName = AT.string "\"" *> AT.takeTill (== '\"') <* AT.string "\""     + +data Line = FirstLine | LastLine Int | Pragma LinePragma deriving (Show,Eq) + +-- | Creates a HashMap whose keys are filenames and values are locations in a +-- preprocessed source code +indexLocations :: +     Int +  -> HaskellFilePath +  -> [LinePragma] +  -> HM.HashMap HaskellFilePath (S.Set FileLocation) +indexLocations totalLines preprocessedFilePath pragmas = +  foldl' add HM.empty . (zip <*> tail) $ +  (FirstLine : map Pragma pragmas) ++ [LastLine totalLines] +  where +    add :: +         HM.HashMap HaskellFilePath (S.Set FileLocation) +      -> (Line, Line) +      -> HM.HashMap HaskellFilePath (S.Set FileLocation) +    -- Interval between the first line and the first pragma +    add hMap (FirstLine, Pragma LinePragma {..}) +      | lineNumberPreprocessed > 1 = +        HM.insertWith +          S.union +          preprocessedFilePath +          (S.singleton (FileLocation 1 lineNumberPreprocessed 0)) +          hMap +      | otherwise = hMap +    -- Interval between two pragmas +    add hMap (Pragma (LinePragma fileName lineNumberPreprocessed1 lineNumberOriginal1), +              Pragma (LinePragma _ lineNumberPreprocessed2 _)) +      | lineNumberPreprocessed2 - lineNumberPreprocessed1 > 1 = +        HM.insertWith +          S.union +          fileName +          (S.singleton +             (FileLocation +                lineNumberOriginal1 +                (lineNumberOriginal1 + +                 (lineNumberPreprocessed2 - lineNumberPreprocessed1 - 2)) +                (lineNumberPreprocessed1 - lineNumberOriginal1 + 1))) +          hMap +      | otherwise = hMap +    -- Interval between the last pragma and the last line +    add hMap (Pragma (LinePragma fileName lineNumberPreprocessed lineNumberOriginal), +              LastLine lastLineNumberPreprocessed) +      | lastLineNumberPreprocessed - lineNumberPreprocessed > 1 = +        HM.insertWith +          S.union +          fileName +          (S.singleton +             (FileLocation +                lineNumberOriginal +                (lineNumberOriginal + (lastLineNumberPreprocessed - lineNumberPreprocessed - 2)) +                (lineNumberPreprocessed - lineNumberOriginal + 1))) +          hMap +      | otherwise = hMap +    add hMap _ = hMap diff --git a/src/HaskellCodeExplorer/Types.hs b/src/HaskellCodeExplorer/Types.hs new file mode 100644 index 0000000..9e3667d --- /dev/null +++ b/src/HaskellCodeExplorer/Types.hs @@ -0,0 +1,880 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE StrictData #-} +{-# LANGUAGE Rank2Types #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module HaskellCodeExplorer.Types where + +import Control.DeepSeq (NFData) +import qualified Data.Aeson as A +import Data.Aeson.Types (Options, defaultOptions, omitNothingFields) +import Data.Generics +  ( Constr +  , Data(..) +  , DataType +  , Fixity(..) +  , constrIndex +  , gcast2 +  , mkConstr +  , mkDataType +  ) +import qualified Data.HashMap.Strict as HM +import Data.Hashable (Hashable) +import qualified Data.IntMap.Strict as IM +import qualified Data.IntervalMap.Strict as IVM +import qualified Data.List as L +import Data.Maybe (fromMaybe, isJust) +import Data.Serialize (Get, Serialize(..)) +import qualified Data.Set as S       +import qualified Data.Text as T +import Data.Text.Encoding (decodeUtf8, encodeUtf8) +import Data.Text.Lazy (toStrict) +import qualified Data.Vector as V +import Data.Version (Version(..),showVersion) +import Documentation.Haddock.Types +  ( DocH(..) +  , Example(..) +  , Header(..) +  , Hyperlink(..) +  , Picture(..) +  ) +import GHC.Generics (Generic) +import Prelude hiding (id) +import Text.Blaze.Html.Renderer.Text (renderHtml) +import qualified Text.Blaze.Html5 as Html +import qualified Text.Blaze.Html5.Attributes as Attr + +-------------------------------------------------------------------------------- +-- Package info +-------------------------------------------------------------------------------- + +data PackageInfo modInfo = PackageInfo +  { id :: PackageId +  , moduleMap :: HM.HashMap HaskellModulePath modInfo +  , moduleNameMap :: HM.HashMap HaskellModuleName (HM.HashMap ComponentId HaskellModulePath) +  , directoryTree :: DirTree +  , externalIdInfoMap :: Trie Char ExternalIdentifierInfo +  -- ^ All external identifiers defined in the package +  , externalIdOccMap :: HM.HashMap ExternalId (S.Set IdentifierSrcSpan) +  -- ^ All occurrences of each external identifier in the package +  } deriving (Show, Eq, Generic, Data) + +data PackageId = PackageId +  { name :: T.Text +  , version :: Data.Version.Version +  } deriving (Show, Eq, Ord, Generic, Data) + +packageIdToText :: PackageId -> T.Text +packageIdToText (PackageId name version) = +  T.concat [name, "-", T.pack $ showVersion version] + +packageName :: PackageInfo a -> T.Text +packageName = +  (name :: (PackageId -> T.Text)) . (id :: PackageInfo a -> PackageId) + +data IdentifierSrcSpan = IdentifierSrcSpan +  { modulePath :: HaskellModulePath +  , line :: Int +  , startColumn :: Int +  , endColumn :: Int +  } deriving (Show, Eq, Ord, Generic, Data) + +data DirTree +  = Dir { name :: FilePath +        , contents :: [DirTree] } +  | File { name :: FilePath +         , path :: FilePath +         , isHaskellModule :: Bool } +  deriving (Show, Eq, Generic, Data) + +newtype ComponentId = ComponentId +  { getComponentId :: T.Text +  } deriving (Show, Eq, Ord, Generic, A.ToJSONKey, Data, Hashable) + +data ComponentType +  = Setup +  | Lib +  | SubLib T.Text +  | FLib T.Text +  | Exe T.Text +  | Test T.Text +  | Bench T.Text +  deriving (Show, Eq, Generic, Data) + +isLibrary :: ComponentType -> Bool +isLibrary Lib = True +isLibrary (SubLib _) = True +isLibrary (FLib _) = True +isLibrary _ = False + +packageInfoBinaryFileName :: FilePath +packageInfoBinaryFileName = "packageInfo" + +packageInfoJsonFileName :: FilePath +packageInfoJsonFileName = "packageInfo.json" + +defaultOutputDirectoryName :: FilePath +defaultOutputDirectoryName = ".haskell-code-explorer" + +-------------------------------------------------------------------------------- +-- A simple Trie implementation +-------------------------------------------------------------------------------- + +data Trie k v = Trie +  { values :: S.Set v +  , children :: HM.HashMap k (Trie k v) +  } deriving (Show, Eq, Generic, Data) + +emptyTrie :: Trie k v +emptyTrie = Trie S.empty HM.empty + +insertToTrie :: +     (Hashable k, Eq k, Ord v) +  => (v -> S.Set v -> S.Set v) +  -> [k] +  -> v +  -> Trie k v +  -> Trie k v +insertToTrie f [] v (Trie vals children) = Trie (f v vals) children +insertToTrie f word@(first:rest) val (Trie vals children) = +  case HM.lookup first children of +    Just trie -> +      Trie vals (HM.insert first (insertToTrie f rest val trie) children) +    Nothing -> +      insertToTrie f word val (Trie vals (HM.insert first emptyTrie children)) + +match :: (Hashable k, Eq k, Ord v) => [k] -> Trie k v -> S.Set v +match (first:rest) (Trie _ children) = +  maybe S.empty (match rest) (HM.lookup first children) +match [] (Trie val children) = +  S.union val $ +  S.unions +    [S.union v $ match [] trie | (_, trie@(Trie v _)) <- HM.toList children] + +-------------------------------------------------------------------------------- +-- Module info +-------------------------------------------------------------------------------- + +data ModuleInfo = ModuleInfo +  { id :: HaskellModulePath +  , name :: HaskellModuleName +  , source :: V.Vector T.Text +    -- ^ Source code of the module +  , transformation :: SourceCodeTransformation +  , exprInfoMap :: ExpressionInfoMap +    -- ^ Type of each expression in the module +  , idOccMap :: IdentifierOccurrenceMap +    -- ^ All occurrences of each identifier in the module +  , idInfoMap :: IdentifierInfoMap +    -- ^ Information about each identifier in the module +  , declarations :: [Declaration] +  , definitionSiteMap :: DefinitionSiteMap +    -- ^ Definition site of each top-level value, type, and type class instance     +  , externalIds :: [ExternalIdentifierInfo] +  } deriving (Show, Eq, Generic, Data) + +type ExpressionInfoMap = IVM.IntervalMap (Int, Int) ExpressionInfo +type IdentifierOccurrenceMap = IM.IntMap [((Int, Int), IdentifierOccurrence)] +type IdentifierInfoMap = HM.HashMap InternalId IdentifierInfo + +data DefinitionSiteMap = DefinitionSiteMap +  { values :: HM.HashMap OccName DefinitionSite +  , types :: HM.HashMap OccName DefinitionSite +  , instances :: HM.HashMap T.Text DefinitionSite +  } deriving (Show, Eq, Generic, Data) + +data DefinitionSite = DefinitionSite +  { location :: LocationInfo +  , documentation :: Maybe HTML +  } deriving (Show, Eq, Generic, Data) + +type HTML = T.Text + +newtype OccName = OccName +  { getOccName :: T.Text +  } deriving (Show, Eq, Ord, Generic, A.ToJSONKey, Data, Hashable) + +-- | 'CompactModuleInfo' contains a subset of fields of 'ModuleInfo'. +data CompactModuleInfo = CompactModuleInfo +  { id :: HaskellModulePath +  , name :: HaskellModuleName +  , exprInfoMap :: ExpressionInfoMap +  , definitionSiteMap :: DefinitionSiteMap +  , source :: V.Vector T.Text +  } deriving (Show, Eq, Generic, Data) + +haskellPreprocessorExtensions :: [FilePath] +haskellPreprocessorExtensions = +  [".hsc", ".chs", ".cpphs", ".gc", ".x", ".y", ".ly"] + +toCompactPackageInfo :: PackageInfo ModuleInfo -> PackageInfo CompactModuleInfo +toCompactPackageInfo PackageInfo {..} = +  PackageInfo +    { id = id     +    , moduleMap = HM.map toCompactModuleInfo moduleMap +    , moduleNameMap = moduleNameMap +    , directoryTree = directoryTree +    , externalIdOccMap = externalIdOccMap +    , externalIdInfoMap = externalIdInfoMap +    } + +toCompactModuleInfo :: ModuleInfo -> CompactModuleInfo +toCompactModuleInfo ModuleInfo {..} = +  CompactModuleInfo +    { id = id +    , name = name +    , exprInfoMap = exprInfoMap +    , definitionSiteMap = definitionSiteMap +    , source = source +    } + +newtype HaskellModuleName = HaskellModuleName +  { getHaskellModuleName :: T.Text +  } deriving (Show, Eq, Ord, Generic, A.ToJSONKey, Data) + +newtype HaskellModulePath = HaskellModulePath +  { getHaskellModulePath :: T.Text +  } deriving (Show, Eq, Ord, Generic, A.ToJSONKey, Data) + +newtype HaskellFilePath = HaskellFilePath +  { getHaskellFilePath :: T.Text +  } deriving (Show, Eq, Ord, Generic, A.ToJSONKey, Data) + +-- | Haskell identifier (value or type) +data IdentifierInfo = IdentifierInfo +  { sort :: NameSort +  , occName :: OccName +  , demangledOccName :: T.Text +  , nameSpace :: NameSpace +  , locationInfo :: LocationInfo +  , idType :: Type +  , details :: Maybe IdDetails +  , doc :: Maybe HTML +  , internalId :: InternalId +  , externalId :: Maybe ExternalId +  , isExported :: Bool +  } deriving (Show, Eq, Ord, Generic, Data) + +data NameSort +  = External +  | Internal +  deriving (Show, Eq, Ord, Generic, Data) + +data NameSpace +  = VarName +  | DataName +  | TvName +  | TcClsName +  deriving (Show, Eq, Ord, Generic, Data) + +data IdDetails +  = VanillaId +  | RecSelId +  | RecSelIdNaughty +  | DataConWorkId +  | DataConWrapId +  | ClassOpId +  | PrimOpId +  | FCallId +  | TickBoxOpId +  | DFunId +  | CoVarId +  | JoinId +  deriving (Show, Eq, Ord, Generic, Data) + +-- | Each Haskell identifier has an 'InternalId' that is unique within a single module +newtype InternalId = InternalId +  { getInternalId :: T.Text +  } deriving (Show, Eq, Ord, Generic, Data, Hashable, A.ToJSONKey) + +newtype ExternalId = ExternalId +  { getExternalId :: T.Text +  } deriving (Show, Eq, Ord, Generic, Data, Hashable, A.ToJSONKey) + +newtype ExternalIdentifierInfo = ExternalIdentifierInfo +  { getIdentifierInfo :: IdentifierInfo +  } deriving (Eq, Show, Generic, Data) + +instance Ord ExternalIdentifierInfo where +  compare (ExternalIdentifierInfo i1) (ExternalIdentifierInfo i2) = +    case compare +           (T.length . demangledOccName $ i1) +           (T.length . demangledOccName $ i2) of +      GT -> GT +      LT -> LT +      EQ -> +        case compare (demangledOccName i1) (demangledOccName i2) of +          GT -> GT +          LT -> LT +          EQ ->             +            compare +              (internalId (i1 :: IdentifierInfo)) +              (internalId (i2 :: IdentifierInfo)) +         +data ExpressionInfo = ExpressionInfo +  { description :: T.Text +  , exprType :: Maybe Type +  } deriving (Show, Eq, Generic, Data) + +-- | Occurrence of an identifier in a source code +data IdentifierOccurrence = IdentifierOccurrence +  { internalId :: Maybe InternalId +  , internalIdFromRenamedSource :: Maybe InternalId +  , isBinder :: Bool +  , instanceResolution :: Maybe InstanceResolution +  , idOccType :: Maybe Type +  -- ^ Instantiated type of an identifier +  , typeArguments :: Maybe [Type] +  , description :: T.Text +  , sort :: IdentifierOccurrenceSort +  } deriving (Show, Eq, Ord, Generic, Data) + +data IdentifierOccurrenceSort +  = ValueId +  | TypeId +  | ModuleId LocationInfo +  deriving (Show, Eq, Ord, Generic, Data) + +data Type = Type +  { components :: [TypeComponent] +  , componentsExpanded :: Maybe [TypeComponent] +  -- ^ Components of a type with all type synonyms expanded +  } deriving (Show, Eq, Ord, Generic, Data) + +data TypeComponent +  = Text T.Text +  | TyCon { internalId :: InternalId +          , name :: T.Text } +  deriving (Show, Eq, Ord, Generic, Data) + +-- | Tree of instances +data InstanceResolution = +  Instance +  { name :: T.Text +  -- ^ Type of an instance, e.g., "instance Show a => ClassName a" +  , instanceType :: Type +  , types :: [Type] +  -- ^ Types at which type variables of a class are instantiated +  , location :: LocationInfo +  , instances :: [InstanceResolution]   +  } +  | Stop +  deriving (Show,Eq,Ord,Generic,Data) + +data SourceCodeTransformation = SourceCodeTransformation +  { totalLines :: Int +  , filePath :: HaskellModulePath +  , linePragmas :: S.Set LinePragma +  , fileIndex :: HM.HashMap HaskellFilePath (S.Set FileLocation) +  -- ^ Map from an original filename to its locations in a preprocessed source code +  } deriving (Show, Eq, Generic, Data) + +-- | Location of a file included by a preprocessor +data FileLocation = FileLocation +  { lineStart :: Int +  , lineEnd :: Int +  , offset :: Int +  -- ^ (line number in a preprocessed file) - (line number in an original file) + 1 +  } deriving (Show, Eq, Generic, Data) + +-- | Line pragma inserted by a preprocessor +data LinePragma = LinePragma +  { filePath :: HaskellFilePath +  , lineNumberPreprocessed :: Int +  , lineNumberOriginal :: Int +  } deriving (Show, Eq, Generic, Data) + +fromOriginalLineNumber :: +     SourceCodeTransformation -> (HaskellFilePath, Int) -> Either T.Text Int +fromOriginalLineNumber SourceCodeTransformation {linePragmas = pragmas} (_originalFileName, originalLineNumber) +  | S.null pragmas = Right originalLineNumber +fromOriginalLineNumber SourceCodeTransformation {fileIndex = index} (originalFileName, originalLineNumber) = +  case HM.lookup originalFileName index of +    Just set -> +      -- lookupGE finds smallest element greater or equal to the given one +      case S.lookupGE (FileLocation 1 originalLineNumber 1) set of +        Just FileLocation {..} -> Right $ originalLineNumber + offset +        Nothing -> +          Left $ +          T.concat +            [ "Cannot find " +            , T.pack . show $ (originalFileName, originalLineNumber) +            , " in " +            , T.pack $ show index +            ] +    Nothing -> +      Left $ +      T.concat +        [ "Cannot find file " +        , T.pack . show $ originalFileName +        , " in " +        , T.pack $ show index +        ] +         +data Declaration = Declaration +  { sort :: DeclarationSort +  , name :: T.Text +  , declType :: Maybe Type +  , isExported :: Bool +  , lineNumber :: Int +  } deriving (Show, Eq, Ord, Generic, Data) + +data DeclarationSort +  = TyClD +  | InstD +  | ValD +  | ForD +  deriving (Show, Eq, Ord, Generic, Data) + +data LocationInfo +  = ExactLocation { packageId :: PackageId +                  , modulePath :: HaskellModulePath +                  , moduleName :: HaskellModuleName +                  , startLine :: Int +                  , endLine :: Int +                  , startColumn :: Int +                  , endColumn :: Int } +  | ApproximateLocation { packageId :: PackageId +                        , moduleName :: HaskellModuleName +                        , entity :: LocatableEntity +                        , name :: T.Text +                        , haddockAnchorId :: Maybe T.Text +                        , componentId :: ComponentId } +  | UnknownLocation T.Text +  deriving (Show, Eq, Ord, Generic, Data) + +data LocatableEntity +  = Typ +  | Val +  | Inst +  | Mod +  deriving (Show, Eq, Ord, Generic, Data) + +-------------------------------------------------------------------------------- +-- Instances +-------------------------------------------------------------------------------- + +deriving instance (Data k) => Data (IVM.Interval k) + +instance (Data k, Data v, Eq k, Ord k, Data (IVM.Interval k)) => +         Data (IVM.IntervalMap k v) where +  gfoldl f z m = z IVM.fromList `f` IVM.toList m +  toConstr _ = fromListConstr +  gunfold k z c = +    case constrIndex c of +      1 -> k (z IVM.fromList) +      _ -> error "gunfold" +  dataTypeOf _ = intervalMapDataType +  dataCast2 = gcast2 + +fromListConstr :: Constr +fromListConstr = mkConstr intervalMapDataType "fromList" [] Prefix + +intervalMapDataType :: DataType +intervalMapDataType = mkDataType "Data.IntervalMap" [fromListConstr] + +deriving instance Generic (IVM.Interval k) + +instance Hashable HaskellModuleName +instance Serialize HaskellModuleName +instance Hashable HaskellModulePath +instance Serialize HaskellModulePath +instance Hashable HaskellFilePath +instance Serialize HaskellFilePath +instance (Serialize k, Serialize v, Ord k) => +         Serialize (IVM.IntervalMap k v) where +  put = put . IVM.toAscList +  get = IVM.fromAscList <$> Data.Serialize.get +instance Ord LinePragma where +  compare p1 p2 = +    compare +      (lineNumberPreprocessed (p1 :: LinePragma)) +      (lineNumberPreprocessed (p2 :: LinePragma)) +instance Ord FileLocation where +  compare l1 l2 = compare (lineEnd l1) (lineEnd l2) +instance Serialize LinePragma +instance Serialize FileLocation +instance Serialize SourceCodeTransformation +instance Serialize IdentifierInfo +instance Serialize InternalId +instance Serialize ExternalId +instance Serialize ExternalIdentifierInfo where +  put (ExternalIdentifierInfo info) = put info +  get = ExternalIdentifierInfo <$>(get :: Get IdentifierInfo) +instance Serialize InstanceResolution +instance Serialize OccName +instance Serialize IdDetails +instance Serialize NameSpace +instance Serialize DefinitionSiteMap +instance Serialize DefinitionSite +instance Serialize Declaration +instance Serialize NameSort +instance Serialize DeclarationSort +instance Serialize PackageId +instance Serialize Data.Version.Version +instance Serialize (PackageInfo ModuleInfo) +instance Serialize (PackageInfo CompactModuleInfo) +instance Serialize IdentifierSrcSpan +instance Serialize DirTree +instance Serialize ComponentId +instance Serialize ComponentType +instance Serialize T.Text where +  put = put . encodeUtf8 +  get = decodeUtf8 <$> Data.Serialize.get +instance (Serialize k, Serialize v, Eq k,Hashable k) => Serialize (HM.HashMap k v) where +  put = put . HM.toList +  get = HM.fromList <$> get +instance Serialize ModuleInfo +instance Serialize CompactModuleInfo +instance (Serialize k) => Serialize (IVM.Interval k) +instance Serialize LocationInfo +instance Serialize IdentifierOccurrence +instance Serialize IdentifierOccurrenceSort +instance Serialize TypeComponent +instance (Serialize a) => Serialize (V.Vector a) where +  put = put . V.toList +  get = V.fromList <$> get +instance Serialize Type +instance Serialize ExpressionInfo +instance Serialize LocatableEntity +instance (Serialize k,Ord k,Serialize v,Ord v,Hashable k) => Serialize (Trie k v) +instance NFData HaskellModuleName +instance NFData HaskellModulePath +instance NFData HaskellFilePath +instance NFData LinePragma +instance NFData FileLocation +instance NFData SourceCodeTransformation +instance NFData IdentifierInfo +instance NFData InternalId +instance NFData ExternalId +instance NFData ExternalIdentifierInfo  +instance NFData InstanceResolution +instance NFData IdDetails +instance NFData NameSpace +instance NFData OccName +instance NFData DefinitionSiteMap +instance NFData DefinitionSite +instance NFData Declaration +instance NFData NameSort +instance NFData DeclarationSort +instance NFData PackageId +instance NFData (PackageInfo ModuleInfo) +instance NFData (PackageInfo CompactModuleInfo) +instance NFData IdentifierSrcSpan +instance NFData DirTree +instance NFData ComponentId +instance NFData ComponentType +instance NFData ModuleInfo +instance NFData CompactModuleInfo +instance NFData LocationInfo +instance NFData IdentifierOccurrence +instance NFData IdentifierOccurrenceSort +instance NFData TypeComponent +instance NFData Type +instance NFData ExpressionInfo +instance NFData LocatableEntity +instance (NFData k, Ord k, NFData v, Ord v, Hashable k) => +         NFData (Trie k v) + +omitNothingOptions :: Options +omitNothingOptions = defaultOptions {omitNothingFields = True} + +instance A.ToJSON (PackageInfo a) where +  toJSON PackageInfo {..} = +    A.object +      [ ("id", A.toJSON $ packageIdToText id) +      , ("directoryTree", A.toJSON directoryTree) +      , ("modules", A.toJSON . HM.map (const ()) $ moduleMap) +      ] + +instance A.ToJSON ModuleInfo where +  toJSON ModuleInfo {..} = +    let sourceCodeLines = zip [1 ..] $ V.toList source +        tokenizedLines = +          L.map +            (\(lineNumber, lineText) -> +               case IM.lookup lineNumber idOccMap of +                 Just identifiers -> (lineNumber, tokenize lineText identifiers) +                 Nothing -> +                   ( lineNumber +                   , [(lineText, (1, T.length lineText + 1), Nothing)])) +            sourceCodeLines +        html = +          Html.table Html.! Attr.class_ "source-code" $ +          Html.tbody $ mapM_ (uncurry lineToHtml) tokenizedLines +     in A.object +          [ ("id", A.toJSON id) +          , ("name", A.toJSON name) +          , ("sourceCodeHtml", A.toJSON . renderHtml $ html) +          , ("identifiers", A.toJSON idInfoMap) +          , ("occurrences", A.toJSON $ idOccurrencesHashMap idOccMap) +          , ("declarations", A.toJSON declarations) +          ] +           +idOccurrencesHashMap :: +     IM.IntMap [((Int, Int), IdentifierOccurrence)] +  -> HM.HashMap T.Text IdentifierOccurrence +idOccurrencesHashMap = +  HM.fromList . +  concatMap +    (\(lineNum, occs) -> +       L.map +         (\((startCol, endCol), occ) -> +            (occurrenceLocationToText lineNum startCol endCol, occ)) +         occs) . +  IM.toList  + +idOccurrenceList :: +     IM.IntMap [((Int, Int), IdentifierOccurrence)] +  -> HM.HashMap T.Text IdentifierOccurrence +idOccurrenceList = +  HM.fromList . +  concatMap +    (\(lineNum, occs) -> +       L.map +         (\((startCol, endCol), occ) -> +            (occurrenceLocationToText lineNum startCol endCol, occ)) +         occs) . +  IM.toList  + +occurrenceLocationToText :: Int -> Int -> Int -> T.Text +occurrenceLocationToText lineNum startCol endCol = +  T.concat +    [ T.pack . show $ lineNum +    , "-" +    , T.pack . show $ startCol +    , "-" +    , T.pack . show $ endCol +    ] + +lineToHtml :: Int +           -> [(T.Text, (Int, Int), Maybe IdentifierOccurrence)] +           -> Html.Html +lineToHtml lineNumber tokens = +  Html.tr $ do +    Html.td Html.! Attr.class_ "line-number" Html.! +      Attr.id (Html.textValue . T.append "LN" . T.pack $ show lineNumber) $ +      Html.toHtml (T.pack $ show lineNumber) +    Html.td Html.! Attr.class_ "line-content" Html.! +      Html.dataAttribute "line" (Html.textValue $ T.pack . show $ lineNumber) Html.! +      Attr.id (Html.textValue . T.append "LC" . T.pack $ show lineNumber) $ +      mapM_ +        (\(content, (start, end), mbIdOcc) -> +           let addPositionAttrs :: Html.Html -> Html.Html +               addPositionAttrs htmlElement = +                 htmlElement Html.! +                 Html.dataAttribute +                   "start" +                   (Html.textValue $ T.pack . show $ start) Html.! +                 Html.dataAttribute "end" (Html.textValue $ T.pack . show $ end) +            in case mbIdOcc of +                 Just idOcc -> +                   addPositionAttrs $ +                   Html.span Html.! Attr.class_ "identifier" Html.! +                   Attr.id +                     (Html.textValue . +                      maybe "" getInternalId . internalIdFromRenamedSource $ +                      idOcc) Html.! +                   Html.dataAttribute +                     "occurrence" +                     (Html.textValue $ +                      occurrenceLocationToText lineNumber start end) Html.! +                   Html.dataAttribute +                     "identifier" +                     (Html.textValue $ +                      maybe "" getInternalId $ +                      internalId (idOcc :: IdentifierOccurrence)) $ +                   Html.toHtml content +                 Nothing -> addPositionAttrs . Html.span . Html.toHtml $ content) +        tokens + +tokenize +  :: forall a. +     T.Text -- ^ Source code +  -> [((Int, Int), a)] -- ^ Identifier locations +                       -- The end position is defined to be the column /after/ the end of the +                       -- span. That is, a span of (1,1)-(1,2) is one character long, and a +                       -- span of (1,1)-(1,1) is zero characters long.                        +  -> [(T.Text, (Int, Int), Maybe a)] +tokenize line = +  L.reverse . +  (\(remainingLine, currentIndex, c) -> +     if T.null remainingLine +       then c +       else (remainingLine, (currentIndex, T.length line + 1), Nothing) : c) . +  L.foldl' split (line, 1, []) +  where +    split :: +         (T.Text, Int, [(T.Text, (Int, Int), Maybe a)]) +      -> ((Int, Int), a) +      -> (T.Text, Int, [(T.Text, (Int, Int), Maybe a)]) +    split (remainingLine, currentIndex, chunks) ((start, end), a) +      | start == currentIndex = +        let (chunk, remainingLine') = T.splitAt (end - start) remainingLine +            chunks' = (chunk, (start, end), Just a) : chunks +         in (remainingLine', end, chunks') +      | otherwise = +        let (chunkNoId, remainingLine') = +              T.splitAt (start - currentIndex) remainingLine +            (chunk, remainingLine'') = T.splitAt (end - start) remainingLine' +         in ( remainingLine'' +            , end +            , (chunk, (start, end), Just a) : +              (chunkNoId, (currentIndex, start), Nothing) : chunks) + +docToHtml :: +     forall mod id. +     (mod -> Html.Html) +  -> (id -> Html.Html) +  -> DocH mod id +  -> HTML +docToHtml modToHtml idToHtml = toStrict . renderHtml . toH +  where +    toH :: DocH mod id -> Html.Html +    toH (DocAppend doc1 doc2) = toH doc1 >> toH doc2 +    toH (DocParagraph doc) = Html.p $ toH doc +    toH (DocIdentifier identifier) = Html.span $ idToHtml identifier +    toH (DocWarning doc) = Html.div Html.! Attr.class_ "warning" $ toH doc +    toH (DocEmphasis doc) = Html.em $ toH doc +    toH DocEmpty = mempty +    toH (DocBold doc) = Html.b $ toH doc +    toH (DocMonospaced doc) = +      Html.span Html.! Attr.class_ "source-code-font" $ toH doc +    toH (DocUnorderedList docs) = Html.ul $ mapM_ (Html.li . toH) docs +    toH (DocOrderedList docs) = Html.ol $ mapM_ (Html.li . toH) docs +    toH (DocDefList docs) = +      Html.dl $ +      mapM_ (\(doc1, doc2) -> Html.dt (toH doc1) >> Html.dd (toH doc2)) docs +    toH (DocCodeBlock doc) = Html.div Html.! Attr.class_ "source-code" $ toH doc +    toH (DocIdentifierUnchecked modName) = modToHtml modName +    toH (DocModule str) = Html.span . Html.toHtml . T.pack $ str +    toH (DocHyperlink (Hyperlink url mbTitle)) = +      Html.a Html.! (Attr.href . Html.textValue . T.pack $ url) $ +      Html.toHtml $ fromMaybe url mbTitle +    toH (DocPic (Picture uri mbTitle)) = +      Html.img Html.! (Attr.src . Html.textValue . T.pack $ uri) Html.! +      (Attr.title . Html.textValue . T.pack $ fromMaybe "" mbTitle) +    toH (DocMathInline str) = +      Html.span . Html.toHtml $ T.pack ("\\(" ++ str ++ "\\)") +    toH (DocMathDisplay str) = +      Html.div . Html.toHtml $ T.pack ("\\[" ++ str ++ "\\]") +    toH (DocAName str) = +      Html.a Html.! (Attr.id . Html.textValue . T.pack $ str) $ mempty +    toH (DocProperty str) = +      Html.div Html.! Attr.class_ "source-code" $ Html.toHtml $ T.pack str +    toH (DocExamples examples) = +      Html.div Html.! Attr.class_ "source-code" $ +      mapM_ +        (\(Example expr results) -> +           let htmlPrompt = Html.span $ Html.toHtml (">>> " :: String) +               htmlExpression = Html.span $ Html.toHtml (expr ++ "\n") +            in htmlPrompt >> htmlExpression >> +               mapM_ (Html.span . Html.toHtml) (unlines results)) +        examples +    toH (DocString str) = Html.span . Html.toHtml $ T.pack str     +    toH (DocHeader (Header level doc)) = toHeader level $ toH doc +      where +        toHeader 1 = Html.h1 +        toHeader 2 = Html.h2 +        toHeader 3 = Html.h3 +        toHeader 4 = Html.h4 +        toHeader 5 = Html.h5 +        toHeader _ = Html.h6 +         +instance A.ToJSON HaskellModuleName where +  toJSON (HaskellModuleName name) = A.String name +instance A.ToJSON HaskellModulePath where +  toJSON (HaskellModulePath path) = A.String path +instance A.ToJSON HaskellFilePath where +  toJSON (HaskellFilePath path) = A.String path   +instance A.ToJSON LinePragma where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON FileLocation where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON IdentifierInfo where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON InternalId where +  toJSON (InternalId text) = A.toJSON text +instance A.ToJSON ExternalId where +  toJSON (ExternalId text) = A.toJSON text     +instance A.ToJSON ExternalIdentifierInfo where +  toJSON (ExternalIdentifierInfo info) = A.toJSON info +instance A.ToJSON InstanceResolution where +  toJSON (Instance name typ types location instances) = +    A.object +      [ "name" A..= A.toJSON name +      , "types" A..= A.toJSON types +      , "location" A..= A.toJSON location +      , "instanceType" A..= A.toJSON typ +      , "instances" A..= +        (A.Array . V.fromList . Prelude.map A.toJSON $ instances) +      ] +  toJSON Stop = A.Null +instance A.ToJSON IdDetails where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON NameSpace where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON Declaration   +instance A.ToJSON NameSort +instance A.ToJSON OccName where +  toJSON (OccName name) = A.String name +instance A.ToJSON DeclarationSort +instance A.ToJSON PackageId +instance A.ToJSON ComponentId where +  toJSON (ComponentId id) = A.toJSON id +instance A.ToJSON ComponentType +instance A.ToJSON LocationInfo where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON LocatableEntity +instance A.ToJSON IdentifierOccurrence where +  toJSON IdentifierOccurrence {..} = +    A.object $ +    [("sort", A.toJSON sort)] ++ +    [("description", A.toJSON description)] ++ +    [("internalId", A.toJSON internalId) | isJust internalId] ++ +    [("isBinder", A.toJSON isBinder) | isBinder] ++ +    [("instanceResolution", A.toJSON instanceResolution) | isJust instanceResolution] ++ +    [("idOccType", A.toJSON idOccType) | isJust idOccType] +instance A.ToJSON IdentifierOccurrenceSort +instance A.ToJSON TypeComponent where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON Type where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON ExpressionInfo where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON DirTree     +instance A.ToJSON DefinitionSite where +  toJSON = A.genericToJSON omitNothingOptions +instance A.ToJSON IdentifierSrcSpan +instance A.ToJSON (IVM.Interval (Int, Int)) where +  toJSON (IVM.IntervalCO a b) = intervalToValue a b +  toJSON (IVM.ClosedInterval a b) = intervalToValue a b +  toJSON (IVM.OpenInterval a b) = intervalToValue a b +  toJSON (IVM.IntervalOC a b) = intervalToValue a b + +intervalToValue :: (Int, Int) -> (Int, Int) -> A.Value +intervalToValue (l1, c1) (l2, c2) = +  A.object +    [ ("start", A.object [("line", A.toJSON l1), ("column", A.toJSON c1)]) +    , ("end", A.object [("line", A.toJSON l2), ("column", A.toJSON c2)]) +    ] + +data SourceCodePreprocessing +  = AfterPreprocessing +  | BeforePreprocessing +  deriving (Show, Eq) + +data Log +  = StdOut +  | ToFile FilePath +  deriving (Show, Eq)  | 
