|
| 1 | +{-# LANGUAGE CPP #-} |
1 | 2 | {-# LANGUAGE FlexibleContexts #-}
|
2 | 3 | {-# LANGUAGE GADTs #-}
|
3 | 4 | {-# LANGUAGE LambdaCase #-}
|
4 | 5 | {-# LANGUAGE OverloadedStrings #-}
|
5 | 6 | {-# LANGUAGE ScopedTypeVariables #-}
|
| 7 | +{-# LANGUAGE ViewPatterns #-} |
| 8 | +{-# OPTIONS_GHC -Wall #-} |
6 | 9 |
|
7 | 10 | module Ide.Plugin.Tactic.LanguageServer where
|
8 | 11 |
|
| 12 | +import ConLike |
9 | 13 | import Control.Arrow
|
10 | 14 | import Control.Monad
|
| 15 | +import Control.Monad.State (State, get, put, evalState) |
11 | 16 | import Control.Monad.Trans.Maybe
|
12 |
| -import Data.Aeson (Value (Object), fromJSON) |
13 |
| -import Data.Aeson.Types (Result (Error, Success)) |
| 17 | +import Data.Aeson (Value (Object), fromJSON) |
| 18 | +import Data.Aeson.Types (Result (Error, Success)) |
14 | 19 | import Data.Coerce
|
15 |
| -import Data.Functor ((<&>)) |
16 |
| -import Data.Generics.Aliases (mkQ) |
17 |
| -import Data.Generics.Schemes (everything) |
18 |
| -import Data.Map (Map) |
19 |
| -import qualified Data.Map as M |
| 20 | +import Data.Functor ((<&>)) |
| 21 | +import Data.Generics.Aliases (mkQ) |
| 22 | +import Data.Generics.Schemes (everything) |
| 23 | +import qualified Data.Map as M |
20 | 24 | import Data.Maybe
|
21 | 25 | import Data.Monoid
|
22 |
| -import qualified Data.Set as S |
23 |
| -import qualified Data.Text as T |
| 26 | +import qualified Data.Set as S |
| 27 | +import qualified Data.Text as T |
24 | 28 | import Data.Traversable
|
25 |
| -import Development.IDE (ShakeExtras, |
26 |
| - getPluginConfig) |
| 29 | +import Development.IDE (ShakeExtras, getPluginConfig) |
27 | 30 | import Development.IDE.Core.PositionMapping
|
28 | 31 | import Development.IDE.Core.RuleTypes
|
29 |
| -import Development.IDE.Core.Service (runAction) |
30 |
| -import Development.IDE.Core.Shake (IdeState (..), |
31 |
| - useWithStale) |
| 32 | +import Development.IDE.Core.Service (runAction) |
| 33 | +import Development.IDE.Core.Shake (IdeState (..), useWithStale) |
32 | 34 | import Development.IDE.GHC.Compat
|
33 |
| -import Development.IDE.GHC.Error (realSrcSpanToRange) |
34 |
| -import Development.IDE.Spans.LocalBindings (Bindings, |
35 |
| - getDefiningBindings) |
36 |
| -import Development.Shake (Action, RuleResult) |
37 |
| -import Development.Shake.Classes |
| 35 | +import Development.IDE.GHC.Error (realSrcSpanToRange) |
| 36 | +import Development.IDE.Spans.LocalBindings (Bindings, getDefiningBindings) |
| 37 | +import Development.Shake (Action, RuleResult) |
| 38 | +import Development.Shake.Classes (Typeable, Binary, Hashable, NFData) |
38 | 39 | import qualified FastString
|
39 |
| -import Ide.Plugin.Config (PluginConfig (plcConfig)) |
40 |
| -import qualified Ide.Plugin.Config as Plugin |
| 40 | +import GhcPlugins (mkAppTys, tupleDataCon, consDataCon) |
| 41 | +import Ide.Plugin.Config (PluginConfig (plcConfig)) |
| 42 | +import qualified Ide.Plugin.Config as Plugin |
41 | 43 | import Ide.Plugin.Tactic.Context
|
42 | 44 | import Ide.Plugin.Tactic.FeatureSet
|
43 | 45 | import Ide.Plugin.Tactic.GHC
|
44 | 46 | import Ide.Plugin.Tactic.Judgements
|
45 | 47 | import Ide.Plugin.Tactic.Range
|
46 |
| -import Ide.Plugin.Tactic.TestTypes (Config, TacticCommand, |
47 |
| - cfg_feature_set, |
48 |
| - emptyConfig) |
| 48 | +import Ide.Plugin.Tactic.TestTypes (TacticCommand, cfg_feature_set, emptyConfig, Config) |
49 | 49 | import Ide.Plugin.Tactic.Types
|
50 |
| -import Language.LSP.Server (MonadLsp) |
| 50 | +import Language.LSP.Server (MonadLsp) |
51 | 51 | import Language.LSP.Types
|
52 | 52 | import OccName
|
53 |
| -import Prelude hiding (span) |
54 |
| -import SrcLoc (containsSpan) |
55 |
| -import TcRnTypes (tcg_binds) |
| 53 | +import Prelude hiding (span) |
| 54 | +import SrcLoc (containsSpan) |
| 55 | +import TcRnTypes (tcg_binds) |
56 | 56 |
|
57 | 57 |
|
58 | 58 | tacticDesc :: T.Text -> T.Text
|
@@ -179,37 +179,162 @@ liftMaybe :: Monad m => Maybe a -> MaybeT m a
|
179 | 179 | liftMaybe a = MaybeT $ pure a
|
180 | 180 |
|
181 | 181 |
|
| 182 | +------------------------------------------------------------------------------ |
| 183 | +-- | Combine two (possibly-overlapping) hypotheses; using the provenance from |
| 184 | +-- the first hypothesis if the bindings overlap. |
182 | 185 | spliceProvenance
|
183 |
| - :: Map OccName Provenance |
184 |
| - -> Hypothesis a |
| 186 | + :: Hypothesis a -- ^ Bindings to keep |
| 187 | + -> Hypothesis a -- ^ Bindings to keep if they don't overlap with the first set |
185 | 188 | -> Hypothesis a
|
186 |
| -spliceProvenance provs x = |
187 |
| - Hypothesis $ flip fmap (unHypothesis x) $ \hi -> |
188 |
| - overProvenance (maybe id const $ M.lookup (hi_name hi) provs) hi |
| 189 | +spliceProvenance top x = |
| 190 | + let bound = S.fromList $ fmap hi_name $ unHypothesis top |
| 191 | + in mappend top $ Hypothesis . filter (flip S.notMember bound . hi_name) $ unHypothesis x |
189 | 192 |
|
190 | 193 |
|
191 | 194 | ------------------------------------------------------------------------------
|
192 | 195 | -- | Compute top-level position vals of a function
|
193 |
| -getRhsPosVals :: RealSrcSpan -> TypecheckedSource -> Map OccName Provenance |
| 196 | +getRhsPosVals :: RealSrcSpan -> TypecheckedSource -> Hypothesis CType |
194 | 197 | getRhsPosVals rss tcs
|
195 |
| - = M.fromList |
196 |
| - $ join |
197 |
| - $ maybeToList |
198 |
| - $ getFirst |
199 |
| - $ everything (<>) (mkQ mempty $ \case |
| 198 | + = everything (<>) (mkQ mempty $ \case |
200 | 199 | TopLevelRHS name ps
|
201 | 200 | (L (RealSrcSpan span) -- body with no guards and a single defn
|
202 | 201 | (HsVar _ (L _ hole)))
|
203 | 202 | | containsSpan rss span -- which contains our span
|
204 | 203 | , isHole $ occName hole -- and the span is a hole
|
205 |
| - -> First $ do |
206 |
| - patnames <- traverse getPatName ps |
207 |
| - pure $ zip patnames $ [0..] <&> \n -> |
208 |
| - TopLevelArgPrv name n (length patnames) |
| 204 | + -> flip evalState 0 $ buildTopLevelHypothesis name ps |
209 | 205 | _ -> mempty
|
210 | 206 | ) tcs
|
211 | 207 |
|
212 | 208 |
|
| 209 | +------------------------------------------------------------------------------ |
| 210 | +-- | Construct a hypothesis given the patterns from the left side of a HsMatch. |
| 211 | +-- These correspond to things that the user put in scope before running |
| 212 | +-- tactics. |
| 213 | +buildTopLevelHypothesis |
| 214 | + :: OccName -- ^ Function name |
| 215 | + -> [PatCompat GhcTc] |
| 216 | + -> State Int (Hypothesis CType) |
| 217 | +buildTopLevelHypothesis name ps = do |
| 218 | + fmap mconcat $ |
| 219 | + for (zip [0..] ps) $ \(ix, p) -> |
| 220 | + buildPatHy (TopLevelArgPrv name ix $ length ps) p |
| 221 | + |
| 222 | + |
| 223 | +------------------------------------------------------------------------------ |
| 224 | +-- | Construct a hypothesis for a single pattern, including building |
| 225 | +-- sub-hypotheses for constructor pattern matches. |
| 226 | +buildPatHy :: Provenance -> PatCompat GhcTc -> State Int (Hypothesis CType) |
| 227 | +buildPatHy prov (fromPatCompatTc -> p0) = |
| 228 | + case p0 of |
| 229 | + VarPat _ x -> pure $ mkIdHypothesis (unLoc x) prov |
| 230 | + LazyPat _ p -> buildPatHy prov p |
| 231 | + AsPat _ x p -> do |
| 232 | + hy' <- buildPatHy prov p |
| 233 | + pure $ mkIdHypothesis (unLoc x) prov <> hy' |
| 234 | + ParPat _ p -> buildPatHy prov p |
| 235 | + BangPat _ p -> buildPatHy prov p |
| 236 | + ViewPat _ _ p -> buildPatHy prov p |
| 237 | + -- Desugar lists into cons |
| 238 | + ListPat _ [] -> pure mempty |
| 239 | + ListPat x@(ListPatTc ty _) (p : ps) -> |
| 240 | + mkDerivedConHypothesis prov consDataCon [ty] |
| 241 | + [ (0, p) |
| 242 | + , (1, toPatCompatTc $ ListPat x ps) |
| 243 | + ] |
| 244 | + -- Desugar tuples into an explicit constructor |
| 245 | + TuplePat tys pats boxity -> |
| 246 | + mkDerivedConHypothesis |
| 247 | + prov |
| 248 | + (tupleDataCon boxity $ length pats) |
| 249 | + tys |
| 250 | + $ zip [0.. ] pats |
| 251 | + ConPatOut (L _ (RealDataCon dc)) args _ _ _ f _ -> |
| 252 | + case f of |
| 253 | + PrefixCon l_pgt -> |
| 254 | + mkDerivedConHypothesis prov dc args $ zip [0..] l_pgt |
| 255 | + InfixCon pgt pgt5 -> |
| 256 | + mkDerivedConHypothesis prov dc args $ zip [0..] [pgt, pgt5] |
| 257 | + RecCon r -> |
| 258 | + mkDerivedRecordHypothesis prov dc args r |
| 259 | +#if __GLASGOW_HASKELL__ >= 808 |
| 260 | + SigPat _ p _ -> buildPatHy prov p |
| 261 | +#endif |
| 262 | +#if __GLASGOW_HASKELL__ == 808 |
| 263 | + XPat p -> buildPatHy prov $ unLoc p |
| 264 | +#endif |
| 265 | + _ -> pure mempty |
| 266 | + |
| 267 | + |
| 268 | +------------------------------------------------------------------------------ |
| 269 | +-- | Like 'mkDerivedConHypothesis', but for record patterns. |
| 270 | +mkDerivedRecordHypothesis |
| 271 | + :: Provenance |
| 272 | + -> DataCon -- ^ Destructing constructor |
| 273 | + -> [Type] -- ^ Applied type variables |
| 274 | + -> HsRecFields GhcTc (PatCompat GhcTc) |
| 275 | + -> State Int (Hypothesis CType) |
| 276 | +mkDerivedRecordHypothesis prov dc args (HsRecFields (fmap unLoc -> fs) _) |
| 277 | + | Just rec_fields <- getRecordFields dc |
| 278 | + = do |
| 279 | + let field_lookup = M.fromList $ zip (fmap (occNameFS . fst) rec_fields) [0..] |
| 280 | + mkDerivedConHypothesis prov dc args $ fs <&> \(HsRecField (L _ rec_occ) p _) -> |
| 281 | + ( field_lookup M.! (occNameFS $ occName $ unLoc $ rdrNameFieldOcc rec_occ) |
| 282 | + , p |
| 283 | + ) |
| 284 | +mkDerivedRecordHypothesis _ _ _ _ = |
| 285 | + error "impossible! using record pattern on something that isn't a record" |
| 286 | + |
| 287 | + |
| 288 | +------------------------------------------------------------------------------ |
| 289 | +-- | Construct a fake variable name. Used to track the provenance of top-level |
| 290 | +-- pattern matches which otherwise wouldn't have anything to attach their |
| 291 | +-- 'TopLevelArgPrv' to. |
| 292 | +mkFakeVar :: State Int OccName |
| 293 | +mkFakeVar = do |
| 294 | + i <- get |
| 295 | + put $ i + 1 |
| 296 | + pure $ mkVarOcc $ "_" <> show i |
| 297 | + |
| 298 | + |
| 299 | +------------------------------------------------------------------------------ |
| 300 | +-- | Construct a fake varible to attach the current 'Provenance' to, and then |
| 301 | +-- build a sub-hypothesis for the pattern match. |
| 302 | +mkDerivedConHypothesis |
| 303 | + :: Provenance |
| 304 | + -> DataCon -- ^ Destructing constructor |
| 305 | + -> [Type] -- ^ Applied type variables |
| 306 | + -> [(Int, PatCompat GhcTc)] -- ^ Patterns, and their order in the data con |
| 307 | + -> State Int (Hypothesis CType) |
| 308 | +mkDerivedConHypothesis prov dc args ps = do |
| 309 | + var <- mkFakeVar |
| 310 | + hy' <- fmap mconcat $ |
| 311 | + for ps $ \(ix, p) -> do |
| 312 | + let prov' = PatternMatchPrv |
| 313 | + $ PatVal (Just var) |
| 314 | + (S.singleton var <> provAncestryOf prov) |
| 315 | + (Uniquely dc) |
| 316 | + ix |
| 317 | + buildPatHy prov' p |
| 318 | + pure |
| 319 | + $ mappend hy' |
| 320 | + $ Hypothesis |
| 321 | + $ pure |
| 322 | + $ HyInfo var (DisallowedPrv AlreadyDestructed prov) |
| 323 | + $ CType |
| 324 | + -- TODO(sandy): This is the completely wrong type, but we don't have a good |
| 325 | + -- way to get the real one. It's probably OK though, since we're generating |
| 326 | + -- this term with a disallowed provenance, and it doesn't actually exist |
| 327 | + -- anyway. |
| 328 | + $ mkAppTys (dataConUserType dc) args |
| 329 | + |
| 330 | + |
| 331 | +------------------------------------------------------------------------------ |
| 332 | +-- | Build a 'Hypothesis' given an 'Id'. |
| 333 | +mkIdHypothesis :: Id -> Provenance -> Hypothesis CType |
| 334 | +mkIdHypothesis (splitId -> (name, ty)) prov = |
| 335 | + Hypothesis $ pure $ HyInfo name prov ty |
| 336 | + |
| 337 | + |
213 | 338 | ------------------------------------------------------------------------------
|
214 | 339 | -- | Is this hole immediately to the right of an equals sign?
|
215 | 340 | isRhsHole :: RealSrcSpan -> TypecheckedSource -> Bool
|
|
0 commit comments