@@ -8,7 +8,7 @@ import Prelude.Compat
9import qualified Data.Map as M
9import qualified Data.Map as M
10import Data.Maybe (mapMaybe)
10import Data.Maybe (mapMaybe)
11−import Data.List ((\\), find)
11+import Data.List ((\\), find, intersect)
12import Control.Monad.Error.Class (MonadError(..))
12import Control.Monad.Error.Class (MonadError(..))
13import Control.Monad.Writer.Class
13import Control.Monad.Writer.Class
14import Control.Monad(unless,when)
14import Control.Monad(unless,when)
@@ -40,8 +40,10 @@ findUnusedImports (Module _ _ _ mdecls mexports) env usedImps = do
40 forM_ (M.toAscList imps) $ \(mni, decls) -> unless (mni `elem` alwaysUsedModules) $
40 forM_ (M.toAscList imps) $ \(mni, decls) -> unless (mni `elem` alwaysUsedModules) $
41 forM_ decls $ \(ss, declType, qualifierName) ->
41 forM_ decls $ \(ss, declType, qualifierName) ->
42 censor (onErrorMessages $ addModuleLocError ss) $ unless (qnameUsed qualifierName) $
42 censor (onErrorMessages $ addModuleLocError ss) $ unless (qnameUsed qualifierName) $
43− let usedNames = mapMaybe (matchName (typeForDCtor mni) qualifierName) $ sugarNames mni ++ M.findWithDefault [] mni usedImps in
43+ let names = sugarNames mni ++ M.findWithDefault [] mni usedImps
44+ usedNames = mapMaybe (matchName (typeForDCtor mni) qualifierName) names
45+ usedDctors = mapMaybe (matchDctor qualifierName) names
44− case declType of
46+ in case declType of
45 Implicit -> when (null usedNames) $ tell $ errorMessage $ UnusedImport mni
47 Implicit -> when (null usedNames) $ tell $ errorMessage $ UnusedImport mni
46 Explicit declrefs -> do
48 Explicit declrefs -> do
47 let idents = mapMaybe runDeclRef declrefs
49 let idents = mapMaybe runDeclRef declrefs
@@ -50,6 +52,17 @@ findUnusedImports (Module _ _ _ mdecls mexports) env usedImps = do
50 (0, _) -> return ()
52 (0, _) -> return ()
51 (n, m) | n == m -> tell $ errorMessage $ UnusedImport mni
53 (n, m) | n == m -> tell $ errorMessage $ UnusedImport mni
52 _ -> tell $ errorMessage $ UnusedExplicitImport mni diff
54 _ -> tell $ errorMessage $ UnusedExplicitImport mni diff
56+ -- If we've not already warned a type is unused, check its data constructors
57+ forM_ (mapMaybe getTypeRef declrefs) $ \(tn, c) -> do
58+ let allCtors = dctorsForType mni tn
59+ when (runProperName tn `elem` usedNames) $ case (c, null $ usedDctors `intersect` allCtors) of
60+ (_, True) -> tell $ errorMessage $ UnusedDctorImport tn
61+ (Just ctors, False) -> let ddiff = ctors \\ usedDctors
62+ in unless (null ddiff) $ tell $ errorMessage $ UnusedDctorExplicitImport tn ddiff
53 _ -> return ()
66 _ -> return ()
55 sugarNames :: ModuleName -> [ Name ]
68 sugarNames :: ModuleName -> [ Name ]
@@ -67,14 +80,23 @@ findUnusedImports (Module _ _ _ mdecls mexports) env usedImps = do
67 qnameUsed (Just qn) = qn `elem` alwaysUsedModules
80 qnameUsed (Just qn) = qn `elem` alwaysUsedModules
68 qnameUsed Nothing = False
81 qnameUsed Nothing = False
83+ dtys :: ModuleName -> [((ProperName, [ProperName]), ModuleName)]
84+ dtys mn = maybe [] exportedTypes $ envModuleExports <$> mn `M.lookup` env
86+ dctorsForType :: ModuleName -> ProperName -> [ProperName]
87+ dctorsForType mn tn =
88+ maybe [] getDctors (find matches $ dtys mn)
90+ matches ((ty, _),_) = ty == tn
91+ getDctors ((_,ctors),_) = ctors
70 typeForDCtor :: ModuleName -> ProperName -> Maybe ProperName
93 typeForDCtor :: ModuleName -> ProperName -> Maybe ProperName
71 typeForDCtor mn pn =
94 typeForDCtor mn pn =
72− getTy <$> find matches tys
95+ getTy <$> find matches (dtys mn)
74 matches ((_, ctors), _) = pn `elem` ctors
97 matches ((_, ctors), _) = pn `elem` ctors
75 getTy ((ty, _), _) = ty
98 getTy ((ty, _), _) = ty
76− tys :: [((ProperName, [ProperName]), ModuleName)]
99+
77− tys = maybe [] exportedTypes $ envModuleExports <$> mn `M.lookup` env
79matchName :: (ProperName -> Maybe ProperName) -> Maybe ModuleName -> Name -> Maybe String
101matchName :: (ProperName -> Maybe ProperName) -> Maybe ModuleName -> Name -> Maybe String
80matchName _ qual (IdentName (Qualified q x)) | q == qual = Just $ showIdent x
102matchName _ qual (IdentName (Qualified q x)) | q == qual = Just $ showIdent x
@@ -82,12 +104,21 @@ matchName _ qual (IsProperName (Qualified q x)) | q == qual = Just $ runProperNa
82matchName lookupDc qual (DctorName (Qualified q x)) | q == qual = runProperName <$> lookupDc x
104matchName lookupDc qual (DctorName (Qualified q x)) | q == qual = runProperName <$> lookupDc x
83matchName _ _ _ = Nothing
105matchName _ _ _ = Nothing
107+matchDctor :: Maybe ModuleName -> Name -> Maybe ProperName
108+matchDctor qual (DctorName (Qualified q x)) | q == qual = Just x
109+matchDctor _ _ = Nothing
85runDeclRef :: DeclarationRef -> Maybe String
111runDeclRef :: DeclarationRef -> Maybe String
86runDeclRef (PositionedDeclarationRef _ _ ref) = runDeclRef ref
112runDeclRef (PositionedDeclarationRef _ _ ref) = runDeclRef ref
87runDeclRef (ValueRef ident) = Just $ showIdent ident
113runDeclRef (ValueRef ident) = Just $ showIdent ident
88runDeclRef (TypeRef pn _) = Just $ runProperName pn
114runDeclRef (TypeRef pn _) = Just $ runProperName pn
89runDeclRef _ = Nothing
115runDeclRef _ = Nothing
117+getTypeRef :: DeclarationRef -> Maybe (ProperName, Maybe [ProperName])
118+getTypeRef (PositionedDeclarationRef _ _ ref) = getTypeRef ref
119+getTypeRef (TypeRef pn x) = Just (pn, x)
120+getTypeRef _ = Nothing
91addModuleLocError :: Maybe SourceSpan -> ErrorMessage -> ErrorMessage
122addModuleLocError :: Maybe SourceSpan -> ErrorMessage -> ErrorMessage
92addModuleLocError sp err =
123addModuleLocError sp err =
93 case sp of
124 case sp of