Skip to content

Commit 1316708

Browse files
committed
getRestrictItem returns a lits of hints
Only sconcat if an exact match is found
1 parent 55adfee commit 1316708

File tree

1 file changed

+83
-74
lines changed

1 file changed

+83
-74
lines changed

src/Hint/Restrict.hs

Lines changed: 83 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -164,88 +164,97 @@ checkPragmas modu flags exts mps =
164164
data QualifiedPostOrPre = QualifiedPostOrPre deriving Eq
165165

166166
checkImports :: 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]
240244
lookupRestrictItem 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

250259
importListToIdents :: IE GhcPs -> [String]
251260
importListToIdents =

0 commit comments

Comments
 (0)