Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0a9b1cb

Browse files
jhrcekmergify[bot]
andauthored
Fix renaming data constructors with fields (resolves #2915, resolves #4083) (#4635)
* Prevent renaming record fields whenever record constructor is renamed * wip * WAP * Update stack yamls, add RecordWildcard test * Looks like RecordWildcards renaming is only broken on GHC 9.6 and 9.8 * Consolidate comment, undo whitespace changes --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 256f834 commit 0a9b1cb

11 files changed

+75
-15
lines changed

‎cabal.project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ packages:
88
./hls-test-utils
99

1010

11-
index-state: 2025-06-07T14:57:40Z
11+
index-state: 2025-06-16T09:44:13Z
1212

1313
tests: True
1414
test-show-details: direct

‎ghcide/ghcide.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ library
7575
, hashable
7676
, hie-bios ^>=0.15.0
7777
, hie-compat ^>=0.3.0.0
78-
, hiedb ^>= 0.6.0.2
78+
, hiedb ^>= 0.7.0.0
7979
, hls-graph == 2.11.0.0
8080
, hls-plugin-api == 2.11.0.0
8181
, implicit-hie >= 0.1.4.0 && < 0.1.5

‎haskell-language-server.cabal

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ library hls-call-hierarchy-plugin
407407
, containers
408408
, extra
409409
, ghcide == 2.11.0.0
410-
, hiedb ^>= 0.6.0.2
410+
, hiedb ^>= 0.7.0.0
411411
, hls-plugin-api == 2.11.0.0
412412
, lens
413413
, lsp >=2.7
@@ -594,7 +594,7 @@ library hls-rename-plugin
594594
, containers
595595
, ghcide == 2.11.0.0
596596
, hashable
597-
, hiedb ^>= 0.6.0.2
597+
, hiedb ^>= 0.7.0.0
598598
, hie-compat
599599
, hls-plugin-api == 2.11.0.0
600600
, haskell-language-server:hls-refactor-plugin

‎plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import Data.List.NonEmpty (NonEmpty ((:|)),
2525
import qualified Data.Map as M
2626
import Data.Maybe
2727
import Data.Mod.Word
28-
import qualified Data.Set as S
2928
import qualified Data.Text as T
3029
import Development.IDE (Recorder, WithPriority,
3130
usePropertyAction)
@@ -42,7 +41,9 @@ import qualified Development.IDE.GHC.ExactPrint as E
4241
import Development.IDE.Plugin.CodeAction
4342
import Development.IDE.Spans.AtPoint
4443
import Development.IDE.Types.Location
44+
import HieDb ((:.) (..))
4545
import HieDb.Query
46+
import HieDb.Types (RefRow (refIsGenerated))
4647
import Ide.Plugin.Error
4748
import Ide.Plugin.Properties
4849
import Ide.PluginUtils
@@ -196,6 +197,8 @@ refsAtName state nfp name = do
196197
dbRefs <- case nameModule_maybe name of
197198
Nothing -> pure []
198199
Just mod -> liftIO $ mapMaybe rowToLoc <$> withHieDb (\hieDb ->
200+
-- See Note [Generated references]
201+
filter (\(refRow HieDb.:. _) -> refIsGenerated refRow) <$>
199202
findReferences
200203
hieDb
201204
True
@@ -230,15 +233,29 @@ handleGetHieAst state nfp =
230233
-- which is bad (see https://github.com/haskell/haskell-language-server/issues/3799)
231234
fmap removeGenerated $ runActionE "Rename.GetHieAst" state $ useE GetHieAst nfp
232235

233-
-- | We don't want to rename in code generated by GHC as this gives false positives.
234-
-- So we restrict the HIE file to remove all the generated code.
236+
{- Note [Generated references]
237+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238+
GHC inserts `Use`s of record constructor everywhere where its record selectors are used,
239+
which leads to record fields being renamed whenever corresponding constructor is renamed.
240+
see https://github.com/haskell/haskell-language-server/issues/2915
241+
To work around this, we filter out compiler-generated references.
242+
-}
235243
removeGenerated :: HieAstResult -> HieAstResult
236-
removeGenerated HAR{..} = HAR{hieAst = go hieAst,..}
244+
removeGenerated HAR{..} =
245+
HAR{hieAst = sourceOnlyAsts, refMap = sourceOnlyRefMap, ..}
237246
where
238-
go :: HieASTs a -> HieASTs a
239-
go hf =
240-
HieASTs (fmap goAst (getAsts hf))
241-
goAst (Node nsi sp xs) = Node (SourcedNodeInfo $ M.restrictKeys (getSourcedNodeInfo nsi) (S.singleton SourceInfo)) sp (map goAst xs)
247+
goAsts :: HieASTs a -> HieASTs a
248+
goAsts (HieASTs asts) = HieASTs (fmap goAst asts)
249+
250+
goAst :: HieAST a -> HieAST a
251+
goAst (Node (SourcedNodeInfo sniMap) sp children) =
252+
let sourceOnlyNodeInfos = SourcedNodeInfo $ M.delete GeneratedInfo sniMap
253+
in Node sourceOnlyNodeInfos sp $ map goAst children
254+
255+
sourceOnlyAsts = goAsts hieAst
256+
-- Also need to regenerate the RefMap, because the one in HAR
257+
-- is generated from HieASTs containing GeneratedInfo
258+
sourceOnlyRefMap = generateReferencesMap $ getAsts sourceOnlyAsts
242259

243260
collectWith :: (Hashable a, Eq b) => (a -> b) -> HashSet a -> [(b, HashSet a)]
244261
collectWith f = map (\(a :| as) -> (f a, HS.fromList (a:as))) . groupWith f . HS.toList

‎plugins/hls-rename-plugin/test/Main.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ tests :: TestTree
2424
tests = testGroup "Rename"
2525
[ goldenWithRename "Data constructor" "DataConstructor" $ \doc ->
2626
rename doc (Position 0 15) "Op"
27+
, goldenWithRename "Data constructor with fields" "DataConstructorWithFields" $ \doc ->
28+
rename doc (Position 1 13) "FooRenamed"
29+
, knownBrokenForGhcVersions [GHC96, GHC98] "renaming Constructor{..} with RecordWildcard removes .." $
30+
goldenWithRename "Data constructor with fields" "DataConstructorWithFieldsRecordWildcards" $ \doc ->
31+
rename doc (Position 1 13) "FooRenamed"
2732
, goldenWithRename "Exported function" "ExportedFunction" $ \doc ->
2833
rename doc (Position 2 1) "quux"
2934
, goldenWithRename "Field Puns" "FieldPuns" $ \doc ->
@@ -113,7 +118,7 @@ goldenWithRename title path act =
113118
goldenWithHaskellDoc (def { plugins = M.fromList [("rename", def { plcConfig = "crossModule" .= True })] })
114119
renamePlugin title testDataDir path "expected" "hs" act
115120

116-
renameExpectError :: (TResponseError Method_TextDocumentRename) -> TextDocumentIdentifier -> Position -> Text -> Session ()
121+
renameExpectError :: TResponseError Method_TextDocumentRename -> TextDocumentIdentifier -> Position -> Text -> Session ()
117122
renameExpectError expectedError doc pos newName = do
118123
let params = RenameParams Nothing doc pos newName
119124
rsp <- request SMethod_TextDocumentRename params
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{-# LANGUAGE NamedFieldPuns #-}
2+
data Foo = FooRenamed { a :: Int, b :: Bool }
3+
4+
foo1 :: Foo
5+
foo1 = FooRenamed { a = 1, b = True }
6+
7+
foo2 :: Foo
8+
foo2 = FooRenamed 1 True
9+
10+
fun1 :: Foo -> Int
11+
fun1 FooRenamed {a} = a
12+
13+
fun2 :: Foo -> Int
14+
fun2 FooRenamed {a = i} = i
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{-# LANGUAGE NamedFieldPuns #-}
2+
data Foo = Foo { a :: Int, b :: Bool }
3+
4+
foo1 :: Foo
5+
foo1 = Foo { a = 1, b = True }
6+
7+
foo2 :: Foo
8+
foo2 = Foo 1 True
9+
10+
fun1 :: Foo -> Int
11+
fun1 Foo {a} = a
12+
13+
fun2 :: Foo -> Int
14+
fun2 Foo {a = i} = i
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{-# LANGUAGE RecordWildCards #-}
2+
data Foo = FooRenamed { a :: Int, b :: Bool }
3+
4+
fun :: Foo -> Int
5+
fun FooRenamed {..} = a
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{-# LANGUAGE RecordWildCards #-}
2+
data Foo = Foo { a :: Int, b :: Bool }
3+
4+
fun :: Foo -> Int
5+
fun Foo {..} = a

‎stack-lts22.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ allow-newer-deps:
2121
extra-deps:
2222
- Diff-0.5
2323
- floskell-0.11.1
24-
- hiedb-0.6.0.2
24+
- hiedb-0.7.0.0
2525
- hie-bios-0.15.0
2626
- implicit-hie-0.1.4.0
2727
- lsp-2.7.0.0

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /