From 3e235d6caf1b2801e272a22633671734fd07bb20 Mon Sep 17 00:00:00 2001 From: Mattia Manzati Date: Tue, 7 Apr 2026 13:11:16 +0200 Subject: [PATCH] feat: port lazy Promise Effect.sync diagnostic --- .../lazy-promise-in-effect-sync-0af7c0f.md | 7 + README.md | 1 + _packages/tsgo/src/metadata.json | 24 ++ .../diagnostics/effectDiagnosticMessages.json | 4 + internal/rules/lazy_promise_in_effect_sync.go | 65 ++++ internal/rules/rules.go | 1 + internal/typeparser/promise_type.go | 31 ++ internal/typeparser/type_parser.go | 1 + schema.json | 5 + shim/checker/extra-shim.json | 3 +- shim/checker/shim.go | 325 ++++++++++++++++++ shim/diagnostics/shim.go | 1 + .../lazyPromiseInEffectSync.errors.txt | 33 ++ ...Sync.flows.lazyPromiseInEffectSync.mermaid | 16 + .../lazyPromiseInEffectSync.flows.txt | 1 + .../lazyPromiseInEffectSync.layers.txt | 1 + .../lazyPromiseInEffectSync.pipings.txt | 99 ++++++ .../lazyPromiseInEffectSync.quickfixes.txt | 23 ++ ...lazyPromiseInEffectSync_preview.errors.txt | 15 + ...ws.lazyPromiseInEffectSync_preview.mermaid | 4 + .../lazyPromiseInEffectSync_preview.flows.txt | 1 + ...lazyPromiseInEffectSync_preview.layers.txt | 1 + ...azyPromiseInEffectSync_preview.pipings.txt | 29 ++ ...PromiseInEffectSync_preview.quickfixes.txt | 13 + .../lazyPromiseInEffectSync.errors.txt | 33 ++ ...Sync.flows.lazyPromiseInEffectSync.mermaid | 16 + .../lazyPromiseInEffectSync.flows.txt | 1 + .../lazyPromiseInEffectSync.layers.txt | 1 + .../lazyPromiseInEffectSync.pipings.txt | 99 ++++++ .../lazyPromiseInEffectSync.quickfixes.txt | 23 ++ ...lazyPromiseInEffectSync_preview.errors.txt | 15 + ...ws.lazyPromiseInEffectSync_preview.mermaid | 4 + .../lazyPromiseInEffectSync_preview.flows.txt | 1 + ...lazyPromiseInEffectSync_preview.layers.txt | 1 + ...azyPromiseInEffectSync_preview.pipings.txt | 29 ++ ...PromiseInEffectSync_preview.quickfixes.txt | 13 + .../effect-v3/lazyPromiseInEffectSync.ts | 20 ++ .../lazyPromiseInEffectSync_preview.ts | 5 + .../effect-v4/lazyPromiseInEffectSync.ts | 20 ++ .../lazyPromiseInEffectSync_preview.ts | 5 + 40 files changed, 989 insertions(+), 1 deletion(-) create mode 100644 .changeset/lazy-promise-in-effect-sync-0af7c0f.md create mode 100644 internal/rules/lazy_promise_in_effect_sync.go create mode 100644 internal/typeparser/promise_type.go create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.errors.txt create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.flows.txt create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.layers.txt create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.pipings.txt create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.quickfixes.txt create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.errors.txt create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.flows.txt create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.layers.txt create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.pipings.txt create mode 100644 testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.quickfixes.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.errors.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.flows.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.layers.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.pipings.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.quickfixes.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.errors.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.flows.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.layers.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.pipings.txt create mode 100644 testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.quickfixes.txt create mode 100644 testdata/tests/effect-v3/lazyPromiseInEffectSync.ts create mode 100644 testdata/tests/effect-v3/lazyPromiseInEffectSync_preview.ts create mode 100644 testdata/tests/effect-v4/lazyPromiseInEffectSync.ts create mode 100644 testdata/tests/effect-v4/lazyPromiseInEffectSync_preview.ts diff --git a/.changeset/lazy-promise-in-effect-sync-0af7c0f.md b/.changeset/lazy-promise-in-effect-sync-0af7c0f.md new file mode 100644 index 0000000..67d50cf --- /dev/null +++ b/.changeset/lazy-promise-in-effect-sync-0af7c0f.md @@ -0,0 +1,7 @@ +--- +"@effect/tsgo": minor +--- + +Add the `lazyPromiseInEffectSync` diagnostic for `Effect.sync` thunks that return the global `Promise` type. + +This ports the upstream language-service behavior to the Go implementation, including v3/v4 examples, baselines, and exact Promise detection via TypeScriptGo checker shims. diff --git a/README.md b/README.md index 92a9b25..3690adf 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Some diagnostics are off by default or have a default severity of suggestion, bu globalErrorInEffectCatch⚠️Warns when catch callbacks return global Error type instead of typed errors✓✓ globalErrorInEffectFailure⚠️Warns when the global Error type is used in an Effect failure channel✓✓ layerMergeAllWithDependencies⚠️🔧Detects interdependencies in Layer.mergeAll calls where one layer provides a service that another layer requires✓✓ + lazyPromiseInEffectSync⚠️Warns when Effect.sync lazily returns a Promise instead of using an async Effect constructor✓✓ leakingRequirements💡Detects implementation services leaked in service methods✓✓ multipleEffectProvide⚠️🔧Warns against chaining Effect.provide calls which can cause service lifecycle issues✓✓ returnEffectInGen💡🔧Warns when returning an Effect in a generator causes nested Effect<Effect<...>>✓✓ diff --git a/_packages/tsgo/src/metadata.json b/_packages/tsgo/src/metadata.json index d14f7db..4818acf 100644 --- a/_packages/tsgo/src/metadata.json +++ b/_packages/tsgo/src/metadata.json @@ -593,6 +593,30 @@ ] } }, + { + "name": "lazyPromiseInEffectSync", + "group": "antipattern", + "description": "Warns when Effect.sync lazily returns a Promise instead of using an async Effect constructor", + "defaultSeverity": "warning", + "fixable": false, + "supportedEffect": [ + "v3", + "v4" + ], + "codes": [ + 377082 + ], + "preview": { + "sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.sync(() =\u003e Promise.resolve(1))\n", + "diagnostics": [ + { + "start": 68, + "end": 92, + "text": "This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync)" + } + ] + } + }, { "name": "leakingRequirements", "group": "antipattern", diff --git a/internal/diagnostics/effectDiagnosticMessages.json b/internal/diagnostics/effectDiagnosticMessages.json index ddf5789..52f5602 100644 --- a/internal/diagnostics/effectDiagnosticMessages.json +++ b/internal/diagnostics/effectDiagnosticMessages.json @@ -299,6 +299,10 @@ "category": "Warning", "code": 377080 }, + "This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync)": { + "category": "Warning", + "code": 377082 + }, "This code declares an async function, consider representing this async control flow with Effect values and `Effect.gen`. effect(asyncFunction)": { "category": "Warning", "code": 377081 diff --git a/internal/rules/lazy_promise_in_effect_sync.go b/internal/rules/lazy_promise_in_effect_sync.go new file mode 100644 index 0000000..05d9c9d --- /dev/null +++ b/internal/rules/lazy_promise_in_effect_sync.go @@ -0,0 +1,65 @@ +package rules + +import ( + "github.com/effect-ts/tsgo/etscore" + "github.com/effect-ts/tsgo/internal/rule" + "github.com/effect-ts/tsgo/internal/typeparser" + "github.com/microsoft/typescript-go/shim/ast" + "github.com/microsoft/typescript-go/shim/checker" + tsdiag "github.com/microsoft/typescript-go/shim/diagnostics" + "github.com/microsoft/typescript-go/shim/scanner" +) + +var LazyPromiseInEffectSync = rule.Rule{ + Name: "lazyPromiseInEffectSync", + Group: "antipattern", + Description: "Warns when Effect.sync lazily returns a Promise instead of using an async Effect constructor", + DefaultSeverity: etscore.SeverityWarning, + SupportedEffect: []string{"v3", "v4"}, + Codes: []int32{ + tsdiag.This_Effect_sync_thunk_returns_a_Promise_Use_Effect_promise_or_Effect_tryPromise_to_represent_async_work_effect_lazyPromiseInEffectSync.Code(), + }, + Run: func(ctx *rule.Context) []*ast.Diagnostic { + var diags []*ast.Diagnostic + var walk ast.Visitor + walk = func(node *ast.Node) bool { + if node == nil { + return false + } + + if node.Kind == ast.KindCallExpression { + call := node.AsCallExpression() + if call != nil && ctx.TypeParser.IsNodeReferenceToEffectModuleApi(call.Expression, "sync") && call.Arguments != nil && len(call.Arguments.Nodes) > 0 { + lazyArg := call.Arguments.Nodes[0] + lazyArgType := ctx.TypeParser.GetTypeAtLocation(lazyArg) + if lazyArgType != nil && thunkReturnsPromise(ctx.Checker, ctx.TypeParser, lazyArgType) { + diags = append(diags, ctx.NewDiagnostic( + ctx.SourceFile, + scanner.GetErrorRangeForNode(ctx.SourceFile, lazyArg), + tsdiag.This_Effect_sync_thunk_returns_a_Promise_Use_Effect_promise_or_Effect_tryPromise_to_represent_async_work_effect_lazyPromiseInEffectSync, + nil, + )) + } + } + } + + node.ForEachChild(walk) + return false + } + + walk(ctx.SourceFile.AsNode()) + return diags + }, +} + +func thunkReturnsPromise(c *checker.Checker, tp *typeparser.TypeParser, lazyArgType *checker.Type) bool { + for _, member := range tp.UnrollUnionMembers(lazyArgType) { + for _, signature := range c.GetSignaturesOfType(member, checker.SignatureKindCall) { + returnType := c.GetReturnTypeOfSignature(signature) + if tp.PromiseType(returnType) != nil { + return true + } + } + } + return false +} diff --git a/internal/rules/rules.go b/internal/rules/rules.go index e55dcd9..1e40ec5 100644 --- a/internal/rules/rules.go +++ b/internal/rules/rules.go @@ -60,6 +60,7 @@ var All = []rule.Rule{ SchemaUnionOfLiterals, MissingEffectServiceDependency, LeakingRequirements, + LazyPromiseInEffectSync, InstanceOfSchema, GenericEffectServices, OverriddenSchemaConstructor, diff --git a/internal/typeparser/promise_type.go b/internal/typeparser/promise_type.go new file mode 100644 index 0000000..2ff71cd --- /dev/null +++ b/internal/typeparser/promise_type.go @@ -0,0 +1,31 @@ +package typeparser + +import ( + "github.com/microsoft/typescript-go/shim/checker" +) + +// PromiseType returns t when it is the global Promise type or a reference to it. +// It intentionally does not match arbitrary thenables or PromiseLike values. +func (tp *TypeParser) PromiseType(t *checker.Type) *checker.Type { + if tp == nil || tp.checker == nil || t == nil { + return nil + } + + return Cached(&tp.links.PromiseType, t, func() *checker.Type { + getGlobalPromiseTypeChecked := checker.Checker_getGlobalPromiseTypeChecked(tp.checker) + if getGlobalPromiseTypeChecked == nil { + return nil + } + + globalPromiseType := getGlobalPromiseTypeChecked() + if globalPromiseType == nil || globalPromiseType == checker.Checker_emptyGenericType(tp.checker) { + return nil + } + + if checker.Checker_isReferenceToType(tp.checker, t, globalPromiseType) || t == globalPromiseType { + return t + } + + return nil + }) +} diff --git a/internal/typeparser/type_parser.go b/internal/typeparser/type_parser.go index 43f4cad..242d051 100644 --- a/internal/typeparser/type_parser.go +++ b/internal/typeparser/type_parser.go @@ -33,6 +33,7 @@ type EffectLinks struct { EffectSchemaTypes core.LinkStore[*checker.Type, *SchemaTypes] IsScopeType core.LinkStore[*checker.Type, bool] IsPipeableType core.LinkStore[*checker.Type, bool] + PromiseType core.LinkStore[*checker.Type, *checker.Type] IsGlobalErrorType core.LinkStore[*checker.Type, bool] IsYieldableErrorType core.LinkStore[*checker.Type, bool] diff --git a/schema.json b/schema.json index 5fc555a..979083a 100644 --- a/schema.json +++ b/schema.json @@ -1834,6 +1834,11 @@ "default": "warning", "description": "Detects interdependencies in Layer.mergeAll calls where one layer provides a service that another layer requires" }, + "lazyPromiseInEffectSync": { + "$ref": "#/definitions/effectLanguageServicePluginSeverityDefinition", + "default": "warning", + "description": "Warns when Effect.sync lazily returns a Promise instead of using an async Effect constructor" + }, "leakingRequirements": { "$ref": "#/definitions/effectLanguageServicePluginSeverityDefinition", "default": "suggestion", diff --git a/shim/checker/extra-shim.json b/shim/checker/extra-shim.json index 1601e60..4e7d792 100644 --- a/shim/checker/extra-shim.json +++ b/shim/checker/extra-shim.json @@ -1,8 +1,9 @@ { "ExtraMethods": { - "Checker": ["isTypeAssignableTo", "isArrayType", "isReadonlyArrayType", "getIndexInfosOfType", "getTypeArguments", "getLiteralTypeFromProperty", "getSymbolIfSameReference", "getSymbolOfDeclaration", "isSignatureAssignableTo", "newFunctionType", "newCallSignature"] + "Checker": ["isTypeAssignableTo", "isArrayType", "isReadonlyArrayType", "getIndexInfosOfType", "getTypeArguments", "getLiteralTypeFromProperty", "getSymbolIfSameReference", "getSymbolOfDeclaration", "isSignatureAssignableTo", "isReferenceToType", "newFunctionType", "newCallSignature"] }, "ExtraFields": { + "Checker": ["getGlobalPromiseTypeChecked", "emptyGenericType"], "IndexInfo": ["keyType", "valueType"] } } diff --git a/shim/checker/shim.go b/shim/checker/shim.go index 2487867..a046309 100644 --- a/shim/checker/shim.go +++ b/shim/checker/shim.go @@ -6,9 +6,14 @@ package checker import "context" import "github.com/microsoft/typescript-go/internal/ast" import "github.com/microsoft/typescript-go/internal/checker" +import "github.com/microsoft/typescript-go/internal/collections" +import "github.com/microsoft/typescript-go/internal/core" import "github.com/microsoft/typescript-go/internal/diagnostics" +import "github.com/microsoft/typescript-go/internal/evaluator" +import "github.com/microsoft/typescript-go/internal/jsnum" import "github.com/microsoft/typescript-go/internal/nodebuilder" import "github.com/microsoft/typescript-go/internal/printer" +import "github.com/microsoft/typescript-go/internal/scanner" import "sync" import "unsafe" @@ -74,6 +79,8 @@ const CheckModeSkipContextSensitive = checker.CheckModeSkipContextSensitive const CheckModeSkipGenericFunctions = checker.CheckModeSkipGenericFunctions const CheckModeTypeOnly = checker.CheckModeTypeOnly type Checker = checker.Checker +//go:linkname Checker_isReferenceToType github.com/microsoft/typescript-go/internal/checker.(*Checker).isReferenceToType +func Checker_isReferenceToType(recv *checker.Checker, t *checker.Type, target *checker.Type) bool //go:linkname Checker_getSymbolIfSameReference github.com/microsoft/typescript-go/internal/checker.(*Checker).getSymbolIfSameReference func Checker_getSymbolIfSameReference(recv *checker.Checker, s1 *ast.Symbol, s2 *ast.Symbol) *ast.Symbol //go:linkname Checker_getSymbolOfDeclaration github.com/microsoft/typescript-go/internal/checker.(*Checker).getSymbolOfDeclaration @@ -96,6 +103,324 @@ func Checker_newCallSignature(recv *checker.Checker, typeParameters []*checker.T func Checker_isTypeAssignableTo(recv *checker.Checker, source *checker.Type, target *checker.Type) bool //go:linkname Checker_isSignatureAssignableTo github.com/microsoft/typescript-go/internal/checker.(*Checker).isSignatureAssignableTo func Checker_isSignatureAssignableTo(recv *checker.Checker, source *checker.Signature, target *checker.Signature, ignoreReturnTypes bool) bool +type extra_Checker struct { + id uint32 + program checker.Program + compilerOptions *core.CompilerOptions + files []*ast.SourceFile + fileIndexMap map[*ast.SourceFile]int + compareSymbols func(*ast.Symbol, *ast.Symbol) int + compareSymbolChains func([]*ast.Symbol, []*ast.Symbol) int + TypeCount uint32 + SymbolCount uint32 + TotalInstantiationCount uint32 + EffectLinks any + instantiationCount uint32 + instantiationDepth uint32 + inlineLevel int + currentNode *ast.Node + varianceTypeParameter *checker.Type + languageVersion core.ScriptTarget + moduleKind core.ModuleKind + moduleResolutionKind core.ModuleResolutionKind + isInferencePartiallyBlocked bool + legacyDecorators bool + emitStandardClassFields bool + strictNullChecks bool + strictFunctionTypes bool + strictBindCallApply bool + strictPropertyInitialization bool + strictBuiltinIteratorReturn bool + noImplicitAny bool + noImplicitThis bool + useUnknownInCatchVariables bool + exactOptionalPropertyTypes bool + canCollectSymbolAliasAccessibilityData bool + wasCanceled bool + saveDeferredDiagnostics bool + arrayVariances []checker.VarianceFlags + globals ast.SymbolTable + evaluate evaluator.Evaluator + stringLiteralTypes map[string]*checker.Type + numberLiteralTypes map[jsnum.Number]*checker.Type + bigintLiteralTypes map[jsnum.PseudoBigInt]*checker.Type + enumLiteralTypes map[checker.EnumLiteralKey]*checker.Type + indexedAccessTypes map[checker.CacheHashKey]*checker.Type + templateLiteralTypes map[checker.CacheHashKey]*checker.Type + stringMappingTypes map[checker.StringMappingKey]*checker.Type + uniqueESSymbolTypes map[*ast.Symbol]*checker.Type + thisExpandoKinds map[*ast.Symbol]int32 + thisExpandoLocations map[*ast.Symbol]*ast.Node + subtypeReductionCache map[checker.CacheHashKey][]*checker.Type + cachedTypes map[checker.CachedTypeKey]*checker.Type + cachedSignatures map[checker.CachedSignatureKey]*checker.Signature + undefinedProperties map[string]*ast.Symbol + narrowedTypes map[checker.NarrowedTypeKey]*checker.Type + assignmentReducedTypes map[checker.AssignmentReducedKey]*checker.Type + discriminatedContextualTypes map[checker.DiscriminatedContextualTypeKey]*checker.Type + instantiationExpressionTypes map[checker.InstantiationExpressionKey]*checker.Type + substitutionTypes map[checker.SubstitutionTypeKey]*checker.Type + reverseMappedCache map[checker.ReverseMappedTypeKey]*checker.Type + reverseHomomorphicMappedCache map[checker.ReverseMappedTypeKey]*checker.Type + iterationTypesCache map[checker.IterationTypesKey]checker.IterationTypes + markerTypes collections.Set[*checker.Type] + undefinedSymbol *ast.Symbol + argumentsSymbol *ast.Symbol + requireSymbol *ast.Symbol + unknownSymbol *ast.Symbol + unresolvedSymbols map[string]*ast.Symbol + errorTypes map[checker.CacheHashKey]*checker.Type + moduleSymbols map[*ast.Node]*ast.Symbol + globalThisSymbol *ast.Symbol + resolveName func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol + resolveNameForSymbolSuggestion func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol + tupleTypes map[checker.CacheHashKey]*checker.Type + unionTypes map[checker.CacheHashKey]*checker.Type + unionOfUnionTypes map[checker.UnionOfUnionKey]*checker.Type + intersectionTypes map[checker.CacheHashKey]*checker.Type + propertiesTypes map[checker.PropertiesTypesKey]*checker.Type + diagnostics ast.DiagnosticsCollection + suggestionDiagnostics ast.DiagnosticsCollection + symbolPool core.Pool[ast.Symbol] + signaturePool core.Pool[checker.Signature] + indexInfoPool core.Pool[checker.IndexInfo] + mergedSymbols map[*ast.Symbol]*ast.Symbol + factory ast.NodeFactory + nodeLinks core.LinkStore[*ast.Node, checker.NodeLinks] + signatureLinks core.LinkStore[*ast.Node, checker.SignatureLinks] + symbolNodeLinks core.LinkStore[*ast.Node, checker.SymbolNodeLinks] + typeNodeLinks core.LinkStore[*ast.Node, checker.TypeNodeLinks] + enumMemberLinks core.LinkStore[*ast.Node, checker.EnumMemberLinks] + assertionLinks core.LinkStore[*ast.Node, checker.AssertionLinks] + arrayLiteralLinks core.LinkStore[*ast.Node, checker.ArrayLiteralLinks] + switchStatementLinks core.LinkStore[*ast.Node, checker.SwitchStatementLinks] + jsxElementLinks core.LinkStore[*ast.Node, checker.JsxElementLinks] + symbolReferenceLinks core.LinkStore[*ast.Symbol, checker.SymbolReferenceLinks] + valueSymbolLinks core.LinkStore[*ast.Symbol, checker.ValueSymbolLinks] + mappedSymbolLinks core.LinkStore[*ast.Symbol, checker.MappedSymbolLinks] + deferredSymbolLinks core.LinkStore[*ast.Symbol, checker.DeferredSymbolLinks] + aliasSymbolLinks core.LinkStore[*ast.Symbol, checker.AliasSymbolLinks] + moduleSymbolLinks core.LinkStore[*ast.Symbol, checker.ModuleSymbolLinks] + lateBoundLinks core.LinkStore[*ast.Symbol, checker.LateBoundLinks] + exportTypeLinks core.LinkStore[*ast.Symbol, checker.ExportTypeLinks] + membersAndExportsLinks core.LinkStore[*ast.Symbol, checker.MembersAndExportsLinks] + typeAliasLinks core.LinkStore[*ast.Symbol, checker.TypeAliasLinks] + declaredTypeLinks core.LinkStore[*ast.Symbol, checker.DeclaredTypeLinks] + spreadLinks core.LinkStore[*ast.Symbol, checker.SpreadLinks] + varianceLinks core.LinkStore[*ast.Symbol, checker.VarianceLinks] + ReverseMappedSymbolLinks core.LinkStore[*ast.Symbol, checker.ReverseMappedSymbolLinks] + markedAssignmentSymbolLinks core.LinkStore[*ast.Symbol, checker.MarkedAssignmentSymbolLinks] + symbolContainerLinks core.LinkStore[*ast.Symbol, checker.ContainingSymbolLinks] + sourceFileLinks core.LinkStore[*ast.SourceFile, checker.SourceFileLinks] + regExpScanner *scanner.Scanner + patternForType map[*checker.Type]*ast.Node + contextFreeTypes map[*ast.Node]*checker.Type + anyType *checker.Type + autoType *checker.Type + wildcardType *checker.Type + blockedStringType *checker.Type + errorType *checker.Type + unresolvedType *checker.Type + nonInferrableAnyType *checker.Type + intrinsicMarkerType *checker.Type + unknownType *checker.Type + undefinedType *checker.Type + undefinedWideningType *checker.Type + missingType *checker.Type + undefinedOrMissingType *checker.Type + optionalType *checker.Type + nullType *checker.Type + nullWideningType *checker.Type + stringType *checker.Type + numberType *checker.Type + bigintType *checker.Type + regularFalseType *checker.Type + falseType *checker.Type + regularTrueType *checker.Type + trueType *checker.Type + booleanType *checker.Type + esSymbolType *checker.Type + voidType *checker.Type + neverType *checker.Type + silentNeverType *checker.Type + implicitNeverType *checker.Type + unreachableNeverType *checker.Type + nonPrimitiveType *checker.Type + stringOrNumberType *checker.Type + stringNumberSymbolType *checker.Type + numberOrBigIntType *checker.Type + templateConstraintType *checker.Type + numericStringType *checker.Type + uniqueLiteralType *checker.Type + uniqueLiteralMapper *checker.TypeMapper + reliabilityFlags checker.RelationComparisonResult + reportUnreliableMapper *checker.TypeMapper + reportUnmeasurableMapper *checker.TypeMapper + restrictiveMapper *checker.TypeMapper + permissiveMapper *checker.TypeMapper + emptyObjectType *checker.Type + emptyJsxObjectType *checker.Type + emptyFreshJsxObjectType *checker.Type + emptyTypeLiteralType *checker.Type + unknownEmptyObjectType *checker.Type + unknownUnionType *checker.Type + emptyGenericType *checker.Type + anyFunctionType *checker.Type + noConstraintType *checker.Type + circularConstraintType *checker.Type + resolvingDefaultType *checker.Type + markerSuperType *checker.Type + markerSubType *checker.Type + markerOtherType *checker.Type + markerSuperTypeForCheck *checker.Type + markerSubTypeForCheck *checker.Type + noTypePredicate *checker.TypePredicate + anySignature *checker.Signature + unknownSignature *checker.Signature + resolvingSignature *checker.Signature + silentNeverSignature *checker.Signature + cachedArgumentsReferenced map[*ast.Node]bool + enumNumberIndexInfo *checker.IndexInfo + anyBaseTypeIndexInfo *checker.IndexInfo + patternAmbientModules []*ast.PatternAmbientModule + patternAmbientModuleAugmentations ast.SymbolTable + globalObjectType *checker.Type + globalFunctionType *checker.Type + globalCallableFunctionType *checker.Type + globalNewableFunctionType *checker.Type + globalArrayType *checker.Type + globalReadonlyArrayType *checker.Type + globalStringType *checker.Type + globalNumberType *checker.Type + globalBooleanType *checker.Type + globalRegExpType *checker.Type + globalThisType *checker.Type + anyArrayType *checker.Type + autoArrayType *checker.Type + anyReadonlyArrayType *checker.Type + deferredGlobalImportMetaExpressionType *checker.Type + contextualBindingPatterns []*ast.Node + emptyStringType *checker.Type + zeroType *checker.Type + zeroBigIntType *checker.Type + typeofType *checker.Type + typeResolutions []checker.TypeResolution + resolutionStart int + inVarianceComputation bool + apparentArgumentCount *int + lastGetCombinedNodeFlagsNode *ast.Node + lastGetCombinedNodeFlagsResult ast.NodeFlags + lastGetCombinedModifierFlagsNode *ast.Node + lastGetCombinedModifierFlagsResult ast.ModifierFlags + freeinferenceState *checker.InferenceState + freeFlowState *checker.FlowState + flowLoopCache map[checker.FlowLoopKey]*checker.Type + flowLoopStack []checker.FlowLoopInfo + sharedFlows []checker.SharedFlow + antecedentTypes []*checker.Type + flowAnalysisDisabled bool + flowInvocationCount int + flowTypeCache map[*ast.Node]*checker.Type + lastFlowNode *ast.FlowNode + lastFlowNodeReachable bool + flowNodeReachable map[*ast.FlowNode]bool + flowNodePostSuper map[*ast.FlowNode]bool + renamedBindingElementsInTypes []*ast.Node + contextualInfos []checker.ContextualInfo + inferenceContextInfos []checker.InferenceContextInfo + awaitedTypeStack []*checker.Type + reverseMappedSourceStack []*checker.Type + reverseMappedTargetStack []*checker.Type + reverseExpandingFlags checker.ExpandingFlags + freeRelater *checker.Relater + subtypeRelation *checker.Relation + strictSubtypeRelation *checker.Relation + assignableRelation *checker.Relation + comparableRelation *checker.Relation + identityRelation *checker.Relation + enumRelation map[checker.EnumRelationKey]checker.RelationComparisonResult + getGlobalESSymbolType func() *checker.Type + getGlobalBigIntType func() *checker.Type + getGlobalImportMetaType func() *checker.Type + getGlobalImportAttributesType func() *checker.Type + getGlobalImportAttributesTypeChecked func() *checker.Type + getGlobalNonNullableTypeAliasOrNil func() *ast.Symbol + getGlobalExtractSymbol func() *ast.Symbol + getGlobalDisposableType func() *checker.Type + getGlobalAsyncDisposableType func() *checker.Type + getGlobalAwaitedSymbol func() *ast.Symbol + getGlobalAwaitedSymbolOrNil func() *ast.Symbol + getGlobalNaNSymbolOrNil func() *ast.Symbol + getGlobalRecordSymbol func() *ast.Symbol + getGlobalTemplateStringsArrayType func() *checker.Type + getGlobalESSymbolConstructorSymbolOrNil func() *ast.Symbol + getGlobalESSymbolConstructorTypeSymbolOrNil func() *ast.Symbol + getGlobalImportCallOptionsType func() *checker.Type + getGlobalImportCallOptionsTypeChecked func() *checker.Type + getGlobalPromiseType func() *checker.Type + getGlobalPromiseTypeChecked func() *checker.Type + getGlobalPromiseLikeType func() *checker.Type + getGlobalPromiseConstructorSymbol func() *ast.Symbol + getGlobalPromiseConstructorSymbolOrNil func() *ast.Symbol + getGlobalOmitSymbol func() *ast.Symbol + getGlobalNoInferSymbolOrNil func() *ast.Symbol + getGlobalIteratorType func() *checker.Type + getGlobalIterableType func() *checker.Type + getGlobalIterableTypeChecked func() *checker.Type + getGlobalIterableIteratorType func() *checker.Type + getGlobalIterableIteratorTypeChecked func() *checker.Type + getGlobalIteratorObjectType func() *checker.Type + getGlobalGeneratorType func() *checker.Type + getGlobalAsyncIteratorType func() *checker.Type + getGlobalAsyncIterableType func() *checker.Type + getGlobalAsyncIterableTypeChecked func() *checker.Type + getGlobalAsyncIterableIteratorType func() *checker.Type + getGlobalAsyncIterableIteratorTypeChecked func() *checker.Type + getGlobalAsyncIteratorObjectType func() *checker.Type + getGlobalAsyncGeneratorType func() *checker.Type + getGlobalIteratorYieldResultType func() *checker.Type + getGlobalIteratorReturnResultType func() *checker.Type + getGlobalTypedPropertyDescriptorType func() *checker.Type + getGlobalClassDecoratorContextType func() *checker.Type + getGlobalClassMethodDecoratorContextType func() *checker.Type + getGlobalClassGetterDecoratorContextType func() *checker.Type + getGlobalClassSetterDecoratorContextType func() *checker.Type + getGlobalClassAccessorDecoratorContxtType func() *checker.Type + getGlobalClassAccessorDecoratorContextType func() *checker.Type + getGlobalClassAccessorDecoratorTargetType func() *checker.Type + getGlobalClassAccessorDecoratorResultType func() *checker.Type + getGlobalClassFieldDecoratorContextType func() *checker.Type + syncIterationTypesResolver *checker.IterationTypesResolver + asyncIterationTypesResolver *checker.IterationTypesResolver + isPrimitiveOrObjectOrEmptyType func(*checker.Type) bool + containsMissingType func(*checker.Type) bool + couldContainTypeVariables func(*checker.Type) bool + isStringIndexSignatureOnlyType func(*checker.Type) bool + markNodeAssignments func(*ast.Node) bool + compareTypesAssignable checker.TypeComparer + emitResolver *checker.EmitResolver + emitResolverOnce sync.Once + _jsxNamespace string + _jsxFactoryEntity *ast.Node + skipDirectInferenceNodes collections.Set[*ast.Node] + ctx context.Context + packagesMap map[string]bool + activeMappers []*checker.TypeMapper + activeTypeMappersCaches []map[checker.CacheHashKey]*checker.Type + ambientModulesOnce sync.Once + ambientModules []*ast.Symbol + withinUnreachableCode bool + reportedUnreachableNodes collections.Set[*ast.Node] + nonExistentProperties collections.Set[checker.NonExistentPropertyKey] + deferredDiagnosticCallbacks []func() + mu sync.Mutex +} +func Checker_getGlobalPromiseTypeChecked(v *checker.Checker) func() *checker.Type { + return ((*extra_Checker)(unsafe.Pointer(v))).getGlobalPromiseTypeChecked +} +func Checker_emptyGenericType(v *checker.Checker) *checker.Type { + return ((*extra_Checker)(unsafe.Pointer(v))).emptyGenericType +} //go:linkname CompareTypes github.com/microsoft/typescript-go/internal/checker.CompareTypes func CompareTypes(t1 *checker.Type, t2 *checker.Type) int type CompositeSignature = checker.CompositeSignature diff --git a/shim/diagnostics/shim.go b/shim/diagnostics/shim.go index a1a9ca4..484ddfd 100644 --- a/shim/diagnostics/shim.go +++ b/shim/diagnostics/shim.go @@ -1730,6 +1730,7 @@ var This_Effect_gen_contains_a_single_return_statement_effect_unnecessaryEffectG var This_Effect_generator_contains_try_Slashcatch_in_this_context_error_handling_is_expressed_with_Effect_APIs_such_as_Effect_try_Effect_tryPromise_Effect_catch_Effect_catchTag_effect_tryCatchInEffectGen = diagnostics.This_Effect_generator_contains_try_Slashcatch_in_this_context_error_handling_is_expressed_with_Effect_APIs_such_as_Effect_try_Effect_tryPromise_Effect_catch_Effect_catchTag_effect_tryCatchInEffectGen var This_Effect_never_succeeds_using_return_yield_Asterisk_preserves_a_definitive_generator_exit_point_for_type_narrowing_and_tooling_support_effect_missingReturnYieldStar = diagnostics.This_Effect_never_succeeds_using_return_yield_Asterisk_preserves_a_definitive_generator_exit_point_for_type_narrowing_and_tooling_support_effect_missingReturnYieldStar var This_Effect_requires_a_service_that_is_missing_from_the_expected_Effect_context_Colon_0_effect_missingEffectContext = diagnostics.This_Effect_requires_a_service_that_is_missing_from_the_expected_Effect_context_Colon_0_effect_missingEffectContext +var This_Effect_sync_thunk_returns_a_Promise_Use_Effect_promise_or_Effect_tryPromise_to_represent_async_work_effect_lazyPromiseInEffectSync = diagnostics.This_Effect_sync_thunk_returns_a_Promise_Use_Effect_promise_or_Effect_tryPromise_to_represent_async_work_effect_lazyPromiseInEffectSync var This_Effect_value_is_neither_yielded_nor_used_in_an_assignment_effect_floatingEffect = diagnostics.This_Effect_value_is_neither_yielded_nor_used_in_an_assignment_effect_floatingEffect var This_JSX_tag_requires_0_to_be_in_scope_but_it_could_not_be_found = diagnostics.This_JSX_tag_requires_0_to_be_in_scope_but_it_could_not_be_found var This_JSX_tag_requires_the_module_path_0_to_exist_but_none_could_be_found_Make_sure_you_have_types_for_the_appropriate_package_installed = diagnostics.This_JSX_tag_requires_the_module_path_0_to_exist_but_none_could_be_found_Make_sure_you_have_types_for_the_appropriate_package_installed diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.errors.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.errors.txt new file mode 100644 index 0000000..06f6c3e --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.errors.txt @@ -0,0 +1,33 @@ +=== Metadata === +Effect version: 3.19.19 + +/.src/lazyPromiseInEffectSync.ts(8,47): warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) +/.src/lazyPromiseInEffectSync.ts(11,45): warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + + +==== /.src/lazyPromiseInEffectSync.ts (2 errors) ==== + // @effect-diagnostics lazyPromiseInEffectSync:warning + import { Effect } from "effect" + + declare const promiseValue: Promise + declare const thenableValue: { then: (onFulfilled: (value: number) => unknown) => unknown } + + // Should trigger - Promise returned from Effect.sync + export const fromPromiseResolve = Effect.sync(() => Promise.resolve(1)) + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + + // Should trigger - declared Promise type returned from Effect.sync + export const fromPromiseValue = Effect.sync(() => promiseValue) + ~~~~~~~~~~~~~~~~~~ +!!! warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + + // Should NOT trigger - sync value + export const fromValue = Effect.sync(() => 1) + + // Should NOT trigger - thenable but not Promise + export const fromThenable = Effect.sync(() => thenableValue) + + // Should NOT trigger - already using the async constructor + export const fromEffectPromise = Effect.promise(() => Promise.resolve(1)) + diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid new file mode 100644 index 0000000..5d472d9 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid @@ -0,0 +1,16 @@ +flowchart TB + 0[["type: #40;#41; =#gt; Promise#lt;number#gt;
node: #40;#41; =#gt; Promise.resolve#40;1#41;"]] + 1[/"type: Promise#lt;number#gt;
node: Promise.resolve#40;1#41;"/] + 2[["type: #40;#41; =#gt; Promise#lt;number#gt;
node: #40;#41; =#gt; promiseValue"]] + 3[/"type: Promise#lt;number#gt;
node: promiseValue"/] + 4[["type: #40;#41; =#gt; number
node: #40;#41; =#gt; 1"]] + 5[/"type: 1
node: 1"/] + 6[["type: #40;#41; =#gt; #123; then: #40;onFulfilled: #40;value: number#41; =#gt; unknown#41; =#gt; unknown; #125;
node: #40;#41; =#gt; thenableValue"]] + 7[/"type: #123; then: #40;onFulfilled: #40;value: number#41; =#gt; unknown#41; =#gt; unknown; #125;
node: thenableValue"/] + 8[["type: #40;#41; =#gt; Promise#lt;number#gt;
node: #40;#41; =#gt; Promise.resolve#40;1#41;"]] + 9[/"type: Promise#lt;number#gt;
node: Promise.resolve#40;1#41;"/] + 1 -->|"kind: potentialReturn"| 0 + 3 -->|"kind: potentialReturn"| 2 + 5 -->|"kind: potentialReturn"| 4 + 7 -->|"kind: potentialReturn"| 6 + 9 -->|"kind: potentialReturn"| 8 \ No newline at end of file diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.flows.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.flows.txt new file mode 100644 index 0000000..17c7570 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.flows.txt @@ -0,0 +1 @@ +/.src/lazyPromiseInEffectSync.ts -> lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.layers.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.layers.txt new file mode 100644 index 0000000..4f9379b --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.layers.txt @@ -0,0 +1 @@ +==== /.src/lazyPromiseInEffectSync.ts (0 layer exports) ==== diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.pipings.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.pipings.txt new file mode 100644 index 0000000..b98d990 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.pipings.txt @@ -0,0 +1,99 @@ +==== /.src/lazyPromiseInEffectSync.ts (7 flows) ==== + +=== Piping Flow === +Location: 8:34 - 8:72 +Node: Effect.sync(() => Promise.resolve(1)) +Node Kind: KindCallExpression + +Subject: () => Promise.resolve(1) +Subject Type: () => Promise + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect, never, never> + +=== Piping Flow === +Location: 8:52 - 8:71 +Node: Promise.resolve(1) +Node Kind: KindCallExpression + +Subject: 1 +Subject Type: 1 + +Transformations (1): + [0] kind: call + callee: Promise.resolve + args: (constant) + outType: Promise + +=== Piping Flow === +Location: 11:32 - 11:64 +Node: Effect.sync(() => promiseValue) +Node Kind: KindCallExpression + +Subject: () => promiseValue +Subject Type: () => Promise + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect, never, never> + +=== Piping Flow === +Location: 14:25 - 14:46 +Node: Effect.sync(() => 1) +Node Kind: KindCallExpression + +Subject: () => 1 +Subject Type: () => number + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect + +=== Piping Flow === +Location: 17:28 - 17:61 +Node: Effect.sync(() => thenableValue) +Node Kind: KindCallExpression + +Subject: () => thenableValue +Subject Type: () => { then: (onFulfilled: (value: number) => unknown) => unknown; } + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect<{ then: (onFulfilled: (value: number) => unknown) => unknown; }, never, never> + +=== Piping Flow === +Location: 20:33 - 20:74 +Node: Effect.promise(() => Promise.resolve(1)) +Node Kind: KindCallExpression + +Subject: () => Promise.resolve(1) +Subject Type: () => Promise + +Transformations (1): + [0] kind: call + callee: Effect.promise + args: (constant) + outType: Effect + +=== Piping Flow === +Location: 20:54 - 20:73 +Node: Promise.resolve(1) +Node Kind: KindCallExpression + +Subject: 1 +Subject Type: 1 + +Transformations (1): + [0] kind: call + callee: Promise.resolve + args: (constant) + outType: Promise diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.quickfixes.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.quickfixes.txt new file mode 100644 index 0000000..e8007f3 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync.quickfixes.txt @@ -0,0 +1,23 @@ +=== Quick Fix Inventory === + +[D1] (8:47-8:71) TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + Fix 0: "Disable lazyPromiseInEffectSync for this line" + Fix 1: "Disable lazyPromiseInEffectSync for entire file" + +[D2] (11:45-11:63) TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + Fix 0: "Disable lazyPromiseInEffectSync for this line" + Fix 1: "Disable lazyPromiseInEffectSync for entire file" + +=== Quick Fix Application Results === + +=== [D1] Fix 0: "Disable lazyPromiseInEffectSync for this line" === +skipped by default + +=== [D1] Fix 1: "Disable lazyPromiseInEffectSync for entire file" === +skipped by default + +=== [D2] Fix 0: "Disable lazyPromiseInEffectSync for this line" === +skipped by default + +=== [D2] Fix 1: "Disable lazyPromiseInEffectSync for entire file" === +skipped by default diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.errors.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.errors.txt new file mode 100644 index 0000000..c9b6eec --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.errors.txt @@ -0,0 +1,15 @@ +=== Metadata === +Effect version: 3.19.19 + +/.src/lazyPromiseInEffectSync_preview.ts(5,36): warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + + +==== /.src/lazyPromiseInEffectSync_preview.ts (1 errors) ==== + // @effect-diagnostics *:off + // @effect-diagnostics lazyPromiseInEffectSync:warning + import { Effect } from "effect" + + export const preview = Effect.sync(() => Promise.resolve(1)) + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid new file mode 100644 index 0000000..6ec5833 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid @@ -0,0 +1,4 @@ +flowchart TB + 0[["type: #40;#41; =#gt; Promise#lt;number#gt;
node: #40;#41; =#gt; Promise.resolve#40;1#41;"]] + 1[/"type: Promise#lt;number#gt;
node: Promise.resolve#40;1#41;"/] + 1 -->|"kind: potentialReturn"| 0 \ No newline at end of file diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.flows.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.flows.txt new file mode 100644 index 0000000..501c7dc --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.flows.txt @@ -0,0 +1 @@ +/.src/lazyPromiseInEffectSync_preview.ts -> lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.layers.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.layers.txt new file mode 100644 index 0000000..84db78b --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.layers.txt @@ -0,0 +1 @@ +==== /.src/lazyPromiseInEffectSync_preview.ts (0 layer exports) ==== diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.pipings.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.pipings.txt new file mode 100644 index 0000000..7479f3b --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.pipings.txt @@ -0,0 +1,29 @@ +==== /.src/lazyPromiseInEffectSync_preview.ts (2 flows) ==== + +=== Piping Flow === +Location: 5:23 - 5:61 +Node: Effect.sync(() => Promise.resolve(1)) +Node Kind: KindCallExpression + +Subject: () => Promise.resolve(1) +Subject Type: () => Promise + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect, never, never> + +=== Piping Flow === +Location: 5:41 - 5:60 +Node: Promise.resolve(1) +Node Kind: KindCallExpression + +Subject: 1 +Subject Type: 1 + +Transformations (1): + [0] kind: call + callee: Promise.resolve + args: (constant) + outType: Promise diff --git a/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.quickfixes.txt b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.quickfixes.txt new file mode 100644 index 0000000..f7d0702 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/lazyPromiseInEffectSync_preview.quickfixes.txt @@ -0,0 +1,13 @@ +=== Quick Fix Inventory === + +[D1] (5:36-5:60) TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + Fix 0: "Disable lazyPromiseInEffectSync for this line" + Fix 1: "Disable lazyPromiseInEffectSync for entire file" + +=== Quick Fix Application Results === + +=== [D1] Fix 0: "Disable lazyPromiseInEffectSync for this line" === +skipped by default + +=== [D1] Fix 1: "Disable lazyPromiseInEffectSync for entire file" === +skipped by default diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.errors.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.errors.txt new file mode 100644 index 0000000..5ce474a --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.errors.txt @@ -0,0 +1,33 @@ +=== Metadata === +Effect version: 4.0.0 + +/.src/lazyPromiseInEffectSync.ts(8,47): warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) +/.src/lazyPromiseInEffectSync.ts(11,45): warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + + +==== /.src/lazyPromiseInEffectSync.ts (2 errors) ==== + // @effect-diagnostics lazyPromiseInEffectSync:warning + import { Effect } from "effect" + + declare const promiseValue: Promise + declare const thenableValue: { then: (onFulfilled: (value: number) => unknown) => unknown } + + // Should trigger - Promise returned from Effect.sync + export const fromPromiseResolve = Effect.sync(() => Promise.resolve(1)) + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + + // Should trigger - declared Promise type returned from Effect.sync + export const fromPromiseValue = Effect.sync(() => promiseValue) + ~~~~~~~~~~~~~~~~~~ +!!! warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + + // Should NOT trigger - sync value + export const fromValue = Effect.sync(() => 1) + + // Should NOT trigger - thenable but not Promise + export const fromThenable = Effect.sync(() => thenableValue) + + // Should NOT trigger - already using the async constructor + export const fromEffectPromise = Effect.promise(() => Promise.resolve(1)) + diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid new file mode 100644 index 0000000..5d472d9 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid @@ -0,0 +1,16 @@ +flowchart TB + 0[["type: #40;#41; =#gt; Promise#lt;number#gt;
node: #40;#41; =#gt; Promise.resolve#40;1#41;"]] + 1[/"type: Promise#lt;number#gt;
node: Promise.resolve#40;1#41;"/] + 2[["type: #40;#41; =#gt; Promise#lt;number#gt;
node: #40;#41; =#gt; promiseValue"]] + 3[/"type: Promise#lt;number#gt;
node: promiseValue"/] + 4[["type: #40;#41; =#gt; number
node: #40;#41; =#gt; 1"]] + 5[/"type: 1
node: 1"/] + 6[["type: #40;#41; =#gt; #123; then: #40;onFulfilled: #40;value: number#41; =#gt; unknown#41; =#gt; unknown; #125;
node: #40;#41; =#gt; thenableValue"]] + 7[/"type: #123; then: #40;onFulfilled: #40;value: number#41; =#gt; unknown#41; =#gt; unknown; #125;
node: thenableValue"/] + 8[["type: #40;#41; =#gt; Promise#lt;number#gt;
node: #40;#41; =#gt; Promise.resolve#40;1#41;"]] + 9[/"type: Promise#lt;number#gt;
node: Promise.resolve#40;1#41;"/] + 1 -->|"kind: potentialReturn"| 0 + 3 -->|"kind: potentialReturn"| 2 + 5 -->|"kind: potentialReturn"| 4 + 7 -->|"kind: potentialReturn"| 6 + 9 -->|"kind: potentialReturn"| 8 \ No newline at end of file diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.flows.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.flows.txt new file mode 100644 index 0000000..17c7570 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.flows.txt @@ -0,0 +1 @@ +/.src/lazyPromiseInEffectSync.ts -> lazyPromiseInEffectSync.flows.lazyPromiseInEffectSync.mermaid diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.layers.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.layers.txt new file mode 100644 index 0000000..4f9379b --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.layers.txt @@ -0,0 +1 @@ +==== /.src/lazyPromiseInEffectSync.ts (0 layer exports) ==== diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.pipings.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.pipings.txt new file mode 100644 index 0000000..b98d990 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.pipings.txt @@ -0,0 +1,99 @@ +==== /.src/lazyPromiseInEffectSync.ts (7 flows) ==== + +=== Piping Flow === +Location: 8:34 - 8:72 +Node: Effect.sync(() => Promise.resolve(1)) +Node Kind: KindCallExpression + +Subject: () => Promise.resolve(1) +Subject Type: () => Promise + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect, never, never> + +=== Piping Flow === +Location: 8:52 - 8:71 +Node: Promise.resolve(1) +Node Kind: KindCallExpression + +Subject: 1 +Subject Type: 1 + +Transformations (1): + [0] kind: call + callee: Promise.resolve + args: (constant) + outType: Promise + +=== Piping Flow === +Location: 11:32 - 11:64 +Node: Effect.sync(() => promiseValue) +Node Kind: KindCallExpression + +Subject: () => promiseValue +Subject Type: () => Promise + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect, never, never> + +=== Piping Flow === +Location: 14:25 - 14:46 +Node: Effect.sync(() => 1) +Node Kind: KindCallExpression + +Subject: () => 1 +Subject Type: () => number + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect + +=== Piping Flow === +Location: 17:28 - 17:61 +Node: Effect.sync(() => thenableValue) +Node Kind: KindCallExpression + +Subject: () => thenableValue +Subject Type: () => { then: (onFulfilled: (value: number) => unknown) => unknown; } + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect<{ then: (onFulfilled: (value: number) => unknown) => unknown; }, never, never> + +=== Piping Flow === +Location: 20:33 - 20:74 +Node: Effect.promise(() => Promise.resolve(1)) +Node Kind: KindCallExpression + +Subject: () => Promise.resolve(1) +Subject Type: () => Promise + +Transformations (1): + [0] kind: call + callee: Effect.promise + args: (constant) + outType: Effect + +=== Piping Flow === +Location: 20:54 - 20:73 +Node: Promise.resolve(1) +Node Kind: KindCallExpression + +Subject: 1 +Subject Type: 1 + +Transformations (1): + [0] kind: call + callee: Promise.resolve + args: (constant) + outType: Promise diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.quickfixes.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.quickfixes.txt new file mode 100644 index 0000000..e8007f3 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync.quickfixes.txt @@ -0,0 +1,23 @@ +=== Quick Fix Inventory === + +[D1] (8:47-8:71) TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + Fix 0: "Disable lazyPromiseInEffectSync for this line" + Fix 1: "Disable lazyPromiseInEffectSync for entire file" + +[D2] (11:45-11:63) TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + Fix 0: "Disable lazyPromiseInEffectSync for this line" + Fix 1: "Disable lazyPromiseInEffectSync for entire file" + +=== Quick Fix Application Results === + +=== [D1] Fix 0: "Disable lazyPromiseInEffectSync for this line" === +skipped by default + +=== [D1] Fix 1: "Disable lazyPromiseInEffectSync for entire file" === +skipped by default + +=== [D2] Fix 0: "Disable lazyPromiseInEffectSync for this line" === +skipped by default + +=== [D2] Fix 1: "Disable lazyPromiseInEffectSync for entire file" === +skipped by default diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.errors.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.errors.txt new file mode 100644 index 0000000..0c3ca37 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.errors.txt @@ -0,0 +1,15 @@ +=== Metadata === +Effect version: 4.0.0 + +/.src/lazyPromiseInEffectSync_preview.ts(5,36): warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + + +==== /.src/lazyPromiseInEffectSync_preview.ts (1 errors) ==== + // @effect-diagnostics *:off + // @effect-diagnostics lazyPromiseInEffectSync:warning + import { Effect } from "effect" + + export const preview = Effect.sync(() => Promise.resolve(1)) + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid new file mode 100644 index 0000000..6ec5833 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid @@ -0,0 +1,4 @@ +flowchart TB + 0[["type: #40;#41; =#gt; Promise#lt;number#gt;
node: #40;#41; =#gt; Promise.resolve#40;1#41;"]] + 1[/"type: Promise#lt;number#gt;
node: Promise.resolve#40;1#41;"/] + 1 -->|"kind: potentialReturn"| 0 \ No newline at end of file diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.flows.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.flows.txt new file mode 100644 index 0000000..501c7dc --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.flows.txt @@ -0,0 +1 @@ +/.src/lazyPromiseInEffectSync_preview.ts -> lazyPromiseInEffectSync_preview.flows.lazyPromiseInEffectSync_preview.mermaid diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.layers.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.layers.txt new file mode 100644 index 0000000..84db78b --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.layers.txt @@ -0,0 +1 @@ +==== /.src/lazyPromiseInEffectSync_preview.ts (0 layer exports) ==== diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.pipings.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.pipings.txt new file mode 100644 index 0000000..7479f3b --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.pipings.txt @@ -0,0 +1,29 @@ +==== /.src/lazyPromiseInEffectSync_preview.ts (2 flows) ==== + +=== Piping Flow === +Location: 5:23 - 5:61 +Node: Effect.sync(() => Promise.resolve(1)) +Node Kind: KindCallExpression + +Subject: () => Promise.resolve(1) +Subject Type: () => Promise + +Transformations (1): + [0] kind: call + callee: Effect.sync + args: (constant) + outType: Effect, never, never> + +=== Piping Flow === +Location: 5:41 - 5:60 +Node: Promise.resolve(1) +Node Kind: KindCallExpression + +Subject: 1 +Subject Type: 1 + +Transformations (1): + [0] kind: call + callee: Promise.resolve + args: (constant) + outType: Promise diff --git a/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.quickfixes.txt b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.quickfixes.txt new file mode 100644 index 0000000..f7d0702 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/lazyPromiseInEffectSync_preview.quickfixes.txt @@ -0,0 +1,13 @@ +=== Quick Fix Inventory === + +[D1] (5:36-5:60) TS377082: This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work. effect(lazyPromiseInEffectSync) + Fix 0: "Disable lazyPromiseInEffectSync for this line" + Fix 1: "Disable lazyPromiseInEffectSync for entire file" + +=== Quick Fix Application Results === + +=== [D1] Fix 0: "Disable lazyPromiseInEffectSync for this line" === +skipped by default + +=== [D1] Fix 1: "Disable lazyPromiseInEffectSync for entire file" === +skipped by default diff --git a/testdata/tests/effect-v3/lazyPromiseInEffectSync.ts b/testdata/tests/effect-v3/lazyPromiseInEffectSync.ts new file mode 100644 index 0000000..790f722 --- /dev/null +++ b/testdata/tests/effect-v3/lazyPromiseInEffectSync.ts @@ -0,0 +1,20 @@ +// @effect-diagnostics lazyPromiseInEffectSync:warning +import { Effect } from "effect" + +declare const promiseValue: Promise +declare const thenableValue: { then: (onFulfilled: (value: number) => unknown) => unknown } + +// Should trigger - Promise returned from Effect.sync +export const fromPromiseResolve = Effect.sync(() => Promise.resolve(1)) + +// Should trigger - declared Promise type returned from Effect.sync +export const fromPromiseValue = Effect.sync(() => promiseValue) + +// Should NOT trigger - sync value +export const fromValue = Effect.sync(() => 1) + +// Should NOT trigger - thenable but not Promise +export const fromThenable = Effect.sync(() => thenableValue) + +// Should NOT trigger - already using the async constructor +export const fromEffectPromise = Effect.promise(() => Promise.resolve(1)) diff --git a/testdata/tests/effect-v3/lazyPromiseInEffectSync_preview.ts b/testdata/tests/effect-v3/lazyPromiseInEffectSync_preview.ts new file mode 100644 index 0000000..6ba4ca4 --- /dev/null +++ b/testdata/tests/effect-v3/lazyPromiseInEffectSync_preview.ts @@ -0,0 +1,5 @@ +// @effect-diagnostics *:off +// @effect-diagnostics lazyPromiseInEffectSync:warning +import { Effect } from "effect" + +export const preview = Effect.sync(() => Promise.resolve(1)) diff --git a/testdata/tests/effect-v4/lazyPromiseInEffectSync.ts b/testdata/tests/effect-v4/lazyPromiseInEffectSync.ts new file mode 100644 index 0000000..790f722 --- /dev/null +++ b/testdata/tests/effect-v4/lazyPromiseInEffectSync.ts @@ -0,0 +1,20 @@ +// @effect-diagnostics lazyPromiseInEffectSync:warning +import { Effect } from "effect" + +declare const promiseValue: Promise +declare const thenableValue: { then: (onFulfilled: (value: number) => unknown) => unknown } + +// Should trigger - Promise returned from Effect.sync +export const fromPromiseResolve = Effect.sync(() => Promise.resolve(1)) + +// Should trigger - declared Promise type returned from Effect.sync +export const fromPromiseValue = Effect.sync(() => promiseValue) + +// Should NOT trigger - sync value +export const fromValue = Effect.sync(() => 1) + +// Should NOT trigger - thenable but not Promise +export const fromThenable = Effect.sync(() => thenableValue) + +// Should NOT trigger - already using the async constructor +export const fromEffectPromise = Effect.promise(() => Promise.resolve(1)) diff --git a/testdata/tests/effect-v4/lazyPromiseInEffectSync_preview.ts b/testdata/tests/effect-v4/lazyPromiseInEffectSync_preview.ts new file mode 100644 index 0000000..6ba4ca4 --- /dev/null +++ b/testdata/tests/effect-v4/lazyPromiseInEffectSync_preview.ts @@ -0,0 +1,5 @@ +// @effect-diagnostics *:off +// @effect-diagnostics lazyPromiseInEffectSync:warning +import { Effect } from "effect" + +export const preview = Effect.sync(() => Promise.resolve(1))