Skip to content

Commit 098bff0

Browse files
authored
Merge branch 'master' into structured-diagnostics
2 parents 90eb271 + 5d221b9 commit 098bff0

File tree

61 files changed

+1113
-322
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1113
-322
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-05-12T13:26:29Z
11+
index-state: 2025-06-16T09:44:13Z
1212

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

docs/support/plugin-support.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ For example, a plugin to provide a formatter which has itself been abandoned has
5555
| `hls-explicit-record-fields-plugin` | 2 | |
5656
| `hls-fourmolu-plugin` | 2 | |
5757
| `hls-gadt-plugin` | 2 | |
58-
| `hls-hlint-plugin` | 2 | 9.10.1 |
58+
| `hls-hlint-plugin` | 2 | |
5959
| `hls-module-name-plugin` | 2 | |
6060
| `hls-notes-plugin` | 2 | |
6161
| `hls-qualify-imported-names-plugin` | 2 | |

flake.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
description = "haskell-language-server development flake";
33

44
inputs = {
5-
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
5+
# Don't use nixpkgs-unstable as aarch64-darwin is currently broken there.
6+
# Check again, when https://github.com/NixOS/nixpkgs/pull/414242 is resolved.
7+
nixpkgs.url = "github:NixOS/nixpkgs/c742ae7908a82c9bf23ce27bfca92a00e9bcd541";
68
flake-utils.url = "github:numtide/flake-utils";
79
# For default.nix
810
flake-compat = {
@@ -66,6 +68,7 @@
6668
buildInputs = [
6769
# Compiler toolchain
6870
hpkgs.ghc
71+
hpkgs.haskell-language-server
6972
pkgs.haskellPackages.cabal-install
7073
# Dependencies needed to build some parts of Hackage
7174
gmp zlib ncurses

ghcide-test/data/references/Fields.hs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{-# LANGUAGE RecordWildCards #-}
2+
module Fields where
3+
4+
data Foo = MkFoo
5+
{
6+
barr :: String,
7+
bazz :: String
8+
}
9+
10+
fooUse0 :: Foo -> String
11+
fooUse0 MkFoo{barr} = "5"
12+
13+
fooUse1 :: Foo -> String
14+
fooUse1 MkFoo{..} = "6"
15+
16+
fooUse2 :: String -> String -> Foo
17+
fooUse2 bar baz =
18+
MkFoo{..}

ghcide-test/data/references/Main.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Main where
22

33
import References
4-
4+
import Fields
55
main :: IO ()
66
main = return ()
77

@@ -12,3 +12,6 @@ b = a + 1
1212

1313
acc :: Account
1414
acc = Savings
15+
16+
fooUse3 :: String -> String -> Foo
17+
fooUse3 bar baz = MkFoo{barr = bar, bazz = baz}

ghcide-test/data/references/hie.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
cradle: {direct: {arguments: ["Main","OtherModule","OtherOtherModule","References"]}}
1+
cradle: {direct: {arguments: ["Main","OtherModule","OtherOtherModule","References", "Fields"]}}

ghcide-test/exe/CompletionTests.hs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import Test.Hls.Util
3333
import Test.Tasty
3434
import Test.Tasty.HUnit
3535

36-
3736
tests :: TestTree
3837
tests
3938
= testGroup "completion"
@@ -61,6 +60,7 @@ completionTest :: HasCallStack => String -> [T.Text] -> Position -> [(T.Text, Co
6160
completionTest name src pos expected = testSessionSingleFile name "A.hs" (T.unlines src) $ do
6261
docId <- openDoc "A.hs" "haskell"
6362
_ <- waitForDiagnostics
63+
6464
compls <- getAndResolveCompletions docId pos
6565
let compls' = [ (_label, _kind, _insertText, _additionalTextEdits) | CompletionItem{..} <- compls]
6666
let emptyToMaybe x = if T.null x then Nothing else Just x
@@ -211,7 +211,38 @@ localCompletionTests = [
211211

212212
compls <- getCompletions doc (Position 0 15)
213213
liftIO $ filter ("AAA" `T.isPrefixOf`) (mapMaybe _insertText compls) @?= ["AAAAA"]
214-
pure ()
214+
pure (),
215+
completionTest
216+
"polymorphic record dot completion"
217+
[ "{-# LANGUAGE OverloadedRecordDot #-}"
218+
, "module A () where"
219+
, "data Record = Record"
220+
, " { field1 :: Int"
221+
, " , field2 :: Int"
222+
, " }"
223+
, -- Without the following, this file doesn't trigger any diagnostics, so completionTest waits forever
224+
"triggerDiag :: UnknownType"
225+
, "foo record = record.f"
226+
]
227+
(Position 7 21)
228+
[("field1", CompletionItemKind_Function, "field1", True, False, Nothing)
229+
,("field2", CompletionItemKind_Function, "field2", True, False, Nothing)
230+
],
231+
completionTest
232+
"qualified polymorphic record dot completion"
233+
[ "{-# LANGUAGE OverloadedRecordDot #-}"
234+
, "module A () where"
235+
, "data Record = Record"
236+
, " { field1 :: Int"
237+
, " , field2 :: Int"
238+
, " }"
239+
, "someValue = undefined"
240+
, "foo = A.someValue.f"
241+
]
242+
(Position 7 19)
243+
[("field1", CompletionItemKind_Function, "field1", True, False, Nothing)
244+
,("field2", CompletionItemKind_Function, "field2", True, False, Nothing)
245+
]
215246
]
216247

217248
nonLocalCompletionTests :: [TestTree]

ghcide-test/exe/Main.hs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ module Main (main) where
3333
import qualified HieDbRetry
3434
import Test.Tasty
3535
import Test.Tasty.Ingredients.Rerun
36-
import Test.Tasty.Runners
3736

3837
import AsyncTests
3938
import BootTests
@@ -71,7 +70,7 @@ import WatchedFileTests
7170
main :: IO ()
7271
main = do
7372
-- We mess with env vars so run single-threaded.
74-
defaultMainWithRerun $ PlusTestOptions mkSequential $ testGroup "ghcide"
73+
defaultMainWithRerun $ testGroup "ghcide"
7574
[ OpenCloseTest.tests
7675
, InitializeResponseTests.tests
7776
, CompletionTests.tests
@@ -105,6 +104,3 @@ main = do
105104
, HieDbRetry.tests
106105
, ExceptionTests.tests
107106
]
108-
where
109-
PlusTestOptions mkSequential _ =sequentialTestGroup "foo" AllFinish []
110-

ghcide-test/exe/ReferenceTests.hs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,28 @@ tests = testGroup "references"
156156
, ("References.hs", 16, 0)
157157
]
158158
]
159+
-- Fields.hs does not depend on Main.hs
160+
-- so we can only find references in Fields.hs
161+
, testGroup "references to record fields"
162+
[ referenceTest "references record fields in the same file"
163+
("Fields.hs", 5, 4)
164+
YesIncludeDeclaration
165+
[ ("Fields.hs", 5, 4)
166+
, ("Fields.hs", 10, 14)
167+
, ("Fields.hs", 13, 14)
168+
]
169+
170+
-- Main.hs depends on Fields.hs, so we can find references
171+
-- from Main.hs to Fields.hs
172+
, referenceTest "references record fields cross modules"
173+
("Main.hs", 16, 24)
174+
YesIncludeDeclaration
175+
[ ("Fields.hs", 5, 4)
176+
, ("Fields.hs", 10, 14)
177+
, ("Fields.hs", 13, 14)
178+
, ("Main.hs", 16, 24)
179+
]
180+
]
159181
]
160182

161183
-- | When we ask for all references to symbol "foo", should the declaration "foo

ghcide/ghcide.cabal

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ library
5757
, deepseq
5858
, dependent-map
5959
, dependent-sum
60-
, Diff ^>=0.5
60+
, Diff ^>=0.5 || ^>=1.0.0
6161
, directory
6262
, dlist
6363
, enummapset
@@ -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

ghcide/session-loader/Development/IDE/Session.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ instance Pretty Log where
223223

224224
-- | Bump this version number when making changes to the format of the data stored in hiedb
225225
hiedbDataVersion :: String
226-
hiedbDataVersion = "1"
226+
hiedbDataVersion = "2"
227227

228228
data CacheDirs = CacheDirs
229229
{ hiCacheDir, hieCacheDir, oCacheDir :: Maybe FilePath}
@@ -956,6 +956,8 @@ CallStack (from HasCallStack):
956956
expectJust, called at compiler\\typecheck\\FamInst.hs:461:30 in ghc:FamInst
957957
```
958958
959+
and many more.
960+
959961
To mitigate this, we set the cache directory for each component dependent
960962
on the components of the current `HscEnv`, additionally to the component options
961963
of the respective components.

ghcide/src/Development/IDE/GHC/Compat/Error.hs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,43 @@ module Development.IDE.GHC.Compat.Error (
1717
DriverMessage (..),
1818
-- * General Diagnostics
1919
Diagnostic(..),
20-
-- * Prisms for error selection
20+
-- * Prisms and lenses for error selection
2121
_TcRnMessage,
22+
_TcRnMessageWithCtx,
2223
_GhcPsMessage,
2324
_GhcDsMessage,
2425
_GhcDriverMessage,
26+
_TcRnMissingSignature,
27+
_TcRnSolverReport,
28+
_TcRnMessageWithInfo,
29+
reportContextL,
30+
reportContentL,
31+
_MismatchMessage,
32+
_TypeEqMismatchActual,
33+
_TypeEqMismatchExpected,
2534
) where
2635

2736
import Control.Lens
37+
import Development.IDE.GHC.Compat (Type)
2838
import GHC.Driver.Errors.Types
2939
import GHC.HsToCore.Errors.Types
3040
import GHC.Tc.Errors.Types
3141
import GHC.Types.Error
3242

33-
_TcRnMessage :: Prism' GhcMessage TcRnMessage
34-
_TcRnMessage = prism' GhcTcRnMessage (\case
43+
-- | Some 'TcRnMessage's are nested in other constructors for additional context.
44+
-- For example, 'TcRnWithHsDocContext' and 'TcRnMessageWithInfo'.
45+
-- However, in most occasions you don't need the additional context and you just want
46+
-- the error message. @'_TcRnMessage'@ recursively unwraps these constructors,
47+
-- until there are no more constructors with additional context.
48+
--
49+
-- Use @'_TcRnMessageWithCtx'@ if you need the additional context. You can always
50+
-- strip it later using @'stripTcRnMessageContext'@.
51+
--
52+
_TcRnMessage :: Fold GhcMessage TcRnMessage
53+
_TcRnMessage = _TcRnMessageWithCtx . to stripTcRnMessageContext
54+
55+
_TcRnMessageWithCtx :: Prism' GhcMessage TcRnMessage
56+
_TcRnMessageWithCtx = prism' GhcTcRnMessage (\case
3557
GhcTcRnMessage tcRnMsg -> Just tcRnMsg
3658
_ -> Nothing)
3759

@@ -66,3 +88,38 @@ stripTcRnMessageContext = \case
6688

6789
msgEnvelopeErrorL :: Lens' (MsgEnvelope e) e
6890
msgEnvelopeErrorL = lens errMsgDiagnostic (\envelope e -> envelope { errMsgDiagnostic = e } )
91+
92+
makePrisms ''TcRnMessage
93+
94+
makeLensesWith
95+
(lensRules & lensField .~ mappingNamer (pure . (++ "L")))
96+
''SolverReportWithCtxt
97+
98+
-- | Focus 'MismatchMsg' from 'TcSolverReportMsg'. Currently, 'MismatchMsg' can be
99+
-- extracted from 'CannotUnifyVariable' and 'Mismatch' constructors.
100+
_MismatchMessage :: Traversal' TcSolverReportMsg MismatchMsg
101+
_MismatchMessage focus (Mismatch msg t a c) = (\msg' -> Mismatch msg' t a c) <$> focus msg
102+
_MismatchMessage focus (CannotUnifyVariable msg a) = flip CannotUnifyVariable a <$> focus msg
103+
_MismatchMessage _ report = pure report
104+
105+
-- | Focus 'teq_mismatch_expected' from 'TypeEqMismatch'.
106+
_TypeEqMismatchExpected :: Traversal' MismatchMsg Type
107+
#if MIN_VERSION_ghc(9,12,0)
108+
_TypeEqMismatchExpected focus mismatch@(TypeEqMismatch _ _ _ expected _ _ _) =
109+
(\expected' -> mismatch { teq_mismatch_expected = expected' }) <$> focus expected
110+
#else
111+
_TypeEqMismatchExpected focus mismatch@(TypeEqMismatch _ _ _ _ expected _ _ _) =
112+
(\expected' -> mismatch { teq_mismatch_expected = expected' }) <$> focus expected
113+
#endif
114+
_TypeEqMismatchExpected _ mismatch = pure mismatch
115+
116+
-- | Focus 'teq_mismatch_actual' from 'TypeEqMismatch'.
117+
_TypeEqMismatchActual :: Traversal' MismatchMsg Type
118+
#if MIN_VERSION_ghc(9,12,0)
119+
_TypeEqMismatchActual focus mismatch@(TypeEqMismatch _ _ _ _ actual _ _) =
120+
(\actual' -> mismatch { teq_mismatch_actual = actual' }) <$> focus actual
121+
#else
122+
_TypeEqMismatchActual focus mismatch@(TypeEqMismatch _ _ _ _ _ actual _ _) =
123+
(\actual' -> mismatch { teq_mismatch_expected = actual' }) <$> focus actual
124+
#endif
125+
_TypeEqMismatchActual _ mismatch = pure mismatch

ghcide/src/Development/IDE/Plugin/Completions/Logic.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,9 @@ getCompletionPrefixFromRope pos@(Position l c) ropetext =
878878
[] -> Nothing
879879
(x:xs) -> do
880880
let modParts = reverse $ filter (not .T.null) xs
881-
modName = T.intercalate "." modParts
881+
-- Must check the prefix is a valid module name, else record dot accesses treat
882+
-- the record name as a qualName for search and generated imports
883+
modName = if all (isUpper . T.head) modParts then T.intercalate "." modParts else ""
882884
return $ PosPrefixInfo { fullLine = curLine, prefixScope = modName, prefixText = x, cursorPos = pos }
883885

884886
completionPrefixPos :: PosPrefixInfo -> Position

0 commit comments

Comments
 (0)