Commit e66f79df authored by Joachim Breitner's avatar Joachim Breitner

Give helpful advice when a fully qualified name is not in scope

This implements #11071. It needs to thread through a GlobalRdrEnv
corresponding to the export list of the module if its exports were not
restricted.

A refactoring of ImportedModsVal into a proper data type follows.

Differential Revision: https://phabricator.haskell.org/D1462
parent 2290c8bd
......@@ -1031,9 +1031,9 @@ mk_mod_usage_info pit hsc_env this_mod direct_imports used_names
(is_direct_import, imp_safe)
= case lookupModuleEnv direct_imports mod of
Just ((_,_,_,safe):_xs) -> (True, safe)
Just _ -> pprPanic "mkUsage: empty direct import" Outputable.empty
Nothing -> (False, safeImplicitImpsReq dflags)
Just ((_,_,_,safe,_,_):_xs) -> (True, safe)
Just _ -> pprPanic "mkUsage: empty direct import" Outputable.empty
Nothing -> (False, safeImplicitImpsReq dflags)
-- Nothing case is for implicit imports like 'System.IO' when 'putStrLn'
-- is used in the source code. We require them to be safe in Safe Haskell
......
......@@ -978,12 +978,12 @@ checkSafeImports dflags tcg_env
condense :: (Module, [ImportedModsVal]) -> Hsc (Module, SrcSpan, IsSafeImport)
condense (_, []) = panic "HscMain.condense: Pattern match failure!"
condense (m, x:xs) = do (_,_,l,s) <- foldlM cond' x xs
condense (m, x:xs) = do (_,_,l,s,_,_) <- foldlM cond' x xs
return (m, l, s)
-- ImportedModsVal = (ModuleName, Bool, SrcSpan, IsSafeImport)
cond' :: ImportedModsVal -> ImportedModsVal -> Hsc ImportedModsVal
cond' v1@(m1,_,l1,s1) (_,_,_,s2)
cond' v1@(m1,_,l1,s1,_,_) (_,_,_,s2,_,_)
| s1 /= s2
= throwErrors $ unitBag $ mkPlainErrMsg dflags l1
(text "Module" <+> ppr m1 <+>
......
......@@ -1029,7 +1029,7 @@ emptyModDetails
-- | Records the modules directly imported by a module for extracting e.g. usage information
type ImportedMods = ModuleEnv [ImportedModsVal]
type ImportedModsVal = (ModuleName, Bool, SrcSpan, IsSafeImport)
type ImportedModsVal = (ModuleName, Bool, SrcSpan, IsSafeImport, Bool, GlobalRdrEnv)
-- | A ModGuts is carried through the compiler, accumulating stuff as it goes
-- There is only one ModGuts at any time, the one for the module
......
......@@ -77,6 +77,7 @@ import DynFlags
import FastString
import Control.Monad
import Data.List
import Data.Function ( on )
import ListSetOps ( minusList )
import Constants ( mAX_TUPLE_SIZE )
......@@ -1635,9 +1636,11 @@ unboundNameX where_look rdr_name extra
then addErr err
else do { local_env <- getLocalRdrEnv
; global_env <- getGlobalRdrEnv
; let suggestions = unknownNameSuggestions_ where_look
; impInfo <- getImports
; let suggestions1 = unknownNameSuggestions_ where_look
dflags global_env local_env rdr_name
; addErr (err $$ suggestions) }
; let suggestions2 = importSuggestions dflags impInfo rdr_name
; addErr (err $$ suggestions1 $$ suggestions2) }
; return (mkUnboundName rdr_name) }
unknownNameErr :: SDoc -> RdrName -> SDoc
......@@ -1653,11 +1656,113 @@ type HowInScope = Either SrcSpan ImpDeclSpec
-- Left loc => locally bound at loc
-- Right ispec => imported as specified by ispec
-- | Generate helpful suggestions if a qualified name Mod.foo is not in scope.
importSuggestions :: DynFlags -> ImportAvails -> RdrName -> SDoc
importSuggestions _dflags imports rdr_name
| not (isQual rdr_name) = Outputable.empty
| null interesting_imports
= hsep
[ ptext (sLit "No module named")
, quotes (ppr mod_name)
, ptext (sLit "is imported.")
]
| null helpful_imports
, [(mod,_)] <- interesting_imports
= hsep
[ ptext (sLit "Module")
, quotes (ppr mod)
, ptext (sLit "does not export")
, quotes (ppr occ_name) <> dot
]
| null helpful_imports
, mods <- map fst interesting_imports
= hsep
[ ptext (sLit "Neither")
, quotedListWithNor (map ppr mods)
, ptext (sLit "exports")
, quotes (ppr occ_name) <> dot
]
| [(mod,(_,_,loc,_,_,_))] <- helpful_imports_non_hiding
= fsep
[ ptext (sLit "Perhaps you want to add")
, quotes (ppr occ_name)
, ptext (sLit "to the import list")
, ptext (sLit "in the import of")
, quotes (ppr mod)
, parens (ppr loc) <> dot
]
| not (null helpful_imports_non_hiding)
= fsep
[ ptext (sLit "Perhaps you want to add")
, quotes (ppr occ_name)
, ptext (sLit "to one of these import lists:")
]
$$
nest 2 (vcat
[ quotes (ppr mod) <+> parens (ppr loc)
| (mod,(_,_,loc,_,_,_)) <- helpful_imports_non_hiding
])
| [(mod,(_,_,loc,_,_,_))] <- helpful_imports_hiding
= fsep
[ ptext (sLit "Perhaps you want to remove")
, quotes (ppr occ_name)
, ptext (sLit "from the explicit hiding list")
, ptext (sLit "in the import of")
, quotes (ppr mod)
, parens (ppr loc) <> dot
]
| not (null helpful_imports_hiding)
= fsep
[ ptext (sLit "Perhaps you want to remove")
, quotes (ppr occ_name)
, ptext (sLit "from the hiding clauses")
, ptext (sLit "in one of these imports:")
]
$$
nest 2 (vcat
[ quotes (ppr mod) <+> parens (ppr loc)
| (mod,(_,_,loc,_,_,_)) <- helpful_imports_hiding
])
| otherwise
= Outputable.empty
where
Just (mod_name, occ_name) = isQual_maybe rdr_name
-- What import statements provide "Mod" at all
interesting_imports = [ (mod, imp)
| (mod, mod_imports) <- moduleEnvToList (imp_mods imports)
, Just imp <- return $ pick 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 = listToMaybe . sortBy (compare `on` prefer) . filter select
where select (name, _,_,_,_,_) = name == mod_name
prefer (_, _, loc, _, hiding, _) = (hiding, loc)
-- 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 = [ (mod, imp)
| (mod, imp@(_, _, _, _, _, all_exports)) <- interesting_imports
, not . null $ lookupGlobalRdrEnv all_exports occ_name
]
-- Which of these do that because of an explicit import list
helpful_imports_non_hiding = [ (mod, imp)
| (mod, imp@(_, _ , _, _, False, _)) <- helpful_imports
]
-- Which of these do that because of an explicit hiding list
helpful_imports_hiding = [ (mod, imp)
| (mod, imp@(_, _ , _, _, True, _)) <- helpful_imports
]
-- | Called from the typechecker (TcErrors) when we find an unbound variable
unknownNameSuggestions :: DynFlags
-> GlobalRdrEnv -> LocalRdrEnv
-> RdrName -> SDoc
-- Called from the typechecker (TcErrors)
-- when we find an unbound variable
unknownNameSuggestions = unknownNameSuggestions_ WL_Any
unknownNameSuggestions_ :: WhereLooking -> DynFlags
......
......@@ -264,12 +264,16 @@ rnImportDecl this_mod
-- filter the imports according to the import declaration
(new_imp_details, gres) <- filterImports iface imp_spec imp_details
-- for certain error messages, we’d like to know what could be imported
-- here, if everything were imported
potential_gres <- mkGlobalRdrEnv . snd <$> filterImports iface imp_spec Nothing
let gbl_env = mkGlobalRdrEnv gres
-- True <=> import M ()
import_all = case imp_details of
Just (is_hiding, L _ ls) -> not is_hiding && null ls
_ -> False
-- import_all == True <=> import M ()
(is_hiding, import_all) = case imp_details of
Just (is_hiding, L _ ls) -> (is_hiding, not is_hiding && null ls)
_ -> (False, False)
-- should the import be safe?
mod_safe' = mod_safe
......@@ -279,7 +283,7 @@ rnImportDecl this_mod
let imports
= (calculateAvails dflags iface mod_safe' want_boot) {
imp_mods = unitModuleEnv (mi_module iface)
[(qual_mod_name, import_all, loc, mod_safe')] }
[(qual_mod_name, import_all, loc, mod_safe', is_hiding, potential_gres)] }
-- Complain if we import a deprecated module
whenWOptM Opt_WarnWarningsDeprecations (
......@@ -1217,7 +1221,7 @@ exports_from_avail (Just (L _ rdr_items)) rdr_env imports this_mod
imported_modules = [ qual_name
| xs <- moduleEnvElts $ imp_mods imports,
(qual_name, _, _, _) <- xs ]
(qual_name, _, _, _, _, _) <- xs ]
exports_from_item :: ExportAccum -> LIE RdrName -> RnM ExportAccum
exports_from_item acc@(ie_names, occs, exports)
......
......@@ -974,7 +974,7 @@ data ImportAvails
-- @
-- it is @Bar@.
--
-- The 'Bool' means:
-- The first 'Bool' means:
--
-- - @True@ => import was @import Foo ()@
--
......@@ -988,6 +988,8 @@ data ImportAvails
--
-- (b) to specify what child modules to initialise
--
-- The second 'Bool' means that the module is safe (see rnImportDecl)
--
-- We need a full ModuleEnv rather than a ModuleNameEnv here,
-- because we might be importing modules of the same name from
-- different packages. (currently not the case, but might be in the
......
......@@ -17,7 +17,8 @@ module Outputable (
-- * Pretty printing combinators
SDoc, runSDoc, initSDocContext,
docToSDoc,
interppSP, interpp'SP, pprQuotedList, pprWithCommas, quotedListWithOr,
interppSP, interpp'SP,
pprQuotedList, pprWithCommas, quotedListWithOr, quotedListWithNor,
empty, nest,
char,
text, ftext, ptext, ztext,
......@@ -910,6 +911,11 @@ quotedListWithOr :: [SDoc] -> SDoc
quotedListWithOr xs@(_:_:_) = quotedList (init xs) <+> ptext (sLit "or") <+> quotes (last xs)
quotedListWithOr xs = quotedList xs
quotedListWithNor :: [SDoc] -> SDoc
-- [x,y,z] ==> `x', `y' nor `z'
quotedListWithNor xs@(_:_:_) = quotedList (init xs) <+> ptext (sLit "nor") <+> quotes (last xs)
quotedListWithNor xs = quotedList xs
{-
************************************************************************
* *
......
Test10313.hs:9:13: error:
The deprecation for ‘solverCheckAndGetModel’
lacks an accompanying binding
Test10313.hs:15:16: error:
Multiple warning declarations for ‘Logic’
also at Test10313.hs:9:13-17
Test10313.hs:15:16: error:
The deprecation for ‘solverCheckAndGetModel’
lacks an accompanying binding
Test10313.hs:16:13: error:
Multiple warning declarations for ‘solverCheckAndGetModel’
also at Test10313.hs:10:13-34
Test10313.hs:30:15: error:
Not in scope: data constructor ‘Bitstream’
Test10313.hs:32:7: error: Not in scope: ‘S.concatMap’
Test10313.hs:32:27: error: Not in scope: ‘GV.stream’
Test10313.hs:33:7: error: Not in scope: ‘S.sized’
Test10313.hs:9:13: error:
The deprecation for ‘solverCheckAndGetModel’
lacks an accompanying binding
Test10313.hs:15:16: error:
Multiple warning declarations for ‘Logic’
also at Test10313.hs:9:13-17
Test10313.hs:15:16: error:
The deprecation for ‘solverCheckAndGetModel’
lacks an accompanying binding
Test10313.hs:16:13: error:
Multiple warning declarations for ‘solverCheckAndGetModel’
also at Test10313.hs:10:13-34
Test10313.hs:30:15: error:
Not in scope: data constructor ‘Bitstream’
Test10313.hs:32:7: error:
Not in scope: ‘S.concatMap’
No module named ‘S’ is imported.
Test10313.hs:32:27: error:
Not in scope: ‘GV.stream’
No module named ‘GV’ is imported.
Test10313.hs:33:7: error:
Not in scope: ‘S.sized’
No module named ‘S’ is imported.
Test10399.hs:10:27: error:
Not in scope: type constructor or class ‘MPISecret’
Test10399.hs:12:39: error: Not in scope: ‘P.base’
Test10399.hs:12:50: error: Not in scope: ‘P.pos’
Test10399.hs:12:60: error: Not in scope: ‘P.form’
Test10399.hs:10:27: error:
Not in scope: type constructor or class ‘MPISecret’
Test10399.hs:12:39: error:
Not in scope: ‘P.base’
No module named ‘P’ is imported.
Test10399.hs:12:50: error:
Not in scope: ‘P.pos’
No module named ‘P’ is imported.
Test10399.hs:12:60: error:
Not in scope: ‘P.form’
No module named ‘P’ is imported.
<interactive>:1:1: Not in scope: ‘System.IO.hPutStrLn’
<interactive>:1:1: error:
Not in scope: ‘System.IO.hPutStrLn’
No module named ‘System.IO’ is imported.
<interactive>:1:1:
<interactive>:1:1: error:
Not in scope: ‘H.bit’
Perhaps you meant ‘Q.bit’ (imported from T8639)
No module named ‘H’ is imported.
<interactive>:1:1: error: Variable not in scope: nubBy
<interactive>:1:1: error: Variable not in scope: nub
<interactive>:1:1: error: Variable not in scope: nubBy
<interactive>:1:1: error: Variable not in scope: nub
<interactive>:1:1: error:
Variable not in scope: nub
Perhaps you meant ‘L.nub’ (imported from Data.List)
<interactive>:1:1: error: Not in scope: ‘L.nub’
<interactive>:1:1: error: Variable not in scope: nub
<interactive>:1:1: error: Variable not in scope: nubBy
<interactive>:1:1: error: Variable not in scope: nub
<interactive>:1:1: error: Variable not in scope: nubBy
<interactive>:1:1: error: Variable not in scope: nub
<interactive>:1:1: error:
Variable not in scope: nub
Perhaps you meant ‘L.nub’ (imported from Data.List)
<interactive>:1:1: error:
Not in scope: ‘L.nub’
No module named ‘L’ is imported.
<interactive>:1:1: error: Variable not in scope: nub
mod134.hs:6:19:
mod134.hs:6:19: error:
Not in scope: ‘Prelude.head’
Perhaps you meant one of these:
‘Prelude.read’ (imported from Prelude),
‘Prelude.reads’ (imported from Prelude),
data constructor ‘Prelude.Left’ (imported from Prelude)
Perhaps you want to remove ‘head’ from the explicit hiding list
in the import of ‘Prelude’ (mod134.hs:4:1-28).
mod62.hs:3:9: Qualified name in binding position: M.y
mod62.hs:3:9: error: Qualified name in binding position: M.y
mod62.hs:3:22:
mod62.hs:3:22: error:
Not in scope: ‘M.y’
Perhaps you meant ‘M.x’ (line 3)
No module named ‘M’ is imported.
mod73.hs:3:7:
mod73.hs:3:7: error:
Not in scope: ‘Prelude.g’
Perhaps you meant one of these:
data constructor ‘Prelude.GT’ (imported from Prelude),
data constructor ‘Prelude.EQ’ (imported from Prelude),
data constructor ‘Prelude.LT’ (imported from Prelude)
Module ‘Prelude’ does not export ‘g’.
mod74.hs:3:7: Not in scope: ‘N.g’
mod74.hs:3:7: error:
Not in scope: ‘N.g’
No module named ‘N’ is imported.
mod88.hs:5:5: Not in scope: data constructor ‘Prelude.Left’
mod88.hs:5:5: error:
Not in scope: data constructor ‘Prelude.Left’
Perhaps you want to add ‘Left’ to the import list in the import of
‘Prelude’ (mod88.hs:4:1-30).
rnfail037.hs:8:7: Not in scope: data constructor ‘Rn037Help.C’
rnfail037.hs:8:7: error:
Not in scope: data constructor ‘Rn037Help.C’
Perhaps you want to remove ‘C’ from the explicit hiding list
in the import of ‘Rn037Help’ (rnfail037.hs:4:1-28).
T10781.hs:12:5: error: Not in scope: ‘Foo._name’
T10781.hs:12:5: error:
Not in scope: ‘Foo._name’
No module named ‘Foo’ is imported.
module T11071 where
import Data.List (lines)
import qualified Data.Map as M ()
import qualified Data.IntMap as M ()
import qualified Data.IntMap as M () -- just to see if this confused the code
import qualified Data.Ord as Ord hiding (Down)
import qualified Data.Map as M' hiding (size, filter)
import qualified Data.Map as M' hiding (size)
import qualified Data.IntMap as M' hiding (size)
import qualified System.IO as M' () -- unrelated
ignore :: a -> IO ()
ignore = const (return ())
main = do
ignore NoSuchModule.foo -- no such module
ignore Data.List.foobar -- does not exist (one import)
ignore M.foobar -- does not exist (two imports)
ignore M'.foobar -- does not exist (three imports)
ignore Data.List.sort -- needs import
ignore Data.List.unlines -- needs import, similar to imported
ignore M.size -- multiple modules to import from
ignore M.valid -- only one module to import from
ignore Ord.Down -- explicit hiding
ignore M'.size -- hidden and/or missing in import list
T11071.hs:19:12: error:
Not in scope: ‘NoSuchModule.foo’
No module named ‘NoSuchModule’ is imported.
T11071.hs:20:12: error:
Not in scope: ‘Data.List.foobar’
Module ‘Data.List’ does not export ‘foobar’.
T11071.hs:21:12: error:
Not in scope: ‘M.foobar’
Neither ‘Data.Map’ nor ‘Data.IntMap’ exports ‘foobar’.
T11071.hs:22:12: error:
Not in scope: ‘M'.foobar’
Neither ‘Data.Map’, ‘Data.IntMap’ nor ‘System.IO’ exports ‘foobar’.
T11071.hs:23:12: error:
Not in scope: ‘Data.List.sort’
Perhaps you want to add ‘sort’ to the import list in the import of
‘Data.List’ (T11071.hs:3:1-24).
T11071.hs:24:12: error:
Not in scope: ‘Data.List.unlines’
Perhaps you meant ‘Data.List.lines’ (imported from Data.List)
Perhaps you want to add ‘unlines’ to the import list
in the import of ‘Data.List’ (T11071.hs:3:1-24).
T11071.hs:25:12: error:
Not in scope: ‘M.size’
Perhaps you want to add ‘size’ to one of these import lists:
‘Data.Map’ (T11071.hs:4:1-33)
‘Data.IntMap’ (T11071.hs:5:1-36)
T11071.hs:26:12: error:
Not in scope: ‘M.valid’
Perhaps you meant one of these:
‘M'.valid’ (imported from Data.Map),
‘M'.valid’ (imported from Data.Map)
Perhaps you want to add ‘valid’ to the import list in the import of
‘Data.Map’ (T11071.hs:4:1-33).
T11071.hs:27:12: error:
Not in scope: data constructor ‘Ord.Down’
Perhaps you want to remove ‘Down’ from the explicit hiding list
in the import of ‘Data.Ord’ (T11071.hs:8:1-46).
T11071.hs:28:12: error:
Not in scope: ‘M'.size’
Perhaps you want to remove ‘size’ from the hiding clauses
in one of these imports:
‘Data.Map’ (T11071.hs:10:1-53)
‘Data.IntMap’ (T11071.hs:12:1-48)
T2901.hs:6:5: Not in scope: data constructor ‘F.Foo’
T2901.hs:6:5: error:
Not in scope: data constructor ‘F.Foo’
No module named ‘F’ is imported.
T2901.hs:6:13: ‘F.field’ is not a (visible) constructor field name
T2901.hs:6:13: error:
‘F.field’ is not a (visible) constructor field name
T5657.hs:3:8: Not in scope: ‘LT..’
T5657.hs:3:8: error:
Not in scope: ‘LT..’
No module named ‘LT’ is imported.
T5657.hs:3:8:
T5657.hs:3:8: error:
A section must be enclosed in parentheses thus: (LT.. GT)
T5892b.hs:11:7: Not in scope: ‘T5892b.subForest’
T5892b.hs:11:7: error:
Not in scope: ‘T5892b.subForest’
No module named ‘T5892b’ is imported.
......@@ -138,3 +138,4 @@ test('T10618', normal, compile_fail, [''])
test('T10668', normal, compile_fail, [''])
test('T5001b', normal, compile_fail, [''])
test('T10781', normal, compile_fail, [''])
test('T11071', normal, compile_fail, [''])
rnfail030.hs:2:21: Not in scope: ‘Data.List.map’
rnfail030.hs:2:21: error:
Not in scope: ‘Data.List.map’
Perhaps you want to add ‘map’ to the import list in the import of
‘Data.List’ (rnfail030.hs:3:1-19).
rnfail031.hs:2:21: Not in scope: ‘Data.List.map’
rnfail031.hs:2:21: error:
Not in scope: ‘Data.List.map’
Perhaps you want to add ‘map’ to the import list in the import of
‘Data.List’ (rnfail031.hs:3:1-36).
rnfail032.hs:2:21:
rnfail032.hs:2:21: error:
Not in scope: ‘Data.List.map’
Perhaps you meant one of these:
‘Data.List.zip’ (imported from Data.List),
‘Data.List.all’ (imported from Data.List),
‘Data.List.and’ (imported from Data.List)
Perhaps you want to remove ‘map’ from the explicit hiding list
in the import of ‘Data.List’ (rnfail032.hs:3:1-41).
rnfail033.hs:2:21:
rnfail033.hs:2:21: error:
Not in scope: ‘Data.List.map’
Perhaps you meant one of these:
‘Data.List.zip’ (imported from Data.List),
‘Data.List.all’ (imported from Data.List),
‘Data.List.and’ (imported from Data.List)
Perhaps you want to remove ‘map’ from the explicit hiding list
in the import of ‘Data.List’ (rnfail033.hs:3:1-31).
rnfail034.hs:4:11: Qualified name in binding position: M.y
rnfail034.hs:4:11: error: Qualified name in binding position: M.y
rnfail034.hs:4:26:
rnfail034.hs:4:26: error:
Not in scope: ‘M.y’
Perhaps you meant ‘M.g’ (line 4)
No module named ‘M’ is imported.
<interactive>:5:9: error:
Not in scope: ‘System.IO.Unsafe.unsafePerformIO’
<interactive>:6:9: error:
Variable not in scope: x :: IO Integer -> t
<interactive>:7:1: error: Variable not in scope: y
<interactive>:5:9: error:
Not in scope: ‘System.IO.Unsafe.unsafePerformIO’
No module named ‘System.IO.Unsafe’ is imported.
<interactive>:6:9: error:
Variable not in scope: x :: IO Integer -> t
<interactive>:7:1: error: Variable not in scope: y
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment