Skip to content

Commit e760b31

Browse files
authored
Fix bug with postqualified imports and qualifiedStyle=unrestricted (#1498)
1 parent 2cb667e commit e760b31

File tree

3 files changed

+87
-11
lines changed

3 files changed

+87
-11
lines changed

Diff for: data/import_style.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
- {name: HypotheticalModule3, importStyle: qualified}
55
- {name: 'HypotheticalModule3.*', importStyle: unqualified}
66
- {name: 'HypotheticalModule3.OtherSubModule', importStyle: unrestricted, qualifiedStyle: post}
7+
- {name: HypotheticalModule4, importStyle: qualified, as: HM4, asRequired: true}
8+
- {name: HypotheticalModule5, importStyle: qualified, qualifiedStyle: post}

Diff for: src/Hint/Restrict.hs

+24-11
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import Data.Set qualified as Set
3131
import Data.Map qualified as Map
3232
import Data.List.Extra
3333
import Data.List.NonEmpty (nonEmpty)
34+
import Data.Either
3435
import Data.Maybe
3536
import Data.Monoid
3637
import Data.Semigroup
@@ -157,6 +158,11 @@ checkPragmas modu flags exts mps =
157158
, not $ null bad]
158159
isGood def mp x = maybe def (within modu "" . riWithin) $ Map.lookup x mp
159160

161+
162+
-- | Extension to GHC's 'ImportDeclQualifiedStyle', expressing @qualifiedStyle: unrestricted@,
163+
-- i.e. the preference of "either pre- or post-, but qualified" in a rule.
164+
data QualifiedPostOrPre = QualifiedPostOrPre deriving Eq
165+
160166
checkImports :: String -> [LImportDecl GhcPs] -> (Bool, Map.Map String RestrictItem) -> [Idea]
161167
checkImports modu lImportDecls (def, mp) = mapMaybe getImportHint lImportDecls
162168
where
@@ -190,30 +196,37 @@ checkImports modu lImportDecls (def, mp) = mapMaybe getImportHint lImportDecls
190196
case fromMaybe ImportStyleUnrestricted $ getAlt riImportStyle of
191197
ImportStyleUnrestricted
192198
| NotQualified <- ideclQualified -> (Nothing, Nothing)
193-
| otherwise -> (second (<> " or unqualified") <$> expectedQualStyle, Nothing)
194-
ImportStyleQualified -> (expectedQualStyleDef, Nothing)
199+
| otherwise -> (Just $ second (<> " or unqualified") expectedQualStyle, Nothing)
200+
ImportStyleQualified -> (Just expectedQualStyle, Nothing)
195201
ImportStyleExplicitOrQualified
196202
| Just (False, _) <- first (== EverythingBut) <$> ideclImportList -> (Nothing, Nothing)
197203
| otherwise ->
198-
( second (<> " or with an explicit import list") <$> expectedQualStyleDef
204+
( Just $ second (<> " or with an explicit import list") expectedQualStyle
199205
, Nothing )
200206
ImportStyleExplicit
201207
| Just (False, _) <- first (== EverythingBut) <$> ideclImportList -> (Nothing, Nothing)
202208
| otherwise ->
203-
( Just (NotQualified, "unqualified")
209+
( Just (Right NotQualified, "unqualified")
204210
, Just $ Just (Exactly, noLocA []) )
205-
ImportStyleUnqualified -> (Just (NotQualified, "unqualified"), Nothing)
206-
expectedQualStyleDef = expectedQualStyle <|> Just (QualifiedPre, "qualified")
211+
ImportStyleUnqualified -> (Just (Right NotQualified, "unqualified"), Nothing)
207212
expectedQualStyle =
208213
case fromMaybe QualifiedStyleUnrestricted $ getAlt riQualifiedStyle of
209-
QualifiedStyleUnrestricted -> Nothing
210-
QualifiedStylePost -> Just (QualifiedPost, "post-qualified")
211-
QualifiedStylePre -> Just (QualifiedPre, "pre-qualified")
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:
212219
qualIdea
213-
| Just ideclQualified == (fst <$> expectedQual) = Nothing
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)
214226
| otherwise = expectedQual
215227
whenJust qualIdea $ \(qual, hint) -> do
216-
let i' = noLoc $ (unLoc i){ ideclQualified = qual
228+
-- convert non-Nothing qualIdea into hlint's refactoring Idea
229+
let i' = noLoc $ (unLoc i){ ideclQualified = fromRight QualifiedPre qual
217230
, ideclImportList = fromMaybe ideclImportList expectedHiding }
218231
msg = moduleNameString (unLoc ideclName) <> " should be imported " <> hint
219232
Left $ warn msg (reLoc i) i' []

Diff for: tests/import_style.test

+61
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,64 @@ OUTPUT
6666
No hints
6767

6868
---------------------------------------------------------------------
69+
RUN tests/importStyle-postqual-pos.hs --hint=data/import_style.yaml -XImportQualifiedPost
70+
FILE tests/importStyle-postqual-pos.hs
71+
import HypotheticalModule1 qualified as HM1
72+
import HypotheticalModule2 qualified
73+
import HypotheticalModule2 qualified as Arbitrary
74+
import HypotheticalModule3 qualified
75+
import HypotheticalModule3 qualified as Arbitrary
76+
import HypotheticalModule4 qualified as HM4
77+
import HypotheticalModule5 qualified
78+
import HypotheticalModule5 qualified as HM5
79+
OUTPUT
80+
No hints
81+
82+
---------------------------------------------------------------------
83+
RUN tests/importStyle-postqual-neg.hs --hint=data/import_style.yaml -XImportQualifiedPost
84+
FILE tests/importStyle-postqual-neg.hs
85+
import HypotheticalModule1 qualified
86+
import qualified HypotheticalModule4
87+
import qualified HypotheticalModule4 as Verbotten
88+
import qualified HypotheticalModule4 as HM4
89+
import HypotheticalModule5 as HM5
90+
import qualified HypotheticalModule5
91+
92+
OUTPUT
93+
tests/importStyle-postqual-neg.hs:1:1-36: Warning: Avoid restricted alias
94+
Found:
95+
import HypotheticalModule1 qualified
96+
Perhaps:
97+
import HypotheticalModule1 qualified as HM1
98+
Note: may break the code
99+
100+
tests/importStyle-postqual-neg.hs:2:1-36: Warning: Avoid restricted alias
101+
Found:
102+
import qualified HypotheticalModule4
103+
Perhaps:
104+
import qualified HypotheticalModule4 as HM4
105+
Note: may break the code
106+
107+
tests/importStyle-postqual-neg.hs:3:1-49: Warning: Avoid restricted alias
108+
Found:
109+
import qualified HypotheticalModule4 as Verbotten
110+
Perhaps:
111+
import qualified HypotheticalModule4 as HM4
112+
Note: may break the code
113+
114+
tests/importStyle-postqual-neg.hs:5:1-33: Warning: HypotheticalModule5 should be imported post-qualified
115+
Found:
116+
import HypotheticalModule5 as HM5
117+
Perhaps:
118+
import HypotheticalModule5 qualified as HM5
119+
Note: may break the code
120+
121+
tests/importStyle-postqual-neg.hs:6:1-36: Warning: HypotheticalModule5 should be imported post-qualified
122+
Found:
123+
import qualified HypotheticalModule5
124+
Perhaps:
125+
import HypotheticalModule5 qualified
126+
Note: may break the code
127+
128+
5 hints
129+
---------------------------------------------------------------------

0 commit comments

Comments
 (0)