summaryrefslogtreecommitdiff
path: root/compiler/basicTypes/RdrName.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/basicTypes/RdrName.hs')
-rw-r--r--compiler/basicTypes/RdrName.hs102
1 files changed, 73 insertions, 29 deletions
diff --git a/compiler/basicTypes/RdrName.hs b/compiler/basicTypes/RdrName.hs
index 610233ed9a..4a9d3e9841 100644
--- a/compiler/basicTypes/RdrName.hs
+++ b/compiler/basicTypes/RdrName.hs
@@ -53,14 +53,14 @@ module RdrName (
-- * GlobalRdrElts
gresFromAvails, gresFromAvail, localGREsFromAvail, availFromGRE,
- greUsedRdrName, greRdrNames, greSrcSpan, greQualModName,
+ greRdrNames, greSrcSpan, greQualModName,
gresToAvailInfo,
-- ** Global 'RdrName' mapping elements: 'GlobalRdrElt', 'Provenance', 'ImportSpec'
GlobalRdrElt(..), isLocalGRE, isRecFldGRE, greLabel,
unQualOK, qualSpecOK, unQualSpecOK,
pprNameProvenance,
- Parent(..),
+ Parent(..), greParent_maybe,
ImportSpec(..), ImpDeclSpec(..), ImpItemSpec(..),
importSpecLoc, importSpecModule, isExplicitItem, bestImport,
@@ -657,18 +657,6 @@ greQualModName gre@(GRE { gre_name = name, gre_lcl = lcl, gre_imp = iss })
| (is:_) <- iss = is_as (is_decl is)
| otherwise = pprPanic "greQualModName" (ppr gre)
-greUsedRdrName :: GlobalRdrElt -> RdrName
--- For imported things, return a RdrName to add to the used-RdrName
--- set, which is used to generate unused-import-decl warnings.
--- Return a Qual RdrName if poss, so that identifies the most
--- specific ImportSpec. See Trac #10890 for some good examples.
-greUsedRdrName gre@GRE{ gre_name = name, gre_lcl = lcl, gre_imp = iss }
- | lcl, Just mod <- nameModule_maybe name = Qual (moduleName mod) occ
- | not (null iss), is <- bestImport iss = Qual (is_as (is_decl is)) occ
- | otherwise = pprTrace "greUsedRdrName" (ppr gre) (Unqual occ)
- where
- occ = greOccName gre
-
greRdrNames :: GlobalRdrElt -> [RdrName]
greRdrNames gre@GRE{ gre_lcl = lcl, gre_imp = iss }
= (if lcl then [unqual] else []) ++ concatMap do_spec (map is_decl iss)
@@ -696,11 +684,11 @@ mkParent _ (Avail _) = NoParent
mkParent n (AvailTC m _ _) | n == m = NoParent
| otherwise = ParentIs m
-greParentName :: GlobalRdrElt -> Maybe Name
-greParentName gre = case gre_par gre of
- NoParent -> Nothing
- ParentIs n -> Just n
- FldParent n _ -> Just n
+greParent_maybe :: GlobalRdrElt -> Maybe Name
+greParent_maybe gre = case gre_par gre of
+ NoParent -> Nothing
+ ParentIs n -> Just n
+ FldParent n _ -> Just n
-- | Takes a list of distinct GREs and folds them
-- into AvailInfos. This is more efficient than mapping each individual
@@ -716,7 +704,7 @@ gresToAvailInfo gres
add :: NameEnv AvailInfo -> GlobalRdrElt -> NameEnv AvailInfo
add env gre = extendNameEnv_Acc comb availFromGRE env
(fromMaybe (gre_name gre)
- (greParentName gre)) gre
+ (greParent_maybe gre)) gre
where
-- We want to insert the child `k` into a list of children but
@@ -1192,10 +1180,7 @@ instance Ord ImpItemSpec where
bestImport :: [ImportSpec] -> ImportSpec
--- Given a non-empty bunch of ImportSpecs, return the one that
--- imported the item most specifically (e.g. by name), using
--- textually-first as a tie breaker. This is used when reporting
--- redundant imports
+-- See Note [Choosing the best import declaration]
bestImport iss
= case sortBy best iss of
(is:_) -> is
@@ -1203,17 +1188,76 @@ bestImport iss
where
best :: ImportSpec -> ImportSpec -> Ordering
-- Less means better
+ -- Unqualified always wins over qualified; then
+ -- import-all wins over import-some; then
+ -- earlier declaration wins over later
best (ImpSpec { is_item = item1, is_decl = d1 })
(ImpSpec { is_item = item2, is_decl = d2 })
- = best_item item1 item2 `thenCmp` (is_dloc d1 `compare` is_dloc d2)
+ = (is_qual d1 `compare` is_qual d2) `thenCmp`
+ (best_item item1 item2) `thenCmp`
+ (is_dloc d1 `compare` is_dloc d2)
best_item :: ImpItemSpec -> ImpItemSpec -> Ordering
best_item ImpAll ImpAll = EQ
- best_item ImpAll (ImpSome {}) = GT
- best_item (ImpSome {}) ImpAll = LT
+ best_item ImpAll (ImpSome {}) = LT
+ best_item (ImpSome {}) ImpAll = GT
best_item (ImpSome { is_explicit = e1 })
- (ImpSome { is_explicit = e2 }) = e2 `compare` e1
- -- False < True, so if e1 is explicit and e2 is not, we get LT
+ (ImpSome { is_explicit = e2 }) = e1 `compare` e2
+ -- False < True, so if e1 is explicit and e2 is not, we get GT
+
+{- Note [Choosing the best import declaration]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When reporting unused import declarations we use the following rules.
+ (see [wiki:Commentary/Compiler/UnusedImports])
+
+Say that an import-item is either
+ * an entire import-all decl (eg import Foo), or
+ * a particular item in an import list (eg import Foo( ..., x, ...)).
+The general idea is that for each /occurrence/ of an imported name, we will
+attribute that use to one import-item. Once we have processed all the
+occurrences, any import items with no uses attributed to them are unused, and are
+warned about. More precisely:
+
+1. For every RdrName in the program text, find its GlobalRdrElt.
+
+2. Then, from the [ImportSpec] (gre_imp) of that GRE, choose one
+ the "chosen import-item", and mark it "used". This is done
+ by 'bestImport'
+
+3. After processing all the RdrNames, bleat about any
+ import-items that are unused.
+ This is done in RnNames.warnUnusedImportDecls.
+
+The function 'bestImport' returns the dominant import among the
+ImportSpecs it is given, implementing Step 2. We say import-item A
+dominates import-item B if we choose A over B. In general, we try to
+choose the import that is most likely to render other imports
+unnecessary. Here is the dominance relationship we choose:
+
+ a) import Foo dominates import qualified Foo.
+
+ b) import Foo dominates import Foo(x).
+
+ c) Otherwise choose the textually first one.
+
+Rationale for (a). Consider
+ import qualified M -- Import #1
+ import M( x ) -- Import #2
+ foo = M.x + x
+
+The unqualified 'x' can only come from import #2. The qualified 'M.x'
+could come from either, but bestImport picks import #2, because it is
+more likely to be useful in other imports, as indeed it is in this
+case (see Trac #5211 for a concrete example).
+
+But the rules are not perfect; consider
+ import qualified M -- Import #1
+ import M( x ) -- Import #2
+ foo = M.x + M.y
+
+The M.x will use import #2, but M.y can only use import #1.
+-}
+
unQualSpecOK :: ImportSpec -> Bool
-- ^ Is in scope unqualified?