@@ -164,88 +164,97 @@ checkPragmas modu flags exts mps =
164164data QualifiedPostOrPre = QualifiedPostOrPre deriving Eq
165165
166166checkImports :: String -> [LImportDecl GhcPs ] -> (Bool , Map. Map String RestrictItem ) -> [Idea ]
167- checkImports modu lImportDecls (def, mp) = mapMaybe getImportHint lImportDecls
167+ checkImports modu lImportDecls (def, mp) = concatMap getImportHint lImportDecls
168168 where
169- getImportHint :: LImportDecl GhcPs -> Maybe Idea
169+ getImportHint :: LImportDecl GhcPs -> [ Idea ]
170170 getImportHint i@ (L _ ImportDecl {.. }) = do
171- let RestrictItem {.. } = getRestrictItem def ideclName mp
172- either (Just . ideaMessage riMessage) (const Nothing ) $ do
173- unless (within modu " " riWithin) $
174- Left $ ideaNoTo $ warn " Avoid restricted module" (reLoc i) (reLoc i) []
175-
176- let importedIdents = Set. fromList $
177- case first (== EverythingBut ) <$> ideclImportList of
178- Just (False , lxs) -> concatMap (importListToIdents . unLoc) (unLoc lxs)
179- _ -> []
180- invalidIdents = case riRestrictIdents of
181- NoRestrictIdents -> Set. empty
182- ForbidIdents badIdents -> importedIdents `Set.intersection` Set. fromList badIdents
183- OnlyIdents onlyIdents -> importedIdents `Set.difference` Set. fromList onlyIdents
184- unless (Set. null invalidIdents) $
185- Left $ ideaNoTo $ warn " Avoid restricted identifiers" (reLoc i) (reLoc i) []
186-
187- let qualAllowed = case (riAs, ideclAs) of
188- ([] , _) -> True
189- (_, Nothing ) -> maybe True not $ getAlt riAsRequired
190- (_, Just (L _ modName)) -> moduleNameString modName `elem` riAs
191- unless qualAllowed $ do
192- let i' = noLoc $ (unLoc i){ ideclAs = noLocA . mkModuleName <$> listToMaybe riAs }
193- Left $ warn " Avoid restricted alias" (reLoc i) i' []
194-
195- let (expectedQual, expectedHiding) =
196- case fromMaybe ImportStyleUnrestricted $ getAlt riImportStyle of
197- ImportStyleUnrestricted
198- | NotQualified <- ideclQualified -> (Nothing , Nothing )
199- | otherwise -> (Just $ second (<> " or unqualified" ) expectedQualStyle, Nothing )
200- ImportStyleQualified -> (Just expectedQualStyle, Nothing )
201- ImportStyleExplicitOrQualified
202- | Just (False , _) <- first (== EverythingBut ) <$> ideclImportList -> (Nothing , Nothing )
203- | otherwise ->
204- ( Just $ second (<> " or with an explicit import list" ) expectedQualStyle
205- , Nothing )
206- ImportStyleExplicit
207- | Just (False , _) <- first (== EverythingBut ) <$> ideclImportList -> (Nothing , Nothing )
208- | otherwise ->
209- ( Just (Right NotQualified , " unqualified" )
210- , Just $ Just (Exactly , noLocA [] ) )
211- ImportStyleUnqualified -> (Just (Right NotQualified , " unqualified" ), Nothing )
212- expectedQualStyle =
213- case fromMaybe QualifiedStyleUnrestricted $ getAlt riQualifiedStyle of
214- QualifiedStyleUnrestricted -> (Left QualifiedPostOrPre , " qualified" )
215- QualifiedStylePost -> (Right QualifiedPost , " post-qualified" )
216- QualifiedStylePre -> (Right QualifiedPre , " pre-qualified" )
217- -- unless expectedQual is Nothing, it holds the Idea (hint) to ultimately emit,
218- -- except in these cases when the rule's requirements are fulfilled in-source:
219- qualIdea
220- -- the rule demands a particular importStyle, and the decl obeys exactly
221- | Just (Right ideclQualified) == (fst <$> expectedQual) = Nothing
222- -- the rule demands a QualifiedPostOrPre import, and the decl does either
223- | Just (Left QualifiedPostOrPre ) == (fst <$> expectedQual)
224- && ideclQualified `elem` [QualifiedPost , QualifiedPre ] = Nothing
225- -- otherwise, expectedQual gets converted into a warning below (or is Nothing)
226- | otherwise = expectedQual
227- whenJust qualIdea $ \ (qual, hint) -> do
228- -- convert non-Nothing qualIdea into hlint's refactoring Idea
229- let i' = noLoc $ (unLoc i){ ideclQualified = fromRight QualifiedPre qual
230- , ideclImportList = fromMaybe ideclImportList expectedHiding }
231- msg = moduleNameString (unLoc ideclName) <> " should be imported " <> hint
232- Left $ warn msg (reLoc i) i' []
233-
234- getRestrictItem :: Bool -> LocatedA ModuleName -> Map. Map String RestrictItem -> RestrictItem
235- getRestrictItem def ideclName =
236- fromMaybe (RestrictItem mempty mempty mempty mempty [(" " ," " ) | def] NoRestrictIdents Nothing )
237- . lookupRestrictItem ideclName
238-
239- lookupRestrictItem :: LocatedA ModuleName -> Map. Map String RestrictItem -> Maybe RestrictItem
171+ let restrictItems = getRestrictItem def ideclName mp
172+ flip mapMaybe restrictItems $ \ RestrictItem {.. } ->
173+ either (Just . ideaMessage riMessage) (const Nothing ) $ do
174+ unless (within modu " " riWithin) $
175+ Left $ ideaNoTo $ warn " Avoid restricted module" (reLoc i) (reLoc i) []
176+
177+ let importedIdents = Set. fromList $
178+ case first (== EverythingBut ) <$> ideclImportList of
179+ Just (False , lxs) -> concatMap (importListToIdents . unLoc) (unLoc lxs)
180+ _ -> []
181+ invalidIdents = case riRestrictIdents of
182+ NoRestrictIdents -> Set. empty
183+ ForbidIdents badIdents -> importedIdents `Set.intersection` Set. fromList badIdents
184+ OnlyIdents onlyIdents -> importedIdents `Set.difference` Set. fromList onlyIdents
185+ unless (Set. null invalidIdents) $
186+ Left $ ideaNoTo $ warn " Avoid restricted identifiers" (reLoc i) (reLoc i) []
187+
188+ let qualAllowed = case (riAs, ideclAs) of
189+ ([] , _) -> True
190+ (_, Nothing ) -> maybe True not $ getAlt riAsRequired
191+ (_, Just (L _ modName)) -> moduleNameString modName `elem` riAs
192+ unless qualAllowed $ do
193+ let i' = noLoc $ (unLoc i){ ideclAs = noLocA . mkModuleName <$> listToMaybe riAs }
194+ Left $ warn " Avoid restricted alias" (reLoc i) i' []
195+
196+ let (expectedQual, expectedHiding) =
197+ case fromMaybe ImportStyleUnrestricted $ getAlt riImportStyle of
198+ ImportStyleUnrestricted
199+ | NotQualified <- ideclQualified -> (Nothing , Nothing )
200+ | otherwise -> (Just $ second (<> " or unqualified" ) expectedQualStyle, Nothing )
201+ ImportStyleQualified -> (Just expectedQualStyle, Nothing )
202+ ImportStyleExplicitOrQualified
203+ | Just (False , _) <- first (== EverythingBut ) <$> ideclImportList -> (Nothing , Nothing )
204+ | otherwise ->
205+ ( Just $ second (<> " or with an explicit import list" ) expectedQualStyle
206+ , Nothing )
207+ ImportStyleExplicit
208+ | Just (False , _) <- first (== EverythingBut ) <$> ideclImportList -> (Nothing , Nothing )
209+ | otherwise ->
210+ ( Just (Right NotQualified , " unqualified" )
211+ , Just $ Just (Exactly , noLocA [] ) )
212+ ImportStyleUnqualified -> (Just (Right NotQualified , " unqualified" ), Nothing )
213+ expectedQualStyle =
214+ case fromMaybe QualifiedStyleUnrestricted $ getAlt riQualifiedStyle of
215+ QualifiedStyleUnrestricted -> (Left QualifiedPostOrPre , " qualified" )
216+ QualifiedStylePost -> (Right QualifiedPost , " post-qualified" )
217+ QualifiedStylePre -> (Right QualifiedPre , " pre-qualified" )
218+ -- unless expectedQual is Nothing, it holds the Idea (hint) to ultimately emit,
219+ -- except in these cases when the rule's requirements are fulfilled in-source:
220+ qualIdea
221+ -- the rule demands a particular importStyle, and the decl obeys exactly
222+ | Just (Right ideclQualified) == (fst <$> expectedQual) = Nothing
223+ -- the rule demands a QualifiedPostOrPre import, and the decl does either
224+ | Just (Left QualifiedPostOrPre ) == (fst <$> expectedQual)
225+ && ideclQualified `elem` [QualifiedPost , QualifiedPre ] = Nothing
226+ -- otherwise, expectedQual gets converted into a warning below (or is Nothing)
227+ | otherwise = expectedQual
228+ whenJust qualIdea $ \ (qual, hint) -> do
229+ -- convert non-Nothing qualIdea into hlint's refactoring Idea
230+ let i' = noLoc $ (unLoc i){ ideclQualified = fromRight QualifiedPre qual
231+ , ideclImportList = fromMaybe ideclImportList expectedHiding }
232+ msg = moduleNameString (unLoc ideclName) <> " should be imported " <> hint
233+ Left $ warn msg (reLoc i) i' []
234+
235+ getRestrictItem :: Bool -> LocatedA ModuleName -> Map. Map String RestrictItem -> [RestrictItem ]
236+ getRestrictItem def ideclName mp =
237+ case lookupRestrictItem ideclName mp of
238+ [] ->
239+ pure (RestrictItem mempty mempty mempty mempty [(" " ," " ) | def] NoRestrictIdents Nothing )
240+ restricts ->
241+ restricts
242+
243+ lookupRestrictItem :: LocatedA ModuleName -> Map. Map String RestrictItem -> [RestrictItem ]
240244lookupRestrictItem ideclName mp =
241245 let moduleName = moduleNameString $ unLoc ideclName
242- exact = Map. lookup moduleName mp
243- wildcard = nonEmpty
246+ mexact = Map. lookup moduleName mp
247+ wildcard = catMaybes . NonEmpty. toList . sequence . nonEmpty
244248 . fmap snd
245249 . reverse -- the hope is less specific matches will end up last, but it's not guaranteed
246250 . filter (liftA2 (&&) (elem ' *' ) (`wildcardMatch` moduleName) . fst )
247251 $ Map. toList mp
248- in exact <> sconcat (sequence wildcard)
252+ in
253+ case mexact of
254+ Nothing ->
255+ wildcard
256+ Just exact ->
257+ [sconcat (exact NonEmpty. :| wildcard)]
249258
250259importListToIdents :: IE GhcPs -> [String ]
251260importListToIdents =
0 commit comments