Skip to content

Commit 4cb9ff1

Browse files
authored
Make splice plugin compatible with GHC 9.2 (#2816)
* Compile and get all tests passing * Add back-compat for GHC 9.0 * Update docs and build flags to enable for 9.2
1 parent 2b94f85 commit 4cb9ff1

File tree

5 files changed

+87
-47
lines changed

5 files changed

+87
-47
lines changed

.github/workflows/test.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ jobs:
106106
os: ${{ runner.os }}
107107

108108
- name: Build
109-
run: cabal build
109+
run: cabal build
110110

111111
- name: Set test options
112112
# run the tests without parallelism, otherwise tasty will attempt to run
@@ -148,7 +148,7 @@ jobs:
148148
env:
149149
HLS_TEST_EXE: hls
150150
HLS_WRAPPER_TEST_EXE: hls-wrapper
151-
run: cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper"
151+
run: cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper"
152152

153153
- if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2'
154154
name: Test hls-brittany-plugin
@@ -178,7 +178,7 @@ jobs:
178178
name: Test hls-haddock-comments-plugin
179179
run: cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS"
180180

181-
- if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2'
181+
- if: matrix.test && matrix.ghc != '9.4.2'
182182
name: Test hls-splice-plugin
183183
run: cabal test hls-splice-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-splice-plugin --test-options="$TEST_OPTS"
184184

docs/support/plugin-support.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,4 @@ For example, a plugin to provide a formatter which has itself been abandoned has
6565
| `hls-haddock-comments-plugin` | 3 | 9.2, 9.4 |
6666
| `hls-stan-plugin` | 3 | 8.6, 9.0, 9.2, 9.4 |
6767
| `hls-retrie-plugin` | 3 | 9.2, 9.4 |
68-
| `hls-splice-plugin` | 3 | 9.2, 9.4 |
68+
| `hls-splice-plugin` | 3 | 9.4 |

haskell-language-server.cabal

+1-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ common pragmas
266266
cpp-options: -Dhls_pragmas
267267

268268
common splice
269-
if flag(splice) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds))
269+
if flag(splice) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds))
270270
build-depends: hls-splice-plugin ^>=1.0.0.1
271271
cpp-options: -Dhls_splice
272272

plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs

+28-18
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ module Development.IDE.GHC.ExactPrint
2525
Annotate,
2626
setPrecedingLinesT,
2727
#else
28+
setPrecedingLines,
2829
addParens,
2930
addParensToCtxt,
3031
modifyAnns,
@@ -56,6 +57,7 @@ import Control.Monad.Trans.Except
5657
import Control.Monad.Zip
5758
import Data.Bifunctor
5859
import Data.Bool (bool)
60+
import Data.Default (Default)
5961
import qualified Data.DList as DL
6062
import Data.Either.Extra (mapLeft)
6163
import Data.Foldable (Foldable (fold))
@@ -101,7 +103,13 @@ import GHC (EpAnn (..),
101103
spanAsAnchor)
102104
import GHC.Parser.Annotation (AnnContext (..),
103105
DeltaPos (SameLine),
104-
EpaLocation (EpaDelta))
106+
EpaLocation (EpaDelta),
107+
deltaPos)
108+
#endif
109+
110+
#if MIN_VERSION_ghc(9,2,0)
111+
setPrecedingLines :: Default t => LocatedAn t a -> Int -> Int -> LocatedAn t a
112+
setPrecedingLines ast n c = setEntryDP ast (deltaPos n c)
105113
#endif
106114

107115
------------------------------------------------------------------------------
@@ -114,10 +122,10 @@ instance Pretty Log where
114122

115123
instance Show (Annotated ParsedSource) where
116124
show _ = "<Annotated ParsedSource>"
117-
125+
118126
instance NFData (Annotated ParsedSource) where
119127
rnf = rwhnf
120-
128+
121129
data GetAnnotatedParsedSource = GetAnnotatedParsedSource
122130
deriving (Eq, Show, Typeable, GHC.Generic)
123131

@@ -374,7 +382,7 @@ graftWithM dst trans = Graft $ \dflags a -> do
374382
#if MIN_VERSION_ghc(9,2,0)
375383
val'' <-
376384
hoistTransform (either Fail.fail pure) $
377-
annotate dflags True $ maybeParensAST val'
385+
annotate dflags False $ maybeParensAST val'
378386
pure val''
379387
#else
380388
(anns, val'') <-
@@ -468,7 +476,17 @@ graftDeclsWithM dst toDecls = Graft $ \dflags a -> do
468476
modifyDeclsT (fmap DL.toList . go) a
469477

