diff options
Diffstat (limited to 'haddock-api/src/Haddock/Interface')
| -rw-r--r-- | haddock-api/src/Haddock/Interface/AttachInstances.hs | 2 | ||||
| -rw-r--r-- | haddock-api/src/Haddock/Interface/Create.hs | 116 | ||||
| -rw-r--r-- | haddock-api/src/Haddock/Interface/Json.hs | 166 | ||||
| -rw-r--r-- | haddock-api/src/Haddock/Interface/LexParseRn.hs | 91 | ||||
| -rw-r--r-- | haddock-api/src/Haddock/Interface/ParseModuleHeader.hs | 229 | ||||
| -rw-r--r-- | haddock-api/src/Haddock/Interface/Rename.hs | 36 | ||||
| -rw-r--r-- | haddock-api/src/Haddock/Interface/Specialize.hs | 9 | 
7 files changed, 465 insertions, 184 deletions
| diff --git a/haddock-api/src/Haddock/Interface/AttachInstances.hs b/haddock-api/src/Haddock/Interface/AttachInstances.hs index 0d3f1ab5..6ef0ed19 100644 --- a/haddock-api/src/Haddock/Interface/AttachInstances.hs +++ b/haddock-api/src/Haddock/Interface/AttachInstances.hs @@ -22,7 +22,7 @@ import Haddock.Convert  import Control.Applicative ((<|>))  import Control.Arrow hiding ((<+>)) -import Data.List +import Data.List (sortBy)  import Data.Ord (comparing)  import Data.Maybe ( maybeToList, mapMaybe, fromMaybe )  import qualified Data.Map as Map diff --git a/haddock-api/src/Haddock/Interface/Create.hs b/haddock-api/src/Haddock/Interface/Create.hs index 76f1f765..72f1ab62 100644 --- a/haddock-api/src/Haddock/Interface/Create.hs +++ b/haddock-api/src/Haddock/Interface/Create.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE StandaloneDeriving, FlexibleInstances, MultiParamTypeClasses, CPP, TupleSections, BangPatterns, LambdaCase, NamedFieldPuns, ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving, FlexibleInstances, MultiParamTypeClasses, CPP, TupleSections, BangPatterns, LambdaCase, NamedFieldPuns, ScopedTypeVariables, RecordWildCards #-}  {-# LANGUAGE TypeFamilies #-}  {-# LANGUAGE ViewPatterns #-}  {-# LANGUAGE DerivingStrategies #-} @@ -38,6 +38,7 @@ import Data.Map (Map)  import Data.List  import Data.Maybe  import Data.Traversable +import GHC.Stack  import GHC.Tc.Utils.Monad (finalSafeMode)  import GHC.Types.Avail hiding (avail) @@ -65,7 +66,6 @@ import GHC.HsToCore.Docs hiding (mkMaps)  import GHC.Parser.Annotation (IsUnicodeSyntax(..))  import GHC.Unit.Module.Warnings -  newtype IfEnv m = IfEnv    {      -- | Lookup names in the enviroment. @@ -687,11 +687,11 @@ availExportItem is_sig modMap thisMod semMod warnings exportedNames                      let newDecl = L loc . SigD noExtField . fromJust $ filterSigNames (== t) sig                      in availExportDecl avail newDecl docs_ -                  L loc (TyClD _ cl@ClassDecl{}) -> do +                  L loc (TyClD _ ClassDecl {..}) -> do                      mdef <- minimalDef t                      let sig = maybeToList $ fmap (noLoc . MinimalSig noExtField NoSourceText . noLoc . fmap noLoc) mdef                      availExportDecl avail -                      (L loc $ TyClD noExtField cl { tcdSigs = sig ++ tcdSigs cl }) docs_ +                      (L loc $ TyClD noExtField ClassDecl { tcdSigs = sig ++ tcdSigs, .. }) docs_                    _ -> availExportDecl avail decl docs_ @@ -716,11 +716,24 @@ availExportItem is_sig modMap thisMod semMod warnings exportedNames          _ -> return [] -    availExportDecl :: AvailInfo -> LHsDecl GhcRn +    -- Tries 'extractDecl' first then falls back to 'hiDecl' if that fails +    availDecl :: Name -> LHsDecl GhcRn -> IfM m (LHsDecl GhcRn) +    availDecl declName parentDecl = +      case extractDecl declMap declName parentDecl of +        Right d -> pure d +        Left err -> do +          synifiedDeclOpt <- hiDecl dflags declName +          case synifiedDeclOpt of +            Just synifiedDecl -> pure synifiedDecl +            Nothing -> pprPanic "availExportItem" (O.text err) + +    availExportDecl :: HasCallStack => AvailInfo -> LHsDecl GhcRn                      -> (DocForDecl Name, [(Name, DocForDecl Name)])                      -> IfM m [ ExportItem GhcRn ]      availExportDecl avail decl (doc, subs)        | availExportsDecl avail = do +          extractedDecl <- availDecl (availName avail) decl +            -- bundled pattern synonyms only make sense if the declaration is            -- exported (otherwise there would be nothing to bundle to)            bundledPatSyns <- findBundledPatterns avail @@ -736,8 +749,7 @@ availExportItem is_sig modMap thisMod semMod warnings exportedNames                  ]            return [ ExportDecl { -                       expItemDecl      = restrictTo (fmap fst subs) -                                            (extractDecl declMap (availName avail) decl) +                       expItemDecl      = restrictTo (fmap fst subs) extractedDecl                       , expItemPats      = bundledPatSyns                       , expItemMbDoc     = doc                       , expItemSubDocs   = subs @@ -747,18 +759,18 @@ availExportItem is_sig modMap thisMod semMod warnings exportedNames                       }                   ] -      | otherwise = -          return [ ExportDecl { -                       expItemDecl      = extractDecl declMap sub decl +      | otherwise = for subs $ \(sub, sub_doc) -> do +          extractedDecl <- availDecl sub decl + +          return ( ExportDecl { +                       expItemDecl      = extractedDecl                       , expItemPats      = []                       , expItemMbDoc     = sub_doc                       , expItemSubDocs   = []                       , expItemInstances = []                       , expItemFixities  = [ (sub, f) | Just f <- [M.lookup sub fixMap] ]                       , expItemSpliced   = False -                     } -                 | (sub, sub_doc) <- subs -                 ] +                     } )      exportedNameSet = mkNameSet exportedNames      isExported n = elemNameSet n exportedNameSet @@ -975,16 +987,26 @@ fullModuleContents is_sig modMap pkgName thisMod semMod warnings gre exportedNam  -- it might be an individual record selector or a class method.  In these  -- cases we have to extract the required declaration (and somehow cobble  -- together a type signature for it...). -extractDecl :: DeclMap -> Name -> LHsDecl GhcRn -> LHsDecl GhcRn +-- +-- This function looks through the declarations in this module to try to find +-- the one with the right name. +extractDecl +  :: HasCallStack +  => DeclMap                   -- ^ all declarations in the file +  -> Name                      -- ^ name of the declaration to extract +  -> LHsDecl GhcRn             -- ^ parent declaration +  -> Either ErrMsg (LHsDecl GhcRn)  extractDecl declMap name decl -  | name `elem` getMainDeclBinder (unLoc decl) = decl +  | name `elem` getMainDeclBinder (unLoc decl) = pure decl    | otherwise  =      case unLoc decl of -      TyClD _ d@ClassDecl {} -> +      TyClD _ d@ClassDecl { tcdLName = L _ clsNm +                          , tcdSigs = clsSigs +                          , tcdATs = clsATs } ->          let            matchesMethod =              [ lsig -            | lsig <- tcdSigs d +            | lsig <- clsSigs              , ClassOpSig _ False _ _ <- pure $ unLoc lsig                -- Note: exclude `default` declarations (see #505)              , name `elem` sigName lsig @@ -992,29 +1014,31 @@ extractDecl declMap name decl            matchesAssociatedType =              [ lfam_decl -            | lfam_decl <- tcdATs d +            | lfam_decl <- clsATs              , name == unLoc (fdLName (unLoc lfam_decl))              ]              -- TODO: document fixity          in case (matchesMethod, matchesAssociatedType)  of -          ([s0], _) -> let (n, tyvar_names) = (tcdName d, tyClDeclTyVars d) -                           L pos sig = addClassContext n tyvar_names s0 -                       in L pos (SigD noExtField sig) -          (_, [L pos fam_decl]) -> L pos (TyClD noExtField (FamDecl noExtField fam_decl)) +          ([s0], _) -> let tyvar_names = tyClDeclTyVars d +                           L pos sig = addClassContext clsNm tyvar_names s0 +                       in pure (L pos (SigD noExtField sig)) +          (_, [L pos fam_decl]) -> pure (L pos (TyClD noExtField (FamDecl noExtField fam_decl)))            ([], [])              | Just (famInstDecl:_) <- M.lookup name declMap              -> extractDecl declMap name famInstDecl -          _ -> pprPanic "extractDecl" (O.text "Ambiguous decl for" O.<+> O.ppr name O.<+> O.text "in class:" -                                         O.$$ O.nest 4 (O.ppr d) -                                         O.$$ O.text "Matches:" -                                         O.$$ O.nest 4 (O.ppr matchesMethod O.<+> O.ppr matchesAssociatedType)) -      TyClD _ d@DataDecl {} -> -        let (n, tyvar_tys) = (tcdName d, lHsQTyVarsToTypes (tyClDeclTyVars d)) -        in if isDataConName name -           then SigD noExtField <$> extractPatternSyn name n tyvar_tys (dd_cons (tcdDataDefn d)) -           else SigD noExtField <$> extractRecSel name n tyvar_tys (dd_cons (tcdDataDefn d)) +          _ -> Left (concat [ "Ambiguous decl for ", getOccString name +                            , " in class ", getOccString clsNm ]) + +      TyClD _ d@DataDecl { tcdLName = L _ dataNm +                         , tcdDataDefn = HsDataDefn { dd_cons = dataCons } } -> do +        let ty_args = lHsQTyVarsToTypes (tyClDeclTyVars d) +        lsig <- if isDataConName name +                  then extractPatternSyn name dataNm ty_args dataCons +                  else extractRecSel name dataNm ty_args dataCons +        pure (SigD noExtField <$> lsig) +        TyClD _ FamDecl {}          | isValName name          , Just (famInst:_) <- M.lookup name declMap @@ -1024,8 +1048,8 @@ extractDecl declMap name decl                                      , feqn_pats  = tys                                      , feqn_rhs   = defn }))) ->          if isDataConName name -        then SigD noExtField <$> extractPatternSyn name n tys (dd_cons defn) -        else SigD noExtField <$> extractRecSel name n tys (dd_cons defn) +        then fmap (SigD noExtField) <$> extractPatternSyn name n tys (dd_cons defn) +        else fmap (SigD noExtField) <$> extractRecSel name n tys (dd_cons defn)        InstD _ (ClsInstD _ ClsInstDecl { cid_datafam_insts = insts })          | isDataConName name ->              let matches = [ d' | L _ d'@(DataFamInstDecl (FamEqn { feqn_rhs = dd })) <- insts @@ -1033,9 +1057,9 @@ extractDecl declMap name decl                                 ]              in case matches of                  [d0] -> extractDecl declMap name (noLoc (InstD noExtField (DataFamInstD noExtField d0))) -                _    -> error "internal: extractDecl (ClsInstD)" +                _    -> Left "internal: extractDecl (ClsInstD)"          | otherwise -> -            let matches = [ d' | L _ d'@(DataFamInstDecl d) +            let matches = [ d' | L _ d'@(DataFamInstDecl d )                                     <- insts                                   -- , L _ ConDecl { con_details = RecCon rec } <- dd_cons (feqn_rhs d)                                 , Just rec <- map (getRecConArgs_maybe . unLoc) (dd_cons (feqn_rhs d)) @@ -1045,16 +1069,18 @@ extractDecl declMap name decl                            ]              in case matches of                [d0] -> extractDecl declMap name (noLoc . InstD noExtField $ DataFamInstD noExtField d0) -              _ -> error "internal: extractDecl (ClsInstD)" -      _ -> pprPanic "extractDecl" $ -        O.text "Unhandled decl for" O.<+> O.ppr name O.<> O.text ":" -        O.$$ O.nest 4 (O.ppr decl) +              _ -> Left "internal: extractDecl (ClsInstD)" +      _ -> Left ("extractDecl: Unhandled decl for " ++ getOccString name) -extractPatternSyn :: Name -> Name -> [LHsTypeArg GhcRn] -> [LConDecl GhcRn] -> LSig GhcRn +extractPatternSyn :: HasCallStack +                  => Name -> Name +                  -> [LHsTypeArg GhcRn] -> [LConDecl GhcRn] +                  -> Either ErrMsg (LSig GhcRn)  extractPatternSyn nm t tvs cons =    case filter matches cons of -    [] -> error "extractPatternSyn: constructor pattern not found" -    con:_ -> extract <$> con +    [] -> Left . O.showSDocOneLine O.defaultSDocContext $ +          O.text "constructor pattern " O.<+> O.ppr nm O.<+> O.text "not found in type" O.<+> O.ppr t +    con:_ -> pure (extract <$> con)   where    matches :: LConDecl GhcRn -> Bool    matches (L _ con) = nm `elem` (unLoc <$> getConNames con) @@ -1089,13 +1115,13 @@ extractPatternSyn nm t tvs cons =                            mkAppTyArg f (HsArgPar _) = HsParTy noExtField f  extractRecSel :: Name -> Name -> [LHsTypeArg GhcRn] -> [LConDecl GhcRn] -              -> LSig GhcRn -extractRecSel _ _ _ [] = error "extractRecSel: selector not found" +              -> Either ErrMsg (LSig GhcRn) +extractRecSel _ _ _ [] = Left "extractRecSel: selector not found"  extractRecSel nm t tvs (L _ con : rest) =    case getRecConArgs_maybe con of      Just (L _ fields) | ((l,L _ (ConDeclField _ _nn ty _)) : _) <- matching_fields fields -> -      L l (TypeSig noExtField [noLoc nm] (mkEmptySigWcType (noLoc (HsFunTy noExtField (HsUnrestrictedArrow NormalSyntax) data_ty (getBangType ty))))) +      pure (L l (TypeSig noExtField [noLoc nm] (mkEmptyWildCardBndrs $ mkEmptySigType (noLoc (HsFunTy noExtField (HsUnrestrictedArrow NormalSyntax) data_ty (getBangType ty))))))      _ -> extractRecSel nm t tvs rest   where    matching_fields :: [LConDeclField GhcRn] -> [(SrcSpan, LConDeclField GhcRn)] diff --git a/haddock-api/src/Haddock/Interface/Json.hs b/haddock-api/src/Haddock/Interface/Json.hs index df585f29..9b80d98f 100644 --- a/haddock-api/src/Haddock/Interface/Json.hs +++ b/haddock-api/src/Haddock/Interface/Json.hs @@ -13,7 +13,6 @@ import GHC.Utils.Outputable  import Control.Arrow  import Data.Map (Map) -import Data.Bifunctor  import qualified Data.Map as Map  import Haddock.Types @@ -58,11 +57,172 @@ jsonMap f g = jsonObject . map (f *** g) . Map.toList  jsonMDoc :: MDoc Name -> JsonDoc  jsonMDoc MetaDoc{..} =    jsonObject [ ("meta", jsonObject [("version", jsonMaybe (jsonString . show) (_version _meta))]) -             , ("doc",  jsonDoc _doc) +             , ("document",  jsonDoc _doc)               ] +showModName :: Wrap (ModuleName, OccName) -> String +showModName = showWrapped (moduleNameString . fst) + +showName :: Wrap Name -> String +showName = showWrapped nameStableString + +  jsonDoc :: Doc Name -> JsonDoc -jsonDoc doc = jsonString (show (bimap (moduleNameString . fst) nameStableString doc)) + +jsonDoc DocEmpty = jsonObject +    [ ("tag", jsonString "DocEmpty") ] + +jsonDoc (DocAppend x y) = jsonObject +    [ ("tag", jsonString "DocAppend") +    , ("first", jsonDoc x) +    , ("second", jsonDoc y) +    ] + +jsonDoc (DocString s) = jsonObject +    [ ("tag", jsonString "DocString") +    , ("string", jsonString s) +    ] + +jsonDoc (DocParagraph x) = jsonObject +    [ ("tag", jsonString "DocParagraph") +    , ("document", jsonDoc x) +    ] + +jsonDoc (DocIdentifier name) = jsonObject +    [ ("tag", jsonString "DocIdentifier") +    , ("name", jsonString (showName name)) +    ] + +jsonDoc (DocIdentifierUnchecked modName) = jsonObject +    [ ("tag", jsonString "DocIdentifierUnchecked") +    , ("modName", jsonString (showModName modName)) +    ] + +jsonDoc (DocModule s) = jsonObject +    [ ("tag", jsonString "DocModule") +    , ("string", jsonString s) +    ] + +jsonDoc (DocWarning x) = jsonObject +    [ ("tag", jsonString "DocWarning") +    , ("document", jsonDoc x) +    ] + +jsonDoc (DocEmphasis x) = jsonObject +    [ ("tag", jsonString "DocEmphasis") +    , ("document", jsonDoc x) +    ] + +jsonDoc (DocMonospaced x) = jsonObject +    [ ("tag", jsonString "DocMonospaced") +    , ("document", jsonDoc x) +    ] + +jsonDoc (DocBold x) = jsonObject +    [ ("tag", jsonString "DocBold") +    , ("document", jsonDoc x) +    ] + +jsonDoc (DocUnorderedList xs) = jsonObject +    [ ("tag", jsonString "DocUnorderedList") +    , ("documents", jsonArray (fmap jsonDoc xs)) +    ] + +jsonDoc (DocOrderedList xs) = jsonObject +    [ ("tag", jsonString "DocOrderedList") +    , ("documents", jsonArray (fmap jsonDoc xs)) +    ] + +jsonDoc (DocDefList xys) = jsonObject +    [ ("tag", jsonString "DocDefList") +    , ("definitions", jsonArray (fmap jsonDef xys)) +    ] +  where +    jsonDef (x, y) = jsonObject [("document", jsonDoc x), ("y", jsonDoc y)] + +jsonDoc (DocCodeBlock x) = jsonObject +    [ ("tag", jsonString "DocCodeBlock") +    , ("document", jsonDoc x) +    ] + +jsonDoc (DocHyperlink hyperlink) = jsonObject +    [ ("tag", jsonString "DocHyperlink") +    , ("hyperlink", jsonHyperlink hyperlink) +    ] +  where +    jsonHyperlink Hyperlink{..} = jsonObject +        [ ("hyperlinkUrl", jsonString hyperlinkUrl) +        , ("hyperlinkLabel", jsonMaybe jsonDoc hyperlinkLabel) +        ] + +jsonDoc (DocPic picture) = jsonObject +    [ ("tag", jsonString "DocPic") +    , ("picture", jsonPicture picture) +    ] +  where +    jsonPicture Picture{..} = jsonObject +        [ ("pictureUrl", jsonString pictureUri) +        , ("pictureLabel", jsonMaybe jsonString pictureTitle) +        ] + +jsonDoc (DocMathInline s) = jsonObject +    [ ("tag", jsonString "DocMathInline") +    , ("string", jsonString s) +    ] + +jsonDoc (DocMathDisplay s) = jsonObject +    [ ("tag", jsonString "DocMathDisplay") +    , ("string", jsonString s) +    ] + +jsonDoc (DocAName s) = jsonObject +    [ ("tag", jsonString "DocAName") +    , ("string", jsonString s) +    ] + +jsonDoc (DocProperty s) = jsonObject +    [ ("tag", jsonString "DocProperty") +    , ("string", jsonString s) +    ] + +jsonDoc (DocExamples examples) = jsonObject +    [ ("tag", jsonString "DocExamples") +    , ("examples", jsonArray (fmap jsonExample examples)) +    ] +  where +    jsonExample Example{..} = jsonObject +        [ ("exampleExpression", jsonString exampleExpression) +        , ("exampleResult", jsonArray (fmap jsonString exampleResult)) +        ] + +jsonDoc (DocHeader header) = jsonObject +    [ ("tag", jsonString "DocHeader") +    , ("header", jsonHeader header) +    ] +  where +    jsonHeader Header{..} = jsonObject +        [ ("headerLevel", jsonInt headerLevel) +        , ("headerTitle", jsonDoc headerTitle) +        ] + +jsonDoc (DocTable table) = jsonObject +    [ ("tag", jsonString "DocTable") +    , ("table", jsonTable table) +    ] +  where +    jsonTable Table{..} = jsonObject +        [ ("tableHeaderRows", jsonArray (fmap jsonTableRow tableHeaderRows)) +        , ("tableBodyRows", jsonArray (fmap jsonTableRow tableBodyRows)) +        ] + +    jsonTableRow TableRow{..} = jsonArray (fmap jsonTableCell tableRowCells) + +    jsonTableCell TableCell{..} = jsonObject +        [ ("tableCellColspan", jsonInt tableCellColspan) +        , ("tableCellRowspan", jsonInt tableCellRowspan) +        , ("tableCellContents", jsonDoc tableCellContents) +        ] +  jsonModule :: Module -> JsonDoc  jsonModule = JSString . moduleStableString diff --git a/haddock-api/src/Haddock/Interface/LexParseRn.hs b/haddock-api/src/Haddock/Interface/LexParseRn.hs index 87064a0f..2df2bbbf 100644 --- a/haddock-api/src/Haddock/Interface/LexParseRn.hs +++ b/haddock-api/src/Haddock/Interface/LexParseRn.hs @@ -20,9 +20,9 @@ module Haddock.Interface.LexParseRn    , processModuleHeader    ) where -import GHC.Types.Avail  import Control.Arrow  import Control.Monad +import Data.Functor  import Data.List ((\\), maximumBy)  import Data.Ord  import Documentation.Haddock.Doc (metaDocConcat) @@ -33,10 +33,11 @@ import Haddock.Interface.ParseModuleHeader  import Haddock.Parser  import Haddock.Types  import GHC.Types.Name +import GHC.Types.Avail ( availName ) +import GHC.Parser.PostProcess  import GHC.Driver.Ppr ( showPpr, showSDoc )  import GHC.Types.Name.Reader  import GHC.Data.EnumSet as EnumSet -import GHC.Rename.Env (dataTcOccs)  processDocStrings :: DynFlags -> Maybe Package -> GlobalRdrEnv -> [HsDocString]                    -> ErrMsgM (Maybe (MDoc Name)) @@ -90,24 +91,38 @@ processModuleHeader dflags pkgName gre safety mayStr = do  -- fallbacks in case we can't locate the identifiers.  --  -- See the comments in the source for implementation commentary. -rename :: DynFlags -> GlobalRdrEnv -> Doc RdrName -> ErrMsgM (Doc Name) +rename :: DynFlags -> GlobalRdrEnv -> Doc NsRdrName -> ErrMsgM (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 +      DocIdentifier i -> do +        let NsRdrName ns x = unwrap i +            occ = rdrNameOcc x +            isValueName = isDataOcc occ || isVarOcc occ + +        let valueNsChoices | isValueName = [x] +                           | otherwise   = [] -- is this ever possible? +            typeNsChoices  | isValueName = [setRdrNameSpace x tcName] +                           | otherwise   = [x] +          -- Generate the choices for the possible kind of thing this -        -- is. -        let choices = dataTcOccs x +        -- is. We narrow down the possibilities with the namespace (if +        -- there is one). +        let choices = case ns of +                        Value -> valueNsChoices +                        Type  -> typeNsChoices +                        None  -> valueNsChoices ++ typeNsChoices          -- Lookup any GlobalRdrElts that match the choices.          case concatMap (\c -> lookupGRE_RdrName c gre) choices of            -- We found no names in the env so we start guessing.            [] ->              case choices of -              -- This shouldn't happen as 'dataTcOccs' always returns at least its input. -              [] -> pure (DocMonospaced (DocString (showPpr dflags x))) +              -- The only way this can happen is if a value namespace was +              -- specified on something that cannot be a value. +              [] -> invalidValue dflags i                -- There was nothing in the environment so we need to                -- pick some default from what's available to us. We @@ -117,14 +132,14 @@ rename dflags gre = rn                -- 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 +              a:_ -> outOfScope dflags ns (i $> a)            -- There is only one name in the environment that matches so            -- use it. -          [a] -> pure (DocIdentifier (greMangledName a)) +          [a] -> pure $ DocIdentifier (i $> greMangledName a)            -- There are multiple names available. -          gres -> ambiguous dflags x gres +          gres -> ambiguous dflags i gres        DocWarning doc -> DocWarning <$> rn doc        DocEmphasis doc -> DocEmphasis <$> rn doc @@ -156,19 +171,25 @@ rename dflags gre = rn  -- 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 -> ErrMsgM (Doc a) -outOfScope dflags x = -  case x of -    Unqual occ -> warnAndMonospace occ -    Qual mdl occ -> pure (DocIdentifierUnchecked (mdl, occ)) -    Orig _ occ -> warnAndMonospace occ -    Exact name -> warnAndMonospace name  -- Shouldn't happen since x is out of scope +outOfScope :: DynFlags -> Namespace -> Wrap RdrName -> ErrMsgM (Doc a) +outOfScope dflags ns x = +  case unwrap x of +    Unqual occ -> warnAndMonospace (x $> occ) +    Qual mdl occ -> pure (DocIdentifierUnchecked (x $> (mdl, occ))) +    Orig _ occ -> warnAndMonospace (x $> occ) +    Exact name -> warnAndMonospace (x $> name)  -- Shouldn't happen since x is out of scope    where +    prefix = case ns of +               Value -> "the value " +               Type -> "the type " +               None -> "" +      warnAndMonospace a = do -      tell ["Warning: '" ++ showPpr dflags a ++ "' is out of scope.\n" ++ +      let a' = showWrapped (showPpr dflags) a +      tell ["Warning: " ++ prefix ++ "'" ++ a' ++ "' is out of scope.\n" ++              "    If you qualify the identifier, haddock can try to link it anyway."] -      pure (monospaced a) -    monospaced a = DocMonospaced (DocString (showPpr dflags a)) +      pure (monospaced a') +    monospaced = DocMonospaced . DocString  -- | Handle ambiguous identifiers.  -- @@ -176,26 +197,42 @@ outOfScope dflags x =  --  -- Emits a warning if the 'GlobalRdrElts's don't belong to the same type or class.  ambiguous :: DynFlags -          -> RdrName +          -> Wrap NsRdrName            -> [GlobalRdrElt] -- ^ More than one @gre@s sharing the same `RdrName` above.            -> ErrMsgM (Doc Name)  ambiguous dflags x gres = do    let noChildren = map availName (gresToAvailInfo gres)        dflt = maximumBy (comparing (isLocalName &&& isTyConName)) noChildren -      msg = "Warning: " ++ x_str ++ " is ambiguous. It is defined\n" ++ +      msg = "Warning: " ++ showNsRdrName dflags x ++ " is ambiguous. It is defined\n" ++              concatMap (\n -> "    * " ++ defnLoc n ++ "\n") (map greMangledName gres) ++              "    You may be able to disambiguate the identifier by qualifying it or\n" ++ -            "    by hiding some imports.\n" ++ -            "    Defaulting to " ++ x_str ++ " defined " ++ defnLoc dflt +            "    by specifying the type/value namespace explicitly.\n" ++ +            "    Defaulting to the one defined " ++ defnLoc dflt    -- TODO: Once we have a syntax for namespace qualification (#667) we may also    -- want to emit a warning when an identifier is a data constructor for a type    -- of the same name, but not the only constructor.    -- For example, for @data D = C | D@, someone may want to reference the @D@    -- constructor.    when (length noChildren > 1) $ tell [msg] -  pure (DocIdentifier dflt) +  pure (DocIdentifier (x $> dflt))    where      isLocalName (nameSrcLoc -> RealSrcLoc {}) = True      isLocalName _ = False -    x_str = '\'' : showPpr dflags x ++ "'"      defnLoc = showSDoc dflags . pprNameDefnLoc + +-- | Handle value-namespaced names that cannot be for values. +-- +-- Emits a warning that the value-namespace is invalid on a non-value identifier. +invalidValue :: DynFlags -> Wrap NsRdrName -> ErrMsgM (Doc a) +invalidValue dflags x = do +  tell ["Warning: " ++ showNsRdrName dflags x ++ " cannot be value, yet it is\n" ++ +            "    namespaced as such. Did you mean to specify a type namespace\n" ++ +            "    instead?"] +  pure (DocMonospaced (DocString (showNsRdrName dflags x))) + +-- | Printable representation of a wrapped and namespaced name +showNsRdrName :: DynFlags -> Wrap NsRdrName -> String +showNsRdrName dflags = (\p i -> p ++ "'" ++ i ++ "'") <$> prefix <*> ident +  where +    ident = showWrapped (showPpr dflags . rdrName) +    prefix = renderNs . namespace . unwrap diff --git a/haddock-api/src/Haddock/Interface/ParseModuleHeader.hs b/haddock-api/src/Haddock/Interface/ParseModuleHeader.hs index 611d8b6f..3e464fbc 100644 --- a/haddock-api/src/Haddock/Interface/ParseModuleHeader.hs +++ b/haddock-api/src/Haddock/Interface/ParseModuleHeader.hs @@ -1,4 +1,6 @@  {-# OPTIONS_GHC -Wwarn #-} +{-# LANGUAGE DeriveFunctor #-} +  -----------------------------------------------------------------------------  -- |  -- Module      :  Haddock.Interface.ParseModuleHeader @@ -11,12 +13,12 @@  -----------------------------------------------------------------------------  module Haddock.Interface.ParseModuleHeader (parseModuleHeader) where -import Control.Monad (mplus) +import Control.Applicative (Alternative (..)) +import Control.Monad (ap)  import Data.Char  import GHC.Driver.Session  import Haddock.Parser  import Haddock.Types -import GHC.Types.Name.Reader  -- -----------------------------------------------------------------------------  -- Parsing module headers @@ -24,37 +26,47 @@ import GHC.Types.Name.Reader  -- NB.  The headers must be given in the order Module, Description,  -- Copyright, License, Maintainer, Stability, Portability, except that  -- any or all may be omitted. -parseModuleHeader :: DynFlags -> Maybe Package -> String -> (HaddockModInfo RdrName, MDoc RdrName) +parseModuleHeader :: DynFlags -> Maybe Package -> String -> (HaddockModInfo NsRdrName, MDoc NsRdrName)  parseModuleHeader dflags pkgName str0 =     let -      getKey :: String -> String -> (Maybe String,String) -      getKey key str = case parseKey key str of -         Nothing -> (Nothing,str) -         Just (value,rest) -> (Just value,rest) - -      (_moduleOpt,str1) = getKey "Module" str0 -      (descriptionOpt,str2) = getKey "Description" str1 -      (copyrightOpt,str3) = getKey "Copyright" str2 -      (licenseOpt,str4) = getKey "License" str3 -      (licenceOpt,str5) = getKey "Licence" str4 -      (spdxLicenceOpt,str6) = getKey "SPDX-License-Identifier" str5 -      (maintainerOpt,str7) = getKey "Maintainer" str6 -      (stabilityOpt,str8) = getKey "Stability" str7 -      (portabilityOpt,str9) = getKey "Portability" str8 +      kvs :: [(String, String)] +      str1 :: String + +      (kvs, str1) = maybe ([], str0) id $ runP fields str0 + +      -- trim whitespaces +      trim :: String -> String +      trim = dropWhile isSpace . reverse . dropWhile isSpace . reverse + +      getKey :: String -> Maybe String +      getKey key = fmap trim (lookup key kvs) + +      descriptionOpt = getKey "Description" +      copyrightOpt   = getKey "Copyright" +      licenseOpt     = getKey "License" +      licenceOpt     = getKey "Licence" +      spdxLicenceOpt = getKey "SPDX-License-Identifier" +      maintainerOpt  = getKey "Maintainer" +      stabilityOpt   = getKey "Stability" +      portabilityOpt = getKey "Portability"     in (HaddockModInfo {            hmi_description = parseString dflags <$> descriptionOpt,            hmi_copyright = copyrightOpt, -          hmi_license = spdxLicenceOpt `mplus` licenseOpt `mplus` licenceOpt, +          hmi_license = spdxLicenceOpt <|> licenseOpt <|> licenceOpt,            hmi_maintainer = maintainerOpt,            hmi_stability = stabilityOpt,            hmi_portability = portabilityOpt,            hmi_safety = Nothing,            hmi_language = Nothing, -- set in LexParseRn            hmi_extensions = [] -- also set in LexParseRn -          }, parseParas dflags pkgName str9) +          }, parseParas dflags pkgName str1) + +------------------------------------------------------------------------------- +-- Small parser to parse module header. +------------------------------------------------------------------------------- --- | This function is how we read keys. +-- | The below is a small parser framework how we read keys.  --  -- all fields in the header are optional and have the form  -- @@ -73,78 +85,105 @@ parseModuleHeader dflags pkgName str0 =  --  -- the value will be "this is a .. description" and the rest will begin  -- at "The module comment". -parseKey :: String -> String -> Maybe (String,String) -parseKey key toParse0 = -   do -      let -         (spaces0,toParse1) = extractLeadingSpaces (dropWhile (`elem` ['\r', '\n']) toParse0) - -         indentation = spaces0 -      afterKey0 <- extractPrefix key toParse1 -      let -         afterKey1 = extractLeadingSpaces afterKey0 -      afterColon0 <- case snd afterKey1 of -         ':':afterColon -> return afterColon -         _ -> Nothing -      let -         (_,afterColon1) = extractLeadingSpaces afterColon0 - -      return (scanKey True indentation afterColon1) -   where -      scanKey :: Bool -> String -> String -> (String,String) -      scanKey _       _           [] = ([],[]) -      scanKey isFirst indentation str = -         let -            (nextLine,rest1) = extractNextLine str - -            accept = isFirst || sufficientIndentation || allSpaces - -            sufficientIndentation = case extractPrefix indentation nextLine of -               Just (c:_) | isSpace c -> True -               _ -> False - -            allSpaces = case extractLeadingSpaces nextLine of -               (_,[]) -> True -               _ -> False -         in -            if accept -               then -                  let -                     (scanned1,rest2) = scanKey False indentation rest1 - -                     scanned2 = case scanned1 of -                        "" -> if allSpaces then "" else nextLine -                        _ -> nextLine ++ "\n" ++ scanned1 -                  in -                     (scanned2,rest2) -               else -                  ([],str) - -      extractLeadingSpaces :: String -> (String,String) -      extractLeadingSpaces [] = ([],[]) -      extractLeadingSpaces (s@(c:cs)) -         | isSpace c = -            let -               (spaces1,cs1) = extractLeadingSpaces cs -            in -               (c:spaces1,cs1) -         | otherwise = ([],s) - -      extractNextLine :: String -> (String,String) -      extractNextLine [] = ([],[]) -      extractNextLine (c:cs) -         | c == '\n' = -            ([],cs) -         | otherwise = -            let -               (line,rest) = extractNextLine cs -            in -               (c:line,rest) - -      -- comparison is case-insensitive. -      extractPrefix :: String -> String -> Maybe String -      extractPrefix [] s = Just s -      extractPrefix _ [] = Nothing -      extractPrefix (c1:cs1) (c2:cs2) -         | toUpper c1 == toUpper c2 = extractPrefix cs1 cs2 -         | otherwise = Nothing + +-- | 'C' is a 'Char' carrying its column. +-- +-- This let us make an indentation-aware parser, as we know current indentation. +-- by looking at the next character in the stream ('curInd'). +-- +-- Thus we can munch all spaces but only not-spaces which are indented. +-- +data C = C {-# UNPACK #-} !Int Char + +newtype P a = P { unP :: [C] -> Maybe ([C], a) } +  deriving Functor + +instance Applicative P where +    pure x = P $ \s -> Just (s, x) +    (<*>)  = ap + +instance Monad P where +    return = pure +    m >>= k = P $ \s0 -> do +        (s1, x) <- unP m s0 +        unP (k x) s1 + +instance Alternative P where +    empty   = P $ \_ -> Nothing +    a <|> b = P $ \s -> unP a s <|> unP b s + +runP :: P a -> String -> Maybe a +runP p input = fmap snd (unP p input') +  where +    input' = concat +        [ zipWith C [0..] l ++ [C (length l) '\n'] +        | l <- lines input +        ] + +------------------------------------------------------------------------------- +-- +------------------------------------------------------------------------------- + +curInd :: P Int +curInd = P $ \s -> Just . (,) s $ case s of +    []        -> 0 +    C i _ : _ -> i + +rest :: P String +rest = P $ \cs -> Just ([], [ c | C _ c <- cs ]) + +munch :: (Int -> Char -> Bool) -> P String +munch p = P $ \cs -> +    let (xs,ys) = takeWhileMaybe p' cs in Just (ys, xs) +  where +    p' (C i c) +        | p i c  = Just c +        | otherwise = Nothing + +munch1 :: (Int -> Char -> Bool) -> P String +munch1 p = P $ \s -> case s of +    [] -> Nothing +    (c:cs) | Just c' <- p' c -> let (xs,ys) = takeWhileMaybe p' cs in Just (ys, c' : xs) +           | otherwise -> Nothing +  where +    p' (C i c) +        | p i c  = Just c +        | otherwise = Nothing + +char :: Char -> P Char +char c = P $ \s -> case s of +    []                        -> Nothing +    (C _ c' : cs) | c == c'   -> Just (cs, c) +                  | otherwise -> Nothing + +skipSpaces :: P () +skipSpaces = P $ \cs -> Just (dropWhile (\(C _ c) -> isSpace c) cs, ()) + +takeWhileMaybe :: (a -> Maybe b) -> [a] -> ([b], [a]) +takeWhileMaybe f = go where +    go xs0@[] = ([], xs0) +    go xs0@(x:xs) = case f x of +        Just y  -> let (ys, zs) = go xs in (y : ys, zs) +        Nothing -> ([], xs0) + +------------------------------------------------------------------------------- +-- Fields +------------------------------------------------------------------------------- + +field :: Int -> P (String, String) +field i = do +    fn <- munch1 $ \_ c -> isAlpha c || c == '-' +    skipSpaces +    _ <- char ':' +    skipSpaces +    val <- munch $ \j c -> isSpace c || j > i +    return (fn, val) + +fields :: P ([(String, String)], String) +fields = do +    skipSpaces +    i  <- curInd +    fs <- many (field i) +    r  <- rest +    return (fs, r) + diff --git a/haddock-api/src/Haddock/Interface/Rename.hs b/haddock-api/src/Haddock/Interface/Rename.hs index 14032d15..b212adce 100644 --- a/haddock-api/src/Haddock/Interface/Rename.hs +++ b/haddock-api/src/Haddock/Interface/Rename.hs @@ -29,12 +29,22 @@ import GHC.Builtin.Types (eqTyCon_RDR)  import Control.Applicative  import Control.Arrow ( first )  import Control.Monad hiding (mapM) +import Data.List (intercalate)  import qualified Data.Map as Map hiding ( Map ) +import qualified Data.Set as Set  import Prelude hiding (mapM)  import GHC.HsToCore.Docs -renameInterface :: DynFlags -> LinkEnv -> Bool -> Interface -> ErrMsgM Interface -renameInterface dflags renamingEnv warnings iface = +-- | Traverse docstrings and ASTs in the Haddock interface, renaming 'Name' to +-- 'DocName'. +-- +-- What this really boils down to is: for each 'Name', figure out which of the +-- modules that export the name is the preferred place to link to. +-- +-- The renamed output gets written into fields in the Haddock interface record +-- that were previously left empty. +renameInterface :: DynFlags -> [String] -> LinkEnv -> Bool -> Interface -> ErrMsgM Interface +renameInterface _dflags ignoredSymbols renamingEnv warnings iface =    -- first create the local env, where every name exported by this module    -- is mapped to itself, and everything else comes from the global renaming @@ -69,8 +79,15 @@ renameInterface dflags renamingEnv warnings iface =        -- Note that since the renamed AST represents equality constraints as        -- @HasOpTy t1 eqTyCon_RDR t2@ (and _not_ as @HsEqTy t1 t2@), we need to        -- manually filter out 'eqTyCon_RDR' (aka @~@). -      strings = [ pretty dflags n + +      qualifiedName n = (moduleNameString $ moduleName $ nameModule n) <> "." <> getOccString n + +      ignoreSet = Set.fromList ignoredSymbols + +      strings = [ qualifiedName n +                  | n <- missingNames +                , not (qualifiedName n `Set.member` ignoreSet)                  , not (isSystemName n)                  , not (isBuiltInSyntax n)                  , Exact n /= eqTyCon_RDR @@ -82,7 +99,7 @@ renameInterface dflags renamingEnv warnings iface =      unless (OptHide `elem` ifaceOptions iface || null strings || not warnings) $        tell ["Warning: " ++ moduleString (ifaceMod iface) ++              ": could not find link destinations for:\n"++ -            unwords ("   " : strings) ] +            intercalate "\n\t- "  ("" : strings) ]      return $ iface { ifaceRnDoc         = finalModuleDoc,                       ifaceRnDocMap      = rnDocMap, @@ -130,6 +147,11 @@ lookupRn name = RnM $ \lkp ->      (False,maps_to) -> (maps_to, (name :))      (True, maps_to) -> (maps_to, id) +-- | Look up a 'Name' in the renaming environment, but don't warn if you don't +-- find the name. Prefer to use 'lookupRn' whenever possible. +lookupRnNoWarn :: Name -> RnM DocName +lookupRnNoWarn name = RnM $ \lkp -> (snd (lkp name), id) +  -- | Run the renamer action using lookup in a 'LinkEnv' as the lookup function.  -- Returns the renamed value along with a list of `Name`'s that could not be  -- renamed because they weren't in the environment. @@ -173,8 +195,8 @@ renameLDocHsSyn :: LHsDocString -> RnM LHsDocString  renameLDocHsSyn = return -renameDoc :: Traversable t => t Name -> RnM (t DocName) -renameDoc = traverse rename +renameDoc :: Traversable t => t (Wrap Name) -> RnM (t (Wrap DocName)) +renameDoc = traverse (traverse rename)  renameFnArgsDoc :: FnArgsDoc Name -> RnM (FnArgsDoc DocName)  renameFnArgsDoc = mapM renameDoc @@ -559,7 +581,7 @@ renameSig sig = case sig of      lnames' <- mapM renameL lnames      return $ FixSig noExtField (FixitySig noExtField lnames' fixity)    MinimalSig _ src (L l s) -> do -    s' <- traverse renameL s +    s' <- traverse (traverse lookupRnNoWarn) s      return $ MinimalSig noExtField src (L l s')    -- we have filtered out all other kinds of signatures in Interface.Create    _ -> error "expected TypeSig" diff --git a/haddock-api/src/Haddock/Interface/Specialize.hs b/haddock-api/src/Haddock/Interface/Specialize.hs index 85055bf4..c6d61d05 100644 --- a/haddock-api/src/Haddock/Interface/Specialize.hs +++ b/haddock-api/src/Haddock/Interface/Specialize.hs @@ -11,6 +11,7 @@ module Haddock.Interface.Specialize      ) where +import Haddock.GhcUtils ( hsTyVarBndrName )  import Haddock.Syb  import Haddock.Types @@ -58,13 +59,9 @@ specialize specs = go spec_map0  -- Again, it is just a convenience function around 'specialize'. Note that  -- length of type list should be the same as the number of binders.  specializeTyVarBndrs :: Data a => LHsQTyVars GhcRn -> [HsType GhcRn] -> a -> a -specializeTyVarBndrs bndrs typs = -    specialize $ zip bndrs' typs +specializeTyVarBndrs bndrs typs = specialize $ zip bndrs' typs    where -    bndrs' = map (bname . unLoc) . hsq_explicit $ bndrs -    bname (UserTyVar _ _ (L _ name)) = name -    bname (KindedTyVar _ _ (L _ name) _) = name -    bname (XTyVarBndr _) = error "haddock:specializeTyVarBndrs" +    bndrs' = map (hsTyVarBndrName . unLoc) . hsq_explicit $ bndrs | 
