- warn name (red_imp, cov_imp)
- = addWarnAt (importSpecLoc red_imp)
- (vcat [ptext SLIT("Redundant import of:") <+> quotes pp_name,
- ptext SLIT("It is also") <+> ppr cov_imp])
- where
- pp_name | is_qual red_decl = ppr (is_as red_decl) <> dot <> ppr occ
- | otherwise = ppr occ
- occ = nameOccName name
- red_decl = is_decl red_imp
-
- redundants :: [ImportSpec] -> [(ImportSpec,ImportSpec)]
- -- The returned pair is (redundant-import, covering-import)
- redundants imps
- = [ (red_imp, cov_imp)
- | red_imp <- imps
- , cov_imp <- take 1 (filter (covers red_imp) imps) ]
-
- -- "red_imp" is a putative redundant import
- -- "cov_imp" potentially covers it
- -- This test decides whether red_imp could be dropped
- --
- -- NOTE: currently the test does not warn about
- -- import M( x )
- -- imoprt N( x )
- -- even if the same underlying 'x' is involved, because dropping
- -- either import would change the qualified names in scope (M.x, N.x)
- -- But if the qualified names aren't used, the import is indeed redundant
- -- Sadly we don't know that. Oh well.
- covers red_imp@(ImpSpec { is_decl = red_decl, is_item = red_item })
- cov_imp@(ImpSpec { is_decl = cov_decl, is_item = cov_item })
- | red_loc == cov_loc
- = False -- Ignore diagonal elements
- | not (is_as red_decl == is_as cov_decl)
- = False -- They bring into scope different qualified names
- | not (is_qual red_decl) && is_qual cov_decl
- = False -- Covering one doesn't bring unqualified name into scope
- | red_selective
- = not cov_selective -- Redundant one is selective and covering one isn't
- || red_later -- Both are explicit; tie-break using red_later
- | otherwise
- = not cov_selective -- Neither import is selective
- && (is_mod red_decl == is_mod cov_decl) -- They import the same module
- && red_later -- Tie-break
- where
- red_loc = importSpecLoc red_imp
- cov_loc = importSpecLoc cov_imp
- red_later = red_loc > cov_loc
- cov_selective = selectiveImpItem cov_item
- red_selective = selectiveImpItem red_item
-
-selectiveImpItem :: ImpItemSpec -> Bool
-selectiveImpItem ImpAll = False
-selectiveImpItem (ImpSome {}) = True
-
--- ToDo: deal with original imports with 'qualified' and 'as M' clauses
-printMinimalImports :: FiniteMap ModuleName AvailEnv -- Minimal imports
- -> RnM ()
-printMinimalImports imps
- = ifOptM Opt_D_dump_minimal_imports $ do {
-
- mod_ies <- mappM to_ies (fmToList imps) ;
- this_mod <- getModule ;
- rdr_env <- getGlobalRdrEnv ;
- ioToTcRn (do { h <- openFile (mkFilename this_mod) WriteMode ;
- printForUser h (mkPrintUnqualified rdr_env)
- (vcat (map ppr_mod_ie mod_ies)) })
- }
+ import_usage :: ImportMap
+ import_usage = foldr (addUsedRdrName rdr_env) Map.empty rdrs
+
+ unused_decl decl@(L loc (ImportDecl { ideclHiding = imps }))
+ = (decl, nubAvails used_avails, unused_imps)
+ where
+ used_avails = Map.lookup (srcSpanStart loc) import_usage `orElse` []
+ dont_report_as_unused = foldr add emptyNameSet used_avails
+ add (Avail n) s = s `addOneToNameSet` n
+ add (AvailTC n ns) s = s `addListToNameSet` (n:ns)
+ -- If you use 'signum' from Num, then the user may well have
+ -- imported Num(signum). We don't want to complain that
+ -- Num is not itself mentioned. Hence adding 'n' as
+ -- well to the list of of "don't report if unused" names
+
+ unused_imps = case imps of
+ Just (False, imp_ies) -> nameSetToList unused_imps
+ where
+ imp_names = mkNameSet (concatMap (ieNames . unLoc) imp_ies)
+ unused_imps = imp_names `minusNameSet` dont_report_as_unused
+
+ _other -> [] -- No explicit import list => no unused-name list
+
+addUsedRdrName :: GlobalRdrEnv -> RdrName -> ImportMap -> ImportMap
+-- For a used RdrName, find all the import decls that brought
+-- it into scope; choose one of them (bestImport), and record
+-- the RdrName in that import decl's entry in the ImportMap
+addUsedRdrName rdr_env rdr imp_map
+ | [gre] <- lookupGRE_RdrName rdr rdr_env
+ , Imported imps <- gre_prov gre
+ = add_imp gre (bestImport imps) imp_map
+ | otherwise
+ = imp_map
+ where
+ add_imp :: GlobalRdrElt -> ImportSpec -> ImportMap -> ImportMap
+ add_imp gre (ImpSpec { is_decl = imp_decl_spec }) imp_map
+ = Map.insertWith add decl_loc [avail] imp_map
+ where
+ add _ avails = avail : avails -- add is really just a specialised (++)
+ decl_loc = srcSpanStart (is_dloc imp_decl_spec)
+ name = gre_name gre
+ avail = case gre_par gre of
+ ParentIs p -> AvailTC p [name]
+ NoParent | isTyConName name -> AvailTC name [name]
+ | otherwise -> Avail name
+
+ bestImport :: [ImportSpec] -> ImportSpec
+ bestImport iss
+ = case partition isImpAll iss of
+ ([], imp_somes) -> textuallyFirst imp_somes
+ (imp_alls, _) -> textuallyFirst imp_alls
+
+ textuallyFirst :: [ImportSpec] -> ImportSpec
+ textuallyFirst iss = case sortWith (is_dloc . is_decl) iss of
+ [] -> pprPanic "textuallyFirst" (ppr iss)
+ (is:_) -> is
+
+ isImpAll :: ImportSpec -> Bool
+ isImpAll (ImpSpec { is_item = ImpAll }) = True
+ isImpAll _other = False
+\end{code}
+
+\begin{code}
+warnUnusedImport :: ImportDeclUsage -> RnM ()
+warnUnusedImport (L loc decl, used, unused)
+ | Just (False,[]) <- ideclHiding decl
+ = return () -- Do not warn for 'import M()'
+ | null used = addWarnAt loc msg1 -- Nothing used; drop entire decl
+ | null unused = return () -- Everything imported is used; nop
+ | otherwise = addWarnAt loc msg2 -- Some imports are unused
+ where
+ msg1 = vcat [pp_herald <+> quotes pp_mod <+> pp_not_used,
+ nest 2 (ptext (sLit "except perhaps to import instances from")
+ <+> quotes pp_mod),
+ ptext (sLit "To import instances alone, use:")
+ <+> ptext (sLit "import") <+> pp_mod <> parens empty ]
+ msg2 = sep [pp_herald <+> quotes (pprWithCommas ppr unused),
+ text "from module" <+> quotes pp_mod <+> pp_not_used]
+ pp_herald = text "The import of"
+ pp_mod = ppr (unLoc (ideclName decl))
+ pp_not_used = text "is redundant"
+\end{code}
+
+To print the minimal imports we walk over the user-supplied import
+decls, and simply trim their import lists. NB that
+
+ * We do *not* change the 'qualified' or 'as' parts!
+
+ * We do not disard a decl altogether; we might need instances
+ from it. Instead we just trim to an empty import list
+
+\begin{code}
+printMinimalImports :: [ImportDeclUsage] -> RnM ()
+printMinimalImports imports_w_usage
+ = do { imports' <- mapM mk_minimal imports_w_usage
+ ; this_mod <- getModule
+ ; liftIO $
+ do { h <- openFile (mkFilename this_mod) WriteMode
+ ; printForUser h neverQualify (vcat (map ppr imports')) }
+ -- The neverQualify is important. We are printing Names
+ -- but they are in the context of an 'import' decl, and
+ -- we never qualify things inside there
+ -- E.g. import Blag( f, b )
+ -- not import Blag( Blag.f, Blag.g )!
+ }