470478

471-
class (Data ast, Typeable l, Outputable l, Outputable ast) => ASTElement l ast | ast -> l where
479+
-- In 9.2+, we need `Default l` to do `setPrecedingLines` on annotated elements.
480+
-- In older versions, we pass around annotations explicitly, so the instance isn't needed.
481+
class
482+
( Data ast
483+
, Typeable l
484+
, Outputable l
485+
, Outputable ast
486+
#if MIN_VERSION_ghc(9,2,0)
487+
, Default l
488+
#endif
489+
) => ASTElement l ast | ast -> l where
472490
parseAST :: Parser (LocatedAn l ast)
473491
maybeParensAST :: LocatedAn l ast -> LocatedAn l ast
474492
{- | Construct a 'Graft', replacing the node at the given 'SrcSpan' with
@@ -520,6 +538,7 @@ fixAnns ParsedModule {..} =
520538

521539
------------------------------------------------------------------------------
522540

541+
523542
-- | Given an 'LHSExpr', compute its exactprint annotations.
524543
-- Note that this function will throw away any existing annotations (and format)
525544
annotate :: (ASTElement l ast, Outputable l)
@@ -533,7 +552,7 @@ annotate dflags needs_space ast = do
533552
let rendered = render dflags ast
534553
#if MIN_VERSION_ghc(9,2,0)
535554
expr' <- lift $ mapLeft show $ parseAST dflags uniq rendered
536-
pure expr'
555+
pure $ setPrecedingLines expr' 0 (bool 0 1 needs_space)
537556
#else
538557
(anns, expr') <- lift $ mapLeft show $ parseAST dflags uniq rendered
539558
let anns' = setPrecedingLines expr' 0 (bool 0 1 needs_space) anns
@@ -542,6 +561,7 @@ annotate dflags needs_space ast = do
542561

543562
-- | Given an 'LHsDecl', compute its exactprint annotations.
544563
annotateDecl :: DynFlags -> LHsDecl GhcPs -> TransformT (Either String) (LHsDecl GhcPs)
564+
#if !MIN_VERSION_ghc(9,2,0)
545565
-- The 'parseDecl' function fails to parse 'FunBind' 'ValD's which contain
546566
-- multiple matches. To work around this, we split the single
547567
-- 'FunBind'-of-multiple-'Match'es into multiple 'FunBind's-of-one-'Match',
@@ -554,17 +574,6 @@ annotateDecl dflags
554574
let set_matches matches =
555575
ValD ext fb { fun_matches = mg { mg_alts = L alt_src matches }}
556576

557-
#if MIN_VERSION_ghc(9,2,0)
558-
alts' <- for alts $ \alt -> do
559-
uniq <- show <$> uniqueSrcSpanT
560-
let rendered = render dflags $ set_matches [alt]
561-
lift (mapLeft show $ parseDecl dflags uniq rendered) >>= \case
562-
(L _ (ValD _ FunBind { fun_matches = MG { mg_alts = L _ [alt']}}))
563-
-> pure alt'
564-
_ -> lift $ Left "annotateDecl: didn't parse a single FunBind match"
565-
566-
pure $ L src $ set_matches alts'
567-
#else
568577
(anns', alts') <- fmap unzip $ for alts $ \alt -> do
569578
uniq <- show <$> uniqueSrcSpanT
570579
let rendered = render dflags $ set_matches [alt]
@@ -580,7 +589,8 @@ annotateDecl dflags ast = do
580589
uniq <- show <$> uniqueSrcSpanT
581590
let rendered = render dflags ast
582591
#if MIN_VERSION_ghc(9,2,0)
583-
lift $ mapLeft show $ parseDecl dflags uniq rendered
592+
expr' <- lift $ mapLeft show $ parseDecl dflags uniq rendered
593+
pure $ setPrecedingLines expr' 1 0
584594
#else
585595
(anns, expr') <- lift $ mapLeft show $ parseDecl dflags uniq rendered
586596
let anns' = setPrecedingLines expr' 1 0 anns

plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs

+54-24
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
{-# LANGUAGE TypeApplications #-}
1616
{-# LANGUAGE TypeFamilies #-}
1717
{-# LANGUAGE ViewPatterns #-}
18+
{-# LANGUAGE PatternSynonyms #-}
1819
{-# LANGUAGE MultiParamTypeClasses #-}
1920
{-# LANGUAGE FlexibleInstances #-}
2021

@@ -51,10 +52,13 @@ import Development.IDE.GHC.Compat.ExactPrint
5152
import qualified Development.IDE.GHC.Compat.Util as Util
5253
import Development.IDE.GHC.ExactPrint
5354
import GHC.Exts
55+
#if __GLASGOW_HASKELL__ >= 902
56+
import GHC.Parser.Annotation (SrcSpanAnn'(..))
57+
import qualified GHC.Types.Error as Error
58+
#endif
5459
import Ide.Plugin.Splice.Types
5560
import Ide.Types
56-
import Language.Haskell.GHC.ExactPrint (setPrecedingLines,
57-
uniqueSrcSpanT)
61+
import Language.Haskell.GHC.ExactPrint (uniqueSrcSpanT)
5862
import Language.LSP.Server
5963
import Language.LSP.Types
6064
import Language.LSP.Types.Capabilities
@@ -135,7 +139,7 @@ expandTHSplice _eStyle ideState params@ExpandSpliceParams {..} = do
135139
graftSpliceWith ::
136140
forall ast.
137141
HasSplice AnnListItem ast =>
138-
Maybe (SrcSpan, Located (ast GhcPs)) ->
142+
Maybe (SrcSpan, LocatedAn AnnListItem (ast GhcPs)) ->
139143
Maybe (Either String WorkspaceEdit)
140144
graftSpliceWith expandeds =
141145
expandeds <&> \(_, expanded) ->
@@ -236,11 +240,11 @@ adjustToRange uri ran (WorkspaceEdit mhult mlt x) =
236240
where
237241
adjustTextEdits :: Traversable f => f TextEdit -> f TextEdit
238242
adjustTextEdits eds =
239-
let Just minStart =
240-
L.fold
241-
(L.premap (view J.range) L.minimum)
242-
eds
243-
in adjustLine minStart <$> eds
243+
let minStart =
244+
case L.fold (L.premap (view J.range) L.minimum) eds of
245+
Nothing -> error "impossible"
246+
Just v -> v
247+
in adjustLine minStart <$> eds
244248

245249
adjustATextEdits :: Traversable f => f (TextEdit |? AnnotatedTextEdit) -> f (TextEdit |? AnnotatedTextEdit)
246250
adjustATextEdits = fmap $ \case
@@ -263,11 +267,23 @@ adjustToRange uri ran (WorkspaceEdit mhult mlt x) =
263267
J.range %~ \r ->
264268
if r == bad then ran else bad
265269

270+
-- Define a pattern to get hold of a `SrcSpan` from the location part of a
271+
-- `GenLocated`. In GHC >= 9.2 this will be a SrcSpanAnn', with annotations;
272+
-- earlier it will just be a plain `SrcSpan`.
273+
{-# COMPLETE AsSrcSpan #-}
274+
#if __GLASGOW_HASKELL__ >= 902
275+
pattern AsSrcSpan :: SrcSpan -> SrcSpanAnn' a
276+
pattern AsSrcSpan locA <- SrcSpanAnn {locA}
277+
#else
278+
pattern AsSrcSpan :: SrcSpan -> SrcSpan
279+
pattern AsSrcSpan loc <- loc
280+
#endif
281+
266282
findSubSpansDesc :: SrcSpan -> [(LHsExpr GhcTc, a)] -> [(SrcSpan, a)]
267283
findSubSpansDesc srcSpan =
268284
sortOn (Down . SubSpan . fst)
269285
. mapMaybe
270-
( \(L spn _, e) -> do
286+
( \(L (AsSrcSpan spn) _, e) -> do
271287
guard (spn `isSubspanOf` srcSpan)
272288
pure (spn, e)
273289
)
@@ -321,7 +337,7 @@ manualCalcEdit ::
321337
manualCalcEdit clientCapabilities reportEditor ran ps hscEnv typechkd srcSpan _eStyle ExpandSpliceParams {..} = do
322338
(warns, resl) <-
323339
ExceptT $ do
324-
((warns, errs), eresl) <-
340+
(msgs, eresl) <-
325341
initTcWithGbl hscEnv typechkd srcSpan $
326342
case classifyAST spliceContext of
327343
IsHsDecl -> fmap (fmap $ adjustToRange uri ran) $
@@ -348,8 +364,16 @@ manualCalcEdit clientCapabilities reportEditor ran ps hscEnv typechkd srcSpan _e
348364
Util.try @_ @SomeException $
349365
(fst <$> expandSplice astP spl)
350366
)
351-
Just <$> either (pure . L _spn) (unRenamedE dflags) eExpr
367+
Just <$> case eExpr of
368+
Left x -> pure $ L _spn x
369+
Right y -> unRenamedE dflags y
352370
_ -> pure Nothing
371+
let (warns, errs) =
372+
#if __GLASGOW_HASKELL__ >= 902
373+
(Error.getWarningMessages msgs, Error.getErrorMessages msgs)
374+
#else
375+
msgs
376+
#endif
353377
pure $ (warns,) <$> fromMaybe (Left $ show errs) eresl
354378

355379
unless
@@ -370,14 +394,17 @@ unRenamedE ::
370394
(Fail.MonadFail m, HasSplice l ast) =>
371395
DynFlags ->
372396
ast GhcRn ->
373-
TransformT m (Located (ast GhcPs))
397+
TransformT m (LocatedAn l (ast GhcPs))
374398
unRenamedE dflags expr = do
375399
uniq <- show <$> uniqueSrcSpanT
376-
(anns, expr') <-
400+
#if __GLASGOW_HASKELL__ >= 902
401+
expr' <-
402+
#else
403+
(_anns, expr') <-
404+
#endif
377405
either (fail . show) pure $
378-
parseAST @_ @(ast GhcPs) dflags uniq $
379-
showSDoc dflags $ ppr expr
380-
let _anns' = setPrecedingLines expr' 0 1 anns
406+
parseAST @_ @(ast GhcPs) dflags uniq $
407+
showSDoc dflags $ ppr expr
381408
pure expr'
382409

383410
data SearchResult r =
@@ -416,11 +443,14 @@ codeAction state plId (CodeActionParams _ _ docId ran _) = liftIO $
416443
RealSrcSpan ->
417444
GenericQ (SearchResult (RealSrcSpan, SpliceContext))
418445
detectSplice spn =
446+
let
447+
spanIsRelevant x = RealSrcSpan spn Nothing `isSubspanOf` x
448+
in
419449
mkQ
420450
Continue
421451
( \case
422-
(L l@(RealSrcSpan spLoc _) expr :: LHsExpr GhcPs)
423-
| RealSrcSpan spn Nothing `isSubspanOf` l ->
452+
(L (AsSrcSpan l@(RealSrcSpan spLoc _)) expr :: LHsExpr GhcPs)
453+
| spanIsRelevant l ->
424454
case expr of
425455
HsSpliceE {} -> Here (spLoc, Expr)
426456
_ -> Continue
@@ -430,23 +460,23 @@ codeAction state plId (CodeActionParams _ _ docId ran _) = liftIO $
430460
#if __GLASGOW_HASKELL__ == 808
431461
(dL @(Pat GhcPs) -> L l@(RealSrcSpan spLoc _) pat :: Located (Pat GhcPs))
432462
#else
433-
(L l@(RealSrcSpan spLoc _) pat :: LPat GhcPs)
463+
(L (AsSrcSpan l@(RealSrcSpan spLoc _)) pat :: LPat GhcPs)
434464
#endif
435-
| RealSrcSpan spn Nothing `isSubspanOf` l ->
465+
| spanIsRelevant l ->
436466
case pat of
437467
SplicePat{} -> Here (spLoc, Pat)
438468
_ -> Continue
439469
_ -> Stop
440470
`extQ` \case
441-
(L l@(RealSrcSpan spLoc _) ty :: LHsType GhcPs)
442-
| RealSrcSpan spn Nothing `isSubspanOf` l ->
471+
(L (AsSrcSpan l@(RealSrcSpan spLoc _)) ty :: LHsType GhcPs)
472+
| spanIsRelevant l ->
443473
case ty of
444474
HsSpliceTy {} -> Here (spLoc, HsType)
445475
_ -> Continue
446476
_ -> Stop
447477
`extQ` \case
448-
(L l@(RealSrcSpan spLoc _) decl :: LHsDecl GhcPs)
449-
| RealSrcSpan spn Nothing `isSubspanOf` l ->
478+
(L (AsSrcSpan l@(RealSrcSpan spLoc _)) decl :: LHsDecl GhcPs)
479+
| spanIsRelevant l ->
450480
case decl of
451481
SpliceD {} -> Here (spLoc, HsDecl)
452482
_ -> Continue

0 commit comments

Comments
 (0)