From: simonpj Date: Fri, 19 Dec 2003 10:34:52 +0000 (+0000) Subject: [project @ 2003-12-19 10:34:51 by simonpj] X-Git-Tag: Approx_11550_changesets_converted~171 X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=commitdiff_plain;h=3674a5cc2879ef276785c1b19af7c6f6bdee1488 [project @ 2003-12-19 10:34:51 by simonpj] --------------------------------------- Precise locations for duplicate imports --------------------------------------- As a displacment activity I added reporting for duplicate imports And more precise locations for the imports themselves. For example: module T where import Maybe (isJust,isJust) import Maybe (isJust) gives Foo.hs: Warning: `isJust' is imported more than once: imported from Maybe at Foo.hs:4:16-21 imported from Maybe at Foo.hs:3:23-28 imported from Maybe at Foo.hs:3:16-21 --- diff --git a/ghc/compiler/basicTypes/RdrName.lhs b/ghc/compiler/basicTypes/RdrName.lhs index b7b9ed2..7557145 100644 --- a/ghc/compiler/basicTypes/RdrName.lhs +++ b/ghc/compiler/basicTypes/RdrName.lhs @@ -473,10 +473,11 @@ pprNameProvenance :: GlobalRdrElt -> SDoc pprNameProvenance (GRE {gre_name = name, gre_prov = LocalDef _}) = ptext SLIT("defined at") <+> ppr (nameSrcLoc name) pprNameProvenance (GRE {gre_name = name, gre_prov = Imported (why:whys) _}) - = sep [ppr_reason why, nest 2 (ppr_defn (nameSrcLoc name))] + = sep [ppr why, nest 2 (ppr_defn (nameSrcLoc name))] -ppr_reason imp_spec - = ptext SLIT("imported from") <+> ppr (is_mod imp_spec) +instance Outputable ImportSpec where + ppr imp_spec + = ptext SLIT("imported from") <+> ppr (is_mod imp_spec) <+> ptext SLIT("at") <+> ppr (is_loc imp_spec) ppr_defn loc | isGoodSrcLoc loc = parens (ptext SLIT("defined at") <+> ppr loc) diff --git a/ghc/compiler/rename/RnNames.lhs b/ghc/compiler/rename/RnNames.lhs index bdedc33..38b3597 100644 --- a/ghc/compiler/rename/RnNames.lhs +++ b/ghc/compiler/rename/RnNames.lhs @@ -32,7 +32,8 @@ import Name ( Name, nameSrcLoc, nameOccName, nameModuleName, nameParent, nameParent_maybe, isExternalName ) import NameSet import NameEnv -import OccName ( OccName, srcDataName, isTcOcc ) +import OccName ( OccName, srcDataName, isTcOcc, OccEnv, elemOccEnv, + mkOccEnv, lookupOccEnv, emptyOccEnv, extendOccEnv ) import HscTypes ( GenAvailInfo(..), AvailInfo, Avails, GhciMode(..), IsBootInterface, IfaceExport, availName, availNames, availsToNameSet, unQualInScope, @@ -49,7 +50,7 @@ import Maybes ( isJust, isNothing, catMaybes, mapCatMaybes ) import SrcLoc ( noSrcLoc, Located(..), mkGeneralSrcSpan, unLoc, noLoc ) import ListSetOps ( removeDups ) -import Util ( sortLt, notNull ) +import Util ( sortLt, notNull, isSingleton ) import List ( partition, insert ) import IO ( openFile, IOMode(..) ) \end{code} @@ -173,11 +174,19 @@ importsFromImportDecl this_mod -- import {-# SOURCE #-} A( AType ) -- -- then you'll get a 'B does not export AType' message. Oh well. + + qual_mod_name = case as_mod of + Nothing -> imp_mod_name + Just another_name -> another_name + imp_spec = ImportSpec { is_mod = imp_mod_name, is_qual = qual_only, + is_loc = loc, is_as = qual_mod_name } + mk_deprec = mi_dep_fn iface in - exportsToAvails filtered_exports `thenM` \ avails -> - -- Filter the imports according to the import list - filterImports imp_mod want_boot imp_details avails `thenM` \ (filtered_avails, explicits) -> + -- Get the total imports, and filter them according to the import list + exportsToAvails filtered_exports `thenM` \ total_avails -> + filterImports iface imp_spec + imp_details total_avails `thenM` \ (avail_env, gbl_env) -> let -- Compute new transitive dependencies @@ -208,24 +217,10 @@ importsFromImportDecl this_mod _ -> Nothing -- Everything is imported, -- (or almost everything [hiding]) - qual_mod_name = case as_mod of - Nothing -> imp_mod_name - Just another_name -> another_name - -- unqual_avails is the Avails that are visible in *unqualified* form -- We need to know this so we know what to export when we see -- module M ( module P ) where ... -- Then we must export whatever came from P unqualified. - imp_spec = ImportSpec { is_mod = imp_mod_name, is_qual = qual_only, - is_loc = loc, is_as = qual_mod_name } - mk_deprec = mi_dep_fn iface - gres = [ GRE { gre_name = name, - gre_prov = Imported [imp_spec] (name `elemNameSet` explicits), - gre_deprec = mk_deprec name } - | avail <- filtered_avails, name <- availNames avail ] - gbl_env = mkGlobalRdrEnv gres - - avail_env = mkAvailEnv filtered_avails imports = ImportAvails { imp_qual = unitModuleEnvByName qual_mod_name avail_env, imp_env = avail_env, @@ -409,52 +404,72 @@ getLocalDeclBinders mod (HsGroup {hs_valds = val_decls, available, and filters it through the import spec (if any). \begin{code} -filterImports :: Module -- The module being imported - -> IsBootInterface -- Tells whether it's a {-# SOURCE #-} import +filterImports :: ModIface + -> ImportSpec -- The span for the entire import decl -> Maybe (Bool, [Located (IE RdrName)]) -- Import spec; True => hiding -> [AvailInfo] -- What's available - -> RnM ([AvailInfo], -- What's imported - NameSet) -- What was imported explicitly + -> RnM (AvailEnv, -- What's imported + GlobalRdrEnv) -- ...in two forms -- Complains if import spec mentions things that the module doesn't export -- Warns/informs if import spec contains duplicates. -filterImports mod from Nothing imports - = returnM (imports, emptyNameSet) - -filterImports mod from (Just (want_hiding, import_items)) total_avails - = mappM (addLocM get_item) import_items `thenM` \ avails_w_explicits_s -> +mkGenericRdrEnv iface imp_spec avails + = mkGlobalRdrEnv [ GRE { gre_name = name, gre_prov = Imported [imp_spec] False, + gre_deprec = mi_dep_fn iface name } + | avail <- avails, name <- availNames avail ] + +filterImports iface imp_spec Nothing total_avails + = returnM (mkAvailEnv total_avails, mkGenericRdrEnv iface imp_spec total_avails) + +filterImports iface imp_spec (Just (want_hiding, import_items)) total_avails + = mapAndUnzipM (addLocM get_item) import_items `thenM` \ (avails, gres) -> let - (item_avails, explicits_s) = unzip (concat avails_w_explicits_s) - explicits = foldl addListToNameSet emptyNameSet explicits_s + all_avails = foldr plusAvailEnv emptyAvailEnv avails + rdr_env = foldr plusGlobalRdrEnv emptyGlobalRdrEnv gres in - if want_hiding then - let -- All imported; item_avails to be hidden - hidden = availsToNameSet item_avails - keep n = not (n `elemNameSet` hidden) - in - returnM (pruneAvails keep total_avails, emptyNameSet) + if not want_hiding then + returnM (all_avails, rdr_env) else - -- Just item_avails imported; nothing to be hidden - returnM (item_avails, explicits) + let -- Hide stuff in all_avails + hidden = availsToNameSet (availEnvElts all_avails) + keep n = not (n `elemNameSet` hidden) + pruned_avails = pruneAvails keep total_avails + in + returnM (mkAvailEnv pruned_avails, mkGenericRdrEnv iface imp_spec pruned_avails) where - import_fm :: FiniteMap OccName AvailInfo - import_fm = listToFM [ (nameOccName name, avail) + import_fm :: OccEnv AvailInfo + import_fm = mkOccEnv [ (nameOccName name, avail) | avail <- total_avails, name <- availNames avail] -- Even though availNames returns data constructors too, -- they won't make any difference because naked entities like T -- in an import list map to TcOccs, not VarOccs. - bale_out item = addErr (badImportItemErr mod from item) `thenM_` - returnM [] + bale_out item = addErr (badImportItemErr iface imp_spec item) `thenM_` + returnM (emptyAvailEnv, emptyGlobalRdrEnv) + + mk_deprec = mi_dep_fn iface - get_item :: IE RdrName -> RnM [(AvailInfo, [Name])] - -- Empty list for a bad item. - -- Singleton is typical case. + succeed_with :: Bool -> AvailInfo -> RnM (AvailEnv, GlobalRdrEnv) + succeed_with all_explicit avail + = do { loc <- getSrcSpanM + ; returnM (unitAvailEnv avail, + mkGlobalRdrEnv (map (mk_gre loc) (availNames avail))) } + where + mk_gre loc name = GRE { gre_name = name, + gre_prov = Imported [this_imp_spec loc] (explicit name), + gre_deprec = mk_deprec name } + this_imp_spec loc = imp_spec { is_loc = loc } + explicit name = all_explicit || name == main_name + main_name = availName avail + + get_item :: IE RdrName -> RnM (AvailEnv, GlobalRdrEnv) + -- Empty result for a bad item. + -- Singleton result is typical case. -- Can have two when we are hiding, and mention C which might be -- both a class and a data constructor. - -- The [Name] is the list of explicitly-mentioned names - get_item item@(IEModuleContents _) = bale_out item + get_item item@(IEModuleContents _) + = bale_out item get_item item@(IEThingAll tc) = case check_item item of @@ -462,24 +477,24 @@ filterImports mod from (Just (want_hiding, import_items)) total_avails Just avail@(AvailTC _ [n]) -> -- This occurs when you import T(..), but -- only export T abstractly. The single [n] -- in the AvailTC is the type or class itself - ifOptM Opt_WarnMisc (addWarn (dodgyImportWarn mod tc)) `thenM_` - returnM [(avail, [availName avail])] - Just avail -> returnM [(avail, [availName avail])] + ifOptM Opt_WarnMisc (addWarn (dodgyImportWarn tc)) `thenM_` + succeed_with False avail + Just avail -> succeed_with False avail get_item item@(IEThingAbs n) | want_hiding -- hiding( C ) -- Here the 'C' can be a data constructor *or* a type/class = case catMaybes [check_item item, check_item (IEVar data_n)] of - [] -> bale_out item - avails -> returnM [(a, []) | a <- avails] - -- The 'explicits' list is irrelevant when hiding + [] -> bale_out item + avails -> returnM (mkAvailEnv avails, emptyGlobalRdrEnv) + -- The GlobalRdrEnv result is irrelevant when hiding where data_n = setRdrNameSpace n srcDataName get_item item = case check_item item of Nothing -> bale_out item - Just avail -> returnM [(avail, availNames avail)] + Just avail -> succeed_with True avail check_item item | isNothing maybe_in_import_avails || @@ -491,7 +506,7 @@ filterImports mod from (Just (want_hiding, import_items)) total_avails where wanted_occ = rdrNameOcc (ieName item) - maybe_in_import_avails = lookupFM import_fm wanted_occ + maybe_in_import_avails = lookupOccEnv import_fm wanted_occ Just avail = maybe_in_import_avails maybe_filtered_avail = filterAvail item avail @@ -561,9 +576,9 @@ type ExportAccum -- The type of the accumulating parameter of ExportOccMap, -- Tracks exported occurrence names AvailEnv) -- The accumulated exported stuff, kept in an env -- so we can common-up related AvailInfos -emptyExportAccum = ([], emptyFM, emptyAvailEnv) +emptyExportAccum = ([], emptyOccEnv, emptyAvailEnv) -type ExportOccMap = FiniteMap OccName (Name, IE RdrName) +type ExportOccMap = OccEnv (Name, IE RdrName) -- Tracks what a particular exported OccName -- in an export list refers to, and which item -- it came from. It's illegal to export two distinct things @@ -706,8 +721,8 @@ check_occs ie occs avail = foldlM check occs (availNames avail) where check occs name - = case lookupFM occs name_occ of - Nothing -> returnM (addToFM occs name_occ (name, ie)) + = case lookupOccEnv occs name_occ of + Nothing -> returnM (extendOccEnv occs name_occ (name, ie)) Just (name', ie') | name == name' -- Duplicate export @@ -735,6 +750,7 @@ reportUnusedNames gbl_env = warnUnusedModules unused_imp_mods `thenM_` warnUnusedTopBinds bad_locals `thenM_` warnUnusedImports bad_imports `thenM_` + warnDuplicateImports dup_imps `thenM_` printMinimalImports minimal_imports where used_names, all_used_names :: NameSet @@ -751,6 +767,7 @@ reportUnusedNames gbl_env defined_and_used, defined_but_not_used :: [GlobalRdrElt] (defined_and_used, defined_but_not_used) = partition is_used defined_names + dup_imps = filter isDupImport defined_and_used is_used gre = gre_name gre `elemNameSet` all_used_names -- Filter out the ones that are @@ -842,6 +859,18 @@ reportUnusedNames gbl_env module_unused mod = mod `elem` unused_imp_mods +isDupImport (GRE {gre_prov = Imported imp_spec True}) = not (isSingleton imp_spec) +isDupImport other = False + +warnDuplicateImports :: [GlobalRdrElt] -> RnM () +warnDuplicateImports gres + = ifOptM Opt_WarnUnusedImports (mapM_ warn gres) + where + warn (GRE { gre_name = name, gre_prov = Imported imps _ }) + = addWarn ((quotes (ppr name) <+> ptext SLIT("is imported more than once:")) + $$ nest 2 (vcat (map ppr imps))) + + -- ToDo: deal with original imports with 'qualified' and 'as M' clauses printMinimalImports :: FiniteMap ModuleName AvailEnv -- Minimal imports -> RnM () @@ -900,16 +929,15 @@ printMinimalImports imps %************************************************************************ \begin{code} -badImportItemErr mod from ie - = sep [ptext SLIT("Module"), quotes (ppr mod), source_import, +badImportItemErr iface imp_spec ie + = sep [ptext SLIT("Module"), quotes (ppr (is_mod imp_spec)), source_import, ptext SLIT("does not export"), quotes (ppr ie)] where - source_import = case from of - True -> ptext SLIT("(hi-boot interface)") - other -> empty + source_import | mi_boot iface = ptext SLIT("(hi-boot interface)") + | otherwise = empty -dodgyImportWarn mod item = dodgyMsg (ptext SLIT("import")) item -dodgyExportWarn item = dodgyMsg (ptext SLIT("export")) item +dodgyImportWarn item = dodgyMsg (ptext SLIT("import")) item +dodgyExportWarn item = dodgyMsg (ptext SLIT("export")) item dodgyMsg kind tc = sep [ ptext SLIT("The") <+> kind <+> ptext SLIT("item") <+> quotes (ppr (IEThingAll tc)),