- full_avail_env :: UniqFM AvailInfo
- full_avail_env = addListToUFM_C plusAvail emptyUFM
- [(name,avail) | avail@(Avail name _) <- concat (eltsFM all_avails)]
- -- NB: full_avail_env won't contain bindings for data constructors and class ops,
- -- which is right and proper; attempts to export them on their own will provoke an error
-
- exports_from_item :: RdrNameIE -> RnMG AvailEnv
- exports_from_item ie@(IEModuleContents mod)
- = case lookupFM all_avails mod of
- Nothing -> failWithRn emptyAvailEnv (modExportErr mod)
- Just avails -> addOccurrenceNames Compulsory [n | Avail n _ <- avails] `thenRn_`
- listToAvailEnv ie avails
-
- exports_from_item ie
- | not (maybeToBool maybe_in_scope)
- = failWithRn emptyAvailEnv (unknownNameErr (ieName ie))
-
-#ifdef DEBUG
- -- I can't see why this should ever happen; if the thing is in scope
- -- at all it ought to have some availability
- | not (maybeToBool maybe_avail)
- = pprTrace "exportsFromAvail: curious Nothing:" (ppr PprDebug name)
- returnRn emptyAvailEnv
-#endif
-
- | not enough_avail
- = failWithRn emptyAvailEnv (exportItemErr ie export_avail)
-
- | otherwise -- Phew! It's OK!
- = addOccurrenceName Compulsory name `thenRn_`
- returnRn (unitAvailEnv ie export_avail)
- where
- maybe_in_scope = lookupNameEnv name_env (ieName ie)
- Just name = maybe_in_scope
- maybe_avail = lookupUFM full_avail_env name
- Just avail = maybe_avail
- export_avail = filterAvail ie avail
- enough_avail = case export_avail of {NotAvailable -> False; other -> True}
-
- -- We export a fixity iff we export a thing with the same (qualified) RdrName
- mk_exported_fixities :: NameSet -> [(OccName, (Fixity, Provenance))]
- mk_exported_fixities exports
- = fmToList (foldr (perhaps_add_fixity exports)
- emptyFM
- (fmToList fixity_env))
-
- perhaps_add_fixity :: NameSet -> (RdrName, (Fixity, Provenance))
- -> FiniteMap OccName (Fixity,Provenance)
- -> FiniteMap OccName (Fixity,Provenance)
- perhaps_add_fixity exports (rdr_name, (fixity, prov)) fix_env
- = let
- do_nothing = fix_env -- The default is to pass on the env unchanged
- in
- -- Step 1: check whether the rdr_name is in scope; if so find its Name
- case lookupFM name_env rdr_name of {
- Nothing -> do_nothing;
- Just fixity_name ->
-
- -- Step 2: check whether the fixity thing is exported
- if not (fixity_name `elemNameSet` exports) then
- do_nothing
- else
-
- -- Step 3: check whether we already have a fixity for the
- -- Name's OccName in the fix_env we are building up. This can easily
- -- happen. the original fixity_env might contain bindings for
- -- M.a and N.a, if a was imported via M and N.
- -- If this does happen, we expect the fixity to be the same either way.
- let
- occ_name = rdrNameOcc rdr_name
- in
- case lookupFM fix_env occ_name of {
- Just (fixity1, prov1) -> -- Got it already
- ASSERT( fixity == fixity1 )
- do_nothing;
- Nothing ->
-
- -- Step 3: add it to the outgoing fix_env
- addToFM fix_env occ_name (fixity,prov)
- }}
-
-mk_export_fn :: [AvailInfo] -> (Name -> ExportFlag)
-mk_export_fn avails
- = \name -> if name `elemNameSet` exported_names
- then Exported
- else NotExported
+ used_names, all_used_names :: NameSet
+ used_names = findUses (tcg_dus gbl_env) emptyNameSet
+ all_used_names = used_names `unionNameSets`
+ mkNameSet (mapCatMaybes nameParent_maybe (nameSetToList used_names))
+ -- A use of C implies a use of T,
+ -- if C was brought into scope by T(..) or T(C)
+
+ -- Collect the defined names from the in-scope environment
+ defined_names :: [GlobalRdrElt]
+ defined_names = globalRdrEnvElts (tcg_rdr_env gbl_env)
+
+ -- Note that defined_and_used, defined_but_not_used
+ -- are both [GRE]; that's why we need defined_and_used
+ -- rather than just all_used_names
+ defined_and_used, defined_but_not_used :: [GlobalRdrElt]
+ (defined_and_used, defined_but_not_used)
+ = partition (gre_is_used all_used_names) defined_names
+
+ -- Find the duplicate imports
+ dup_imps = filter is_dup defined_and_used
+ is_dup (GRE {gre_prov = Imported imp_spec True}) = not (isSingleton imp_spec)
+ is_dup other = False
+
+ -- Filter out the ones that are
+ -- (a) defined in this module, and
+ -- (b) not defined by a 'deriving' clause
+ -- The latter have an Internal Name, so we can filter them out easily
+ unused_locals :: [GlobalRdrElt]
+ unused_locals = filter is_unused_local defined_but_not_used
+ is_unused_local :: GlobalRdrElt -> Bool
+ is_unused_local gre = isLocalGRE gre && isExternalName (gre_name gre)
+
+ unused_imports :: [GlobalRdrElt]
+ unused_imports = filter unused_imp defined_but_not_used
+ unused_imp (GRE {gre_prov = Imported imp_specs True})
+ = not (all (module_unused . is_mod) imp_specs)
+ -- Don't complain about unused imports if we've already said the
+ -- entire import is unused
+ unused_imp other = False
+
+ -- 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 Module 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_name = n, gre_prov = Imported imp_specs _}) acc
+ = addToFM_C plusAvailEnv acc (is_mod (head imp_specs))
+ (unitAvailEnv (mk_avail n (nameParent_maybe n)))
+ add_name other acc
+ = acc
+
+ -- 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
+
+ -- n is the name of the thing, p is the name of its parent
+ mk_avail n (Just p) = AvailTC p [p,n]
+ mk_avail n Nothing | isTcOcc (nameOccName n) = AvailTC n [n]
+ | otherwise = Avail n
+
+ add_inst_mod (mod,_,_) acc
+ | mod `elemFM` acc = acc -- We import something already
+ | otherwise = addToFM acc mod emptyAvailEnv
+ where
+ -- 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, Maybe Bool, SrcSpan)]
+ -- See the type of the imp_mods for this triple
+ direct_import_mods = moduleEnvElts (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: does not deal correctly with multiple imports of the same module
+ -- becuase direct_import_mods has only one entry per module
+ unused_imp_mods = [(mod,loc) | (mod,imp,loc) <- direct_import_mods,
+ not (mod `elemFM` minimal_imports1),
+ mod /= pRELUDE,
+ imp /= Just False]
+ -- The Just False part is not to complain about
+ -- import M (), which is an idiom for importing
+ -- instance declarations
+
+ module_unused :: Module -> Bool
+ module_unused mod = any (((==) mod) . fst) unused_imp_mods
+
+---------------------
+warnDuplicateImports :: [GlobalRdrElt] -> RnM ()
+warnDuplicateImports gres
+ = ifOptM Opt_WarnUnusedImports (mapM_ warn gres)