diff --git a/.changeset/unsafe-effect-type-assertion.md b/.changeset/unsafe-effect-type-assertion.md new file mode 100644 index 00000000..364fe8c4 --- /dev/null +++ b/.changeset/unsafe-effect-type-assertion.md @@ -0,0 +1,7 @@ +--- +"@effect/tsgo": minor +--- + +Add the `unsafeEffectTypeAssertion` diagnostic and quick-fix to detect assertions that unsafely narrow Effect error or requirements channels. + +This also ports the matching v3/v4 examples, preview coverage, and baselines for the new rule. diff --git a/README.md b/README.md index 310b9460..1c17fd7c 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ Some diagnostics are off by default or have a default severity of suggestion, bu preferSchemaOverJson💡Suggests using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify✓✓ processEnv➖Warns when reading process.env outside Effect generators instead of using Effect Config✓✓ processEnvInEffect➖Warns when reading process.env inside Effect generators instead of using Effect Config✓✓ + unsafeEffectTypeAssertion➖🔧Detects unsafe type assertions that narrow Effect error or requirements channels✓✓ Style Cleanup, consistency, and idiomatic Effect code. catchAllToMapError💡🔧Suggests using Effect.mapError instead of Effect.catch + Effect.fail✓✓ deterministicKeys➖🔧Enforces deterministic naming for service/tag/error identifiers based on class names✓✓ diff --git a/_packages/tsgo/src/metadata.json b/_packages/tsgo/src/metadata.json index 604f2f3c..239327bc 100644 --- a/_packages/tsgo/src/metadata.json +++ b/_packages/tsgo/src/metadata.json @@ -45,7 +45,8 @@ "nodeBuiltinImport": "warning", "preferSchemaOverJson": "warning", "processEnv": "warning", - "processEnvInEffect": "warning" + "processEnvInEffect": "warning", + "unsafeEffectTypeAssertion": "warning" } } ], @@ -1313,6 +1314,30 @@ ] } }, + { + "name": "unsafeEffectTypeAssertion", + "group": "effectNative", + "description": "Detects unsafe type assertions that narrow Effect error or requirements channels", + "defaultSeverity": "off", + "fixable": true, + "supportedEffect": [ + "v3", + "v4" + ], + "codes": [ + 377075 + ], + "preview": { + "sourceText": "import { Effect } from \"effect\"\n\ndeclare const program: Effect.Effect\u003cstring, \"boom\", \"service\"\u003e\n\nexport const preview = program as Effect.Effect\u003cstring, never, never\u003e\n", + "diagnostics": [ + { + "start": 121, + "end": 167, + "text": "This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)" + } + ] + } + }, { "name": "catchAllToMapError", "group": "style", diff --git a/internal/diagnostics/effectDiagnosticMessages.json b/internal/diagnostics/effectDiagnosticMessages.json index 5debdeb4..2838cf19 100644 --- a/internal/diagnostics/effectDiagnosticMessages.json +++ b/internal/diagnostics/effectDiagnosticMessages.json @@ -99,6 +99,14 @@ "category": "Suggestion", "code": 377074 }, + "This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)": { + "category": "Warning", + "code": 377075 + }, + "The {0} channel is narrowed from `{1}` to `{2}`. effect(unsafeEffectTypeAssertion)": { + "category": "Warning", + "code": 377087 + }, "This code uses `JSON.parse` or `JSON.stringify`. Effect Schema provides Effect-aware APIs for JSON parsing and stringifying. effect(preferSchemaOverJson)": { "category": "Suggestion", "code": 377026 diff --git a/internal/fixables/fixables.go b/internal/fixables/fixables.go index f8f5c8ac..574c1d6b 100644 --- a/internal/fixables/fixables.go +++ b/internal/fixables/fixables.go @@ -31,6 +31,7 @@ var All = []fixable.Fixable{ OverriddenSchemaConstructorFix, InstanceOfSchemaFix, LayerMergeAllWithDependenciesFix, + UnsafeEffectTypeAssertionFix, MissingEffectErrorCatchFix, MultipleEffectProvideFix, SchemaStructWithTagFix, diff --git a/internal/fixables/unsafe_effect_type_assertion.go b/internal/fixables/unsafe_effect_type_assertion.go new file mode 100644 index 00000000..cca817c7 --- /dev/null +++ b/internal/fixables/unsafe_effect_type_assertion.go @@ -0,0 +1,39 @@ +package fixables + +import ( + "github.com/effect-ts/tsgo/internal/fixable" + "github.com/effect-ts/tsgo/internal/rewriter" + "github.com/effect-ts/tsgo/internal/rules" + tsdiag "github.com/microsoft/typescript-go/shim/diagnostics" + "github.com/microsoft/typescript-go/shim/ls" +) + +var UnsafeEffectTypeAssertionFix = fixable.Fixable{ + Name: "unsafeEffectTypeAssertion", + Description: "Remove the unsafe Effect assertion", + ErrorCodes: []int32{tsdiag.This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion.Code()}, + FixIDs: []string{"unsafeEffectTypeAssertion_fix"}, + Run: runUnsafeEffectTypeAssertionFix, +} + +func runUnsafeEffectTypeAssertionFix(ctx *fixable.Context) []ls.CodeAction { + matches := rules.AnalyzeUnsafeEffectTypeAssertion(ctx.TypeParser, ctx.Checker, ctx.SourceFile) + for _, match := range matches { + diagRange := match.Location + if !diagRange.Intersects(ctx.Span) && !ctx.Span.ContainedBy(diagRange) { + continue + } + + if action := ctx.NewFixAction(fixable.FixAction{ + Description: "Remove the unsafe Effect assertion", + Run: func(tracker *rewriter.Tracker) { + tracker.ReplaceNode(ctx.SourceFile, match.AssertionNode, match.ExpressionNode, nil) + }, + }); action != nil { + return []ls.CodeAction{*action} + } + return nil + } + + return nil +} diff --git a/internal/rules/rules.go b/internal/rules/rules.go index 73edd330..0dffd5e5 100644 --- a/internal/rules/rules.go +++ b/internal/rules/rules.go @@ -52,6 +52,7 @@ var All = []rule.Rule{ AsyncFunction, ScopeInLayerEffect, StrictEffectProvide, + UnsafeEffectTypeAssertion, MultipleEffectProvide, MissingLayerContext, LayerMergeAllWithDependencies, diff --git a/internal/rules/unsafe_effect_type_assertion.go b/internal/rules/unsafe_effect_type_assertion.go new file mode 100644 index 00000000..f257b0ce --- /dev/null +++ b/internal/rules/unsafe_effect_type_assertion.go @@ -0,0 +1,158 @@ +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" + "github.com/microsoft/typescript-go/shim/core" + tsdiag "github.com/microsoft/typescript-go/shim/diagnostics" + "github.com/microsoft/typescript-go/shim/scanner" +) + +var UnsafeEffectTypeAssertion = rule.Rule{ + Name: "unsafeEffectTypeAssertion", + Group: "effectNative", + Description: "Detects unsafe type assertions that narrow Effect error or requirements channels", + DefaultSeverity: etscore.SeverityOff, + SupportedEffect: []string{"v3", "v4"}, + Codes: []int32{tsdiag.This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion.Code()}, + Run: func(ctx *rule.Context) []*ast.Diagnostic { + matches := AnalyzeUnsafeEffectTypeAssertion(ctx.TypeParser, ctx.Checker, ctx.SourceFile) + diags := make([]*ast.Diagnostic, len(matches)) + for i, match := range matches { + diags[i] = ctx.NewDiagnostic( + match.SourceFile, + match.Location, + tsdiag.This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion, + unsafeEffectTypeAssertionRelatedInformation(ctx, match), + ) + } + return diags + }, +} + +type UnsafeEffectTypeAssertionChannel struct { + Name string + Original string + Asserted string +} + +type UnsafeEffectTypeAssertionMatch struct { + SourceFile *ast.SourceFile + Location core.TextRange + AssertionNode *ast.Node + ExpressionNode *ast.Node + LocationNode *ast.Node + Channels []UnsafeEffectTypeAssertionChannel +} + +func AnalyzeUnsafeEffectTypeAssertion(tp *typeparser.TypeParser, c *checker.Checker, sf *ast.SourceFile) []UnsafeEffectTypeAssertionMatch { + if tp == nil || c == nil || sf == nil { + return nil + } + + var matches []UnsafeEffectTypeAssertionMatch + nodesToVisit := make([]*ast.Node, 0) + pushChild := func(child *ast.Node) bool { + nodesToVisit = append(nodesToVisit, child) + return false + } + sf.AsNode().ForEachChild(pushChild) + + for len(nodesToVisit) > 0 { + node := nodesToVisit[len(nodesToVisit)-1] + nodesToVisit = nodesToVisit[:len(nodesToVisit)-1] + node.ForEachChild(pushChild) + + if node.Kind != ast.KindAsExpression && node.Kind != ast.KindTypeAssertionExpression { + continue + } + + expr := node.Expression() + if expr == nil { + continue + } + + originalType := tp.GetTypeAtLocation(expr) + assertedType := tp.GetTypeAtLocation(node) + if originalType == nil || assertedType == nil { + continue + } + + originalEffect := tp.EffectType(originalType, expr) + if originalEffect == nil { + continue + } + + assertedEffect := tp.EffectType(assertedType, node) + if assertedEffect == nil { + continue + } + + channels := make([]UnsafeEffectTypeAssertionChannel, 0, 2) + if originalEffect.E != nil && assertedEffect.E != nil && !isAnyType(originalEffect.E) && !checker.Checker_isTypeAssignableTo(c, originalEffect.E, assertedEffect.E) { + channels = append(channels, UnsafeEffectTypeAssertionChannel{ + Name: "error", + Original: c.TypeToString(originalEffect.E), + Asserted: c.TypeToString(assertedEffect.E), + }) + } + if originalEffect.R != nil && assertedEffect.R != nil && !isAnyType(originalEffect.R) && !checker.Checker_isTypeAssignableTo(c, originalEffect.R, assertedEffect.R) { + channels = append(channels, UnsafeEffectTypeAssertionChannel{ + Name: "requirements", + Original: c.TypeToString(originalEffect.R), + Asserted: c.TypeToString(assertedEffect.R), + }) + } + if len(channels) == 0 { + continue + } + + locationNode := node + if typeNode := node.Type(); typeNode != nil { + locationNode = typeNode + } + + matches = append(matches, UnsafeEffectTypeAssertionMatch{ + SourceFile: sf, + Location: scanner.GetErrorRangeForNode(sf, node), + AssertionNode: node, + ExpressionNode: expr, + LocationNode: locationNode, + Channels: channels, + }) + } + + return matches +} + +func isAnyType(t *checker.Type) bool { + return t != nil && t.Flags()&checker.TypeFlagsAny != 0 +} + +func unsafeEffectTypeAssertionRelatedInformation(ctx *rule.Context, match UnsafeEffectTypeAssertionMatch) []*ast.Diagnostic { + if ctx == nil || len(match.Channels) == 0 { + return nil + } + + locationNode := match.LocationNode + if locationNode == nil { + locationNode = match.AssertionNode + } + + related := make([]*ast.Diagnostic, 0, len(match.Channels)) + for _, channel := range match.Channels { + related = append(related, ctx.NewDiagnostic( + match.SourceFile, + scanner.GetErrorRangeForNode(match.SourceFile, locationNode), + tsdiag.The_0_channel_is_narrowed_from_1_to_2_effect_unsafeEffectTypeAssertion, + nil, + channel.Name, + channel.Original, + channel.Asserted, + )) + } + return related +} diff --git a/shim/diagnostics/shim.go b/shim/diagnostics/shim.go index 29b64a68..c81db504 100644 --- a/shim/diagnostics/shim.go +++ b/shim/diagnostics/shim.go @@ -1607,6 +1607,7 @@ var Tagged_template_expressions_are_not_permitted_in_an_optional_chain = diagnos var Target_allows_only_0_element_s_but_source_may_have_more = diagnostics.Target_allows_only_0_element_s_but_source_may_have_more var Target_requires_0_element_s_but_source_may_have_fewer = diagnostics.Target_requires_0_element_s_but_source_may_have_fewer var Target_signature_provides_too_few_arguments_Expected_0_or_more_but_got_1 = diagnostics.Target_signature_provides_too_few_arguments_Expected_0_or_more_but_got_1 +var The_0_channel_is_narrowed_from_1_to_2_effect_unsafeEffectTypeAssertion = diagnostics.The_0_channel_is_narrowed_from_1_to_2_effect_unsafeEffectTypeAssertion var The_0_modifier_can_only_be_used_in_TypeScript_files = diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files var The_0_operator_cannot_be_applied_to_type_symbol = diagnostics.The_0_operator_cannot_be_applied_to_type_symbol var The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead = diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead @@ -1824,6 +1825,7 @@ var This_syntax_is_reserved_in_files_with_the_mts_or_cts_extension_Use_an_as_exp var This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found = diagnostics.This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found var This_syntax_requires_an_imported_helper_named_1_which_does_not_exist_in_0_Consider_upgrading_your_version_of_0 = diagnostics.This_syntax_requires_an_imported_helper_named_1_which_does_not_exist_in_0_Consider_upgrading_your_version_of_0 var This_syntax_requires_an_imported_helper_named_1_with_2_parameters_which_is_not_compatible_with_the_one_in_0_Consider_upgrading_your_version_of_0 = diagnostics.This_syntax_requires_an_imported_helper_named_1_with_2_parameters_which_is_not_compatible_with_the_one_in_0_Consider_upgrading_your_version_of_0 +var This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion = diagnostics.This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion var This_type_parameter_might_need_an_extends_0_constraint = diagnostics.This_type_parameter_might_need_an_extends_0_constraint var This_use_of_import_is_invalid_import_calls_can_be_written_but_they_must_have_parentheses_and_cannot_have_type_arguments = diagnostics.This_use_of_import_is_invalid_import_calls_can_be_written_but_they_must_have_parentheses_and_cannot_have_type_arguments var This_uses_the_Effect_do_emulation_Effect_gen_or_Effect_fn_achieve_the_same_result_with_native_JS_scopes_effect_effectDoNotation = diagnostics.This_uses_the_Effect_do_emulation_Effect_gen_or_Effect_fn_achieve_the_same_result_with_native_JS_scopes_effect_effectDoNotation diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.errors.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.errors.txt new file mode 100644 index 00000000..59e98a23 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.errors.txt @@ -0,0 +1,36 @@ +=== Metadata === +Effect version: 3.19.19 + +/.src/unsafeEffectTypeAssertion.ts(10,28): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +/.src/unsafeEffectTypeAssertion.ts(14,36): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +/.src/unsafeEffectTypeAssertion.ts(15,29): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + + +==== /.src/unsafeEffectTypeAssertion.ts (3 errors) ==== + // @effect-diagnostics unsafeEffectTypeAssertion:warning + import { Effect } from "effect" + + declare const program: Effect.Effect + declare const anyError: Effect.Effect + declare const anyRequirements: Effect.Effect + declare const noRequirements: Effect.Effect + declare const noError: Effect.Effect + + export const narrowsBoth = program as Effect.Effect + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(10,39): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(10,39): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion) + export const skipsAnyError = anyError as Effect.Effect + export const skipsAnyRequirements = anyRequirements as Effect.Effect + export const safeWiden = program as Effect.Effect + export const narrowsRequirements = noError as Effect.Effect + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(14,47): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion) + export const narrowsError = noRequirements as Effect.Effect + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(15,47): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion) + export const notEffect = 1 as number + diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.txt new file mode 100644 index 00000000..10c94b4e --- /dev/null +++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.txt @@ -0,0 +1 @@ +/.src/unsafeEffectTypeAssertion.ts -> unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid new file mode 100644 index 00000000..a3f2505c --- /dev/null +++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid @@ -0,0 +1,8 @@ +flowchart TB + 0[/"type: Effect#lt;string, never, never#gt;
node: program as Effect.Effect#lt;string, never, never#gt;"/] + 1[/"type: Effect#lt;string, never, #quot;service#quot;#gt;
node: anyError as Effect.Effect#lt;string, never, #quot;service#quot;#gt;"/] + 2[/"type: Effect#lt;string, #quot;boom#quot;, never#gt;
node: anyRequirements as Effect.Effect#lt;string, #quot;boom#quot;, never#gt;"/] + 3[/"type: Effect#lt;string, #quot;boom#quot; #124; #quot;other#quot;, #quot;service#quot;#gt;
node: program as Effect.Effect#lt;string, #quot;boom#quot; #124; #quot;other#quot;, #quot;service#quot;#gt;"/] + 4[/"type: Effect#lt;string, never, never#gt;
node: noError as Effect.Effect#lt;string, never, never#gt;"/] + 5[/"type: Effect#lt;string, never, never#gt;
node: noRequirements as Effect.Effect#lt;string, never, never#gt;"/] + 6[/"type: number
node: 1 as number"/] \ No newline at end of file diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.layers.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.layers.txt new file mode 100644 index 00000000..e4b2c247 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.layers.txt @@ -0,0 +1 @@ +==== /.src/unsafeEffectTypeAssertion.ts (0 layer exports) ==== diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.pipings.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.pipings.txt new file mode 100644 index 00000000..4f383896 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.pipings.txt @@ -0,0 +1 @@ +==== /.src/unsafeEffectTypeAssertion.ts (0 flows) ==== diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.quickfixes.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.quickfixes.txt new file mode 100644 index 00000000..a3628506 --- /dev/null +++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.quickfixes.txt @@ -0,0 +1,99 @@ +=== Quick Fix Inventory === + +[D1] (10:28-10:74) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + Fix 0: "Disable unsafeEffectTypeAssertion for this line" + Fix 1: "Disable unsafeEffectTypeAssertion for entire file" + Fix 2: "Remove the unsafe Effect assertion" + +[D2] (14:36-14:82) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + Fix 0: "Disable unsafeEffectTypeAssertion for this line" + Fix 1: "Disable unsafeEffectTypeAssertion for entire file" + Fix 2: "Remove the unsafe Effect assertion" + +[D3] (15:29-15:82) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + Fix 0: "Disable unsafeEffectTypeAssertion for this line" + Fix 1: "Disable unsafeEffectTypeAssertion for entire file" + Fix 2: "Remove the unsafe Effect assertion" + +=== Quick Fix Application Results === + +=== [D1] Fix 0: "Disable unsafeEffectTypeAssertion for this line" === +skipped by default + +=== [D1] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" === +skipped by default + +=== [D1] Fix 2: "Remove the unsafe Effect assertion" === + +--- file:///.src/unsafeEffectTypeAssertion.ts --- +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect +declare const anyError: Effect.Effect +declare const anyRequirements: Effect.Effect +declare const noRequirements: Effect.Effect +declare const noError: Effect.Effect + +export const narrowsBoth = program +export const skipsAnyError = anyError as Effect.Effect +export const skipsAnyRequirements = anyRequirements as Effect.Effect +export const safeWiden = program as Effect.Effect +export const narrowsRequirements = noError as Effect.Effect +export const narrowsError = noRequirements as Effect.Effect +export const notEffect = 1 as number + + +=== [D2] Fix 0: "Disable unsafeEffectTypeAssertion for this line" === +skipped by default + +=== [D2] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" === +skipped by default + +=== [D2] Fix 2: "Remove the unsafe Effect assertion" === + +--- file:///.src/unsafeEffectTypeAssertion.ts --- +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect +declare const anyError: Effect.Effect +declare const anyRequirements: Effect.Effect +declare const noRequirements: Effect.Effect +declare const noError: Effect.Effect + +export const narrowsBoth = program as Effect.Effect +export const skipsAnyError = anyError as Effect.Effect +export const skipsAnyRequirements = anyRequirements as Effect.Effect +export const safeWiden = program as Effect.Effect +export const narrowsRequirements = noError +export const narrowsError = noRequirements as Effect.Effect +export const notEffect = 1 as number + + +=== [D3] Fix 0: "Disable unsafeEffectTypeAssertion for this line" === +skipped by default + +=== [D3] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" === +skipped by default + +=== [D3] Fix 2: "Remove the unsafe Effect assertion" === + +--- file:///.src/unsafeEffectTypeAssertion.ts --- +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect +declare const anyError: Effect.Effect +declare const anyRequirements: Effect.Effect +declare const noRequirements: Effect.Effect +declare const noError: Effect.Effect + +export const narrowsBoth = program as Effect.Effect +export const skipsAnyError = anyError as Effect.Effect +export const skipsAnyRequirements = anyRequirements as Effect.Effect +export const safeWiden = program as Effect.Effect +export const narrowsRequirements = noError as Effect.Effect +export const narrowsError = noRequirements +export const notEffect = 1 as number + diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.errors.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.errors.txt new file mode 100644 index 00000000..f7d62150 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.errors.txt @@ -0,0 +1,36 @@ +=== Metadata === +Effect version: 4.0.0 + +/.src/unsafeEffectTypeAssertion.ts(10,28): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +/.src/unsafeEffectTypeAssertion.ts(14,36): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +/.src/unsafeEffectTypeAssertion.ts(15,29): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + + +==== /.src/unsafeEffectTypeAssertion.ts (3 errors) ==== + // @effect-diagnostics unsafeEffectTypeAssertion:warning + import { Effect } from "effect" + + declare const program: Effect.Effect + declare const anyError: Effect.Effect + declare const anyRequirements: Effect.Effect + declare const noRequirements: Effect.Effect + declare const noError: Effect.Effect + + export const narrowsBoth = program as Effect.Effect + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(10,39): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(10,39): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion) + export const skipsAnyError = anyError as Effect.Effect + export const skipsAnyRequirements = anyRequirements as Effect.Effect + export const safeWiden = program as Effect.Effect + export const narrowsRequirements = noError as Effect.Effect + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(14,47): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion) + export const narrowsError = noRequirements as Effect.Effect + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(15,47): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion) + export const notEffect = 1 as number + diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.txt new file mode 100644 index 00000000..10c94b4e --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.txt @@ -0,0 +1 @@ +/.src/unsafeEffectTypeAssertion.ts -> unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid new file mode 100644 index 00000000..a3f2505c --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid @@ -0,0 +1,8 @@ +flowchart TB + 0[/"type: Effect#lt;string, never, never#gt;
node: program as Effect.Effect#lt;string, never, never#gt;"/] + 1[/"type: Effect#lt;string, never, #quot;service#quot;#gt;
node: anyError as Effect.Effect#lt;string, never, #quot;service#quot;#gt;"/] + 2[/"type: Effect#lt;string, #quot;boom#quot;, never#gt;
node: anyRequirements as Effect.Effect#lt;string, #quot;boom#quot;, never#gt;"/] + 3[/"type: Effect#lt;string, #quot;boom#quot; #124; #quot;other#quot;, #quot;service#quot;#gt;
node: program as Effect.Effect#lt;string, #quot;boom#quot; #124; #quot;other#quot;, #quot;service#quot;#gt;"/] + 4[/"type: Effect#lt;string, never, never#gt;
node: noError as Effect.Effect#lt;string, never, never#gt;"/] + 5[/"type: Effect#lt;string, never, never#gt;
node: noRequirements as Effect.Effect#lt;string, never, never#gt;"/] + 6[/"type: number
node: 1 as number"/] \ No newline at end of file diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.layers.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.layers.txt new file mode 100644 index 00000000..e4b2c247 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.layers.txt @@ -0,0 +1 @@ +==== /.src/unsafeEffectTypeAssertion.ts (0 layer exports) ==== diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.pipings.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.pipings.txt new file mode 100644 index 00000000..4f383896 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.pipings.txt @@ -0,0 +1 @@ +==== /.src/unsafeEffectTypeAssertion.ts (0 flows) ==== diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.quickfixes.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.quickfixes.txt new file mode 100644 index 00000000..a3628506 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.quickfixes.txt @@ -0,0 +1,99 @@ +=== Quick Fix Inventory === + +[D1] (10:28-10:74) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + Fix 0: "Disable unsafeEffectTypeAssertion for this line" + Fix 1: "Disable unsafeEffectTypeAssertion for entire file" + Fix 2: "Remove the unsafe Effect assertion" + +[D2] (14:36-14:82) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + Fix 0: "Disable unsafeEffectTypeAssertion for this line" + Fix 1: "Disable unsafeEffectTypeAssertion for entire file" + Fix 2: "Remove the unsafe Effect assertion" + +[D3] (15:29-15:82) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + Fix 0: "Disable unsafeEffectTypeAssertion for this line" + Fix 1: "Disable unsafeEffectTypeAssertion for entire file" + Fix 2: "Remove the unsafe Effect assertion" + +=== Quick Fix Application Results === + +=== [D1] Fix 0: "Disable unsafeEffectTypeAssertion for this line" === +skipped by default + +=== [D1] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" === +skipped by default + +=== [D1] Fix 2: "Remove the unsafe Effect assertion" === + +--- file:///.src/unsafeEffectTypeAssertion.ts --- +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect +declare const anyError: Effect.Effect +declare const anyRequirements: Effect.Effect +declare const noRequirements: Effect.Effect +declare const noError: Effect.Effect + +export const narrowsBoth = program +export const skipsAnyError = anyError as Effect.Effect +export const skipsAnyRequirements = anyRequirements as Effect.Effect +export const safeWiden = program as Effect.Effect +export const narrowsRequirements = noError as Effect.Effect +export const narrowsError = noRequirements as Effect.Effect +export const notEffect = 1 as number + + +=== [D2] Fix 0: "Disable unsafeEffectTypeAssertion for this line" === +skipped by default + +=== [D2] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" === +skipped by default + +=== [D2] Fix 2: "Remove the unsafe Effect assertion" === + +--- file:///.src/unsafeEffectTypeAssertion.ts --- +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect +declare const anyError: Effect.Effect +declare const anyRequirements: Effect.Effect +declare const noRequirements: Effect.Effect +declare const noError: Effect.Effect + +export const narrowsBoth = program as Effect.Effect +export const skipsAnyError = anyError as Effect.Effect +export const skipsAnyRequirements = anyRequirements as Effect.Effect +export const safeWiden = program as Effect.Effect +export const narrowsRequirements = noError +export const narrowsError = noRequirements as Effect.Effect +export const notEffect = 1 as number + + +=== [D3] Fix 0: "Disable unsafeEffectTypeAssertion for this line" === +skipped by default + +=== [D3] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" === +skipped by default + +=== [D3] Fix 2: "Remove the unsafe Effect assertion" === + +--- file:///.src/unsafeEffectTypeAssertion.ts --- +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect +declare const anyError: Effect.Effect +declare const anyRequirements: Effect.Effect +declare const noRequirements: Effect.Effect +declare const noError: Effect.Effect + +export const narrowsBoth = program as Effect.Effect +export const skipsAnyError = anyError as Effect.Effect +export const skipsAnyRequirements = anyRequirements as Effect.Effect +export const safeWiden = program as Effect.Effect +export const narrowsRequirements = noError as Effect.Effect +export const narrowsError = noRequirements +export const notEffect = 1 as number + diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.errors.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.errors.txt new file mode 100644 index 00000000..ee9be326 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.errors.txt @@ -0,0 +1,19 @@ +=== Metadata === +Effect version: 4.0.0 + +/.src/unsafeEffectTypeAssertion_preview.ts(7,24): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + + +==== /.src/unsafeEffectTypeAssertion_preview.ts (1 errors) ==== + // @effect-diagnostics *:off + // @effect-diagnostics unsafeEffectTypeAssertion:warning + import { Effect } from "effect" + + declare const program: Effect.Effect + + export const preview = program as Effect.Effect + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion_preview.ts(7,35): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion) +!!! related TS377087 /.src/unsafeEffectTypeAssertion_preview.ts(7,35): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion) + diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.txt new file mode 100644 index 00000000..701be638 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.txt @@ -0,0 +1 @@ +/.src/unsafeEffectTypeAssertion_preview.ts -> unsafeEffectTypeAssertion_preview.flows.unsafeEffectTypeAssertion_preview.mermaid diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.unsafeEffectTypeAssertion_preview.mermaid b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.unsafeEffectTypeAssertion_preview.mermaid new file mode 100644 index 00000000..563348eb --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.unsafeEffectTypeAssertion_preview.mermaid @@ -0,0 +1,2 @@ +flowchart TB + 0[/"type: Effect#lt;string, never, never#gt;
node: program as Effect.Effect#lt;string, never, never#gt;"/] \ No newline at end of file diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.layers.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.layers.txt new file mode 100644 index 00000000..6cf91914 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.layers.txt @@ -0,0 +1 @@ +==== /.src/unsafeEffectTypeAssertion_preview.ts (0 layer exports) ==== diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.pipings.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.pipings.txt new file mode 100644 index 00000000..51cc63d1 --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.pipings.txt @@ -0,0 +1 @@ +==== /.src/unsafeEffectTypeAssertion_preview.ts (0 flows) ==== diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.quickfixes.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.quickfixes.txt new file mode 100644 index 00000000..0066698b --- /dev/null +++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.quickfixes.txt @@ -0,0 +1,26 @@ +=== Quick Fix Inventory === + +[D1] (7:24-7:70) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion) + Fix 0: "Disable unsafeEffectTypeAssertion for this line" + Fix 1: "Disable unsafeEffectTypeAssertion for entire file" + Fix 2: "Remove the unsafe Effect assertion" + +=== Quick Fix Application Results === + +=== [D1] Fix 0: "Disable unsafeEffectTypeAssertion for this line" === +skipped by default + +=== [D1] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" === +skipped by default + +=== [D1] Fix 2: "Remove the unsafe Effect assertion" === + +--- file:///.src/unsafeEffectTypeAssertion_preview.ts --- +// @effect-diagnostics *:off +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect + +export const preview = program + diff --git a/testdata/tests/effect-v3/unsafeEffectTypeAssertion.ts b/testdata/tests/effect-v3/unsafeEffectTypeAssertion.ts new file mode 100644 index 00000000..a2b6b1c5 --- /dev/null +++ b/testdata/tests/effect-v3/unsafeEffectTypeAssertion.ts @@ -0,0 +1,16 @@ +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect +declare const anyError: Effect.Effect +declare const anyRequirements: Effect.Effect +declare const noRequirements: Effect.Effect +declare const noError: Effect.Effect + +export const narrowsBoth = program as Effect.Effect +export const skipsAnyError = anyError as Effect.Effect +export const skipsAnyRequirements = anyRequirements as Effect.Effect +export const safeWiden = program as Effect.Effect +export const narrowsRequirements = noError as Effect.Effect +export const narrowsError = noRequirements as Effect.Effect +export const notEffect = 1 as number diff --git a/testdata/tests/effect-v4/unsafeEffectTypeAssertion.ts b/testdata/tests/effect-v4/unsafeEffectTypeAssertion.ts new file mode 100644 index 00000000..a2b6b1c5 --- /dev/null +++ b/testdata/tests/effect-v4/unsafeEffectTypeAssertion.ts @@ -0,0 +1,16 @@ +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect +declare const anyError: Effect.Effect +declare const anyRequirements: Effect.Effect +declare const noRequirements: Effect.Effect +declare const noError: Effect.Effect + +export const narrowsBoth = program as Effect.Effect +export const skipsAnyError = anyError as Effect.Effect +export const skipsAnyRequirements = anyRequirements as Effect.Effect +export const safeWiden = program as Effect.Effect +export const narrowsRequirements = noError as Effect.Effect +export const narrowsError = noRequirements as Effect.Effect +export const notEffect = 1 as number diff --git a/testdata/tests/effect-v4/unsafeEffectTypeAssertion_preview.ts b/testdata/tests/effect-v4/unsafeEffectTypeAssertion_preview.ts new file mode 100644 index 00000000..7068fd1f --- /dev/null +++ b/testdata/tests/effect-v4/unsafeEffectTypeAssertion_preview.ts @@ -0,0 +1,7 @@ +// @effect-diagnostics *:off +// @effect-diagnostics unsafeEffectTypeAssertion:warning +import { Effect } from "effect" + +declare const program: Effect.Effect + +export const preview = program as Effect.Effect