-
- unused_imports :: [GlobalRdrElt]
- unused_imports = mapCatMaybes unused_imp defined_but_not_used
- unused_imp :: GlobalRdrElt -> Maybe GlobalRdrElt -- Result has trimmed Imported provenances
- unused_imp (GRE {gre_prov = LocalDef}) = Nothing
- unused_imp gre@(GRE {gre_prov = Imported imp_specs})
- | null trimmed_specs = Nothing
- | otherwise = Just (gre {gre_prov = Imported trimmed_specs})
- where
- trimmed_specs = filter report_if_unused imp_specs
-
- -- To figure out the minimal set of imports, start with the things
- -- that are in scope (i.e. in gbl_env). Then just combine them
- -- into a bunch of avails, so they are properly grouped
- --
- -- BUG WARNING: this does not deal properly with qualified imports!
- minimal_imports :: FiniteMap ModuleName AvailEnv
- minimal_imports0 = foldr add_expall emptyFM expall_mods
- minimal_imports1 = foldr add_name minimal_imports0 defined_and_used
- minimal_imports = foldr add_inst_mod minimal_imports1 direct_import_mods
- -- The last line makes sure that we retain all direct imports
- -- even if we import nothing explicitly.
- -- It's not necessarily redundant to import such modules. Consider
- -- module This
- -- import M ()
- --
- -- The import M() is not *necessarily* redundant, even if
- -- we suck in no instance decls from M (e.g. it contains
- -- no instance decls, or This contains no code). It may be
- -- that we import M solely to ensure that M's orphan instance
- -- decls (or those in its imports) are visible to people who
- -- import This. Sigh.
- -- There's really no good way to detect this, so the error message
- -- in RnEnv.warnUnusedModules is weakened instead
-
- -- We've carefully preserved the provenance so that we can
- -- construct minimal imports that import the name by (one of)
- -- the same route(s) as the programmer originally did.
- add_name gre@(GRE {gre_prov = Imported (imp_spec:_)}) acc
- = addToFM_C plusAvailEnv acc
- (importSpecModule imp_spec) (unitAvailEnv (greAvail gre))
- add_name _ acc = acc -- Local
-
- -- Modules mentioned as 'module M' in the export list
- expall_mods = case export_decls of
- Nothing -> []
- Just es -> [m | L _ (IEModuleContents m) <- es]
-
- -- This is really bogus. The idea is that if we see 'module M' in
- -- the export list we must retain the import decls that drive it
- -- If we aren't careful we might see
- -- module A( module M ) where
- -- import M
- -- import N
- -- and suppose that N exports everything that M does. Then we
- -- must not drop the import of M even though N brings it all into
- -- scope.
- --
- -- BUG WARNING: 'module M' exports aside, what if M.x is mentioned?!
- --
- -- The reason that add_expall is bogus is that it doesn't take
- -- qualified imports into account. But it's an improvement.
- add_expall mod acc = addToFM_C plusAvailEnv acc mod emptyAvailEnv
-
- add_inst_mod (mod, _) acc
- | mod_name `elemFM` acc = acc -- We import something already
- | otherwise = addToFM acc mod_name emptyAvailEnv
- where
- mod_name = moduleName mod
- -- Add an empty collection of imports for a module
- -- from which we have sucked only instance decls
-
- imports = tcg_imports gbl_env
-
- direct_import_mods :: [(Module, [(ModuleName, Bool, SrcSpan)])]
- -- See the type of the imp_mods for this triple
- direct_import_mods = fmToList (imp_mods imports)
-
- -- unused_imp_mods are the directly-imported modules
- -- that are not mentioned in minimal_imports1
- -- [Note: not 'minimal_imports', because that includes directly-imported
- -- modules even if we use nothing from them; see notes above]
- --
- -- BUG WARNING: this code is generally buggy
- unused_imp_mods :: [(ModuleName, SrcSpan)]
- unused_imp_mods = [(mod_name,loc)
- | (mod, xs) <- direct_import_mods,
- (_, no_imp, loc) <- xs,
- let mod_name = moduleName mod,
- not (mod_name `elemFM` minimal_imports1),
- moduleName mod /= pRELUDE_NAME,
- -- XXX not really correct, but we don't want
- -- to generate warnings when compiling against
- -- a compat version of base.
- not no_imp]
- -- The not no_imp part is not to complain about
- -- import M (), which is an idiom for importing
- -- instance declarations
-
- module_unused :: ModuleName -> Bool
- module_unused mod = any (((==) mod) . fst) unused_imp_mods
-
- report_if_unused :: ImportSpec -> Bool
- -- Do we want to report this as an unused import?
- report_if_unused (ImpSpec {is_decl = d, is_item = i})
- = not (module_unused (is_mod d)) -- Not if we've already said entire import is unused
- && isExplicitItem i -- Only if the import was explicit
-
----------------------
-warnDuplicateImports :: [GlobalRdrElt] -> RnM ()
--- Given the GREs for names that are used, figure out which imports
--- could be omitted without changing the top-level environment.
+\end{code}
+
+%*********************************************************
+%* *
+\subsection{Unused imports}
+%* *
+%*********************************************************
+
+This code finds which import declarations are unused. The
+specification and implementation notes are here:
+ http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/UnusedImports
+
+\begin{code}
+type ImportDeclUsage
+ = ( LImportDecl Name -- The import declaration
+ , [AvailInfo] -- What *is* used (normalised)
+ , [Name] ) -- What is imported but *not* used
+\end{code}
+
+\begin{code}
+warnUnusedImportDecls :: TcGblEnv -> RnM ()
+warnUnusedImportDecls gbl_env
+ = do { uses <- readMutVar (tcg_used_rdrnames gbl_env)
+ ; let imports = filter explicit_import (tcg_rn_imports gbl_env)
+ rdr_env = tcg_rdr_env gbl_env
+
+ ; let usage :: [ImportDeclUsage]
+ usage = findImportUsage imports rdr_env (Set.elems uses)
+
+ ; ifDOptM Opt_WarnUnusedImports $
+ mapM_ warnUnusedImport usage
+
+ ; ifDOptM Opt_D_dump_minimal_imports $
+ printMinimalImports usage }
+ where
+ explicit_import (L loc _) = isGoodSrcSpan loc
+ -- Filter out the implicit Prelude import
+ -- which we do not want to bleat about
+\end{code}
+
+\begin{code}
+findImportUsage :: [LImportDecl Name]
+ -> GlobalRdrEnv
+ -> [RdrName]
+ -> [ImportDeclUsage]
+
+type ImportMap = Map SrcLoc [AvailInfo]
+-- The intermediate data struture records, for each import
+-- declaration, what stuff brought into scope by that
+-- declaration is actually used in the module.