packages feed

hls-tactics-plugin 1.0.0.0 → 1.1.0.0

raw patch · 231 files changed

+6128/−3685 lines, 231 filesdep +hls-test-utilsdep +tasty-hspecdep +unordered-containersdep −bytestringdep −checkersdep −data-defaultdep ~basedep ~ghcidedep ~hls-plugin-apinew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: hls-test-utils, tasty-hspec, unordered-containers

Dependencies removed: bytestring, checkers, data-default, hie-bios, lsp-test, megaparsec, tasty, tasty-ant-xml, tasty-expected-failure, tasty-golden, tasty-rerun

Dependency ranges changed: base, ghcide, hls-plugin-api, shake

API changes (from Hackage documentation)

- Ide.Plugin.Tactic.Auto: auto :: TacticsM ()
- Ide.Plugin.Tactic.CaseSplit: iterateSplit :: AgdaMatch -> [AgdaMatch]
- Ide.Plugin.Tactic.CaseSplit: mkFirstAgda :: [Pat GhcPs] -> HsExpr GhcPs -> AgdaMatch
- Ide.Plugin.Tactic.CaseSplit: splitToDecl :: OccName -> [AgdaMatch] -> LHsDecl GhcPs
- Ide.Plugin.Tactic.CodeGen: addUnusedTopVals :: MonadState TacticState m => Set OccName -> m ()
- Ide.Plugin.Tactic.CodeGen: buildDataCon :: Judgement -> DataCon -> [Type] -> RuleM (Trace, LHsExpr GhcPs)
- Ide.Plugin.Tactic.CodeGen: countRecursiveCall :: TacticState -> TacticState
- Ide.Plugin.Tactic.CodeGen: dataConInstOrigArgTys' :: DataCon -> [Type] -> [Type]
- Ide.Plugin.Tactic.CodeGen: destruct' :: (DataCon -> Judgement -> Rule) -> HyInfo CType -> Judgement -> Rule
- Ide.Plugin.Tactic.CodeGen: destructLambdaCase' :: (DataCon -> Judgement -> Rule) -> Judgement -> Rule
- Ide.Plugin.Tactic.CodeGen: destructMatches :: (DataCon -> Judgement -> Rule) -> Maybe OccName -> CType -> Judgement -> RuleM (Trace, [RawMatch])
- Ide.Plugin.Tactic.CodeGen: infixifyPatIfNecessary :: DataCon -> Pat GhcPs -> Pat GhcPs
- Ide.Plugin.Tactic.CodeGen: mkDestructPat :: DataCon -> [OccName] -> Pat GhcPs
- Ide.Plugin.Tactic.CodeGen: unzipTrace :: [(Trace, a)] -> (Trace, [a])
- Ide.Plugin.Tactic.CodeGen: useOccName :: MonadState TacticState m => Judgement -> OccName -> m ()
- Ide.Plugin.Tactic.CodeGen.Utils: appDollar :: HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs
- Ide.Plugin.Tactic.CodeGen.Utils: bvar' :: BVar a => OccName -> a
- Ide.Plugin.Tactic.CodeGen.Utils: coerceName :: HasOccName a => a -> RdrNameStr
- Ide.Plugin.Tactic.CodeGen.Utils: infixCall :: String -> HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs
- Ide.Plugin.Tactic.CodeGen.Utils: mkCon :: DataCon -> [LHsExpr GhcPs] -> LHsExpr GhcPs
- Ide.Plugin.Tactic.CodeGen.Utils: mkFunc :: String -> HsExpr GhcPs
- Ide.Plugin.Tactic.CodeGen.Utils: mkVal :: String -> HsExpr GhcPs
- Ide.Plugin.Tactic.CodeGen.Utils: var' :: Var a => OccName -> a
- Ide.Plugin.Tactic.Context: contextMethodHypothesis :: Context -> Hypothesis CType
- Ide.Plugin.Tactic.Context: definedThetaType :: Context -> OccName -> Maybe CType
- Ide.Plugin.Tactic.Context: excludeForbiddenMethods :: [HyInfo a] -> [HyInfo a]
- Ide.Plugin.Tactic.Context: getCurrentDefinitions :: MonadReader Context m => m [(OccName, CType)]
- Ide.Plugin.Tactic.Context: getFunBindId :: HsBindLR GhcTc GhcTc -> [Id]
- Ide.Plugin.Tactic.Context: getModuleHypothesis :: MonadReader Context m => m [(OccName, CType)]
- Ide.Plugin.Tactic.Context: mkContext :: FeatureSet -> [(OccName, CType)] -> TcGblEnv -> Context
- Ide.Plugin.Tactic.Context: splitId :: Id -> (OccName, CType)
- Ide.Plugin.Tactic.Debug: trace :: String -> a -> a
- Ide.Plugin.Tactic.Debug: traceFX :: String -> (a -> String) -> a -> a
- Ide.Plugin.Tactic.Debug: traceIdX :: Show a => String -> a -> a
- Ide.Plugin.Tactic.Debug: traceM :: Applicative f => String -> f ()
- Ide.Plugin.Tactic.Debug: traceMX :: (Monad m, Show a) => String -> a -> m ()
- Ide.Plugin.Tactic.Debug: traceShowId :: Show a => a -> a
- Ide.Plugin.Tactic.Debug: traceX :: Show a => String -> a -> b -> b
- Ide.Plugin.Tactic.Debug: unsafeRender :: Outputable a => a -> String
- Ide.Plugin.Tactic.Debug: unsafeRender' :: SDoc -> String
- Ide.Plugin.Tactic.FeatureSet: CantHaveAnEmptyDataType :: Feature
- Ide.Plugin.Tactic.FeatureSet: allFeatures :: FeatureSet
- Ide.Plugin.Tactic.FeatureSet: data Feature
- Ide.Plugin.Tactic.FeatureSet: defaultFeatures :: FeatureSet
- Ide.Plugin.Tactic.FeatureSet: hasFeature :: Feature -> FeatureSet -> Bool
- Ide.Plugin.Tactic.FeatureSet: instance GHC.Classes.Eq Ide.Plugin.Tactic.FeatureSet.Feature
- Ide.Plugin.Tactic.FeatureSet: instance GHC.Classes.Ord Ide.Plugin.Tactic.FeatureSet.Feature
- Ide.Plugin.Tactic.FeatureSet: instance GHC.Enum.Bounded Ide.Plugin.Tactic.FeatureSet.Feature
- Ide.Plugin.Tactic.FeatureSet: instance GHC.Enum.Enum Ide.Plugin.Tactic.FeatureSet.Feature
- Ide.Plugin.Tactic.FeatureSet: instance GHC.Read.Read Ide.Plugin.Tactic.FeatureSet.Feature
- Ide.Plugin.Tactic.FeatureSet: instance GHC.Show.Show Ide.Plugin.Tactic.FeatureSet.Feature
- Ide.Plugin.Tactic.FeatureSet: parseFeatureSet :: Text -> FeatureSet
- Ide.Plugin.Tactic.FeatureSet: prettyFeatureSet :: FeatureSet -> String
- Ide.Plugin.Tactic.FeatureSet: type FeatureSet = Set Feature
- Ide.Plugin.Tactic.GHC: algebraicTyCon :: Type -> Maybe TyCon
- Ide.Plugin.Tactic.GHC: allOccNames :: Data a => a -> Set OccName
- Ide.Plugin.Tactic.GHC: cloneTyVar :: TyVar -> TyVar
- Ide.Plugin.Tactic.GHC: containsHole :: Data a => a -> Bool
- Ide.Plugin.Tactic.GHC: containsHsVar :: Data a => RdrName -> a -> Bool
- Ide.Plugin.Tactic.GHC: dataConExTys :: DataCon -> [TyCoVar]
- Ide.Plugin.Tactic.GHC: eqRdrName :: RdrName -> RdrName -> Bool
- Ide.Plugin.Tactic.GHC: freshTyvars :: MonadState TacticState m => Type -> m Type
- Ide.Plugin.Tactic.GHC: fromPatCompatPs :: PatCompat GhcPs -> Pat GhcPs
- Ide.Plugin.Tactic.GHC: fromPatCompatTc :: PatCompat GhcTc -> Pat GhcTc
- Ide.Plugin.Tactic.GHC: getPatName :: PatCompat GhcTc -> Maybe OccName
- Ide.Plugin.Tactic.GHC: getRecordFields :: DataCon -> Maybe [(OccName, CType)]
- Ide.Plugin.Tactic.GHC: instantiateType :: Type -> ([TyVar], Type)
- Ide.Plugin.Tactic.GHC: isFunction :: Type -> Bool
- Ide.Plugin.Tactic.GHC: isHole :: OccName -> Bool
- Ide.Plugin.Tactic.GHC: lambdaCaseable :: Type -> Maybe Bool
- Ide.Plugin.Tactic.GHC: pattern Case :: HsExpr GhcPs -> [(Pat GhcPs, HsExpr GhcPs)] -> HsExpr GhcPs
- Ide.Plugin.Tactic.GHC: pattern Lambda :: [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs
- Ide.Plugin.Tactic.GHC: pattern SinglePatMatch :: Pat GhcPs -> HsExpr GhcPs -> Match GhcPs (LHsExpr GhcPs)
- Ide.Plugin.Tactic.GHC: pattern TopLevelRHS :: OccName -> [PatCompat GhcTc] -> LHsExpr GhcTc -> Match GhcTc (LHsExpr GhcTc)
- Ide.Plugin.Tactic.GHC: pattern UnguardedRHSs :: HsExpr GhcPs -> GRHSs GhcPs (LHsExpr GhcPs)
- Ide.Plugin.Tactic.GHC: tacticsSplitFunTy :: Type -> ([TyVar], ThetaType, [Type], Type)
- Ide.Plugin.Tactic.GHC: tacticsThetaTy :: Type -> ThetaType
- Ide.Plugin.Tactic.GHC: tcTyVar_maybe :: Type -> Maybe Var
- Ide.Plugin.Tactic.GHC: type PatCompat pass = LPat pass
- Ide.Plugin.Tactic.GHC: unXPat :: Pat GhcPs -> Pat GhcPs
- Ide.Plugin.Tactic.GHC: unpackMatches :: [Match GhcPs (LHsExpr GhcPs)] -> Maybe [(Pat GhcPs, HsExpr GhcPs)]
- Ide.Plugin.Tactic.Judgements: blacklistingDestruct :: Judgement -> Judgement
- Ide.Plugin.Tactic.Judgements: disallowing :: DisallowReason -> [OccName] -> Judgement' a -> Judgement' a
- Ide.Plugin.Tactic.Judgements: filterPosition :: OccName -> Int -> Judgement -> Judgement
- Ide.Plugin.Tactic.Judgements: filterSameTypeFromOtherPositions :: DataCon -> Int -> Judgement -> Judgement
- Ide.Plugin.Tactic.Judgements: hyByName :: Hypothesis a -> Map OccName (HyInfo a)
- Ide.Plugin.Tactic.Judgements: hyNamesInScope :: Hypothesis a -> Set OccName
- Ide.Plugin.Tactic.Judgements: hypothesisFromBindings :: RealSrcSpan -> Bindings -> Hypothesis CType
- Ide.Plugin.Tactic.Judgements: introducingLambda :: Maybe OccName -> [(OccName, a)] -> Judgement' a -> Judgement' a
- Ide.Plugin.Tactic.Judgements: introducingPat :: Maybe OccName -> DataCon -> [(OccName, a)] -> Judgement' a -> Judgement' a
- Ide.Plugin.Tactic.Judgements: introducingRecursively :: [(OccName, a)] -> Judgement' a -> Judgement' a
- Ide.Plugin.Tactic.Judgements: isDestructBlacklisted :: Judgement -> Bool
- Ide.Plugin.Tactic.Judgements: isPatternMatch :: Provenance -> Bool
- Ide.Plugin.Tactic.Judgements: isSplitWhitelisted :: Judgement -> Bool
- Ide.Plugin.Tactic.Judgements: isTopHole :: Context -> Judgement' a -> Maybe OccName
- Ide.Plugin.Tactic.Judgements: isTopLevel :: Provenance -> Bool
- Ide.Plugin.Tactic.Judgements: jEntireHypothesis :: Judgement' a -> Hypothesis a
- Ide.Plugin.Tactic.Judgements: jGoal :: Judgement' a -> a
- Ide.Plugin.Tactic.Judgements: jHypothesis :: Judgement' a -> Hypothesis a
- Ide.Plugin.Tactic.Judgements: jLocalHypothesis :: Judgement' a -> Hypothesis a
- Ide.Plugin.Tactic.Judgements: jPatHypothesis :: Judgement' a -> Map OccName PatVal
- Ide.Plugin.Tactic.Judgements: mkFirstJudgement :: Hypothesis CType -> Bool -> Type -> Judgement' CType
- Ide.Plugin.Tactic.Judgements: substJdg :: TCvSubst -> Judgement -> Judgement
- Ide.Plugin.Tactic.Judgements: unsetIsTopHole :: Judgement' a -> Judgement' a
- Ide.Plugin.Tactic.Judgements: unwhitelistingSplit :: Judgement -> Judgement
- Ide.Plugin.Tactic.Judgements: withNewGoal :: a -> Judgement' a -> Judgement' a
- Ide.Plugin.Tactic.KnownStrategies: deriveFmap :: TacticsM ()
- Ide.Plugin.Tactic.KnownStrategies: known :: String -> TacticsM () -> TacticsM ()
- Ide.Plugin.Tactic.KnownStrategies: knownStrategies :: TacticsM ()
- Ide.Plugin.Tactic.KnownStrategies.QuickCheck: Generator :: Integer -> HsExpr GhcPs -> Generator
- Ide.Plugin.Tactic.KnownStrategies.QuickCheck: [genExpr] :: Generator -> HsExpr GhcPs
- Ide.Plugin.Tactic.KnownStrategies.QuickCheck: [genRecursiveCount] :: Generator -> Integer
- Ide.Plugin.Tactic.KnownStrategies.QuickCheck: data Generator
- Ide.Plugin.Tactic.KnownStrategies.QuickCheck: deriveArbitrary :: TacticsM ()
- Ide.Plugin.Tactic.KnownStrategies.QuickCheck: doesTypeContain :: TyCon -> Type -> Bool
- Ide.Plugin.Tactic.KnownStrategies.QuickCheck: mkArbitraryCall :: TyCon -> Integer -> Type -> HsExpr GhcPs
- Ide.Plugin.Tactic.KnownStrategies.QuickCheck: mkGenerator :: TyCon -> [Type] -> DataCon -> Generator
- Ide.Plugin.Tactic.LanguageServer: getFeatureSet :: MonadLsp Config m => ShakeExtras -> m FeatureSet
- Ide.Plugin.Tactic.LanguageServer: getIdeDynflags :: IdeState -> NormalizedFilePath -> MaybeT IO DynFlags
- Ide.Plugin.Tactic.LanguageServer: getRhsPosVals :: RealSrcSpan -> TypecheckedSource -> Map OccName Provenance
- Ide.Plugin.Tactic.LanguageServer: getSpanAndTypeAtHole :: PositionMapping -> Range -> HieASTs b -> Maybe (Span, b)
- Ide.Plugin.Tactic.LanguageServer: isRhsHole :: RealSrcSpan -> TypecheckedSource -> Bool
- Ide.Plugin.Tactic.LanguageServer: judgementForHole :: IdeState -> NormalizedFilePath -> Range -> FeatureSet -> MaybeT IO (Range, Judgement, Context, DynFlags)
- Ide.Plugin.Tactic.LanguageServer: liftMaybe :: Monad m => Maybe a -> MaybeT m a
- Ide.Plugin.Tactic.LanguageServer: mkJudgementAndContext :: FeatureSet -> Type -> Bindings -> RealSrcSpan -> TcModuleResult -> (Judgement, Context)
- Ide.Plugin.Tactic.LanguageServer: runIde :: IdeState -> Action a -> IO a
- Ide.Plugin.Tactic.LanguageServer: runStaleIde :: forall a r. (r ~ RuleResult a, Eq a, Hashable a, Binary a, Show a, Typeable a, NFData a, Show r, Typeable r, NFData r) => IdeState -> NormalizedFilePath -> a -> MaybeT IO (r, PositionMapping)
- Ide.Plugin.Tactic.LanguageServer: spliceProvenance :: Map OccName Provenance -> Hypothesis a -> Hypothesis a
- Ide.Plugin.Tactic.LanguageServer: tacticDesc :: Text -> Text
- Ide.Plugin.Tactic.LanguageServer: tcCommandName :: TacticCommand -> Text
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: TacticParams :: Uri -> Range -> Text -> TacticParams
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: [tp_file] :: TacticParams -> Uri
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: [tp_range] :: TacticParams -> Range
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: [tp_var_name] :: TacticParams -> Text
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: commandProvider :: TacticCommand -> TacticProvider
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: commandTactic :: TacticCommand -> OccName -> TacticsM ()
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: data TacticParams
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Tactic.LanguageServer.TacticProviders.TacticParams
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Tactic.LanguageServer.TacticProviders.TacticParams
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: instance GHC.Classes.Eq Ide.Plugin.Tactic.LanguageServer.TacticProviders.TacticParams
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: instance GHC.Generics.Generic Ide.Plugin.Tactic.LanguageServer.TacticProviders.TacticParams
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: instance GHC.Show.Show Ide.Plugin.Tactic.LanguageServer.TacticProviders.TacticParams
- Ide.Plugin.Tactic.LanguageServer.TacticProviders: tcCommandId :: TacticCommand -> CommandId
- Ide.Plugin.Tactic.Machinery: Penalize :: a -> Penalize a
- Ide.Plugin.Tactic.Machinery: Reward :: a -> Reward a
- Ide.Plugin.Tactic.Machinery: assoc23 :: (a, b, c) -> (a, (b, c))
- Ide.Plugin.Tactic.Machinery: guardStructurallySmallerRecursion :: TacticState -> Maybe TacticError
- Ide.Plugin.Tactic.Machinery: instance GHC.Classes.Eq a => GHC.Classes.Eq (Ide.Plugin.Tactic.Machinery.Penalize a)
- Ide.Plugin.Tactic.Machinery: instance GHC.Classes.Eq a => GHC.Classes.Eq (Ide.Plugin.Tactic.Machinery.Reward a)
- Ide.Plugin.Tactic.Machinery: instance GHC.Classes.Ord a => GHC.Classes.Ord (Ide.Plugin.Tactic.Machinery.Penalize a)
- Ide.Plugin.Tactic.Machinery: instance GHC.Classes.Ord a => GHC.Classes.Ord (Ide.Plugin.Tactic.Machinery.Reward a)
- Ide.Plugin.Tactic.Machinery: instance GHC.Show.Show a => GHC.Show.Show (Ide.Plugin.Tactic.Machinery.Penalize a)
- Ide.Plugin.Tactic.Machinery: instance GHC.Show.Show a => GHC.Show.Show (Ide.Plugin.Tactic.Machinery.Reward a)
- Ide.Plugin.Tactic.Machinery: markStructuralySmallerRecursion :: MonadState TacticState m => PatVal -> m ()
- Ide.Plugin.Tactic.Machinery: methodHypothesis :: PredType -> Maybe [HyInfo CType]
- Ide.Plugin.Tactic.Machinery: newSubgoal :: Judgement -> Rule
- Ide.Plugin.Tactic.Machinery: newtype Penalize a
- Ide.Plugin.Tactic.Machinery: newtype Reward a
- Ide.Plugin.Tactic.Machinery: requireConcreteHole :: TacticsM a -> TacticsM a
- Ide.Plugin.Tactic.Machinery: runTactic :: Context -> Judgement -> TacticsM () -> Either [TacticError] RunTacticResults
- Ide.Plugin.Tactic.Machinery: scoreSolution :: LHsExpr GhcPs -> TacticState -> [Judgement] -> (Penalize Int, Reward Bool, Penalize Int, Penalize Int, Reward Int, Penalize Int, Penalize Int)
- Ide.Plugin.Tactic.Machinery: solutionSize :: LHsExpr GhcPs -> Int
- Ide.Plugin.Tactic.Machinery: substCTy :: TCvSubst -> CType -> CType
- Ide.Plugin.Tactic.Machinery: tracePrim :: String -> Trace
- Ide.Plugin.Tactic.Machinery: tracing :: Functor m => String -> TacticT jdg (Trace, ext) err s m a -> TacticT jdg (Trace, ext) err s m a
- Ide.Plugin.Tactic.Machinery: tryUnifyUnivarsButNotSkolems :: Set TyVar -> CType -> CType -> Maybe TCvSubst
- Ide.Plugin.Tactic.Machinery: unify :: CType -> CType -> RuleM ()
- Ide.Plugin.Tactic.Naming: filterReplace :: (a -> Bool) -> a -> [a] -> [a]
- Ide.Plugin.Tactic.Naming: getInScope :: Map OccName a -> [OccName]
- Ide.Plugin.Tactic.Naming: mkGoodName :: Set OccName -> Type -> OccName
- Ide.Plugin.Tactic.Naming: mkManyGoodNames :: (Traversable t, Monad m) => Set OccName -> t Type -> m (t OccName)
- Ide.Plugin.Tactic.Naming: mkTyConName :: TyCon -> String
- Ide.Plugin.Tactic.Naming: mkTyName :: Type -> String
- Ide.Plugin.Tactic.Range: rangeToRealSrcSpan :: String -> Range -> RealSrcSpan
- Ide.Plugin.Tactic.Range: rangeToSrcSpan :: String -> Range -> SrcSpan
- Ide.Plugin.Tactic.Simplify: simplify :: LHsExpr GhcPs -> LHsExpr GhcPs
- Ide.Plugin.Tactic.Tactics: allNames :: Judgement -> Set OccName
- Ide.Plugin.Tactic.Tactics: apply :: HyInfo CType -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: assume :: OccName -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: assumption :: TacticsM ()
- Ide.Plugin.Tactic.Tactics: attemptOn :: (Judgement -> [a]) -> (a -> TacticsM ()) -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: auto' :: Int -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: destruct :: HyInfo CType -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: destructAuto :: HyInfo CType -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: destructLambdaCase :: TacticsM ()
- Ide.Plugin.Tactic.Tactics: homo :: HyInfo CType -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: homoLambdaCase :: TacticsM ()
- Ide.Plugin.Tactic.Tactics: intros :: TacticsM ()
- Ide.Plugin.Tactic.Tactics: localTactic :: TacticsM a -> (Judgement -> Judgement) -> TacticsM a
- Ide.Plugin.Tactic.Tactics: matching :: (Judgement -> TacticsM ()) -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: overAlgebraicTerms :: (HyInfo CType -> TacticsM ()) -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: overFunctions :: (HyInfo CType -> TacticsM ()) -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: recursion :: TacticsM ()
- Ide.Plugin.Tactic.Tactics: requireNewHoles :: TacticsM () -> TacticsM ()
- Ide.Plugin.Tactic.Tactics: runTactic :: Context -> Judgement -> TacticsM () -> Either [TacticError] RunTacticResults
- Ide.Plugin.Tactic.Tactics: split :: TacticsM ()
- Ide.Plugin.Tactic.Tactics: splitAuto :: TacticsM ()
- Ide.Plugin.Tactic.Tactics: splitDataCon :: DataCon -> TacticsM ()
- Ide.Plugin.Tactic.TestTypes: Auto :: TacticCommand
- Ide.Plugin.Tactic.TestTypes: Config :: FeatureSet -> Config
- Ide.Plugin.Tactic.TestTypes: Destruct :: TacticCommand
- Ide.Plugin.Tactic.TestTypes: DestructLambdaCase :: TacticCommand
- Ide.Plugin.Tactic.TestTypes: Homomorphism :: TacticCommand
- Ide.Plugin.Tactic.TestTypes: HomomorphismLambdaCase :: TacticCommand
- Ide.Plugin.Tactic.TestTypes: Intros :: TacticCommand
- Ide.Plugin.Tactic.TestTypes: [cfg_feature_set] :: Config -> FeatureSet
- Ide.Plugin.Tactic.TestTypes: data TacticCommand
- Ide.Plugin.Tactic.TestTypes: emptyConfig :: Config
- Ide.Plugin.Tactic.TestTypes: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Tactic.TestTypes.Config
- Ide.Plugin.Tactic.TestTypes: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Tactic.TestTypes.Config
- Ide.Plugin.Tactic.TestTypes: instance GHC.Classes.Eq Ide.Plugin.Tactic.TestTypes.TacticCommand
- Ide.Plugin.Tactic.TestTypes: instance GHC.Classes.Ord Ide.Plugin.Tactic.TestTypes.TacticCommand
- Ide.Plugin.Tactic.TestTypes: instance GHC.Enum.Bounded Ide.Plugin.Tactic.TestTypes.TacticCommand
- Ide.Plugin.Tactic.TestTypes: instance GHC.Enum.Enum Ide.Plugin.Tactic.TestTypes.TacticCommand
- Ide.Plugin.Tactic.TestTypes: instance GHC.Show.Show Ide.Plugin.Tactic.TestTypes.TacticCommand
- Ide.Plugin.Tactic.TestTypes: newtype Config
- Ide.Plugin.Tactic.TestTypes: tacticTitle :: TacticCommand -> Text -> Text
- Ide.Plugin.Tactic.Types: AgdaMatch :: [Pat GhcPs] -> HsExpr GhcPs -> AgdaMatch
- Ide.Plugin.Tactic.Types: AlreadyDestructed :: DisallowReason
- Ide.Plugin.Tactic.Types: CType :: Type -> CType
- Ide.Plugin.Tactic.Types: ClassMethodPrv :: Uniquely Class -> Provenance
- Ide.Plugin.Tactic.Types: Context :: [(OccName, CType)] -> [(OccName, CType)] -> FeatureSet -> Context
- Ide.Plugin.Tactic.Types: DisallowedPrv :: DisallowReason -> Provenance -> Provenance
- Ide.Plugin.Tactic.Types: ExtractM :: Reader Context a -> ExtractM a
- Ide.Plugin.Tactic.Types: GoalMismatch :: String -> CType -> TacticError
- Ide.Plugin.Tactic.Types: HyInfo :: OccName -> Provenance -> a -> HyInfo a
- Ide.Plugin.Tactic.Types: Hypothesis :: [HyInfo a] -> Hypothesis a
- Ide.Plugin.Tactic.Types: IncorrectDataCon :: DataCon -> TacticError
- Ide.Plugin.Tactic.Types: Judgement :: !Hypothesis a -> !Bool -> !Bool -> !Bool -> !a -> Judgement' a
- Ide.Plugin.Tactic.Types: NoApplicableTactic :: TacticError
- Ide.Plugin.Tactic.Types: NoProgress :: TacticError
- Ide.Plugin.Tactic.Types: NotInScope :: OccName -> TacticError
- Ide.Plugin.Tactic.Types: PatVal :: Maybe OccName -> Set OccName -> Uniquely DataCon -> Int -> PatVal
- Ide.Plugin.Tactic.Types: PatternMatchPrv :: PatVal -> Provenance
- Ide.Plugin.Tactic.Types: RecursionOnWrongParam :: OccName -> Int -> OccName -> TacticError
- Ide.Plugin.Tactic.Types: RecursiveCall :: DisallowReason
- Ide.Plugin.Tactic.Types: RecursivePrv :: Provenance
- Ide.Plugin.Tactic.Types: Rose :: Tree a -> Rose a
- Ide.Plugin.Tactic.Types: RunTacticResults :: Trace -> LHsExpr GhcPs -> [(Trace, LHsExpr GhcPs)] -> Judgement -> Context -> RunTacticResults
- Ide.Plugin.Tactic.Types: Shadowed :: DisallowReason
- Ide.Plugin.Tactic.Types: TacticState :: !Set TyVar -> !TCvSubst -> !Set OccName -> !Set OccName -> !Set OccName -> ![Maybe PatVal] -> !Int -> !UniqSupply -> TacticState
- Ide.Plugin.Tactic.Types: TooPolymorphic :: TacticError
- Ide.Plugin.Tactic.Types: TopLevelArgPrv :: OccName -> Int -> Provenance
- Ide.Plugin.Tactic.Types: UndefinedHypothesis :: OccName -> TacticError
- Ide.Plugin.Tactic.Types: UnhelpfulDestruct :: OccName -> TacticError
- Ide.Plugin.Tactic.Types: UnhelpfulSplit :: OccName -> TacticError
- Ide.Plugin.Tactic.Types: UnificationError :: CType -> CType -> TacticError
- Ide.Plugin.Tactic.Types: Uniquely :: a -> Uniquely a
- Ide.Plugin.Tactic.Types: UnsolvedSubgoals :: [Judgement] -> TacticError
- Ide.Plugin.Tactic.Types: UserPrv :: Provenance
- Ide.Plugin.Tactic.Types: WrongBranch :: Int -> DisallowReason
- Ide.Plugin.Tactic.Types: [_jBlacklistDestruct] :: Judgement' a -> !Bool
- Ide.Plugin.Tactic.Types: [_jGoal] :: Judgement' a -> !a
- Ide.Plugin.Tactic.Types: [_jHypothesis] :: Judgement' a -> !Hypothesis a
- Ide.Plugin.Tactic.Types: [_jIsTopHole] :: Judgement' a -> !Bool
- Ide.Plugin.Tactic.Types: [_jWhitelistSplit] :: Judgement' a -> !Bool
- Ide.Plugin.Tactic.Types: [amBody] :: AgdaMatch -> HsExpr GhcPs
- Ide.Plugin.Tactic.Types: [amPats] :: AgdaMatch -> [Pat GhcPs]
- Ide.Plugin.Tactic.Types: [ctxDefiningFuncs] :: Context -> [(OccName, CType)]
- Ide.Plugin.Tactic.Types: [ctxFeatureSet] :: Context -> FeatureSet
- Ide.Plugin.Tactic.Types: [ctxModuleFuncs] :: Context -> [(OccName, CType)]
- Ide.Plugin.Tactic.Types: [getViaUnique] :: Uniquely a -> a
- Ide.Plugin.Tactic.Types: [hi_name] :: HyInfo a -> OccName
- Ide.Plugin.Tactic.Types: [hi_provenance] :: HyInfo a -> Provenance
- Ide.Plugin.Tactic.Types: [hi_type] :: HyInfo a -> a
- Ide.Plugin.Tactic.Types: [pv_ancestry] :: PatVal -> Set OccName
- Ide.Plugin.Tactic.Types: [pv_datacon] :: PatVal -> Uniquely DataCon
- Ide.Plugin.Tactic.Types: [pv_position] :: PatVal -> Int
- Ide.Plugin.Tactic.Types: [pv_scrutinee] :: PatVal -> Maybe OccName
- Ide.Plugin.Tactic.Types: [rtr_ctx] :: RunTacticResults -> Context
- Ide.Plugin.Tactic.Types: [rtr_extract] :: RunTacticResults -> LHsExpr GhcPs
- Ide.Plugin.Tactic.Types: [rtr_jdg] :: RunTacticResults -> Judgement
- Ide.Plugin.Tactic.Types: [rtr_other_solns] :: RunTacticResults -> [(Trace, LHsExpr GhcPs)]
- Ide.Plugin.Tactic.Types: [rtr_trace] :: RunTacticResults -> Trace
- Ide.Plugin.Tactic.Types: [ts_intro_vals] :: TacticState -> !Set OccName
- Ide.Plugin.Tactic.Types: [ts_recursion_count] :: TacticState -> !Int
- Ide.Plugin.Tactic.Types: [ts_recursion_stack] :: TacticState -> ![Maybe PatVal]
- Ide.Plugin.Tactic.Types: [ts_skolems] :: TacticState -> !Set TyVar
- Ide.Plugin.Tactic.Types: [ts_unifier] :: TacticState -> !TCvSubst
- Ide.Plugin.Tactic.Types: [ts_unique_gen] :: TacticState -> !UniqSupply
- Ide.Plugin.Tactic.Types: [ts_unused_top_vals] :: TacticState -> !Set OccName
- Ide.Plugin.Tactic.Types: [ts_used_vals] :: TacticState -> !Set OccName
- Ide.Plugin.Tactic.Types: [unCType] :: CType -> Type
- Ide.Plugin.Tactic.Types: [unExtractM] :: ExtractM a -> Reader Context a
- Ide.Plugin.Tactic.Types: [unHypothesis] :: Hypothesis a -> [HyInfo a]
- Ide.Plugin.Tactic.Types: data AgdaMatch
- Ide.Plugin.Tactic.Types: data Context
- Ide.Plugin.Tactic.Types: data DisallowReason
- Ide.Plugin.Tactic.Types: data HyInfo a
- Ide.Plugin.Tactic.Types: data Judgement' a
- Ide.Plugin.Tactic.Types: data Name
- Ide.Plugin.Tactic.Types: data OccName
- Ide.Plugin.Tactic.Types: data PatVal
- Ide.Plugin.Tactic.Types: data Provenance
- Ide.Plugin.Tactic.Types: data Range
- Ide.Plugin.Tactic.Types: data RunTacticResults
- Ide.Plugin.Tactic.Types: data TacticError
- Ide.Plugin.Tactic.Types: data TacticState
- Ide.Plugin.Tactic.Types: data Type
- Ide.Plugin.Tactic.Types: defaultTacticState :: TacticState
- Ide.Plugin.Tactic.Types: dropEveryOther :: [a] -> [a]
- Ide.Plugin.Tactic.Types: emptyContext :: Context
- Ide.Plugin.Tactic.Types: freshUnique :: MonadState TacticState m => m Unique
- Ide.Plugin.Tactic.Types: instance Control.Monad.Reader.Class.MonadReader Ide.Plugin.Tactic.Types.Context Ide.Plugin.Tactic.Types.ExtractM
- Ide.Plugin.Tactic.Types: instance GHC.Base.Applicative Ide.Plugin.Tactic.Types.ExtractM
- Ide.Plugin.Tactic.Types: instance GHC.Base.Functor Ide.Plugin.Tactic.Types.ExtractM
- Ide.Plugin.Tactic.Types: instance GHC.Base.Functor Ide.Plugin.Tactic.Types.HyInfo
- Ide.Plugin.Tactic.Types: instance GHC.Base.Functor Ide.Plugin.Tactic.Types.Hypothesis
- Ide.Plugin.Tactic.Types: instance GHC.Base.Functor Ide.Plugin.Tactic.Types.Judgement'
- Ide.Plugin.Tactic.Types: instance GHC.Base.Functor Ide.Plugin.Tactic.Types.Rose
- Ide.Plugin.Tactic.Types: instance GHC.Base.Monad Ide.Plugin.Tactic.Types.ExtractM
- Ide.Plugin.Tactic.Types: instance GHC.Base.Monoid (Ide.Plugin.Tactic.Types.Hypothesis a)
- Ide.Plugin.Tactic.Types: instance GHC.Base.Monoid a => GHC.Base.Monoid (Ide.Plugin.Tactic.Types.Rose a)
- Ide.Plugin.Tactic.Types: instance GHC.Base.Semigroup (Ide.Plugin.Tactic.Types.Hypothesis a)
- Ide.Plugin.Tactic.Types: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Ide.Plugin.Tactic.Types.Rose a)
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq Ide.Plugin.Tactic.Types.CType
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq Ide.Plugin.Tactic.Types.Context
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq Ide.Plugin.Tactic.Types.DisallowReason
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq Ide.Plugin.Tactic.Types.PatVal
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq Ide.Plugin.Tactic.Types.Provenance
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq Ide.Plugin.Tactic.Types.TacticError
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Ide.Plugin.Tactic.Types.HyInfo a)
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Ide.Plugin.Tactic.Types.Hypothesis a)
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Ide.Plugin.Tactic.Types.Judgement' a)
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Ide.Plugin.Tactic.Types.Rose a)
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Ord Ide.Plugin.Tactic.Types.CType
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Ord Ide.Plugin.Tactic.Types.Context
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Ord Ide.Plugin.Tactic.Types.DisallowReason
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Ord Ide.Plugin.Tactic.Types.PatVal
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Ord Ide.Plugin.Tactic.Types.Provenance
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Ord a => GHC.Classes.Ord (Ide.Plugin.Tactic.Types.HyInfo a)
- Ide.Plugin.Tactic.Types: instance GHC.Classes.Ord a => GHC.Classes.Ord (Ide.Plugin.Tactic.Types.Hypothesis a)
- Ide.Plugin.Tactic.Types: instance GHC.Generics.Generic (Ide.Plugin.Tactic.Types.HyInfo a)
- Ide.Plugin.Tactic.Types: instance GHC.Generics.Generic (Ide.Plugin.Tactic.Types.Hypothesis a)
- Ide.Plugin.Tactic.Types: instance GHC.Generics.Generic (Ide.Plugin.Tactic.Types.Judgement' a)
- Ide.Plugin.Tactic.Types: instance GHC.Generics.Generic (Ide.Plugin.Tactic.Types.Rose a)
- Ide.Plugin.Tactic.Types: instance GHC.Generics.Generic Ide.Plugin.Tactic.Types.DisallowReason
- Ide.Plugin.Tactic.Types: instance GHC.Generics.Generic Ide.Plugin.Tactic.Types.PatVal
- Ide.Plugin.Tactic.Types: instance GHC.Generics.Generic Ide.Plugin.Tactic.Types.Provenance
- Ide.Plugin.Tactic.Types: instance GHC.Generics.Generic Ide.Plugin.Tactic.Types.TacticState
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show (GHC.Hs.Expr.HsExpr GHC.Hs.Extension.GhcPs)
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show (GHC.Hs.Pat.Pat GHC.Hs.Extension.GhcPs)
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show (Ide.Plugin.Tactic.Types.Rose GHC.Base.String)
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Class.Class
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show DataCon.DataCon
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Ide.Plugin.Tactic.Types.AgdaMatch
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Ide.Plugin.Tactic.Types.CType
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Ide.Plugin.Tactic.Types.Context
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Ide.Plugin.Tactic.Types.DisallowReason
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Ide.Plugin.Tactic.Types.PatVal
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Ide.Plugin.Tactic.Types.Provenance
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Ide.Plugin.Tactic.Types.RunTacticResults
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Ide.Plugin.Tactic.Types.TacticError
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Ide.Plugin.Tactic.Types.TacticState
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show OccName.OccName
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show TyCoSubst.TCvSubst
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show UniqSupply.UniqSupply
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show Var.Var
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show a => GHC.Show.Show (Ide.Plugin.Tactic.Types.HyInfo a)
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show a => GHC.Show.Show (Ide.Plugin.Tactic.Types.Hypothesis a)
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show a => GHC.Show.Show (Ide.Plugin.Tactic.Types.Judgement' a)
- Ide.Plugin.Tactic.Types: instance GHC.Show.Show a => GHC.Show.Show (Ide.Plugin.Tactic.Types.Uniquely a)
- Ide.Plugin.Tactic.Types: instance Refinery.ProofState.MonadExtract (Ide.Plugin.Tactic.Types.Trace, GHC.Hs.Expr.LHsExpr GHC.Hs.Extension.GhcPs) Ide.Plugin.Tactic.Types.ExtractM
- Ide.Plugin.Tactic.Types: instance Unique.Uniquable a => GHC.Classes.Eq (Ide.Plugin.Tactic.Types.Uniquely a)
- Ide.Plugin.Tactic.Types: instance Unique.Uniquable a => GHC.Classes.Ord (Ide.Plugin.Tactic.Types.Uniquely a)
- Ide.Plugin.Tactic.Types: newtype CType
- Ide.Plugin.Tactic.Types: newtype ExtractM a
- Ide.Plugin.Tactic.Types: newtype Hypothesis a
- Ide.Plugin.Tactic.Types: newtype Rose a
- Ide.Plugin.Tactic.Types: newtype Uniquely a
- Ide.Plugin.Tactic.Types: overProvenance :: (Provenance -> Provenance) -> HyInfo a -> HyInfo a
- Ide.Plugin.Tactic.Types: popRecursionStack :: TacticState -> TacticState
- Ide.Plugin.Tactic.Types: pushRecursionStack :: TacticState -> TacticState
- Ide.Plugin.Tactic.Types: rose :: (Eq a, Monoid a) => a -> [Rose a] -> Rose a
- Ide.Plugin.Tactic.Types: type Judgement = Judgement' CType
- Ide.Plugin.Tactic.Types: type Rule = RuleM (Trace, LHsExpr GhcPs)
- Ide.Plugin.Tactic.Types: type RuleM = RuleT Judgement (Trace, LHsExpr GhcPs) TacticError TacticState ExtractM
- Ide.Plugin.Tactic.Types: type Span = RealSrcSpan
- Ide.Plugin.Tactic.Types: type TacticsM = TacticT Judgement (Trace, LHsExpr GhcPs) TacticError TacticState ExtractM
- Ide.Plugin.Tactic.Types: type Trace = Rose String
- Ide.Plugin.Tactic.Types: type TyVar = Var
- Ide.Plugin.Tactic.Types: unsafeDefaultUniqueSupply :: UniqSupply
- Ide.Plugin.Tactic.Types: withIntroducedVals :: (Set OccName -> Set OccName) -> TacticState -> TacticState
- Ide.Plugin.Tactic.Types: withRecursionStack :: ([Maybe PatVal] -> [Maybe PatVal]) -> TacticState -> TacticState
- Ide.Plugin.Tactic.Types: withUsedVals :: (Set OccName -> Set OccName) -> TacticState -> TacticState
+ Ide.Plugin.Tactic: DestructAll :: TacticCommand
+ Ide.Plugin.Tactic: Refine :: TacticCommand
+ Ide.Plugin.Tactic: UseDataCon :: TacticCommand
+ Wingman.Auto: auto :: TacticsM ()
+ Wingman.CaseSplit: iterateSplit :: AgdaMatch -> [AgdaMatch]
+ Wingman.CaseSplit: mkFirstAgda :: [Pat GhcPs] -> HsExpr GhcPs -> AgdaMatch
+ Wingman.CaseSplit: splitToDecl :: OccName -> [AgdaMatch] -> LHsDecl GhcPs
+ Wingman.CodeGen: buildDataCon :: Bool -> Judgement -> ConLike -> [Type] -> RuleM (Synthesized (LHsExpr GhcPs))
+ Wingman.CodeGen: conLikeExTys :: ConLike -> [TyCoVar]
+ Wingman.CodeGen: conLikeInstOrigArgTys' :: ConLike -> [Type] -> [Type]
+ Wingman.CodeGen: destruct' :: (ConLike -> Judgement -> Rule) -> HyInfo CType -> Judgement -> Rule
+ Wingman.CodeGen: destructLambdaCase' :: (ConLike -> Judgement -> Rule) -> Judgement -> Rule
+ Wingman.CodeGen: destructMatches :: (ConLike -> Judgement -> Rule) -> Maybe OccName -> CType -> Judgement -> RuleM (Synthesized [RawMatch])
+ Wingman.CodeGen: infixifyPatIfNecessary :: ConLike -> Pat GhcPs -> Pat GhcPs
+ Wingman.CodeGen: mkApply :: OccName -> [HsExpr GhcPs] -> LHsExpr GhcPs
+ Wingman.CodeGen: mkDestructPat :: ConLike -> [OccName] -> Pat GhcPs
+ Wingman.CodeGen: patSynExTys :: PatSyn -> [TyCoVar]
+ Wingman.CodeGen: unzipTrace :: [Synthesized a] -> Synthesized [a]
+ Wingman.CodeGen.Utils: appDollar :: HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs
+ Wingman.CodeGen.Utils: bvar' :: BVar a => OccName -> a
+ Wingman.CodeGen.Utils: coerceName :: HasOccName a => a -> RdrNameStr
+ Wingman.CodeGen.Utils: infixCall :: String -> HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs
+ Wingman.CodeGen.Utils: mkCon :: ConLike -> [Type] -> [LHsExpr GhcPs] -> LHsExpr GhcPs
+ Wingman.CodeGen.Utils: mkFunc :: String -> HsExpr GhcPs
+ Wingman.CodeGen.Utils: mkVal :: String -> HsExpr GhcPs
+ Wingman.CodeGen.Utils: var' :: Var a => OccName -> a
+ Wingman.Context: getCurrentDefinitions :: MonadReader Context m => m [(OccName, CType)]
+ Wingman.Context: getFunBindId :: HsBindLR GhcTc GhcTc -> [Id]
+ Wingman.Context: getInstance :: MonadReader Context m => Class -> [Type] -> m (Maybe (Class, PredType))
+ Wingman.Context: getKnownInstance :: MonadReader Context m => (KnownThings -> Class) -> [Type] -> m (Maybe (Class, PredType))
+ Wingman.Context: getKnownThing :: MonadReader Context m => (KnownThings -> a) -> m a
+ Wingman.Context: hasClassInstance :: MonadReader Context m => PredType -> m Bool
+ Wingman.Context: mkContext :: FeatureSet -> [(OccName, CType)] -> TcGblEnv -> ExternalPackageState -> KnownThings -> [Evidence] -> Context
+ Wingman.Context: splitId :: Id -> (OccName, CType)
+ Wingman.Debug: trace :: String -> a -> a
+ Wingman.Debug: traceFX :: String -> (a -> String) -> a -> a
+ Wingman.Debug: traceIdX :: Show a => String -> a -> a
+ Wingman.Debug: traceM :: Applicative f => String -> f ()
+ Wingman.Debug: traceMX :: (Monad m, Show a) => String -> a -> m ()
+ Wingman.Debug: traceShowId :: Show a => a -> a
+ Wingman.Debug: traceX :: Show a => String -> a -> b -> b
+ Wingman.Debug: unsafeRender :: Outputable a => a -> String
+ Wingman.Debug: unsafeRender' :: SDoc -> String
+ Wingman.FeatureSet: FeatureDestructAll :: Feature
+ Wingman.FeatureSet: FeatureKnownMonoid :: Feature
+ Wingman.FeatureSet: FeatureRefineHole :: Feature
+ Wingman.FeatureSet: FeatureUseDataCon :: Feature
+ Wingman.FeatureSet: allFeatures :: FeatureSet
+ Wingman.FeatureSet: data Feature
+ Wingman.FeatureSet: defaultFeatures :: FeatureSet
+ Wingman.FeatureSet: hasFeature :: Feature -> FeatureSet -> Bool
+ Wingman.FeatureSet: instance GHC.Classes.Eq Wingman.FeatureSet.Feature
+ Wingman.FeatureSet: instance GHC.Classes.Ord Wingman.FeatureSet.Feature
+ Wingman.FeatureSet: instance GHC.Enum.Bounded Wingman.FeatureSet.Feature
+ Wingman.FeatureSet: instance GHC.Enum.Enum Wingman.FeatureSet.Feature
+ Wingman.FeatureSet: instance GHC.Read.Read Wingman.FeatureSet.Feature
+ Wingman.FeatureSet: instance GHC.Show.Show Wingman.FeatureSet.Feature
+ Wingman.FeatureSet: parseFeatureSet :: Text -> FeatureSet
+ Wingman.FeatureSet: prettyFeatureSet :: FeatureSet -> String
+ Wingman.FeatureSet: type FeatureSet = Set Feature
+ Wingman.GHC: algebraicTyCon :: Type -> Maybe TyCon
+ Wingman.GHC: allOccNames :: Data a => a -> Set OccName
+ Wingman.GHC: class PatCompattable p
+ Wingman.GHC: cloneTyVar :: TyVar -> TyVar
+ Wingman.GHC: containsHole :: Data a => a -> Bool
+ Wingman.GHC: containsHsVar :: Data a => RdrName -> a -> Bool
+ Wingman.GHC: dataConExTys :: DataCon -> [TyCoVar]
+ Wingman.GHC: eqRdrName :: RdrName -> RdrName -> Bool
+ Wingman.GHC: freshTyvars :: MonadState TacticState m => Type -> m Type
+ Wingman.GHC: fromPatCompat :: PatCompattable p => PatCompat p -> Pat p
+ Wingman.GHC: getRecordFields :: ConLike -> Maybe [(OccName, CType)]
+ Wingman.GHC: instance Wingman.GHC.PatCompattable GHC.Hs.Extension.GhcPs
+ Wingman.GHC: instance Wingman.GHC.PatCompattable GHC.Hs.Extension.GhcTc
+ Wingman.GHC: instantiateType :: Type -> ([TyVar], Type)
+ Wingman.GHC: isFunction :: Type -> Bool
+ Wingman.GHC: isHole :: OccName -> Bool
+ Wingman.GHC: knownClass :: TcGblEnv -> HscEnvEq -> OccName -> MaybeT IO Class
+ Wingman.GHC: knownThing :: (TyThing -> Maybe a) -> TcGblEnv -> HscEnvEq -> OccName -> MaybeT IO a
+ Wingman.GHC: knownThings :: TcGblEnv -> HscEnvEq -> MaybeT IO KnownThings
+ Wingman.GHC: lambdaCaseable :: Type -> Maybe Bool
+ Wingman.GHC: liftMaybe :: Monad m => Maybe a -> MaybeT m a
+ Wingman.GHC: pattern AMatch :: HsMatchContext (NameOrRdrName (IdP GhcPs)) -> [Pat GhcPs] -> HsExpr GhcPs -> Match GhcPs (LHsExpr GhcPs)
+ Wingman.GHC: pattern Case :: PatCompattable p => HsExpr p -> [(Pat p, LHsExpr p)] -> HsExpr p
+ Wingman.GHC: pattern Lambda :: [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs
+ Wingman.GHC: pattern SinglePatMatch :: PatCompattable p => Pat p -> LHsExpr p -> Match p (LHsExpr p)
+ Wingman.GHC: pattern TopLevelRHS :: OccName -> [PatCompat GhcTc] -> LHsExpr GhcTc -> Match GhcTc (LHsExpr GhcTc)
+ Wingman.GHC: pattern UnguardedRHSs :: LHsExpr p -> GRHSs p (LHsExpr p)
+ Wingman.GHC: sloppyEqOccName :: OccName -> OccName -> Bool
+ Wingman.GHC: tacticsGetDataCons :: Type -> Maybe ([DataCon], [Type])
+ Wingman.GHC: tacticsSplitFunTy :: Type -> ([TyVar], ThetaType, [Type], Type)
+ Wingman.GHC: tacticsThetaTy :: Type -> ThetaType
+ Wingman.GHC: tcTyVar_maybe :: Type -> Maybe Var
+ Wingman.GHC: toPatCompat :: PatCompattable p => Pat p -> PatCompat p
+ Wingman.GHC: type PatCompat pass = LPat pass
+ Wingman.GHC: unXPat :: Pat GhcPs -> Pat GhcPs
+ Wingman.GHC: unpackMatches :: PatCompattable p => [Match p (LHsExpr p)] -> Maybe [(Pat p, LHsExpr p)]
+ Wingman.Judgements: blacklistingDestruct :: Judgement -> Judgement
+ Wingman.Judgements: buildHypothesis :: [(Name, Maybe Type)] -> Hypothesis CType
+ Wingman.Judgements: disallowing :: DisallowReason -> Set OccName -> Judgement' a -> Judgement' a
+ Wingman.Judgements: expandDisallowed :: Provenance -> Provenance
+ Wingman.Judgements: extremelyStupid__definingFunction :: Context -> OccName
+ Wingman.Judgements: filterAncestry :: Foldable t => t OccName -> DisallowReason -> Judgement -> Judgement
+ Wingman.Judgements: filterPosition :: OccName -> Int -> Judgement -> Judgement
+ Wingman.Judgements: filterSameTypeFromOtherPositions :: ConLike -> Int -> Judgement -> Judgement
+ Wingman.Judgements: findDconPositionVals :: Judgement' a -> ConLike -> Int -> [OccName]
+ Wingman.Judgements: findPositionVal :: Judgement' a -> OccName -> Int -> Maybe OccName
+ Wingman.Judgements: getAncestry :: Judgement' a -> OccName -> Set OccName
+ Wingman.Judgements: getPatVal :: Provenance -> Maybe PatVal
+ Wingman.Judgements: hasPositionalAncestry :: Foldable t => t OccName -> Judgement -> OccName -> Maybe Bool
+ Wingman.Judgements: hyByName :: Hypothesis a -> Map OccName (HyInfo a)
+ Wingman.Judgements: hyNamesInScope :: Hypothesis a -> Set OccName
+ Wingman.Judgements: hypothesisFromBindings :: Tracked age RealSrcSpan -> Tracked age Bindings -> Hypothesis CType
+ Wingman.Judgements: introduce :: Hypothesis a -> Judgement' a -> Judgement' a
+ Wingman.Judgements: introduceHypothesis :: (Int -> Int -> Provenance) -> [(OccName, a)] -> Hypothesis a
+ Wingman.Judgements: isAlreadyDestructed :: Provenance -> Bool
+ Wingman.Judgements: isDestructBlacklisted :: Judgement -> Bool
+ Wingman.Judgements: isDisallowed :: Provenance -> Bool
+ Wingman.Judgements: isLocalHypothesis :: Provenance -> Bool
+ Wingman.Judgements: isPatternMatch :: Provenance -> Bool
+ Wingman.Judgements: isSplitWhitelisted :: Judgement -> Bool
+ Wingman.Judgements: isTopHole :: Context -> Judgement' a -> Maybe OccName
+ Wingman.Judgements: isTopLevel :: Provenance -> Bool
+ Wingman.Judgements: jAcceptableDestructTargets :: Judgement' CType -> [HyInfo CType]
+ Wingman.Judgements: jAncestryMap :: Judgement' a -> Map OccName (Set OccName)
+ Wingman.Judgements: jEntireHypothesis :: Judgement' a -> Hypothesis a
+ Wingman.Judgements: jGoal :: Judgement' a -> a
+ Wingman.Judgements: jHasBoundArgs :: Judgement' a -> Bool
+ Wingman.Judgements: jHypothesis :: Judgement' a -> Hypothesis a
+ Wingman.Judgements: jLocalHypothesis :: Judgement' a -> Hypothesis a
+ Wingman.Judgements: jPatHypothesis :: Judgement' a -> Map OccName PatVal
+ Wingman.Judgements: lambdaHypothesis :: Maybe OccName -> [(OccName, a)] -> Hypothesis a
+ Wingman.Judgements: mkFirstJudgement :: Hypothesis CType -> Bool -> Type -> Judgement' CType
+ Wingman.Judgements: patternHypothesis :: Maybe OccName -> ConLike -> Judgement' a -> [(OccName, a)] -> Hypothesis a
+ Wingman.Judgements: provAncestryOf :: Provenance -> Set OccName
+ Wingman.Judgements: recursiveHypothesis :: [(OccName, a)] -> Hypothesis a
+ Wingman.Judgements: substJdg :: TCvSubst -> Judgement -> Judgement
+ Wingman.Judgements: unsetIsTopHole :: Judgement' a -> Judgement' a
+ Wingman.Judgements: unwhitelistingSplit :: Judgement -> Judgement
+ Wingman.Judgements: withNewGoal :: a -> Judgement' a -> Judgement' a
+ Wingman.Judgements.SYB: everythingContaining :: forall r. Monoid r => SrcSpan -> GenericQ r -> GenericQ r
+ Wingman.Judgements.SYB: genericIsSubspan :: SrcSpan -> GenericQ (Maybe Bool)
+ Wingman.Judgements.SYB: mkQ1 :: forall a r f. (Data a, Data (f ())) => f () -> r -> (forall b. f b -> r) -> a -> r
+ Wingman.Judgements.SYB: sameTypeModuloLastApp :: forall a b. (Typeable a, Typeable b) => Bool
+ Wingman.Judgements.Theta: data Evidence
+ Wingman.Judgements.Theta: evidenceToHypothesis :: Evidence -> Hypothesis CType
+ Wingman.Judgements.Theta: evidenceToSubst :: Evidence -> TacticState -> TacticState
+ Wingman.Judgements.Theta: evidenceToThetaType :: [Evidence] -> Set CType
+ Wingman.Judgements.Theta: getEvidenceAtHole :: Tracked age SrcSpan -> Tracked age (LHsBinds GhcTc) -> [Evidence]
+ Wingman.Judgements.Theta: instance GHC.Show.Show Wingman.Judgements.Theta.Evidence
+ Wingman.Judgements.Theta: mkEvidence :: PredType -> [Evidence]
+ Wingman.KnownStrategies: deriveFmap :: TacticsM ()
+ Wingman.KnownStrategies: deriveMappend :: TacticsM ()
+ Wingman.KnownStrategies: deriveMempty :: TacticsM ()
+ Wingman.KnownStrategies: featureGuard :: Feature -> TacticsM a -> TacticsM a
+ Wingman.KnownStrategies: known :: String -> TacticsM () -> TacticsM ()
+ Wingman.KnownStrategies: knownStrategies :: TacticsM ()
+ Wingman.KnownStrategies.QuickCheck: Generator :: Integer -> HsExpr GhcPs -> Generator
+ Wingman.KnownStrategies.QuickCheck: [genExpr] :: Generator -> HsExpr GhcPs
+ Wingman.KnownStrategies.QuickCheck: [genRecursiveCount] :: Generator -> Integer
+ Wingman.KnownStrategies.QuickCheck: data Generator
+ Wingman.KnownStrategies.QuickCheck: deriveArbitrary :: TacticsM ()
+ Wingman.KnownStrategies.QuickCheck: doesTypeContain :: TyCon -> Type -> Bool
+ Wingman.KnownStrategies.QuickCheck: mkArbitraryCall :: TyCon -> Integer -> Type -> HsExpr GhcPs
+ Wingman.KnownStrategies.QuickCheck: mkGenerator :: TyCon -> [Type] -> DataCon -> Generator
+ Wingman.LanguageServer: WriteDiagnostics :: WriteDiagnostics
+ Wingman.LanguageServer: buildPatHy :: Provenance -> PatCompat GhcTc -> State Int (Hypothesis CType)
+ Wingman.LanguageServer: buildTopLevelHypothesis :: OccName -> [PatCompat GhcTc] -> State Int (Hypothesis CType)
+ Wingman.LanguageServer: data WriteDiagnostics
+ Wingman.LanguageServer: getAlreadyDestructed :: Tracked age SrcSpan -> Tracked age (LHsBinds GhcTc) -> Set OccName
+ Wingman.LanguageServer: getFeatureSet :: MonadLsp Config m => PluginId -> m FeatureSet
+ Wingman.LanguageServer: getIdeDynflags :: IdeState -> NormalizedFilePath -> MaybeT IO DynFlags
+ Wingman.LanguageServer: getRhsPosVals :: Tracked age RealSrcSpan -> Tracked age TypecheckedSource -> Hypothesis CType
+ Wingman.LanguageServer: getSpanAndTypeAtHole :: Tracked age Range -> Tracked age (HieASTs b) -> Maybe (Tracked age RealSrcSpan, b)
+ Wingman.LanguageServer: getTacticConfig :: MonadLsp Config m => PluginId -> m Config
+ Wingman.LanguageServer: instance Control.DeepSeq.NFData Wingman.LanguageServer.WriteDiagnostics
+ Wingman.LanguageServer: instance Data.Binary.Class.Binary Wingman.LanguageServer.WriteDiagnostics
+ Wingman.LanguageServer: instance Data.Hashable.Class.Hashable Wingman.LanguageServer.WriteDiagnostics
+ Wingman.LanguageServer: instance GHC.Classes.Eq Wingman.LanguageServer.WriteDiagnostics
+ Wingman.LanguageServer: instance GHC.Generics.Generic Wingman.LanguageServer.WriteDiagnostics
+ Wingman.LanguageServer: instance GHC.Show.Show Wingman.LanguageServer.WriteDiagnostics
+ Wingman.LanguageServer: isRhsHole :: Tracked age RealSrcSpan -> Tracked age TypecheckedSource -> Bool
+ Wingman.LanguageServer: judgementForHole :: IdeState -> NormalizedFilePath -> Tracked 'Current Range -> FeatureSet -> MaybeT IO (Tracked 'Current Range, Judgement, Context, DynFlags)
+ Wingman.LanguageServer: mkDerivedConHypothesis :: Provenance -> ConLike -> [Type] -> [(Int, PatCompat GhcTc)] -> State Int (Hypothesis CType)
+ Wingman.LanguageServer: mkDerivedRecordHypothesis :: Provenance -> ConLike -> [Type] -> HsRecFields GhcTc (PatCompat GhcTc) -> State Int (Hypothesis CType)
+ Wingman.LanguageServer: mkDiagnostic :: DiagnosticSeverity -> Range -> Diagnostic
+ Wingman.LanguageServer: mkFakeVar :: State Int OccName
+ Wingman.LanguageServer: mkIdHypothesis :: Id -> Provenance -> Hypothesis CType
+ Wingman.LanguageServer: mkJudgementAndContext :: FeatureSet -> Type -> TrackedStale Bindings -> Tracked 'Current RealSrcSpan -> TrackedStale TcGblEnv -> ExternalPackageState -> KnownThings -> Maybe (Judgement, Context)
+ Wingman.LanguageServer: mkShowMessageParams :: UserFacingMessage -> ShowMessageParams
+ Wingman.LanguageServer: properties :: Properties '[ 'PropertyKey "hole_severity" ('TEnum (Maybe DiagnosticSeverity)), 'PropertyKey "max_use_ctor_actions" 'TInteger, 'PropertyKey "features" 'TString, 'PropertyKey "timeout_duration" 'TInteger]
+ Wingman.LanguageServer: runCurrentIde :: forall a r. (r ~ RuleResult a, Eq a, Hashable a, Binary a, Show a, Typeable a, NFData a, Show r, Typeable r, NFData r) => IdeState -> NormalizedFilePath -> a -> MaybeT IO (Tracked 'Current r)
+ Wingman.LanguageServer: runIde :: IdeState -> Action a -> IO a
+ Wingman.LanguageServer: runStaleIde :: forall a r. (r ~ RuleResult a, Eq a, Hashable a, Binary a, Show a, Typeable a, NFData a, Show r, Typeable r, NFData r) => IdeState -> NormalizedFilePath -> a -> MaybeT IO (TrackedStale r)
+ Wingman.LanguageServer: showLspMessage :: MonadLsp cfg m => ShowMessageParams -> m ()
+ Wingman.LanguageServer: spliceProvenance :: Hypothesis a -> Hypothesis a -> Hypothesis a
+ Wingman.LanguageServer: tacticDesc :: Text -> Text
+ Wingman.LanguageServer: tcCommandName :: TacticCommand -> Text
+ Wingman.LanguageServer: ufmSeverity :: UserFacingMessage -> MessageType
+ Wingman.LanguageServer: unsafeRunStaleIde :: forall a r. (r ~ RuleResult a, Eq a, Hashable a, Binary a, Show a, Typeable a, NFData a, Show r, Typeable r, NFData r) => IdeState -> NormalizedFilePath -> a -> MaybeT IO r
+ Wingman.LanguageServer: wingmanRules :: PluginId -> Rules ()
+ Wingman.LanguageServer.TacticProviders: TacticParams :: Uri -> Tracked 'Current Range -> Text -> TacticParams
+ Wingman.LanguageServer.TacticProviders: TacticProviderData :: DynFlags -> Config -> PluginId -> Uri -> Tracked 'Current Range -> Judgement -> TacticProviderData
+ Wingman.LanguageServer.TacticProviders: [tp_file] :: TacticParams -> Uri
+ Wingman.LanguageServer.TacticProviders: [tp_range] :: TacticParams -> Tracked 'Current Range
+ Wingman.LanguageServer.TacticProviders: [tp_var_name] :: TacticParams -> Text
+ Wingman.LanguageServer.TacticProviders: [tpd_config] :: TacticProviderData -> Config
+ Wingman.LanguageServer.TacticProviders: [tpd_dflags] :: TacticProviderData -> DynFlags
+ Wingman.LanguageServer.TacticProviders: [tpd_jdg] :: TacticProviderData -> Judgement
+ Wingman.LanguageServer.TacticProviders: [tpd_plid] :: TacticProviderData -> PluginId
+ Wingman.LanguageServer.TacticProviders: [tpd_range] :: TacticProviderData -> Tracked 'Current Range
+ Wingman.LanguageServer.TacticProviders: [tpd_uri] :: TacticProviderData -> Uri
+ Wingman.LanguageServer.TacticProviders: commandProvider :: TacticCommand -> TacticProvider
+ Wingman.LanguageServer.TacticProviders: commandTactic :: TacticCommand -> OccName -> TacticsM ()
+ Wingman.LanguageServer.TacticProviders: data TacticParams
+ Wingman.LanguageServer.TacticProviders: data TacticProviderData
+ Wingman.LanguageServer.TacticProviders: instance Data.Aeson.Types.FromJSON.FromJSON Wingman.LanguageServer.TacticProviders.TacticParams
+ Wingman.LanguageServer.TacticProviders: instance Data.Aeson.Types.ToJSON.ToJSON Wingman.LanguageServer.TacticProviders.TacticParams
+ Wingman.LanguageServer.TacticProviders: instance GHC.Classes.Eq Wingman.LanguageServer.TacticProviders.TacticParams
+ Wingman.LanguageServer.TacticProviders: instance GHC.Generics.Generic Wingman.LanguageServer.TacticProviders.TacticParams
+ Wingman.LanguageServer.TacticProviders: instance GHC.Show.Show Wingman.LanguageServer.TacticProviders.TacticParams
+ Wingman.LanguageServer.TacticProviders: tcCommandId :: TacticCommand -> CommandId
+ Wingman.Machinery: Penalize :: a -> Penalize a
+ Wingman.Machinery: Reward :: a -> Reward a
+ Wingman.Machinery: assoc23 :: (a, b, c) -> (a, (b, c))
+ Wingman.Machinery: attemptWhen :: TacticsM a -> TacticsM a -> Bool -> TacticsM a
+ Wingman.Machinery: instance GHC.Classes.Eq a => GHC.Classes.Eq (Wingman.Machinery.Penalize a)
+ Wingman.Machinery: instance GHC.Classes.Eq a => GHC.Classes.Eq (Wingman.Machinery.Reward a)
+ Wingman.Machinery: instance GHC.Classes.Ord a => GHC.Classes.Ord (Wingman.Machinery.Penalize a)
+ Wingman.Machinery: instance GHC.Classes.Ord a => GHC.Classes.Ord (Wingman.Machinery.Reward a)
+ Wingman.Machinery: instance GHC.Show.Show a => GHC.Show.Show (Wingman.Machinery.Penalize a)
+ Wingman.Machinery: instance GHC.Show.Show a => GHC.Show.Show (Wingman.Machinery.Reward a)
+ Wingman.Machinery: mappingExtract :: Functor m => (ext -> ext) -> TacticT jdg ext err s m a -> TacticT jdg ext err s m a
+ Wingman.Machinery: markRecursion :: Functor m => TacticT jdg (Synthesized ext) err s m a -> TacticT jdg (Synthesized ext) err s m a
+ Wingman.Machinery: methodHypothesis :: PredType -> Maybe [HyInfo CType]
+ Wingman.Machinery: newSubgoal :: Judgement -> Rule
+ Wingman.Machinery: newtype Penalize a
+ Wingman.Machinery: newtype Reward a
+ Wingman.Machinery: peek :: (ext -> TacticT jdg ext err s m ()) -> TacticT jdg ext err s m ()
+ Wingman.Machinery: requireConcreteHole :: TacticsM a -> TacticsM a
+ Wingman.Machinery: runTactic :: Context -> Judgement -> TacticsM () -> Either [TacticError] RunTacticResults
+ Wingman.Machinery: scoreSolution :: Synthesized (LHsExpr GhcPs) -> Judgement -> [Judgement] -> (Penalize Int, Reward Bool, Penalize Int, Penalize Int, Reward Int, Penalize Int, Penalize Int)
+ Wingman.Machinery: solutionSize :: LHsExpr GhcPs -> Int
+ Wingman.Machinery: substCTy :: TCvSubst -> CType -> CType
+ Wingman.Machinery: tracePrim :: String -> Trace
+ Wingman.Machinery: tracing :: Functor m => String -> TacticT jdg (Synthesized ext) err s m a -> TacticT jdg (Synthesized ext) err s m a
+ Wingman.Machinery: try' :: Functor m => TacticT jdg ext err s m () -> TacticT jdg ext err s m ()
+ Wingman.Machinery: tryUnifyUnivarsButNotSkolems :: Set TyVar -> CType -> CType -> Maybe TCvSubst
+ Wingman.Machinery: unify :: CType -> CType -> RuleM ()
+ Wingman.Machinery: updateSubst :: TCvSubst -> TacticState -> TacticState
+ Wingman.Naming: filterReplace :: (a -> Bool) -> a -> [a] -> [a]
+ Wingman.Naming: getInScope :: Map OccName a -> [OccName]
+ Wingman.Naming: mkGoodName :: Set OccName -> Type -> OccName
+ Wingman.Naming: mkManyGoodNames :: (Traversable t, Monad m) => Set OccName -> t Type -> m (t OccName)
+ Wingman.Naming: mkTyConName :: TyCon -> String
+ Wingman.Naming: mkTyName :: Type -> String
+ Wingman.Plugin: Auto :: TacticCommand
+ Wingman.Plugin: Destruct :: TacticCommand
+ Wingman.Plugin: DestructAll :: TacticCommand
+ Wingman.Plugin: DestructLambdaCase :: TacticCommand
+ Wingman.Plugin: Homomorphism :: TacticCommand
+ Wingman.Plugin: HomomorphismLambdaCase :: TacticCommand
+ Wingman.Plugin: Intros :: TacticCommand
+ Wingman.Plugin: Refine :: TacticCommand
+ Wingman.Plugin: UseDataCon :: TacticCommand
+ Wingman.Plugin: data TacticCommand
+ Wingman.Plugin: descriptor :: PluginId -> PluginDescriptor IdeState
+ Wingman.Plugin: tacticTitle :: TacticCommand -> Text -> Text
+ Wingman.Range: rangeToRealSrcSpan :: String -> Range -> RealSrcSpan
+ Wingman.Range: rangeToSrcSpan :: String -> Range -> SrcSpan
+ Wingman.Simplify: simplify :: LHsExpr GhcPs -> LHsExpr GhcPs
+ Wingman.Tactics: allNames :: Judgement -> Set OccName
+ Wingman.Tactics: apply :: HyInfo CType -> TacticsM ()
+ Wingman.Tactics: applyByName :: OccName -> TacticsM ()
+ Wingman.Tactics: applyMethod :: Class -> PredType -> OccName -> TacticsM ()
+ Wingman.Tactics: assume :: OccName -> TacticsM ()
+ Wingman.Tactics: assumption :: TacticsM ()
+ Wingman.Tactics: attemptOn :: (Judgement -> [a]) -> (a -> TacticsM ()) -> TacticsM ()
+ Wingman.Tactics: auto' :: Int -> TacticsM ()
+ Wingman.Tactics: destruct :: HyInfo CType -> TacticsM ()
+ Wingman.Tactics: destructAll :: TacticsM ()
+ Wingman.Tactics: destructAuto :: HyInfo CType -> TacticsM ()
+ Wingman.Tactics: destructLambdaCase :: TacticsM ()
+ Wingman.Tactics: destructOrHomoAuto :: HyInfo CType -> TacticsM ()
+ Wingman.Tactics: homo :: HyInfo CType -> TacticsM ()
+ Wingman.Tactics: homoLambdaCase :: TacticsM ()
+ Wingman.Tactics: intros :: TacticsM ()
+ Wingman.Tactics: localTactic :: TacticsM a -> (Judgement -> Judgement) -> TacticsM a
+ Wingman.Tactics: matching :: (Judgement -> TacticsM ()) -> TacticsM ()
+ Wingman.Tactics: overAlgebraicTerms :: (HyInfo CType -> TacticsM ()) -> TacticsM ()
+ Wingman.Tactics: overFunctions :: (HyInfo CType -> TacticsM ()) -> TacticsM ()
+ Wingman.Tactics: recursion :: TacticsM ()
+ Wingman.Tactics: refine :: TacticsM ()
+ Wingman.Tactics: requireNewHoles :: TacticsM () -> TacticsM ()
+ Wingman.Tactics: restrictPositionForApplication :: TacticsM () -> TacticsM () -> TacticsM ()
+ Wingman.Tactics: runTactic :: Context -> Judgement -> TacticsM () -> Either [TacticError] RunTacticResults
+ Wingman.Tactics: split :: TacticsM ()
+ Wingman.Tactics: splitAuto :: TacticsM ()
+ Wingman.Tactics: splitConLike :: ConLike -> TacticsM ()
+ Wingman.Tactics: splitDataCon :: DataCon -> TacticsM ()
+ Wingman.Tactics: splitSingle :: TacticsM ()
+ Wingman.Tactics: userSplit :: OccName -> TacticsM ()
+ Wingman.Types: AgdaMatch :: [Pat GhcPs] -> HsExpr GhcPs -> AgdaMatch
+ Wingman.Types: AlreadyDestructed :: DisallowReason
+ Wingman.Types: Auto :: TacticCommand
+ Wingman.Types: CType :: Type -> CType
+ Wingman.Types: ClassMethodPrv :: Uniquely Class -> Provenance
+ Wingman.Types: Config :: FeatureSet -> Int -> Int -> Config
+ Wingman.Types: Context :: [(OccName, CType)] -> [(OccName, CType)] -> FeatureSet -> KnownThings -> InstEnvs -> Set CType -> Context
+ Wingman.Types: Destruct :: TacticCommand
+ Wingman.Types: DestructAll :: TacticCommand
+ Wingman.Types: DestructLambdaCase :: TacticCommand
+ Wingman.Types: DisallowedPrv :: DisallowReason -> Provenance -> Provenance
+ Wingman.Types: ExtractM :: Reader Context a -> ExtractM a
+ Wingman.Types: GoalMismatch :: String -> CType -> TacticError
+ Wingman.Types: Homomorphism :: TacticCommand
+ Wingman.Types: HomomorphismLambdaCase :: TacticCommand
+ Wingman.Types: HyInfo :: OccName -> Provenance -> a -> HyInfo a
+ Wingman.Types: Hypothesis :: [HyInfo a] -> Hypothesis a
+ Wingman.Types: IncorrectDataCon :: DataCon -> TacticError
+ Wingman.Types: InfrastructureError :: Text -> UserFacingMessage
+ Wingman.Types: Intros :: TacticCommand
+ Wingman.Types: Judgement :: !Hypothesis a -> !Bool -> !Bool -> !Bool -> !a -> Judgement' a
+ Wingman.Types: KnownThings :: Class -> Class -> KnownThings
+ Wingman.Types: NoApplicableTactic :: TacticError
+ Wingman.Types: NoProgress :: TacticError
+ Wingman.Types: NotInScope :: OccName -> TacticError
+ Wingman.Types: NothingToDo :: UserFacingMessage
+ Wingman.Types: PatVal :: Maybe OccName -> Set OccName -> Uniquely ConLike -> Int -> PatVal
+ Wingman.Types: PatternMatchPrv :: PatVal -> Provenance
+ Wingman.Types: RecursionOnWrongParam :: OccName -> Int -> OccName -> TacticError
+ Wingman.Types: RecursiveCall :: DisallowReason
+ Wingman.Types: RecursivePrv :: Provenance
+ Wingman.Types: Refine :: TacticCommand
+ Wingman.Types: Rose :: Tree a -> Rose a
+ Wingman.Types: RunTacticResults :: Trace -> LHsExpr GhcPs -> [Synthesized (LHsExpr GhcPs)] -> Judgement -> Context -> RunTacticResults
+ Wingman.Types: Shadowed :: DisallowReason
+ Wingman.Types: Synthesized :: Trace -> Hypothesis CType -> Set OccName -> Sum Int -> a -> Synthesized a
+ Wingman.Types: TacticErrors :: UserFacingMessage
+ Wingman.Types: TacticState :: !Set TyVar -> !TCvSubst -> !UniqSupply -> TacticState
+ Wingman.Types: TimedOut :: UserFacingMessage
+ Wingman.Types: TooPolymorphic :: TacticError
+ Wingman.Types: TopLevelArgPrv :: OccName -> Int -> Int -> Provenance
+ Wingman.Types: UndefinedHypothesis :: OccName -> TacticError
+ Wingman.Types: UnhelpfulDestruct :: OccName -> TacticError
+ Wingman.Types: UnhelpfulSplit :: OccName -> TacticError
+ Wingman.Types: UnificationError :: CType -> CType -> TacticError
+ Wingman.Types: Uniquely :: a -> Uniquely a
+ Wingman.Types: UnsolvedSubgoals :: [Judgement] -> TacticError
+ Wingman.Types: UseDataCon :: TacticCommand
+ Wingman.Types: UserPrv :: Provenance
+ Wingman.Types: WrongBranch :: Int -> DisallowReason
+ Wingman.Types: [_jBlacklistDestruct] :: Judgement' a -> !Bool
+ Wingman.Types: [_jGoal] :: Judgement' a -> !a
+ Wingman.Types: [_jHypothesis] :: Judgement' a -> !Hypothesis a
+ Wingman.Types: [_jIsTopHole] :: Judgement' a -> !Bool
+ Wingman.Types: [_jWhitelistSplit] :: Judgement' a -> !Bool
+ Wingman.Types: [amBody] :: AgdaMatch -> HsExpr GhcPs
+ Wingman.Types: [amPats] :: AgdaMatch -> [Pat GhcPs]
+ Wingman.Types: [cfg_feature_set] :: Config -> FeatureSet
+ Wingman.Types: [cfg_max_use_ctor_actions] :: Config -> Int
+ Wingman.Types: [cfg_timeout_seconds] :: Config -> Int
+ Wingman.Types: [ctxDefiningFuncs] :: Context -> [(OccName, CType)]
+ Wingman.Types: [ctxFeatureSet] :: Context -> FeatureSet
+ Wingman.Types: [ctxInstEnvs] :: Context -> InstEnvs
+ Wingman.Types: [ctxKnownThings] :: Context -> KnownThings
+ Wingman.Types: [ctxModuleFuncs] :: Context -> [(OccName, CType)]
+ Wingman.Types: [ctxTheta] :: Context -> Set CType
+ Wingman.Types: [getViaUnique] :: Uniquely a -> a
+ Wingman.Types: [hi_name] :: HyInfo a -> OccName
+ Wingman.Types: [hi_provenance] :: HyInfo a -> Provenance
+ Wingman.Types: [hi_type] :: HyInfo a -> a
+ Wingman.Types: [kt_monoid] :: KnownThings -> Class
+ Wingman.Types: [kt_semigroup] :: KnownThings -> Class
+ Wingman.Types: [pv_ancestry] :: PatVal -> Set OccName
+ Wingman.Types: [pv_datacon] :: PatVal -> Uniquely ConLike
+ Wingman.Types: [pv_position] :: PatVal -> Int
+ Wingman.Types: [pv_scrutinee] :: PatVal -> Maybe OccName
+ Wingman.Types: [rtr_ctx] :: RunTacticResults -> Context
+ Wingman.Types: [rtr_extract] :: RunTacticResults -> LHsExpr GhcPs
+ Wingman.Types: [rtr_jdg] :: RunTacticResults -> Judgement
+ Wingman.Types: [rtr_other_solns] :: RunTacticResults -> [Synthesized (LHsExpr GhcPs)]
+ Wingman.Types: [rtr_trace] :: RunTacticResults -> Trace
+ Wingman.Types: [syn_recursion_count] :: Synthesized a -> Sum Int
+ Wingman.Types: [syn_scoped] :: Synthesized a -> Hypothesis CType
+ Wingman.Types: [syn_trace] :: Synthesized a -> Trace
+ Wingman.Types: [syn_used_vals] :: Synthesized a -> Set OccName
+ Wingman.Types: [syn_val] :: Synthesized a -> a
+ Wingman.Types: [ts_skolems] :: TacticState -> !Set TyVar
+ Wingman.Types: [ts_unifier] :: TacticState -> !TCvSubst
+ Wingman.Types: [ts_unique_gen] :: TacticState -> !UniqSupply
+ Wingman.Types: [unCType] :: CType -> Type
+ Wingman.Types: [unExtractM] :: ExtractM a -> Reader Context a
+ Wingman.Types: [unHypothesis] :: Hypothesis a -> [HyInfo a]
+ Wingman.Types: data AgdaMatch
+ Wingman.Types: data Config
+ Wingman.Types: data Context
+ Wingman.Types: data DisallowReason
+ Wingman.Types: data HyInfo a
+ Wingman.Types: data Judgement' a
+ Wingman.Types: data KnownThings
+ Wingman.Types: data Name
+ Wingman.Types: data OccName
+ Wingman.Types: data PatVal
+ Wingman.Types: data Provenance
+ Wingman.Types: data RunTacticResults
+ Wingman.Types: data Synthesized a
+ Wingman.Types: data TacticCommand
+ Wingman.Types: data TacticError
+ Wingman.Types: data TacticState
+ Wingman.Types: data Type
+ Wingman.Types: data UserFacingMessage
+ Wingman.Types: defaultTacticState :: TacticState
+ Wingman.Types: dropEveryOther :: [a] -> [a]
+ Wingman.Types: emptyContext :: Context
+ Wingman.Types: freshUnique :: MonadState TacticState m => m Unique
+ Wingman.Types: instance (GHC.Classes.Eq a, GHC.Base.Monoid a) => GHC.Base.Monoid (Wingman.Types.Rose a)
+ Wingman.Types: instance (GHC.Classes.Eq a, GHC.Base.Monoid a) => GHC.Base.Semigroup (Wingman.Types.Rose a)
+ Wingman.Types: instance Control.Monad.Reader.Class.MonadReader Wingman.Types.Context Wingman.Types.ExtractM
+ Wingman.Types: instance Data.Foldable.Foldable Wingman.Types.Synthesized
+ Wingman.Types: instance Data.Traversable.Traversable Wingman.Types.Synthesized
+ Wingman.Types: instance GHC.Base.Applicative Wingman.Types.ExtractM
+ Wingman.Types: instance GHC.Base.Applicative Wingman.Types.Synthesized
+ Wingman.Types: instance GHC.Base.Functor Wingman.Types.ExtractM
+ Wingman.Types: instance GHC.Base.Functor Wingman.Types.HyInfo
+ Wingman.Types: instance GHC.Base.Functor Wingman.Types.Hypothesis
+ Wingman.Types: instance GHC.Base.Functor Wingman.Types.Judgement'
+ Wingman.Types: instance GHC.Base.Functor Wingman.Types.Rose
+ Wingman.Types: instance GHC.Base.Functor Wingman.Types.Synthesized
+ Wingman.Types: instance GHC.Base.Monad Wingman.Types.ExtractM
+ Wingman.Types: instance GHC.Base.Monoid (Wingman.Types.Hypothesis a)
+ Wingman.Types: instance GHC.Base.Semigroup (Wingman.Types.Hypothesis a)
+ Wingman.Types: instance GHC.Classes.Eq Wingman.Types.CType
+ Wingman.Types: instance GHC.Classes.Eq Wingman.Types.DisallowReason
+ Wingman.Types: instance GHC.Classes.Eq Wingman.Types.PatVal
+ Wingman.Types: instance GHC.Classes.Eq Wingman.Types.Provenance
+ Wingman.Types: instance GHC.Classes.Eq Wingman.Types.TacticCommand
+ Wingman.Types: instance GHC.Classes.Eq Wingman.Types.TacticError
+ Wingman.Types: instance GHC.Classes.Eq Wingman.Types.UserFacingMessage
+ Wingman.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Wingman.Types.HyInfo a)
+ Wingman.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Wingman.Types.Hypothesis a)
+ Wingman.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Wingman.Types.Judgement' a)
+ Wingman.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Wingman.Types.Rose a)
+ Wingman.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Wingman.Types.Synthesized a)
+ Wingman.Types: instance GHC.Classes.Ord Wingman.Types.CType
+ Wingman.Types: instance GHC.Classes.Ord Wingman.Types.DisallowReason
+ Wingman.Types: instance GHC.Classes.Ord Wingman.Types.PatVal
+ Wingman.Types: instance GHC.Classes.Ord Wingman.Types.Provenance
+ Wingman.Types: instance GHC.Classes.Ord Wingman.Types.TacticCommand
+ Wingman.Types: instance GHC.Classes.Ord a => GHC.Classes.Ord (Wingman.Types.HyInfo a)
+ Wingman.Types: instance GHC.Classes.Ord a => GHC.Classes.Ord (Wingman.Types.Hypothesis a)
+ Wingman.Types: instance GHC.Enum.Bounded Wingman.Types.TacticCommand
+ Wingman.Types: instance GHC.Enum.Enum Wingman.Types.TacticCommand
+ Wingman.Types: instance GHC.Generics.Generic (Wingman.Types.HyInfo a)
+ Wingman.Types: instance GHC.Generics.Generic (Wingman.Types.Hypothesis a)
+ Wingman.Types: instance GHC.Generics.Generic (Wingman.Types.Judgement' a)
+ Wingman.Types: instance GHC.Generics.Generic (Wingman.Types.Rose a)
+ Wingman.Types: instance GHC.Generics.Generic (Wingman.Types.Synthesized a)
+ Wingman.Types: instance GHC.Generics.Generic Wingman.Types.DisallowReason
+ Wingman.Types: instance GHC.Generics.Generic Wingman.Types.PatVal
+ Wingman.Types: instance GHC.Generics.Generic Wingman.Types.Provenance
+ Wingman.Types: instance GHC.Generics.Generic Wingman.Types.TacticState
+ Wingman.Types: instance GHC.Show.Show (GHC.Hs.Decls.HsDecl GHC.Hs.Extension.GhcPs)
+ Wingman.Types: instance GHC.Show.Show (GHC.Hs.Expr.HsExpr GHC.Hs.Extension.GhcPs)
+ Wingman.Types: instance GHC.Show.Show (GHC.Hs.Pat.Pat GHC.Hs.Extension.GhcPs)
+ Wingman.Types: instance GHC.Show.Show (GHC.Hs.Types.LHsSigType GHC.Hs.Extension.GhcPs)
+ Wingman.Types: instance GHC.Show.Show (Wingman.Types.Rose GHC.Base.String)
+ Wingman.Types: instance GHC.Show.Show Class.Class
+ Wingman.Types: instance GHC.Show.Show ConLike.ConLike
+ Wingman.Types: instance GHC.Show.Show DataCon.DataCon
+ Wingman.Types: instance GHC.Show.Show Name.Name
+ Wingman.Types: instance GHC.Show.Show OccName.OccName
+ Wingman.Types: instance GHC.Show.Show TyCoRep.Type
+ Wingman.Types: instance GHC.Show.Show TyCoSubst.TCvSubst
+ Wingman.Types: instance GHC.Show.Show TyCon.TyCon
+ Wingman.Types: instance GHC.Show.Show UniqSupply.UniqSupply
+ Wingman.Types: instance GHC.Show.Show Var.Var
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.AgdaMatch
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.CType
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.Context
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.DisallowReason
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.PatVal
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.Provenance
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.RunTacticResults
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.TacticCommand
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.TacticError
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.TacticState
+ Wingman.Types: instance GHC.Show.Show Wingman.Types.UserFacingMessage
+ Wingman.Types: instance GHC.Show.Show a => GHC.Show.Show (Wingman.Types.HyInfo a)
+ Wingman.Types: instance GHC.Show.Show a => GHC.Show.Show (Wingman.Types.Hypothesis a)
+ Wingman.Types: instance GHC.Show.Show a => GHC.Show.Show (Wingman.Types.Judgement' a)
+ Wingman.Types: instance GHC.Show.Show a => GHC.Show.Show (Wingman.Types.Synthesized a)
+ Wingman.Types: instance GHC.Show.Show a => GHC.Show.Show (Wingman.Types.Uniquely a)
+ Wingman.Types: instance Refinery.ProofState.MonadExtract (Wingman.Types.Synthesized (GHC.Hs.Expr.LHsExpr GHC.Hs.Extension.GhcPs)) Wingman.Types.ExtractM
+ Wingman.Types: instance Unique.Uniquable a => GHC.Classes.Eq (Wingman.Types.Uniquely a)
+ Wingman.Types: instance Unique.Uniquable a => GHC.Classes.Ord (Wingman.Types.Uniquely a)
+ Wingman.Types: mapTrace :: (Trace -> Trace) -> Synthesized a -> Synthesized a
+ Wingman.Types: newtype CType
+ Wingman.Types: newtype ExtractM a
+ Wingman.Types: newtype Hypothesis a
+ Wingman.Types: newtype Rose a
+ Wingman.Types: newtype Uniquely a
+ Wingman.Types: overProvenance :: (Provenance -> Provenance) -> HyInfo a -> HyInfo a
+ Wingman.Types: rose :: (Eq a, Monoid a) => a -> [Rose a] -> Rose a
+ Wingman.Types: tacticTitle :: TacticCommand -> Text -> Text
+ Wingman.Types: type Judgement = Judgement' CType
+ Wingman.Types: type Rule = RuleM (Synthesized (LHsExpr GhcPs))
+ Wingman.Types: type RuleM = RuleT Judgement (Synthesized (LHsExpr GhcPs)) TacticError TacticState ExtractM
+ Wingman.Types: type Span = RealSrcSpan
+ Wingman.Types: type TacticsM = TacticT Judgement (Synthesized (LHsExpr GhcPs)) TacticError TacticState ExtractM
+ Wingman.Types: type Trace = Rose String
+ Wingman.Types: type TyVar = Var
+ Wingman.Types: unsafeDefaultUniqueSupply :: UniqSupply

Files

README.md view
@@ -34,6 +34,78 @@ [hls]: https://github.com/haskell/haskell-language-server/releases  +## Editor Configuration++### Enabling Jump to Hole++Set the `haskell.plugin.tactics.config.hole_severity` config option to `4`, or+`hint` if your editor uses a GUI for its configuration. This has the potential+to negatively impact performance --- please holler if you notice any appreciable+slowdown by enabling this option.+++### coc.nvim++The following vimscript maps Wingman code-actions to your leader key:++```viml+" use [h and ]h to navigate between holes+nnoremap <silent> [h :<C-U>call CocActionAsync('diagnosticPrevious', 'hint')<CR>+nnoremap <silent> ]h :<C-U>call <SID>JumpToNextHole()<CR>++" <leader>d to perform a pattern match, <leader>n to fill a hole+nnoremap <silent> <leader>d  :<C-u>set operatorfunc=<SID>WingmanDestruct<CR>g@l+nnoremap <silent> <leader>n  :<C-u>set operatorfunc=<SID>WingmanFillHole<CR>g@l++" beta only+nnoremap <silent> <leader>r  :<C-u>set operatorfunc=<SID>WingmanRefine<CR>g@l+nnoremap <silent> <leader>c  :<C-u>set operatorfunc=<SID>WingmanUseCtor<CR>g@l+nnoremap <silent> <leader>a  :<C-u>set operatorfunc=<SID>WingmanDestructAll<CR>g@l+++function! s:JumpToNextHole()+  call CocActionAsync('diagnosticNext', 'hint')+endfunction++function! s:GotoNextHole()+  " wait for the hole diagnostics to reload+  sleep 500m+  " and then jump to the next hole+  normal 0+  call <SID>JumpToNextHole()+endfunction++function! s:WingmanRefine(type)+  call CocAction('codeAction', a:type, ['refactor.wingman.refine'])+  call <SID>GotoNextHole()+endfunction++function! s:WingmanDestruct(type)+  call CocAction('codeAction', a:type, ['refactor.wingman.caseSplit'])+  call <SID>GotoNextHole()+endfunction++function! s:WingmanDestructAll(type)+  call CocAction('codeAction', a:type, ['refactor.wingman.splitFuncArgs'])+  call <SID>GotoNextHole()+endfunction++function! s:WingmanFillHole(type)+  call CocAction('codeAction', a:type, ['refactor.wingman.fillHole'])+  call <SID>GotoNextHole()+endfunction++function! s:WingmanUseCtor(type)+  call CocAction('codeAction', a:type, ['refactor.wingman.useConstructor'])+  call <SID>GotoNextHole()+endfunction+```++### Other Editors++Please open a PR if you have a working configuration!++ ## Features  * [Type-directed code synthesis][auto], including pattern matching and recursion
hls-tactics-plugin.cabal view
@@ -1,7 +1,7 @@-cabal-version:      2.2+cabal-version:      2.4 category:           Development name:               hls-tactics-plugin-version:            1.0.0.0+version:            1.1.0.0 synopsis:           Wingman plugin for Haskell Language Server description:        Please see README.md author:             Sandy Maguire, Reed Mullanix@@ -14,7 +14,10 @@ build-type:         Simple extra-source-files:   README.md---   ChangeLog.md+  test/golden/*.cabal+  test/golden/*.expected+  test/golden/*.hs+  test/golden/*.yaml  flag pedantic   description: Enable -Werror@@ -22,39 +25,44 @@   manual:      True  library-  hs-source-dirs:   src+  hs-source-dirs:     src   exposed-modules:     Ide.Plugin.Tactic-    Ide.Plugin.Tactic.Auto-    Ide.Plugin.Tactic.CaseSplit-    Ide.Plugin.Tactic.CodeGen-    Ide.Plugin.Tactic.CodeGen.Utils-    Ide.Plugin.Tactic.Context-    Ide.Plugin.Tactic.Debug-    Ide.Plugin.Tactic.FeatureSet-    Ide.Plugin.Tactic.GHC-    Ide.Plugin.Tactic.Judgements-    Ide.Plugin.Tactic.KnownStrategies-    Ide.Plugin.Tactic.KnownStrategies.QuickCheck-    Ide.Plugin.Tactic.LanguageServer-    Ide.Plugin.Tactic.LanguageServer.TacticProviders-    Ide.Plugin.Tactic.Machinery-    Ide.Plugin.Tactic.Naming-    Ide.Plugin.Tactic.Range-    Ide.Plugin.Tactic.Simplify-    Ide.Plugin.Tactic.Tactics-    Ide.Plugin.Tactic.Types-    Ide.Plugin.Tactic.TestTypes+    Wingman.Auto+    Wingman.CaseSplit+    Wingman.CodeGen+    Wingman.CodeGen.Utils+    Wingman.Context+    Wingman.Debug+    Wingman.FeatureSet+    Wingman.GHC+    Wingman.Judgements+    Wingman.Judgements.SYB+    Wingman.Judgements.Theta+    Wingman.KnownStrategies+    Wingman.KnownStrategies.QuickCheck+    Wingman.LanguageServer+    Wingman.LanguageServer.TacticProviders+    Wingman.Machinery+    Wingman.Naming+    Wingman.Plugin+    Wingman.Range+    Wingman.Simplify+    Wingman.Tactics+    Wingman.Types    ghc-options:-    -Wno-name-shadowing -Wredundant-constraints -Wno-unticked-promoted-constructors+    -Wall -Wno-name-shadowing -Wredundant-constraints+    -Wno-unticked-promoted-constructors+   if flag(pedantic)     ghc-options: -Werror    build-depends:     , aeson-    , base           >=4.12 && <5+    , base                  >=4.12    && <5     , containers+    , deepseq     , directory     , extra     , filepath@@ -64,80 +72,85 @@     , ghc-boot-th     , ghc-exactprint     , ghc-source-gen-    , ghcide         ^>= 1.0.0.0-    , lsp-    , hls-plugin-api ^>= 1.0.0.0+    , ghcide                ^>=1.2+    , hls-plugin-api        ^>=1.1     , lens+    , lsp     , mtl-    , refinery       ^>=0.3-    , retrie         >=0.1.1.0-    , shake          >=0.17.5+    , refinery              ^>=0.3+    , retrie                >=0.1.1.0+    , shake     , syb     , text     , transformers-    , deepseq--  default-language: Haskell2010-  default-extensions: DataKinds, TypeOperators-+    , unordered-containers -executable test-server   default-language:   Haskell2010-  build-depends:-    , base-    , data-default-    , ghcide-    , hls-tactics-plugin-    , hls-plugin-api-    , shake-  main-is: Server.hs-  hs-source-dirs: test-  ghc-options:-    "-with-rtsopts=-I0 -A128M"-    -threaded -Wall -Wno-name-shadowing -Wredundant-constraints+  default-extensions:+    DataKinds+    DeriveAnyClass+    DeriveDataTypeable+    DeriveFoldable+    DeriveFunctor+    DeriveGeneric+    DeriveTraversable+    DerivingStrategies+    DerivingVia+    FlexibleContexts+    FlexibleInstances+    GADTs+    GeneralizedNewtypeDeriving+    LambdaCase+    MultiParamTypeClasses+    NumDecimals+    OverloadedLabels+    PatternSynonyms+    ScopedTypeVariables+    TypeApplications+    TypeOperators+    ViewPatterns  test-suite tests-  type: exitcode-stdio-1.0-  main-is: Main.hs+  type:               exitcode-stdio-1.0+  main-is:            Main.hs   other-modules:     AutoTupleSpec-    GoldenSpec+    CodeAction.AutoSpec+    CodeAction.DestructAllSpec+    CodeAction.DestructSpec+    CodeAction.IntrosSpec+    CodeAction.RefineSpec+    CodeAction.UseDataConSpec+    ProviderSpec+    Spec     UnificationSpec-  hs-source-dirs:-      test-  ghc-options: -Wall -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N+    Utils++  hs-source-dirs:     test+  ghc-options:+    -Wall -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N+   build-depends:-      QuickCheck     , aeson     , base-    , bytestring-    , checkers     , containers-    , data-default     , deepseq     , directory     , filepath     , ghc-    , ghcide                >= 0.7.5.0-    , hie-bios+    , ghcide     , hls-plugin-api     , hls-tactics-plugin+    , hls-test-utils  ^>= 1.0     , hspec     , hspec-expectations     , lens-    , lsp-test     , lsp-types-    , megaparsec     , mtl-    , tasty-    , tasty-ant-xml           >=1.1.6-    , tasty-expected-failure-    , tasty-golden+    , QuickCheck+    , tasty-hspec     , tasty-hunit-    , tasty-rerun     , text-  build-tool-depends:-      hspec-discover:hspec-discover-    , hls-tactics-plugin:test-server -any-  default-language: Haskell2010 +  build-tool-depends: hspec-discover:hspec-discover -any+  default-language:   Haskell2010
src/Ide/Plugin/Tactic.hs view
@@ -1,11 +1,3 @@-{-# LANGUAGE FlexibleContexts    #-}-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE LambdaCase          #-}-{-# LANGUAGE NumDecimals         #-}-{-# LANGUAGE OverloadedStrings   #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications    #-}- -- | A plugin that uses tactics to synthesize code module Ide.Plugin.Tactic   ( descriptor@@ -13,259 +5,5 @@   , TacticCommand (..)   ) where -import           Bag                                              (bagToList,-                                                                   listToBag)-import           Control.Exception                                (evaluate)-import           Control.Monad-import           Control.Monad.Trans-import           Control.Monad.Trans.Maybe-import           Data.Aeson-import           Data.Bifunctor                                   (Bifunctor (bimap))-import           Data.Bool                                        (bool)-import           Data.Data                                        (Data)-import           Data.Generics.Aliases                            (mkQ)-import           Data.Generics.Schemes                            (everything)-import           Data.Maybe-import           Data.Monoid-import qualified Data.Text                                        as T-import           Data.Traversable-import           Development.IDE.Core.Shake                       (IdeState (..))-import           Development.IDE.GHC.Compat-import           Development.IDE.GHC.ExactPrint-import           Development.Shake.Classes-import           Ide.Plugin.Tactic.CaseSplit-import           Ide.Plugin.Tactic.FeatureSet                     (Feature (..),-                                                                   hasFeature)-import           Ide.Plugin.Tactic.GHC-import           Ide.Plugin.Tactic.LanguageServer-import           Ide.Plugin.Tactic.LanguageServer.TacticProviders-import           Ide.Plugin.Tactic.Range-import           Ide.Plugin.Tactic.Tactics-import           Ide.Plugin.Tactic.TestTypes-import           Ide.Plugin.Tactic.Types-import           Ide.Types-import           Language.LSP.Server-import           Language.LSP.Types-import           Language.LSP.Types.Capabilities-import           OccName-import           Prelude                                          hiding (span)-import           System.Timeout---descriptor :: PluginId -> PluginDescriptor IdeState-descriptor plId = (defaultPluginDescriptor plId)-    { pluginCommands-        = fmap (\tc ->-            PluginCommand-              (tcCommandId tc)-              (tacticDesc $ tcCommandName tc)-              (tacticCmd $ commandTactic tc))-              [minBound .. maxBound]-    , pluginHandlers =-        mkPluginHandler STextDocumentCodeAction codeActionProvider-    }----codeActionProvider :: PluginMethodHandler IdeState TextDocumentCodeAction-codeActionProvider state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) range _ctx)-  | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri = do-      features <- getFeatureSet (shakeExtras state)-      liftIO $ fromMaybeT (Right $ List []) $ do-        (_, jdg, _, dflags) <- judgementForHole state nfp range features-        actions <- lift $-          -- This foldMap is over the function monoid.-          foldMap commandProvider [minBound .. maxBound]-            dflags-            features-            plId-            uri-            range-            jdg-        pure $ Right $ List actions-codeActionProvider _ _ _ = pure $ Right $ List []---tacticCmd :: (OccName -> TacticsM ()) -> CommandFunction IdeState TacticParams-tacticCmd tac state (TacticParams uri range var_name)-  | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri = do-      features <- getFeatureSet (shakeExtras state)-      ccs <- getClientCapabilities-      res <- liftIO $ fromMaybeT (Right Nothing) $ do-        (range', jdg, ctx, dflags) <- judgementForHole state nfp range features-        let span = rangeToRealSrcSpan (fromNormalizedFilePath nfp) range'-        pm <- MaybeT $ useAnnotatedSource "tacticsCmd" state nfp--        timingOut 2e8 $ join $-          bimap (mkErr InvalidRequest . T.pack . show)-                (mkWorkspaceEdits span dflags ccs uri pm)-            $ runTactic ctx jdg $ tac $ mkVarOcc $ T.unpack var_name--      case res of-        Left err -> pure $ Left err-        Right medit -> do-          forM_ medit $ \edit ->-            sendRequest-              SWorkspaceApplyEdit-              (ApplyWorkspaceEditParams Nothing edit)-              (const $ pure ())-          pure $ Right Null-tacticCmd _ _ _ =-  pure $ Left $ mkErr InvalidRequest "Bad URI"---timingOut-    :: Int                     -- ^ Time in microseconds-    -> Either ResponseError a  -- ^ Computation to run-    -> MaybeT IO (Either ResponseError a)-timingOut t m = do-  x <- lift $ timeout t $ evaluate m-  pure $ joinNote (mkErr InvalidRequest "timed out") x---mkErr :: ErrorCode -> T.Text -> ResponseError-mkErr code err = ResponseError code err Nothing---joinNote :: e -> Maybe (Either e a) -> Either e a-joinNote e Nothing  = Left e-joinNote _ (Just a) = a------------------------------------------------------------------------------------ | Turn a 'RunTacticResults' into concrete edits to make in the source--- document.-mkWorkspaceEdits-    :: RealSrcSpan-    -> DynFlags-    -> ClientCapabilities-    -> Uri-    -> Annotated ParsedSource-    -> RunTacticResults-    -> Either ResponseError (Maybe WorkspaceEdit)-mkWorkspaceEdits span dflags ccs uri pm rtr = do-  let g = graftHole (RealSrcSpan span) rtr-      response = transform dflags ccs uri g pm-   in case response of-        Right res -> Right $ Just res-        Left err  -> Left $ mkErr InternalError $ T.pack err------------------------------------------------------------------------------------ | Graft a 'RunTacticResults' into the correct place in an AST. Correctly--- deals with top-level holes, in which we might need to fiddle with the--- 'Match's that bind variables.-graftHole-    :: SrcSpan-    -> RunTacticResults-    -> Graft (Either String) ParsedSource-graftHole span rtr-  | _jIsTopHole (rtr_jdg rtr)-      = graftSmallestDeclsWithM span-      $ graftDecl span $ \pats ->-        splitToDecl (fst $ last $ ctxDefiningFuncs $ rtr_ctx rtr)-      $ iterateSplit-      $ mkFirstAgda (fmap unXPat pats)-      $ unLoc-      $ rtr_extract rtr-graftHole span rtr-  = graftWithoutParentheses span-    -- Parenthesize the extract iff we're not in a top level hole-  $ bool maybeParensAST id (_jIsTopHole $ rtr_jdg rtr)-  $ rtr_extract rtr------------------------------------------------------------------------------------ | Merge in the 'Match'es of a 'FunBind' into a 'HsDecl'. Used to perform--- agda-style case splitting in which we need to separate one 'Match' into--- many, without affecting any matches which might exist but don't need to be--- split.-mergeFunBindMatches-    :: ([Pat GhcPs] -> LHsDecl GhcPs)-    -> SrcSpan-    -> HsBind GhcPs-    -> Either String (HsBind GhcPs)-mergeFunBindMatches make_decl span-    (fb@FunBind {fun_matches = mg@MG {mg_alts = L alts_src alts}}) =-  pure $ fb-    { fun_matches = mg-      { mg_alts = L alts_src $ do-          alt@(L alt_src match) <- alts-          case span `isSubspanOf` alt_src of-            True -> do-              let pats = fmap fromPatCompatPs $ m_pats match-                  L _ (ValD _ (FunBind {fun_matches = MG-                        {mg_alts = L _ to_add}})) = make_decl pats-              to_add-            False -> pure alt-      }-    }-mergeFunBindMatches _ _ _ =-  Left "mergeFunBindMatches: called on something that isnt a funbind"---throwError :: String -> TransformT (Either String) a-throwError = lift . Left------------------------------------------------------------------------------------ | Helper function to route 'mergeFunBindMatches' into the right place in an--- AST --- correctly dealing with inserting into instance declarations.-graftDecl-    :: SrcSpan-    -> ([Pat GhcPs] -> LHsDecl GhcPs)-    -> LHsDecl GhcPs-    -> TransformT (Either String) (Maybe [LHsDecl GhcPs])-graftDecl span-    make_decl-    (L src (ValD ext fb))-  = either throwError (pure . Just . pure . L src . ValD ext) $-      mergeFunBindMatches make_decl span fb--- TODO(sandy): add another case for default methods in class definitions-graftDecl span-    make_decl-    (L src (InstD ext-      cid@ClsInstD{cid_inst =-        cidi@ClsInstDecl{cid_sigs = _sigs, cid_binds = binds}}))-  = do-      binds' <--        for (bagToList binds) $ \b@(L bsrc bind) -> do-          case bind of-            fb@FunBind{} | span `isSubspanOf` bsrc ->-              either throwError (pure . L bsrc) $-                mergeFunBindMatches make_decl span fb-            _ -> pure b--      pure $ Just $ pure $ L src $ InstD ext $ cid-        { cid_inst = cidi-          { cid_binds = listToBag binds'-          }-        }-graftDecl span _ x = do-  traceMX "biggest" $-    unsafeRender $-      locateBiggest @(Match GhcPs (LHsExpr GhcPs)) span x-  traceMX "first" $-    unsafeRender $-      locateFirst @(Match GhcPs (LHsExpr GhcPs)) x-  throwError "graftDecl: don't know about this AST form"---fromMaybeT :: Functor m => a -> MaybeT m a -> m a-fromMaybeT def = fmap (fromMaybe def) . runMaybeT---locateBiggest :: (Data r, Data a) => SrcSpan -> a -> Maybe r-locateBiggest ss x = getFirst $ everything (<>)-  ( mkQ mempty $ \case-    L span r | ss `isSubspanOf` span -> pure r-    _                                -> mempty-  ) x---locateFirst :: (Data r, Data a) => a -> Maybe r-locateFirst x = getFirst $ everything (<>)-  ( mkQ mempty $ \case-    r -> pure r-  ) x+import Wingman.Plugin 
− src/Ide/Plugin/Tactic/Auto.hs
@@ -1,28 +0,0 @@-module Ide.Plugin.Tactic.Auto where--import Control.Monad.State (gets)-import Ide.Plugin.Tactic.Context-import Ide.Plugin.Tactic.Judgements-import Ide.Plugin.Tactic.KnownStrategies-import Ide.Plugin.Tactic.Machinery (tracing)-import Ide.Plugin.Tactic.Tactics-import Ide.Plugin.Tactic.Types-import Refinery.Tactic------------------------------------------------------------------------------------ | Automatically solve a goal.-auto :: TacticsM ()-auto = do-  jdg <- goal-  skolems <- gets ts_skolems-  current <- getCurrentDefinitions-  traceMX "goal" jdg-  traceMX "ctx" current-  traceMX "skolems" skolems-  commit knownStrategies-    . tracing "auto"-    . localTactic (auto' 4)-    . disallowing RecursiveCall-    $ fmap fst current-
− src/Ide/Plugin/Tactic/CaseSplit.hs
@@ -1,87 +0,0 @@-{-# LANGUAGE LambdaCase          #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications    #-}-{-# LANGUAGE ViewPatterns        #-}--module Ide.Plugin.Tactic.CaseSplit-  ( mkFirstAgda-  , iterateSplit-  , splitToDecl-  ) where--import           Data.Bool (bool)-import           Data.Data-import           Data.Generics-import           Data.Set (Set)-import qualified Data.Set as S-import           Development.IDE.GHC.Compat-import           GHC.Exts (IsString(fromString))-import           GHC.SourceGen (funBinds, match, wildP)-import           Ide.Plugin.Tactic.GHC-import           Ide.Plugin.Tactic.Types-import           OccName------------------------------------------------------------------------------------- | Construct an 'AgdaMatch' from patterns in scope (should be the LHS of the--- match) and a body.-mkFirstAgda :: [Pat GhcPs] -> HsExpr GhcPs -> AgdaMatch-mkFirstAgda pats (Lambda pats' body) = mkFirstAgda (pats <> pats') body-mkFirstAgda pats body = AgdaMatch pats body------------------------------------------------------------------------------------ | Transform an 'AgdaMatch' whose body is a case over a bound pattern, by--- splitting it into multiple matches: one for each alternative of the case.-agdaSplit :: AgdaMatch -> [AgdaMatch]-agdaSplit (AgdaMatch pats (Case (HsVar _ (L _ var)) matches)) = do-  (pat, body) <- matches-  -- TODO(sandy): use an at pattern if necessary-  pure $ AgdaMatch (rewriteVarPat var pat pats) body-agdaSplit x = [x]------------------------------------------------------------------------------------ | Replace unused bound patterns with wild patterns.-wildify :: AgdaMatch -> AgdaMatch-wildify (AgdaMatch pats body) =-  let make_wild = bool id (wildifyT (allOccNames body)) $ not $ containsHole body-   in AgdaMatch (make_wild pats) body------------------------------------------------------------------------------------ | Helper function for 'wildify'.-wildifyT :: Data a => Set OccName -> a -> a-wildifyT (S.map occNameString -> used) = everywhere $ mkT $ \case-  VarPat _ (L _ var) | S.notMember (occNameString $ occName var) used -> wildP-  (x :: Pat GhcPs) -> x------------------------------------------------------------------------------------ | Replace a 'VarPat' with the given @'Pat' GhcPs@.-rewriteVarPat :: Data a => RdrName -> Pat GhcPs -> a -> a-rewriteVarPat name rep = everywhere $ mkT $ \case-  VarPat _ (L _ var) | eqRdrName name var -> rep-  (x :: Pat GhcPs) -> x------------------------------------------------------------------------------------ | Construct an 'HsDecl' from a set of 'AgdaMatch'es.-splitToDecl-    :: OccName  -- ^ The name of the function-    -> [AgdaMatch]-    -> LHsDecl GhcPs-splitToDecl name ams = noLoc $ funBinds (fromString . occNameString . occName $ name) $ do-  AgdaMatch pats body <- ams-  pure $ match pats body------------------------------------------------------------------------------------ | Sometimes 'agdaSplit' exposes another opportunity to do 'agdaSplit'. This--- function runs it a few times, hoping it will find a fixpoint.-iterateSplit :: AgdaMatch -> [AgdaMatch]-iterateSplit am =-  let iterated = iterate (agdaSplit =<<) $ pure am-   in fmap wildify . head . drop 5 $ iterated-
− src/Ide/Plugin/Tactic/CodeGen.hs
@@ -1,205 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TupleSections    #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE ViewPatterns     #-}--module Ide.Plugin.Tactic.CodeGen-  ( module Ide.Plugin.Tactic.CodeGen-  , module Ide.Plugin.Tactic.CodeGen.Utils-  ) where--import           Control.Lens ((+~), (%~), (<>~))-import           Control.Monad.Except-import           Control.Monad.State (MonadState)-import           Control.Monad.State.Class (modify)-import           Data.Generics.Product (field)-import           Data.List-import qualified Data.Map as M-import qualified Data.Set as S-import           Data.Traversable-import           DataCon-import           Development.IDE.GHC.Compat-import           GHC.Exts-import           GHC.SourceGen.Binds-import           GHC.SourceGen.Expr-import           GHC.SourceGen.Overloaded-import           GHC.SourceGen.Pat-import           Ide.Plugin.Tactic.GHC-import           Ide.Plugin.Tactic.Judgements-import           Ide.Plugin.Tactic.Machinery-import           Ide.Plugin.Tactic.Naming-import           Ide.Plugin.Tactic.Types-import           Ide.Plugin.Tactic.CodeGen.Utils-import           Type hiding (Var)---useOccName :: MonadState TacticState m => Judgement -> OccName -> m ()-useOccName jdg name =-  -- Only score points if this is in the local hypothesis-  case M.lookup name $ hyByName $ jLocalHypothesis jdg of-    Just{}  -> modify-             $ (withUsedVals $ S.insert name)-             . (field @"ts_unused_top_vals" %~ S.delete name)-    Nothing -> pure ()------------------------------------------------------------------------------------ | Doing recursion incurs a small penalty in the score.-countRecursiveCall :: TacticState -> TacticState-countRecursiveCall = field @"ts_recursion_count" +~ 1------------------------------------------------------------------------------------ | Insert some values into the unused top values field. These are--- subsequently removed via 'useOccName'.-addUnusedTopVals :: MonadState TacticState m => S.Set OccName -> m ()-addUnusedTopVals vals = modify $ field @"ts_unused_top_vals" <>~ vals---destructMatches-    :: (DataCon -> Judgement -> Rule)-       -- ^ How to construct each match-    -> Maybe OccName-       -- ^ Scrutinee-    -> CType-       -- ^ Type being destructed-    -> Judgement-    -> RuleM (Trace, [RawMatch])-destructMatches f scrut t jdg = do-  let hy = jEntireHypothesis jdg-      g  = jGoal jdg-  case splitTyConApp_maybe $ unCType t of-    Nothing -> throwError $ GoalMismatch "destruct" g-    Just (tc, apps) -> do-      let dcs = tyConDataCons tc-      case dcs of-        [] -> throwError $ GoalMismatch "destruct" g-        _ -> fmap unzipTrace $ for dcs $ \dc -> do-          let args = dataConInstOrigArgTys' dc apps-          names <- mkManyGoodNames (hyNamesInScope hy) args-          let hy' = zip names $ coerce args-              j = introducingPat scrut dc hy'-                $ withNewGoal g jdg-          (tr, sg) <- f dc j-          modify $ withIntroducedVals $ mappend $ S.fromList names-          pure ( rose ("match " <> show dc <> " {" <>-                          intercalate ", " (fmap show names) <> "}")-                    $ pure tr-               , match [mkDestructPat dc names] $ unLoc sg-               )------------------------------------------------------------------------------------ | Produces a pattern for a data con and the names of its fields.-mkDestructPat :: DataCon -> [OccName] -> Pat GhcPs-mkDestructPat dcon names-  | isTupleDataCon dcon =-      tuple pat_args-  | otherwise =-      infixifyPatIfNecessary dcon $-        conP-          (coerceName $ dataConName dcon)-          pat_args-  where-    pat_args = fmap bvar' names---infixifyPatIfNecessary :: DataCon -> Pat GhcPs -> Pat GhcPs-infixifyPatIfNecessary dcon x-  | dataConIsInfix dcon =-      case x of-        ConPatIn op (PrefixCon [lhs, rhs]) ->-          ConPatIn op $ InfixCon lhs rhs-        y -> y-  | otherwise = x----unzipTrace :: [(Trace, a)] -> (Trace, [a])-unzipTrace l =-  let (trs, as) = unzip l-   in (rose mempty trs, as)----- | Essentially same as 'dataConInstOrigArgTys' in GHC,---  but only accepts universally quantified types as the second arguments---  and automatically introduces existentials.------ NOTE: The behaviour depends on GHC's 'dataConInstOrigArgTys'.---       We need some tweaks if the compiler changes the implementation.-dataConInstOrigArgTys'-  :: DataCon-      -- ^ 'DataCon'structor-  -> [Type]-      -- ^ /Universally/ quantified type arguments to a result type.-      --   It /MUST NOT/ contain any dictionaries, coercion and existentials.-      ---      --   For example, for @MkMyGADT :: b -> MyGADT a c@, we-      --   must pass @[a, c]@ as this argument but not @b@, as @b@ is an existential.-  -> [Type]-      -- ^ Types of arguments to the DataCon with returned type is instantiated with the second argument.-dataConInstOrigArgTys' con uniTys =-  let exvars = dataConExTys con-   in dataConInstOrigArgTys con $-        uniTys ++ fmap mkTyVarTy exvars-      -- Rationale: At least in GHC <= 8.10, 'dataConInstOrigArgTys'-      -- unifies the second argument with DataCon's universals followed by existentials.-      -- If the definition of 'dataConInstOrigArgTys' changes,-      -- this place must be changed accordingly.----------------------------------------------------------------------------------- | Combinator for performing case splitting, and running sub-rules on the--- resulting matches.--destruct' :: (DataCon -> Judgement -> Rule) -> HyInfo CType -> Judgement -> Rule-destruct' f hi jdg = do-  when (isDestructBlacklisted jdg) $ throwError NoApplicableTactic-  let term = hi_name hi-  useOccName jdg term-  (tr, ms)-      <- destructMatches-           f-           (Just term)-           (hi_type hi)-           $ disallowing AlreadyDestructed [term] jdg-  pure ( rose ("destruct " <> show term) $ pure tr-       , noLoc $ case' (var' term) ms-       )------------------------------------------------------------------------------------ | Combinator for performign case splitting, and running sub-rules on the--- resulting matches.-destructLambdaCase' :: (DataCon -> Judgement -> Rule) -> Judgement -> Rule-destructLambdaCase' f jdg = do-  when (isDestructBlacklisted jdg) $ throwError NoApplicableTactic-  let g  = jGoal jdg-  case splitFunTy_maybe (unCType g) of-    Just (arg, _) | isAlgType arg ->-      fmap (fmap noLoc lambdaCase) <$>-        destructMatches f Nothing (CType arg) jdg-    _ -> throwError $ GoalMismatch "destructLambdaCase'" g------------------------------------------------------------------------------------ | Construct a data con with subgoals for each field.-buildDataCon-    :: Judgement-    -> DataCon            -- ^ The data con to build-    -> [Type]             -- ^ Type arguments for the data con-    -> RuleM (Trace, LHsExpr GhcPs)-buildDataCon jdg dc tyapps = do-  let args = dataConInstOrigArgTys' dc tyapps-  (tr, sgs)-      <- fmap unzipTrace-       $ traverse ( \(arg, n) ->-                    newSubgoal-                  . filterSameTypeFromOtherPositions dc n-                  . blacklistingDestruct-                  . flip withNewGoal jdg-                  $ CType arg-                  ) $ zip args [0..]-  pure-    . (rose (show dc) $ pure tr,)-    $ mkCon dc sgs-
− src/Ide/Plugin/Tactic/CodeGen/Utils.hs
@@ -1,72 +0,0 @@-{-# LANGUAGE ViewPatterns #-}--module Ide.Plugin.Tactic.CodeGen.Utils where--import Data.List-import DataCon-import Development.IDE.GHC.Compat-import GHC.Exts-import GHC.SourceGen (recordConE, RdrNameStr)-import GHC.SourceGen.Overloaded-import Ide.Plugin.Tactic.GHC (getRecordFields)-import Name------------------------------------------------------------------------------------ | Make a data constructor with the given arguments.-mkCon :: DataCon -> [LHsExpr GhcPs] -> LHsExpr GhcPs-mkCon dcon (fmap unLoc -> args)-  | isTupleDataCon dcon =-      noLoc $ tuple args-  | dataConIsInfix dcon-  , (lhs : rhs : args') <- args =-      noLoc $ foldl' (@@) (op lhs (coerceName dcon_name) rhs) args'-  | Just fields <- getRecordFields dcon =-      noLoc $ recordConE (coerceName dcon_name) $ do-        (arg, (field, _)) <- zip args fields-        pure (coerceName field, arg)-  | otherwise =-      noLoc $ foldl' (@@) (bvar' $ occName dcon_name) args-  where-    dcon_name = dataConName dcon---coerceName :: HasOccName a => a -> RdrNameStr-coerceName = fromString . occNameString . occName------------------------------------------------------------------------------------ | Like 'var', but works over standard GHC 'OccName's.-var' :: Var a => OccName -> a-var' = var . fromString . occNameString------------------------------------------------------------------------------------ | Like 'bvar', but works over standard GHC 'OccName's.-bvar' :: BVar a => OccName -> a-bvar' = bvar . fromString . occNameString------------------------------------------------------------------------------------ | Get an HsExpr corresponding to a function name.-mkFunc :: String -> HsExpr GhcPs-mkFunc = var' . mkVarOcc------------------------------------------------------------------------------------ | Get an HsExpr corresponding to a value name.-mkVal :: String -> HsExpr GhcPs-mkVal = var' . mkVarOcc------------------------------------------------------------------------------------ | Like 'op', but easier to call.-infixCall :: String -> HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs-infixCall s = flip op (fromString s)------------------------------------------------------------------------------------ | Like '(@@)', but uses a dollar instead of parentheses.-appDollar :: HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs-appDollar = infixCall "$"-
− src/Ide/Plugin/Tactic/Context.hs
@@ -1,95 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE LambdaCase       #-}--module Ide.Plugin.Tactic.Context where--import           Bag-import           Control.Arrow-import           Control.Monad.Reader-import           Data.List-import           Data.Maybe (mapMaybe)-import           Data.Set (Set)-import qualified Data.Set as S-import           Development.IDE.GHC.Compat-import           Ide.Plugin.Tactic.GHC (tacticsThetaTy)-import           Ide.Plugin.Tactic.Machinery (methodHypothesis)-import           Ide.Plugin.Tactic.Types-import           OccName-import           TcRnTypes-import           TcType (substTy, tcSplitSigmaTy)-import           Unify (tcUnifyTy)-import Ide.Plugin.Tactic.FeatureSet (FeatureSet)---mkContext :: FeatureSet -> [(OccName, CType)] -> TcGblEnv -> Context-mkContext features locals tcg = Context-  { ctxDefiningFuncs = locals-  , ctxModuleFuncs = fmap splitId-                   . (getFunBindId =<<)-                   . fmap unLoc-                   . bagToList-                   $ tcg_binds tcg-  , ctxFeatureSet = features-  }------------------------------------------------------------------------------------ | Find all of the class methods that exist from the givens in the context.-contextMethodHypothesis :: Context -> Hypothesis CType-contextMethodHypothesis ctx-  = Hypothesis-  . excludeForbiddenMethods-  . join-  . concatMap-      ( mapMaybe methodHypothesis-      . tacticsThetaTy-      . unCType-      )-  . mapMaybe (definedThetaType ctx)-  . fmap fst-  $ ctxDefiningFuncs ctx------------------------------------------------------------------------------------ | Many operations are defined in typeclasses for performance reasons, rather--- than being a true part of the class. This function filters out those, in--- order to keep our hypothesis space small.-excludeForbiddenMethods :: [HyInfo a] -> [HyInfo a]-excludeForbiddenMethods = filter (not . flip S.member forbiddenMethods . hi_name)-  where-    forbiddenMethods :: Set OccName-    forbiddenMethods = S.map mkVarOcc $ S.fromList-      [ -- monadfail-        "fail"-      ]------------------------------------------------------------------------------------ | Given the name of a function that exists in 'ctxDefiningFuncs', get its--- theta type.-definedThetaType :: Context -> OccName -> Maybe CType-definedThetaType ctx name = do-  (_, CType mono) <- find ((== name) . fst) $ ctxDefiningFuncs ctx-  (_, CType poly) <- find ((== name) . fst) $ ctxModuleFuncs ctx-  let (_, _, poly') = tcSplitSigmaTy poly-  subst <- tcUnifyTy poly' mono-  pure $ CType $ substTy subst $ snd $ splitForAllTys poly---splitId :: Id -> (OccName, CType)-splitId = occName &&& CType . idType---getFunBindId :: HsBindLR GhcTc GhcTc -> [Id]-getFunBindId (AbsBinds _ _ _ abes _ _ _)-  = abes >>= \case-      ABE _ poly _ _ _ -> pure poly-      _ -> []-getFunBindId _ = []---getCurrentDefinitions :: MonadReader Context m => m [(OccName, CType)]-getCurrentDefinitions = asks ctxDefiningFuncs--getModuleHypothesis :: MonadReader Context m => m [(OccName, CType)]-getModuleHypothesis = asks ctxModuleFuncs
− src/Ide/Plugin/Tactic/Debug.hs
@@ -1,59 +0,0 @@-{-# LANGUAGE BangPatterns     #-}-{-# LANGUAGE CPP              #-}-{-# LANGUAGE TypeApplications #-}--module Ide.Plugin.Tactic.Debug-  ( unsafeRender-  , unsafeRender'-  , traceM-  , traceShowId-  , trace-  , traceX-  , traceIdX-  , traceMX-  , traceFX-  ) where--import Control.DeepSeq-import Control.Exception-import Debug.Trace-import DynFlags (unsafeGlobalDynFlags)-import Outputable hiding ((<>))-import System.IO.Unsafe (unsafePerformIO)--#if __GLASGOW_HASKELL__ >= 808-import PlainPanic (PlainGhcException)-type GHC_EXCEPTION = PlainGhcException-#else-import Panic (GhcException)-type GHC_EXCEPTION = GhcException-#endif------------------------------------------------------------------------------------ | Print something-unsafeRender :: Outputable a => a -> String-unsafeRender = unsafeRender' . ppr---unsafeRender' :: SDoc -> String-unsafeRender' sdoc = unsafePerformIO $ do-  let z = showSDoc unsafeGlobalDynFlags sdoc-  -- We might not have unsafeGlobalDynFlags (like during testing), in which-  -- case GHC panics. Instead of crashing, let's just fail to print.-  !res <- try @GHC_EXCEPTION $ evaluate $ deepseq z z-  pure $ either (const "<unsafeRender'>") id res-{-# NOINLINE unsafeRender' #-}--traceMX :: (Monad m, Show a) => String -> a -> m ()-traceMX str a = traceM $ mappend ("!!!" <> str <> ": ") $ show a--traceX :: (Show a) => String -> a -> b -> b-traceX str a = trace (mappend ("!!!" <> str <> ": ") $ show a)--traceIdX :: (Show a) => String -> a -> a-traceIdX str a = trace (mappend ("!!!" <> str <> ": ") $ show a) a--traceFX :: String -> (a -> String) -> a -> a-traceFX str f a = trace (mappend ("!!!" <> str <> ": ") $ f a) a-
− src/Ide/Plugin/Tactic/FeatureSet.hs
@@ -1,92 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE PatternSynonyms   #-}-{-# LANGUAGE ViewPatterns      #-}--module Ide.Plugin.Tactic.FeatureSet-  ( Feature (..)-  , FeatureSet-  , hasFeature-  , defaultFeatures-  , allFeatures-  , parseFeatureSet-  , prettyFeatureSet-  ) where--import           Data.List (intercalate)-import           Data.Maybe (mapMaybe, listToMaybe)-import           Data.Set (Set)-import qualified Data.Set as S-import qualified Data.Text as T------------------------------------------------------------------------------------ | All the available features. A 'FeatureSet' describes the ones currently--- available to the user.-data Feature = CantHaveAnEmptyDataType-  deriving (Eq, Ord, Show, Read, Enum, Bounded)------------------------------------------------------------------------------------ | A collection of enabled features.-type FeatureSet = Set Feature------------------------------------------------------------------------------------ | Parse a feature set.-parseFeatureSet :: T.Text -> FeatureSet-parseFeatureSet-  = mappend defaultFeatures-  . S.fromList-  . mapMaybe (readMaybe . mappend featurePrefix . rot13 . T.unpack)-  . T.split (== '/')------------------------------------------------------------------------------------ | Features that are globally enabled for all users.-defaultFeatures :: FeatureSet-defaultFeatures = S.fromList-  [-  ]------------------------------------------------------------------------------------ | All available features.-allFeatures :: FeatureSet-allFeatures = S.fromList $ enumFromTo minBound maxBound------------------------------------------------------------------------------------ | Pretty print a feature set.-prettyFeatureSet :: FeatureSet -> String-prettyFeatureSet-  = intercalate "/"-  . fmap (rot13 . drop (length featurePrefix) . show)-  . S.toList------------------------------------------------------------------------------------ | Is a given 'Feature' currently enabled?-hasFeature :: Feature -> FeatureSet -> Bool-hasFeature = S.member------------------------------------------------------------------------------------ | Like 'read', but not partial.-readMaybe :: Read a => String -> Maybe a-readMaybe = fmap fst . listToMaybe . reads---featurePrefix :: String-featurePrefix = "Feature"---rot13 :: String -> String-rot13 = fmap (toEnum . rot13int . fromEnum)---rot13int :: Integral a => a -> a-rot13int x-  | (fromIntegral x :: Word) - 97 < 26 = 97 + rem (x - 84) 26-  | (fromIntegral x :: Word) - 65 < 26 = 65 + rem (x - 52) 26-  | otherwise   = x-
− src/Ide/Plugin/Tactic/GHC.hs
@@ -1,292 +0,0 @@-{-# LANGUAGE CPP                 #-}-{-# LANGUAGE FlexibleContexts    #-}-{-# LANGUAGE LambdaCase          #-}-{-# LANGUAGE OverloadedStrings   #-}-{-# LANGUAGE PatternSynonyms     #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications    #-}-{-# LANGUAGE ViewPatterns        #-}--module Ide.Plugin.Tactic.GHC where--import           Control.Monad.State-import           Data.Function (on)-import           Data.List (isPrefixOf)-import qualified Data.Map as M-import           Data.Maybe (isJust)-import           Data.Set (Set)-import qualified Data.Set as S-import           Data.Traversable-import           DataCon-import           Development.IDE.GHC.Compat-import           GHC.SourceGen (match, case', lambda)-import           Generics.SYB (mkQ, everything, listify, Data, mkT, everywhere)-import           Ide.Plugin.Tactic.Types-import           OccName-import           TcType-import           TyCoRep-import           Type-import           TysWiredIn (intTyCon, floatTyCon, doubleTyCon, charTyCon)-import           Unique-import           Var---tcTyVar_maybe :: Type -> Maybe Var-tcTyVar_maybe ty | Just ty' <- tcView ty = tcTyVar_maybe ty'-tcTyVar_maybe (CastTy ty _) = tcTyVar_maybe ty  -- look through casts, as-                                                -- this is only used for-                                                -- e.g., FlexibleContexts-tcTyVar_maybe (TyVarTy v)   = Just v-tcTyVar_maybe _             = Nothing---instantiateType :: Type -> ([TyVar], Type)-instantiateType t = do-  let vs  = tyCoVarsOfTypeList t-      vs' = fmap cloneTyVar vs-      subst = foldr (\(v,t) a -> extendTCvSubst a v $ TyVarTy t) emptyTCvSubst-            $ zip vs vs'-   in (vs', substTy subst t)---cloneTyVar :: TyVar -> TyVar-cloneTyVar t =-  let uniq = getUnique t-      some_magic_number = 49-   in setVarUnique t $ deriveUnique uniq some_magic_number------------------------------------------------------------------------------------ | Is this a function type?-isFunction :: Type -> Bool-isFunction (tacticsSplitFunTy -> (_, _, [], _)) = False-isFunction _ = True------------------------------------------------------------------------------------ | Split a function, also splitting out its quantified variables and theta--- context.-tacticsSplitFunTy :: Type -> ([TyVar], ThetaType, [Type], Type)-tacticsSplitFunTy t-  = let (vars, theta, t') = tcSplitSigmaTy t-        (args, res) = tcSplitFunTys t'-     in (vars, theta, args, res)------------------------------------------------------------------------------------ | Rip the theta context out of a regular type.-tacticsThetaTy :: Type -> ThetaType-tacticsThetaTy (tcSplitSigmaTy -> (_, theta,  _)) = theta------------------------------------------------------------------------------------ | Instantiate all of the quantified type variables in a type with fresh--- skolems.-freshTyvars :: MonadState TacticState m => Type -> m Type-freshTyvars t = do-  let (tvs, _, _, _) = tacticsSplitFunTy t-  reps <- fmap M.fromList-        $ for tvs $ \tv -> do-            uniq <- freshUnique-            pure (tv, setTyVarUnique tv uniq)-  pure $-    everywhere-      (mkT $ \tv ->-        case M.lookup tv reps of-          Just tv' -> tv'-          Nothing -> tv-      ) t------------------------------------------------------------------------------------ | Given a datacon, extract its record fields' names and types. Returns--- nothing if the datacon is not a record.-getRecordFields :: DataCon -> Maybe [(OccName, CType)]-getRecordFields dc =-  case dataConFieldLabels dc of-    [] -> Nothing-    lbls -> for lbls $ \lbl -> do-      (_, ty) <- dataConFieldType_maybe dc $ flLabel lbl-      pure (mkVarOccFS $ flLabel lbl, CType ty)------------------------------------------------------------------------------------ | Is this an algebraic type?-algebraicTyCon :: Type -> Maybe TyCon-algebraicTyCon (splitTyConApp_maybe -> Just (tycon, _))-  | tycon == intTyCon    = Nothing-  | tycon == floatTyCon  = Nothing-  | tycon == doubleTyCon = Nothing-  | tycon == charTyCon   = Nothing-  | tycon == funTyCon    = Nothing-  | otherwise = Just tycon-algebraicTyCon _ = Nothing------------------------------------------------------------------------------------ | We can't compare 'RdrName' for equality directly. Instead, compare them by--- their 'OccName's.-eqRdrName :: RdrName -> RdrName -> Bool-eqRdrName = (==) `on` occNameString . occName------------------------------------------------------------------------------------ | Does this thing contain any references to 'HsVar's with the given--- 'RdrName'?-containsHsVar :: Data a => RdrName -> a -> Bool-containsHsVar name x = not $ null $ listify (-  \case-    ((HsVar _ (L _ a)) :: HsExpr GhcPs) | eqRdrName a name -> True-    _ -> False-  ) x------------------------------------------------------------------------------------ | Does this thing contain any holes?-containsHole :: Data a => a -> Bool-containsHole x = not $ null $ listify (-  \case-    ((HsVar _ (L _ name)) :: HsExpr GhcPs) -> isHole $ occName name-    _ -> False-  ) x------------------------------------------------------------------------------------ | Check if an 'OccName' is a hole-isHole :: OccName -> Bool--- TODO(sandy): Make this more robust-isHole = isPrefixOf "_" . occNameString------------------------------------------------------------------------------------ | Get all of the referenced occnames.-allOccNames :: Data a => a -> Set OccName-allOccNames = everything (<>) $ mkQ mempty $ \case-    a -> S.singleton a-------------------------------------------------------------------------------------- | A pattern over the otherwise (extremely) messy AST for lambdas.-pattern Lambda :: [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs-pattern Lambda pats body <--  HsLam _-    (MG {mg_alts = L _ [L _-      (Match { m_pats = fmap fromPatCompatPs -> pats-             , m_grhss = UnguardedRHSs body-             })]})-  where-    -- If there are no patterns to bind, just stick in the body-    Lambda [] body   = body-    Lambda pats body = lambda pats body------------------------------------------------------------------------------------ | A GRHS that caontains no guards.-pattern UnguardedRHSs :: HsExpr GhcPs -> GRHSs GhcPs (LHsExpr GhcPs)-pattern UnguardedRHSs body <--  GRHSs {grhssGRHSs = [L _ (GRHS _ [] (L _ body))]}------------------------------------------------------------------------------------ | A match with a single pattern. Case matches are always 'SinglePatMatch'es.-pattern SinglePatMatch :: Pat GhcPs -> HsExpr GhcPs -> Match GhcPs (LHsExpr GhcPs)-pattern SinglePatMatch pat body <--  Match { m_pats = [fromPatCompatPs -> pat]-        , m_grhss = UnguardedRHSs body-        }------------------------------------------------------------------------------------ | Helper function for defining the 'Case' pattern.-unpackMatches :: [Match GhcPs (LHsExpr GhcPs)] -> Maybe [(Pat GhcPs, HsExpr GhcPs)]-unpackMatches [] = Just []-unpackMatches (SinglePatMatch pat body : matches) =-  (:) <$> pure (pat, body) <*> unpackMatches matches-unpackMatches _ = Nothing------------------------------------------------------------------------------------ | A pattern over the otherwise (extremely) messy AST for lambdas.-pattern Case :: HsExpr GhcPs -> [(Pat GhcPs, HsExpr GhcPs)] -> HsExpr GhcPs-pattern Case scrutinee matches <--  HsCase _ (L _ scrutinee)-    (MG {mg_alts = L _ (fmap unLoc -> unpackMatches -> Just matches)})-  where-    Case scrutinee matches =-      case' scrutinee $ fmap (\(pat, body) -> match [pat] body) matches------------------------------------------------------------------------------------ | Can ths type be lambda-cased?------ Return: 'Nothing' if no---         @Just False@ if it can't be homomorphic---         @Just True@ if it can-lambdaCaseable :: Type -> Maybe Bool-lambdaCaseable (splitFunTy_maybe -> Just (arg, res))-  | isJust (algebraicTyCon arg)-  = Just $ isJust $ algebraicTyCon res-lambdaCaseable _ = Nothing---- It's hard to generalize over these since weird type families are involved.-fromPatCompatTc :: PatCompat GhcTc -> Pat GhcTc-fromPatCompatPs :: PatCompat GhcPs -> Pat GhcPs-#if __GLASGOW_HASKELL__ == 808-type PatCompat pass = Pat pass-fromPatCompatTc = id-fromPatCompatPs = id-#else-type PatCompat pass = LPat pass-fromPatCompatTc = unLoc-fromPatCompatPs = unLoc-#endif----------------------------------------------------------------------------------- | Should make sure it's a fun bind-pattern TopLevelRHS :: OccName -> [PatCompat GhcTc] -> LHsExpr GhcTc -> Match GhcTc (LHsExpr GhcTc)-pattern TopLevelRHS name ps body <--  Match _-    (FunRhs (L _ (occName -> name)) _ _)-    ps-    (GRHSs _-      [L _ (GRHS _ [] body)] _)--getPatName :: PatCompat GhcTc -> Maybe OccName-getPatName (fromPatCompatTc -> p0) =-  case p0 of-    VarPat  _ x   -> Just $ occName $ unLoc x-    LazyPat _ p   -> getPatName p-    AsPat   _ x _ -> Just $ occName $ unLoc x-    ParPat  _ p   -> getPatName p-    BangPat _ p   -> getPatName p-    ViewPat _ _ p -> getPatName p-#if __GLASGOW_HASKELL__ >= 808-    SigPat  _ p _ -> getPatName p-#endif-#if __GLASGOW_HASKELL__ == 808-    XPat   p      -> getPatName $ unLoc p-#endif-    _             -> Nothing--dataConExTys :: DataCon -> [TyCoVar]-#if __GLASGOW_HASKELL__ >= 808-dataConExTys = DataCon.dataConExTyCoVars-#else-dataConExTys = DataCon.dataConExTyVars-#endif------------------------------------------------------------------------------------ | In GHC 8.8, sometimes patterns are wrapped in 'XPat'.--- The nitty gritty details are explained at--- https://blog.shaynefletcher.org/2020/03/ghc-haskell-pats-and-lpats.html------ We need to remove these in order to succesfull find patterns.-unXPat :: Pat GhcPs -> Pat GhcPs-#if __GLASGOW_HASKELL__ == 808-unXPat (XPat (L _ pat)) = unXPat pat-#endif-unXPat pat = pat-
− src/Ide/Plugin/Tactic/Judgements.hs
@@ -1,407 +0,0 @@-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE ViewPatterns     #-}--module Ide.Plugin.Tactic.Judgements-  ( blacklistingDestruct-  , unwhitelistingSplit-  , introducingLambda-  , introducingRecursively-  , introducingPat-  , jGoal-  , jHypothesis-  , jEntireHypothesis-  , jPatHypothesis-  , substJdg-  , unsetIsTopHole-  , filterSameTypeFromOtherPositions-  , isDestructBlacklisted-  , withNewGoal-  , jLocalHypothesis-  , isSplitWhitelisted-  , isPatternMatch-  , filterPosition-  , isTopHole-  , disallowing-  , mkFirstJudgement-  , hypothesisFromBindings-  , isTopLevel-  , hyNamesInScope-  , hyByName-  ) where--import           Control.Arrow-import           Control.Lens hiding (Context)-import           Data.Bool-import           Data.Char-import           Data.Coerce-import           Data.Generics.Product (field)-import           Data.Map (Map)-import qualified Data.Map as M-import           Data.Maybe-import           Data.Set (Set)-import qualified Data.Set as S-import           DataCon (DataCon)-import           Development.IDE.Spans.LocalBindings-import           Ide.Plugin.Tactic.Types-import           OccName-import           SrcLoc-import           Type------------------------------------------------------------------------------------ | Given a 'SrcSpan' and a 'Bindings', create a hypothesis.-hypothesisFromBindings :: RealSrcSpan -> Bindings -> Hypothesis CType-hypothesisFromBindings span bs = buildHypothesis $ getLocalScope bs span------------------------------------------------------------------------------------ | Convert a @Set Id@ into a hypothesis.-buildHypothesis :: [(Name, Maybe Type)] -> Hypothesis CType-buildHypothesis-  = Hypothesis-  . mapMaybe go-  where-    go (occName -> occ, t)-      | Just ty <- t-      , isAlpha . head . occNameString $ occ = Just $ HyInfo occ UserPrv $ CType ty-      | otherwise = Nothing---blacklistingDestruct :: Judgement -> Judgement-blacklistingDestruct =-  field @"_jBlacklistDestruct" .~ True---unwhitelistingSplit :: Judgement -> Judgement-unwhitelistingSplit =-  field @"_jWhitelistSplit" .~ False---isDestructBlacklisted :: Judgement -> Bool-isDestructBlacklisted = _jBlacklistDestruct---isSplitWhitelisted :: Judgement -> Bool-isSplitWhitelisted = _jWhitelistSplit---withNewGoal :: a -> Judgement' a -> Judgement' a-withNewGoal t = field @"_jGoal" .~ t------------------------------------------------------------------------------------ | Helper function for implementing functions which introduce new hypotheses.-introducing-    :: (Int -> Provenance)  -- ^ A function from the position of the arg to its-                            -- provenance.-    -> [(OccName, a)]-    -> Judgement' a-    -> Judgement' a-introducing f ns =-  field @"_jHypothesis" <>~ (Hypothesis $ zip [0..] ns <&>-    \(pos, (name, ty)) -> HyInfo name (f pos) ty)------------------------------------------------------------------------------------ | Introduce bindings in the context of a lamba.-introducingLambda-    :: Maybe OccName   -- ^ The name of the top level function. For any other-                       -- function, this should be 'Nothing'.-    -> [(OccName, a)]-    -> Judgement' a-    -> Judgement' a-introducingLambda func = introducing $ \pos ->-  maybe UserPrv (\x -> TopLevelArgPrv x pos) func------------------------------------------------------------------------------------ | Introduce a binding in a recursive context.-introducingRecursively :: [(OccName, a)] -> Judgement' a -> Judgement' a-introducingRecursively = introducing $ const RecursivePrv------------------------------------------------------------------------------------ | Check whether any of the given occnames are an ancestor of the term.-hasPositionalAncestry-    :: Foldable t-    => t OccName   -- ^ Desired ancestors.-    -> Judgement-    -> OccName     -- ^ Potential child-    -> Maybe Bool  -- ^ Just True if the result is the oldest positional ancestor-                   -- just false if it's a descendent-                   -- otherwise nothing-hasPositionalAncestry ancestors jdg name-  | not $ null ancestors-  = case any (== name) ancestors of-      True  -> Just True-      False ->-        case M.lookup name $ jAncestryMap jdg of-          Just ancestry ->-            bool Nothing (Just False) $ any (flip S.member ancestry) ancestors-          Nothing -> Nothing-  | otherwise = Nothing------------------------------------------------------------------------------------ | Helper function for disallowing hypotheses that have the wrong ancestry.-filterAncestry-    :: Foldable t-    => t OccName-    -> DisallowReason-    -> Judgement-    -> Judgement-filterAncestry ancestry reason jdg =-    disallowing reason (M.keys $ M.filterWithKey go $ hyByName $ jHypothesis jdg) jdg-  where-    go name _-      = not-      . isJust-      $ hasPositionalAncestry ancestry jdg name------------------------------------------------------------------------------------ | @filter defn pos@ removes any hypotheses which are bound in @defn@ to--- a position other than @pos@. Any terms whose ancestry doesn't include @defn@--- remain.-filterPosition :: OccName -> Int -> Judgement -> Judgement-filterPosition defn pos jdg =-  filterAncestry (findPositionVal jdg defn pos) (WrongBranch pos) jdg------------------------------------------------------------------------------------ | Helper function for determining the ancestry list for 'filterPosition'.-findPositionVal :: Judgement' a -> OccName -> Int -> Maybe OccName-findPositionVal jdg defn pos = listToMaybe $ do-  -- It's important to inspect the entire hypothesis here, as we need to trace-  -- ancstry through potentially disallowed terms in the hypothesis.-  (name, hi) <- M.toList $ M.map (overProvenance expandDisallowed) $ hyByName $ jEntireHypothesis jdg-  case hi_provenance hi of-    TopLevelArgPrv defn' pos'-      | defn == defn'-      , pos  == pos' -> pure name-    PatternMatchPrv pv-      | pv_scrutinee pv == Just defn-      , pv_position pv  == pos -> pure name-    _ -> []------------------------------------------------------------------------------------ | Helper function for determining the ancestry list for--- 'filterSameTypeFromOtherPositions'.-findDconPositionVals :: Judgement' a -> DataCon -> Int -> [OccName]-findDconPositionVals jdg dcon pos = do-  (name, hi) <- M.toList $ hyByName $ jHypothesis jdg-  case hi_provenance hi of-    PatternMatchPrv pv-      | pv_datacon  pv == Uniquely dcon-      , pv_position pv == pos -> pure name-    _ -> []------------------------------------------------------------------------------------ | Disallow any hypotheses who have the same type as anything bound by the--- given position for the datacon. Used to ensure recursive functions like--- 'fmap' preserve the relative ordering of their arguments by eliminating any--- other term which might match.-filterSameTypeFromOtherPositions :: DataCon -> Int -> Judgement -> Judgement-filterSameTypeFromOtherPositions dcon pos jdg =-  let hy = hyByName-         . jHypothesis-         $ filterAncestry-             (findDconPositionVals jdg dcon pos)-             (WrongBranch pos)-             jdg-      tys = S.fromList $ hi_type <$> M.elems hy-      to_remove =-        M.filter (flip S.member tys . hi_type) (hyByName $ jHypothesis jdg)-          M.\\ hy-   in disallowing Shadowed (M.keys to_remove) jdg------------------------------------------------------------------------------------ | Return the ancestry of a 'PatVal', or 'mempty' otherwise.-getAncestry :: Judgement' a -> OccName -> Set OccName-getAncestry jdg name =-  case M.lookup name $ jPatHypothesis jdg of-    Just pv -> pv_ancestry pv-    Nothing -> mempty---jAncestryMap :: Judgement' a -> Map OccName (Set OccName)-jAncestryMap jdg =-  flip M.map (jPatHypothesis jdg) pv_ancestry------------------------------------------------------------------------------------ TODO(sandy): THIS THING IS A BIG BIG HACK------ Why? 'ctxDefiningFuncs' is _all_ of the functions currently beind defined--- (eg, we might be in a where block). The head of this list is not guaranteed--- to be the one we're interested in.-extremelyStupid__definingFunction :: Context -> OccName-extremelyStupid__definingFunction =-  fst . head . ctxDefiningFuncs------------------------------------------------------------------------------------ | Pattern vals are currently tracked in jHypothesis, with an extra piece of--- data sitting around in jPatternVals.-introducingPat-    :: Maybe OccName-    -> DataCon-    -> [(OccName, a)]-    -> Judgement' a-    -> Judgement' a-introducingPat scrutinee dc ns jdg-  = introducing (\pos ->-      PatternMatchPrv $-        PatVal-          scrutinee-          (maybe mempty-                (\scrut -> S.singleton scrut <> getAncestry jdg scrut)-                scrutinee)-          (Uniquely dc)-          pos-    ) ns jdg------------------------------------------------------------------------------------ | Prevent some occnames from being used in the hypothesis. This will hide--- them from 'jHypothesis', but not from 'jEntireHypothesis'.-disallowing :: DisallowReason -> [OccName] -> Judgement' a -> Judgement' a-disallowing reason (S.fromList -> ns) =-  field @"_jHypothesis" %~ (\z -> Hypothesis . flip fmap (unHypothesis z) $ \hi ->-    case S.member (hi_name hi) ns of-      True -> overProvenance (DisallowedPrv reason) hi-      False -> hi-                           )------------------------------------------------------------------------------------ | The hypothesis, consisting of local terms and the ambient environment--- (impors and class methods.) Hides disallowed values.-jHypothesis :: Judgement' a -> Hypothesis a-jHypothesis-  = Hypothesis-  . filter (not . isDisallowed . hi_provenance)-  . unHypothesis-  . jEntireHypothesis------------------------------------------------------------------------------------ | The whole hypothesis, including things disallowed.-jEntireHypothesis :: Judgement' a -> Hypothesis a-jEntireHypothesis = _jHypothesis------------------------------------------------------------------------------------ | Just the local hypothesis.-jLocalHypothesis :: Judgement' a -> Hypothesis a-jLocalHypothesis-  = Hypothesis-  . filter (isLocalHypothesis . hi_provenance)-  . unHypothesis-  . jHypothesis------------------------------------------------------------------------------------ | If we're in a top hole, the name of the defining function.-isTopHole :: Context -> Judgement' a -> Maybe OccName-isTopHole ctx =-  bool Nothing (Just $ extremelyStupid__definingFunction ctx) . _jIsTopHole---unsetIsTopHole :: Judgement' a -> Judgement' a-unsetIsTopHole = field @"_jIsTopHole" .~ False------------------------------------------------------------------------------------ | What names are currently in scope in the hypothesis?-hyNamesInScope :: Hypothesis a -> Set OccName-hyNamesInScope = M.keysSet . hyByName------------------------------------------------------------------------------------ | Fold a hypothesis into a single mapping from name to info. This--- unavoidably will cause duplicate names (things like methods) to shadow one--- another.-hyByName :: Hypothesis a -> Map OccName (HyInfo a)-hyByName-  = M.fromList-  . fmap (hi_name &&& id)-  . unHypothesis------------------------------------------------------------------------------------ | Only the hypothesis members which are pattern vals-jPatHypothesis :: Judgement' a -> Map OccName PatVal-jPatHypothesis-  = M.mapMaybe (getPatVal . hi_provenance)-  . hyByName-  . jHypothesis---getPatVal :: Provenance-> Maybe PatVal-getPatVal prov =-  case prov of-    PatternMatchPrv pv -> Just pv-    _                  -> Nothing---jGoal :: Judgement' a -> a-jGoal = _jGoal---substJdg :: TCvSubst -> Judgement -> Judgement-substJdg subst = fmap $ coerce . substTy subst . coerce---mkFirstJudgement-    :: Hypothesis CType-    -> Bool  -- ^ are we in the top level rhs hole?-    -> Type-    -> Judgement' CType-mkFirstJudgement hy top goal = Judgement-  { _jHypothesis        = hy-  , _jBlacklistDestruct = False-  , _jWhitelistSplit    = True-  , _jIsTopHole         = top-  , _jGoal              = CType goal-  }------------------------------------------------------------------------------------ | Is this a top level function binding?-isTopLevel :: Provenance -> Bool-isTopLevel TopLevelArgPrv{} = True-isTopLevel _                = False------------------------------------------------------------------------------------ | Is this a local function argument, pattern match or user val?-isLocalHypothesis :: Provenance -> Bool-isLocalHypothesis UserPrv{}         = True-isLocalHypothesis PatternMatchPrv{} = True-isLocalHypothesis TopLevelArgPrv{}  = True-isLocalHypothesis _                 = False------------------------------------------------------------------------------------ | Is this a pattern match?-isPatternMatch :: Provenance -> Bool-isPatternMatch PatternMatchPrv{} = True-isPatternMatch _                 = False------------------------------------------------------------------------------------ | Was this term ever disallowed?-isDisallowed :: Provenance -> Bool-isDisallowed DisallowedPrv{} = True-isDisallowed _               = False------------------------------------------------------------------------------------ | Eliminates 'DisallowedPrv' provenances.-expandDisallowed :: Provenance -> Provenance-expandDisallowed (DisallowedPrv _ prv) = expandDisallowed prv-expandDisallowed prv = prv
− src/Ide/Plugin/Tactic/KnownStrategies.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE LambdaCase #-}--module Ide.Plugin.Tactic.KnownStrategies where--import Control.Monad.Error.Class-import Ide.Plugin.Tactic.Context (getCurrentDefinitions)-import Ide.Plugin.Tactic.Tactics-import Ide.Plugin.Tactic.Types-import OccName (mkVarOcc)-import Refinery.Tactic-import Ide.Plugin.Tactic.Machinery (tracing)-import Ide.Plugin.Tactic.KnownStrategies.QuickCheck (deriveArbitrary)---knownStrategies :: TacticsM ()-knownStrategies = choice-  [ known "fmap" deriveFmap-  , known "arbitrary" deriveArbitrary-  ]---known :: String -> TacticsM () -> TacticsM ()-known name t = do-  getCurrentDefinitions >>= \case-    [(def, _)] | def == mkVarOcc name ->-      tracing ("known " <> name) t-    _ -> throwError NoApplicableTactic---deriveFmap :: TacticsM ()-deriveFmap = do-  try intros-  overAlgebraicTerms homo-  choice-    [ overFunctions apply >> auto' 2-    , assumption-    , recursion-    ]-
− src/Ide/Plugin/Tactic/KnownStrategies/QuickCheck.hs
@@ -1,110 +0,0 @@-{-# LANGUAGE ViewPatterns #-}--module Ide.Plugin.Tactic.KnownStrategies.QuickCheck where--import Control.Monad.Except (MonadError(throwError))-import Data.Bool (bool)-import Data.List (partition)-import DataCon ( DataCon, dataConName )-import Development.IDE.GHC.Compat (HsExpr, GhcPs, noLoc)-import GHC.Exts ( IsString(fromString) )-import GHC.List ( foldl' )-import GHC.SourceGen (int)-import GHC.SourceGen.Binds ( match, valBind )-import GHC.SourceGen.Expr ( case', lambda, let' )-import GHC.SourceGen.Overloaded ( App((@@)), HasList(list) )-import GHC.SourceGen.Pat ( conP )-import Ide.Plugin.Tactic.CodeGen-import Ide.Plugin.Tactic.Judgements (jGoal)-import Ide.Plugin.Tactic.Machinery (tracePrim)-import Ide.Plugin.Tactic.Types-import OccName (occNameString,  mkVarOcc, HasOccName(occName) )-import Refinery.Tactic (goal,  rule )-import TyCon (tyConName,  TyCon, tyConDataCons )-import Type ( splitTyConApp_maybe )-import Data.Generics (mkQ, everything)------------------------------------------------------------------------------------ | Known tactic for deriving @arbitrary :: Gen a@. This tactic splits the--- type's data cons into terminal and inductive cases, and generates code that--- produces a terminal if the QuickCheck size parameter is <=1, or any data con--- otherwise. It correctly scales recursive parameters, ensuring termination.-deriveArbitrary :: TacticsM ()-deriveArbitrary = do-  ty <- jGoal <$> goal-  case splitTyConApp_maybe $ unCType ty of-    Just (gen_tc, [splitTyConApp_maybe -> Just (tc, apps)])-        | occNameString (occName $ tyConName gen_tc) == "Gen" -> do-      rule $ \_ -> do-        let dcs = tyConDataCons tc-            (terminal, big) = partition ((== 0) . genRecursiveCount)-                        $ fmap (mkGenerator tc apps) dcs-            terminal_expr = mkVal "terminal"-            oneof_expr = mkVal "oneof"-        pure-          ( tracePrim "deriveArbitrary"-          , noLoc $-              let' [valBind (fromString "terminal") $ list $ fmap genExpr terminal] $-                appDollar (mkFunc "sized") $ lambda [bvar' (mkVarOcc "n")] $-                  case' (infixCall "<=" (mkVal "n") (int 1))-                    [ match [conP (fromString "True") []] $-                        oneof_expr @@ terminal_expr-                    , match [conP (fromString "False") []] $-                        appDollar oneof_expr $-                          infixCall "<>"-                            (list $ fmap genExpr big)-                            terminal_expr-                    ]-          )-    _ -> throwError $ GoalMismatch "deriveArbitrary" ty------------------------------------------------------------------------------------ | Helper data type for the generator of a specific data con.-data Generator = Generator-  { genRecursiveCount :: Integer-  , genExpr :: HsExpr GhcPs-  }------------------------------------------------------------------------------------ | Make a 'Generator' for a given tycon instantiated with the given @[Type]@.-mkGenerator :: TyCon -> [Type] -> DataCon -> Generator-mkGenerator tc apps dc = do-  let dc_expr   = var' $ occName $ dataConName dc-      args = dataConInstOrigArgTys' dc apps-      num_recursive_calls = sum $ fmap (bool 0 1 . doesTypeContain tc) args-      mkArbitrary = mkArbitraryCall tc num_recursive_calls-  Generator num_recursive_calls $ case args of-    []  -> mkFunc "pure" @@ dc_expr-    (a : as) ->-      foldl'-        (infixCall "<*>")-        (infixCall "<$>" dc_expr $ mkArbitrary a)-        (fmap mkArbitrary as)------------------------------------------------------------------------------------ | Check if the given 'TyCon' exists anywhere in the 'Type'.-doesTypeContain :: TyCon -> Type -> Bool-doesTypeContain recursive_tc =-  everything (||) $ mkQ False (== recursive_tc)------------------------------------------------------------------------------------ | Generate the correct sort of call to @arbitrary@. For recursive calls, we--- need to scale down the size parameter, either by a constant factor of 1 if--- it's the only recursive parameter, or by @`div` n@ where n is the number of--- recursive parameters. For all other types, just call @arbitrary@ directly.-mkArbitraryCall :: TyCon -> Integer -> Type -> HsExpr GhcPs-mkArbitraryCall recursive_tc n ty =-  let arbitrary = mkFunc "arbitrary"-   in case doesTypeContain recursive_tc ty of-        True ->-          mkFunc "scale"-            @@ bool (mkFunc "flip" @@ mkFunc "div" @@ int n)-                    (mkFunc "subtract" @@ int 1)-                    (n == 1)-            @@ arbitrary-        False -> arbitrary
− src/Ide/Plugin/Tactic/LanguageServer.hs
@@ -1,212 +0,0 @@-{-# LANGUAGE FlexibleContexts    #-}-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE LambdaCase          #-}-{-# LANGUAGE OverloadedStrings   #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Ide.Plugin.Tactic.LanguageServer where--import           Control.Arrow-import           Control.Monad-import           Control.Monad.Trans.Maybe-import           Data.Aeson                           (Value (Object), fromJSON)-import           Data.Aeson.Types                     (Result (Error, Success))-import           Data.Coerce-import           Data.Functor                         ((<&>))-import           Data.Generics.Aliases                (mkQ)-import           Data.Generics.Schemes                (everything)-import           Data.Map                             (Map)-import qualified Data.Map                             as M-import           Data.Maybe-import           Data.Monoid-import qualified Data.Set                             as S-import qualified Data.Text                            as T-import           Data.Traversable-import           Development.IDE                      (ShakeExtras,-                                                       getPluginConfig)-import           Development.IDE.Core.PositionMapping-import           Development.IDE.Core.RuleTypes-import           Development.IDE.Core.Service         (runAction)-import           Development.IDE.Core.Shake           (IdeState (..),-                                                       useWithStale)-import           Development.IDE.GHC.Compat-import           Development.IDE.GHC.Error            (realSrcSpanToRange)-import           Development.IDE.Spans.LocalBindings  (Bindings,-                                                       getDefiningBindings)-import           Development.Shake                    (Action, RuleResult)-import           Development.Shake.Classes-import qualified FastString-import           Ide.Plugin.Config                    (PluginConfig (plcConfig))-import qualified Ide.Plugin.Config                    as Plugin-import           Ide.Plugin.Tactic.Context-import           Ide.Plugin.Tactic.FeatureSet-import           Ide.Plugin.Tactic.GHC-import           Ide.Plugin.Tactic.Judgements-import           Ide.Plugin.Tactic.Range-import           Ide.Plugin.Tactic.TestTypes          (TacticCommand,-                                                       cfg_feature_set)-import           Ide.Plugin.Tactic.Types-import           Language.LSP.Server                  (MonadLsp)-import           Language.LSP.Types-import           OccName-import           Prelude                              hiding (span)-import           SrcLoc                               (containsSpan)-import           TcRnTypes                            (tcg_binds)---tacticDesc :: T.Text -> T.Text-tacticDesc name = "fill the hole using the " <> name <> " tactic"------------------------------------------------------------------------------------ | The name of the command for the LS.-tcCommandName :: TacticCommand -> T.Text-tcCommandName = T.pack . show---runIde :: IdeState -> Action a -> IO a-runIde state = runAction "tactic" state---runStaleIde-    :: forall a r-     . ( r ~ RuleResult a-       , Eq a , Hashable a , Binary a , Show a , Typeable a , NFData a-       , Show r, Typeable r, NFData r-       )-    => IdeState-    -> NormalizedFilePath-    -> a-    -> MaybeT IO (r, PositionMapping)-runStaleIde state nfp a = MaybeT $ runIde state $ useWithStale a nfp------------------------------------------------------------------------------------ | Get the current feature set from the plugin config.-getFeatureSet :: MonadLsp Plugin.Config m => ShakeExtras -> m FeatureSet-getFeatureSet extras = do-  pcfg <- getPluginConfig extras "tactics"-  pure $ case fromJSON $ Object $ plcConfig pcfg of-    Success cfg -> cfg_feature_set cfg-    Error _     -> defaultFeatures---getIdeDynflags-    :: IdeState-    -> NormalizedFilePath-    -> MaybeT IO DynFlags-getIdeDynflags state nfp = do-  -- Ok to use the stale 'ModIface', since all we need is its 'DynFlags'-  -- which don't change very often.-  ((modsum,_), _) <- runStaleIde state nfp GetModSummaryWithoutTimestamps-  pure $ ms_hspp_opts modsum------------------------------------------------------------------------------------ | Find the last typechecked module, and find the most specific span, as well--- as the judgement at the given range.-judgementForHole-    :: IdeState-    -> NormalizedFilePath-    -> Range-    -> FeatureSet-    -> MaybeT IO (Range, Judgement, Context, DynFlags)-judgementForHole state nfp range features = do-  (asts, amapping) <- runStaleIde state nfp GetHieAst-  case asts of-    HAR _ _  _ _ (HieFromDisk _) -> fail "Need a fresh hie file"-    HAR _ hf _ _ HieFresh -> do-      (binds, _) <- runStaleIde state nfp GetBindings-      (tcmod, _) <- runStaleIde state nfp TypeCheck-      (rss, g)   <- liftMaybe $ getSpanAndTypeAtHole amapping range hf-      resulting_range <- liftMaybe $ toCurrentRange amapping $ realSrcSpanToRange rss-      let (jdg, ctx) = mkJudgementAndContext features g binds rss tcmod-      dflags <- getIdeDynflags state nfp-      pure (resulting_range, jdg, ctx, dflags)---mkJudgementAndContext-    :: FeatureSet-    -> Type-    -> Bindings-    -> RealSrcSpan-    -> TcModuleResult-    -> (Judgement, Context)-mkJudgementAndContext features g binds rss tcmod = do-      let tcg  = tmrTypechecked tcmod-          tcs = tcg_binds tcg-          ctx = mkContext features-                  (mapMaybe (sequenceA . (occName *** coerce))-                    $ getDefiningBindings binds rss)-                  tcg-          top_provs = getRhsPosVals rss tcs-          local_hy = spliceProvenance top_provs-                   $ hypothesisFromBindings rss binds-          cls_hy = contextMethodHypothesis ctx-       in ( mkFirstJudgement-              (local_hy <> cls_hy)-              (isRhsHole rss tcs)-              g-          , ctx-          )---getSpanAndTypeAtHole-    :: PositionMapping-    -> Range-    -> HieASTs b-    -> Maybe (Span, b)-getSpanAndTypeAtHole amapping range hf = do-  range' <- fromCurrentRange amapping range-  join $ listToMaybe $ M.elems $ flip M.mapWithKey (getAsts hf) $ \fs ast ->-    case selectSmallestContaining (rangeToRealSrcSpan (FastString.unpackFS fs) range') ast of-      Nothing -> Nothing-      Just ast' -> do-        let info = nodeInfo ast'-        ty <- listToMaybe $ nodeType info-        guard $ ("HsUnboundVar","HsExpr") `S.member` nodeAnnotations info-        pure (nodeSpan ast', ty)---liftMaybe :: Monad m => Maybe a -> MaybeT m a-liftMaybe a = MaybeT $ pure a---spliceProvenance-    :: Map OccName Provenance-    -> Hypothesis a-    -> Hypothesis a-spliceProvenance provs x =-  Hypothesis $ flip fmap (unHypothesis x) $ \hi ->-    overProvenance (maybe id const $ M.lookup (hi_name hi) provs) hi------------------------------------------------------------------------------------ | Compute top-level position vals of a function-getRhsPosVals :: RealSrcSpan -> TypecheckedSource -> Map OccName Provenance-getRhsPosVals rss tcs-  = M.fromList-  $ join-  $ maybeToList-  $ getFirst-  $ everything (<>) (mkQ mempty $ \case-      TopLevelRHS name ps-          (L (RealSrcSpan span)  -- body with no guards and a single defn-            (HsVar _ (L _ hole)))-        | containsSpan rss span  -- which contains our span-        , isHole $ occName hole  -- and the span is a hole-        -> First $ do-            patnames <- traverse getPatName ps-            pure $ zip patnames $ [0..] <&> TopLevelArgPrv name-      _ -> mempty-  ) tcs------------------------------------------------------------------------------------ | Is this hole immediately to the right of an equals sign?-isRhsHole :: RealSrcSpan -> TypecheckedSource -> Bool-isRhsHole rss tcs = everything (||) (mkQ False $ \case-  TopLevelRHS _ _ (L (RealSrcSpan span) _) -> containsSpan rss span-  _                                        -> False-  ) tcs-
− src/Ide/Plugin/Tactic/LanguageServer/TacticProviders.hs
@@ -1,192 +0,0 @@-{-# LANGUAGE DeriveAnyClass     #-}-{-# LANGUAGE DeriveGeneric      #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE OverloadedStrings  #-}-{-# LANGUAGE ViewPatterns       #-}--module Ide.Plugin.Tactic.LanguageServer.TacticProviders-  ( commandProvider-  , commandTactic-  , tcCommandId-  , TacticParams (..)-  ) where--import           Control.Monad-import           Control.Monad.Error.Class (MonadError(throwError))-import           Data.Aeson-import           Data.Coerce-import qualified Data.Map as M-import           Data.Maybe-import           Data.Monoid-import qualified Data.Text as T-import           Data.Traversable-import           Development.IDE.GHC.Compat-import           GHC.Generics-import           GHC.LanguageExtensions.Type (Extension (LambdaCase))-import           Ide.Plugin.Tactic.Auto-import           Ide.Plugin.Tactic.FeatureSet-import           Ide.Plugin.Tactic.GHC-import           Ide.Plugin.Tactic.Judgements-import           Ide.Plugin.Tactic.Tactics-import           Ide.Plugin.Tactic.TestTypes-import           Ide.Plugin.Tactic.Types-import           Ide.PluginUtils-import           Ide.Types-import           Language.LSP.Types-import           OccName-import           Prelude hiding (span)-import           Refinery.Tactic (goal)------------------------------------------------------------------------------------ | A mapping from tactic commands to actual tactics for refinery.-commandTactic :: TacticCommand -> OccName -> TacticsM ()-commandTactic Auto         = const auto-commandTactic Intros       = const intros-commandTactic Destruct     = useNameFromHypothesis destruct-commandTactic Homomorphism = useNameFromHypothesis homo-commandTactic DestructLambdaCase     = const destructLambdaCase-commandTactic HomomorphismLambdaCase = const homoLambdaCase------------------------------------------------------------------------------------ | Mapping from tactic commands to their contextual providers. See 'provide',--- 'filterGoalType' and 'filterBindingType' for the nitty gritty.-commandProvider :: TacticCommand -> TacticProvider-commandProvider Auto  = provide Auto ""-commandProvider Intros =-  filterGoalType isFunction $-    provide Intros ""-commandProvider Destruct =-  filterBindingType destructFilter $ \occ _ ->-    provide Destruct $ T.pack $ occNameString occ-commandProvider Homomorphism =-  filterBindingType homoFilter $ \occ _ ->-    provide Homomorphism $ T.pack $ occNameString occ-commandProvider DestructLambdaCase =-  requireExtension LambdaCase $-    filterGoalType (isJust . lambdaCaseable) $-      provide DestructLambdaCase ""-commandProvider HomomorphismLambdaCase =-  requireExtension LambdaCase $-    filterGoalType ((== Just True) . lambdaCaseable) $-      provide HomomorphismLambdaCase ""------------------------------------------------------------------------------------ | A 'TacticProvider' is a way of giving context-sensitive actions to the LS--- UI.-type TacticProvider-     = DynFlags-    -> FeatureSet-    -> PluginId-    -> Uri-    -> Range-    -> Judgement-    -> IO [Command |? CodeAction]---data TacticParams = TacticParams-    { tp_file     :: Uri    -- ^ Uri of the file to fill the hole in-    , tp_range    :: Range  -- ^ The range of the hole-    , tp_var_name :: T.Text-    }-  deriving stock (Show, Eq, Generic)-  deriving anyclass (ToJSON, FromJSON)------------------------------------------------------------------------------------ | Restrict a 'TacticProvider', making sure it appears only when the given--- 'Feature' is in the feature set.-requireFeature :: Feature -> TacticProvider -> TacticProvider-requireFeature f tp dflags fs plId uri range jdg = do-  guard $ hasFeature f fs-  tp dflags fs plId uri range jdg------------------------------------------------------------------------------------ | Restrict a 'TacticProvider', making sure it appears only when the given--- predicate holds for the goal.-requireExtension :: Extension -> TacticProvider -> TacticProvider-requireExtension ext tp dflags fs plId uri range jdg =-  case xopt ext dflags of-    True  -> tp dflags fs plId uri range jdg-    False -> pure []------------------------------------------------------------------------------------ | Restrict a 'TacticProvider', making sure it appears only when the given--- predicate holds for the goal.-filterGoalType :: (Type -> Bool) -> TacticProvider -> TacticProvider-filterGoalType p tp dflags fs plId uri range jdg =-  case p $ unCType $ jGoal jdg of-    True  -> tp dflags fs plId uri range jdg-    False -> pure []------------------------------------------------------------------------------------ | Multiply a 'TacticProvider' for each binding, making sure it appears only--- when the given predicate holds over the goal and binding types.-filterBindingType-    :: (Type -> Type -> Bool)  -- ^ Goal and then binding types.-    -> (OccName -> Type -> TacticProvider)-    -> TacticProvider-filterBindingType p tp dflags fs plId uri range jdg =-  let hy = jHypothesis jdg-      g  = jGoal jdg-   in fmap join $ for (unHypothesis hy) $ \hi ->-        let ty = unCType $ hi_type hi-         in case p (unCType g) ty of-              True  -> tp (hi_name hi) ty dflags fs plId uri range jdg-              False -> pure []------------------------------------------------------------------------------------- | Lift a function over 'HyInfo's to one that takes an 'OccName' and tries to--- look it up in the hypothesis.-useNameFromHypothesis :: (HyInfo CType -> TacticsM a) -> OccName -> TacticsM a-useNameFromHypothesis f name = do-  hy <- jHypothesis <$> goal-  case M.lookup name $ hyByName hy of-    Just hi -> f hi-    Nothing -> throwError $ NotInScope name------------------------------------------------------------------------------------ | Terminal constructor for providing context-sensitive tactics. Tactics--- given by 'provide' are always available.-provide :: TacticCommand -> T.Text -> TacticProvider-provide tc name _ _ plId uri range _ = do-  let title = tacticTitle tc name-      params = TacticParams { tp_file = uri , tp_range = range , tp_var_name = name }-      cmd = mkLspCommand plId (tcCommandId tc) title (Just [toJSON params])-  pure-    $ pure-    $ InR-    $ CodeAction title (Just CodeActionQuickFix) Nothing Nothing Nothing Nothing-    $ Just cmd------------------------------------------------------------------------------------ | Construct a 'CommandId'-tcCommandId :: TacticCommand -> CommandId-tcCommandId c = coerce $ T.pack $ "tactics" <> show c <> "Command"------------------------------------------------------------------------------------- | We should show homos only when the goal type is the same as the binding--- type, and that both are usual algebraic types.-homoFilter :: Type -> Type -> Bool-homoFilter (algebraicTyCon -> Just t1) (algebraicTyCon -> Just t2) = t1 == t2-homoFilter _ _ = False------------------------------------------------------------------------------------ | We should show destruct for bindings only when those bindings have usual--- algebraic types.-destructFilter :: Type -> Type -> Bool-destructFilter _ (algebraicTyCon -> Just _) = True-destructFilter _ _ = False-
− src/Ide/Plugin/Tactic/Machinery.hs
@@ -1,253 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE DerivingVia           #-}-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE LambdaCase            #-}-{-# LANGUAGE MonoLocalBinds        #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RecordWildCards       #-}-{-# LANGUAGE ViewPatterns          #-}-{-# OPTIONS_GHC -fno-warn-orphans  #-}--module Ide.Plugin.Tactic.Machinery-  ( module Ide.Plugin.Tactic.Machinery-  ) where--import           Class (Class(classTyVars))-import           Control.Arrow-import           Control.Monad.Error.Class-import           Control.Monad.Reader-import           Control.Monad.State (MonadState(..))-import           Control.Monad.State.Class (gets, modify)-import           Control.Monad.State.Strict (StateT (..))-import           Data.Bool (bool)-import           Data.Coerce-import           Data.Either-import           Data.Foldable-import           Data.Functor ((<&>))-import           Data.Generics (mkQ, everything, gcount)-import           Data.List (sortBy)-import qualified Data.Map as M-import           Data.Ord (comparing, Down(..))-import           Data.Set (Set)-import qualified Data.Set as S-import           Development.IDE.GHC.Compat-import           Ide.Plugin.Tactic.Judgements-import           Ide.Plugin.Tactic.Simplify (simplify)-import           Ide.Plugin.Tactic.Types-import           OccName (HasOccName(occName))-import           Refinery.ProofState-import           Refinery.Tactic-import           Refinery.Tactic.Internal-import           TcType-import           Type-import           Unify---substCTy :: TCvSubst -> CType -> CType-substCTy subst = coerce . substTy subst . coerce------------------------------------------------------------------------------------ | Produce a subgoal that must be solved before we can solve the original--- goal.-newSubgoal-    :: Judgement-    -> Rule-newSubgoal j = do-    unifier <- gets ts_unifier-    subgoal-      $ substJdg unifier-      $ unsetIsTopHole j------------------------------------------------------------------------------------ | Attempt to generate a term of the right type using in-scope bindings, and--- a given tactic.-runTactic-    :: Context-    -> Judgement-    -> TacticsM ()       -- ^ Tactic to use-    -> Either [TacticError] RunTacticResults-runTactic ctx jdg t =-    let skolems = S.fromList-                $ foldMap (tyCoVarsOfTypeWellScoped . unCType)-                $ (:) (jGoal jdg)-                $ fmap hi_type-                $ toList-                $ hyByName-                $ jHypothesis jdg-        unused_topvals = M.keysSet-                       $ M.filter (isTopLevel . hi_provenance)-                       $ hyByName-                       $ jHypothesis jdg-        tacticState =-          defaultTacticState-            { ts_skolems = skolems-            , ts_unused_top_vals = unused_topvals-            }-    in case partitionEithers-          . flip runReader ctx-          . unExtractM-          $ runTacticT t jdg tacticState of-      (errs, []) -> Left $ take 50 errs-      (_, fmap assoc23 -> solns) -> do-        let sorted =-              flip sortBy solns $ comparing $ \((_, ext), (jdg, holes)) ->-                Down $ scoreSolution ext jdg holes-        case sorted of-          (((tr, ext), _) : _) ->-            Right $-              RunTacticResults-                { rtr_trace = tr-                , rtr_extract = simplify ext-                , rtr_other_solns = reverse . fmap fst $ take 5 sorted-                , rtr_jdg = jdg-                , rtr_ctx = ctx-                }-          -- guaranteed to not be empty-          _ -> Left []--assoc23 :: (a, b, c) -> (a, (b, c))-assoc23 (a, b, c) = (a, (b, c))---tracePrim :: String -> Trace-tracePrim = flip rose []---tracing-    :: Functor m-    => String-    -> TacticT jdg (Trace, ext) err s m a-    -> TacticT jdg (Trace, ext) err s m a-tracing s (TacticT m)-  = TacticT $ StateT $ \jdg ->-      mapExtract' (first $ rose s . pure) $ runStateT m jdg------------------------------------------------------------------------------------ | Recursion is allowed only when we can prove it is on a structurally--- smaller argument. The top of the 'ts_recursion_stack' witnesses the smaller--- pattern val.-guardStructurallySmallerRecursion-    :: TacticState-    -> Maybe TacticError-guardStructurallySmallerRecursion s =-  case head $ ts_recursion_stack s of-     Just _  -> Nothing-     Nothing -> Just NoProgress------------------------------------------------------------------------------------ | Mark that the current recursive call is structurally smaller, due to--- having been matched on a pattern value.------ Implemented by setting the top of the 'ts_recursion_stack'.-markStructuralySmallerRecursion :: MonadState TacticState m => PatVal -> m ()-markStructuralySmallerRecursion pv = do-  modify $ withRecursionStack $ \case-    (_ : bs) -> Just pv : bs-    []       -> []------------------------------------------------------------------------------------ | Given the results of running a tactic, score the solutions by--- desirability.------ TODO(sandy): This function is completely unprincipled and was just hacked--- together to produce the right test results.-scoreSolution-    :: LHsExpr GhcPs-    -> TacticState-    -> [Judgement]-    -> ( Penalize Int  -- number of holes-       , Reward Bool   -- all bindings used-       , Penalize Int  -- unused top-level bindings-       , Penalize Int  -- number of introduced bindings-       , Reward Int    -- number used bindings-       , Penalize Int  -- number of recursive calls-       , Penalize Int  -- size of extract-       )-scoreSolution ext TacticState{..} holes-  = ( Penalize $ length holes-    , Reward   $ S.null $ ts_intro_vals S.\\ ts_used_vals-    , Penalize $ S.size ts_unused_top_vals-    , Penalize $ S.size ts_intro_vals-    , Reward   $ S.size ts_used_vals-    , Penalize ts_recursion_count-    , Penalize $ solutionSize ext-    )------------------------------------------------------------------------------------ | Compute the number of 'LHsExpr' nodes; used as a rough metric for code--- size.-solutionSize :: LHsExpr GhcPs -> Int-solutionSize = everything (+) $ gcount $ mkQ False $ \case-  (_ :: LHsExpr GhcPs) -> True---newtype Penalize a = Penalize a-  deriving (Eq, Ord, Show) via (Down a)--newtype Reward a = Reward a-  deriving (Eq, Ord, Show) via a------------------------------------------------------------------------------------ | Like 'tcUnifyTy', but takes a list of skolems to prevent unification of.-tryUnifyUnivarsButNotSkolems :: Set TyVar -> CType -> CType -> Maybe TCvSubst-tryUnifyUnivarsButNotSkolems skolems goal inst =-  case tcUnifyTysFG-         (bool BindMe Skolem . flip S.member skolems)-         [unCType inst]-         [unCType goal] of-    Unifiable subst -> pure subst-    _ -> Nothing------------------------------------------------------------------------------------- | Attempt to unify two types.-unify :: CType -- ^ The goal type-      -> CType -- ^ The type we are trying unify the goal type with-      -> RuleM ()-unify goal inst = do-  skolems <- gets ts_skolems-  case tryUnifyUnivarsButNotSkolems skolems goal inst of-    Just subst ->-      modify (\s -> s { ts_unifier = unionTCvSubst subst (ts_unifier s) })-    Nothing -> throwError (UnificationError inst goal)------------------------------------------------------------------------------------ | Get the class methods of a 'PredType', correctly dealing with--- instantiation of quantified class types.-methodHypothesis :: PredType -> Maybe [HyInfo CType]-methodHypothesis ty = do-  (tc, apps) <- splitTyConApp_maybe ty-  cls <- tyConClass_maybe tc-  let methods = classMethods cls-      tvs     = classTyVars cls-      subst   = zipTvSubst tvs apps-  sc_methods <- fmap join-              $ traverse (methodHypothesis . substTy subst)-              $ classSCTheta cls-  pure $ mappend sc_methods $ methods <&> \method ->-    let (_, _, ty) = tcSplitSigmaTy $ idType method-    in ( HyInfo (occName method) (ClassMethodPrv $ Uniquely cls) $ CType $ substTy subst ty-       )------------------------------------------------------------------------------------ | Run the given tactic iff the current hole contains no univars. Skolems and--- already decided univars are OK though.-requireConcreteHole :: TacticsM a -> TacticsM a-requireConcreteHole m = do-  jdg     <- goal-  skolems <- gets ts_skolems-  let vars = S.fromList $ tyCoVarsOfTypeWellScoped $ unCType $ jGoal jdg-  case S.size $ vars S.\\ skolems of-    0 -> m-    _ -> throwError TooPolymorphic
− src/Ide/Plugin/Tactic/Naming.hs
@@ -1,97 +0,0 @@-{-# LANGUAGE ViewPatterns #-}--module Ide.Plugin.Tactic.Naming where--import           Control.Monad.State.Strict-import           Data.Bool (bool)-import           Data.Char-import           Data.Map (Map)-import qualified Data.Map as M-import           Data.Set (Set)-import qualified Data.Set as S-import           Data.Traversable-import           Name-import           TcType-import           TyCon-import           Type-import           TysWiredIn (listTyCon, pairTyCon, unitTyCon)------------------------------------------------------------------------------------ | Use type information to create a reasonable name.-mkTyName :: Type -> String--- eg. mkTyName (a -> B) = "fab"-mkTyName (tcSplitFunTys -> ([a@(isFunTy -> False)], b))-  = "f" ++ mkTyName a ++ mkTyName b--- eg. mkTyName (a -> b -> C) = "f_C"-mkTyName (tcSplitFunTys -> (_:_, b))-  = "f_" ++ mkTyName b--- eg. mkTyName (Either A B) = "eab"-mkTyName (splitTyConApp_maybe -> Just (c, args))-  = mkTyConName c ++ foldMap mkTyName args--- eg. mkTyName (f a) = "fa"-mkTyName (tcSplitAppTys -> (t, args@(_:_)))-  = mkTyName t ++ foldMap mkTyName args--- eg. mkTyName a = "a"-mkTyName (getTyVar_maybe -> Just tv)-  = occNameString $ occName tv--- eg. mkTyName (forall x. y) = "y"-mkTyName (tcSplitSigmaTy -> (_:_, _, t))-  = mkTyName t-mkTyName _ = "x"------------------------------------------------------------------------------------ | Get a good name for a type constructor.-mkTyConName :: TyCon -> String-mkTyConName tc-  | tc == listTyCon = "l_"-  | tc == pairTyCon = "p_"-  | tc == unitTyCon = "unit"-  | otherwise-      = take 1-      . fmap toLower-      . filterReplace isSymbol      's'-      . filterReplace isPunctuation 'p'-      . occNameString-      $ getOccName tc------------------------------------------------------------------------------------ | Maybe replace an element in the list if the predicate matches-filterReplace :: (a -> Bool) -> a -> [a] -> [a]-filterReplace f r = fmap (\a -> bool a r $ f a)------------------------------------------------------------------------------------ | Produce a unique, good name for a type.-mkGoodName-    :: Set OccName  -- ^ Bindings in scope; used to ensure we don't shadow anything-    -> Type       -- ^ The type to produce a name for-    -> OccName-mkGoodName in_scope t =-  let tn = mkTyName t-   in mkVarOcc $ case S.member (mkVarOcc tn) in_scope of-        True -> tn ++ show (length in_scope)-        False -> tn------------------------------------------------------------------------------------ | Like 'mkGoodName' but creates several apart names.-mkManyGoodNames-  :: (Traversable t, Monad m)-  => Set OccName-  -> t Type-  -> m (t OccName)-mkManyGoodNames in_scope args =-  flip evalStateT in_scope $ for args $ \at -> do-    in_scope <- get-    let n = mkGoodName in_scope at-    modify $ S.insert n-    pure n------------------------------------------------------------------------------------ | Which names are in scope?-getInScope :: Map OccName a -> [OccName]-getInScope = M.keys
− src/Ide/Plugin/Tactic/Range.hs
@@ -1,18 +0,0 @@-module Ide.Plugin.Tactic.Range where--import qualified FastString as FS-import           Development.IDE.Types.Location-import           SrcLoc----------------------------------------------------------------------------------- | Convert a DAML compiler Range to a GHC SrcSpan--- TODO(sandy): this doesn't belong here-rangeToSrcSpan :: String -> Range -> SrcSpan-rangeToSrcSpan file range = RealSrcSpan $ rangeToRealSrcSpan file range--rangeToRealSrcSpan :: String -> Range -> RealSrcSpan-rangeToRealSrcSpan file (Range (Position startLn startCh) (Position endLn endCh)) =-    mkRealSrcSpan-      (mkRealSrcLoc (FS.fsLit file) (startLn + 1) (startCh + 1))-      (mkRealSrcLoc (FS.fsLit file) (endLn + 1) (endCh + 1))-
− src/Ide/Plugin/Tactic/Simplify.hs
@@ -1,108 +0,0 @@-{-# LANGUAGE LambdaCase          #-}-{-# LANGUAGE OverloadedStrings   #-}-{-# LANGUAGE PatternSynonyms     #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE ViewPatterns        #-}--module Ide.Plugin.Tactic.Simplify-  ( simplify-  ) where--import Data.Generics (everywhere, mkT, GenericT)-import Data.List.Extra (unsnoc)-import Data.Monoid (Endo (..))-import Development.IDE.GHC.Compat-import GHC.SourceGen (var)-import GHC.SourceGen.Expr (lambda)-import Ide.Plugin.Tactic.CodeGen.Utils-import Ide.Plugin.Tactic.GHC (fromPatCompatPs, containsHsVar)------------------------------------------------------------------------------------ | A pattern over the otherwise (extremely) messy AST for lambdas.-pattern Lambda :: [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs-pattern Lambda pats body <--  HsLam _-    (MG {mg_alts = L _ [L _-      (Match { m_pats = fmap fromPatCompatPs -> pats-             , m_grhss = GRHSs {grhssGRHSs = [L _ (-                 GRHS _ [] (L _ body))]}-             })]})-  where-    -- If there are no patterns to bind, just stick in the body-    Lambda [] body   = body-    Lambda pats body = lambda pats body------------------------------------------------------------------------------------ | Simlify an expression.-simplify :: LHsExpr GhcPs -> LHsExpr GhcPs-simplify-  = head-  . drop 3   -- Do three passes; this should be good enough for the limited-             -- amount of gas we give to auto-  . iterate (everywhere $ foldEndo-    [ simplifyEtaReduce-    , simplifyRemoveParens-    , simplifyCompose-    ])------------------------------------------------------------------------------------ | Like 'foldMap' but for endomorphisms.-foldEndo :: Foldable t => t (a -> a) -> a -> a-foldEndo = appEndo . foldMap Endo------------------------------------------------------------------------------------ | Perform an eta reduction. For example, transforms @\x -> (f g) x@ into--- @f g@.-simplifyEtaReduce :: GenericT-simplifyEtaReduce = mkT $ \case-  Lambda-      [VarPat _ (L _ pat)]-      (HsVar _ (L _ a)) | pat == a ->-    var "id"-  Lambda-      (unsnoc -> Just (pats, (VarPat _ (L _ pat))))-      (HsApp _ (L _ f) (L _ (HsVar _ (L _ a))))-      | pat == a-        -- We can only perform this simplifiation if @pat@ is otherwise unused.-      , not (containsHsVar pat f) ->-    Lambda pats f-  x -> x------------------------------------------------------------------------------------ | Perform an eta-reducing function composition. For example, transforms--- @\x -> f (g (h x))@ into @f . g . h@.-simplifyCompose :: GenericT-simplifyCompose = mkT $ \case-  Lambda-      (unsnoc -> Just (pats, (VarPat _ (L _ pat))))-      (unroll -> (fs@(_:_), (HsVar _ (L _ a))))-      | pat == a-        -- We can only perform this simplifiation if @pat@ is otherwise unused.-      , not (containsHsVar pat fs) ->-    Lambda pats (foldr1 (infixCall ".") fs)-  x -> x------------------------------------------------------------------------------------ | Removes unnecessary parentheses on any token that doesn't need them.-simplifyRemoveParens :: GenericT-simplifyRemoveParens = mkT $ \case-  HsPar _ (L _ x) | isAtomicHsExpr x -> x-  (x :: HsExpr GhcPs) -> x------------------------------------------------------------------------------------ | Unrolls a right-associative function application of the form--- @HsApp f (HsApp g (HsApp h x))@ into @([f, g, h], x)@.-unroll :: HsExpr GhcPs -> ([HsExpr GhcPs], HsExpr GhcPs)-unroll (HsPar _ (L _ x)) = unroll x-unroll (HsApp _ (L _ f) (L _ a)) =-  let (fs, r) = unroll a-   in (f : fs, r)-unroll x = ([], x)-
− src/Ide/Plugin/Tactic/Tactics.hs
@@ -1,281 +0,0 @@-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ViewPatterns          #-}--module Ide.Plugin.Tactic.Tactics-  ( module Ide.Plugin.Tactic.Tactics-  , runTactic-  ) where--import           Control.Monad (when)-import           Control.Monad.Except (throwError)-import           Control.Monad.Reader.Class (MonadReader(ask))-import           Control.Monad.State.Class-import           Control.Monad.State.Strict (StateT(..), runStateT)-import           Data.Bool (bool)-import           Data.Foldable-import           Data.List-import qualified Data.Map as M-import           Data.Maybe-import           Data.Set (Set)-import qualified Data.Set as S-import           DataCon-import           Development.IDE.GHC.Compat-import           GHC.Exts-import           GHC.SourceGen.Expr-import           GHC.SourceGen.Overloaded-import           Ide.Plugin.Tactic.CodeGen-import           Ide.Plugin.Tactic.Context-import           Ide.Plugin.Tactic.GHC-import           Ide.Plugin.Tactic.Judgements-import           Ide.Plugin.Tactic.Machinery-import           Ide.Plugin.Tactic.Naming-import           Ide.Plugin.Tactic.Types-import           Name (occNameString)-import           Refinery.Tactic-import           Refinery.Tactic.Internal-import           TcType-import           Type hiding (Var)------------------------------------------------------------------------------------ | Use something in the hypothesis to fill the hole.-assumption :: TacticsM ()-assumption = attemptOn (S.toList . allNames) assume------------------------------------------------------------------------------------ | Use something named in the hypothesis to fill the hole.-assume :: OccName -> TacticsM ()-assume name = rule $ \jdg -> do-  let g  = jGoal jdg-  case M.lookup name $ hyByName $ jHypothesis jdg of-    Just (hi_type -> ty) -> do-      unify ty $ jGoal jdg-      for_ (M.lookup name $ jPatHypothesis jdg) markStructuralySmallerRecursion-      useOccName jdg name-      pure $ (tracePrim $ "assume " <> occNameString name, ) $ noLoc $ var' name-    Nothing -> throwError $ UndefinedHypothesis name---recursion :: TacticsM ()-recursion = requireConcreteHole $ tracing "recursion" $ do-  defs <- getCurrentDefinitions-  attemptOn (const defs) $ \(name, ty) -> do-    modify $ pushRecursionStack .  countRecursiveCall-    ensure guardStructurallySmallerRecursion popRecursionStack $ do-      (localTactic (apply $ HyInfo name RecursivePrv ty) $ introducingRecursively defs)-        <@> fmap (localTactic assumption . filterPosition name) [0..]------------------------------------------------------------------------------------ | Introduce a lambda binding every variable.-intros :: TacticsM ()-intros = rule $ \jdg -> do-  let hy = jHypothesis jdg-      g  = jGoal jdg-  ctx <- ask-  case tcSplitFunTys $ unCType g of-    ([], _) -> throwError $ GoalMismatch "intros" g-    (as, b) -> do-      vs <- mkManyGoodNames (hyNamesInScope $ jEntireHypothesis jdg) as-      let top_hole = isTopHole ctx jdg-          jdg' = introducingLambda top_hole (zip vs $ coerce as)-               $ withNewGoal (CType b) jdg-      modify $ withIntroducedVals $ mappend $ S.fromList vs-      when (isJust top_hole) $ addUnusedTopVals $ S.fromList vs-      (tr, sg) <- newSubgoal jdg'-      pure-        . (rose ("intros {" <> intercalate ", " (fmap show vs) <> "}") $ pure tr, )-        . noLoc-        . lambda (fmap bvar' vs)-        $ unLoc sg------------------------------------------------------------------------------------ | Case split, and leave holes in the matches.-destructAuto :: HyInfo CType -> TacticsM ()-destructAuto hi = requireConcreteHole $ tracing "destruct(auto)" $ do-  jdg <- goal-  let subtactic = rule $ destruct' (const subgoal) hi-  case isPatternMatch $ hi_provenance hi of-    True ->-      pruning subtactic $ \jdgs ->-        let getHyTypes = S.fromList . fmap hi_type . unHypothesis . jHypothesis-            new_hy = foldMap getHyTypes jdgs-            old_hy = getHyTypes jdg-        in case S.null $ new_hy S.\\ old_hy of-              True  -> Just $ UnhelpfulDestruct $ hi_name hi-              False -> Nothing-    False -> subtactic------------------------------------------------------------------------------------ | Case split, and leave holes in the matches.-destruct :: HyInfo CType -> TacticsM ()-destruct hi = requireConcreteHole $ tracing "destruct(user)" $-  rule $ destruct' (const subgoal) hi------------------------------------------------------------------------------------ | Case split, using the same data constructor in the matches.-homo :: HyInfo CType -> TacticsM ()-homo = requireConcreteHole . tracing "homo" . rule . destruct' (\dc jdg ->-  buildDataCon jdg dc $ snd $ splitAppTys $ unCType $ jGoal jdg)------------------------------------------------------------------------------------ | LambdaCase split, and leave holes in the matches.-destructLambdaCase :: TacticsM ()-destructLambdaCase = tracing "destructLambdaCase" $ rule $ destructLambdaCase' (const subgoal)------------------------------------------------------------------------------------ | LambdaCase split, using the same data constructor in the matches.-homoLambdaCase :: TacticsM ()-homoLambdaCase =-  tracing "homoLambdaCase" $-    rule $ destructLambdaCase' $ \dc jdg ->-      buildDataCon jdg dc-        . snd-        . splitAppTys-        . unCType-        $ jGoal jdg---apply :: HyInfo CType -> TacticsM ()-apply hi = requireConcreteHole $ tracing ("apply' " <> show (hi_name hi)) $ do-  jdg <- goal-  let hy = jHypothesis jdg-      g  = jGoal jdg-      ty = unCType $ hi_type hi-      func = hi_name hi-  ty' <- freshTyvars ty-  let (_, _, args, ret) = tacticsSplitFunTy ty'-  requireNewHoles $ rule $ \jdg -> do-    unify g (CType ret)-    useOccName jdg func-    (tr, sgs)-        <- fmap unzipTrace-        $ traverse ( newSubgoal-                    . blacklistingDestruct-                    . flip withNewGoal jdg-                    . CType-                    ) args-    pure-      . (tr, )-      . noLoc-      . foldl' (@@) (var' func)-      $ fmap unLoc sgs------------------------------------------------------------------------------------ | Choose between each of the goal's data constructors.-split :: TacticsM ()-split = tracing "split(user)" $ do-  jdg <- goal-  let g = jGoal jdg-  case splitTyConApp_maybe $ unCType g of-    Nothing -> throwError $ GoalMismatch "split" g-    Just (tc, _) -> do-      let dcs = tyConDataCons tc-      choice $ fmap splitDataCon dcs------------------------------------------------------------------------------------ | Choose between each of the goal's data constructors. Different than--- 'split' because it won't split a data con if it doesn't result in any new--- goals.-splitAuto :: TacticsM ()-splitAuto = requireConcreteHole $ tracing "split(auto)" $ do-  jdg <- goal-  let g = jGoal jdg-  case splitTyConApp_maybe $ unCType g of-    Nothing -> throwError $ GoalMismatch "split" g-    Just (tc, _) -> do-      let dcs = tyConDataCons tc-      case isSplitWhitelisted jdg of-        True -> choice $ fmap splitDataCon dcs-        False -> do-          choice $ flip fmap dcs $ \dc -> requireNewHoles $-            splitDataCon dc------------------------------------------------------------------------------------ | Allow the given tactic to proceed if and only if it introduces holes that--- have a different goal than current goal.-requireNewHoles :: TacticsM () -> TacticsM ()-requireNewHoles m = do-  jdg <- goal-  pruning m $ \jdgs ->-    case null jdgs || any (/= jGoal jdg) (fmap jGoal jdgs) of-      True  -> Nothing-      False -> Just NoProgress------------------------------------------------------------------------------------ | Attempt to instantiate the given data constructor to solve the goal.-splitDataCon :: DataCon -> TacticsM ()-splitDataCon dc =-  requireConcreteHole $ tracing ("splitDataCon:" <> show dc) $ rule $ \jdg -> do-    let g = jGoal jdg-    case splitTyConApp_maybe $ unCType g of-      Just (tc, apps) -> do-        case elem dc $ tyConDataCons tc of-          True -> buildDataCon (unwhitelistingSplit jdg) dc apps-          False -> throwError $ IncorrectDataCon dc-      Nothing -> throwError $ GoalMismatch "splitDataCon" g------------------------------------------------------------------------------------ | @matching f@ takes a function from a judgement to a @Tactic@, and--- then applies the resulting @Tactic@.-matching :: (Judgement -> TacticsM ()) -> TacticsM ()-matching f = TacticT $ StateT $ \s -> runStateT (unTacticT $ f s) s---attemptOn :: (Judgement -> [a]) -> (a -> TacticsM ()) -> TacticsM ()-attemptOn getNames tac = matching (choice . fmap (\s -> tac s) . getNames)---localTactic :: TacticsM a -> (Judgement -> Judgement) -> TacticsM a-localTactic t f = do-  TacticT $ StateT $ \jdg ->-    runStateT (unTacticT t) $ f jdg---auto' :: Int -> TacticsM ()-auto' 0 = throwError NoProgress-auto' n = do-  let loop = auto' (n - 1)-  try intros-  choice-    [ overFunctions $ \fname -> do-        apply fname-        loop-    , overAlgebraicTerms $ \aname -> do-        destructAuto aname-        loop-    , splitAuto >> loop-    , assumption >> loop-    , recursion-    ]--overFunctions :: (HyInfo CType -> TacticsM ()) -> TacticsM ()-overFunctions =-  attemptOn $ filter (isFunction . unCType . hi_type)-           . unHypothesis-           . jHypothesis--overAlgebraicTerms :: (HyInfo CType -> TacticsM ()) -> TacticsM ()-overAlgebraicTerms =-  attemptOn $ filter (isJust . algebraicTyCon . unCType . hi_type)-            . unHypothesis-            . jHypothesis---allNames :: Judgement -> Set OccName-allNames = hyNamesInScope . jHypothesis-
− src/Ide/Plugin/Tactic/TestTypes.hs
@@ -1,50 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Ide.Plugin.Tactic.TestTypes where--import qualified Data.Text as T-import Data.Aeson-import Ide.Plugin.Tactic.FeatureSet----------------------------------------------------------------------------------- | The list of tactics exposed to the outside world. These are attached to--- actual tactics via 'commandTactic' and are contextually provided to the--- editor via 'commandProvider'.-data TacticCommand-  = Auto-  | Intros-  | Destruct-  | Homomorphism-  | DestructLambdaCase-  | HomomorphismLambdaCase-  deriving (Eq, Ord, Show, Enum, Bounded)---- | Generate a title for the command.-tacticTitle :: TacticCommand -> T.Text -> T.Text-tacticTitle Auto _ = "Attempt to fill hole"-tacticTitle Intros _ = "Introduce lambda"-tacticTitle Destruct var = "Case split on " <> var-tacticTitle Homomorphism var = "Homomorphic case split on " <> var-tacticTitle DestructLambdaCase _ = "Lambda case split"-tacticTitle HomomorphismLambdaCase _ = "Homomorphic lambda case split"------------------------------------------------------------------------------------ | Plugin configuration for tactics-newtype Config = Config-  { cfg_feature_set :: FeatureSet-  }--emptyConfig :: Config-emptyConfig = Config defaultFeatures--instance ToJSON Config where-  toJSON (Config features) = object-    [ "features" .= prettyFeatureSet features-    ]--instance FromJSON Config where-  parseJSON = withObject "Config" $ \obj -> do-    features <- parseFeatureSet <$> obj .: "features"-    pure $ Config features-
− src/Ide/Plugin/Tactic/Types.hs
@@ -1,389 +0,0 @@-{-# LANGUAGE DeriveFunctor              #-}-{-# LANGUAGE DeriveGeneric              #-}-{-# LANGUAGE DerivingVia                #-}-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses      #-}-{-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE TypeApplications           #-}-{-# OPTIONS_GHC -fno-warn-orphans       #-}--module Ide.Plugin.Tactic.Types-  ( module Ide.Plugin.Tactic.Types-  , module Ide.Plugin.Tactic.Debug-  , OccName-  , Name-  , Type-  , TyVar-  , Span-  , Range-  ) where--import Control.Lens hiding (Context, (.=))-import Control.Monad.Reader-import Control.Monad.State-import Data.Coerce-import Data.Function-import Data.Generics.Product (field)-import Data.Set (Set)-import Data.Tree-import Development.IDE.GHC.Compat hiding (Node)-import Development.IDE.GHC.Orphans ()-import Development.IDE.Types.Location-import GHC.Generics-import Ide.Plugin.Tactic.Debug-import Ide.Plugin.Tactic.FeatureSet (FeatureSet)-import OccName-import Refinery.Tactic-import System.IO.Unsafe (unsafePerformIO)-import Type-import UniqSupply (takeUniqFromSupply, mkSplitUniqSupply, UniqSupply)-import Unique (nonDetCmpUnique, Uniquable, getUnique, Unique)------------------------------------------------------------------------------------ | A wrapper around 'Type' which supports equality and ordering.-newtype CType = CType { unCType :: Type }--instance Eq CType where-  (==) = eqType `on` unCType--instance Ord CType where-  compare = nonDetCmpType `on` unCType--instance Show CType where-  show  = unsafeRender . unCType--instance Show OccName where-  show  = unsafeRender--instance Show Var where-  show  = unsafeRender--instance Show TCvSubst where-  show  = unsafeRender--instance Show DataCon where-  show  = unsafeRender--instance Show Class where-  show  = unsafeRender--instance Show (HsExpr GhcPs) where-  show  = unsafeRender--instance Show (Pat GhcPs) where-  show  = unsafeRender----------------------------------------------------------------------------------data TacticState = TacticState-    { ts_skolems   :: !(Set TyVar)-      -- ^ The known skolems.-    , ts_unifier   :: !TCvSubst-      -- ^ The current substitution of univars.-    , ts_used_vals :: !(Set OccName)-      -- ^ Set of values used by tactics.-    , ts_intro_vals :: !(Set OccName)-      -- ^ Set of values introduced by tactics.-    , ts_unused_top_vals :: !(Set OccName)-      -- ^ Set of currently unused arguments to the function being defined.-    , ts_recursion_stack :: ![Maybe PatVal]-      -- ^ Stack for tracking whether or not the current recursive call has-      -- used at least one smaller pat val. Recursive calls for which this-      -- value is 'False' are guaranteed to loop, and must be pruned.-    , ts_recursion_count :: !Int-      -- ^ Number of calls to recursion. We penalize each.-    , ts_unique_gen :: !UniqSupply-    } deriving stock (Show, Generic)--instance Show UniqSupply where-  show _ = "<uniqsupply>"------------------------------------------------------------------------------------ | A 'UniqSupply' to use in 'defaultTacticState'-unsafeDefaultUniqueSupply :: UniqSupply-unsafeDefaultUniqueSupply =-  unsafePerformIO $ mkSplitUniqSupply '🚒'-{-# NOINLINE unsafeDefaultUniqueSupply #-}---defaultTacticState :: TacticState-defaultTacticState =-  TacticState-    { ts_skolems         = mempty-    , ts_unifier         = emptyTCvSubst-    , ts_used_vals       = mempty-    , ts_intro_vals      = mempty-    , ts_unused_top_vals = mempty-    , ts_recursion_stack = mempty-    , ts_recursion_count = 0-    , ts_unique_gen      = unsafeDefaultUniqueSupply-    }------------------------------------------------------------------------------------ | Generate a new 'Unique'-freshUnique :: MonadState TacticState m => m Unique-freshUnique = do-  (uniq, supply) <- gets $ takeUniqFromSupply . ts_unique_gen-  modify' $! field @"ts_unique_gen" .~ supply-  pure uniq---withRecursionStack-  :: ([Maybe PatVal] -> [Maybe PatVal]) -> TacticState -> TacticState-withRecursionStack f =-  field @"ts_recursion_stack" %~ f--pushRecursionStack :: TacticState -> TacticState-pushRecursionStack = withRecursionStack (Nothing :)--popRecursionStack :: TacticState -> TacticState-popRecursionStack = withRecursionStack tail---withUsedVals :: (Set OccName -> Set OccName) -> TacticState -> TacticState-withUsedVals f =-  field @"ts_used_vals" %~ f---withIntroducedVals :: (Set OccName -> Set OccName) -> TacticState -> TacticState-withIntroducedVals f =-  field @"ts_intro_vals" %~ f------------------------------------------------------------------------------------ | Describes where hypotheses came from. Used extensively to prune stupid--- solutions from the search space.-data Provenance-  = -- | An argument given to the topmost function that contains the current-    -- hole. Recursive calls are restricted to values whose provenance lines up-    -- with the same argument.-    TopLevelArgPrv-      OccName   -- ^ Binding function-      Int       -- ^ Argument Position-    -- | A binding created in a pattern match.-  | PatternMatchPrv PatVal-    -- | A class method from the given context.-  | ClassMethodPrv-      (Uniquely Class)     -- ^ Class-    -- | A binding explicitly written by the user.-  | UserPrv-    -- | The recursive hypothesis. Present only in the context of the recursion-    -- tactic.-  | RecursivePrv-    -- | A hypothesis which has been disallowed for some reason. It's important-    -- to keep these in the hypothesis set, rather than filtering it, in order-    -- to continue tracking downstream provenance.-  | DisallowedPrv DisallowReason Provenance-  deriving stock (Eq, Show, Generic, Ord)------------------------------------------------------------------------------------ | Why was a hypothesis disallowed?-data DisallowReason-  = WrongBranch Int-  | Shadowed-  | RecursiveCall-  | AlreadyDestructed-  deriving stock (Eq, Show, Generic, Ord)------------------------------------------------------------------------------------ | Provenance of a pattern value.-data PatVal = PatVal-  { pv_scrutinee :: Maybe OccName-    -- ^ Original scrutinee which created this PatVal. Nothing, for lambda-    -- case.-  , pv_ancestry  :: Set OccName-    -- ^ The set of values which had to be destructed to discover this term.-    -- Always contains the scrutinee.-  , pv_datacon   :: Uniquely DataCon-    -- ^ The datacon which introduced this term.-  , pv_position  :: Int-    -- ^ The position of this binding in the datacon's arguments.-  } deriving stock (Eq, Show, Generic, Ord)------------------------------------------------------------------------------------ | A wrapper which uses a 'Uniquable' constraint for providing 'Eq' and 'Ord'--- instances.-newtype Uniquely a = Uniquely { getViaUnique :: a }-  deriving Show via a--instance Uniquable a => Eq (Uniquely a) where-  (==) = (==) `on` getUnique . getViaUnique--instance Uniquable a => Ord (Uniquely a) where-  compare = nonDetCmpUnique `on` getUnique . getViaUnique---newtype Hypothesis a = Hypothesis-  { unHypothesis :: [HyInfo a]-  }-  deriving stock (Functor, Eq, Show, Generic, Ord)-  deriving newtype (Semigroup, Monoid)------------------------------------------------------------------------------------ | The provenance and type of a hypothesis term.-data HyInfo a = HyInfo-  { hi_name       :: OccName-  , hi_provenance :: Provenance-  , hi_type       :: a-  }-  deriving stock (Functor, Eq, Show, Generic, Ord)------------------------------------------------------------------------------------ | Map a function over the provenance.-overProvenance :: (Provenance -> Provenance) -> HyInfo a -> HyInfo a-overProvenance f (HyInfo name prv ty) = HyInfo name (f prv) ty------------------------------------------------------------------------------------ | The current bindings and goal for a hole to be filled by refinery.-data Judgement' a = Judgement-  { _jHypothesis :: !(Hypothesis a)-  , _jBlacklistDestruct :: !Bool-  , _jWhitelistSplit :: !Bool-  , _jIsTopHole    :: !Bool-  , _jGoal         :: !a-  }-  deriving stock (Eq, Generic, Functor, Show)--type Judgement = Judgement' CType---newtype ExtractM a = ExtractM { unExtractM :: Reader Context a }-    deriving (Functor, Applicative, Monad, MonadReader Context)----------------------------------------------------------------------------------- | Orphan instance for producing holes when attempting to solve tactics.-instance MonadExtract (Trace, LHsExpr GhcPs) ExtractM where-  hole = pure (mempty, noLoc $ HsVar noExtField $ noLoc $ Unqual $ mkVarOcc "_")------------------------------------------------------------------------------------ | Reasons a tactic might fail.-data TacticError-  = UndefinedHypothesis OccName-  | GoalMismatch String CType-  | UnsolvedSubgoals [Judgement]-  | UnificationError CType CType-  | NoProgress-  | NoApplicableTactic-  | IncorrectDataCon DataCon-  | RecursionOnWrongParam OccName Int OccName-  | UnhelpfulDestruct OccName-  | UnhelpfulSplit OccName-  | TooPolymorphic-  | NotInScope OccName-  deriving stock (Eq)--instance Show TacticError where-    show (UndefinedHypothesis name) =-      occNameString name <> " is not available in the hypothesis."-    show (GoalMismatch tac (CType typ)) =-      mconcat-        [ "The tactic "-        , tac-        , " doesn't apply to goal type "-        , unsafeRender typ-        ]-    show (UnsolvedSubgoals _) =-      "There were unsolved subgoals"-    show (UnificationError (CType t1) (CType t2)) =-        mconcat-          [ "Could not unify "-          , unsafeRender t1-          , " and "-          , unsafeRender t2-          ]-    show NoProgress =-      "Unable to make progress"-    show NoApplicableTactic =-      "No tactic could be applied"-    show (IncorrectDataCon dcon) =-      "Data con doesn't align with goal type (" <> unsafeRender dcon <> ")"-    show (RecursionOnWrongParam call p arg) =-      "Recursion on wrong param (" <> show call <> ") on arg"-        <> show p <> ": " <> show arg-    show (UnhelpfulDestruct n) =-      "Destructing patval " <> show n <> " leads to no new types"-    show (UnhelpfulSplit n) =-      "Splitting constructor " <> show n <> " leads to no new goals"-    show TooPolymorphic =-      "The tactic isn't applicable because the goal is too polymorphic"-    show (NotInScope name) =-      "Tried to do something with the out of scope name " <> show name----------------------------------------------------------------------------------type TacticsM  = TacticT Judgement (Trace, LHsExpr GhcPs) TacticError TacticState ExtractM-type RuleM     = RuleT Judgement (Trace, LHsExpr GhcPs) TacticError TacticState ExtractM-type Rule      = RuleM (Trace, LHsExpr GhcPs)--type Trace = Rose String------------------------------------------------------------------------------------ | The Reader context of tactics and rules-data Context = Context-  { ctxDefiningFuncs :: [(OccName, CType)]-    -- ^ The functions currently being defined-  , ctxModuleFuncs :: [(OccName, CType)]-    -- ^ Everything defined in the current module-  , ctxFeatureSet :: FeatureSet-  }-  deriving stock (Eq, Ord, Show)------------------------------------------------------------------------------------ | An empty context-emptyContext :: Context-emptyContext  = Context mempty mempty mempty---newtype Rose a = Rose (Tree a)-  deriving stock (Eq, Functor, Generic)--instance Show (Rose String) where-  show = unlines . dropEveryOther . lines . drawTree . coerce--dropEveryOther :: [a] -> [a]-dropEveryOther []           = []-dropEveryOther [a]          = [a]-dropEveryOther (a : _ : as) = a : dropEveryOther as--instance Semigroup a => Semigroup (Rose a) where-  Rose (Node a as) <> Rose (Node b bs) = Rose $ Node (a <> b) (as <> bs)--instance Monoid a => Monoid (Rose a) where-  mempty = Rose $ Node mempty mempty--rose :: (Eq a, Monoid a) => a -> [Rose a] -> Rose a-rose a [Rose (Node a' rs)] | a' == mempty = Rose $ Node a rs-rose a rs = Rose $ Node a $ coerce rs------------------------------------------------------------------------------------ | The results of 'Ide.Plugin.Tactic.Machinery.runTactic'-data RunTacticResults = RunTacticResults-  { rtr_trace       :: Trace-  , rtr_extract     :: LHsExpr GhcPs-  , rtr_other_solns :: [(Trace, LHsExpr GhcPs)]-  , rtr_jdg         :: Judgement-  , rtr_ctx         :: Context-  } deriving Show---data AgdaMatch = AgdaMatch-  { amPats :: [Pat GhcPs]-  , amBody :: HsExpr GhcPs-  }-  deriving (Show)-
+ src/Wingman/Auto.hs view
@@ -0,0 +1,30 @@+module Wingman.Auto where++import           Control.Monad.State (gets)+import qualified Data.Set as S+import           Refinery.Tactic+import           Wingman.Context+import           Wingman.Judgements+import           Wingman.KnownStrategies+import           Wingman.Machinery (tracing)+import           Wingman.Tactics+import           Wingman.Types+++------------------------------------------------------------------------------+-- | Automatically solve a goal.+auto :: TacticsM ()+auto = do+  jdg <- goal+  skolems <- gets ts_skolems+  current <- getCurrentDefinitions+  traceMX "goal" jdg+  traceMX "ctx" current+  traceMX "skolems" skolems+  commit knownStrategies+    . tracing "auto"+    . localTactic (auto' 4)+    . disallowing RecursiveCall+    . S.fromList+    $ fmap fst current+
+ src/Wingman/CaseSplit.hs view
@@ -0,0 +1,82 @@+module Wingman.CaseSplit+  ( mkFirstAgda+  , iterateSplit+  , splitToDecl+  ) where++import           Data.Bool (bool)+import           Data.Data+import           Data.Generics+import           Data.Set (Set)+import qualified Data.Set as S+import           Development.IDE.GHC.Compat+import           GHC.Exts (IsString (fromString))+import           GHC.SourceGen (funBinds, match, wildP)+import           OccName+import           Wingman.GHC+import           Wingman.Types++++------------------------------------------------------------------------------+-- | Construct an 'AgdaMatch' from patterns in scope (should be the LHS of the+-- match) and a body.+mkFirstAgda :: [Pat GhcPs] -> HsExpr GhcPs -> AgdaMatch+mkFirstAgda pats (Lambda pats' body) = mkFirstAgda (pats <> pats') body+mkFirstAgda pats body                = AgdaMatch pats body+++------------------------------------------------------------------------------+-- | Transform an 'AgdaMatch' whose body is a case over a bound pattern, by+-- splitting it into multiple matches: one for each alternative of the case.+agdaSplit :: AgdaMatch -> [AgdaMatch]+agdaSplit (AgdaMatch pats (Case (HsVar _ (L _ var)) matches)) = do+  (pat, body) <- matches+  -- TODO(sandy): use an at pattern if necessary+  pure $ AgdaMatch (rewriteVarPat var pat pats) $ unLoc body+agdaSplit x = [x]+++------------------------------------------------------------------------------+-- | Replace unused bound patterns with wild patterns.+wildify :: AgdaMatch -> AgdaMatch+wildify (AgdaMatch pats body) =+  let make_wild = bool id (wildifyT (allOccNames body)) $ not $ containsHole body+   in AgdaMatch (make_wild pats) body+++------------------------------------------------------------------------------+-- | Helper function for 'wildify'.+wildifyT :: Data a => Set OccName -> a -> a+wildifyT (S.map occNameString -> used) = everywhere $ mkT $ \case+  VarPat _ (L _ var) | S.notMember (occNameString $ occName var) used -> wildP+  (x :: Pat GhcPs)                                                    -> x+++------------------------------------------------------------------------------+-- | Replace a 'VarPat' with the given @'Pat' GhcPs@.+rewriteVarPat :: Data a => RdrName -> Pat GhcPs -> a -> a+rewriteVarPat name rep = everywhere $ mkT $ \case+  VarPat _ (L _ var) | eqRdrName name var -> rep+  (x :: Pat GhcPs)                        -> x+++------------------------------------------------------------------------------+-- | Construct an 'HsDecl' from a set of 'AgdaMatch'es.+splitToDecl+    :: OccName  -- ^ The name of the function+    -> [AgdaMatch]+    -> LHsDecl GhcPs+splitToDecl name ams = noLoc $ funBinds (fromString . occNameString . occName $ name) $ do+  AgdaMatch pats body <- ams+  pure $ match pats body+++------------------------------------------------------------------------------+-- | Sometimes 'agdaSplit' exposes another opportunity to do 'agdaSplit'. This+-- function runs it a few times, hoping it will find a fixpoint.+iterateSplit :: AgdaMatch -> [AgdaMatch]+iterateSplit am =+  let iterated = iterate (agdaSplit =<<) $ pure am+   in fmap wildify . head . drop 5 $ iterated+
+ src/Wingman/CodeGen.hs view
@@ -0,0 +1,215 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE TupleSections    #-}+{-# LANGUAGE TypeApplications #-}++module Wingman.CodeGen+  ( module Wingman.CodeGen+  , module Wingman.CodeGen.Utils+  ) where+++import           ConLike+import           Control.Lens ((%~), (<>~), (&))+import           Control.Monad.Except+import           Control.Monad.State+import           Data.Bool (bool)+import           Data.Generics.Labels ()+import           Data.List+import           Data.Monoid (Endo(..))+import qualified Data.Set as S+import           Data.Traversable+import           DataCon+import           Development.IDE.GHC.Compat+import           GHC.Exts+import           GHC.SourceGen.Binds+import           GHC.SourceGen.Expr+import           GHC.SourceGen.Overloaded+import           GHC.SourceGen.Pat+import           GhcPlugins (isSymOcc)+import           PatSyn+import           Type hiding (Var)+import           Wingman.CodeGen.Utils+import           Wingman.GHC+import           Wingman.Judgements+import           Wingman.Judgements.Theta+import           Wingman.Machinery+import           Wingman.Naming+import           Wingman.Types+++destructMatches+    :: (ConLike -> Judgement -> Rule)+       -- ^ How to construct each match+    -> Maybe OccName+       -- ^ Scrutinee+    -> CType+       -- ^ Type being destructed+    -> Judgement+    -> RuleM (Synthesized [RawMatch])+destructMatches f scrut t jdg = do+  let hy = jEntireHypothesis jdg+      g  = jGoal jdg+  case splitTyConApp_maybe $ unCType t of+    Nothing -> throwError $ GoalMismatch "destruct" g+    Just (tc, apps) -> do+      let dcs = tyConDataCons tc+      case dcs of+        [] -> throwError $ GoalMismatch "destruct" g+        _ -> fmap unzipTrace $ for dcs $ \dc -> do+          let con = RealDataCon dc+              ev = concatMap mkEvidence $ dataConInstArgTys dc apps+              -- We explicitly do not need to add the method hypothesis to+              -- #syn_scoped+              method_hy = foldMap evidenceToHypothesis ev+              args = conLikeInstOrigArgTys' con apps+          modify $ appEndo $ foldMap (Endo . evidenceToSubst) ev+          subst <- gets ts_unifier+          names <- mkManyGoodNames (hyNamesInScope hy) args+          let hy' = patternHypothesis scrut con jdg+                  $ zip names+                  $ coerce args+              j = fmap (CType . substTyAddInScope subst . unCType)+                $ introduce hy'+                $ introduce method_hy+                $ withNewGoal g jdg+          ext <- f con j+          pure $ ext+            & #syn_trace %~ rose ("match " <> show dc <> " {" <> intercalate ", " (fmap show names) <> "}")+                          . pure+            & #syn_scoped <>~ hy'+            & #syn_val     %~ match [mkDestructPat con names] . unLoc+++------------------------------------------------------------------------------+-- | Produces a pattern for a data con and the names of its fields.+mkDestructPat :: ConLike -> [OccName] -> Pat GhcPs+mkDestructPat con names+  | RealDataCon dcon <- con+  , isTupleDataCon dcon =+      tuple pat_args+  | otherwise =+      infixifyPatIfNecessary con $+        conP+          (coerceName $ conLikeName con)+          pat_args+  where+    pat_args = fmap bvar' names+++infixifyPatIfNecessary :: ConLike -> Pat GhcPs -> Pat GhcPs+infixifyPatIfNecessary dcon x+  | conLikeIsInfix dcon =+      case x of+        ConPatIn op (PrefixCon [lhs, rhs]) ->+          ConPatIn op $ InfixCon lhs rhs+        y -> y+  | otherwise = x++++unzipTrace :: [Synthesized a] -> Synthesized [a]+unzipTrace = sequenceA+++-- | Essentially same as 'dataConInstOrigArgTys' in GHC,+--  but only accepts universally quantified types as the second arguments+--  and automatically introduces existentials.+--+-- NOTE: The behaviour depends on GHC's 'dataConInstOrigArgTys'.+--       We need some tweaks if the compiler changes the implementation.+conLikeInstOrigArgTys'+  :: ConLike+      -- ^ 'DataCon'structor+  -> [Type]+      -- ^ /Universally/ quantified type arguments to a result type.+      --   It /MUST NOT/ contain any dictionaries, coercion and existentials.+      --+      --   For example, for @MkMyGADT :: b -> MyGADT a c@, we+      --   must pass @[a, c]@ as this argument but not @b@, as @b@ is an existential.+  -> [Type]+      -- ^ Types of arguments to the ConLike with returned type is instantiated with the second argument.+conLikeInstOrigArgTys' con uniTys =+  let exvars = conLikeExTys con+   in conLikeInstOrigArgTys con $+        uniTys ++ fmap mkTyVarTy exvars+      -- Rationale: At least in GHC <= 8.10, 'dataConInstOrigArgTys'+      -- unifies the second argument with DataCon's universals followed by existentials.+      -- If the definition of 'dataConInstOrigArgTys' changes,+      -- this place must be changed accordingly.+++conLikeExTys :: ConLike -> [TyCoVar]+conLikeExTys (RealDataCon d) = dataConExTys d+conLikeExTys (PatSynCon p) = patSynExTys p++patSynExTys :: PatSyn -> [TyCoVar]+patSynExTys ps = patSynExTyVars ps+++------------------------------------------------------------------------------+-- | Combinator for performing case splitting, and running sub-rules on the+-- resulting matches.++destruct' :: (ConLike -> Judgement -> Rule) -> HyInfo CType -> Judgement -> Rule+destruct' f hi jdg = do+  when (isDestructBlacklisted jdg) $ throwError NoApplicableTactic+  let term = hi_name hi+  ext+      <- destructMatches+           f+           (Just term)+           (hi_type hi)+           $ disallowing AlreadyDestructed (S.singleton term) jdg+  pure $ ext+    & #syn_trace     %~ rose ("destruct " <> show term) . pure+    & #syn_used_vals %~ S.insert term+    & #syn_val       %~ noLoc . case' (var' term)+++------------------------------------------------------------------------------+-- | Combinator for performign case splitting, and running sub-rules on the+-- resulting matches.+destructLambdaCase' :: (ConLike -> Judgement -> Rule) -> Judgement -> Rule+destructLambdaCase' f jdg = do+  when (isDestructBlacklisted jdg) $ throwError NoApplicableTactic+  let g  = jGoal jdg+  case splitFunTy_maybe (unCType g) of+    Just (arg, _) | isAlgType arg ->+      fmap (fmap noLoc lambdaCase) <$>+        destructMatches f Nothing (CType arg) jdg+    _ -> throwError $ GoalMismatch "destructLambdaCase'" g+++------------------------------------------------------------------------------+-- | Construct a data con with subgoals for each field.+buildDataCon+    :: Bool       -- Should we blacklist destruct?+    -> Judgement+    -> ConLike            -- ^ The data con to build+    -> [Type]             -- ^ Type arguments for the data con+    -> RuleM (Synthesized (LHsExpr GhcPs))+buildDataCon should_blacklist jdg dc tyapps = do+  let args = conLikeInstOrigArgTys' dc tyapps+  ext+      <- fmap unzipTrace+       $ traverse ( \(arg, n) ->+                    newSubgoal+                  . filterSameTypeFromOtherPositions dc n+                  . bool id blacklistingDestruct should_blacklist+                  . flip withNewGoal jdg+                  $ CType arg+                  ) $ zip args [0..]+  pure $ ext+    & #syn_trace %~ rose (show dc) . pure+    & #syn_val   %~ mkCon dc tyapps+++------------------------------------------------------------------------------+-- | Make a function application, correctly handling the infix case.+mkApply :: OccName -> [HsExpr GhcPs] -> LHsExpr GhcPs+mkApply occ (lhs : rhs : more)+  | isSymOcc occ+  = noLoc $ foldl' (@@) (op lhs (coerceName occ) rhs) more+mkApply occ args = noLoc $ foldl' (@@) (var' occ) args+
+ src/Wingman/CodeGen/Utils.hs view
@@ -0,0 +1,83 @@+module Wingman.CodeGen.Utils where++import ConLike (ConLike(RealDataCon), conLikeName)+import Data.List+import DataCon+import Development.IDE.GHC.Compat+import GHC.Exts+import GHC.SourceGen (RdrNameStr, recordConE, string)+import GHC.SourceGen.Overloaded+import GhcPlugins (nilDataCon, charTy, eqType)+import Name+import Wingman.GHC (getRecordFields)+++------------------------------------------------------------------------------+-- | Make a data constructor with the given arguments.+mkCon :: ConLike -> [Type] -> [LHsExpr GhcPs] -> LHsExpr GhcPs+mkCon con apps (fmap unLoc -> args)+  | RealDataCon dcon <- con+  , dcon == nilDataCon+  , [ty] <- apps+  , ty `eqType` charTy = noLoc $ string ""++  | RealDataCon dcon <- con+  , isTupleDataCon dcon =+      noLoc $ tuple args++  | RealDataCon dcon <- con+  , dataConIsInfix dcon+  , (lhs : rhs : args') <- args =+      noLoc $ foldl' (@@) (op lhs (coerceName con_name) rhs) args'++  | Just fields <- getRecordFields con+  , length fields >= 2 =  --  record notation is unnatural on single field ctors+      noLoc $ recordConE (coerceName con_name) $ do+        (arg, (field, _)) <- zip args fields+        pure (coerceName field, arg)++  | otherwise =+      noLoc $ foldl' (@@) (bvar' $ occName con_name) args+  where+    con_name = conLikeName con+++coerceName :: HasOccName a => a -> RdrNameStr+coerceName = fromString . occNameString . occName+++------------------------------------------------------------------------------+-- | Like 'var', but works over standard GHC 'OccName's.+var' :: Var a => OccName -> a+var' = var . fromString . occNameString+++------------------------------------------------------------------------------+-- | Like 'bvar', but works over standard GHC 'OccName's.+bvar' :: BVar a => OccName -> a+bvar' = bvar . fromString . occNameString+++------------------------------------------------------------------------------+-- | Get an HsExpr corresponding to a function name.+mkFunc :: String -> HsExpr GhcPs+mkFunc = var' . mkVarOcc+++------------------------------------------------------------------------------+-- | Get an HsExpr corresponding to a value name.+mkVal :: String -> HsExpr GhcPs+mkVal = var' . mkVarOcc+++------------------------------------------------------------------------------+-- | Like 'op', but easier to call.+infixCall :: String -> HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs+infixCall s = flip op (fromString s)+++------------------------------------------------------------------------------+-- | Like '(@@)', but uses a dollar instead of parentheses.+appDollar :: HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs+appDollar = infixCall "$"+
+ src/Wingman/Context.hs view
@@ -0,0 +1,113 @@+module Wingman.Context where++import           Bag+import           Control.Arrow+import           Control.Monad.Reader+import           Data.Foldable.Extra (allM)+import           Data.Maybe (fromMaybe, isJust)+import qualified Data.Set as S+import           Development.IDE.GHC.Compat+import           GhcPlugins (ExternalPackageState (eps_inst_env), piResultTys)+import           InstEnv (lookupInstEnv, InstEnvs(..), is_dfun)+import           OccName+import           TcRnTypes+import           TcType (tcSplitTyConApp, tcSplitPhiTy)+import           TysPrim (alphaTys)+import           Wingman.FeatureSet (FeatureSet)+import           Wingman.Judgements.Theta+import           Wingman.Types+++mkContext+    :: FeatureSet+    -> [(OccName, CType)]+    -> TcGblEnv+    -> ExternalPackageState+    -> KnownThings+    -> [Evidence]+    -> Context+mkContext features locals tcg eps kt ev = Context+  { ctxDefiningFuncs = locals+  , ctxModuleFuncs = fmap splitId+                   . (getFunBindId =<<)+                   . fmap unLoc+                   . bagToList+                   $ tcg_binds tcg+  , ctxFeatureSet = features+  , ctxInstEnvs =+      InstEnvs+        (eps_inst_env eps)+        (tcg_inst_env tcg)+        (tcVisibleOrphanMods tcg)+  , ctxKnownThings = kt+  , ctxTheta = evidenceToThetaType ev+  }+++splitId :: Id -> (OccName, CType)+splitId = occName &&& CType . idType+++getFunBindId :: HsBindLR GhcTc GhcTc -> [Id]+getFunBindId (AbsBinds _ _ _ abes _ _ _)+  = abes >>= \case+      ABE _ poly _ _ _ -> pure poly+      _                -> []+getFunBindId _ = []+++getCurrentDefinitions :: MonadReader Context m => m [(OccName, CType)]+getCurrentDefinitions = asks ctxDefiningFuncs+++------------------------------------------------------------------------------+-- | Extract something from 'KnownThings'.+getKnownThing :: MonadReader Context m => (KnownThings -> a) -> m a+getKnownThing f = asks $ f . ctxKnownThings+++------------------------------------------------------------------------------+-- | Like 'getInstance', but uses a class from the 'KnownThings'.+getKnownInstance :: MonadReader Context m => (KnownThings -> Class) -> [Type] -> m (Maybe (Class, PredType))+getKnownInstance f tys = do+  cls <- getKnownThing f+  getInstance cls tys+++------------------------------------------------------------------------------+-- | Determine if there is an instance that exists for the given 'Class' at the+-- specified types. Deeply checks contexts to ensure the instance is actually+-- real.+--+-- If so, this returns a 'PredType' that corresponds to the type of the+-- dictionary.+getInstance :: MonadReader Context m => Class -> [Type] -> m (Maybe (Class, PredType))+getInstance cls tys = do+  env <- asks ctxInstEnvs+  let (mres, _, _) = lookupInstEnv False env cls tys+  case mres of+    ((inst, mapps) : _) -> do+      -- Get the instantiated type of the dictionary+      let df = piResultTys (idType $ is_dfun inst) $ zipWith fromMaybe alphaTys mapps+      -- pull off its resulting arguments+      let (theta, df') = tcSplitPhiTy df+      allM hasClassInstance theta >>= \case+        True -> pure $ Just (cls, df')+        False -> pure Nothing+    _ -> pure Nothing+++------------------------------------------------------------------------------+-- | Like 'getInstance', but only returns whether or not it succeeded. Can fail+-- fast, and uses a cached Theta from the context.+hasClassInstance :: MonadReader Context m => PredType -> m Bool+hasClassInstance predty = do+  theta <- asks ctxTheta+  case S.member (CType predty) theta of+    True -> pure True+    False -> do+      let (con, apps) = tcSplitTyConApp predty+      case tyConClass_maybe con of+        Nothing -> pure False+        Just cls -> fmap isJust $ getInstance cls apps+
+ src/Wingman/Debug.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE BangPatterns     #-}+{-# LANGUAGE CPP              #-}+{-# LANGUAGE TypeApplications #-}++module Wingman.Debug+  ( unsafeRender+  , unsafeRender'+  , traceM+  , traceShowId+  , trace+  , traceX+  , traceIdX+  , traceMX+  , traceFX+  ) where++import           Control.DeepSeq+import           Control.Exception+import           Debug.Trace+import           DynFlags          (unsafeGlobalDynFlags)+import           Outputable        hiding ((<>))+import           System.IO.Unsafe  (unsafePerformIO)++#if __GLASGOW_HASKELL__ >= 808+import           PlainPanic        (PlainGhcException)+type GHC_EXCEPTION = PlainGhcException+#else+import           Panic             (GhcException)+type GHC_EXCEPTION = GhcException+#endif+++------------------------------------------------------------------------------+-- | Print something+unsafeRender :: Outputable a => a -> String+unsafeRender = unsafeRender' . ppr+++unsafeRender' :: SDoc -> String+unsafeRender' sdoc = unsafePerformIO $ do+  let z = showSDoc unsafeGlobalDynFlags sdoc+  -- We might not have unsafeGlobalDynFlags (like during testing), in which+  -- case GHC panics. Instead of crashing, let's just fail to print.+  !res <- try @GHC_EXCEPTION $ evaluate $ deepseq z z+  pure $ either (const "<unsafeRender'>") id res+{-# NOINLINE unsafeRender' #-}++traceMX :: (Monad m, Show a) => String -> a -> m ()+traceMX str a = traceM $ mappend ("!!!" <> str <> ": ") $ show a++traceX :: (Show a) => String -> a -> b -> b+traceX str a = trace (mappend ("!!!" <> str <> ": ") $ show a)++traceIdX :: (Show a) => String -> a -> a+traceIdX str a = trace (mappend ("!!!" <> str <> ": ") $ show a) a++traceFX :: String -> (a -> String) -> a -> a+traceFX str f a = trace (mappend ("!!!" <> str <> ": ") $ f a) a+
+ src/Wingman/FeatureSet.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE OverloadedStrings #-}++module Wingman.FeatureSet+  ( Feature (..)+  , FeatureSet+  , hasFeature+  , defaultFeatures+  , allFeatures+  , parseFeatureSet+  , prettyFeatureSet+  ) where++import           Data.List  (intercalate)+import           Data.Maybe (listToMaybe, mapMaybe)+import           Data.Set   (Set)+import qualified Data.Set   as S+import qualified Data.Text  as T+++------------------------------------------------------------------------------+-- | All the available features. A 'FeatureSet' describes the ones currently+-- available to the user.+data Feature+  = FeatureDestructAll+  | FeatureUseDataCon+  | FeatureRefineHole+  | FeatureKnownMonoid+  deriving (Eq, Ord, Show, Read, Enum, Bounded)+++------------------------------------------------------------------------------+-- | A collection of enabled features.+type FeatureSet = Set Feature+++------------------------------------------------------------------------------+-- | Parse a feature set.+parseFeatureSet :: T.Text -> FeatureSet+parseFeatureSet+  = mappend defaultFeatures+  . S.fromList+  . mapMaybe (readMaybe . mappend featurePrefix . rot13 . T.unpack)+  . T.split (== '/')+++------------------------------------------------------------------------------+-- | Features that are globally enabled for all users.+defaultFeatures :: FeatureSet+defaultFeatures = S.fromList+  [+  ]+++------------------------------------------------------------------------------+-- | All available features.+allFeatures :: FeatureSet+allFeatures = S.fromList $ enumFromTo minBound maxBound+++------------------------------------------------------------------------------+-- | Pretty print a feature set.+prettyFeatureSet :: FeatureSet -> String+prettyFeatureSet+  = intercalate "/"+  . fmap (rot13 . drop (length featurePrefix) . show)+  . S.toList+++------------------------------------------------------------------------------+-- | Is a given 'Feature' currently enabled?+hasFeature :: Feature -> FeatureSet -> Bool+hasFeature = S.member+++------------------------------------------------------------------------------+-- | Like 'read', but not partial.+readMaybe :: Read a => String -> Maybe a+readMaybe = fmap fst . listToMaybe . reads+++featurePrefix :: String+featurePrefix = "Feature"+++rot13 :: String -> String+rot13 = fmap (toEnum . rot13int . fromEnum)+++rot13int :: Integral a => a -> a+rot13int x+  | (fromIntegral x :: Word) - 97 < 26 = 97 + rem (x - 84) 26+  | (fromIntegral x :: Word) - 65 < 26 = 65 + rem (x - 52) 26+  | otherwise   = x+
+ src/Wingman/GHC.hs view
@@ -0,0 +1,346 @@+{-# LANGUAGE CPP               #-}+{-# LANGUAGE OverloadedStrings #-}++module Wingman.GHC where++import           ConLike+import           Control.Applicative (empty)+import           Control.Monad.State+import           Control.Monad.Trans.Maybe (MaybeT(..))+import           Data.Function (on)+import           Data.Functor ((<&>))+import           Data.List (isPrefixOf)+import qualified Data.Map as M+import           Data.Maybe (isJust)+import           Data.Set (Set)+import qualified Data.Set as S+import           Data.Traversable+import           DataCon+import           Development.IDE (HscEnvEq (hscEnv))+import           Development.IDE.Core.Compile (lookupName)+import           Development.IDE.GHC.Compat+import           GHC.SourceGen (lambda)+import           Generics.SYB (Data, everything, everywhere, listify, mkQ, mkT)+import           GhcPlugins (extractModule, GlobalRdrElt (gre_name))+import           OccName+import           TcRnMonad+import           TcType+import           TyCoRep+import           Type+import           TysWiredIn (charTyCon, doubleTyCon, floatTyCon, intTyCon)+import           Unique+import           Var+import           Wingman.Types+++tcTyVar_maybe :: Type -> Maybe Var+tcTyVar_maybe ty | Just ty' <- tcView ty = tcTyVar_maybe ty'+tcTyVar_maybe (CastTy ty _) = tcTyVar_maybe ty  -- look through casts, as+                                                -- this is only used for+                                                -- e.g., FlexibleContexts+tcTyVar_maybe (TyVarTy v)   = Just v+tcTyVar_maybe _             = Nothing+++instantiateType :: Type -> ([TyVar], Type)+instantiateType t = do+  let vs  = tyCoVarsOfTypeList t+      vs' = fmap cloneTyVar vs+      subst = foldr (\(v,t) a -> extendTCvSubst a v $ TyVarTy t) emptyTCvSubst+            $ zip vs vs'+   in (vs', substTy subst t)+++cloneTyVar :: TyVar -> TyVar+cloneTyVar t =+  let uniq = getUnique t+      some_magic_number = 49+   in setVarUnique t $ deriveUnique uniq some_magic_number+++------------------------------------------------------------------------------+-- | Is this a function type?+isFunction :: Type -> Bool+isFunction (tacticsSplitFunTy -> (_, _, [], _)) = False+isFunction _                                    = True+++------------------------------------------------------------------------------+-- | Split a function, also splitting out its quantified variables and theta+-- context.+tacticsSplitFunTy :: Type -> ([TyVar], ThetaType, [Type], Type)+tacticsSplitFunTy t+  = let (vars, theta, t') = tcSplitSigmaTy t+        (args, res) = tcSplitFunTys t'+     in (vars, theta, args, res)+++------------------------------------------------------------------------------+-- | Rip the theta context out of a regular type.+tacticsThetaTy :: Type -> ThetaType+tacticsThetaTy (tcSplitSigmaTy -> (_, theta,  _)) = theta+++------------------------------------------------------------------------------+-- | Get the data cons of a type, if it has any.+tacticsGetDataCons :: Type -> Maybe ([DataCon], [Type])+tacticsGetDataCons ty | Just _ <- algebraicTyCon ty =+  splitTyConApp_maybe ty <&> \(tc, apps) ->+    ( filter (not . dataConCannotMatch apps) $ tyConDataCons tc+    , apps+    )+tacticsGetDataCons _ = Nothing++------------------------------------------------------------------------------+-- | Instantiate all of the quantified type variables in a type with fresh+-- skolems.+freshTyvars :: MonadState TacticState m => Type -> m Type+freshTyvars t = do+  let (tvs, _, _, _) = tacticsSplitFunTy t+  reps <- fmap M.fromList+        $ for tvs $ \tv -> do+            uniq <- freshUnique+            pure (tv, setTyVarUnique tv uniq)+  pure $+    everywhere+      (mkT $ \tv ->+        case M.lookup tv reps of+          Just tv' -> tv'+          Nothing  -> tv+      ) t+++------------------------------------------------------------------------------+-- | Given a datacon, extract its record fields' names and types. Returns+-- nothing if the datacon is not a record.+getRecordFields :: ConLike -> Maybe [(OccName, CType)]+getRecordFields dc =+  case conLikeFieldLabels dc of+    [] -> Nothing+    lbls -> for lbls $ \lbl -> do+      let ty = conLikeFieldType dc $ flLabel lbl+      pure (mkVarOccFS $ flLabel lbl, CType ty)+++------------------------------------------------------------------------------+-- | Is this an algebraic type?+algebraicTyCon :: Type -> Maybe TyCon+algebraicTyCon (splitTyConApp_maybe -> Just (tycon, _))+  | tycon == intTyCon    = Nothing+  | tycon == floatTyCon  = Nothing+  | tycon == doubleTyCon = Nothing+  | tycon == charTyCon   = Nothing+  | tycon == funTyCon    = Nothing+  | otherwise = Just tycon+algebraicTyCon _ = Nothing+++------------------------------------------------------------------------------+-- | We can't compare 'RdrName' for equality directly. Instead, sloppily+-- compare them by their 'OccName's.+eqRdrName :: RdrName -> RdrName -> Bool+eqRdrName = (==) `on` occNameString . occName+++------------------------------------------------------------------------------+-- | Compare two 'OccName's for unqualified equality.+sloppyEqOccName :: OccName -> OccName -> Bool+sloppyEqOccName = (==) `on` occNameString+++------------------------------------------------------------------------------+-- | Does this thing contain any references to 'HsVar's with the given+-- 'RdrName'?+containsHsVar :: Data a => RdrName -> a -> Bool+containsHsVar name x = not $ null $ listify (+  \case+    ((HsVar _ (L _ a)) :: HsExpr GhcPs) | eqRdrName a name -> True+    _                                                      -> False+  ) x+++------------------------------------------------------------------------------+-- | Does this thing contain any holes?+containsHole :: Data a => a -> Bool+containsHole x = not $ null $ listify (+  \case+    ((HsVar _ (L _ name)) :: HsExpr GhcPs) -> isHole $ occName name+    _                                      -> False+  ) x+++------------------------------------------------------------------------------+-- | Check if an 'OccName' is a hole+isHole :: OccName -> Bool+-- TODO(sandy): Make this more robust+isHole = isPrefixOf "_" . occNameString+++------------------------------------------------------------------------------+-- | Get all of the referenced occnames.+allOccNames :: Data a => a -> Set OccName+allOccNames = everything (<>) $ mkQ mempty $ \case+    a -> S.singleton a+++------------------------------------------------------------------------------+-- | Unpack the relevant parts of a 'Match'+pattern AMatch :: HsMatchContext (NameOrRdrName (IdP GhcPs)) -> [Pat GhcPs] -> HsExpr GhcPs -> Match GhcPs (LHsExpr GhcPs)+pattern AMatch ctx pats body <-+  Match { m_ctxt = ctx+        , m_pats = fmap fromPatCompat -> pats+        , m_grhss = UnguardedRHSs (unLoc -> body)+        }+++------------------------------------------------------------------------------+-- | A pattern over the otherwise (extremely) messy AST for lambdas.+pattern Lambda :: [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs+pattern Lambda pats body <-+  HsLam _+    (MG {mg_alts = L _ [L _ (AMatch _ pats body) ]})+  where+    -- If there are no patterns to bind, just stick in the body+    Lambda [] body   = body+    Lambda pats body = lambda pats body+++------------------------------------------------------------------------------+-- | A GRHS that caontains no guards.+pattern UnguardedRHSs :: LHsExpr p -> GRHSs p (LHsExpr p)+pattern UnguardedRHSs body <-+  GRHSs {grhssGRHSs = [L _ (GRHS _ [] body)]}+++------------------------------------------------------------------------------+-- | A match with a single pattern. Case matches are always 'SinglePatMatch'es.+pattern SinglePatMatch :: PatCompattable p => Pat p -> LHsExpr p -> Match p (LHsExpr p)+pattern SinglePatMatch pat body <-+  Match { m_pats = [fromPatCompat -> pat]+        , m_grhss = UnguardedRHSs body+        }+++------------------------------------------------------------------------------+-- | Helper function for defining the 'Case' pattern.+unpackMatches :: PatCompattable p => [Match p (LHsExpr p)] -> Maybe [(Pat p, LHsExpr p)]+unpackMatches [] = Just []+unpackMatches (SinglePatMatch pat body : matches) =+  (:) <$> pure (pat, body) <*> unpackMatches matches+unpackMatches _ = Nothing+++------------------------------------------------------------------------------+-- | A pattern over the otherwise (extremely) messy AST for lambdas.+pattern Case :: PatCompattable p => HsExpr p -> [(Pat p, LHsExpr p)] -> HsExpr p+pattern Case scrutinee matches <-+  HsCase _ (L _ scrutinee)+    (MG {mg_alts = L _ (fmap unLoc -> unpackMatches -> Just matches)})+++------------------------------------------------------------------------------+-- | Can ths type be lambda-cased?+--+-- Return: 'Nothing' if no+--         @Just False@ if it can't be homomorphic+--         @Just True@ if it can+lambdaCaseable :: Type -> Maybe Bool+lambdaCaseable (splitFunTy_maybe -> Just (arg, res))+  | isJust (algebraicTyCon arg)+  = Just $ isJust $ algebraicTyCon res+lambdaCaseable _ = Nothing++class PatCompattable p where+  fromPatCompat :: PatCompat p -> Pat p+  toPatCompat :: Pat p -> PatCompat p++#if __GLASGOW_HASKELL__ == 808+instance PatCompattable GhcTc where+  fromPatCompat = id+  toPatCompat = id++instance PatCompattable GhcPs where+  fromPatCompat = id+  toPatCompat = id++type PatCompat pass = Pat pass+#else+instance PatCompattable GhcTc where+  fromPatCompat = unLoc+  toPatCompat = noLoc++instance PatCompattable GhcPs where+  fromPatCompat = unLoc+  toPatCompat = noLoc++type PatCompat pass = LPat pass+#endif++------------------------------------------------------------------------------+-- | Should make sure it's a fun bind+pattern TopLevelRHS :: OccName -> [PatCompat GhcTc] -> LHsExpr GhcTc -> Match GhcTc (LHsExpr GhcTc)+pattern TopLevelRHS name ps body <-+  Match _+    (FunRhs (L _ (occName -> name)) _ _)+    ps+    (GRHSs _+      [L _ (GRHS _ [] body)] _)+++dataConExTys :: DataCon -> [TyCoVar]+#if __GLASGOW_HASKELL__ >= 808+dataConExTys = DataCon.dataConExTyCoVars+#else+dataConExTys = DataCon.dataConExTyVars+#endif+++------------------------------------------------------------------------------+-- | In GHC 8.8, sometimes patterns are wrapped in 'XPat'.+-- The nitty gritty details are explained at+-- https://blog.shaynefletcher.org/2020/03/ghc-haskell-pats-and-lpats.html+--+-- We need to remove these in order to succesfull find patterns.+unXPat :: Pat GhcPs -> Pat GhcPs+#if __GLASGOW_HASKELL__ == 808+unXPat (XPat (L _ pat)) = unXPat pat+#endif+unXPat pat              = pat+++------------------------------------------------------------------------------+-- | Build a 'KnownThings'.+knownThings :: TcGblEnv -> HscEnvEq -> MaybeT IO KnownThings+knownThings tcg hscenv= do+  let cls = knownClass tcg hscenv+  KnownThings+    <$> cls (mkClsOcc "Semigroup")+    <*> cls (mkClsOcc "Monoid")+++------------------------------------------------------------------------------+-- | Like 'knownThing' but specialized to classes.+knownClass :: TcGblEnv -> HscEnvEq -> OccName -> MaybeT IO Class+knownClass = knownThing $ \case+  ATyCon tc -> tyConClass_maybe tc+  _         -> Nothing+++------------------------------------------------------------------------------+-- | Helper function for defining 'knownThings'.+knownThing :: (TyThing -> Maybe a) -> TcGblEnv -> HscEnvEq -> OccName -> MaybeT IO a+knownThing f tcg hscenv occ = do+  let modul = extractModule tcg+      rdrenv = tcg_rdr_env tcg++  case lookupOccEnv rdrenv occ of+    Nothing -> empty+    Just elts -> do+      mvar <- lift $ lookupName (hscEnv hscenv) modul $ gre_name $ head elts+      case mvar of+        Just tt -> liftMaybe $ f tt+        _ -> empty++liftMaybe :: Monad m => Maybe a -> MaybeT m a+liftMaybe a = MaybeT $ pure a+
+ src/Wingman/Judgements.hs view
@@ -0,0 +1,429 @@+module Wingman.Judgements where++import           ConLike (ConLike)+import           Control.Arrow+import           Control.Lens hiding (Context)+import           Data.Bool+import           Data.Char+import           Data.Coerce+import           Data.Generics.Product (field)+import           Data.Map (Map)+import qualified Data.Map as M+import           Data.Maybe+import           Data.Set (Set)+import qualified Data.Set as S+import           Development.IDE.Core.UseStale (Tracked, unTrack)+import           Development.IDE.Spans.LocalBindings+import           OccName+import           SrcLoc+import           Type+import           Wingman.GHC (algebraicTyCon)+import           Wingman.Types+++------------------------------------------------------------------------------+-- | Given a 'SrcSpan' and a 'Bindings', create a hypothesis.+hypothesisFromBindings :: Tracked age RealSrcSpan -> Tracked age Bindings -> Hypothesis CType+hypothesisFromBindings (unTrack -> span) (unTrack -> bs) = buildHypothesis $ getLocalScope bs span+++------------------------------------------------------------------------------+-- | Convert a @Set Id@ into a hypothesis.+buildHypothesis :: [(Name, Maybe Type)] -> Hypothesis CType+buildHypothesis+  = Hypothesis+  . mapMaybe go+  where+    go (occName -> occ, t)+      | Just ty <- t+      , isAlpha . head . occNameString $ occ = Just $ HyInfo occ UserPrv $ CType ty+      | otherwise = Nothing+++blacklistingDestruct :: Judgement -> Judgement+blacklistingDestruct =+  field @"_jBlacklistDestruct" .~ True+++unwhitelistingSplit :: Judgement -> Judgement+unwhitelistingSplit =+  field @"_jWhitelistSplit" .~ False+++isDestructBlacklisted :: Judgement -> Bool+isDestructBlacklisted = _jBlacklistDestruct+++isSplitWhitelisted :: Judgement -> Bool+isSplitWhitelisted = _jWhitelistSplit+++withNewGoal :: a -> Judgement' a -> Judgement' a+withNewGoal t = field @"_jGoal" .~ t+++introduce :: Hypothesis a -> Judgement' a -> Judgement' a+-- NOTE(sandy): It's important that we put the new hypothesis terms first,+-- since 'jAcceptableDestructTargets' will never destruct a pattern that occurs+-- after a previously-destructed term.+introduce hy = field @"_jHypothesis" %~ mappend hy+++------------------------------------------------------------------------------+-- | Helper function for implementing functions which introduce new hypotheses.+introduceHypothesis+    :: (Int -> Int -> Provenance)+        -- ^ A function from the total number of args and position of this arg+        -- to its provenance.+    -> [(OccName, a)]+    -> Hypothesis a+introduceHypothesis f ns =+  Hypothesis $ zip [0..] ns <&> \(pos, (name, ty)) ->+    HyInfo name (f (length ns) pos) ty+++------------------------------------------------------------------------------+-- | Introduce bindings in the context of a lamba.+lambdaHypothesis+    :: Maybe OccName   -- ^ The name of the top level function. For any other+                       -- function, this should be 'Nothing'.+    -> [(OccName, a)]+    -> Hypothesis a+lambdaHypothesis func =+  introduceHypothesis $ \count pos ->+    maybe UserPrv (\x -> TopLevelArgPrv x pos count) func+++------------------------------------------------------------------------------+-- | Introduce a binding in a recursive context.+recursiveHypothesis :: [(OccName, a)] -> Hypothesis a+recursiveHypothesis = introduceHypothesis $ const $ const RecursivePrv+++------------------------------------------------------------------------------+-- | Check whether any of the given occnames are an ancestor of the term.+hasPositionalAncestry+    :: Foldable t+    => t OccName   -- ^ Desired ancestors.+    -> Judgement+    -> OccName     -- ^ Potential child+    -> Maybe Bool  -- ^ Just True if the result is the oldest positional ancestor+                   -- just false if it's a descendent+                   -- otherwise nothing+hasPositionalAncestry ancestors jdg name+  | not $ null ancestors+  = case any (== name) ancestors of+      True  -> Just True+      False ->+        case M.lookup name $ jAncestryMap jdg of+          Just ancestry ->+            bool Nothing (Just False) $ any (flip S.member ancestry) ancestors+          Nothing -> Nothing+  | otherwise = Nothing+++------------------------------------------------------------------------------+-- | Helper function for disallowing hypotheses that have the wrong ancestry.+filterAncestry+    :: Foldable t+    => t OccName+    -> DisallowReason+    -> Judgement+    -> Judgement+filterAncestry ancestry reason jdg =+    disallowing reason (M.keysSet $ M.filterWithKey go $ hyByName $ jHypothesis jdg) jdg+  where+    go name _+      = not+      . isJust+      $ hasPositionalAncestry ancestry jdg name+++------------------------------------------------------------------------------+-- | @filter defn pos@ removes any hypotheses which are bound in @defn@ to+-- a position other than @pos@. Any terms whose ancestry doesn't include @defn@+-- remain.+filterPosition :: OccName -> Int -> Judgement -> Judgement+filterPosition defn pos jdg =+  filterAncestry (findPositionVal jdg defn pos) (WrongBranch pos) jdg+++------------------------------------------------------------------------------+-- | Helper function for determining the ancestry list for 'filterPosition'.+findPositionVal :: Judgement' a -> OccName -> Int -> Maybe OccName+findPositionVal jdg defn pos = listToMaybe $ do+  -- It's important to inspect the entire hypothesis here, as we need to trace+  -- ancstry through potentially disallowed terms in the hypothesis.+  (name, hi) <- M.toList+              $ M.map (overProvenance expandDisallowed)+              $ hyByName+              $ jEntireHypothesis jdg+  case hi_provenance hi of+    TopLevelArgPrv defn' pos' _+      | defn == defn'+      , pos  == pos' -> pure name+    PatternMatchPrv pv+      | pv_scrutinee pv == Just defn+      , pv_position pv  == pos -> pure name+    _ -> []+++------------------------------------------------------------------------------+-- | Helper function for determining the ancestry list for+-- 'filterSameTypeFromOtherPositions'.+findDconPositionVals :: Judgement' a -> ConLike -> Int -> [OccName]+findDconPositionVals jdg dcon pos = do+  (name, hi) <- M.toList $ hyByName $ jHypothesis jdg+  case hi_provenance hi of+    PatternMatchPrv pv+      | pv_datacon  pv == Uniquely dcon+      , pv_position pv == pos -> pure name+    _ -> []+++------------------------------------------------------------------------------+-- | Disallow any hypotheses who have the same type as anything bound by the+-- given position for the datacon. Used to ensure recursive functions like+-- 'fmap' preserve the relative ordering of their arguments by eliminating any+-- other term which might match.+filterSameTypeFromOtherPositions :: ConLike -> Int -> Judgement -> Judgement+filterSameTypeFromOtherPositions dcon pos jdg =+  let hy = hyByName+         . jHypothesis+         $ filterAncestry+             (findDconPositionVals jdg dcon pos)+             (WrongBranch pos)+             jdg+      tys = S.fromList $ hi_type <$> M.elems hy+      to_remove =+        M.filter (flip S.member tys . hi_type) (hyByName $ jHypothesis jdg)+          M.\\ hy+   in disallowing Shadowed (M.keysSet to_remove) jdg+++------------------------------------------------------------------------------+-- | Return the ancestry of a 'PatVal', or 'mempty' otherwise.+getAncestry :: Judgement' a -> OccName -> Set OccName+getAncestry jdg name =+  case M.lookup name $ jPatHypothesis jdg of+    Just pv -> pv_ancestry pv+    Nothing -> mempty+++jAncestryMap :: Judgement' a -> Map OccName (Set OccName)+jAncestryMap jdg =+  flip M.map (jPatHypothesis jdg) pv_ancestry+++provAncestryOf :: Provenance -> Set OccName+provAncestryOf (TopLevelArgPrv o _ _) = S.singleton o+provAncestryOf (PatternMatchPrv (PatVal mo so _ _)) =+  maybe mempty S.singleton mo <> so+provAncestryOf (ClassMethodPrv _) = mempty+provAncestryOf UserPrv = mempty+provAncestryOf RecursivePrv = mempty+provAncestryOf (DisallowedPrv _ p2) = provAncestryOf p2+++------------------------------------------------------------------------------+-- TODO(sandy): THIS THING IS A BIG BIG HACK+--+-- Why? 'ctxDefiningFuncs' is _all_ of the functions currently beind defined+-- (eg, we might be in a where block). The head of this list is not guaranteed+-- to be the one we're interested in.+extremelyStupid__definingFunction :: Context -> OccName+extremelyStupid__definingFunction =+  fst . head . ctxDefiningFuncs+++patternHypothesis+    :: Maybe OccName+    -> ConLike+    -> Judgement' a+    -> [(OccName, a)]+    -> Hypothesis a+patternHypothesis scrutinee dc jdg+  = introduceHypothesis $ \_ pos ->+      PatternMatchPrv $+        PatVal+          scrutinee+          (maybe+              mempty+              (\scrut -> S.singleton scrut <> getAncestry jdg scrut)+              scrutinee)+          (Uniquely dc)+          pos+++------------------------------------------------------------------------------+-- | Prevent some occnames from being used in the hypothesis. This will hide+-- them from 'jHypothesis', but not from 'jEntireHypothesis'.+disallowing :: DisallowReason -> S.Set OccName -> Judgement' a -> Judgement' a+disallowing reason ns =+  field @"_jHypothesis" %~ (\z -> Hypothesis . flip fmap (unHypothesis z) $ \hi ->+    case S.member (hi_name hi) ns of+      True  -> overProvenance (DisallowedPrv reason) hi+      False -> hi+                           )+++------------------------------------------------------------------------------+-- | The hypothesis, consisting of local terms and the ambient environment+-- (impors and class methods.) Hides disallowed values.+jHypothesis :: Judgement' a -> Hypothesis a+jHypothesis+  = Hypothesis+  . filter (not . isDisallowed . hi_provenance)+  . unHypothesis+  . jEntireHypothesis+++------------------------------------------------------------------------------+-- | The whole hypothesis, including things disallowed.+jEntireHypothesis :: Judgement' a -> Hypothesis a+jEntireHypothesis = _jHypothesis+++------------------------------------------------------------------------------+-- | Just the local hypothesis.+jLocalHypothesis :: Judgement' a -> Hypothesis a+jLocalHypothesis+  = Hypothesis+  . filter (isLocalHypothesis . hi_provenance)+  . unHypothesis+  . jHypothesis+++------------------------------------------------------------------------------+-- | Given a judgment, return the hypotheses that are acceptable to destruct.+--+-- We use the ordering of the hypothesis for this purpose. Since new bindings+-- are always inserted at the beginning, we can impose a canonical ordering on+-- which order to try destructs by what order they are introduced --- stopping+-- at the first one we've already destructed.+jAcceptableDestructTargets :: Judgement' CType -> [HyInfo CType]+jAcceptableDestructTargets+  = filter (isJust . algebraicTyCon . unCType . hi_type)+  . takeWhile (not . isAlreadyDestructed . hi_provenance)+  . unHypothesis+  . jEntireHypothesis+++------------------------------------------------------------------------------+-- | If we're in a top hole, the name of the defining function.+isTopHole :: Context -> Judgement' a -> Maybe OccName+isTopHole ctx =+  bool Nothing (Just $ extremelyStupid__definingFunction ctx) . _jIsTopHole+++unsetIsTopHole :: Judgement' a -> Judgement' a+unsetIsTopHole = field @"_jIsTopHole" .~ False+++------------------------------------------------------------------------------+-- | What names are currently in scope in the hypothesis?+hyNamesInScope :: Hypothesis a -> Set OccName+hyNamesInScope = M.keysSet . hyByName+++------------------------------------------------------------------------------+-- | Are there any top-level function argument bindings in this judgement?+jHasBoundArgs :: Judgement' a -> Bool+jHasBoundArgs+  = not+  . null+  . filter (isTopLevel . hi_provenance)+  . unHypothesis+  . jLocalHypothesis+++------------------------------------------------------------------------------+-- | Fold a hypothesis into a single mapping from name to info. This+-- unavoidably will cause duplicate names (things like methods) to shadow one+-- another.+hyByName :: Hypothesis a -> Map OccName (HyInfo a)+hyByName+  = M.fromList+  . fmap (hi_name &&& id)+  . unHypothesis+++------------------------------------------------------------------------------+-- | Only the hypothesis members which are pattern vals+jPatHypothesis :: Judgement' a -> Map OccName PatVal+jPatHypothesis+  = M.mapMaybe (getPatVal . hi_provenance)+  . hyByName+  . jHypothesis+++getPatVal :: Provenance-> Maybe PatVal+getPatVal prov =+  case prov of+    PatternMatchPrv pv -> Just pv+    _                  -> Nothing+++jGoal :: Judgement' a -> a+jGoal = _jGoal+++substJdg :: TCvSubst -> Judgement -> Judgement+substJdg subst = fmap $ coerce . substTy subst . coerce+++mkFirstJudgement+    :: Hypothesis CType+    -> Bool  -- ^ are we in the top level rhs hole?+    -> Type+    -> Judgement' CType+mkFirstJudgement hy top goal = Judgement+  { _jHypothesis        = hy+  , _jBlacklistDestruct = False+  , _jWhitelistSplit    = True+  , _jIsTopHole         = top+  , _jGoal              = CType goal+  }+++------------------------------------------------------------------------------+-- | Is this a top level function binding?+isTopLevel :: Provenance -> Bool+isTopLevel TopLevelArgPrv{} = True+isTopLevel _                = False+++------------------------------------------------------------------------------+-- | Is this a local function argument, pattern match or user val?+isLocalHypothesis :: Provenance -> Bool+isLocalHypothesis UserPrv{}         = True+isLocalHypothesis PatternMatchPrv{} = True+isLocalHypothesis TopLevelArgPrv{}  = True+isLocalHypothesis _                 = False+++------------------------------------------------------------------------------+-- | Is this a pattern match?+isPatternMatch :: Provenance -> Bool+isPatternMatch PatternMatchPrv{} = True+isPatternMatch _                 = False+++------------------------------------------------------------------------------+-- | Was this term ever disallowed?+isDisallowed :: Provenance -> Bool+isDisallowed DisallowedPrv{} = True+isDisallowed _               = False++------------------------------------------------------------------------------+-- | Has this term already been disallowed?+isAlreadyDestructed :: Provenance -> Bool+isAlreadyDestructed (DisallowedPrv AlreadyDestructed _) = True+isAlreadyDestructed _ = False+++------------------------------------------------------------------------------+-- | Eliminates 'DisallowedPrv' provenances.+expandDisallowed :: Provenance -> Provenance+expandDisallowed (DisallowedPrv _ prv) = expandDisallowed prv+expandDisallowed prv                   = prv
+ src/Wingman/Judgements/SYB.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE RankNTypes          #-}++-- | Custom SYB traversals+module Wingman.Judgements.SYB where++import Data.Foldable (foldl')+import Data.Generics hiding (typeRep)+import Development.IDE.GHC.Compat+import GHC.Exts (Any)+import Type.Reflection+import Unsafe.Coerce (unsafeCoerce)+++------------------------------------------------------------------------------+-- | Like 'everything', but only looks inside 'Located' terms that contain the+-- given 'SrcSpan'.+everythingContaining+    :: forall r+     . Monoid r+    => SrcSpan+    -> GenericQ r+    -> GenericQ r+everythingContaining dst f = go+  where+    go :: GenericQ r+    go x =+      case genericIsSubspan dst x of+        Just False -> mempty+        _ -> foldl' (<>) (f x) (gmapQ go x)+++------------------------------------------------------------------------------+-- | Helper function for implementing 'everythingWithin'+--+-- NOTE(sandy): Subtly broken. In an ideal world, this function shuld return+-- @Just False@ for nodes of /any type/ which do not contain the span. But if+-- this functionality exists anywhere within the SYB machinery, I have yet to+-- find it.+genericIsSubspan+    :: SrcSpan+    -> GenericQ (Maybe Bool)+genericIsSubspan dst = mkQ1 (L noSrcSpan ()) Nothing $ \case+  L span _ -> Just $ dst `isSubspanOf` span+++------------------------------------------------------------------------------+-- | Like 'mkQ', but allows for polymorphic instantiation of its specific case.+-- This instantation matches whenever the dynamic value has the same+-- constructor as the proxy @f ()@ value.+mkQ1 :: forall a r f+      . (Data a, Data (f ()))+     => f ()                  -- ^ Polymorphic constructor to match on+     -> r                     -- ^ Default value+     -> (forall b. f b -> r)  -- ^ Polymorphic match+     -> a+     -> r+mkQ1 proxy r br a =+    case l_con == a_con && sameTypeModuloLastApp @a @(f ()) of+      -- We have proven that the two values share the same constructor, and+      -- that they have the same type if you ignore the final application.+      -- Therefore, it is safe to coerce @a@ to @f b@, since @br@ is universal+      -- over @b@ and can't inspect it.+      True  -> br $ unsafeCoerce @_ @(f Any) a+      False -> r+  where+    l_con = toConstr proxy+    a_con = toConstr a+++------------------------------------------------------------------------------+-- | Given @a ~ f1 a1@ and @b ~ f2 b2@, returns true if @f1 ~ f2@.+sameTypeModuloLastApp :: forall a b. (Typeable a, Typeable b) => Bool+sameTypeModuloLastApp =+  let tyrep1 = typeRep @a+      tyrep2 = typeRep @b+   in case (tyrep1 , tyrep2) of+        (App a _, App b _) ->+          case eqTypeRep a b of+            Just HRefl -> True+            Nothing    -> False+        _ -> False+
+ src/Wingman/Judgements/Theta.hs view
@@ -0,0 +1,188 @@+{-# LANGUAGE CPP          #-}+{-# LANGUAGE ViewPatterns #-}++module Wingman.Judgements.Theta+  ( Evidence+  , getEvidenceAtHole+  , mkEvidence+  , evidenceToSubst+  , evidenceToHypothesis+  , evidenceToThetaType+  ) where++import           Class (classTyVars)+import           Control.Applicative (empty)+import           Data.Maybe (fromMaybe, mapMaybe, maybeToList)+import           Data.Set (Set)+import qualified Data.Set as S+import           Development.IDE.Core.UseStale+import           Development.IDE.GHC.Compat+import           Generics.SYB hiding (tyConName, empty)+import           GhcPlugins (mkVarOcc, splitTyConApp_maybe, getTyVar_maybe, zipTvSubst)+#if __GLASGOW_HASKELL__ > 806+import           GhcPlugins (eqTyCon)+#else+import           GhcPlugins (nameRdrName, tyConName)+import           PrelNames (eqTyCon_RDR)+#endif+import           TcEvidence+import           TcType (substTy)+import           TcType (tcTyConAppTyCon_maybe)+import           TysPrim (eqPrimTyCon)+import           Wingman.Machinery+import           Wingman.Types+++------------------------------------------------------------------------------+-- | Something we've learned about the type environment.+data Evidence+    -- | The two types are equal, via a @a ~ b@ relationship+  = EqualityOfTypes Type Type+    -- | We have an instance in scope+  | HasInstance PredType+  deriving (Show)+++------------------------------------------------------------------------------+-- | Given a 'PredType', pull an 'Evidence' out of it.+mkEvidence :: PredType -> [Evidence]+mkEvidence (getEqualityTheta -> Just (a, b))+  = pure $ EqualityOfTypes a b+mkEvidence inst@(tcTyConAppTyCon_maybe -> Just (tyConClass_maybe -> Just cls)) = do+  (_, apps) <- maybeToList $ splitTyConApp_maybe inst+  let tvs     = classTyVars cls+      subst   = zipTvSubst tvs apps+  sc_ev <- traverse (mkEvidence . substTy subst) $ classSCTheta cls+  HasInstance inst : sc_ev+mkEvidence _ = empty+++------------------------------------------------------------------------------+-- | Build a set of 'PredType's from the evidence.+evidenceToThetaType :: [Evidence] -> Set CType+evidenceToThetaType evs = S.fromList $ do+  HasInstance t <- evs+  pure $ CType t+++------------------------------------------------------------------------------+-- | Compute all the 'Evidence' implicitly bound at the given 'SrcSpan'.+getEvidenceAtHole :: Tracked age SrcSpan -> Tracked age (LHsBinds GhcTc) -> [Evidence]+getEvidenceAtHole (unTrack -> dst)+  = concatMap mkEvidence+  . (everything (<>) $+        mkQ mempty (absBinds dst) `extQ` wrapperBinds dst `extQ` matchBinds dst)+  . unTrack+++------------------------------------------------------------------------------+-- | Update our knowledge of which types are equal.+evidenceToSubst :: Evidence -> TacticState -> TacticState+evidenceToSubst (EqualityOfTypes a b) ts =+  let tyvars = S.fromList $ mapMaybe getTyVar_maybe [a, b]+      -- If we can unify our skolems, at least one is no longer a skolem.+      -- Removing them from this set ensures we can get a subtitution between+      -- the two. But it's okay to leave them in 'ts_skolems' in general, since+      -- they won't exist after running this substitution.+      skolems = ts_skolems ts S.\\ tyvars+   in+  case tryUnifyUnivarsButNotSkolems skolems (CType a) (CType b) of+    Just subst -> updateSubst subst ts+    Nothing -> ts+evidenceToSubst HasInstance{} ts = ts+++------------------------------------------------------------------------------+-- | Get all of the methods that are in scope from this piece of 'Evidence'.+evidenceToHypothesis :: Evidence -> Hypothesis CType+evidenceToHypothesis EqualityOfTypes{} = mempty+evidenceToHypothesis (HasInstance t) =+  Hypothesis . excludeForbiddenMethods . fromMaybe [] $ methodHypothesis t+++------------------------------------------------------------------------------+-- | Given @a ~ b@ or @a ~# b@, returns @Just (a, b)@, otherwise @Nothing@.+getEqualityTheta :: PredType -> Maybe (Type, Type)+getEqualityTheta (splitTyConApp_maybe -> Just (tc, [_k, a, b]))+#if __GLASGOW_HASKELL__ > 806+  | tc == eqTyCon+#else+  | nameRdrName (tyConName tc) == eqTyCon_RDR+#endif+  = Just (a, b)+getEqualityTheta (splitTyConApp_maybe -> Just (tc, [_k1, _k2, a, b]))+  | tc == eqPrimTyCon = Just (a, b)+getEqualityTheta _ = Nothing+++------------------------------------------------------------------------------+-- | Many operations are defined in typeclasses for performance reasons, rather+-- than being a true part of the class. This function filters out those, in+-- order to keep our hypothesis space small.+excludeForbiddenMethods :: [HyInfo a] -> [HyInfo a]+excludeForbiddenMethods = filter (not . flip S.member forbiddenMethods . hi_name)+  where+    forbiddenMethods :: Set OccName+    forbiddenMethods = S.map mkVarOcc $ S.fromList+      [ -- monadfail+        "fail"+        -- show+      , "showsPrec", "showList"+        -- functor+      , "<$"+        -- applicative+      , "liftA2", "<*", "*>"+        -- monad+      , "return", ">>"+        -- alternative+      , "some", "many"+        -- foldable+      , "foldr1", "foldl1", "elem", "maximum", "minimum", "sum", "product"+        -- traversable+      , "sequenceA", "mapM", "sequence"+        -- semigroup+      , "sconcat", "stimes"+        -- monoid+      , "mconcat"+      ]+++------------------------------------------------------------------------------+-- | Extract evidence from 'AbsBinds' in scope.+absBinds ::  SrcSpan -> LHsBindLR GhcTc GhcTc -> [PredType]+absBinds dst (L src (AbsBinds _ _ h _ _ _ _))+  | dst `isSubspanOf` src = fmap idType h+absBinds _ _ = []+++------------------------------------------------------------------------------+-- | Extract evidence from 'HsWrapper's in scope+wrapperBinds ::  SrcSpan -> LHsExpr GhcTc -> [PredType]+wrapperBinds dst (L src (HsWrap _ h _))+  | dst `isSubspanOf` src = wrapper h+wrapperBinds _ _ = []+++------------------------------------------------------------------------------+-- | Extract evidence from the 'ConPatOut's bound in this 'Match'.+matchBinds :: SrcSpan -> LMatch GhcTc (LHsExpr GhcTc) -> [PredType]+matchBinds dst (L src (Match _ _ pats _))+  | dst `isSubspanOf` src = everything (<>) (mkQ mempty patBinds) pats+matchBinds _ _ = []+++------------------------------------------------------------------------------+-- | Extract evidence from a 'ConPatOut'.+patBinds ::  Pat GhcTc -> [PredType]+patBinds (ConPatOut { pat_dicts = dicts })+  = fmap idType dicts+patBinds _ = []+++------------------------------------------------------------------------------+-- | Extract the types of the evidence bindings in scope.+wrapper ::  HsWrapper -> [PredType]+wrapper (WpCompose h h2) = wrapper h <> wrapper h2+wrapper (WpEvLam v) = [idType v]+wrapper _ = []+
+ src/Wingman/KnownStrategies.hs view
@@ -0,0 +1,97 @@+module Wingman.KnownStrategies where++import Control.Monad.Error.Class+import OccName (mkVarOcc)+import Refinery.Tactic+import Wingman.Context (getCurrentDefinitions, getKnownInstance)+import Wingman.KnownStrategies.QuickCheck (deriveArbitrary)+import Wingman.Machinery (tracing)+import Wingman.Tactics+import Wingman.Types+import Wingman.Judgements (jGoal)+import Data.Foldable (for_)+import Wingman.FeatureSet+import Control.Applicative (empty)+import Control.Monad.Reader.Class (asks)+++knownStrategies :: TacticsM ()+knownStrategies = choice+  [ known "fmap" deriveFmap+  , known "mempty" deriveMempty+  , known "arbitrary" deriveArbitrary+  , featureGuard FeatureKnownMonoid $ known "<>" deriveMappend+  , featureGuard FeatureKnownMonoid $ known "mappend" deriveMappend+  ]+++------------------------------------------------------------------------------+-- | Guard a tactic behind a feature.+featureGuard :: Feature -> TacticsM a -> TacticsM a+featureGuard feat t = do+  fs <- asks ctxFeatureSet+  case hasFeature feat fs of+    True -> t+    False -> empty+++known :: String -> TacticsM () -> TacticsM ()+known name t = do+  getCurrentDefinitions >>= \case+    [(def, _)] | def == mkVarOcc name ->+      tracing ("known " <> name) t+    _ -> throwError NoApplicableTactic+++deriveFmap :: TacticsM ()+deriveFmap = do+  try intros+  overAlgebraicTerms homo+  choice+    [ overFunctions apply >> auto' 2+    , assumption+    , recursion+    ]+++------------------------------------------------------------------------------+-- | We derive mappend by binding the arguments, introducing the constructor,+-- and then calling mappend recursively. At each recursive call, we filter away+-- any binding that isn't in an analogous position.+--+-- The recursive call first attempts to use an instace in scope. If that fails,+-- it fals back to trying a theta method from the hypothesis with the correct+-- name.+deriveMappend :: TacticsM ()+deriveMappend = do+  try intros+  destructAll+  split+  g <- goal+  minst <- getKnownInstance kt_semigroup+         . pure+         . unCType+         $ jGoal g+  for_ minst $ \(cls, df) -> do+    restrictPositionForApplication+      (applyMethod cls df $ mkVarOcc "<>")+      assumption+  try $+    restrictPositionForApplication+      (applyByName $ mkVarOcc "<>")+      assumption+++------------------------------------------------------------------------------+-- | We derive mempty by introducing the constructor, and then trying to+-- 'mempty' everywhere. This smaller 'mempty' might come from an instance in+-- scope, or it might come from the hypothesis theta.+deriveMempty :: TacticsM ()+deriveMempty = do+  split+  g <- goal+  minst <- getKnownInstance kt_monoid [unCType $ jGoal g]+  for_ minst $ \(cls, df) -> do+    applyMethod cls df $ mkVarOcc "mempty"+  try assumption+
+ src/Wingman/KnownStrategies/QuickCheck.hs view
@@ -0,0 +1,114 @@+module Wingman.KnownStrategies.QuickCheck where++import ConLike (ConLike(RealDataCon))+import Control.Monad.Except (MonadError (throwError))+import Data.Bool (bool)+import Data.Generics (everything, mkQ)+import Data.List (partition)+import DataCon (DataCon, dataConName)+import Development.IDE.GHC.Compat (GhcPs, HsExpr, noLoc)+import GHC.Exts (IsString (fromString))+import GHC.List (foldl')+import GHC.SourceGen (int)+import GHC.SourceGen.Binds (match, valBind)+import GHC.SourceGen.Expr (case', lambda, let')+import GHC.SourceGen.Overloaded (App ((@@)), HasList (list))+import GHC.SourceGen.Pat (conP)+import OccName (HasOccName (occName), mkVarOcc, occNameString)+import Refinery.Tactic (goal, rule)+import TyCon (TyCon, tyConDataCons, tyConName)+import Type (splitTyConApp_maybe)+import Wingman.CodeGen+import Wingman.Judgements (jGoal)+import Wingman.Machinery (tracePrim)+import Wingman.Types+++------------------------------------------------------------------------------+-- | Known tactic for deriving @arbitrary :: Gen a@. This tactic splits the+-- type's data cons into terminal and inductive cases, and generates code that+-- produces a terminal if the QuickCheck size parameter is <=1, or any data con+-- otherwise. It correctly scales recursive parameters, ensuring termination.+deriveArbitrary :: TacticsM ()+deriveArbitrary = do+  ty <- jGoal <$> goal+  case splitTyConApp_maybe $ unCType ty of+    Just (gen_tc, [splitTyConApp_maybe -> Just (tc, apps)])+        | occNameString (occName $ tyConName gen_tc) == "Gen" -> do+      rule $ \_ -> do+        let dcs = tyConDataCons tc+            (terminal, big) = partition ((== 0) . genRecursiveCount)+                        $ fmap (mkGenerator tc apps) dcs+            terminal_expr = mkVal "terminal"+            oneof_expr = mkVal "oneof"+        pure+          $ Synthesized (tracePrim "deriveArbitrary")+              -- TODO(sandy): This thing is not actually empty! We produced+              -- a bespoke binding "terminal", and a not-so-bespoke "n".+              -- But maybe it's fine for known rules?+              mempty+              mempty+              mempty+          $ noLoc $+              let' [valBind (fromString "terminal") $ list $ fmap genExpr terminal] $+                appDollar (mkFunc "sized") $ lambda [bvar' (mkVarOcc "n")] $+                  case' (infixCall "<=" (mkVal "n") (int 1))+                    [ match [conP (fromString "True") []] $+                        oneof_expr @@ terminal_expr+                    , match [conP (fromString "False") []] $+                        appDollar oneof_expr $+                          infixCall "<>"+                            (list $ fmap genExpr big)+                            terminal_expr+                    ]+    _ -> throwError $ GoalMismatch "deriveArbitrary" ty+++------------------------------------------------------------------------------+-- | Helper data type for the generator of a specific data con.+data Generator = Generator+  { genRecursiveCount :: Integer+  , genExpr           :: HsExpr GhcPs+  }+++------------------------------------------------------------------------------+-- | Make a 'Generator' for a given tycon instantiated with the given @[Type]@.+mkGenerator :: TyCon -> [Type] -> DataCon -> Generator+mkGenerator tc apps dc = do+  let dc_expr   = var' $ occName $ dataConName dc+      args = conLikeInstOrigArgTys' (RealDataCon dc) apps+      num_recursive_calls = sum $ fmap (bool 0 1 . doesTypeContain tc) args+      mkArbitrary = mkArbitraryCall tc num_recursive_calls+  Generator num_recursive_calls $ case args of+    []  -> mkFunc "pure" @@ dc_expr+    (a : as) ->+      foldl'+        (infixCall "<*>")+        (infixCall "<$>" dc_expr $ mkArbitrary a)+        (fmap mkArbitrary as)+++------------------------------------------------------------------------------+-- | Check if the given 'TyCon' exists anywhere in the 'Type'.+doesTypeContain :: TyCon -> Type -> Bool+doesTypeContain recursive_tc =+  everything (||) $ mkQ False (== recursive_tc)+++------------------------------------------------------------------------------+-- | Generate the correct sort of call to @arbitrary@. For recursive calls, we+-- need to scale down the size parameter, either by a constant factor of 1 if+-- it's the only recursive parameter, or by @`div` n@ where n is the number of+-- recursive parameters. For all other types, just call @arbitrary@ directly.+mkArbitraryCall :: TyCon -> Integer -> Type -> HsExpr GhcPs+mkArbitraryCall recursive_tc n ty =+  let arbitrary = mkFunc "arbitrary"+   in case doesTypeContain recursive_tc ty of+        True ->+          mkFunc "scale"+            @@ bool (mkFunc "flip" @@ mkFunc "div" @@ int n)+                    (mkFunc "subtract" @@ int 1)+                    (n == 1)+            @@ arbitrary+        False -> arbitrary
+ src/Wingman/LanguageServer.hs view
@@ -0,0 +1,516 @@+{-# LANGUAGE CPP               #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies      #-}++module Wingman.LanguageServer where++import           ConLike+import           Control.Arrow+import           Control.Monad+import           Control.Monad.State (State, get, put, evalState)+import           Control.Monad.Trans.Maybe+import           Data.Coerce+import           Data.Functor ((<&>))+import           Data.Generics.Aliases (mkQ)+import           Data.Generics.Schemes (everything)+import qualified Data.HashMap.Strict as Map+import           Data.IORef (readIORef)+import qualified Data.Map as M+import           Data.Maybe+import           Data.Monoid+import           Data.Set (Set)+import qualified Data.Set as S+import qualified Data.Text as T+import           Data.Traversable+import           Development.IDE (getFilesOfInterest, ShowDiagnostic (ShowDiag), srcSpanToRange)+import           Development.IDE (hscEnv)+import           Development.IDE.Core.RuleTypes+import           Development.IDE.Core.Rules (usePropertyAction)+import           Development.IDE.Core.Service (runAction)+import           Development.IDE.Core.Shake (IdeState (..), uses, define, use)+import qualified Development.IDE.Core.Shake as IDE+import           Development.IDE.Core.UseStale+import           Development.IDE.GHC.Compat+import           Development.IDE.GHC.Error (realSrcSpanToRange)+import           Development.IDE.Spans.LocalBindings (Bindings, getDefiningBindings)+import           Development.Shake (Action, RuleResult, Rules, action)+import           Development.Shake.Classes (Typeable, Binary, Hashable, NFData)+import qualified FastString+import           GHC.Generics (Generic)+import           GhcPlugins (tupleDataCon, consDataCon, substTyAddInScope, ExternalPackageState, HscEnv (hsc_EPS), liftIO)+import qualified Ide.Plugin.Config as Plugin+import           Ide.Plugin.Properties+import           Ide.PluginUtils (usePropertyLsp)+import           Ide.Types (PluginId)+import           Language.LSP.Server (MonadLsp, sendNotification)+import           Language.LSP.Types+import           OccName+import           Prelude hiding (span)+import           SrcLoc (containsSpan)+import           TcRnTypes (tcg_binds, TcGblEnv)+import           Wingman.Context+import           Wingman.FeatureSet+import           Wingman.GHC+import           Wingman.Judgements+import           Wingman.Judgements.SYB (everythingContaining)+import           Wingman.Judgements.Theta+import           Wingman.Range+import           Wingman.Types+++tacticDesc :: T.Text -> T.Text+tacticDesc name = "fill the hole using the " <> name <> " tactic"+++------------------------------------------------------------------------------+-- | The name of the command for the LS.+tcCommandName :: TacticCommand -> T.Text+tcCommandName = T.pack . show+++runIde :: IdeState -> Action a -> IO a+runIde state = runAction "tactic" state+++runCurrentIde+    :: forall a r+     . ( r ~ RuleResult a+       , Eq a , Hashable a , Binary a , Show a , Typeable a , NFData a+       , Show r, Typeable r, NFData r+       )+    => IdeState+    -> NormalizedFilePath+    -> a+    -> MaybeT IO (Tracked 'Current r)+runCurrentIde state nfp a = MaybeT $ fmap (fmap unsafeMkCurrent) $ runIde state $ use a nfp+++runStaleIde+    :: forall a r+     . ( r ~ RuleResult a+       , Eq a , Hashable a , Binary a , Show a , Typeable a , NFData a+       , Show r, Typeable r, NFData r+       )+    => IdeState+    -> NormalizedFilePath+    -> a+    -> MaybeT IO (TrackedStale r)+runStaleIde state nfp a = MaybeT $ runIde state $ useWithStale a nfp+++unsafeRunStaleIde+    :: forall a r+     . ( r ~ RuleResult a+       , Eq a , Hashable a , Binary a , Show a , Typeable a , NFData a+       , Show r, Typeable r, NFData r+       )+    => IdeState+    -> NormalizedFilePath+    -> a+    -> MaybeT IO r+unsafeRunStaleIde state nfp a = do+  (r, _) <- MaybeT $ runIde state $ IDE.useWithStale a nfp+  pure r+++------------------------------------------------------------------------------++properties :: Properties+  '[ 'PropertyKey "hole_severity" ('TEnum (Maybe DiagnosticSeverity))+   , 'PropertyKey "max_use_ctor_actions" 'TInteger+   , 'PropertyKey "features" 'TString+   , 'PropertyKey "timeout_duration" 'TInteger+   ]+properties = emptyProperties+  & defineIntegerProperty #timeout_duration+    "The timeout for Wingman actions, in seconds" 2+  & defineStringProperty #features+    "Feature set used by Wingman" ""+  & defineIntegerProperty #max_use_ctor_actions+    "Maximum number of `Use constructor <x>` code actions that can appear" 5+  & defineEnumProperty #hole_severity+    "The severity to use when showing hole diagnostics. These are noisy, but some editors don't allow jumping to all severities."+    [ (Just DsError,   "error")+    , (Just DsWarning, "warning")+    , (Just DsInfo,    "info")+    , (Just DsHint,    "hint")+    , (Nothing,        "none")+    ]+    Nothing+++-- | Get the the plugin config+getTacticConfig :: MonadLsp Plugin.Config m => PluginId -> m Config+getTacticConfig pId =+  Config+    <$> (parseFeatureSet <$> usePropertyLsp #features pId properties)+    <*> usePropertyLsp #max_use_ctor_actions pId properties+    <*> usePropertyLsp #timeout_duration pId properties++------------------------------------------------------------------------------+-- | Get the current feature set from the plugin config.+getFeatureSet :: MonadLsp Plugin.Config m => PluginId -> m FeatureSet+getFeatureSet  = fmap cfg_feature_set . getTacticConfig+++getIdeDynflags+    :: IdeState+    -> NormalizedFilePath+    -> MaybeT IO DynFlags+getIdeDynflags state nfp = do+  -- Ok to use the stale 'ModIface', since all we need is its 'DynFlags'+  -- which don't change very often.+  msr <- unsafeRunStaleIde state nfp GetModSummaryWithoutTimestamps+  pure $ ms_hspp_opts $ msrModSummary msr+++------------------------------------------------------------------------------+-- | Find the last typechecked module, and find the most specific span, as well+-- as the judgement at the given range.+judgementForHole+    :: IdeState+    -> NormalizedFilePath+    -> Tracked 'Current Range+    -> FeatureSet+    -> MaybeT IO (Tracked 'Current Range, Judgement, Context, DynFlags)+judgementForHole state nfp range features = do+  TrackedStale asts amapping  <- runStaleIde state nfp GetHieAst+  case unTrack asts of+    HAR _ _  _ _ (HieFromDisk _) -> fail "Need a fresh hie file"+    HAR _ (unsafeCopyAge asts -> hf) _ _ HieFresh -> do+      range' <- liftMaybe $ mapAgeFrom amapping range+      binds <- runStaleIde state nfp GetBindings+      tcg <- fmap (fmap tmrTypechecked)+           $ runStaleIde state nfp TypeCheck+      hscenv <- runStaleIde state nfp GhcSessionDeps++      (rss, g) <- liftMaybe $ getSpanAndTypeAtHole range' hf+      new_rss <- liftMaybe $ mapAgeTo amapping rss++      -- KnownThings is just the instances in scope. There are no ranges+      -- involved, so it's not crucial to track ages.+      let henv = untrackedStaleValue $ hscenv+      eps <- liftIO $ readIORef $ hsc_EPS $ hscEnv henv+      kt <- knownThings (untrackedStaleValue tcg) henv++      (jdg, ctx) <- liftMaybe $ mkJudgementAndContext features g binds new_rss tcg eps kt++      dflags <- getIdeDynflags state nfp+      pure (fmap realSrcSpanToRange new_rss, jdg, ctx, dflags)+++mkJudgementAndContext+    :: FeatureSet+    -> Type+    -> TrackedStale Bindings+    -> Tracked 'Current RealSrcSpan+    -> TrackedStale TcGblEnv+    -> ExternalPackageState+    -> KnownThings+    -> Maybe (Judgement, Context)+mkJudgementAndContext features g (TrackedStale binds bmap) rss (TrackedStale tcg tcgmap) eps kt = do+  binds_rss <- mapAgeFrom bmap rss+  tcg_rss <- mapAgeFrom tcgmap rss++  let tcs = fmap tcg_binds tcg+      ctx = mkContext features+              (mapMaybe (sequenceA . (occName *** coerce))+                $ unTrack+                $ getDefiningBindings <$> binds <*> binds_rss)+              (unTrack tcg)+              eps+              kt+              evidence+      top_provs = getRhsPosVals tcg_rss tcs+      already_destructed = getAlreadyDestructed (fmap RealSrcSpan tcg_rss) tcs+      local_hy = spliceProvenance top_provs+               $ hypothesisFromBindings binds_rss binds+      evidence = getEvidenceAtHole (fmap RealSrcSpan tcg_rss) tcs+      cls_hy = foldMap evidenceToHypothesis evidence+      subst = ts_unifier $ appEndo (foldMap (Endo . evidenceToSubst) evidence) defaultTacticState+  pure $+    ( disallowing AlreadyDestructed already_destructed+    $ fmap (CType . substTyAddInScope subst . unCType) $ mkFirstJudgement+          (local_hy <> cls_hy)+          (isRhsHole tcg_rss tcs)+          g+    , ctx+    )+++------------------------------------------------------------------------------+-- | Determine which bindings have already been destructed by the location of+-- the hole.+getAlreadyDestructed+    :: Tracked age SrcSpan+    -> Tracked age (LHsBinds GhcTc)+    -> Set OccName+getAlreadyDestructed (unTrack -> span) (unTrack -> binds) =+  everythingContaining span+    (mkQ mempty $ \case+      Case (HsVar _ (L _ (occName -> var))) _ ->+        S.singleton var+      (_ :: HsExpr GhcTc) -> mempty+    ) binds+++getSpanAndTypeAtHole+    :: Tracked age Range+    -> Tracked age (HieASTs b)+    -> Maybe (Tracked age RealSrcSpan, b)+getSpanAndTypeAtHole r@(unTrack -> range) (unTrack -> hf) = do+  join $ listToMaybe $ M.elems $ flip M.mapWithKey (getAsts hf) $ \fs ast ->+    case selectSmallestContaining (rangeToRealSrcSpan (FastString.unpackFS fs) range) ast of+      Nothing -> Nothing+      Just ast' -> do+        let info = nodeInfo ast'+        ty <- listToMaybe $ nodeType info+        guard $ ("HsUnboundVar","HsExpr") `S.member` nodeAnnotations info+        -- Ensure we're actually looking at a hole here+        guard $ all (either (const False) $ isHole . occName)+          $ M.keysSet $ nodeIdentifiers info+        pure (unsafeCopyAge r $ nodeSpan ast', ty)++++------------------------------------------------------------------------------+-- | Combine two (possibly-overlapping) hypotheses; using the provenance from+-- the first hypothesis if the bindings overlap.+spliceProvenance+    :: Hypothesis a  -- ^ Bindings to keep+    -> Hypothesis a  -- ^ Bindings to keep if they don't overlap with the first set+    -> Hypothesis a+spliceProvenance top x =+  let bound = S.fromList $ fmap hi_name $ unHypothesis top+   in mappend top $ Hypothesis . filter (flip S.notMember bound . hi_name) $ unHypothesis x+++------------------------------------------------------------------------------+-- | Compute top-level position vals of a function+getRhsPosVals+    :: Tracked age RealSrcSpan+    -> Tracked age TypecheckedSource+    -> Hypothesis CType+getRhsPosVals (unTrack -> rss) (unTrack -> tcs)+  = everything (<>) (mkQ mempty $ \case+      TopLevelRHS name ps+          (L (RealSrcSpan span)  -- body with no guards and a single defn+            (HsVar _ (L _ hole)))+        | containsSpan rss span  -- which contains our span+        , isHole $ occName hole  -- and the span is a hole+        -> flip evalState 0 $ buildTopLevelHypothesis name ps+      _ -> mempty+  ) tcs+++------------------------------------------------------------------------------+-- | Construct a hypothesis given the patterns from the left side of a HsMatch.+-- These correspond to things that the user put in scope before running+-- tactics.+buildTopLevelHypothesis+    :: OccName  -- ^ Function name+    -> [PatCompat GhcTc]+    -> State Int (Hypothesis CType)+buildTopLevelHypothesis name ps = do+  fmap mconcat $+    for (zip [0..] ps) $ \(ix, p) ->+      buildPatHy (TopLevelArgPrv name ix $ length ps) p+++------------------------------------------------------------------------------+-- | Construct a hypothesis for a single pattern, including building+-- sub-hypotheses for constructor pattern matches.+buildPatHy :: Provenance -> PatCompat GhcTc -> State Int (Hypothesis CType)+buildPatHy prov (fromPatCompat -> p0) =+  case p0 of+    VarPat  _ x   -> pure $ mkIdHypothesis (unLoc x) prov+    LazyPat _ p   -> buildPatHy prov p+    AsPat   _ x p -> do+      hy' <- buildPatHy prov p+      pure $ mkIdHypothesis (unLoc x) prov <> hy'+    ParPat  _ p   -> buildPatHy prov p+    BangPat _ p   -> buildPatHy prov p+    ViewPat _ _ p -> buildPatHy prov p+    -- Desugar lists into cons+    ListPat _ [] -> pure mempty+    ListPat x@(ListPatTc ty _) (p : ps) ->+      mkDerivedConHypothesis prov (RealDataCon consDataCon) [ty]+        [ (0, p)+        , (1, toPatCompat $ ListPat x ps)+        ]+    -- Desugar tuples into an explicit constructor+    TuplePat tys pats boxity ->+      mkDerivedConHypothesis+        prov+        (RealDataCon $ tupleDataCon boxity $ length pats)+        tys+          $ zip [0.. ] pats+    ConPatOut (L _ con) args _ _ _ f _ ->+      case f of+        PrefixCon l_pgt ->+          mkDerivedConHypothesis prov con args $ zip [0..] l_pgt+        InfixCon pgt pgt5 ->+          mkDerivedConHypothesis prov con args $ zip [0..] [pgt, pgt5]+        RecCon r ->+          mkDerivedRecordHypothesis prov con args r+#if __GLASGOW_HASKELL__ >= 808+    SigPat  _ p _ -> buildPatHy prov p+#endif+#if __GLASGOW_HASKELL__ == 808+    XPat   p      -> buildPatHy prov $ unLoc p+#endif+    _             -> pure mempty+++------------------------------------------------------------------------------+-- | Like 'mkDerivedConHypothesis', but for record patterns.+mkDerivedRecordHypothesis+    :: Provenance+    -> ConLike  -- ^ Destructing constructor+    -> [Type]   -- ^ Applied type variables+    -> HsRecFields GhcTc (PatCompat GhcTc)+    -> State Int (Hypothesis CType)+mkDerivedRecordHypothesis prov dc args (HsRecFields (fmap unLoc -> fs) _)+  | Just rec_fields <- getRecordFields dc+  = do+    let field_lookup = M.fromList $ zip (fmap (occNameFS . fst) rec_fields) [0..]+    mkDerivedConHypothesis prov dc args $ fs <&> \(HsRecField (L _ rec_occ) p _) ->+      ( field_lookup M.! (occNameFS $ occName $ unLoc $ rdrNameFieldOcc rec_occ)+      , p+      )+mkDerivedRecordHypothesis _ _ _ _ =+  error "impossible! using record pattern on something that isn't a record"+++------------------------------------------------------------------------------+-- | Construct a fake variable name. Used to track the provenance of top-level+-- pattern matches which otherwise wouldn't have anything to attach their+-- 'TopLevelArgPrv' to.+mkFakeVar :: State Int OccName+mkFakeVar = do+  i <- get+  put $ i + 1+  pure $ mkVarOcc $ "_" <> show i+++------------------------------------------------------------------------------+-- | Construct a fake varible to attach the current 'Provenance' to, and then+-- build a sub-hypothesis for the pattern match.+mkDerivedConHypothesis+    :: Provenance+    -> ConLike                   -- ^ Destructing constructor+    -> [Type]                    -- ^ Applied type variables+    -> [(Int, PatCompat GhcTc)]  -- ^ Patterns, and their order in the data con+    -> State Int (Hypothesis CType)+mkDerivedConHypothesis prov dc args ps = do+  var <- mkFakeVar+  hy' <- fmap mconcat $+    for ps $ \(ix, p) -> do+      let prov' = PatternMatchPrv+               $ PatVal (Just var)+                        (S.singleton var <> provAncestryOf prov)+                        (Uniquely dc)+                        ix+      buildPatHy prov' p+  pure+    $ mappend hy'+    $ Hypothesis+    $ pure+    $ HyInfo var (DisallowedPrv AlreadyDestructed prov)+    $ CType+    -- TODO(sandy): This is the completely wrong type, but we don't have a good+    -- way to get the real one. It's probably OK though, since we're generating+    -- this term with a disallowed provenance, and it doesn't actually exist+    -- anyway.+    $ conLikeResTy dc args+++------------------------------------------------------------------------------+-- | Build a 'Hypothesis' given an 'Id'.+mkIdHypothesis :: Id -> Provenance -> Hypothesis CType+mkIdHypothesis (splitId -> (name, ty)) prov =+  Hypothesis $ pure $ HyInfo name prov ty+++------------------------------------------------------------------------------+-- | Is this hole immediately to the right of an equals sign?+isRhsHole :: Tracked age RealSrcSpan -> Tracked age TypecheckedSource -> Bool+isRhsHole (unTrack -> rss) (unTrack -> tcs) =+  everything (||) (mkQ False $ \case+      TopLevelRHS _ _ (L (RealSrcSpan span) _) -> containsSpan rss span+      _                                        -> False+    ) tcs+++ufmSeverity :: UserFacingMessage -> MessageType+ufmSeverity TacticErrors            = MtError+ufmSeverity TimedOut                = MtInfo+ufmSeverity NothingToDo             = MtInfo+ufmSeverity (InfrastructureError _) = MtError+++mkShowMessageParams :: UserFacingMessage -> ShowMessageParams+mkShowMessageParams ufm = ShowMessageParams (ufmSeverity ufm) $ T.pack $ show ufm+++showLspMessage :: MonadLsp cfg m => ShowMessageParams -> m ()+showLspMessage = sendNotification SWindowShowMessage+++-- This rule only exists for generating file diagnostics+-- so the RuleResult is empty+data WriteDiagnostics = WriteDiagnostics+    deriving (Eq, Show, Typeable, Generic)++instance Hashable WriteDiagnostics+instance NFData   WriteDiagnostics+instance Binary   WriteDiagnostics++type instance RuleResult WriteDiagnostics = ()++wingmanRules :: PluginId -> Rules ()+wingmanRules plId = do+  define $ \WriteDiagnostics nfp ->+    usePropertyAction #hole_severity plId properties >>= \case+      Nothing -> pure (mempty, Just ())+      Just severity ->+        use GetParsedModule nfp >>= \case+          Nothing ->+            pure ([], Nothing)+          Just pm -> do+            let holes :: [Range]+                holes =+                  everything (<>)+                    (mkQ mempty $ \case+                      L span (HsVar _ (L _ name))+                        | isHole (occName name) ->+                            maybeToList $ srcSpanToRange span+                      L span (HsUnboundVar _ (TrueExprHole occ))+                        | isHole occ ->+                            maybeToList $ srcSpanToRange span+#if __GLASGOW_HASKELL__ <= 808+                      L span (EWildPat _) ->+                        maybeToList $ srcSpanToRange span+#endif+                      (_ :: LHsExpr GhcPs) -> mempty+                    ) $ pm_parsed_source pm+            pure+              ( fmap (\r -> (nfp, ShowDiag, mkDiagnostic severity r)) holes+              , Just ()+              )++  action $ do+    files <- getFilesOfInterest+    void $ uses WriteDiagnostics $ Map.keys files+++mkDiagnostic :: DiagnosticSeverity -> Range -> Diagnostic+mkDiagnostic severity r =+  Diagnostic r+    (Just severity)+    (Just $ InR "hole")+    (Just "wingman")+    "Hole"+    (Just $ List [DtUnnecessary])+    Nothing+
+ src/Wingman/LanguageServer/TacticProviders.hs view
@@ -0,0 +1,296 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-}++module Wingman.LanguageServer.TacticProviders+  ( commandProvider+  , commandTactic+  , tcCommandId+  , TacticParams (..)+  , TacticProviderData (..)+  ) where++import           Control.Monad+import           Control.Monad.Error.Class (MonadError (throwError))+import           Data.Aeson+import           Data.Bool (bool)+import           Data.Coerce+import qualified Data.Map as M+import           Data.Maybe+import           Data.Monoid+import qualified Data.Text as T+import           Data.Traversable+import           DataCon (dataConName)+import           Development.IDE.Core.UseStale (Tracked, Age(..))+import           Development.IDE.GHC.Compat+import           GHC.Generics+import           GHC.LanguageExtensions.Type (Extension (LambdaCase))+import           Ide.PluginUtils+import           Ide.Types+import           Language.LSP.Types+import           OccName+import           Prelude hiding (span)+import           Refinery.Tactic (goal)+import           Wingman.Auto+import           Wingman.FeatureSet+import           Wingman.GHC+import           Wingman.Judgements+import           Wingman.Tactics+import           Wingman.Types+++------------------------------------------------------------------------------+-- | A mapping from tactic commands to actual tactics for refinery.+commandTactic :: TacticCommand -> OccName -> TacticsM ()+commandTactic Auto                   = const auto+commandTactic Intros                 = const intros+commandTactic Destruct               = useNameFromHypothesis destruct+commandTactic Homomorphism           = useNameFromHypothesis homo+commandTactic DestructLambdaCase     = const destructLambdaCase+commandTactic HomomorphismLambdaCase = const homoLambdaCase+commandTactic DestructAll            = const destructAll+commandTactic UseDataCon             = userSplit+commandTactic Refine                 = const refine+++------------------------------------------------------------------------------+-- | The LSP kind+tacticKind :: TacticCommand -> T.Text+tacticKind Auto                   = "fillHole"+tacticKind Intros                 = "introduceLambda"+tacticKind Destruct               = "caseSplit"+tacticKind Homomorphism           = "homomorphicCaseSplit"+tacticKind DestructLambdaCase     = "lambdaCase"+tacticKind HomomorphismLambdaCase = "homomorphicLambdaCase"+tacticKind DestructAll            = "splitFuncArgs"+tacticKind UseDataCon             = "useConstructor"+tacticKind Refine                 = "refine"+++------------------------------------------------------------------------------+-- | Whether or not this code action is preferred -- ostensibly refers to+-- whether or not we can bind it to a key in vs code?+tacticPreferred :: TacticCommand -> Bool+tacticPreferred Auto                   = True+tacticPreferred Intros                 = True+tacticPreferred Destruct               = True+tacticPreferred Homomorphism           = False+tacticPreferred DestructLambdaCase     = False+tacticPreferred HomomorphismLambdaCase = False+tacticPreferred DestructAll            = True+tacticPreferred UseDataCon             = True+tacticPreferred Refine                 = True+++mkTacticKind :: TacticCommand -> CodeActionKind+mkTacticKind =+  CodeActionUnknown . mappend "refactor.wingman." . tacticKind+++------------------------------------------------------------------------------+-- | Mapping from tactic commands to their contextual providers. See 'provide',+-- 'filterGoalType' and 'filterBindingType' for the nitty gritty.+commandProvider :: TacticCommand -> TacticProvider+commandProvider Auto  = provide Auto ""+commandProvider Intros =+  filterGoalType isFunction $+    provide Intros ""+commandProvider Destruct =+  filterBindingType destructFilter $ \occ _ ->+    provide Destruct $ T.pack $ occNameString occ+commandProvider Homomorphism =+  filterBindingType homoFilter $ \occ _ ->+    provide Homomorphism $ T.pack $ occNameString occ+commandProvider DestructLambdaCase =+  requireExtension LambdaCase $+    filterGoalType (isJust . lambdaCaseable) $+      provide DestructLambdaCase ""+commandProvider HomomorphismLambdaCase =+  requireExtension LambdaCase $+    filterGoalType ((== Just True) . lambdaCaseable) $+      provide HomomorphismLambdaCase ""+commandProvider DestructAll =+  requireFeature FeatureDestructAll $+    withJudgement $ \jdg ->+      case _jIsTopHole jdg && jHasBoundArgs jdg of+        True  -> provide DestructAll ""+        False -> mempty+commandProvider UseDataCon =+  withConfig $ \cfg ->+    requireFeature FeatureUseDataCon $+      filterTypeProjection+          ( guardLength (<= cfg_max_use_ctor_actions cfg)+          . fromMaybe []+          . fmap fst+          . tacticsGetDataCons+          ) $ \dcon ->+        provide UseDataCon+          . T.pack+          . occNameString+          . occName+          $ dataConName dcon+commandProvider Refine =+  requireFeature FeatureRefineHole $+    provide Refine ""+++------------------------------------------------------------------------------+-- | Return an empty list if the given predicate doesn't hold over the length+guardLength :: (Int -> Bool) -> [a] -> [a]+guardLength f as = bool [] as $ f $ length as+++------------------------------------------------------------------------------+-- | A 'TacticProvider' is a way of giving context-sensitive actions to the LS+-- UI.+type TacticProvider+     = TacticProviderData+    -> IO [Command |? CodeAction]++data TacticProviderData = TacticProviderData+  { tpd_dflags :: DynFlags+  , tpd_config :: Config+  , tpd_plid   :: PluginId+  , tpd_uri    :: Uri+  , tpd_range  :: Tracked 'Current Range+  , tpd_jdg    :: Judgement+  }+++data TacticParams = TacticParams+    { tp_file     :: Uri    -- ^ Uri of the file to fill the hole in+    , tp_range    :: Tracked 'Current Range  -- ^ The range of the hole+    , tp_var_name :: T.Text+    }+  deriving stock (Show, Eq, Generic)+  deriving anyclass (ToJSON, FromJSON)+++------------------------------------------------------------------------------+-- | Restrict a 'TacticProvider', making sure it appears only when the given+-- 'Feature' is in the feature set.+requireFeature :: Feature -> TacticProvider -> TacticProvider+requireFeature f tp tpd =+  case hasFeature f $ cfg_feature_set $ tpd_config tpd of+    True  -> tp tpd+    False -> pure []+++------------------------------------------------------------------------------+-- | Restrict a 'TacticProvider', making sure it appears only when the given+-- predicate holds for the goal.+requireExtension :: Extension -> TacticProvider -> TacticProvider+requireExtension ext tp tpd =+  case xopt ext $ tpd_dflags tpd of+    True  -> tp tpd+    False -> pure []+++------------------------------------------------------------------------------+-- | Restrict a 'TacticProvider', making sure it appears only when the given+-- predicate holds for the goal.+filterGoalType :: (Type -> Bool) -> TacticProvider -> TacticProvider+filterGoalType p tp tpd =+  case p $ unCType $ jGoal $ tpd_jdg tpd of+    True  -> tp tpd+    False -> pure []+++------------------------------------------------------------------------------+-- | Restrict a 'TacticProvider', making sure it appears only when the given+-- predicate holds for the goal.+withJudgement :: (Judgement -> TacticProvider) -> TacticProvider+withJudgement tp tpd = tp (tpd_jdg tpd) tpd+++------------------------------------------------------------------------------+-- | Multiply a 'TacticProvider' for each binding, making sure it appears only+-- when the given predicate holds over the goal and binding types.+filterBindingType+    :: (Type -> Type -> Bool)  -- ^ Goal and then binding types.+    -> (OccName -> Type -> TacticProvider)+    -> TacticProvider+filterBindingType p tp tpd =+  let jdg = tpd_jdg tpd+      hy  = jLocalHypothesis jdg+      g   = jGoal jdg+   in fmap join $ for (unHypothesis hy) $ \hi ->+        let ty = unCType $ hi_type hi+         in case p (unCType g) ty of+              True  -> tp (hi_name hi) ty tpd+              False -> pure []+++------------------------------------------------------------------------------+-- | Multiply a 'TacticProvider' by some feature projection out of the goal+-- type. Used e.g. to crete a code action for every data constructor.+filterTypeProjection+    :: (Type -> [a])  -- ^ Features of the goal to look into further+    -> (a -> TacticProvider)+    -> TacticProvider+filterTypeProjection p tp tpd =+  fmap join $ for (p $ unCType $ jGoal $ tpd_jdg tpd) $ \a ->+      tp a tpd+++------------------------------------------------------------------------------+-- | Get access to the 'Config' when building a 'TacticProvider'.+withConfig :: (Config -> TacticProvider) -> TacticProvider+withConfig tp tpd = tp (tpd_config tpd) tpd++++------------------------------------------------------------------------------+-- | Lift a function over 'HyInfo's to one that takes an 'OccName' and tries to+-- look it up in the hypothesis.+useNameFromHypothesis :: (HyInfo CType -> TacticsM a) -> OccName -> TacticsM a+useNameFromHypothesis f name = do+  hy <- jHypothesis <$> goal+  case M.lookup name $ hyByName hy of+    Just hi -> f hi+    Nothing -> throwError $ NotInScope name+++------------------------------------------------------------------------------+-- | Terminal constructor for providing context-sensitive tactics. Tactics+-- given by 'provide' are always available.+provide :: TacticCommand -> T.Text -> TacticProvider+provide tc name TacticProviderData{..} = do+  let title = tacticTitle tc name+      params = TacticParams { tp_file = tpd_uri , tp_range = tpd_range , tp_var_name = name }+      cmd = mkLspCommand tpd_plid (tcCommandId tc) title (Just [toJSON params])+  pure+    $ pure+    $ InR+    $ CodeAction+        { _title       = title+        , _kind        = Just $ mkTacticKind tc+        , _diagnostics = Nothing+        , _isPreferred = Just $ tacticPreferred tc+        , _disabled    = Nothing+        , _edit        = Nothing+        , _command     = Just cmd+        , _xdata       = Nothing+        }+++------------------------------------------------------------------------------+-- | Construct a 'CommandId'+tcCommandId :: TacticCommand -> CommandId+tcCommandId c = coerce $ T.pack $ "tactics" <> show c <> "Command"+++------------------------------------------------------------------------------+-- | We should show homos only when the goal type is the same as the binding+-- type, and that both are usual algebraic types.+homoFilter :: Type -> Type -> Bool+homoFilter (algebraicTyCon -> Just t1) (algebraicTyCon -> Just t2) = t1 == t2+homoFilter _ _                                                     = False+++------------------------------------------------------------------------------+-- | We should show destruct for bindings only when those bindings have usual+-- algebraic types.+destructFilter :: Type -> Type -> Bool+destructFilter _ (algebraicTyCon -> Just _) = True+destructFilter _ _                          = False+
+ src/Wingman/Machinery.hs view
@@ -0,0 +1,300 @@+{-# LANGUAGE RecordWildCards       #-}++module Wingman.Machinery where++import           Class (Class (classTyVars))+import           Control.Lens ((<>~))+import           Control.Monad.Error.Class+import           Control.Monad.Reader+import           Control.Monad.State.Class (gets, modify)+import           Control.Monad.State.Strict (StateT (..))+import           Data.Bool (bool)+import           Data.Coerce+import           Data.Either+import           Data.Foldable+import           Data.Functor ((<&>))+import           Data.Generics (everything, gcount, mkQ)+import           Data.Generics.Product (field')+import           Data.List (sortBy)+import qualified Data.Map as M+import           Data.Maybe (mapMaybe)+import           Data.Monoid (getSum)+import           Data.Ord (Down (..), comparing)+import           Data.Set (Set)+import qualified Data.Set as S+import           Development.IDE.GHC.Compat+import           OccName (HasOccName (occName))+import           Refinery.ProofState+import           Refinery.Tactic+import           Refinery.Tactic.Internal+import           TcType+import           Type+import           Unify+import           Wingman.Judgements+import           Wingman.Simplify (simplify)+import           Wingman.Types+++substCTy :: TCvSubst -> CType -> CType+substCTy subst = coerce . substTy subst . coerce+++------------------------------------------------------------------------------+-- | Produce a subgoal that must be solved before we can solve the original+-- goal.+newSubgoal+    :: Judgement+    -> Rule+newSubgoal j = do+    unifier <- gets ts_unifier+    subgoal+      $ substJdg unifier+      $ unsetIsTopHole j+++------------------------------------------------------------------------------+-- | Attempt to generate a term of the right type using in-scope bindings, and+-- a given tactic.+runTactic+    :: Context+    -> Judgement+    -> TacticsM ()       -- ^ Tactic to use+    -> Either [TacticError] RunTacticResults+runTactic ctx jdg t =+    let skolems = S.fromList+                $ foldMap (tyCoVarsOfTypeWellScoped . unCType)+                $ (:) (jGoal jdg)+                $ fmap hi_type+                $ toList+                $ hyByName+                $ jHypothesis jdg+        tacticState =+          defaultTacticState+            { ts_skolems = skolems+            }+    in case partitionEithers+          . flip runReader ctx+          . unExtractM+          $ runTacticT t jdg tacticState of+      (errs, []) -> Left $ take 50 errs+      (_, fmap assoc23 -> solns) -> do+        let sorted =+              flip sortBy solns $ comparing $ \(ext, (_, holes)) ->+                Down $ scoreSolution ext jdg holes+        case sorted of+          ((syn, _) : _) ->+            Right $+              RunTacticResults+                { rtr_trace = syn_trace syn+                , rtr_extract = simplify $ syn_val syn+                , rtr_other_solns = reverse . fmap fst $ sorted+                , rtr_jdg = jdg+                , rtr_ctx = ctx+                }+          -- guaranteed to not be empty+          _ -> Left []++assoc23 :: (a, b, c) -> (a, (b, c))+assoc23 (a, b, c) = (a, (b, c))+++tracePrim :: String -> Trace+tracePrim = flip rose []+++------------------------------------------------------------------------------+-- | Mark that a tactic used the given string in its extract derivation. Mainly+-- used for debugging the search when things go terribly wrong.+tracing+    :: Functor m+    => String+    -> TacticT jdg (Synthesized ext) err s m a+    -> TacticT jdg (Synthesized ext) err s m a+tracing s = mappingExtract (mapTrace $ rose s . pure)+++------------------------------------------------------------------------------+-- | Mark that a tactic performed recursion. Doing so incurs a small penalty in+-- the score.+markRecursion+    :: Functor m+    => TacticT jdg (Synthesized ext) err s m a+    -> TacticT jdg (Synthesized ext) err s m a+markRecursion = mappingExtract (field' @"syn_recursion_count" <>~ 1)+++------------------------------------------------------------------------------+-- | Map a function over the extract created by a tactic.+mappingExtract+    :: Functor m+    => (ext -> ext)+    -> TacticT jdg ext err s m a+    -> TacticT jdg ext err s m a+mappingExtract f (TacticT m)+  = TacticT $ StateT $ \jdg ->+      mapExtract' f $ runStateT m jdg+++------------------------------------------------------------------------------+-- | Given the results of running a tactic, score the solutions by+-- desirability.+--+-- NOTE: This function is completely unprincipled and was just hacked together+-- to produce the right test results.+scoreSolution+    :: Synthesized (LHsExpr GhcPs)+    -> Judgement+    -> [Judgement]+    -> ( Penalize Int  -- number of holes+       , Reward Bool   -- all bindings used+       , Penalize Int  -- unused top-level bindings+       , Penalize Int  -- number of introduced bindings+       , Reward Int    -- number used bindings+       , Penalize Int  -- number of recursive calls+       , Penalize Int  -- size of extract+       )+scoreSolution ext goal holes+  = ( Penalize $ length holes+    , Reward   $ S.null $ intro_vals S.\\ used_vals+    , Penalize $ S.size unused_top_vals+    , Penalize $ S.size intro_vals+    , Reward   $ S.size used_vals + length used_user_vals+    , Penalize $ getSum $ syn_recursion_count ext+    , Penalize $ solutionSize $ syn_val ext+    )+  where+    initial_scope = hyByName $ jEntireHypothesis goal+    intro_vals = M.keysSet $ hyByName $ syn_scoped ext+    used_vals = S.intersection intro_vals $ syn_used_vals ext+    used_user_vals = filter (isLocalHypothesis . hi_provenance)+                   $ mapMaybe (flip M.lookup initial_scope)+                   $ S.toList+                   $ syn_used_vals ext+    top_vals = S.fromList+             . fmap hi_name+             . filter (isTopLevel . hi_provenance)+             . unHypothesis+             $ syn_scoped ext+    unused_top_vals = top_vals S.\\ used_vals+++------------------------------------------------------------------------------+-- | Compute the number of 'LHsExpr' nodes; used as a rough metric for code+-- size.+solutionSize :: LHsExpr GhcPs -> Int+solutionSize = everything (+) $ gcount $ mkQ False $ \case+  (_ :: LHsExpr GhcPs) -> True+++newtype Penalize a = Penalize a+  deriving (Eq, Ord, Show) via (Down a)++newtype Reward a = Reward a+  deriving (Eq, Ord, Show) via a+++------------------------------------------------------------------------------+-- | Like 'tcUnifyTy', but takes a list of skolems to prevent unification of.+tryUnifyUnivarsButNotSkolems :: Set TyVar -> CType -> CType -> Maybe TCvSubst+tryUnifyUnivarsButNotSkolems skolems goal inst =+  case tcUnifyTysFG+         (bool BindMe Skolem . flip S.member skolems)+         [unCType inst]+         [unCType goal] of+    Unifiable subst -> pure subst+    _               -> Nothing+++updateSubst :: TCvSubst -> TacticState -> TacticState+updateSubst subst s = s { ts_unifier = unionTCvSubst subst (ts_unifier s) }++++------------------------------------------------------------------------------+-- | Attempt to unify two types.+unify :: CType -- ^ The goal type+      -> CType -- ^ The type we are trying unify the goal type with+      -> RuleM ()+unify goal inst = do+  skolems <- gets ts_skolems+  case tryUnifyUnivarsButNotSkolems skolems goal inst of+    Just subst ->+      modify $ updateSubst subst+    Nothing -> throwError (UnificationError inst goal)+++------------------------------------------------------------------------------+-- | Prefer the first tactic to the second, if the bool is true. Otherwise, just run the second tactic.+--+-- This is useful when you have a clever pruning solution that isn't always+-- applicable.+attemptWhen :: TacticsM a -> TacticsM a -> Bool -> TacticsM a+attemptWhen _  t2 False = t2+attemptWhen t1 t2 True  = commit t1 t2+++------------------------------------------------------------------------------+-- | Get the class methods of a 'PredType', correctly dealing with+-- instantiation of quantified class types.+methodHypothesis :: PredType -> Maybe [HyInfo CType]+methodHypothesis ty = do+  (tc, apps) <- splitTyConApp_maybe ty+  cls <- tyConClass_maybe tc+  let methods = classMethods cls+      tvs     = classTyVars cls+      subst   = zipTvSubst tvs apps+  pure $ methods <&> \method ->+    let (_, _, ty) = tcSplitSigmaTy $ idType method+    in ( HyInfo (occName method) (ClassMethodPrv $ Uniquely cls) $ CType $ substTy subst ty+       )+++------------------------------------------------------------------------------+-- | Mystical time-traveling combinator for inspecting the extracts produced by+-- a tactic. We can use it to guard that extracts match certain predicates, for+-- example.+--+-- Note, that this thing is WEIRD. To illustrate:+--+-- @@+-- peek f+-- blah+-- @@+--+-- Here, @f@ can inspect the extract _produced by @blah@,_  which means the+-- causality appears to go backwards.+--+-- 'peek' should be exposed directly by @refinery@ in the next release.+peek :: (ext -> TacticT jdg ext err s m ()) -> TacticT jdg ext err s m ()+peek k = tactic $ \j -> Subgoal ((), j) $ \e -> proofState (k e) j+++------------------------------------------------------------------------------+-- | Run the given tactic iff the current hole contains no univars. Skolems and+-- already decided univars are OK though.+requireConcreteHole :: TacticsM a -> TacticsM a+requireConcreteHole m = do+  jdg     <- goal+  skolems <- gets ts_skolems+  let vars = S.fromList $ tyCoVarsOfTypeWellScoped $ unCType $ jGoal jdg+  case S.size $ vars S.\\ skolems of+    0 -> m+    _ -> throwError TooPolymorphic+++------------------------------------------------------------------------------+-- | The 'try' that comes in refinery 0.3 causes unnecessary backtracking and+-- balloons the search space. This thing just tries it, but doesn't backtrack+-- if it fails.+--+-- NOTE(sandy): But there's a bug! Or at least, something not understood here.+-- Using this everywhere breaks te tests, and neither I nor TOTBWF are sure+-- why.  Prefer 'try' if you can, and only try this as a last resort.+--+-- TODO(sandy): Remove this when we upgrade to 0.4+try'+    :: Functor m+    => TacticT jdg ext err s m ()+    -> TacticT jdg ext err s m ()+try' t = commit t $ pure ()+
+ src/Wingman/Naming.hs view
@@ -0,0 +1,96 @@+module Wingman.Naming where++import           Control.Monad.State.Strict+import           Data.Bool (bool)+import           Data.Char+import           Data.Map (Map)+import qualified Data.Map as M+import           Data.Set (Set)+import qualified Data.Set as S+import           Data.Traversable+import           Name+import           TcType+import           TyCon+import           Type+import           TysWiredIn (listTyCon, pairTyCon, unitTyCon)+++------------------------------------------------------------------------------+-- | Use type information to create a reasonable name.+mkTyName :: Type -> String+-- eg. mkTyName (a -> B) = "fab"+mkTyName (tcSplitFunTys -> ([a@(isFunTy -> False)], b))+  = "f" ++ mkTyName a ++ mkTyName b+-- eg. mkTyName (a -> b -> C) = "f_C"+mkTyName (tcSplitFunTys -> (_:_, b))+  = "f_" ++ mkTyName b+-- eg. mkTyName (Either A B) = "eab"+mkTyName (splitTyConApp_maybe -> Just (c, args))+  = mkTyConName c ++ foldMap mkTyName args+-- eg. mkTyName (f a) = "fa"+mkTyName (tcSplitAppTys -> (t, args@(_:_)))+  = mkTyName t ++ foldMap mkTyName args+-- eg. mkTyName a = "a"+mkTyName (getTyVar_maybe -> Just tv)+  = occNameString $ occName tv+-- eg. mkTyName (forall x. y) = "y"+mkTyName (tcSplitSigmaTy -> (_:_, _, t))+  = mkTyName t+mkTyName _ = "x"+++------------------------------------------------------------------------------+-- | Get a good name for a type constructor.+mkTyConName :: TyCon -> String+mkTyConName tc+  | tc == listTyCon = "l_"+  | tc == pairTyCon = "p_"+  | tc == unitTyCon = "unit"+  | otherwise+      = take 1+      . fmap toLower+      . filterReplace isSymbol      's'+      . filterReplace isPunctuation 'p'+      . occNameString+      $ getOccName tc+++------------------------------------------------------------------------------+-- | Maybe replace an element in the list if the predicate matches+filterReplace :: (a -> Bool) -> a -> [a] -> [a]+filterReplace f r = fmap (\a -> bool a r $ f a)+++------------------------------------------------------------------------------+-- | Produce a unique, good name for a type.+mkGoodName+    :: Set OccName  -- ^ Bindings in scope; used to ensure we don't shadow anything+    -> Type       -- ^ The type to produce a name for+    -> OccName+mkGoodName in_scope t =+  let tn = mkTyName t+   in mkVarOcc $ case S.member (mkVarOcc tn) in_scope of+        True  -> tn ++ show (length in_scope)+        False -> tn+++------------------------------------------------------------------------------+-- | Like 'mkGoodName' but creates several apart names.+mkManyGoodNames+  :: (Traversable t, Monad m)+  => Set OccName+  -> t Type+  -> m (t OccName)+mkManyGoodNames in_scope args =+  flip evalStateT in_scope $ for args $ \at -> do+    in_scope <- get+    let n = mkGoodName in_scope at+    modify $ S.insert n+    pure n+++------------------------------------------------------------------------------+-- | Which names are in scope?+getInScope :: Map OccName a -> [OccName]+getInScope = M.keys+
+ src/Wingman/Plugin.hs view
@@ -0,0 +1,216 @@+{-# LANGUAGE OverloadedStrings #-}++-- | A plugin that uses tactics to synthesize code+module Wingman.Plugin+  ( descriptor+  , tacticTitle+  , TacticCommand (..)+  ) where++import           Control.Exception (evaluate)+import           Control.Monad+import           Control.Monad.Trans+import           Control.Monad.Trans.Maybe+import           Data.Aeson+import           Data.Bifunctor (first)+import           Data.Data+import           Data.Foldable (for_)+import           Data.Maybe+import qualified Data.Text as T+import           Development.IDE.Core.Shake (IdeState (..))+import           Development.IDE.Core.UseStale (Tracked, TrackedStale(..), unTrack, mapAgeFrom, unsafeMkCurrent)+import           Development.IDE.GHC.Compat+import           Development.IDE.GHC.ExactPrint+import           Ide.Types+import           Language.LSP.Server+import           Language.LSP.Types+import           Language.LSP.Types.Capabilities+import           OccName+import           Prelude hiding (span)+import           System.Timeout+import           Wingman.CaseSplit+import           Wingman.GHC+import           Wingman.LanguageServer+import           Wingman.LanguageServer.TacticProviders+import           Wingman.Machinery (scoreSolution)+import           Wingman.Range+import           Wingman.Tactics+import           Wingman.Types+++descriptor :: PluginId -> PluginDescriptor IdeState+descriptor plId = (defaultPluginDescriptor plId)+  { pluginCommands+      = fmap (\tc ->+          PluginCommand+            (tcCommandId tc)+            (tacticDesc $ tcCommandName tc)+            (tacticCmd (commandTactic tc) plId))+            [minBound .. maxBound]+  , pluginHandlers =+      mkPluginHandler STextDocumentCodeAction codeActionProvider+  , pluginRules = wingmanRules plId+  , pluginCustomConfig =+      mkCustomConfig properties+  }+++codeActionProvider :: PluginMethodHandler IdeState TextDocumentCodeAction+codeActionProvider state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) (unsafeMkCurrent -> range) _ctx)+  | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri = do+      cfg <- getTacticConfig plId+      liftIO $ fromMaybeT (Right $ List []) $ do+        (_, jdg, _, dflags) <- judgementForHole state nfp range $ cfg_feature_set cfg+        actions <- lift $+          -- This foldMap is over the function monoid.+          foldMap commandProvider [minBound .. maxBound] $ TacticProviderData+            { tpd_dflags = dflags+            , tpd_config = cfg+            , tpd_plid   = plId+            , tpd_uri    = uri+            , tpd_range  = range+            , tpd_jdg    = jdg+            }+        pure $ Right $ List actions+codeActionProvider _ _ _ = pure $ Right $ List []+++showUserFacingMessage+    :: MonadLsp cfg m+    => UserFacingMessage+    -> m (Either ResponseError a)+showUserFacingMessage ufm = do+  showLspMessage $ mkShowMessageParams ufm+  pure $ Left $ mkErr InternalError $ T.pack $ show ufm+++tacticCmd :: (OccName -> TacticsM ()) -> PluginId -> CommandFunction IdeState TacticParams+tacticCmd tac pId state (TacticParams uri range var_name)+  | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri = do+      features <- getFeatureSet pId+      ccs <- getClientCapabilities+      cfg <- getTacticConfig pId+      res <- liftIO $ runMaybeT $ do+        (range', jdg, ctx, dflags) <- judgementForHole state nfp range features+        let span = fmap (rangeToRealSrcSpan (fromNormalizedFilePath nfp)) range'+        TrackedStale pm pmmap <- runStaleIde state nfp GetAnnotatedParsedSource+        pm_span <- liftMaybe $ mapAgeFrom pmmap span++        timingOut (cfg_timeout_seconds cfg * seconds) $ join $+          case runTactic ctx jdg $ tac $ mkVarOcc $ T.unpack var_name of+            Left _ -> Left TacticErrors+            Right rtr ->+              case rtr_extract rtr of+                L _ (HsVar _ (L _ rdr)) | isHole (occName rdr) ->+                  Left NothingToDo+                _ -> pure $ mkWorkspaceEdits pm_span dflags ccs uri pm rtr++      case res of+        Nothing -> do+          showUserFacingMessage TimedOut+        Just (Left ufm) -> do+          showUserFacingMessage ufm+        Just (Right edit) -> do+          _ <- sendRequest+            SWorkspaceApplyEdit+            (ApplyWorkspaceEditParams Nothing edit)+            (const $ pure ())+          pure $ Right Null+tacticCmd _ _ _ _ =+  pure $ Left $ mkErr InvalidRequest "Bad URI"+++------------------------------------------------------------------------------+-- | The number of microseconds in a second+seconds :: Num a => a+seconds = 1e6+++timingOut+    :: Int  -- ^ Time in microseconds+    -> a    -- ^ Computation to run+    -> MaybeT IO a+timingOut t m = MaybeT $ timeout t $ evaluate m+++mkErr :: ErrorCode -> T.Text -> ResponseError+mkErr code err = ResponseError code err Nothing+++------------------------------------------------------------------------------+-- | Turn a 'RunTacticResults' into concrete edits to make in the source+-- document.+mkWorkspaceEdits+    :: Tracked age RealSrcSpan+    -> DynFlags+    -> ClientCapabilities+    -> Uri+    -> Tracked age (Annotated ParsedSource)+    -> RunTacticResults+    -> Either UserFacingMessage WorkspaceEdit+mkWorkspaceEdits (unTrack -> span) dflags ccs uri (unTrack -> pm) rtr = do+  for_ (rtr_other_solns rtr) $ \soln -> do+    traceMX "other solution" $ syn_val soln+    traceMX "with score" $ scoreSolution soln (rtr_jdg rtr) []+  traceMX "solution" $ rtr_extract rtr+  let g = graftHole (RealSrcSpan span) rtr+      response = transform dflags ccs uri g pm+   in first (InfrastructureError . T.pack) response+++------------------------------------------------------------------------------+-- | Graft a 'RunTacticResults' into the correct place in an AST. Correctly+-- deals with top-level holes, in which we might need to fiddle with the+-- 'Match's that bind variables.+graftHole+    :: SrcSpan+    -> RunTacticResults+    -> Graft (Either String) ParsedSource+graftHole span rtr+  | _jIsTopHole (rtr_jdg rtr)+      = genericGraftWithSmallestM (Proxy @(Located [LMatch GhcPs (LHsExpr GhcPs)])) span $ \dflags ->+        everywhereM'+          $ mkBindListT $ \ix ->+            graftDecl dflags span ix $ \name pats ->+            splitToDecl (occName name)+          $ iterateSplit+          $ mkFirstAgda (fmap unXPat pats)+          $ unLoc+          $ rtr_extract rtr+graftHole span rtr+  = graft span+  $ rtr_extract rtr+++------------------------------------------------------------------------------+-- | Helper function to route 'mergeFunBindMatches' into the right place in an+-- AST --- correctly dealing with inserting into instance declarations.+graftDecl+    :: DynFlags+    -> SrcSpan+    -> Int+    -> (RdrName -> [Pat GhcPs] -> LHsDecl GhcPs)+    -> LMatch GhcPs (LHsExpr GhcPs)+    -> TransformT (Either String) [LMatch GhcPs (LHsExpr GhcPs)]+graftDecl dflags dst ix make_decl (L src (AMatch (FunRhs (L _ name) _ _) pats _))+  | dst `isSubspanOf` src = do+      L _ dec <- annotateDecl dflags $ make_decl name pats+      case dec of+        ValD _ (FunBind { fun_matches = MG { mg_alts = L _ alts@(first_match : _)}+                  }) -> do+          -- For whatever reason, ExactPrint annotates newlines to the ends of+          -- case matches and type signatures, but only allows us to insert+          -- them at the beginning of those things. Thus, we need want to+          -- insert a preceeding newline (done in 'annotateDecl') on all+          -- matches, except for the first one --- since it gets its newline+          -- from the line above.+          when (ix == 0) $+            setPrecedingLinesT first_match 0 0+          pure alts+        _ -> lift $ Left "annotateDecl didn't produce a funbind"+graftDecl _ _ _ _ x = pure $ pure x+++fromMaybeT :: Functor m => a -> MaybeT m a -> m a+fromMaybeT def = fmap (fromMaybe def) . runMaybeT+
+ src/Wingman/Range.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE RankNTypes     #-}++module Wingman.Range where++import           Development.IDE hiding (rangeToRealSrcSpan, rangeToSrcSpan)+import qualified FastString as FS+import           SrcLoc+++------------------------------------------------------------------------------+-- | Convert a DAML compiler Range to a GHC SrcSpan+-- TODO(sandy): this doesn't belong here+rangeToSrcSpan :: String -> Range -> SrcSpan+rangeToSrcSpan file range = RealSrcSpan $ rangeToRealSrcSpan file range+++rangeToRealSrcSpan :: String -> Range -> RealSrcSpan+rangeToRealSrcSpan file (Range (Position startLn startCh) (Position endLn endCh)) =+  mkRealSrcSpan+    (mkRealSrcLoc (FS.fsLit file) (startLn + 1) (startCh + 1))+    (mkRealSrcLoc (FS.fsLit file) (endLn + 1) (endCh + 1))+
+ src/Wingman/Simplify.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE OverloadedStrings #-}++module Wingman.Simplify+  ( simplify+  ) where++import Data.Generics (GenericT, everywhere, mkT)+import Data.List.Extra (unsnoc)+import Data.Monoid (Endo (..))+import Development.IDE.GHC.Compat+import GHC.SourceGen (var)+import GHC.SourceGen.Expr (lambda)+import Wingman.CodeGen.Utils+import Wingman.GHC (containsHsVar, fromPatCompat)+++------------------------------------------------------------------------------+-- | A pattern over the otherwise (extremely) messy AST for lambdas.+pattern Lambda :: [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs+pattern Lambda pats body <-+  HsLam _+    (MG {mg_alts = L _ [L _+      (Match { m_pats = fmap fromPatCompat -> pats+             , m_grhss = GRHSs {grhssGRHSs = [L _ (+                 GRHS _ [] (L _ body))]}+             })]})+  where+    -- If there are no patterns to bind, just stick in the body+    Lambda [] body   = body+    Lambda pats body = lambda pats body+++------------------------------------------------------------------------------+-- | Simlify an expression.+simplify :: LHsExpr GhcPs -> LHsExpr GhcPs+simplify+  = head+  . drop 3   -- Do three passes; this should be good enough for the limited+             -- amount of gas we give to auto+  . iterate (everywhere $ foldEndo+    [ simplifyEtaReduce+    , simplifyRemoveParens+    , simplifyCompose+    ])+++------------------------------------------------------------------------------+-- | Like 'foldMap' but for endomorphisms.+foldEndo :: Foldable t => t (a -> a) -> a -> a+foldEndo = appEndo . foldMap Endo+++------------------------------------------------------------------------------+-- | Perform an eta reduction. For example, transforms @\x -> (f g) x@ into+-- @f g@.+simplifyEtaReduce :: GenericT+simplifyEtaReduce = mkT $ \case+  Lambda+      [VarPat _ (L _ pat)]+      (HsVar _ (L _ a)) | pat == a ->+    var "id"+  Lambda+      (unsnoc -> Just (pats, (VarPat _ (L _ pat))))+      (HsApp _ (L _ f) (L _ (HsVar _ (L _ a))))+      | pat == a+        -- We can only perform this simplifiation if @pat@ is otherwise unused.+      , not (containsHsVar pat f) ->+    Lambda pats f+  x -> x+++------------------------------------------------------------------------------+-- | Perform an eta-reducing function composition. For example, transforms+-- @\x -> f (g (h x))@ into @f . g . h@.+simplifyCompose :: GenericT+simplifyCompose = mkT $ \case+  Lambda+      (unsnoc -> Just (pats, (VarPat _ (L _ pat))))+      (unroll -> (fs@(_:_), (HsVar _ (L _ a))))+      | pat == a+        -- We can only perform this simplifiation if @pat@ is otherwise unused.+      , not (containsHsVar pat fs) ->+    Lambda pats (foldr1 (infixCall ".") fs)+  x -> x+++------------------------------------------------------------------------------+-- | Removes unnecessary parentheses on any token that doesn't need them.+simplifyRemoveParens :: GenericT+simplifyRemoveParens = mkT $ \case+  HsPar _ (L _ x) | isAtomicHsExpr x -> x+  (x :: HsExpr GhcPs)                -> x+++------------------------------------------------------------------------------+-- | Unrolls a right-associative function application of the form+-- @HsApp f (HsApp g (HsApp h x))@ into @([f, g, h], x)@.+unroll :: HsExpr GhcPs -> ([HsExpr GhcPs], HsExpr GhcPs)+unroll (HsPar _ (L _ x)) = unroll x+unroll (HsApp _ (L _ f) (L _ a)) =+  let (fs, r) = unroll a+   in (f : fs, r)+unroll x = ([], x)+
+ src/Wingman/Tactics.hs view
@@ -0,0 +1,397 @@+module Wingman.Tactics+  ( module Wingman.Tactics+  , runTactic+  ) where++import           ConLike (ConLike(RealDataCon))+import           Control.Applicative (Alternative(empty))+import           Control.Lens ((&), (%~), (<>~))+import           Control.Monad (unless)+import           Control.Monad.Except (throwError)+import           Control.Monad.Reader.Class (MonadReader (ask))+import           Control.Monad.State.Strict (StateT(..), runStateT)+import           Data.Foldable+import           Data.Functor ((<&>))+import           Data.Generics.Labels ()+import           Data.List+import qualified Data.Map as M+import           Data.Maybe+import           Data.Set (Set)+import qualified Data.Set as S+import           DataCon+import           Development.IDE.GHC.Compat+import           GHC.Exts+import           GHC.SourceGen.Expr+import           Name (occNameString, occName)+import           Refinery.Tactic+import           Refinery.Tactic.Internal+import           TcType+import           Type hiding (Var)+import           Wingman.CodeGen+import           Wingman.Context+import           Wingman.GHC+import           Wingman.Judgements+import           Wingman.Machinery+import           Wingman.Naming+import           Wingman.Types+++------------------------------------------------------------------------------+-- | Use something in the hypothesis to fill the hole.+assumption :: TacticsM ()+assumption = attemptOn (S.toList . allNames) assume+++------------------------------------------------------------------------------+-- | Use something named in the hypothesis to fill the hole.+assume :: OccName -> TacticsM ()+assume name = rule $ \jdg -> do+  case M.lookup name $ hyByName $ jHypothesis jdg of+    Just (hi_type -> ty) -> do+      unify ty $ jGoal jdg+      pure $+        -- This slightly terrible construct is producing a mostly-empty+        -- 'Synthesized'; but there is no monoid instance to do something more+        -- reasonable for a default value.+        (pure (noLoc $ var' name))+          { syn_trace = tracePrim $ "assume " <> occNameString name+          , syn_used_vals = S.singleton name+          }+    Nothing -> throwError $ UndefinedHypothesis name+++recursion :: TacticsM ()+-- TODO(sandy): This tactic doesn't fire for the @AutoThetaFix@ golden test,+-- presumably due to running afoul of 'requireConcreteHole'. Look into this!+recursion = requireConcreteHole $ tracing "recursion" $ do+  defs <- getCurrentDefinitions+  attemptOn (const defs) $ \(name, ty) -> markRecursion $ do+    -- Peek allows us to look at the extract produced by this block.+    peek $ \ext -> do+      jdg <- goal+      let pat_vals = jPatHypothesis jdg+      -- Make sure that the recursive call contains at least one already-bound+      -- pattern value. This ensures it is structurally smaller, and thus+      -- suggests termination.+      unless (any (flip M.member pat_vals) $ syn_used_vals ext) empty++    let hy' = recursiveHypothesis defs+    localTactic (apply $ HyInfo name RecursivePrv ty) (introduce hy')+      <@> fmap (localTactic assumption . filterPosition name) [0..]+++restrictPositionForApplication :: TacticsM () -> TacticsM () -> TacticsM ()+restrictPositionForApplication f app = do+  -- NOTE(sandy): Safe use of head; context is guaranteed to have a defining+  -- binding+  name <- head . fmap fst <$> getCurrentDefinitions+  f <@>+    fmap+      (localTactic app . filterPosition name) [0..]+++------------------------------------------------------------------------------+-- | Introduce a lambda binding every variable.+intros :: TacticsM ()+intros = rule $ \jdg -> do+  let g  = jGoal jdg+  ctx <- ask+  case tcSplitFunTys $ unCType g of+    ([], _) -> throwError $ GoalMismatch "intros" g+    (as, b) -> do+      vs <- mkManyGoodNames (hyNamesInScope $ jEntireHypothesis jdg) as+      let top_hole = isTopHole ctx jdg+          hy' = lambdaHypothesis top_hole $ zip vs $ coerce as+          jdg' = introduce hy'+               $ withNewGoal (CType b) jdg+      ext <- newSubgoal jdg'+      pure $+        ext+          & #syn_trace %~ rose ("intros {" <> intercalate ", " (fmap show vs) <> "}")+                        . pure+          & #syn_scoped <>~ hy'+          & #syn_val   %~ noLoc . lambda (fmap bvar' vs) . unLoc+++------------------------------------------------------------------------------+-- | Case split, and leave holes in the matches.+destructAuto :: HyInfo CType -> TacticsM ()+destructAuto hi = requireConcreteHole $ tracing "destruct(auto)" $ do+  jdg <- goal+  let subtactic = destructOrHomoAuto hi+  case isPatternMatch $ hi_provenance hi of+    True ->+      pruning subtactic $ \jdgs ->+        let getHyTypes = S.fromList . fmap hi_type . unHypothesis . jHypothesis+            new_hy = foldMap getHyTypes jdgs+            old_hy = getHyTypes jdg+        in case S.null $ new_hy S.\\ old_hy of+              True  -> Just $ UnhelpfulDestruct $ hi_name hi+              False -> Nothing+    False -> subtactic+++------------------------------------------------------------------------------+-- | When running auto, in order to prune the auto search tree, we try+-- a homomorphic destruct whenever possible. If that produces any results, we+-- can probably just prune the other side.+destructOrHomoAuto :: HyInfo CType -> TacticsM ()+destructOrHomoAuto hi = tracing "destructOrHomoAuto" $ do+  jdg <- goal+  let g  = unCType $ jGoal jdg+      ty = unCType $ hi_type hi++  attemptWhen+      (rule $ destruct' (\dc jdg ->+        buildDataCon False jdg dc $ snd $ splitAppTys g) hi)+      (rule $ destruct' (const subgoal) hi)+    $ case (splitTyConApp_maybe g, splitTyConApp_maybe ty) of+        (Just (gtc, _), Just (tytc, _)) -> gtc == tytc+        _ -> False+++------------------------------------------------------------------------------+-- | Case split, and leave holes in the matches.+destruct :: HyInfo CType -> TacticsM ()+destruct hi = requireConcreteHole $ tracing "destruct(user)" $+  rule $ destruct' (const subgoal) hi+++------------------------------------------------------------------------------+-- | Case split, using the same data constructor in the matches.+homo :: HyInfo CType -> TacticsM ()+homo = requireConcreteHole . tracing "homo" . rule . destruct' (\dc jdg ->+  buildDataCon False jdg dc $ snd $ splitAppTys $ unCType $ jGoal jdg)+++------------------------------------------------------------------------------+-- | LambdaCase split, and leave holes in the matches.+destructLambdaCase :: TacticsM ()+destructLambdaCase = tracing "destructLambdaCase" $ rule $ destructLambdaCase' (const subgoal)+++------------------------------------------------------------------------------+-- | LambdaCase split, using the same data constructor in the matches.+homoLambdaCase :: TacticsM ()+homoLambdaCase =+  tracing "homoLambdaCase" $+    rule $ destructLambdaCase' $ \dc jdg ->+      buildDataCon False jdg dc+        . snd+        . splitAppTys+        . unCType+        $ jGoal jdg+++apply :: HyInfo CType -> TacticsM ()+apply hi = requireConcreteHole $ tracing ("apply' " <> show (hi_name hi)) $ do+  jdg <- goal+  let g  = jGoal jdg+      ty = unCType $ hi_type hi+      func = hi_name hi+  ty' <- freshTyvars ty+  let (_, _, args, ret) = tacticsSplitFunTy ty'+  rule $ \jdg -> do+    unify g (CType ret)+    ext+        <- fmap unzipTrace+        $ traverse ( newSubgoal+                    . blacklistingDestruct+                    . flip withNewGoal jdg+                    . CType+                    ) args+    pure $+      ext+        & #syn_used_vals %~ S.insert func+        & #syn_val       %~ mkApply func . fmap unLoc+++------------------------------------------------------------------------------+-- | Choose between each of the goal's data constructors.+split :: TacticsM ()+split = tracing "split(user)" $ do+  jdg <- goal+  let g = jGoal jdg+  case tacticsGetDataCons $ unCType g of+    Nothing -> throwError $ GoalMismatch "split" g+    Just (dcs, _) -> choice $ fmap splitDataCon dcs+++------------------------------------------------------------------------------+-- | Choose between each of the goal's data constructors. Different than+-- 'split' because it won't split a data con if it doesn't result in any new+-- goals.+splitAuto :: TacticsM ()+splitAuto = requireConcreteHole $ tracing "split(auto)" $ do+  jdg <- goal+  let g = jGoal jdg+  case tacticsGetDataCons $ unCType g of+    Nothing -> throwError $ GoalMismatch "split" g+    Just (dcs, _) -> do+      case isSplitWhitelisted jdg of+        True -> choice $ fmap splitDataCon dcs+        False -> do+          choice $ flip fmap dcs $ \dc -> requireNewHoles $+            splitDataCon dc+++------------------------------------------------------------------------------+-- | Like 'split', but only works if there is a single matching data+-- constructor for the goal.+splitSingle :: TacticsM ()+splitSingle = tracing "splitSingle" $ do+  jdg <- goal+  let g = jGoal jdg+  case tacticsGetDataCons $ unCType g of+    Just ([dc], _) -> do+      splitDataCon dc+    _ -> throwError $ GoalMismatch "splitSingle" g+++------------------------------------------------------------------------------+-- | Allow the given tactic to proceed if and only if it introduces holes that+-- have a different goal than current goal.+requireNewHoles :: TacticsM () -> TacticsM ()+requireNewHoles m = do+  jdg <- goal+  pruning m $ \jdgs ->+    case null jdgs || any (/= jGoal jdg) (fmap jGoal jdgs) of+      True  -> Nothing+      False -> Just NoProgress+++------------------------------------------------------------------------------+-- | Attempt to instantiate the given ConLike to solve the goal.+--+-- INVARIANT: Assumes the given ConLike is appropriate to construct the type+-- with.+splitConLike :: ConLike -> TacticsM ()+splitConLike dc =+  requireConcreteHole $ tracing ("splitDataCon:" <> show dc) $ rule $ \jdg -> do+    let g = jGoal jdg+    case splitTyConApp_maybe $ unCType g of+      Just (_, apps) -> do+        buildDataCon True (unwhitelistingSplit jdg) dc apps+      Nothing -> throwError $ GoalMismatch "splitDataCon" g++------------------------------------------------------------------------------+-- | Attempt to instantiate the given data constructor to solve the goal.+--+-- INVARIANT: Assumes the given datacon is appropriate to construct the type+-- with.+splitDataCon :: DataCon -> TacticsM ()+splitDataCon = splitConLike . RealDataCon+++------------------------------------------------------------------------------+-- | Perform a case split on each top-level argument. Used to implement the+-- "Destruct all function arguments" action.+destructAll :: TacticsM ()+destructAll = do+  jdg <- goal+  let args = fmap fst+           $ sortOn (Down . snd)+           $ mapMaybe (\(hi, prov) ->+              case prov of+                TopLevelArgPrv _ idx _ -> pure (hi, idx)+                _ -> Nothing+                )+           $ fmap (\hi -> (hi, hi_provenance hi))+           $ filter (isAlgType . unCType . hi_type)+           $ unHypothesis+           $ jHypothesis jdg+  for_ args destruct++--------------------------------------------------------------------------------+-- | User-facing tactic to implement "Use constructor <x>"+userSplit :: OccName -> TacticsM ()+userSplit occ = do+  jdg <- goal+  let g = jGoal jdg+  -- TODO(sandy): It's smelly that we need to find the datacon to generate the+  -- code action, send it as a string, and then look it up again. Can we push+  -- this over LSP somehow instead?+  case splitTyConApp_maybe $ unCType g of+    Just (tc, _) -> do+      case find (sloppyEqOccName occ . occName . dataConName)+             $ tyConDataCons tc of+        Just dc -> splitDataCon dc+        Nothing -> throwError $ NotInScope occ+    Nothing -> throwError $ NotInScope occ+++------------------------------------------------------------------------------+-- | @matching f@ takes a function from a judgement to a @Tactic@, and+-- then applies the resulting @Tactic@.+matching :: (Judgement -> TacticsM ()) -> TacticsM ()+matching f = TacticT $ StateT $ \s -> runStateT (unTacticT $ f s) s+++attemptOn :: (Judgement -> [a]) -> (a -> TacticsM ()) -> TacticsM ()+attemptOn getNames tac = matching (choice . fmap (\s -> tac s) . getNames)+++localTactic :: TacticsM a -> (Judgement -> Judgement) -> TacticsM a+localTactic t f = do+  TacticT $ StateT $ \jdg ->+    runStateT (unTacticT t) $ f jdg+++refine :: TacticsM ()+refine = do+  try' intros+  try' splitSingle+  try' intros+++auto' :: Int -> TacticsM ()+auto' 0 = throwError NoProgress+auto' n = do+  let loop = auto' (n - 1)+  try intros+  choice+    [ overFunctions $ \fname -> do+        apply fname+        loop+    , overAlgebraicTerms $ \aname -> do+        destructAuto aname+        loop+    , splitAuto >> loop+    , assumption >> loop+    , recursion+    ]++overFunctions :: (HyInfo CType -> TacticsM ()) -> TacticsM ()+overFunctions =+  attemptOn $ filter (isFunction . unCType . hi_type)+           . unHypothesis+           . jHypothesis++overAlgebraicTerms :: (HyInfo CType -> TacticsM ()) -> TacticsM ()+overAlgebraicTerms =+  attemptOn jAcceptableDestructTargets+++allNames :: Judgement -> Set OccName+allNames = hyNamesInScope . jHypothesis+++applyMethod :: Class -> PredType -> OccName -> TacticsM ()+applyMethod cls df method_name = do+  case find ((== method_name) . occName) $ classMethods cls of+    Just method -> do+      let (_, apps) = splitAppTys df+      let ty = piResultTys (idType method) apps+      apply $ HyInfo method_name (ClassMethodPrv $ Uniquely cls) $ CType ty+    Nothing -> throwError $ NotInScope method_name+++applyByName :: OccName -> TacticsM ()+applyByName name = do+  g <- goal+  choice $ (unHypothesis (jHypothesis g)) <&> \hi ->+    case hi_name hi == name of+      True  -> apply hi+      False -> empty++
+ src/Wingman/Types.hs view
@@ -0,0 +1,489 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++module Wingman.Types+  ( module Wingman.Types+  , module Wingman.Debug+  , OccName+  , Name+  , Type+  , TyVar+  , Span+  ) where++import           ConLike (ConLike)+import           Control.Lens hiding (Context)+import           Control.Monad.Reader+import           Control.Monad.State+import           Data.Coerce+import           Data.Function+import           Data.Generics.Product (field)+import           Data.List.NonEmpty (NonEmpty (..))+import           Data.Semigroup+import           Data.Set (Set)+import           Data.Text (Text)+import qualified Data.Text as T+import           Data.Tree+import           Development.IDE.GHC.Compat hiding (Node)+import           Development.IDE.GHC.Orphans ()+import           GHC.Generics+import           GHC.SourceGen (var)+import           InstEnv (InstEnvs(..))+import           OccName+import           Refinery.Tactic+import           System.IO.Unsafe (unsafePerformIO)+import           Type (TCvSubst, Var, eqType, nonDetCmpType, emptyTCvSubst)+import           UniqSupply (takeUniqFromSupply, mkSplitUniqSupply, UniqSupply)+import           Unique (nonDetCmpUnique, Uniquable, getUnique, Unique)+import           Wingman.Debug+import           Wingman.FeatureSet+++------------------------------------------------------------------------------+-- | The list of tactics exposed to the outside world. These are attached to+-- actual tactics via 'commandTactic' and are contextually provided to the+-- editor via 'commandProvider'.+data TacticCommand+  = Auto+  | Intros+  | Destruct+  | Homomorphism+  | DestructLambdaCase+  | HomomorphismLambdaCase+  | DestructAll+  | UseDataCon+  | Refine+  deriving (Eq, Ord, Show, Enum, Bounded)++-- | Generate a title for the command.+tacticTitle :: TacticCommand -> T.Text -> T.Text+tacticTitle = (mappend "Wingman: " .) . go+  where+    go Auto _                   = "Attempt to fill hole"+    go Intros _                 = "Introduce lambda"+    go Destruct var             = "Case split on " <> var+    go Homomorphism var         = "Homomorphic case split on " <> var+    go DestructLambdaCase _     = "Lambda case split"+    go HomomorphismLambdaCase _ = "Homomorphic lambda case split"+    go DestructAll _            = "Split all function arguments"+    go UseDataCon dcon          = "Use constructor " <> dcon+    go Refine _                 = "Refine hole"+++------------------------------------------------------------------------------+-- | Plugin configuration for tactics+data Config = Config+  { cfg_feature_set          :: FeatureSet+  , cfg_max_use_ctor_actions :: Int+  , cfg_timeout_seconds      :: Int+  }++------------------------------------------------------------------------------+-- | A wrapper around 'Type' which supports equality and ordering.+newtype CType = CType { unCType :: Type }++instance Eq CType where+  (==) = eqType `on` unCType++instance Ord CType where+  compare = nonDetCmpType `on` unCType++instance Show CType where+  show  = unsafeRender . unCType++instance Show OccName where+  show  = unsafeRender++instance Show Name where+  show  = unsafeRender++instance Show Type where+  show  = unsafeRender++instance Show Var where+  show  = unsafeRender++instance Show TCvSubst where+  show  = unsafeRender++instance Show DataCon where+  show  = unsafeRender++instance Show Class where+  show  = unsafeRender++instance Show (HsExpr GhcPs) where+  show  = unsafeRender++instance Show (HsDecl GhcPs) where+  show  = unsafeRender++instance Show (Pat GhcPs) where+  show  = unsafeRender++instance Show (LHsSigType GhcPs) where+  show  = unsafeRender++instance Show TyCon where+  show  = unsafeRender++instance Show ConLike where+  show  = unsafeRender+++------------------------------------------------------------------------------+-- | The state that should be shared between subgoals. Extracts move towards+-- the root, judgments move towards the leaves, and the state moves *sideways*.+data TacticState = TacticState+    { ts_skolems         :: !(Set TyVar)+      -- ^ The known skolems.+    , ts_unifier         :: !TCvSubst+    , ts_unique_gen :: !UniqSupply+    } deriving stock (Show, Generic)++instance Show UniqSupply where+  show _ = "<uniqsupply>"+++------------------------------------------------------------------------------+-- | A 'UniqSupply' to use in 'defaultTacticState'+unsafeDefaultUniqueSupply :: UniqSupply+unsafeDefaultUniqueSupply =+  unsafePerformIO $ mkSplitUniqSupply '🚒'+{-# NOINLINE unsafeDefaultUniqueSupply #-}+++defaultTacticState :: TacticState+defaultTacticState =+  TacticState+    { ts_skolems         = mempty+    , ts_unifier         = emptyTCvSubst+    , ts_unique_gen      = unsafeDefaultUniqueSupply+    }+++------------------------------------------------------------------------------+-- | Generate a new 'Unique'+freshUnique :: MonadState TacticState m => m Unique+freshUnique = do+  (uniq, supply) <- gets $ takeUniqFromSupply . ts_unique_gen+  modify' $! field @"ts_unique_gen" .~ supply+  pure uniq+++------------------------------------------------------------------------------+-- | Describes where hypotheses came from. Used extensively to prune stupid+-- solutions from the search space.+data Provenance+  = -- | An argument given to the topmost function that contains the current+    -- hole. Recursive calls are restricted to values whose provenance lines up+    -- with the same argument.+    TopLevelArgPrv+      OccName   -- ^ Binding function+      Int       -- ^ Argument Position+      Int       -- ^ of how many arguments total?+    -- | A binding created in a pattern match.+  | PatternMatchPrv PatVal+    -- | A class method from the given context.+  | ClassMethodPrv+      (Uniquely Class)     -- ^ Class+    -- | A binding explicitly written by the user.+  | UserPrv+    -- | The recursive hypothesis. Present only in the context of the recursion+    -- tactic.+  | RecursivePrv+    -- | A hypothesis which has been disallowed for some reason. It's important+    -- to keep these in the hypothesis set, rather than filtering it, in order+    -- to continue tracking downstream provenance.+  | DisallowedPrv DisallowReason Provenance+  deriving stock (Eq, Show, Generic, Ord)+++------------------------------------------------------------------------------+-- | Why was a hypothesis disallowed?+data DisallowReason+  = WrongBranch Int+  | Shadowed+  | RecursiveCall+  | AlreadyDestructed+  deriving stock (Eq, Show, Generic, Ord)+++------------------------------------------------------------------------------+-- | Provenance of a pattern value.+data PatVal = PatVal+  { pv_scrutinee :: Maybe OccName+    -- ^ Original scrutinee which created this PatVal. Nothing, for lambda+    -- case.+  , pv_ancestry  :: Set OccName+    -- ^ The set of values which had to be destructed to discover this term.+    -- Always contains the scrutinee.+  , pv_datacon   :: Uniquely ConLike+    -- ^ The datacon which introduced this term.+  , pv_position  :: Int+    -- ^ The position of this binding in the datacon's arguments.+  } deriving stock (Eq, Show, Generic, Ord)+++------------------------------------------------------------------------------+-- | A wrapper which uses a 'Uniquable' constraint for providing 'Eq' and 'Ord'+-- instances.+newtype Uniquely a = Uniquely { getViaUnique :: a }+  deriving Show via a++instance Uniquable a => Eq (Uniquely a) where+  (==) = (==) `on` getUnique . getViaUnique++instance Uniquable a => Ord (Uniquely a) where+  compare = nonDetCmpUnique `on` getUnique . getViaUnique+++-- NOTE(sandy): The usage of list here is mostly for convenience, but if it's+-- ever changed, make sure to correspondingly update+-- 'jAcceptableDestructTargets' so that it correctly identifies newly+-- introduced terms.+newtype Hypothesis a = Hypothesis+  { unHypothesis :: [HyInfo a]+  }+  deriving stock (Functor, Eq, Show, Generic, Ord)+  deriving newtype (Semigroup, Monoid)+++------------------------------------------------------------------------------+-- | The provenance and type of a hypothesis term.+data HyInfo a = HyInfo+  { hi_name       :: OccName+  , hi_provenance :: Provenance+  , hi_type       :: a+  }+  deriving stock (Functor, Eq, Show, Generic, Ord)+++------------------------------------------------------------------------------+-- | Map a function over the provenance.+overProvenance :: (Provenance -> Provenance) -> HyInfo a -> HyInfo a+overProvenance f (HyInfo name prv ty) = HyInfo name (f prv) ty+++------------------------------------------------------------------------------+-- | The current bindings and goal for a hole to be filled by refinery.+data Judgement' a = Judgement+  { _jHypothesis        :: !(Hypothesis a)+  , _jBlacklistDestruct :: !Bool+  , _jWhitelistSplit    :: !Bool+  , _jIsTopHole         :: !Bool+  , _jGoal              :: !a+  }+  deriving stock (Eq, Generic, Functor, Show)++type Judgement = Judgement' CType+++newtype ExtractM a = ExtractM { unExtractM :: Reader Context a }+    deriving newtype (Functor, Applicative, Monad, MonadReader Context)++------------------------------------------------------------------------------+-- | Orphan instance for producing holes when attempting to solve tactics.+instance MonadExtract (Synthesized (LHsExpr GhcPs)) ExtractM where+  hole = pure . pure . noLoc $ var "_"+++------------------------------------------------------------------------------+-- | Reasons a tactic might fail.+data TacticError+  = UndefinedHypothesis OccName+  | GoalMismatch String CType+  | UnsolvedSubgoals [Judgement]+  | UnificationError CType CType+  | NoProgress+  | NoApplicableTactic+  | IncorrectDataCon DataCon+  | RecursionOnWrongParam OccName Int OccName+  | UnhelpfulDestruct OccName+  | UnhelpfulSplit OccName+  | TooPolymorphic+  | NotInScope OccName+  deriving stock (Eq)++instance Show TacticError where+    show (UndefinedHypothesis name) =+      occNameString name <> " is not available in the hypothesis."+    show (GoalMismatch tac (CType typ)) =+      mconcat+        [ "The tactic "+        , tac+        , " doesn't apply to goal type "+        , unsafeRender typ+        ]+    show (UnsolvedSubgoals _) =+      "There were unsolved subgoals"+    show (UnificationError (CType t1) (CType t2)) =+        mconcat+          [ "Could not unify "+          , unsafeRender t1+          , " and "+          , unsafeRender t2+          ]+    show NoProgress =+      "Unable to make progress"+    show NoApplicableTactic =+      "No tactic could be applied"+    show (IncorrectDataCon dcon) =+      "Data con doesn't align with goal type (" <> unsafeRender dcon <> ")"+    show (RecursionOnWrongParam call p arg) =+      "Recursion on wrong param (" <> show call <> ") on arg"+        <> show p <> ": " <> show arg+    show (UnhelpfulDestruct n) =+      "Destructing patval " <> show n <> " leads to no new types"+    show (UnhelpfulSplit n) =+      "Splitting constructor " <> show n <> " leads to no new goals"+    show TooPolymorphic =+      "The tactic isn't applicable because the goal is too polymorphic"+    show (NotInScope name) =+      "Tried to do something with the out of scope name " <> show name+++------------------------------------------------------------------------------+type TacticsM  = TacticT Judgement (Synthesized (LHsExpr GhcPs)) TacticError TacticState ExtractM+type RuleM     = RuleT Judgement (Synthesized (LHsExpr GhcPs)) TacticError TacticState ExtractM+type Rule      = RuleM (Synthesized (LHsExpr GhcPs))++type Trace = Rose String++------------------------------------------------------------------------------+-- | The extract for refinery. Represents a "synthesized attribute" in the+-- context of attribute grammars. In essence, 'Synthesized' describes+-- information we'd like to pass from leaves of the tactics search upwards.+-- This includes the actual AST we've generated (in 'syn_val').+data Synthesized a = Synthesized+  { syn_trace  :: Trace+    -- ^ A tree describing which tactics were used produce the 'syn_val'.+    -- Mainly for debugging when you get the wrong answer, to see the other+    -- things it tried.+  , syn_scoped :: Hypothesis CType+    -- ^ All of the bindings created to produce the 'syn_val'.+  , syn_used_vals :: Set OccName+    -- ^ The values used when synthesizing the 'syn_val'.+  , syn_recursion_count :: Sum Int+    -- ^ The number of recursive calls+  , syn_val    :: a+  }+  deriving (Eq, Show, Functor, Foldable, Traversable, Generic)++mapTrace :: (Trace -> Trace) -> Synthesized a -> Synthesized a+mapTrace f (Synthesized tr sc uv rc a) = Synthesized (f tr) sc uv rc a+++------------------------------------------------------------------------------+-- | This might not be lawful, due to the semigroup on 'Trace' maybe not being+-- lawful. But that's only for debug output, so it's not anything I'm concerned+-- about.+instance Applicative Synthesized where+  pure = Synthesized mempty mempty mempty mempty+  Synthesized tr1 sc1 uv1 rc1 f <*> Synthesized tr2 sc2 uv2 rc2 a =+    Synthesized (tr1 <> tr2) (sc1 <> sc2) (uv1 <> uv2) (rc1 <> rc2) $ f a+++------------------------------------------------------------------------------+-- | The Reader context of tactics and rules+data Context = Context+  { ctxDefiningFuncs :: [(OccName, CType)]+    -- ^ The functions currently being defined+  , ctxModuleFuncs   :: [(OccName, CType)]+    -- ^ Everything defined in the current module+  , ctxFeatureSet    :: FeatureSet+  , ctxKnownThings   :: KnownThings+  , ctxInstEnvs      :: InstEnvs+  , ctxTheta         :: Set CType+  }++instance Show Context where+  show (Context {..}) = mconcat+    [ "Context "+    , showsPrec 10 ctxDefiningFuncs ""+    , showsPrec 10 ctxModuleFuncs ""+    , showsPrec 10 ctxFeatureSet ""+    , showsPrec 10 ctxTheta ""+    ]+++------------------------------------------------------------------------------+-- | Things we'd like to look up, that don't exist in TysWiredIn.+data KnownThings = KnownThings+  { kt_semigroup :: Class+  , kt_monoid    :: Class+  }+++------------------------------------------------------------------------------+-- | An empty context+emptyContext :: Context+emptyContext+  = Context+      { ctxDefiningFuncs = mempty+      , ctxModuleFuncs = mempty+      , ctxFeatureSet = mempty+      , ctxKnownThings = error "empty known things from emptyContext"+      , ctxInstEnvs = InstEnvs mempty mempty mempty+      , ctxTheta = mempty+      }+++newtype Rose a = Rose (Tree a)+  deriving stock (Eq, Functor, Generic)++instance Show (Rose String) where+  show = unlines . dropEveryOther . lines . drawTree . coerce++dropEveryOther :: [a] -> [a]+dropEveryOther []           = []+dropEveryOther [a]          = [a]+dropEveryOther (a : _ : as) = a : dropEveryOther as++------------------------------------------------------------------------------+-- | This might not be lawful! I didn't check, and it feels sketchy.+instance (Eq a, Monoid a) => Semigroup (Rose a) where+  Rose (Node a as) <> Rose (Node b bs) = Rose $ Node (a <> b) (as <> bs)+  sconcat (a :| as) = rose mempty $ a : as++instance (Eq a, Monoid a) => Monoid (Rose a) where+  mempty = Rose $ Node mempty mempty++rose :: (Eq a, Monoid a) => a -> [Rose a] -> Rose a+rose a [Rose (Node a' rs)] | a' == mempty = Rose $ Node a rs+rose a rs = Rose $ Node a $ coerce rs+++------------------------------------------------------------------------------+-- | The results of 'Wingman.Machinery.runTactic'+data RunTacticResults = RunTacticResults+  { rtr_trace       :: Trace+  , rtr_extract     :: LHsExpr GhcPs+  , rtr_other_solns :: [Synthesized (LHsExpr GhcPs)]+  , rtr_jdg         :: Judgement+  , rtr_ctx         :: Context+  } deriving Show+++data AgdaMatch = AgdaMatch+  { amPats :: [Pat GhcPs]+  , amBody :: HsExpr GhcPs+  }+  deriving (Show)+++data UserFacingMessage+  = TacticErrors+  | TimedOut+  | NothingToDo+  | InfrastructureError Text+  deriving Eq++instance Show UserFacingMessage where+  show TacticErrors            = "Wingman couldn't find a solution"+  show TimedOut                = "Wingman timed out while trying to find a solution"+  show NothingToDo             = "Nothing to do"+  show (InfrastructureError t) = "Internal error: " <> T.unpack t+
test/AutoTupleSpec.hs view
@@ -2,17 +2,17 @@  module AutoTupleSpec where -import Data.Either (isRight)-import Ide.Plugin.Tactic.Judgements (mkFirstJudgement)-import Ide.Plugin.Tactic.Machinery-import Ide.Plugin.Tactic.Tactics (auto')-import Ide.Plugin.Tactic.Types-import OccName (mkVarOcc)-import Test.Hspec-import Test.QuickCheck-import Type (mkTyVarTy)-import TysPrim (alphaTyVars)-import TysWiredIn (mkBoxedTupleTy)+import           Data.Either                  (isRight)+import           Wingman.Judgements (mkFirstJudgement)+import           Wingman.Machinery+import           Wingman.Tactics    (auto')+import           Wingman.Types+import           OccName                      (mkVarOcc)+import           Test.Hspec+import           Test.QuickCheck+import           Type                         (mkTyVarTy)+import           TysPrim                      (alphaTyVars)+import           TysWiredIn                   (mkBoxedTupleTy)   spec :: Spec
+ test/CodeAction/AutoSpec.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ViewPatterns          #-}++module CodeAction.AutoSpec where++import Wingman.Types+import Test.Hspec+import Utils+import Wingman.FeatureSet (allFeatures)+++spec :: Spec+spec = do+  let autoTest = goldenTest Auto ""+      autoTestNoWhitespace = goldenTestNoWhitespace Auto ""++  describe "golden" $ do+    autoTest 11  8 "AutoSplitGADT.hs"+    autoTest  2 11 "GoldenEitherAuto.hs"+    autoTest  4 12 "GoldenJoinCont.hs"+    autoTest  3 11 "GoldenIdentityFunctor.hs"+    autoTest  7 11 "GoldenIdTypeFam.hs"+    autoTest  2 15 "GoldenEitherHomomorphic.hs"+    autoTest  2  8 "GoldenNote.hs"+    autoTest  2 12 "GoldenPureList.hs"+    autoTest  2 12 "GoldenListFmap.hs"+    autoTest  2 13 "GoldenFromMaybe.hs"+    autoTest  2 10 "GoldenFoldr.hs"+    autoTest  2  8 "GoldenSwap.hs"+    autoTest  4 11 "GoldenFmapTree.hs"+    autoTest  7 13 "GoldenGADTAuto.hs"+    autoTest  2 12 "GoldenSwapMany.hs"+    autoTest  4 12 "GoldenBigTuple.hs"+    autoTest  2 10 "GoldenShow.hs"+    autoTest  2 15 "GoldenShowCompose.hs"+    autoTest  2  8 "GoldenShowMapChar.hs"+    autoTest  7  8 "GoldenSuperclass.hs"+    autoTest  2 12 "GoldenSafeHead.hs"+    autoTest  2 12 "FmapBoth.hs"+    autoTest  7  8 "RecordCon.hs"+    autoTest  6  8 "NewtypeRecord.hs"+    autoTest  2 14 "FmapJoin.hs"+    autoTest  2  9 "Fgmap.hs"+    autoTest  4 19 "FmapJoinInLet.hs"+    autoTest  9 12 "AutoEndo.hs"+    autoTest  2 16 "AutoEmptyString.hs"+    autoTest  7 35 "AutoPatSynUse.hs"+    autoTest  2 28 "AutoZip.hs"+    autoTest  2 17 "AutoInfixApply.hs"+    autoTest  2 19 "AutoInfixApplyMany.hs"+    autoTest  2 25 "AutoInfixInfix.hs"++    failing "flaky in CI" $+      autoTest 2 11 "GoldenApplicativeThen.hs"++    failing "not enough auto gas" $+      autoTest 5 18 "GoldenFish.hs"++  describe "theta" $ do+    autoTest 12 10 "AutoThetaFix.hs"+    autoTest  7 20 "AutoThetaRankN.hs"+    autoTest  6 10 "AutoThetaGADT.hs"+    autoTest  6  8 "AutoThetaGADTDestruct.hs"+    autoTest  4  8 "AutoThetaEqCtx.hs"+    autoTest  6 10 "AutoThetaEqGADT.hs"+    autoTest  6  8 "AutoThetaEqGADTDestruct.hs"+    autoTest  6 10 "AutoThetaRefl.hs"+    autoTest  6  8 "AutoThetaReflDestruct.hs"++  describe "known" $ do+    autoTest 25 13 "GoldenArbitrary.hs"+    autoTestNoWhitespace+              6 10 "KnownBigSemigroup.hs"+    autoTest  4 10 "KnownThetaSemigroup.hs"+    autoTest  6 10 "KnownCounterfactualSemigroup.hs"+    autoTest 10 10 "KnownModuleInstanceSemigroup.hs"+    autoTest  4 22 "KnownDestructedSemigroup.hs"+    autoTest  4 10 "KnownMissingSemigroup.hs"+    autoTest  7 12 "KnownMonoid.hs"+    autoTest  7 12 "KnownPolyMonoid.hs"+    autoTest  7 12 "KnownMissingMonoid.hs"+++  describe "messages" $ do+    mkShowMessageTest allFeatures Auto "" 2 8 "MessageForallA.hs" TacticErrors+
+ test/CodeAction/DestructAllSpec.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ViewPatterns          #-}++module CodeAction.DestructAllSpec where++import Wingman.Types+import Test.Hspec+import Utils+++spec :: Spec+spec = do+  let destructAllTest = goldenTest DestructAll ""+  describe "provider" $ do+    mkTest+      "Requires args on lhs of ="+      "DestructAllProvider.hs" 3 21+      [ (not, DestructAll, "")+      ]+    mkTest+      "Can't be a non-top-hole"+      "DestructAllProvider.hs" 8 19+      [ (not, DestructAll, "")+      , (id, Destruct, "a")+      , (id, Destruct, "b")+      ]+    mkTest+      "Provides a destruct all otherwise"+      "DestructAllProvider.hs" 12 22+      [ (id, DestructAll, "")+      ]++  describe "golden" $ do+    destructAllTest 2 11 "DestructAllAnd.hs"+    destructAllTest 4 23 "DestructAllMany.hs"+    destructAllTest 2 18 "DestructAllNonVarTopMatch.hs"+    destructAllTest 2 18 "DestructAllFunc.hs"+
+ test/CodeAction/DestructSpec.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ViewPatterns          #-}++module CodeAction.DestructSpec where++import Wingman.Types+import Test.Hspec+import Utils+++spec :: Spec+spec = do+  let destructTest = goldenTest Destruct++  describe "golden" $ do+    destructTest "gadt" 7 17 "GoldenGADTDestruct.hs"+    destructTest "gadt" 8 17 "GoldenGADTDestructCoercion.hs"+    destructTest "a"    7 25 "SplitPattern.hs"++  describe "layout" $ do+    destructTest "b"  4  3 "LayoutBind.hs"+    destructTest "b"  2 15 "LayoutDollarApp.hs"+    destructTest "b"  2 18 "LayoutOpApp.hs"+    destructTest "b"  2 14 "LayoutLam.hs"+    destructTest "x" 11 15 "LayoutSplitWhere.hs"+    destructTest "x"  3 12 "LayoutSplitClass.hs"+    destructTest "b"  3  9 "LayoutSplitGuard.hs"+    destructTest "b"  4 13 "LayoutSplitLet.hs"+    destructTest "a"  4  7 "LayoutSplitIn.hs"+    destructTest "a"  4 31 "LayoutSplitViewPat.hs"+    destructTest "a"  7 17 "LayoutSplitPattern.hs"+    destructTest "a"  8 26 "LayoutSplitPatSyn.hs"+
+ test/CodeAction/IntrosSpec.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ViewPatterns          #-}++module CodeAction.IntrosSpec where++import Wingman.Types+import Test.Hspec+import Utils+++spec :: Spec+spec = do+  let introsTest = goldenTest Intros ""++  describe "golden" $ do+    introsTest 2 8 "GoldenIntros.hs"+
+ test/CodeAction/RefineSpec.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ViewPatterns          #-}++module CodeAction.RefineSpec where++import Wingman.Types+import Test.Hspec+import Utils+import Wingman.FeatureSet (allFeatures)+++spec :: Spec+spec = do+  let refineTest = goldenTest Refine ""++  describe "golden" $ do+    refineTest 2 8 "RefineIntro.hs"+    refineTest 2 8 "RefineCon.hs"+    refineTest 4 8 "RefineReader.hs"+    refineTest 8 8 "RefineGADT.hs"++  describe "messages" $ do+    mkShowMessageTest allFeatures Refine "" 2 8 "MessageForallA.hs" NothingToDo+
+ test/CodeAction/UseDataConSpec.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ViewPatterns          #-}++module CodeAction.UseDataConSpec where++import qualified Data.Text as T+import           Wingman.Types+import           Test.Hspec+import           Utils+++spec :: Spec+spec = do+  let useTest = goldenTest UseDataCon++  describe "provider" $ do+    mkTest+      "Suggests all data cons for Either"+      "ConProviders.hs" 5 6+      [ (id, UseDataCon, "Left")+      , (id, UseDataCon, "Right")+      , (not, UseDataCon, ":")+      , (not, UseDataCon, "[]")+      , (not, UseDataCon, "C1")+      ]+    mkTest+      "Suggests no data cons for big types"+      "ConProviders.hs" 11 17 $ do+        c <- [1 :: Int .. 10]+        pure $ (not, UseDataCon, T.pack $ show c)+    mkTest+      "Suggests only matching data cons for GADT"+      "ConProviders.hs" 20 12+      [ (id, UseDataCon, "IntGADT")+      , (id, UseDataCon, "VarGADT")+      , (not, UseDataCon, "BoolGADT")+      ]++  describe "golden" $ do+    useTest "(,)"   2 8 "UseConPair.hs"+    useTest "Left"  2 8 "UseConLeft.hs"+    useTest "Right" 2 8 "UseConRight.hs"+
− test/GoldenSpec.hs
@@ -1,224 +0,0 @@-{-# LANGUAGE DataKinds             #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE OverloadedStrings     #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE ViewPatterns          #-}--module GoldenSpec where--import           Control.Applicative.Combinators ( skipManyTill )-import           Control.Lens hiding ((<.>), failing)-import           Control.Monad (unless)-import           Control.Monad.IO.Class-import           Data.Aeson-import           Data.Default (Default(def))-import           Data.Foldable-import qualified Data.Map as M-import           Data.Maybe-import           Data.Text (Text)-import qualified Data.Text.IO as T-import qualified Ide.Plugin.Config as Plugin-import           Ide.Plugin.Tactic.FeatureSet (FeatureSet, allFeatures)-import           Ide.Plugin.Tactic.TestTypes-import           Language.LSP.Test-import           Language.LSP.Types-import           Language.LSP.Types.Lens hiding (id, capabilities, message, executeCommand, applyEdit, rename, line, title, name, actions)-import           System.Directory (doesFileExist)-import           System.FilePath-import           Test.Hspec---spec :: Spec-spec = do-  describe "code action availability" $ do-    mkTest-      "Produces intros code action"-      "T1.hs" 2 14-      [ (id, Intros, "")-      ]-    mkTest-      "Produces destruct and homomorphism code actions"-      "T2.hs" 2 21-      [ (id, Destruct, "eab")-      , (id, Homomorphism, "eab")-      ]-    mkTest-      "Won't suggest homomorphism on the wrong type"-      "T2.hs" 8 8-      [ (not, Homomorphism, "global")-      ]-    mkTest-      "Won't suggest intros on the wrong type"-      "T2.hs" 8 8-      [ (not, Intros, "")-      ]-    mkTest-      "Produces (homomorphic) lambdacase code actions"-      "T3.hs" 4 24-      [ (id, HomomorphismLambdaCase, "")-      , (id, DestructLambdaCase, "")-      ]-    mkTest-      "Produces lambdacase code actions"-      "T3.hs" 7 13-      [ (id, DestructLambdaCase, "")-      ]-    mkTest-      "Doesn't suggest lambdacase without -XLambdaCase"-      "T2.hs" 11 25-      [ (not, DestructLambdaCase, "")-      ]--  describe "golden tests" $ do-    let goldenTest = mkGoldenTest allFeatures-        autoTest = mkGoldenTest allFeatures Auto ""--    goldenTest Intros "" "GoldenIntros.hs" 2 8-    autoTest "GoldenEitherAuto.hs"        2 11-    autoTest "GoldenJoinCont.hs"          4 12-    autoTest "GoldenIdentityFunctor.hs"   3 11-    autoTest "GoldenIdTypeFam.hs"         7 11-    autoTest "GoldenEitherHomomorphic.hs" 2 15-    autoTest "GoldenNote.hs"              2 8-    autoTest "GoldenPureList.hs"          2 12-    autoTest "GoldenListFmap.hs"          2 12-    autoTest "GoldenFromMaybe.hs"         2 13-    autoTest "GoldenFoldr.hs"             2 10-    autoTest "GoldenSwap.hs"              2 8-    autoTest "GoldenFmapTree.hs"          4 11-    goldenTest Destruct "gadt"-             "GoldenGADTDestruct.hs"      7 17-    goldenTest Destruct "gadt"-             "GoldenGADTDestructCoercion.hs" 8 17-    autoTest "GoldenGADTAuto.hs"    7 13-    autoTest "GoldenSwapMany.hs"    2 12-    autoTest "GoldenBigTuple.hs"    4 12-    autoTest "GoldenShow.hs"        2 10-    autoTest "GoldenShowCompose.hs" 2 15-    autoTest "GoldenShowMapChar.hs" 2 8-    autoTest "GoldenSuperclass.hs"  7 8-    failing "flaky in CI" $-      autoTest "GoldenApplicativeThen.hs" 2 11-    autoTest "GoldenSafeHead.hs"  2 12-    failing "not enough auto gas" $-      autoTest "GoldenFish.hs" 5 18-    autoTest "GoldenArbitrary.hs" 25 13-    autoTest "FmapBoth.hs"        2 12-    autoTest "RecordCon.hs"       7  8-    autoTest "FmapJoin.hs"        2 14-    autoTest "Fgmap.hs"           2 9-    autoTest "FmapJoinInLet.hs"   4 19-    goldenTest Destruct "a"-      "SplitPattern.hs"  7 25------------------------------------------------------------------------------------ | Get a range at the given line and column corresponding to having nothing--- selected.------ NB: These coordinates are in "file space", ie, 1-indexed.-pointRange :: Int -> Int -> Range-pointRange-  (subtract 1 -> line)-  (subtract 1 -> col) =-    Range (Position line col) (Position line $ col + 1)------------------------------------------------------------------------------------ | Get the title of a code action.-codeActionTitle :: (Command |? CodeAction) -> Maybe Text-codeActionTitle InL{} = Nothing-codeActionTitle (InR(CodeAction title _ _ _ _ _ _)) = Just title------------------------------------------------------------------------------------ | Make a tactic unit test.-mkTest-    :: Foldable t-    => String  -- ^ The test name-    -> FilePath  -- ^ The file to load-    -> Int  -- ^ Cursor line-    -> Int  -- ^ Cursor columnn-    -> t ( Bool -> Bool   -- Use 'not' for actions that shouldnt be present-         , TacticCommand  -- An expected command ...-         , Text           -- ... for this variable-         ) -- ^ A collection of (un)expected code actions.-    -> SpecWith (Arg Bool)-mkTest name fp line col ts = it name $ do-  runSession testCommand fullCaps tacticPath $ do-    doc <- openDoc fp "haskell"-    _ <- waitForDiagnostics-    actions <- getCodeActions doc $ pointRange line col-    let titles = mapMaybe codeActionTitle actions-    for_ ts $ \(f, tc, var) -> do-      let title = tacticTitle tc var-      liftIO $-        (title `elem` titles) `shouldSatisfy` f---setFeatureSet :: FeatureSet -> Session ()-setFeatureSet features = do-  let unObject (Object obj) = obj-      unObject _ = undefined-      def_config = def :: Plugin.Config-      config =-        def_config-          { Plugin.plugins = M.fromList [("tactics",-              def { Plugin.plcConfig = unObject $ toJSON $-                emptyConfig { cfg_feature_set = features }}-          )] <> Plugin.plugins def_config }--  sendNotification SWorkspaceDidChangeConfiguration $-    DidChangeConfigurationParams $-      toJSON config---mkGoldenTest-    :: FeatureSet-    -> TacticCommand-    -> Text-    -> FilePath-    -> Int-    -> Int-    -> SpecWith ()-mkGoldenTest features tc occ input line col =-  it (input <> " (golden)") $ do-    runSession testCommand fullCaps tacticPath $ do-      setFeatureSet features-      doc <- openDoc input "haskell"-      _ <- waitForDiagnostics-      actions <- getCodeActions doc $ pointRange line col-      Just (InR CodeAction {_command = Just c})-        <- pure $ find ((== Just (tacticTitle tc occ)) . codeActionTitle) actions-      executeCommand c-      _resp <- skipManyTill anyMessage (message SWorkspaceApplyEdit)-      edited <- documentContents doc-      let expected_name = tacticPath </> input <.> "expected"-      -- Write golden tests if they don't already exist-      liftIO $ (doesFileExist expected_name >>=) $ flip unless $ do-        T.writeFile expected_name edited-      expected <- liftIO $ T.readFile expected_name-      liftIO $ edited `shouldBe` expected------------------------------------------------------------------------------------ | Don't run a test.-failing :: Applicative m => String -> b -> m ()-failing _ _ = pure ()---tacticPath :: FilePath-tacticPath = "test/golden"---testCommand :: String-testCommand = "test-server"---executeCommandWithResp :: Command -> Session (ResponseMessage 'WorkspaceExecuteCommand)-executeCommandWithResp cmd = do-  let args = decode $ encode $ fromJust $ cmd ^. arguments-      execParams = ExecuteCommandParams Nothing (cmd ^. command) args-  request SWorkspaceExecuteCommand execParams-
test/Main.hs view
@@ -1,1 +1,8 @@-{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Main #-}+module Main where++import qualified Spec+import Test.Hls+import Test.Tasty.Hspec++main :: IO ()+main = testSpecs Spec.spec >>= defaultTestRunner . testGroup "tactics"
+ test/ProviderSpec.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ViewPatterns          #-}++module ProviderSpec where++import Wingman.Types+import Test.Hspec+import Utils+++spec :: Spec+spec = do+  mkTest+    "Produces intros code action"+    "T1.hs" 2 14+    [ (id, Intros, "")+    ]+  mkTest+    "Produces destruct and homomorphism code actions"+    "T2.hs" 2 21+    [ (id, Destruct, "eab")+    , (id, Homomorphism, "eab")+    ]+  mkTest+    "Won't suggest homomorphism on the wrong type"+    "T2.hs" 8 8+    [ (not, Homomorphism, "global")+    ]+  mkTest+    "Won't suggest intros on the wrong type"+    "T2.hs" 8 8+    [ (not, Intros, "")+    ]+  mkTest+    "Produces (homomorphic) lambdacase code actions"+    "T3.hs" 4 24+    [ (id, HomomorphismLambdaCase, "")+    , (id, DestructLambdaCase, "")+    ]+  mkTest+    "Produces lambdacase code actions"+    "T3.hs" 7 13+    [ (id, DestructLambdaCase, "")+    ]+  mkTest+    "Doesn't suggest lambdacase without -XLambdaCase"+    "T2.hs" 11 25+    [ (not, DestructLambdaCase, "")+    ]++  mkTest+    "Doesn't suggest destruct if already destructed"+    "ProvideAlreadyDestructed.hs" 6 18+    [ (not, Destruct, "x")+    ]++  mkTest+    "...but does suggest destruct if destructed in a different branch"+    "ProvideAlreadyDestructed.hs" 9 7+    [ (id, Destruct, "x")+    ]++  mkTest+    "Doesn't suggest destruct on class methods"+    "ProvideLocalHyOnly.hs" 2 12+    [ (not, Destruct, "mempty")+    ]+
− test/Server.hs
@@ -1,19 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ViewPatterns      #-}--module Main(main) where--import           Data.Default-import           Development.IDE.Main-import qualified Development.IDE.Plugin.HLS.GhcIde as Ghcide-import           Ide.Plugin.Tactic as T-import           Ide.PluginUtils--main :: IO ()-main = defaultMain def-  { argsHlsPlugins = pluginDescToIdePlugins $-    [ T.descriptor "tactic"-    ] <>-    Ghcide.descriptors-  }-
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}
test/UnificationSpec.hs view
@@ -10,19 +10,14 @@ import qualified Data.Set as S import           Data.Traversable import           Data.Tuple (swap)-import           Ide.Plugin.Tactic.Debug-import           Ide.Plugin.Tactic.Machinery-import           Ide.Plugin.Tactic.Types-import           TcType (tcGetTyVar_maybe, substTy)+import           TcType (substTy, tcGetTyVar_maybe) import           Test.Hspec import           Test.QuickCheck import           Type (mkTyVarTy) import           TysPrim (alphaTyVars) import           TysWiredIn (mkBoxedTupleTy)---instance Show Type where-  show = unsafeRender+import           Wingman.Machinery+import           Wingman.Types   spec :: Spec
+ test/Utils.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ViewPatterns          #-}++module Utils where++import           Control.DeepSeq (deepseq)+import qualified Control.Exception as E+import           Control.Lens hiding (failing, (<.>), (.=))+import           Control.Monad (unless)+import           Control.Monad.IO.Class+import           Data.Aeson+import           Data.Foldable+import           Data.Function (on)+import qualified Data.Map as M+import           Data.Maybe+import           Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.IO as T+import qualified Ide.Plugin.Config as Plugin+import           Ide.Plugin.Tactic as Tactic+import           Language.LSP.Types+import           Language.LSP.Types.Lens hiding (actions, applyEdit, capabilities, executeCommand, id, line, message, name, rename, title)+import           System.Directory (doesFileExist)+import           System.FilePath+import           Test.Hls+import           Test.Hspec+import           Test.Hspec.Formatters (FailureReason(ExpectedButGot))+import           Test.Tasty.HUnit (Assertion, HUnitFailure(..))+import           Wingman.FeatureSet (FeatureSet, allFeatures, prettyFeatureSet)+import           Wingman.LanguageServer (mkShowMessageParams)+import           Wingman.Types++plugin :: PluginDescriptor IdeState+plugin = Tactic.descriptor "tactics"++------------------------------------------------------------------------------+-- | Get a range at the given line and column corresponding to having nothing+-- selected.+--+-- NB: These coordinates are in "file space", ie, 1-indexed.+pointRange :: Int -> Int -> Range+pointRange+  (subtract 1 -> line)+  (subtract 1 -> col) =+    Range (Position line col) (Position line $ col + 1)+++------------------------------------------------------------------------------+-- | Get the title of a code action.+codeActionTitle :: (Command |? CodeAction) -> Maybe Text+codeActionTitle InL{}                               = Nothing+codeActionTitle (InR(CodeAction title _ _ _ _ _ _ _)) = Just title+++------------------------------------------------------------------------------+-- | Make a tactic unit test.+mkTest+    :: Foldable t+    => String  -- ^ The test name+    -> FilePath  -- ^ The file to load+    -> Int  -- ^ Cursor line+    -> Int  -- ^ Cursor columnn+    -> t ( Bool -> Bool   -- Use 'not' for actions that shouldnt be present+         , TacticCommand  -- An expected command ...+         , Text           -- ... for this variable+         ) -- ^ A collection of (un)expected code actions.+    -> SpecWith (Arg Bool)+mkTest name fp line col ts = it name $ do+  runSessionWithServer plugin tacticPath $ do+    setFeatureSet allFeatures+    doc <- openDoc fp "haskell"+    _ <- waitForDiagnostics+    actions <- getCodeActions doc $ pointRange line col+    let titles = mapMaybe codeActionTitle actions+    for_ ts $ \(f, tc, var) -> do+      let title = tacticTitle tc var+      liftIO $+        (title `elem` titles) `shouldSatisfy` f+++setFeatureSet :: FeatureSet -> Session ()+setFeatureSet features = do+  let unObject (Object obj) = obj+      unObject _            = undefined+      def_config = def :: Plugin.Config+      config =+        def_config+          { Plugin.plugins = M.fromList [("tactics",+              def { Plugin.plcConfig = unObject $ object ["features" .= prettyFeatureSet features] }+          )] <> Plugin.plugins def_config }++  sendNotification SWorkspaceDidChangeConfiguration $+    DidChangeConfigurationParams $+      toJSON config+++mkGoldenTest+    :: (Text -> Text -> Assertion)+    -> FeatureSet+    -> TacticCommand+    -> Text+    -> Int+    -> Int+    -> FilePath+    -> SpecWith ()+mkGoldenTest eq features tc occ line col input =+  it (input <> " (golden)") $ do+    runSessionWithServer plugin tacticPath $ do+      setFeatureSet features+      doc <- openDoc input "haskell"+      _ <- waitForDiagnostics+      actions <- getCodeActions doc $ pointRange line col+      Just (InR CodeAction {_command = Just c})+        <- pure $ find ((== Just (tacticTitle tc occ)) . codeActionTitle) actions+      executeCommand c+      _resp <- skipManyTill anyMessage (message SWorkspaceApplyEdit)+      edited <- documentContents doc+      let expected_name = input <.> "expected"+      -- Write golden tests if they don't already exist+      liftIO $ (doesFileExist expected_name >>=) $ flip unless $ do+        T.writeFile expected_name edited+      expected <- liftIO $ T.readFile expected_name+      liftIO $ edited `eq` expected++mkShowMessageTest+    :: FeatureSet+    -> TacticCommand+    -> Text+    -> Int+    -> Int+    -> FilePath+    -> UserFacingMessage+    -> SpecWith ()+mkShowMessageTest features tc occ line col input ufm =+  it (input <> " (golden)") $ do+    runSessionWithServer plugin tacticPath $ do+      setFeatureSet features+      doc <- openDoc input "haskell"+      _ <- waitForDiagnostics+      actions <- getCodeActions doc $ pointRange line col+      Just (InR CodeAction {_command = Just c})+        <- pure $ find ((== Just (tacticTitle tc occ)) . codeActionTitle) actions+      executeCommand c+      NotificationMessage _ _ err <- skipManyTill anyMessage (message SWindowShowMessage)+      liftIO $ err `shouldBe` mkShowMessageParams ufm+++goldenTest :: TacticCommand -> Text -> Int -> Int -> FilePath -> SpecWith ()+goldenTest = mkGoldenTest shouldBe allFeatures++goldenTestNoWhitespace :: TacticCommand -> Text -> Int -> Int -> FilePath -> SpecWith ()+goldenTestNoWhitespace = mkGoldenTest shouldBeIgnoringSpaces allFeatures+++shouldBeIgnoringSpaces :: Text -> Text -> Assertion+shouldBeIgnoringSpaces = assertFun f ""+  where+    f = (==) `on` T.unwords . T.words+++assertFun+    :: Show a+    => (a -> a -> Bool)+    -> String -- ^ The message prefix+    -> a      -- ^ The expected value+    -> a      -- ^ The actual value+    -> Assertion+assertFun eq preface expected actual =+  unless (eq actual expected) $ do+    (prefaceMsg+      `deepseq` expectedMsg+      `deepseq` actualMsg+      `deepseq`+        E.throwIO+          (HUnitFailure Nothing $ show $ ExpectedButGot prefaceMsg expectedMsg actualMsg))+  where+    prefaceMsg+      | null preface = Nothing+      | otherwise = Just preface+    expectedMsg = show expected+    actualMsg = show actual++++------------------------------------------------------------------------------+-- | Don't run a test.+failing :: Applicative m => String -> b -> m ()+failing _ _ = pure ()+++tacticPath :: FilePath+tacticPath = "test/golden"+++executeCommandWithResp :: Command -> Session (ResponseMessage 'WorkspaceExecuteCommand)+executeCommandWithResp cmd = do+  let args = decode $ encode $ fromJust $ cmd ^. arguments+      execParams = ExecuteCommandParams Nothing (cmd ^. command) args+  request SWorkspaceExecuteCommand execParams+
+ test/golden/AutoEmptyString.hs view
@@ -0,0 +1,2 @@+empty_string :: String+empty_string = _
+ test/golden/AutoEmptyString.hs.expected view
@@ -0,0 +1,2 @@+empty_string :: String+empty_string = ""
+ test/golden/AutoEndo.hs view
@@ -0,0 +1,10 @@+data Synthesized b a = Synthesized+  { syn_trace :: b+  , syn_val   :: a+  }+  deriving (Eq, Show)+++mapTrace :: (b -> b) -> Synthesized b a -> Synthesized b a+mapTrace = _+
+ test/golden/AutoEndo.hs.expected view
@@ -0,0 +1,11 @@+data Synthesized b a = Synthesized+  { syn_trace :: b+  , syn_val   :: a+  }+  deriving (Eq, Show)+++mapTrace :: (b -> b) -> Synthesized b a -> Synthesized b a+mapTrace fbb (Synthesized b a)+  = Synthesized {syn_trace = fbb b, syn_val = a}+
+ test/golden/AutoInfixApply.hs view
@@ -0,0 +1,3 @@+test :: (a -> b -> c) -> a -> (a -> b) -> c+test (/:) a f = _+
+ test/golden/AutoInfixApply.hs.expected view
@@ -0,0 +1,3 @@+test :: (a -> b -> c) -> a -> (a -> b) -> c+test (/:) a f = a /: f a+
+ test/golden/AutoInfixApplyMany.hs view
@@ -0,0 +1,3 @@+test :: (a -> b -> x -> c) -> a -> (a -> b) -> x -> c+test (/:) a f x = _+
+ test/golden/AutoInfixApplyMany.hs.expected view
@@ -0,0 +1,3 @@+test :: (a -> b -> x -> c) -> a -> (a -> b) -> x -> c+test (/:) a f x = (a /: f a) x+
+ test/golden/AutoInfixInfix.hs view
@@ -0,0 +1,2 @@+test :: (a -> b -> c) -> (c -> d -> e) -> a -> (a -> b) -> d -> e+test (/:) (-->) a f x = _
+ test/golden/AutoInfixInfix.hs.expected view
@@ -0,0 +1,2 @@+test :: (a -> b -> c) -> (c -> d -> e) -> a -> (a -> b) -> d -> e+test (/:) (-->) a f x = (a /: f a) --> x
+ test/golden/AutoPatSynUse.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE PatternSynonyms #-}++pattern JustSingleton :: a -> Maybe [a]+pattern JustSingleton a <- Just [a]++amIASingleton :: Maybe [a] -> Maybe a+amIASingleton (JustSingleton a) = _+
+ test/golden/AutoPatSynUse.hs.expected view
@@ -0,0 +1,8 @@+{-# LANGUAGE PatternSynonyms #-}++pattern JustSingleton :: a -> Maybe [a]+pattern JustSingleton a <- Just [a]++amIASingleton :: Maybe [a] -> Maybe a+amIASingleton (JustSingleton a) = Just a+
+ test/golden/AutoSplitGADT.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE GADTs #-}++data GADT b a where+  GBool :: b -> GADT b Bool+  GInt :: GADT b Int++-- wingman would prefer to use GBool since then it can use its argument. But+-- that won't unify with GADT Int, so it is forced to pick GInt and ignore the+-- argument.+test :: b -> GADT b Int+test = _+
+ test/golden/AutoSplitGADT.hs.expected view
@@ -0,0 +1,12 @@+{-# LANGUAGE GADTs #-}++data GADT b a where+  GBool :: b -> GADT b Bool+  GInt :: GADT b Int++-- wingman would prefer to use GBool since then it can use its argument. But+-- that won't unify with GADT Int, so it is forced to pick GInt and ignore the+-- argument.+test :: b -> GADT b Int+test _ = GInt+
+ test/golden/AutoThetaEqCtx.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE GADTs #-}++fun2 :: (a ~ b) => a -> b+fun2 = _ -- id+
+ test/golden/AutoThetaEqCtx.hs.expected view
@@ -0,0 +1,5 @@+{-# LANGUAGE GADTs #-}++fun2 :: (a ~ b) => a -> b+fun2 = id -- id+
+ test/golden/AutoThetaEqGADT.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}++data Y a b = a ~ b => Y++fun3 :: Y a b -> a -> b+fun3 Y = _+
+ test/golden/AutoThetaEqGADT.hs.expected view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}++data Y a b = a ~ b => Y++fun3 :: Y a b -> a -> b+fun3 Y = id+
+ test/golden/AutoThetaEqGADTDestruct.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE GADTs #-}++data Y a b = a ~ b => Y++fun3 :: Y a b -> a -> b+fun3 = _++
+ test/golden/AutoThetaEqGADTDestruct.hs.expected view
@@ -0,0 +1,8 @@+{-# LANGUAGE GADTs #-}++data Y a b = a ~ b => Y++fun3 :: Y a b -> a -> b+fun3 Y a = a++
+ test/golden/AutoThetaFix.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE UndecidableInstances #-}++data Fix f a = Fix (f (Fix f a))++instance ( Functor f+           -- FIXME(sandy): Unfortunately, the recursion tactic fails to fire+           -- on this case. By explicitly adding the @Functor (Fix f)@+           -- dictionary, we can get Wingman to generate the right definition.+         , Functor (Fix f)+         ) => Functor (Fix f) where+  fmap = _+
+ test/golden/AutoThetaFix.hs.expected view
@@ -0,0 +1,13 @@+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE UndecidableInstances #-}++data Fix f a = Fix (f (Fix f a))++instance ( Functor f+           -- FIXME(sandy): Unfortunately, the recursion tactic fails to fire+           -- on this case. By explicitly adding the @Functor (Fix f)@+           -- dictionary, we can get Wingman to generate the right definition.+         , Functor (Fix f)+         ) => Functor (Fix f) where+  fmap fab (Fix fffa) = Fix (fmap (fmap fab) fffa)+
+ test/golden/AutoThetaGADT.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}++data X f = Monad f => X++fun1 :: X f -> a -> f a+fun1 X = _+
+ test/golden/AutoThetaGADT.hs.expected view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}++data X f = Monad f => X++fun1 :: X f -> a -> f a+fun1 X = pure+
+ test/golden/AutoThetaGADTDestruct.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}++data X f = Monad f => X++fun1 :: X f -> a -> f a+fun1 = _+
+ test/golden/AutoThetaGADTDestruct.hs.expected view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}++data X f = Monad f => X++fun1 :: X f -> a -> f a+fun1 X a = pure a+
+ test/golden/AutoThetaRankN.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE RankNTypes #-}++showMe :: (forall x. Show x => x -> String) -> Int -> String+showMe f = f++showedYou :: Int -> String+showedYou = showMe _+
+ test/golden/AutoThetaRankN.hs.expected view
@@ -0,0 +1,8 @@+{-# LANGUAGE RankNTypes #-}++showMe :: (forall x. Show x => x -> String) -> Int -> String+showMe f = f++showedYou :: Int -> String+showedYou = showMe show+
+ test/golden/AutoThetaRefl.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}++data Z a b where Z :: Z a a++fun4 :: Z a b -> a -> b+fun4 Z = _ -- id+
+ test/golden/AutoThetaRefl.hs.expected view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}++data Z a b where Z :: Z a a++fun4 :: Z a b -> a -> b+fun4 Z = id -- id+
+ test/golden/AutoThetaReflDestruct.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE GADTs #-}++data Z a b where Z :: Z a a++fun4 :: Z a b -> a -> b+fun4 = _ -- id++
+ test/golden/AutoThetaReflDestruct.hs.expected view
@@ -0,0 +1,8 @@+{-# LANGUAGE GADTs #-}++data Z a b where Z :: Z a a++fun4 :: Z a b -> a -> b+fun4 Z a = a -- id++
+ test/golden/AutoZip.hs view
@@ -0,0 +1,3 @@+zip_it_up_and_zip_it_out :: [a] -> [b] -> [(a, b)]+zip_it_up_and_zip_it_out = _+
+ test/golden/AutoZip.hs.expected view
@@ -0,0 +1,6 @@+zip_it_up_and_zip_it_out :: [a] -> [b] -> [(a, b)]+zip_it_up_and_zip_it_out _ [] = []+zip_it_up_and_zip_it_out [] (_ : _) = []+zip_it_up_and_zip_it_out (a : l_a5) (b : l_b3)+  = (a, b) : zip_it_up_and_zip_it_out l_a5 l_b3+
+ test/golden/ConProviders.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE GADTs #-}++-- Should suggest Left and Right, but not []+t1 :: Either a b+t1 = _+++data ManyConstructors = C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 | C10++noCtorsIfMany :: ManyConstructors+noCtorsIfMany = _+++data GADT a where+  IntGADT  :: GADT Int+  BoolGADT :: GADT Bool+  VarGADT  :: GADT a++gadtCtor :: GADT Int+gadtCtor = _+
+ test/golden/DestructAllAnd.hs view
@@ -0,0 +1,2 @@+and :: Bool -> Bool -> Bool+and x y = _
+ test/golden/DestructAllAnd.hs.expected view
@@ -0,0 +1,5 @@+and :: Bool -> Bool -> Bool+and False False = _+and True False = _+and False True = _+and True True = _
+ test/golden/DestructAllFunc.hs view
@@ -0,0 +1,3 @@+has_a_func :: Bool -> (a -> b) -> Bool+has_a_func x y = _+
+ test/golden/DestructAllFunc.hs.expected view
@@ -0,0 +1,4 @@+has_a_func :: Bool -> (a -> b) -> Bool+has_a_func False y = _+has_a_func True y = _+
+ test/golden/DestructAllMany.hs view
@@ -0,0 +1,4 @@+data ABC = A | B | C++many :: () -> Either a b -> Bool -> Maybe ABC -> ABC -> ()+many u e b mabc abc = _
+ test/golden/DestructAllMany.hs.expected view
@@ -0,0 +1,27 @@+data ABC = A | B | C++many :: () -> Either a b -> Bool -> Maybe ABC -> ABC -> ()+many () (Left a) False Nothing A = _+many () (Right b5) False Nothing A = _+many () (Left a) True Nothing A = _+many () (Right b5) True Nothing A = _+many () (Left a6) False (Just a) A = _+many () (Right b6) False (Just a) A = _+many () (Left a6) True (Just a) A = _+many () (Right b6) True (Just a) A = _+many () (Left a) False Nothing B = _+many () (Right b5) False Nothing B = _+many () (Left a) True Nothing B = _+many () (Right b5) True Nothing B = _+many () (Left a6) False (Just a) B = _+many () (Right b6) False (Just a) B = _+many () (Left a6) True (Just a) B = _+many () (Right b6) True (Just a) B = _+many () (Left a) False Nothing C = _+many () (Right b5) False Nothing C = _+many () (Left a) True Nothing C = _+many () (Right b5) True Nothing C = _+many () (Left a6) False (Just a) C = _+many () (Right b6) False (Just a) C = _+many () (Left a6) True (Just a) C = _+many () (Right b6) True (Just a) C = _
+ test/golden/DestructAllNonVarTopMatch.hs view
@@ -0,0 +1,3 @@+and :: (a, b) -> Bool -> Bool -> Bool+and (a, b) x y = _+
+ test/golden/DestructAllNonVarTopMatch.hs.expected view
@@ -0,0 +1,6 @@+and :: (a, b) -> Bool -> Bool -> Bool+and (a, b) False False = _+and (a, b) True False = _+and (a, b) False True = _+and (a, b) True True = _+
+ test/golden/DestructAllProvider.hs view
@@ -0,0 +1,12 @@+-- we need to name the args ourselves first+nothingToDestruct :: [a] -> [a] -> [a]+nothingToDestruct = _+++-- can't destruct all for non-top-level holes+notTop :: Bool -> Bool -> Bool+notTop a b = a && _++-- destruct all is ok+canDestructAll :: Bool -> Bool -> Bool+canDestructAll a b = _
+ test/golden/Fgmap.hs view
@@ -0,0 +1,2 @@+fgmap :: (Functor f, Functor g) => (a -> b) -> (f (g a) -> f (g b))+fgmap = _
+ test/golden/Fgmap.hs.expected view
@@ -0,0 +1,2 @@+fgmap :: (Functor f, Functor g) => (a -> b) -> (f (g a) -> f (g b))+fgmap = fmap . fmap
+ test/golden/FmapBoth.hs view
@@ -0,0 +1,3 @@+fmapBoth :: (Functor f, Functor g) => (a -> b) -> (f a, g a) -> (f b, g b)+fmapBoth = _+
+ test/golden/FmapBoth.hs.expected view
@@ -0,0 +1,3 @@+fmapBoth :: (Functor f, Functor g) => (a -> b) -> (f a, g a) -> (f b, g b)+fmapBoth fab (fa, ga) = (fmap fab fa, fmap fab ga)+
+ test/golden/FmapJoin.hs view
@@ -0,0 +1,2 @@+fJoin :: (Monad m, Monad f) => f (m (m a)) -> f (m a)+fJoin = fmap _
+ test/golden/FmapJoin.hs.expected view
@@ -0,0 +1,2 @@+fJoin :: (Monad m, Monad f) => f (m (m a)) -> f (m a)+fJoin = fmap (\ mma -> mma >>= id)
+ test/golden/FmapJoinInLet.hs view
@@ -0,0 +1,4 @@+{-# LANGUAGE ScopedTypeVariables #-}++fJoin :: forall f m a. (Monad m, Monad f) => f (m (m a)) -> f (m a)+fJoin =  let f = (_ :: m (m a) -> m a) in fmap f
+ test/golden/FmapJoinInLet.hs.expected view
@@ -0,0 +1,4 @@+{-# LANGUAGE ScopedTypeVariables #-}++fJoin :: forall f m a. (Monad m, Monad f) => f (m (m a)) -> f (m a)+fJoin =  let f = ( (\ mma -> mma >>= id) :: m (m a) -> m a) in fmap f
+ test/golden/GoldenApplicativeThen.hs view
@@ -0,0 +1,2 @@+useThen :: Applicative f => f Int -> f a -> f a+useThen = _
+ test/golden/GoldenArbitrary.hs view
@@ -0,0 +1,26 @@+-- Emulate a quickcheck import; deriveArbitrary works on any type with the+-- right name and kind+data Gen a++data Obj+  = Square Int Int+  | Circle Int+  | Polygon [(Int, Int)]+  | Rotate2 Double Obj+  | Empty+  | Full+  | Complement Obj+  | UnionR Double [Obj]+  | DifferenceR Double Obj [Obj]+  | IntersectR Double [Obj]+  | Translate Double Double Obj+  | Scale Double Double Obj+  | Mirror Double Double Obj+  | Outset Double Obj+  | Shell Double Obj+  | WithRounding Double Obj+++arbitrary :: Gen Obj+arbitrary = _+
+ test/golden/GoldenArbitrary.hs.expected view
@@ -0,0 +1,53 @@+-- Emulate a quickcheck import; deriveArbitrary works on any type with the+-- right name and kind+data Gen a++data Obj+  = Square Int Int+  | Circle Int+  | Polygon [(Int, Int)]+  | Rotate2 Double Obj+  | Empty+  | Full+  | Complement Obj+  | UnionR Double [Obj]+  | DifferenceR Double Obj [Obj]+  | IntersectR Double [Obj]+  | Translate Double Double Obj+  | Scale Double Double Obj+  | Mirror Double Double Obj+  | Outset Double Obj+  | Shell Double Obj+  | WithRounding Double Obj+++arbitrary :: Gen Obj+arbitrary+  = let+      terminal+        = [(Square <$> arbitrary) <*> arbitrary, Circle <$> arbitrary,+           Polygon <$> arbitrary, pure Empty, pure Full]+    in+      sized+        $ (\ n+             -> case n <= 1 of+                  True -> oneof terminal+                  False+                    -> oneof+                         $ ([(Rotate2 <$> arbitrary) <*> scale (subtract 1) arbitrary,+                             Complement <$> scale (subtract 1) arbitrary,+                             (UnionR <$> arbitrary) <*> scale (subtract 1) arbitrary,+                             ((DifferenceR <$> arbitrary) <*> scale (flip div 2) arbitrary)+                               <*> scale (flip div 2) arbitrary,+                             (IntersectR <$> arbitrary) <*> scale (subtract 1) arbitrary,+                             ((Translate <$> arbitrary) <*> arbitrary)+                               <*> scale (subtract 1) arbitrary,+                             ((Scale <$> arbitrary) <*> arbitrary)+                               <*> scale (subtract 1) arbitrary,+                             ((Mirror <$> arbitrary) <*> arbitrary)+                               <*> scale (subtract 1) arbitrary,+                             (Outset <$> arbitrary) <*> scale (subtract 1) arbitrary,+                             (Shell <$> arbitrary) <*> scale (subtract 1) arbitrary,+                             (WithRounding <$> arbitrary) <*> scale (subtract 1) arbitrary]+                              <> terminal))+
+ test/golden/GoldenBigTuple.hs view
@@ -0,0 +1,4 @@+-- There used to be a bug where we were unable to perform a nested split. The+-- more serious regression test of this is 'AutoTupleSpec'.+bigTuple :: (a, b, c, d) -> (a, b, (c, d))+bigTuple = _
+ test/golden/GoldenBigTuple.hs.expected view
@@ -0,0 +1,4 @@+-- There used to be a bug where we were unable to perform a nested split. The+-- more serious regression test of this is 'AutoTupleSpec'.+bigTuple :: (a, b, c, d) -> (a, b, (c, d))+bigTuple (a, b, c, d) = (a, b, (c, d))
+ test/golden/GoldenEitherAuto.hs view
@@ -0,0 +1,2 @@+either' :: (a -> c) -> (b -> c) -> Either a b -> c+either' = _
+ test/golden/GoldenEitherAuto.hs.expected view
@@ -0,0 +1,3 @@+either' :: (a -> c) -> (b -> c) -> Either a b -> c+either' fac _ (Left a) = fac a+either' _ fbc (Right b) = fbc b
+ test/golden/GoldenEitherHomomorphic.hs view
@@ -0,0 +1,2 @@+eitherSplit :: a -> Either (a -> b) (a -> c) -> Either b c+eitherSplit = _
+ test/golden/GoldenEitherHomomorphic.hs.expected view
@@ -0,0 +1,3 @@+eitherSplit :: a -> Either (a -> b) (a -> c) -> Either b c+eitherSplit a (Left fab) = Left (fab a)+eitherSplit a (Right fac) = Right (fac a)
+ test/golden/GoldenFish.hs view
@@ -0,0 +1,5 @@+-- There was an old bug where we would only pull skolems from the hole, rather+-- than the entire hypothesis. Because of this, the 'b' here would be+-- considered a univar, which could then be unified with the skolem 'c'.+fish :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c+fish amb bmc a = _
+ test/golden/GoldenFmapTree.hs view
@@ -0,0 +1,4 @@+data Tree a = Leaf a | Branch (Tree a) (Tree a)++instance Functor Tree where+   fmap = _
+ test/golden/GoldenFmapTree.hs.expected view
@@ -0,0 +1,5 @@+data Tree a = Leaf a | Branch (Tree a) (Tree a)++instance Functor Tree where+   fmap fab (Leaf a) = Leaf (fab a)+   fmap fab (Branch ta2 ta3) = Branch (fmap fab ta2) (fmap fab ta3)
+ test/golden/GoldenFoldr.hs view
@@ -0,0 +1,2 @@+foldr2 :: (a -> b -> b) -> b -> [a] -> b+foldr2 = _
+ test/golden/GoldenFoldr.hs.expected view
@@ -0,0 +1,3 @@+foldr2 :: (a -> b -> b) -> b -> [a] -> b+foldr2 _ b [] = b+foldr2 f_b b (a : l_a4) = f_b a (foldr2 f_b b l_a4)
+ test/golden/GoldenFromMaybe.hs view
@@ -0,0 +1,2 @@+fromMaybe :: a -> Maybe a -> a+fromMaybe = _
+ test/golden/GoldenFromMaybe.hs.expected view
@@ -0,0 +1,3 @@+fromMaybe :: a -> Maybe a -> a+fromMaybe a Nothing = a+fromMaybe _ (Just a2) = a2
+ test/golden/GoldenGADTAuto.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}+module GoldenGADTAuto where+data CtxGADT a where+  MkCtxGADT :: (Show a, Eq a) => a -> CtxGADT a++ctxGADT :: CtxGADT ()+ctxGADT = _auto
+ test/golden/GoldenGADTAuto.hs.expected view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}+module GoldenGADTAuto where+data CtxGADT a where+  MkCtxGADT :: (Show a, Eq a) => a -> CtxGADT a++ctxGADT :: CtxGADT ()+ctxGADT = MkCtxGADT ()
+ test/golden/GoldenGADTDestruct.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}+module GoldenGADTDestruct where+data CtxGADT where+  MkCtxGADT :: (Show a, Eq a) => a -> CtxGADT++ctxGADT :: CtxGADT -> String+ctxGADT gadt = _decons
+ test/golden/GoldenGADTDestruct.hs.expected view
@@ -0,0 +1,7 @@+{-# LANGUAGE GADTs #-}+module GoldenGADTDestruct where+data CtxGADT where+  MkCtxGADT :: (Show a, Eq a) => a -> CtxGADT++ctxGADT :: CtxGADT -> String+ctxGADT (MkCtxGADT a) = _
+ test/golden/GoldenGADTDestructCoercion.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE GADTs #-}+module GoldenGADTDestruct where+data E a b where+  E :: forall a b. (b ~ a, Ord a) => b -> E a [a]++ctxGADT :: E a b -> String+ctxGADT gadt = _decons
+ test/golden/GoldenGADTDestructCoercion.hs.expected view
@@ -0,0 +1,8 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE GADTs #-}+module GoldenGADTDestruct where+data E a b where+  E :: forall a b. (b ~ a, Ord a) => b -> E a [a]++ctxGADT :: E a b -> String+ctxGADT (E b) = _
+ test/golden/GoldenIdTypeFam.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE TypeFamilies #-}++type family TyFam+type instance TyFam = Int++tyblah' :: TyFam -> Int+tyblah' = _
+ test/golden/GoldenIdTypeFam.hs.expected view
@@ -0,0 +1,7 @@+{-# LANGUAGE TypeFamilies #-}++type family TyFam+type instance TyFam = Int++tyblah' :: TyFam -> Int+tyblah' = id
+ test/golden/GoldenIdentityFunctor.hs view
@@ -0,0 +1,3 @@+data Ident a = Ident a+instance Functor Ident where+   fmap = _
+ test/golden/GoldenIdentityFunctor.hs.expected view
@@ -0,0 +1,3 @@+data Ident a = Ident a+instance Functor Ident where+   fmap fab (Ident a) = Ident (fab a)
+ test/golden/GoldenIntros.hs view
@@ -0,0 +1,2 @@+blah :: Int -> Bool -> (a -> b) -> String -> Int+blah = _
+ test/golden/GoldenIntros.hs.expected view
@@ -0,0 +1,2 @@+blah :: Int -> Bool -> (a -> b) -> String -> Int+blah i b fab l_c = _
+ test/golden/GoldenJoinCont.hs view
@@ -0,0 +1,4 @@+type Cont r a = ((a -> r) -> r)++joinCont :: Cont r (Cont r a) -> Cont r a+joinCont = _
+ test/golden/GoldenJoinCont.hs.expected view
@@ -0,0 +1,4 @@+type Cont r a = ((a -> r) -> r)++joinCont :: Cont r (Cont r a) -> Cont r a+joinCont f_r far = f_r (\ f_r2 -> f_r2 far)
+ test/golden/GoldenListFmap.hs view
@@ -0,0 +1,2 @@+fmapList :: (a -> b) -> [a] -> [b]+fmapList = _
+ test/golden/GoldenListFmap.hs.expected view
@@ -0,0 +1,3 @@+fmapList :: (a -> b) -> [a] -> [b]+fmapList _ [] = []+fmapList fab (a : l_a3) = fab a : fmapList fab l_a3
+ test/golden/GoldenNote.hs view
@@ -0,0 +1,2 @@+note :: e -> Maybe a -> Either e a+note = _
+ test/golden/GoldenNote.hs.expected view
@@ -0,0 +1,3 @@+note :: e -> Maybe a -> Either e a+note e Nothing = Left e+note _ (Just a) = Right a
+ test/golden/GoldenPureList.hs view
@@ -0,0 +1,2 @@+pureList :: a -> [a]+pureList = _
+ test/golden/GoldenPureList.hs.expected view
@@ -0,0 +1,2 @@+pureList :: a -> [a]+pureList a = a : []
+ test/golden/GoldenSafeHead.hs view
@@ -0,0 +1,2 @@+safeHead :: [x] -> Maybe x+safeHead = _
+ test/golden/GoldenSafeHead.hs.expected view
@@ -0,0 +1,3 @@+safeHead :: [x] -> Maybe x+safeHead [] = Nothing+safeHead (x : _) = Just x
+ test/golden/GoldenShow.hs view
@@ -0,0 +1,2 @@+showMe :: Show a => a -> String+showMe = _
+ test/golden/GoldenShow.hs.expected view
@@ -0,0 +1,2 @@+showMe :: Show a => a -> String+showMe = show
+ test/golden/GoldenShowCompose.hs view
@@ -0,0 +1,2 @@+showCompose :: Show a => (b -> a) -> b -> String+showCompose = _
+ test/golden/GoldenShowCompose.hs.expected view
@@ -0,0 +1,2 @@+showCompose :: Show a => (b -> a) -> b -> String+showCompose fba = show . fba
+ test/golden/GoldenShowMapChar.hs view
@@ -0,0 +1,2 @@+test :: Show a => a -> (String -> b) -> b+test = _
+ test/golden/GoldenShowMapChar.hs.expected view
@@ -0,0 +1,2 @@+test :: Show a => a -> (String -> b) -> b+test a fl_cb = fl_cb (show a)
+ test/golden/GoldenSuperclass.hs view
@@ -0,0 +1,8 @@+class Super a where+    super :: a++class Super a => Sub a++blah :: Sub a => a+blah = _+
+ test/golden/GoldenSuperclass.hs.expected view
@@ -0,0 +1,8 @@+class Super a where+    super :: a++class Super a => Sub a++blah :: Sub a => a+blah = super+
+ test/golden/GoldenSwap.hs view
@@ -0,0 +1,2 @@+swap :: (a, b) -> (b, a)+swap = _
+ test/golden/GoldenSwap.hs.expected view
@@ -0,0 +1,2 @@+swap :: (a, b) -> (b, a)+swap (a, b) = (b, a)
+ test/golden/GoldenSwapMany.hs view
@@ -0,0 +1,2 @@+swapMany :: (a, b, c, d, e) -> (e, d, c, b, a)+swapMany = _
+ test/golden/GoldenSwapMany.hs.expected view
@@ -0,0 +1,2 @@+swapMany :: (a, b, c, d, e) -> (e, d, c, b, a)+swapMany (a, b, c, d, e) = (e, d, c, b, a)
+ test/golden/KnownBigSemigroup.hs view
@@ -0,0 +1,7 @@+import Data.Monoid++data Big a = Big [Bool] (Sum Int) String (Endo a) Any++instance Semigroup (Big a) where+  (<>) = _+
+ test/golden/KnownBigSemigroup.hs.expected view
@@ -0,0 +1,9 @@+import Data.Monoid++data Big a = Big [Bool] (Sum Int) String (Endo a) Any++instance Semigroup (Big a) where+  (<>) (Big l_b7 si8 l_c9 ea10 a11) (Big l_b si l_c ea a)+    = Big+        (l_b7 <> l_b) (si8 <> si) (l_c9 <> l_c) (ea10 <> ea) (a11 <> a)+
+ test/golden/KnownCounterfactualSemigroup.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE UndecidableInstances #-}++data Semi = Semi [String] Int++instance Semigroup Int => Semigroup Semi where+  (<>) = _+
+ test/golden/KnownCounterfactualSemigroup.hs.expected view
@@ -0,0 +1,8 @@+{-# LANGUAGE UndecidableInstances #-}++data Semi = Semi [String] Int++instance Semigroup Int => Semigroup Semi where+  (<>) (Semi l_l_c5 i6) (Semi l_l_c i)+    = Semi (l_l_c5 <> l_l_c) (i6 <> i)+
+ test/golden/KnownDestructedSemigroup.hs view
@@ -0,0 +1,5 @@+data Test a = Test [a]++instance Semigroup (Test a) where+  Test a <> Test c = _+
+ test/golden/KnownDestructedSemigroup.hs.expected view
@@ -0,0 +1,5 @@+data Test a = Test [a]++instance Semigroup (Test a) where+  (<>) (Test a) (Test c) = Test (a <> c)+
+ test/golden/KnownMissingMonoid.hs view
@@ -0,0 +1,8 @@+data Mono a = Monoid [String] a++instance Semigroup (Mono a) where+  (<>) = undefined++instance Monoid (Mono a) where+  mempty = _+
+ test/golden/KnownMissingMonoid.hs.expected view
@@ -0,0 +1,8 @@+data Mono a = Monoid [String] a++instance Semigroup (Mono a) where+  (<>) = undefined++instance Monoid (Mono a) where+  mempty = Monoid mempty _+
+ test/golden/KnownMissingSemigroup.hs view
@@ -0,0 +1,5 @@+data Semi = Semi [String] Int++instance Semigroup Semi where+  (<>) = _+
+ test/golden/KnownMissingSemigroup.hs.expected view
@@ -0,0 +1,5 @@+data Semi = Semi [String] Int++instance Semigroup Semi where+  (<>) (Semi l_l_c4 i5) (Semi l_l_c i) = Semi (l_l_c4 <> l_l_c) _+
+ test/golden/KnownModuleInstanceSemigroup.hs view
@@ -0,0 +1,11 @@+data Foo = Foo++instance Semigroup Foo where+  (<>) _ _ = Foo+++data Bar = Bar Foo Foo++instance Semigroup Bar where+  (<>) = _+
+ test/golden/KnownModuleInstanceSemigroup.hs.expected view
@@ -0,0 +1,11 @@+data Foo = Foo++instance Semigroup Foo where+  (<>) _ _ = Foo+++data Bar = Bar Foo Foo++instance Semigroup Bar where+  (<>) (Bar f4 f5) (Bar f f3) = Bar (f4 <> f) (f5 <> f3)+
+ test/golden/KnownMonoid.hs view
@@ -0,0 +1,8 @@+data Mono = Monoid [String]++instance Semigroup Mono where+  (<>) = undefined++instance Monoid Mono where+  mempty = _+
+ test/golden/KnownMonoid.hs.expected view
@@ -0,0 +1,8 @@+data Mono = Monoid [String]++instance Semigroup Mono where+  (<>) = undefined++instance Monoid Mono where+  mempty = Monoid mempty+
+ test/golden/KnownPolyMonoid.hs view
@@ -0,0 +1,8 @@+data Mono a = Monoid [String] a++instance Semigroup (Mono a) where+  (<>) = undefined++instance Monoid a => Monoid (Mono a) where+  mempty = _+
+ test/golden/KnownPolyMonoid.hs.expected view
@@ -0,0 +1,8 @@+data Mono a = Monoid [String] a++instance Semigroup (Mono a) where+  (<>) = undefined++instance Monoid a => Monoid (Mono a) where+  mempty = Monoid mempty mempty+
+ test/golden/KnownThetaSemigroup.hs view
@@ -0,0 +1,5 @@+data Semi a = Semi a++instance Semigroup a => Semigroup (Semi a) where+  (<>) = _+
+ test/golden/KnownThetaSemigroup.hs.expected view
@@ -0,0 +1,5 @@+data Semi a = Semi a++instance Semigroup a => Semigroup (Semi a) where+  (<>) (Semi a4) (Semi a) = Semi (a4 <> a)+
+ test/golden/LayoutBind.hs view
@@ -0,0 +1,6 @@+test :: Bool -> IO ()+test b = do+  putStrLn "hello"+  _+  pure ()+
+ test/golden/LayoutBind.hs.expected view
@@ -0,0 +1,8 @@+test :: Bool -> IO ()+test b = do+  putStrLn "hello"+  case b of+    False -> _+    True -> _+  pure ()+
+ test/golden/LayoutDollarApp.hs view
@@ -0,0 +1,3 @@+test :: Bool -> Bool+test b = id $ _+
+ test/golden/LayoutDollarApp.hs.expected view
@@ -0,0 +1,5 @@+test :: Bool -> Bool+test b = id $ (case b of+   False -> _+   True -> _)+
+ test/golden/LayoutLam.hs view
@@ -0,0 +1,3 @@+test :: Bool -> Bool+test = \b -> _+
+ test/golden/LayoutLam.hs.expected view
@@ -0,0 +1,5 @@+test :: Bool -> Bool+test = \b -> case b of+  False -> _+  True -> _+
+ test/golden/LayoutOpApp.hs view
@@ -0,0 +1,2 @@+test :: Bool -> Bool+test b = True && _
+ test/golden/LayoutOpApp.hs.expected view
@@ -0,0 +1,4 @@+test :: Bool -> Bool+test b = True && (case b of+   False -> _+   True -> _)
+ test/golden/LayoutSplitClass.hs view
@@ -0,0 +1,4 @@+class Test a where+  test :: Bool -> a+  test x = _+
+ test/golden/LayoutSplitClass.hs.expected view
@@ -0,0 +1,5 @@+class Test a where+  test :: Bool -> a+  test False = _+  test True = _+
+ test/golden/LayoutSplitGuard.hs view
@@ -0,0 +1,3 @@+test :: Bool -> Bool -> Bool+test a b+  | a = _
+ test/golden/LayoutSplitGuard.hs.expected view
@@ -0,0 +1,5 @@+test :: Bool -> Bool -> Bool+test a b+  | a = (case b of+   False -> _+   True -> _)
+ test/golden/LayoutSplitIn.hs view
@@ -0,0 +1,5 @@+test :: a+test =+  let a = (1,"bbb")+   in _+
+ test/golden/LayoutSplitIn.hs.expected view
@@ -0,0 +1,5 @@+test :: a+test =+  let a = (1,"bbb")+   in case a of { (i, l_c) -> _ }+
+ test/golden/LayoutSplitLet.hs view
@@ -0,0 +1,6 @@+test :: a+test =+  let t :: Bool -> a+      t b = _+   in _+
+ test/golden/LayoutSplitLet.hs.expected view
@@ -0,0 +1,7 @@+test :: a+test =+  let t :: Bool -> a+      t False = _+      t True = _+   in _+
+ test/golden/LayoutSplitPatSyn.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE PatternSynonyms #-}++pattern JustSingleton :: a -> Maybe [a]+pattern JustSingleton a <- Just [a]+++test :: Maybe [Bool] -> Maybe Bool+test (JustSingleton a) = _++
+ test/golden/LayoutSplitPatSyn.hs.expected view
@@ -0,0 +1,11 @@+{-# LANGUAGE PatternSynonyms #-}++pattern JustSingleton :: a -> Maybe [a]+pattern JustSingleton a <- Just [a]+++test :: Maybe [Bool] -> Maybe Bool+test (JustSingleton False) = _+test (JustSingleton True) = _++
+ test/golden/LayoutSplitPattern.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE PatternSynonyms #-}++pattern Blah :: a -> Maybe a+pattern Blah a = Just a++test :: Maybe Bool -> a+test (Blah a) = _+
+ test/golden/LayoutSplitPattern.hs.expected view
@@ -0,0 +1,9 @@+{-# LANGUAGE PatternSynonyms #-}++pattern Blah :: a -> Maybe a+pattern Blah a = Just a++test :: Maybe Bool -> a+test (Blah False) = _+test (Blah True) = _+
+ test/golden/LayoutSplitViewPat.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE ViewPatterns #-}++splitLookup :: [(Int, String)] -> String+splitLookup (lookup 5 -> a) = _+
+ test/golden/LayoutSplitViewPat.hs.expected view
@@ -0,0 +1,6 @@+{-# LANGUAGE ViewPatterns #-}++splitLookup :: [(Int, String)] -> String+splitLookup (lookup 5 -> Nothing) = _+splitLookup (lookup 5 -> (Just l_c)) = _+
+ test/golden/LayoutSplitWhere.hs view
@@ -0,0 +1,12 @@+data A = A | B | C++some :: A -> IO ()+some a = do+    foo+    bar a+  where+      foo = putStrLn "Hi"++      bar :: A -> IO ()+      bar x = _+
+ test/golden/LayoutSplitWhere.hs.expected view
@@ -0,0 +1,14 @@+data A = A | B | C++some :: A -> IO ()+some a = do+    foo+    bar a+  where+      foo = putStrLn "Hi"++      bar :: A -> IO ()+      bar A = _+      bar B = _+      bar C = _+
+ test/golden/MessageForallA.hs view
@@ -0,0 +1,2 @@+test :: a+test = _
+ test/golden/NewtypeRecord.hs view
@@ -0,0 +1,7 @@+newtype MyRecord a = Record+    { field1 :: a+    }++blah :: (a -> Int) -> a -> MyRecord a+blah = _+
+ test/golden/NewtypeRecord.hs.expected view
@@ -0,0 +1,7 @@+newtype MyRecord a = Record+    { field1 :: a+    }++blah :: (a -> Int) -> a -> MyRecord a+blah _ = Record+
+ test/golden/ProvideAlreadyDestructed.hs view
@@ -0,0 +1,9 @@+foo :: Bool -> ()+foo x =+  if True+    then+      case x of+        True  -> _+        False -> ()+    else+      _
+ test/golden/ProvideLocalHyOnly.hs view
@@ -0,0 +1,2 @@+basilisk :: Monoid Bool => a+basilisk = _
+ test/golden/RecordCon.hs view
@@ -0,0 +1,9 @@+data MyRecord a = Record+    { field1 :: a+    , field2 :: Int+    }++blah :: (a -> Int) -> a -> MyRecord a+blah = _++
+ test/golden/RecordCon.hs.expected view
@@ -0,0 +1,9 @@+data MyRecord a = Record+    { field1 :: a+    , field2 :: Int+    }++blah :: (a -> Int) -> a -> MyRecord a+blah fai a = Record {field1 = a, field2 = fai a}++
+ test/golden/RefineCon.hs view
@@ -0,0 +1,3 @@+test :: ((), (b, c), d)+test = _+
+ test/golden/RefineCon.hs.expected view
@@ -0,0 +1,3 @@+test :: ((), (b, c), d)+test = (_, _, _)+
+ test/golden/RefineGADT.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE GADTs #-}++data GADT a where+  One :: (b -> Int) -> GADT Int+  Two :: GADT Bool++test :: z -> GADT Int+test = _+
+ test/golden/RefineGADT.hs.expected view
@@ -0,0 +1,9 @@+{-# LANGUAGE GADTs #-}++data GADT a where+  One :: (b -> Int) -> GADT Int+  Two :: GADT Bool++test :: z -> GADT Int+test z = One (\ b -> _)+
+ test/golden/RefineIntro.hs view
@@ -0,0 +1,2 @@+test :: a -> Either a b+test = _
+ test/golden/RefineIntro.hs.expected view
@@ -0,0 +1,2 @@+test :: a -> Either a b+test a = _
+ test/golden/RefineReader.hs view
@@ -0,0 +1,5 @@+newtype Reader r a = Reader (r -> a)++test :: b -> Reader r a+test = _+
+ test/golden/RefineReader.hs.expected view
@@ -0,0 +1,5 @@+newtype Reader r a = Reader (r -> a)++test :: b -> Reader r a+test b = Reader (\ r -> _)+
+ test/golden/SplitPattern.hs view
@@ -0,0 +1,8 @@+data ADT = One | Two Int | Three | Four Bool ADT | Five++case_split :: ADT -> Int+case_split One        = _+case_split (Two i)    = _+case_split Three      = _+case_split (Four b a) = _+case_split Five       = _
+ test/golden/SplitPattern.hs.expected view
@@ -0,0 +1,12 @@+data ADT = One | Two Int | Three | Four Bool ADT | Five++case_split :: ADT -> Int+case_split One        = _+case_split (Two i)    = _+case_split Three      = _+case_split (Four b One) = _+case_split (Four b (Two i)) = _+case_split (Four b Three) = _+case_split (Four b (Four b3 a4)) = _+case_split (Four b Five) = _+case_split Five       = _
+ test/golden/T1.hs view
@@ -0,0 +1,3 @@+fmapEither :: (a -> b) -> Either c a -> Either c b+fmapEither = _lalala+
+ test/golden/T2.hs view
@@ -0,0 +1,12 @@+eitherFmap :: (a -> b) -> Either e a -> Either e b+eitherFmap fa eab = _++global :: Bool+global = True++foo :: Int+foo  = _++dontSuggestLambdaCase :: Either a b -> Int+dontSuggestLambdaCase = _+
+ test/golden/T3.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE LambdaCase #-}++suggestHomomorphicLC :: Either a b -> Either a b+suggestHomomorphicLC = _++suggestLC :: Either a b -> Int+suggestLC = _+
+ test/golden/UseConLeft.hs view
@@ -0,0 +1,3 @@+test :: Either a b+test = _+
+ test/golden/UseConLeft.hs.expected view
@@ -0,0 +1,3 @@+test :: Either a b+test = Left _+
+ test/golden/UseConPair.hs view
@@ -0,0 +1,2 @@+test :: (a, b)+test = _
+ test/golden/UseConPair.hs.expected view
@@ -0,0 +1,2 @@+test :: (a, b)+test = (_, _)
+ test/golden/UseConRight.hs view
@@ -0,0 +1,3 @@+test :: Either a b+test = _+
+ test/golden/UseConRight.hs.expected view
@@ -0,0 +1,3 @@+test :: Either a b+test = Right _+
+ test/golden/hie.yaml view
@@ -0,0 +1,1 @@+cradle: {direct: {arguments: ["T1", "T2", "T3"]}}
+ test/golden/test.cabal view
@@ -0,0 +1,17 @@+name:                test+version:             0.1.0.0+-- synopsis:+-- description:+license:             BSD3+author:              Author name here+maintainer:          example@example.com+copyright:           2017 Author name here+category:            Web+build-type:          Simple+cabal-version:       >=1.10++library+  exposed-modules:     T1, T2+  build-depends:       base >= 4.7 && < 5+  default-language:    Haskell2010+  ghc-options:         -Wall -fwarn-unused-imports