    {-

    This module contains helper functions for reporting and creating
    unbound variables.

    -}
    module GHC.Rename.Unbound
       ( mkUnboundName
       , mkUnboundNameRdr
       , isUnboundName
       , reportUnboundName
       , unknownNameSuggestions
       , WhereLooking(..)
       , unboundName
       , unboundNameX
       , notInScopeErr
       , exactNameErr
       )
    where

    import GHC.Prelude

    import GHC.Driver.Session
    import GHC.Driver.Ppr

    import GHC.Tc.Utils.Monad
    import GHC.Builtin.Names ( mkUnboundName, isUnboundName, getUnique)
    import GHC.Utils.Outputable as Outputable
    import GHC.Utils.Misc

    import GHC.Data.Maybe
    import GHC.Data.FastString

    import GHC.Types.SrcLoc as SrcLoc
    import GHC.Types.Name
    import GHC.Types.Name.Reader
    import GHC.Types.Unique.DFM (udfmToList)

    import GHC.Unit.Module
    import GHC.Unit.Module.Imported
    import GHC.Unit.Home.ModInfo

    import Data.List (sortBy, partition, nub)
    import Data.Function ( on )

    {-
    ************************************************************************
    *                                                                      *
                   What to do when a lookup fails
    *                                                                      *
    ************************************************************************
    -}

    data WhereLooking = WL_Any        -- Any binding
                      | WL_Global     -- Any top-level binding (local or imported)
                      | WL_LocalTop   -- Any top-level binding in this module
                      | WL_LocalOnly
                            -- Only local bindings
                            -- (pattern synonyms declaractions,
                            -- see Note [Renaming pattern synonym variables])

    mkUnboundNameRdr :: RdrName -> Name
    mkUnboundNameRdr :: RdrName -> Name
    mkUnboundNameRdr RdrName
    rdr = OccName -> Name
    mkUnboundName (RdrName -> OccName
    rdrNameOcc RdrName
    rdr)

    reportUnboundName :: RdrName -> RnM Name
    reportUnboundName :: RdrName -> RnM Name
    reportUnboundName RdrName
    rdr = WhereLooking -> RdrName -> RnM Name
    unboundName WhereLooking
    WL_Any RdrName
    rdr

    unboundName :: WhereLooking -> RdrName -> RnM Name
    unboundName :: WhereLooking -> RdrName -> RnM Name
    unboundName WhereLooking
    wl RdrName
    rdr = WhereLooking -> RdrName -> SDoc -> RnM Name
    unboundNameX WhereLooking
    wl RdrName
    rdr SDoc
    Outputable.empty

    unboundNameX :: WhereLooking -> RdrName -> SDoc -> RnM Name
    unboundNameX :: WhereLooking -> RdrName -> SDoc -> RnM Name
    unboundNameX WhereLooking
    where_look RdrName
    rdr_name SDoc
    extra
      = do  { DynFlags
    dflags <- IOEnv (Env TcGblEnv TcLclEnv) DynFlags
    forall (m :: * -> *). HasDynFlags m => m DynFlags
    getDynFlags
            ; let show_helpful_errors :: Bool
    show_helpful_errors = GeneralFlag -> DynFlags -> Bool
    gopt GeneralFlag
    Opt_HelpfulErrors DynFlags
    dflags
                  err :: SDoc
    err = RdrName -> SDoc
    notInScopeErr RdrName
    rdr_name SDoc -> SDoc -> SDoc
    $$ SDoc
    extra
            ; if Bool -> Bool
    not Bool
    show_helpful_errors
              then SDoc -> TcRn ()
    addErr SDoc
    err
              else do { LocalRdrEnv
    local_env  <- RnM LocalRdrEnv
    getLocalRdrEnv
                      ; GlobalRdrEnv
    global_env <- TcRn GlobalRdrEnv
    getGlobalRdrEnv
                      ; ImportAvails
    impInfo <- TcRn ImportAvails
    getImports
                      ; Module
    currmod <- IOEnv (Env TcGblEnv TcLclEnv) Module
    forall (m :: * -> *). HasModule m => m Module
    getModule
                      ; HomePackageTable
    hpt <- TcRnIf TcGblEnv TcLclEnv HomePackageTable
    forall gbl lcl. TcRnIf gbl lcl HomePackageTable
    getHpt
                      ; let suggestions :: SDoc
    suggestions = WhereLooking
    -> DynFlags
    -> HomePackageTable
    -> Module
    -> GlobalRdrEnv
    -> LocalRdrEnv
    -> ImportAvails
    -> RdrName
    -> SDoc
    unknownNameSuggestions_ WhereLooking
    where_look
                              DynFlags
    dflags HomePackageTable
    hpt Module
    currmod GlobalRdrEnv
    global_env LocalRdrEnv
    local_env ImportAvails
    impInfo
                              RdrName
    rdr_name
                      ; SDoc -> TcRn ()
    addErr (SDoc
    err SDoc -> SDoc -> SDoc
    $$ SDoc
    suggestions) }
            ; Name -> RnM Name
    forall (m :: * -> *) a. Monad m => a -> m a
    return (RdrName -> Name
    mkUnboundNameRdr RdrName
    rdr_name) }

    notInScopeErr :: RdrName -> SDoc
    notInScopeErr :: RdrName -> SDoc
    notInScopeErr RdrName
    rdr_name
      = case RdrName -> Maybe Name
    isExact_maybe RdrName
    rdr_name of
          Just Name
    name -> Name -> SDoc
    exactNameErr Name
    name
          Maybe Name
    Nothing -> SDoc -> Int -> SDoc -> SDoc
    hang (String -> SDoc
    text String
    "Not in scope:")
                      Int
    2 (SDoc
    what SDoc -> SDoc -> SDoc
    <+> SDoc -> SDoc
    quotes (RdrName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr RdrName
    rdr_name))
      where
        what :: SDoc
    what = NameSpace -> SDoc
    pprNonVarNameSpace (OccName -> NameSpace
    occNameSpace (RdrName -> OccName
    rdrNameOcc RdrName
    rdr_name))

    type HowInScope = Either SrcSpan ImpDeclSpec
         -- Left loc    =>  locally bound at loc
         -- Right ispec =>  imported as specified by ispec

    -- | Called from the typechecker ("GHC.Tc.Errors") when we find an unbound variable
    unknownNameSuggestions :: DynFlags
                           -> HomePackageTable -> Module
                           -> GlobalRdrEnv -> LocalRdrEnv -> ImportAvails
                           -> RdrName -> SDoc
    unknownNameSuggestions :: DynFlags
    -> HomePackageTable
    -> Module
    -> GlobalRdrEnv
    -> LocalRdrEnv
    -> ImportAvails
    -> RdrName
    -> SDoc
    unknownNameSuggestions = WhereLooking
    -> DynFlags
    -> HomePackageTable
    -> Module
    -> GlobalRdrEnv
    -> LocalRdrEnv
    -> ImportAvails
    -> RdrName
    -> SDoc
    unknownNameSuggestions_ WhereLooking
    WL_Any

    unknownNameSuggestions_ :: WhereLooking -> DynFlags
                           -> HomePackageTable -> Module
                           -> GlobalRdrEnv -> LocalRdrEnv -> ImportAvails
                           -> RdrName -> SDoc
    unknownNameSuggestions_ :: WhereLooking
    -> DynFlags
    -> HomePackageTable
    -> Module
    -> GlobalRdrEnv
    -> LocalRdrEnv
    -> ImportAvails
    -> RdrName
    -> SDoc
    unknownNameSuggestions_ WhereLooking
    where_look DynFlags
    dflags HomePackageTable
    hpt Module
    curr_mod GlobalRdrEnv
    global_env LocalRdrEnv
    local_env
                              ImportAvails
    imports RdrName
    tried_rdr_name =
        WhereLooking
    -> DynFlags -> GlobalRdrEnv -> LocalRdrEnv -> RdrName -> SDoc
    similarNameSuggestions WhereLooking
    where_look DynFlags
    dflags GlobalRdrEnv
    global_env LocalRdrEnv
    local_env RdrName
    tried_rdr_name SDoc -> SDoc -> SDoc
    $$
        WhereLooking
    -> GlobalRdrEnv
    -> HomePackageTable
    -> Module
    -> ImportAvails
    -> RdrName
    -> SDoc
    importSuggestions WhereLooking
    where_look GlobalRdrEnv
    global_env HomePackageTable
    hpt
                          Module
    curr_mod ImportAvails
    imports RdrName
    tried_rdr_name SDoc -> SDoc -> SDoc
    $$
        RdrName -> SDoc
    extensionSuggestions RdrName
    tried_rdr_name SDoc -> SDoc -> SDoc
    $$
        GlobalRdrEnv -> RdrName -> SDoc
    fieldSelectorSuggestions GlobalRdrEnv
    global_env RdrName
    tried_rdr_name

    -- | When the name is in scope as field whose selector has been suppressed by
    -- NoFieldSelectors, display a helpful message explaining this.
    fieldSelectorSuggestions :: GlobalRdrEnv -> RdrName -> SDoc
    fieldSelectorSuggestions :: GlobalRdrEnv -> RdrName -> SDoc
    fieldSelectorSuggestions GlobalRdrEnv
    global_env RdrName
    tried_rdr_name
      | [GlobalRdrElt] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [GlobalRdrElt]
    gres = SDoc
    Outputable.empty
      | Bool
    otherwise = String -> SDoc
    text String
    "NB:"
          SDoc -> SDoc -> SDoc
    <+> SDoc -> SDoc
    quotes (RdrName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr RdrName
    tried_rdr_name)
          SDoc -> SDoc -> SDoc
    <+> String -> SDoc
    text String
    "is a field selector" SDoc -> SDoc -> SDoc
    <+> SDoc
    whose
          SDoc -> SDoc -> SDoc
    $$ String -> SDoc
    text String
    "that has been suppressed by NoFieldSelectors"
      where
        gres :: [GlobalRdrElt]
    gres = (GlobalRdrElt -> Bool) -> [GlobalRdrElt] -> [GlobalRdrElt]
    forall a. (a -> Bool) -> [a] -> [a]
    filter GlobalRdrElt -> Bool
    isNoFieldSelectorGRE ([GlobalRdrElt] -> [GlobalRdrElt])
    -> [GlobalRdrElt] -> [GlobalRdrElt]
    forall a b. (a -> b) -> a -> b
    $
                   RdrName -> GlobalRdrEnv -> [GlobalRdrElt]
    lookupGRE_RdrName' RdrName
    tried_rdr_name GlobalRdrEnv
    global_env
        parents :: [Name]
    parents = [ Name
    parent | ParentIs Name
    parent <- (GlobalRdrElt -> Parent) -> [GlobalRdrElt] -> [Parent]
    forall a b. (a -> b) -> [a] -> [b]
    map GlobalRdrElt -> Parent
    gre_par [GlobalRdrElt]
    gres ]

        -- parents may be empty if this is a pattern synonym field without a selector
        whose :: SDoc
    whose | [Name] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [Name]
    parents = SDoc
    empty
              | Bool
    otherwise    = String -> SDoc
    text String
    "belonging to the type" SDoc -> SDoc -> SDoc
    <> [Name] -> SDoc
    forall a. [a] -> SDoc
    plural [Name]
    parents
                                 SDoc -> SDoc -> SDoc
    <+> [Name] -> SDoc
    forall a. Outputable a => [a] -> SDoc
    pprQuotedList [Name]
    parents

    similarNameSuggestions :: WhereLooking -> DynFlags
                            -> GlobalRdrEnv -> LocalRdrEnv
                            -> RdrName -> SDoc
    similarNameSuggestions :: WhereLooking
    -> DynFlags -> GlobalRdrEnv -> LocalRdrEnv -> RdrName -> SDoc
    similarNameSuggestions WhereLooking
    where_look DynFlags
    dflags GlobalRdrEnv
    global_env
                            LocalRdrEnv
    local_env RdrName
    tried_rdr_name
      = case [(RdrName, HowInScope)]
    suggest of
          []  -> SDoc
    Outputable.empty
          [(RdrName, HowInScope)
    p] -> SDoc
    perhaps SDoc -> SDoc -> SDoc
    <+> (RdrName, HowInScope) -> SDoc
    pp_item (RdrName, HowInScope)
    p
          [(RdrName, HowInScope)]
    ps  -> [SDoc] -> SDoc
    sep [ SDoc
    perhaps SDoc -> SDoc -> SDoc
    <+> String -> SDoc
    text String
    "one of these:"
                     , Int -> SDoc -> SDoc
    nest Int
    2 (((RdrName, HowInScope) -> SDoc) -> [(RdrName, HowInScope)] -> SDoc
    forall a. (a -> SDoc) -> [a] -> SDoc
    pprWithCommas (RdrName, HowInScope) -> SDoc
    pp_item [(RdrName, HowInScope)]
    ps) ]
      where
        all_possibilities :: [(String, (RdrName, HowInScope))]
        all_possibilities :: [(String, (RdrName, HowInScope))]
    all_possibilities
           =  [ (DynFlags -> RdrName -> String
    forall a. Outputable a => DynFlags -> a -> String
    showPpr DynFlags
    dflags RdrName
    r, (RdrName
    r, SrcSpan -> HowInScope
    forall a b. a -> Either a b
    Left SrcSpan
    loc))
              | (RdrName
    r,SrcSpan
    loc) <- LocalRdrEnv -> [(RdrName, SrcSpan)]
    local_possibilities LocalRdrEnv
    local_env ]
           [(String, (RdrName, HowInScope))]
    -> [(String, (RdrName, HowInScope))]
    -> [(String, (RdrName, HowInScope))]
    forall a. [a] -> [a] -> [a]
    ++ [ (DynFlags -> RdrName -> String
    forall a. Outputable a => DynFlags -> a -> String
    showPpr DynFlags
    dflags RdrName
    r, (RdrName, HowInScope)
    rp) | (RdrName
    r, (RdrName, HowInScope)
    rp) <- GlobalRdrEnv -> [(RdrName, (RdrName, HowInScope))]
    global_possibilities GlobalRdrEnv
    global_env ]

        suggest :: [(RdrName, HowInScope)]
    suggest = String
    -> [(String, (RdrName, HowInScope))] -> [(RdrName, HowInScope)]
    forall a. String -> [(String, a)] -> [a]
    fuzzyLookup (DynFlags -> RdrName -> String
    forall a. Outputable a => DynFlags -> a -> String
    showPpr DynFlags
    dflags RdrName
    tried_rdr_name) [(String, (RdrName, HowInScope))]
    all_possibilities
        perhaps :: SDoc
    perhaps = String -> SDoc
    text String
    "Perhaps you meant"

        pp_item :: (RdrName, HowInScope) -> SDoc
        pp_item :: (RdrName, HowInScope) -> SDoc
    pp_item (RdrName
    rdr, Left SrcSpan
    loc) = RdrName -> SDoc
    pp_ns RdrName
    rdr SDoc -> SDoc -> SDoc
    <+> SDoc -> SDoc
    quotes (RdrName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr RdrName
    rdr) SDoc -> SDoc -> SDoc
    <+> SDoc
    loc' -- Locally defined
            where loc' :: SDoc
    loc' = case SrcSpan
    loc of
                         UnhelpfulSpan UnhelpfulSpanReason
    l -> SDoc -> SDoc
    parens (UnhelpfulSpanReason -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr UnhelpfulSpanReason
    l)
                         RealSrcSpan RealSrcSpan
    l Maybe BufSpan
    _ -> SDoc -> SDoc
    parens (String -> SDoc
    text String
    "line" SDoc -> SDoc -> SDoc
    <+> Int -> SDoc
    int (RealSrcSpan -> Int
    srcSpanStartLine RealSrcSpan
    l))
        pp_item (RdrName
    rdr, Right ImpDeclSpec
    is) = RdrName -> SDoc
    pp_ns RdrName
    rdr SDoc -> SDoc -> SDoc
    <+> SDoc -> SDoc
    quotes (RdrName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr RdrName
    rdr) SDoc -> SDoc -> SDoc
    <+>   -- Imported
                                  SDoc -> SDoc
    parens (String -> SDoc
    text String
    "imported from" SDoc -> SDoc -> SDoc
    <+> ModuleName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr (ImpDeclSpec -> ModuleName
    is_mod ImpDeclSpec
    is))

        pp_ns :: RdrName -> SDoc
        pp_ns :: RdrName -> SDoc
    pp_ns RdrName
    rdr | NameSpace
    ns NameSpace -> NameSpace -> Bool
    forall a. Eq a => a -> a -> Bool
    /= NameSpace
    tried_ns = NameSpace -> SDoc
    pprNameSpace NameSpace
    ns
                  | Bool
    otherwise      = SDoc
    Outputable.empty
          where ns :: NameSpace
    ns = RdrName -> NameSpace
    rdrNameSpace RdrName
    rdr

        tried_occ :: OccName
    tried_occ     = RdrName -> OccName
    rdrNameOcc RdrName
    tried_rdr_name
        tried_is_sym :: Bool
    tried_is_sym  = OccName -> Bool
    isSymOcc OccName
    tried_occ
        tried_ns :: NameSpace
    tried_ns      = OccName -> NameSpace
    occNameSpace OccName
    tried_occ
        tried_is_qual :: Bool
    tried_is_qual = RdrName -> Bool
    isQual RdrName
    tried_rdr_name

        correct_name_space :: OccName -> Bool
    correct_name_space OccName
    occ =  NameSpace -> NameSpace -> Bool
    nameSpacesRelated (OccName -> NameSpace
    occNameSpace OccName
    occ) NameSpace
    tried_ns
                               Bool -> Bool -> Bool
    && OccName -> Bool
    isSymOcc OccName
    occ Bool -> Bool -> Bool
    forall a. Eq a => a -> a -> Bool
    == Bool
    tried_is_sym
            -- Treat operator and non-operators as non-matching
            -- This heuristic avoids things like
            --      Not in scope 'f'; perhaps you meant '+' (from Prelude)

        local_ok :: Bool
    local_ok = case WhereLooking
    where_look of { WhereLooking
    WL_Any -> Bool
    True
                                      ; WhereLooking
    WL_LocalOnly -> Bool
    True
                                      ; WhereLooking
    _ -> Bool
    False }
        local_possibilities :: LocalRdrEnv -> [(RdrName, SrcSpan)]
        local_possibilities :: LocalRdrEnv -> [(RdrName, SrcSpan)]
    local_possibilities LocalRdrEnv
    env
          | Bool
    tried_is_qual = []
          | Bool -> Bool
    not Bool
    local_ok  = []
          | Bool
    otherwise     = [ (OccName -> RdrName
    mkRdrUnqual OccName
    occ, Name -> SrcSpan
    nameSrcSpan Name
    name)
                            | Name
    name <- LocalRdrEnv -> [Name]
    localRdrEnvElts LocalRdrEnv
    env
                            , let occ :: OccName
    occ = Name -> OccName
    nameOccName Name
    name
                            , OccName -> Bool
    correct_name_space OccName
    occ]

        global_possibilities :: GlobalRdrEnv -> [(RdrName, (RdrName, HowInScope))]
        global_possibilities :: GlobalRdrEnv -> [(RdrName, (RdrName, HowInScope))]
    global_possibilities GlobalRdrEnv
    global_env
          | Bool
    tried_is_qual = [ (RdrName
    rdr_qual, (RdrName
    rdr_qual, HowInScope
    how))
                            | GlobalRdrElt
    gre <- GlobalRdrEnv -> [GlobalRdrElt]
    globalRdrEnvElts GlobalRdrEnv
    global_env
                            , WhereLooking -> GlobalRdrElt -> Bool
    isGreOk WhereLooking
    where_look GlobalRdrElt
    gre
                            , Bool -> Bool
    not (GlobalRdrElt -> Bool
    isNoFieldSelectorGRE GlobalRdrElt
    gre)
                            , let occ :: OccName
    occ = GlobalRdrElt -> OccName
    greOccName GlobalRdrElt
    gre
                            , OccName -> Bool
    correct_name_space OccName
    occ
                            , (ModuleName
    mod, HowInScope
    how) <- GlobalRdrElt -> [(ModuleName, HowInScope)]
    qualsInScope GlobalRdrElt
    gre
                            , let rdr_qual :: RdrName
    rdr_qual = ModuleName -> OccName -> RdrName
    mkRdrQual ModuleName
    mod OccName
    occ ]

          | Bool
    otherwise = [ (RdrName
    rdr_unqual, (RdrName, HowInScope)
    pair)
                        | GlobalRdrElt
    gre <- GlobalRdrEnv -> [GlobalRdrElt]
    globalRdrEnvElts GlobalRdrEnv
    global_env
                        , WhereLooking -> GlobalRdrElt -> Bool
    isGreOk WhereLooking
    where_look GlobalRdrElt
    gre
                        , Bool -> Bool
    not (GlobalRdrElt -> Bool
    isNoFieldSelectorGRE GlobalRdrElt
    gre)
                        , let occ :: OccName
    occ = GlobalRdrElt -> OccName
    greOccName GlobalRdrElt
    gre
                              rdr_unqual :: RdrName
    rdr_unqual = OccName -> RdrName
    mkRdrUnqual OccName
    occ
                        , OccName -> Bool
    correct_name_space OccName
    occ
                        , (RdrName, HowInScope)
    pair <- case (GlobalRdrElt -> [HowInScope]
    unquals_in_scope GlobalRdrElt
    gre, GlobalRdrElt -> [(RdrName, HowInScope)]
    quals_only GlobalRdrElt
    gre) of
                                    (HowInScope
    how:[HowInScope]
    _, [(RdrName, HowInScope)]
    _)    -> [ (RdrName
    rdr_unqual, HowInScope
    how) ]
                                    ([],    (RdrName, HowInScope)
    pr:[(RdrName, HowInScope)]
    _) -> [ (RdrName, HowInScope)
    pr ]  -- See Note [Only-quals]
                                    ([],    [])   -> [] ]

                  -- Note [Only-quals]
                  -- The second alternative returns those names with the same
                  -- OccName as the one we tried, but live in *qualified* imports
                  -- e.g. if you have:
                  --
                  -- > import qualified Data.Map as Map
                  -- > foo :: Map
                  --
                  -- then we suggest @Map.Map@.

        --------------------
        unquals_in_scope :: GlobalRdrElt -> [HowInScope]
        unquals_in_scope :: GlobalRdrElt -> [HowInScope]
    unquals_in_scope (gre :: GlobalRdrElt
    gre@GRE { gre_lcl :: GlobalRdrElt -> Bool
    gre_lcl = Bool
    lcl, gre_imp :: GlobalRdrElt -> [ImportSpec]
    gre_imp = [ImportSpec]
    is })
          | Bool
    lcl       = [ SrcSpan -> HowInScope
    forall a b. a -> Either a b
    Left (GlobalRdrElt -> SrcSpan
    greDefinitionSrcSpan GlobalRdrElt
    gre) ]
          | Bool
    otherwise = [ ImpDeclSpec -> HowInScope
    forall a b. b -> Either a b
    Right ImpDeclSpec
    ispec
                        | ImportSpec
    i <- [ImportSpec]
    is, let ispec :: ImpDeclSpec
    ispec = ImportSpec -> ImpDeclSpec
    is_decl ImportSpec
    i
                        , Bool -> Bool
    not (ImpDeclSpec -> Bool
    is_qual ImpDeclSpec
    ispec) ]

        --------------------
        quals_only :: GlobalRdrElt -> [(RdrName, HowInScope)]
        -- Ones for which *only* the qualified version is in scope
        quals_only :: GlobalRdrElt -> [(RdrName, HowInScope)]
    quals_only (gre :: GlobalRdrElt
    gre@GRE { gre_imp :: GlobalRdrElt -> [ImportSpec]
    gre_imp = [ImportSpec]
    is })
          = [ (ModuleName -> OccName -> RdrName
    mkRdrQual (ImpDeclSpec -> ModuleName
    is_as ImpDeclSpec
    ispec) (GlobalRdrElt -> OccName
    greOccName GlobalRdrElt
    gre), ImpDeclSpec -> HowInScope
    forall a b. b -> Either a b
    Right ImpDeclSpec
    ispec)
            | ImportSpec
    i <- [ImportSpec]
    is, let ispec :: ImpDeclSpec
    ispec = ImportSpec -> ImpDeclSpec
    is_decl ImportSpec
    i, ImpDeclSpec -> Bool
    is_qual ImpDeclSpec
    ispec ]

    -- | Generate helpful suggestions if a qualified name Mod.foo is not in scope.
    importSuggestions :: WhereLooking
                      -> GlobalRdrEnv
                      -> HomePackageTable -> Module
                      -> ImportAvails -> RdrName -> SDoc
    importSuggestions :: WhereLooking
    -> GlobalRdrEnv
    -> HomePackageTable
    -> Module
    -> ImportAvails
    -> RdrName
    -> SDoc
    importSuggestions WhereLooking
    where_look GlobalRdrEnv
    global_env HomePackageTable
    hpt Module
    currMod ImportAvails
    imports RdrName
    rdr_name
      | WhereLooking
    WL_LocalOnly <- WhereLooking
    where_look                 = SDoc
    Outputable.empty
      | Bool -> Bool
    not (RdrName -> Bool
    isQual RdrName
    rdr_name Bool -> Bool -> Bool
    || RdrName -> Bool
    isUnqual RdrName
    rdr_name) = SDoc
    Outputable.empty
      | [(Module, ImportedModsVal)] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [(Module, ImportedModsVal)]
    interesting_imports
      , Just ModuleName
    name <- Maybe ModuleName
    mod_name
      , ModuleName -> Bool
    show_not_imported_line ModuleName
    name
      = [SDoc] -> SDoc
    hsep
          [ String -> SDoc
    text String
    "No module named"
          , SDoc -> SDoc
    quotes (ModuleName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr ModuleName
    name)
          , String -> SDoc
    text String
    "is imported."
          ]
      | Bool
    is_qualified
      , [(Module, ImportedModsVal)] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [(Module, ImportedModsVal)]
    helpful_imports
      , [(Module
    mod,ImportedModsVal
    _)] <- [(Module, ImportedModsVal)]
    interesting_imports
      = [SDoc] -> SDoc
    hsep
          [ String -> SDoc
    text String
    "Module"
          , SDoc -> SDoc
    quotes (Module -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Module
    mod)
          , String -> SDoc
    text String
    "does not export"
          , SDoc -> SDoc
    quotes (OccName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr OccName
    occ_name) SDoc -> SDoc -> SDoc
    <> SDoc
    dot
          ]
      | Bool
    is_qualified
      , [(Module, ImportedModsVal)] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [(Module, ImportedModsVal)]
    helpful_imports
      , Bool -> Bool
    not ([(Module, ImportedModsVal)] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [(Module, ImportedModsVal)]
    interesting_imports)
      , [Module]
    mods <- ((Module, ImportedModsVal) -> Module)
    -> [(Module, ImportedModsVal)] -> [Module]
    forall a b. (a -> b) -> [a] -> [b]
    map (Module, ImportedModsVal) -> Module
    forall a b. (a, b) -> a
    fst [(Module, ImportedModsVal)]
    interesting_imports
      = [SDoc] -> SDoc
    hsep
          [ String -> SDoc
    text String
    "Neither"
          , [SDoc] -> SDoc
    quotedListWithNor ((Module -> SDoc) -> [Module] -> [SDoc]
    forall a b. (a -> b) -> [a] -> [b]
    map Module -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr [Module]
    mods)
          , String -> SDoc
    text String
    "exports"
          , SDoc -> SDoc
    quotes (OccName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr OccName
    occ_name) SDoc -> SDoc -> SDoc
    <> SDoc
    dot
          ]
      | [(Module
    mod,ImportedModsVal
    imv)] <- [(Module, ImportedModsVal)]
    helpful_imports_non_hiding
      = [SDoc] -> SDoc
    fsep
          [ String -> SDoc
    text String
    "Perhaps you want to add"
          , SDoc -> SDoc
    quotes (OccName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr OccName
    occ_name)
          , String -> SDoc
    text String
    "to the import list"
          , String -> SDoc
    text String
    "in the import of"
          , SDoc -> SDoc
    quotes (Module -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Module
    mod)
          , SDoc -> SDoc
    parens (SrcSpan -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr (ImportedModsVal -> SrcSpan
    imv_span ImportedModsVal
    imv)) SDoc -> SDoc -> SDoc
    <> SDoc
    dot
          ]
      | Bool -> Bool
    not ([(Module, ImportedModsVal)] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [(Module, ImportedModsVal)]
    helpful_imports_non_hiding)
      = [SDoc] -> SDoc
    fsep
          [ String -> SDoc
    text String
    "Perhaps you want to add"
          , SDoc -> SDoc
    quotes (OccName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr OccName
    occ_name)
          , String -> SDoc
    text String
    "to one of these import lists:"
          ]
        SDoc -> SDoc -> SDoc
    $$
        Int -> SDoc -> SDoc
    nest Int
    2 ([SDoc] -> SDoc
    vcat
            [ SDoc -> SDoc
    quotes (Module -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Module
    mod) SDoc -> SDoc -> SDoc
    <+> SDoc -> SDoc
    parens (SrcSpan -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr (ImportedModsVal -> SrcSpan
    imv_span ImportedModsVal
    imv))
            | (Module
    mod,ImportedModsVal
    imv) <- [(Module, ImportedModsVal)]
    helpful_imports_non_hiding
            ])
      | [(Module
    mod,ImportedModsVal
    imv)] <- [(Module, ImportedModsVal)]
    helpful_imports_hiding
      = [SDoc] -> SDoc
    fsep
          [ String -> SDoc
    text String
    "Perhaps you want to remove"
          , SDoc -> SDoc
    quotes (OccName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr OccName
    occ_name)
          , String -> SDoc
    text String
    "from the explicit hiding list"
          , String -> SDoc
    text String
    "in the import of"
          , SDoc -> SDoc
    quotes (Module -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Module
    mod)
          , SDoc -> SDoc
    parens (SrcSpan -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr (ImportedModsVal -> SrcSpan
    imv_span ImportedModsVal
    imv)) SDoc -> SDoc -> SDoc
    <> SDoc
    dot
          ]
      | Bool -> Bool
    not ([(Module, ImportedModsVal)] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [(Module, ImportedModsVal)]
    helpful_imports_hiding)
      = [SDoc] -> SDoc
    fsep
          [ String -> SDoc
    text String
    "Perhaps you want to remove"
          , SDoc -> SDoc
    quotes (OccName -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr OccName
    occ_name)
          , String -> SDoc
    text String
    "from the hiding clauses"
          , String -> SDoc
    text String
    "in one of these imports:"
          ]
        SDoc -> SDoc -> SDoc
    $$
        Int -> SDoc -> SDoc
    nest Int
    2 ([SDoc] -> SDoc
    vcat
            [ SDoc -> SDoc
    quotes (Module -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Module
    mod) SDoc -> SDoc -> SDoc
    <+> SDoc -> SDoc
    parens (SrcSpan -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr (ImportedModsVal -> SrcSpan
    imv_span ImportedModsVal
    imv))
            | (Module
    mod,ImportedModsVal
    imv) <- [(Module, ImportedModsVal)]
    helpful_imports_hiding
            ])
      | Bool
    otherwise
      = SDoc
    Outputable.empty
     where
      is_qualified :: Bool
    is_qualified = RdrName -> Bool
    isQual RdrName
    rdr_name
      (Maybe ModuleName
    mod_name, OccName
    occ_name) = case RdrName
    rdr_name of
        Unqual OccName
    occ_name        -> (Maybe ModuleName
    forall a. Maybe a
    Nothing, OccName
    occ_name)
        Qual ModuleName
    mod_name OccName
    occ_name -> (ModuleName -> Maybe ModuleName
    forall a. a -> Maybe a
    Just ModuleName
    mod_name, OccName
    occ_name)
        RdrName
    _                      -> String -> (Maybe ModuleName, OccName)
    forall a. HasCallStack => String -> a
    error String
    "importSuggestions: dead code"

      -- What import statements provide "Mod" at all
      -- or, if this is an unqualified name, are not qualified imports
      interesting_imports :: [(Module, ImportedModsVal)]
    interesting_imports = [ (Module
    mod, ImportedModsVal
    imp)
        | (Module
    mod, [ImportedBy]
    mod_imports) <- ModuleEnv [ImportedBy] -> [(Module, [ImportedBy])]
    forall a. ModuleEnv a -> [(Module, a)]
    moduleEnvToList (ImportAvails -> ModuleEnv [ImportedBy]
    imp_mods ImportAvails
    imports)
        , Just ImportedModsVal
    imp <- Maybe ImportedModsVal -> [Maybe ImportedModsVal]
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Maybe ImportedModsVal -> [Maybe ImportedModsVal])
    -> Maybe ImportedModsVal -> [Maybe ImportedModsVal]
    forall a b. (a -> b) -> a -> b
    $ [ImportedModsVal] -> Maybe ImportedModsVal
    pick ([ImportedBy] -> [ImportedModsVal]
    importedByUser [ImportedBy]
    mod_imports)
        ]

      -- We want to keep only one for each original module; preferably one with an
      -- explicit import list (for no particularly good reason)
      pick :: [ImportedModsVal] -> Maybe ImportedModsVal
      pick :: [ImportedModsVal] -> Maybe ImportedModsVal
    pick = [ImportedModsVal] -> Maybe ImportedModsVal
    forall a. [a] -> Maybe a
    listToMaybe ([ImportedModsVal] -> Maybe ImportedModsVal)
    -> ([ImportedModsVal] -> [ImportedModsVal])
    -> [ImportedModsVal]
    -> Maybe ImportedModsVal
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ImportedModsVal -> ImportedModsVal -> Ordering)
    -> [ImportedModsVal] -> [ImportedModsVal]
    forall a. (a -> a -> Ordering) -> [a] -> [a]
    sortBy ImportedModsVal -> ImportedModsVal -> Ordering
    cmp ([ImportedModsVal] -> [ImportedModsVal])
    -> ([ImportedModsVal] -> [ImportedModsVal])
    -> [ImportedModsVal]
    -> [ImportedModsVal]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ImportedModsVal -> Bool) -> [ImportedModsVal] -> [ImportedModsVal]
    forall a. (a -> Bool) -> [a] -> [a]
    filter ImportedModsVal -> Bool
    select
        where select :: ImportedModsVal -> Bool
    select ImportedModsVal
    imv = case Maybe ModuleName
    mod_name of Just ModuleName
    name -> ImportedModsVal -> ModuleName
    imv_name ImportedModsVal
    imv ModuleName -> ModuleName -> Bool
    forall a. Eq a => a -> a -> Bool
    == ModuleName
    name
                                            Maybe ModuleName
    Nothing   -> Bool -> Bool
    not (ImportedModsVal -> Bool
    imv_qualified ImportedModsVal
    imv)
              cmp :: ImportedModsVal -> ImportedModsVal -> Ordering
    cmp ImportedModsVal
    a ImportedModsVal
    b =
                (Bool -> Bool -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare (Bool -> Bool -> Ordering)
    -> (ImportedModsVal -> Bool)
    -> ImportedModsVal
    -> ImportedModsVal
    -> Ordering
    forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
    `on` ImportedModsVal -> Bool
    imv_is_hiding) ImportedModsVal
    a ImportedModsVal
    b
                  Ordering -> Ordering -> Ordering
    `thenCmp`
                (SrcSpan -> SrcSpan -> Ordering
    SrcLoc.leftmost_smallest (SrcSpan -> SrcSpan -> Ordering)
    -> (ImportedModsVal -> SrcSpan)
    -> ImportedModsVal
    -> ImportedModsVal
    -> Ordering
    forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
    `on` ImportedModsVal -> SrcSpan
    imv_span) ImportedModsVal
    a ImportedModsVal
    b

      -- Which of these would export a 'foo'
      -- (all of these are restricted imports, because if they were not, we
      -- wouldn't have an out-of-scope error in the first place)
      helpful_imports :: [(Module, ImportedModsVal)]
    helpful_imports = ((Module, ImportedModsVal) -> Bool)
    -> [(Module, ImportedModsVal)] -> [(Module, ImportedModsVal)]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Module, ImportedModsVal) -> Bool
    helpful [(Module, ImportedModsVal)]
    interesting_imports
        where helpful :: (Module, ImportedModsVal) -> Bool
    helpful (Module
    _,ImportedModsVal
    imv)
                = Bool -> Bool
    not (Bool -> Bool)
    -> ([GlobalRdrElt] -> Bool) -> [GlobalRdrElt] -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [GlobalRdrElt] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null ([GlobalRdrElt] -> Bool) -> [GlobalRdrElt] -> Bool
    forall a b. (a -> b) -> a -> b
    $ GlobalRdrEnv -> OccName -> [GlobalRdrElt]
    lookupGlobalRdrEnv (ImportedModsVal -> GlobalRdrEnv
    imv_all_exports ImportedModsVal
    imv) OccName
    occ_name

      -- Which of these do that because of an explicit hiding list resp. an
      -- explicit import list
      ([(Module, ImportedModsVal)]
    helpful_imports_hiding, [(Module, ImportedModsVal)]
    helpful_imports_non_hiding)
        = ((Module, ImportedModsVal) -> Bool)
    -> [(Module, ImportedModsVal)]
    -> ([(Module, ImportedModsVal)], [(Module, ImportedModsVal)])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    partition (ImportedModsVal -> Bool
    imv_is_hiding (ImportedModsVal -> Bool)
    -> ((Module, ImportedModsVal) -> ImportedModsVal)
    -> (Module, ImportedModsVal)
    -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Module, ImportedModsVal) -> ImportedModsVal
    forall a b. (a, b) -> b
    snd) [(Module, ImportedModsVal)]
    helpful_imports

      -- See note [When to show/hide the module-not-imported line]
      show_not_imported_line :: ModuleName -> Bool                    -- #15611
      show_not_imported_line :: ModuleName -> Bool
    show_not_imported_line ModuleName
    modnam
          | ModuleName
    modnam ModuleName -> [ModuleName] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [ModuleName]
    globMods                = Bool
    False    -- #14225     -- 1
          | Module -> ModuleName
    forall unit. GenModule unit -> ModuleName
    moduleName Module
    currMod ModuleName -> ModuleName -> Bool
    forall a. Eq a => a -> a -> Bool
    == ModuleName
    modnam          = Bool
    False                  -- 2.1
          | ModuleName -> [Unique] -> Bool
    forall {a}. Uniquable a => a -> [Unique] -> Bool
    is_last_loaded_mod ModuleName
    modnam [Unique]
    hpt_uniques = Bool
    False                  -- 2.2
          | Bool
    otherwise                             = Bool
    True
        where
          hpt_uniques :: [Unique]
    hpt_uniques = ((Unique, HomeModInfo) -> Unique)
    -> [(Unique, HomeModInfo)] -> [Unique]
    forall a b. (a -> b) -> [a] -> [b]
    map (Unique, HomeModInfo) -> Unique
    forall a b. (a, b) -> a
    fst (HomePackageTable -> [(Unique, HomeModInfo)]
    forall key elt. UniqDFM key elt -> [(Unique, elt)]
    udfmToList HomePackageTable
    hpt)
          is_last_loaded_mod :: a -> [Unique] -> Bool
    is_last_loaded_mod a
    _ []         = Bool
    False
          is_last_loaded_mod a
    modnam [Unique]
    uniqs = [Unique] -> Unique
    forall a. [a] -> a
    last [Unique]
    uniqs Unique -> Unique -> Bool
    forall a. Eq a => a -> a -> Bool
    == a -> Unique
    forall a. Uniquable a => a -> Unique
    getUnique a
    modnam
          globMods :: [ModuleName]
    globMods = [ModuleName] -> [ModuleName]
    forall a. Eq a => [a] -> [a]
    nub [ ModuleName
    mod
                         | GlobalRdrElt
    gre <- GlobalRdrEnv -> [GlobalRdrElt]
    globalRdrEnvElts GlobalRdrEnv
    global_env
                         , WhereLooking -> GlobalRdrElt -> Bool
    isGreOk WhereLooking
    where_look GlobalRdrElt
    gre
                         , (ModuleName
    mod, HowInScope
    _) <- GlobalRdrElt -> [(ModuleName, HowInScope)]
    qualsInScope GlobalRdrElt
    gre
                         ]

    extensionSuggestions :: RdrName -> SDoc
    extensionSuggestions :: RdrName -> SDoc
    extensionSuggestions RdrName
    rdrName
      | RdrName
    rdrName RdrName -> RdrName -> Bool
    forall a. Eq a => a -> a -> Bool
    == NameSpace -> FastString -> RdrName
    mkUnqual NameSpace
    varName (String -> FastString
    fsLit String
    "mdo") Bool -> Bool -> Bool
    ||
        RdrName
    rdrName RdrName -> RdrName -> Bool
    forall a. Eq a => a -> a -> Bool
    == NameSpace -> FastString -> RdrName
    mkUnqual NameSpace
    varName (String -> FastString
    fsLit String
    "rec")
          = String -> SDoc
    text String
    "Perhaps you meant to use RecursiveDo"
      | Bool
    otherwise = SDoc
    Outputable.empty

    qualsInScope :: GlobalRdrElt -> [(ModuleName, HowInScope)]
    -- Ones for which the qualified version is in scope
    qualsInScope :: GlobalRdrElt -> [(ModuleName, HowInScope)]
    qualsInScope gre :: GlobalRdrElt
    gre@GRE { gre_lcl :: GlobalRdrElt -> Bool
    gre_lcl = Bool
    lcl, gre_imp :: GlobalRdrElt -> [ImportSpec]
    gre_imp = [ImportSpec]
    is }
          | Bool
    lcl = case GlobalRdrElt -> Maybe Module
    greDefinitionModule GlobalRdrElt
    gre of
                    Maybe Module
    Nothing -> []
                    Just Module
    m  -> [(Module -> ModuleName
    forall unit. GenModule unit -> ModuleName
    moduleName Module
    m, SrcSpan -> HowInScope
    forall a b. a -> Either a b
    Left (GlobalRdrElt -> SrcSpan
    greDefinitionSrcSpan GlobalRdrElt
    gre))]
          | Bool
    otherwise = [ (ImpDeclSpec -> ModuleName
    is_as ImpDeclSpec
    ispec, ImpDeclSpec -> HowInScope
    forall a b. b -> Either a b
    Right ImpDeclSpec
    ispec)
                        | ImportSpec
    i <- [ImportSpec]
    is, let ispec :: ImpDeclSpec
    ispec = ImportSpec -> ImpDeclSpec
    is_decl ImportSpec
    i ]

    isGreOk :: WhereLooking -> GlobalRdrElt -> Bool
    isGreOk :: WhereLooking -> GlobalRdrElt -> Bool
    isGreOk WhereLooking
    where_look = case WhereLooking
    where_look of
                             WhereLooking
    WL_LocalTop  -> GlobalRdrElt -> Bool
    isLocalGRE
                             WhereLooking
    WL_LocalOnly -> Bool -> GlobalRdrElt -> Bool
    forall a b. a -> b -> a
    const Bool
    False
                             WhereLooking
    _            -> Bool -> GlobalRdrElt -> Bool
    forall a b. a -> b -> a
    const Bool
    True

    {- Note [When to show/hide the module-not-imported line]           -- #15611
    For the error message:
        Not in scope X.Y
        Module X does not export Y
        No module named ‘X’ is imported:
    there are 2 cases, where we hide the last "no module is imported" line:
    1. If the module X has been imported.
    2. If the module X is the current module. There are 2 subcases:
       2.1 If the unknown module name is in a input source file,
           then we can use the getModule function to get the current module name.
           (See test T15611a)
       2.2 If the unknown module name has been entered by the user in GHCi,
           then the getModule function returns something like "interactive:Ghci1",
           and we have to check the current module in the last added entry of
           the HomePackageTable. (See test T15611b)
    -}

    exactNameErr :: Name -> SDoc
    exactNameErr :: Name -> SDoc
    exactNameErr Name
    name =
      SDoc -> Int -> SDoc -> SDoc
    hang (String -> SDoc
    text String
    "The exact Name" SDoc -> SDoc -> SDoc
    <+> SDoc -> SDoc
    quotes (Name -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Name
    name) SDoc -> SDoc -> SDoc
    <+> PtrString -> SDoc
    ptext (String -> PtrString
    sLit String
    "is not in scope"))
        Int
    2 ([SDoc] -> SDoc
    vcat [ String -> SDoc
    text String
    "Probable cause: you used a unique Template Haskell name (NameU), "
                , String -> SDoc
    text String
    "perhaps via newName, but did not bind it"
                , String -> SDoc
    text String
    "If that's it, then -ddump-splices might be useful" ])