Skip to content

Commit ed37b61

Browse files
tscholakpepeiborra
andauthored
don't crash when an unused operator import ends in . (#2123)
* replace head with safer uncons * if empty, stay empty * add test for removal of operators ending with '.' * make remove-import-actions tests pass * add more tests and debug output for modifyBinding * implement reviewers suggestions * restore ghcide/bench/example/HLS Co-authored-by: Pepe Iborra <pepeiborra@gmail.com>
1 parent 2e0f6cd commit ed37b61

File tree

2 files changed

+47
-7
lines changed

2 files changed

+47
-7
lines changed

ghcide/src/Development/IDE/Plugin/CodeAction.hs

+8-7
Original file line numberDiff line numberDiff line change
@@ -1483,20 +1483,21 @@ rangesForBindingImport :: ImportDecl GhcPs -> String -> [Range]
14831483
rangesForBindingImport ImportDecl{ideclHiding = Just (False, L _ lies)} b =
14841484
concatMap (mapMaybe srcSpanToRange . rangesForBinding' b') lies
14851485
where
1486-
b' = modifyBinding b
1486+
b' = wrapOperatorInParens b
14871487
rangesForBindingImport _ _ = []
14881488

1489-
modifyBinding :: String -> String
1490-
modifyBinding = wrapOperatorInParens . unqualify
1491-
where
1492-
wrapOperatorInParens x = if isAlpha (head x) then x else "(" <> x <> ")"
1493-
unqualify x = snd $ breakOnEnd "." x
1489+
wrapOperatorInParens :: String -> String
1490+
wrapOperatorInParens x =
1491+
case uncons x of
1492+
Just (h, _t) -> if isAlpha h then x else "(" <> x <> ")"
1493+
Nothing -> mempty
14941494

14951495
smallerRangesForBindingExport :: [LIE GhcPs] -> String -> [Range]
14961496
smallerRangesForBindingExport lies b =
14971497
concatMap (mapMaybe srcSpanToRange . ranges') lies
14981498
where
1499-
b' = modifyBinding b
1499+
unqualify = snd . breakOnEnd "."
1500+
b' = wrapOperatorInParens . unqualify $ b
15001501
ranges' (L _ (IEThingWith _ thing _ inners labels))
15011502
| showSDocUnsafe (ppr thing) == b' = []
15021503
| otherwise =

ghcide/test/exe/Main.hs

+39
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,33 @@ removeImportTests = testGroup "remove import actions"
11501150
, "type T = K.Type"
11511151
]
11521152
liftIO $ expectedContentAfterAction @=? contentAfterAction
1153+
, testSession "remove unused operators whose name ends with '.'" $ do
1154+
let contentA = T.unlines
1155+
[ "module ModuleA where"
1156+
, "(@.) = 0 -- Must have an operator whose name ends with '.'"
1157+
, "a = 1 -- .. but also something else"
1158+
]
1159+
_docA <- createDoc "ModuleA.hs" "haskell" contentA
1160+
let contentB = T.unlines
1161+
[ "{-# OPTIONS_GHC -Wunused-imports #-}"
1162+
, "module ModuleB where"
1163+
, "import ModuleA (a, (@.))"
1164+
, "x = a -- Must use something from module A, but not (@.)"
1165+
]
1166+
docB <- createDoc "ModuleB.hs" "haskell" contentB
1167+
_ <- waitForDiagnostics
1168+
[InR action@CodeAction { _title = actionTitle }, _]
1169+
<- getCodeActions docB (Range (Position 2 0) (Position 2 5))
1170+
liftIO $ "Remove @. from import" @=? actionTitle
1171+
executeCodeAction action
1172+
contentAfterAction <- documentContents docB
1173+
let expectedContentAfterAction = T.unlines
1174+
[ "{-# OPTIONS_GHC -Wunused-imports #-}"
1175+
, "module ModuleB where"
1176+
, "import ModuleA (a)"
1177+
, "x = a -- Must use something from module A, but not (@.)"
1178+
]
1179+
liftIO $ expectedContentAfterAction @=? contentAfterAction
11531180
]
11541181

11551182
extendImportTests :: TestTree
@@ -3311,6 +3338,18 @@ removeExportTests = testGroup "remove export actions"
33113338
, "import qualified Data.List as M"
33123339
, "a :: ()"
33133340
, "a = ()"])
3341+
, testSession "qualified re-export ending in '.'" $ template
3342+
(T.unlines
3343+
[ "module A ((M.@.),a) where"
3344+
, "import qualified Data.List as M"
3345+
, "a :: ()"
3346+
, "a = ()"])
3347+
"Remove ‘M.@.’ from export"
3348+
(Just $ T.unlines
3349+
[ "module A (a) where"
3350+
, "import qualified Data.List as M"
3351+
, "a :: ()"
3352+
, "a = ()"])
33143353
, testSession "export module" $ template
33153354
(T.unlines
33163355
[ "module A (module B) where"

0 commit comments

Comments
 (0)