@effect/language-service 0.21.3 → 0.21.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1766,33 +1766,6 @@ var createDiagnosticExecutor = fn("LSP.createCommentDirectivesProcessor")(
1766
1766
  const execute = fn("LSP.ruleExecutor")(function* (rule) {
1767
1767
  const ruleNameLowered = rule.name.toLowerCase();
1768
1768
  if (skippedRules.indexOf(ruleNameLowered) > -1) return [];
1769
- let modifiedDiagnostics = yield* rule.apply(sourceFile);
1770
- const newLevel = pluginOptions.diagnosticSeverity[ruleNameLowered];
1771
- if (newLevel) {
1772
- for (const emitted of modifiedDiagnostics) {
1773
- emitted.category = newLevel && newLevel in levelToDiagnosticCategory ? levelToDiagnosticCategory[newLevel] : emitted.category;
1774
- }
1775
- }
1776
- for (const emitted of modifiedDiagnostics.slice(0)) {
1777
- let newLevel2 = void 0;
1778
- if (!(ruleNameLowered in sectionOverrides || ruleNameLowered in lineOverrides)) continue;
1779
- const lineOverride = (lineOverrides[ruleNameLowered] || []).find(
1780
- (_) => _.pos < emitted.node.getStart(sourceFile) && _.end >= emitted.node.getEnd()
1781
- );
1782
- if (lineOverride) {
1783
- newLevel2 = lineOverride.level;
1784
- } else {
1785
- const sectionOverride = (sectionOverrides[ruleNameLowered] || []).find(
1786
- (_) => _.pos < emitted.node.getStart(sourceFile)
1787
- );
1788
- if (sectionOverride) newLevel2 = sectionOverride.level;
1789
- }
1790
- if (newLevel2 === "off") {
1791
- modifiedDiagnostics = modifiedDiagnostics.filter((_) => _ !== emitted);
1792
- } else {
1793
- emitted.category = newLevel2 && newLevel2 in levelToDiagnosticCategory ? levelToDiagnosticCategory[newLevel2] : emitted.category;
1794
- }
1795
- }
1796
1769
  const fixByDisableNextLine = (_) => ({
1797
1770
  fixName: rule.name + "_skipNextLine",
1798
1771
  description: "Disable " + rule.name + " for this line",
@@ -1825,11 +1798,34 @@ var createDiagnosticExecutor = fn("LSP.createCommentDirectivesProcessor")(
1825
1798
  )
1826
1799
  )
1827
1800
  };
1828
- const rulesWithDisableFix = modifiedDiagnostics.map((diagnostic) => ({
1829
- ...diagnostic,
1830
- fixes: diagnostic.fixes.concat([fixByDisableNextLine(diagnostic), fixByDisableEntireFile])
1831
- }));
1832
- return rulesWithDisableFix;
1801
+ let modifiedDiagnostics = [];
1802
+ yield* rule.apply(sourceFile, (entry) => {
1803
+ modifiedDiagnostics.push({
1804
+ ...entry,
1805
+ fixes: entry.fixes.concat([fixByDisableNextLine(entry), fixByDisableEntireFile])
1806
+ });
1807
+ });
1808
+ if (!(ruleNameLowered in pluginOptions.diagnosticSeverity || ruleNameLowered in sectionOverrides || ruleNameLowered in lineOverrides)) return modifiedDiagnostics;
1809
+ for (const emitted of modifiedDiagnostics.slice(0)) {
1810
+ let newLevel = pluginOptions.diagnosticSeverity[ruleNameLowered];
1811
+ const lineOverride = (lineOverrides[ruleNameLowered] || []).find(
1812
+ (_) => _.pos < emitted.node.getStart(sourceFile) && _.end >= emitted.node.getEnd()
1813
+ );
1814
+ if (lineOverride) {
1815
+ newLevel = lineOverride.level;
1816
+ } else {
1817
+ const sectionOverride = (sectionOverrides[ruleNameLowered] || []).find(
1818
+ (_) => _.pos < emitted.node.getStart(sourceFile)
1819
+ );
1820
+ if (sectionOverride) newLevel = sectionOverride.level;
1821
+ }
1822
+ if (newLevel === "off") {
1823
+ modifiedDiagnostics = modifiedDiagnostics.filter((_) => _ !== emitted);
1824
+ } else {
1825
+ emitted.category = newLevel && newLevel in levelToDiagnosticCategory ? levelToDiagnosticCategory[newLevel] : emitted.category;
1826
+ }
1827
+ }
1828
+ return modifiedDiagnostics;
1833
1829
  });
1834
1830
  return { execute };
1835
1831
  }
@@ -2100,6 +2096,16 @@ function make4(ts, typeChecker) {
2100
2096
  "TypeParser.effectType",
2101
2097
  (type) => type
2102
2098
  );
2099
+ const strictEffectType = cachedBy(
2100
+ fn("TypeParser.strictEffectType")(function* (type, atLocation) {
2101
+ if (type.symbol.name !== "Effect" || type.aliasSymbol) {
2102
+ return yield* typeParserIssue("Type name should be Effect with no alias symbol", type, atLocation);
2103
+ }
2104
+ return yield* effectType(type, atLocation);
2105
+ }),
2106
+ "TypeParser.strictEffectType",
2107
+ (type) => type
2108
+ );
2103
2109
  const layerType = cachedBy(
2104
2110
  fn("TypeParser.layerType")(function* (type, atLocation) {
2105
2111
  yield* pipeableType(type, atLocation);
@@ -2451,6 +2457,7 @@ function make4(ts, typeChecker) {
2451
2457
  );
2452
2458
  return {
2453
2459
  effectType,
2460
+ strictEffectType,
2454
2461
  layerType,
2455
2462
  fiberType,
2456
2463
  effectSubtype,
@@ -2537,6 +2544,7 @@ var getMissingTypeEntriesInTargetType = fn(
2537
2544
  "TypeCheckerApi.getMissingTypeEntriesInTargetType"
2538
2545
  )(
2539
2546
  function* (realType, expectedType) {
2547
+ if (realType === expectedType) return [];
2540
2548
  const typeChecker = yield* service(TypeCheckerApi);
2541
2549
  const result = [];
2542
2550
  let toTest = [realType];
@@ -2846,12 +2854,11 @@ var programResolvedCacheSize = /* @__PURE__ */ new Map();
2846
2854
  var duplicatePackage = createDiagnostic({
2847
2855
  name: "duplicatePackage",
2848
2856
  code: 6,
2849
- apply: fn("duplicatePackage.apply")(function* (sourceFile) {
2857
+ apply: fn("duplicatePackage.apply")(function* (sourceFile, report) {
2850
2858
  const ts = yield* service(TypeScriptApi);
2851
2859
  const program = yield* service(TypeScriptProgram);
2852
2860
  const options = yield* service(LanguageServicePluginOptions);
2853
- const effectDiagnostics = [];
2854
- if (sourceFile.statements.length < 1) return [];
2861
+ if (sourceFile.statements.length < 1) return;
2855
2862
  let resolvedPackages = checkedPackagesCache.get(sourceFile.fileName) || {};
2856
2863
  const newResolvedModuleSize = hasProperty(program, "resolvedModules") && hasProperty(program.resolvedModules, "size") && isNumber(program.resolvedModules.size) ? program.resolvedModules.size : 0;
2857
2864
  const oldResolvedSize = programResolvedCacheSize.get(sourceFile.fileName) || -1;
@@ -2875,7 +2882,7 @@ var duplicatePackage = createDiagnostic({
2875
2882
  for (const packageName of Object.keys(resolvedPackages)) {
2876
2883
  if (Object.keys(resolvedPackages[packageName]).length > 1) {
2877
2884
  const versions = Object.keys(resolvedPackages[packageName]);
2878
- effectDiagnostics.push({
2885
+ report({
2879
2886
  node: sourceFile.statements[0],
2880
2887
  category: ts.DiagnosticCategory.Warning,
2881
2888
  messageText: `Package ${packageName} is referenced multiple times with different versions (${versions.join(", ")}) and may cause unexpected type errors.
@@ -2887,7 +2894,6 @@ ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageNam
2887
2894
  });
2888
2895
  }
2889
2896
  }
2890
- return effectDiagnostics;
2891
2897
  })
2892
2898
  });
2893
2899
 
@@ -2895,7 +2901,7 @@ ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageNam
2895
2901
  var floatingEffect = createDiagnostic({
2896
2902
  name: "floatingEffect",
2897
2903
  code: 3,
2898
- apply: fn("floatingEffect.apply")(function* (sourceFile) {
2904
+ apply: fn("floatingEffect.apply")(function* (sourceFile, report) {
2899
2905
  const ts = yield* service(TypeScriptApi);
2900
2906
  const typeChecker = yield* service(TypeCheckerApi);
2901
2907
  const typeParser = yield* service(TypeParser);
@@ -2906,7 +2912,6 @@ var floatingEffect = createDiagnostic({
2906
2912
  if (ts.isBinaryExpression(expression) && expression.operatorToken && (expression.operatorToken.kind === ts.SyntaxKind.EqualsToken || expression.operatorToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken || expression.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken || expression.operatorToken.kind === ts.SyntaxKind.BarBarEqualsToken)) return false;
2907
2913
  return true;
2908
2914
  }
2909
- const effectDiagnostics = [];
2910
2915
  const nodeToVisit = [];
2911
2916
  const appendNodeToVisit = (node) => {
2912
2917
  nodeToVisit.push(node);
@@ -2926,7 +2931,7 @@ var floatingEffect = createDiagnostic({
2926
2931
  option
2927
2932
  );
2928
2933
  if (isNone2(allowedFloatingEffects)) {
2929
- effectDiagnostics.push({
2934
+ report({
2930
2935
  node,
2931
2936
  category: ts.DiagnosticCategory.Error,
2932
2937
  messageText: `Effect must be yielded or assigned to a variable.`,
@@ -2935,7 +2940,52 @@ var floatingEffect = createDiagnostic({
2935
2940
  }
2936
2941
  }
2937
2942
  }
2938
- return effectDiagnostics;
2943
+ })
2944
+ });
2945
+
2946
+ // src/diagnostics/genericEffectServices.ts
2947
+ var genericEffectServices = createDiagnostic({
2948
+ name: "genericEffectServices",
2949
+ code: 10,
2950
+ apply: fn("genericEffectServices.apply")(function* (sourceFile, report) {
2951
+ const ts = yield* service(TypeScriptApi);
2952
+ const typeParser = yield* service(TypeParser);
2953
+ const typeChecker = yield* service(TypeCheckerApi);
2954
+ const nodeToVisit = [];
2955
+ const appendNodeToVisit = (node) => {
2956
+ nodeToVisit.push(node);
2957
+ return void 0;
2958
+ };
2959
+ ts.forEachChild(sourceFile, appendNodeToVisit);
2960
+ while (nodeToVisit.length > 0) {
2961
+ const node = nodeToVisit.shift();
2962
+ const typesToCheck = [];
2963
+ if (ts.isClassDeclaration(node) && node.name && node.typeParameters && node.heritageClauses) {
2964
+ const classSym = typeChecker.getSymbolAtLocation(node.name);
2965
+ if (classSym) {
2966
+ const type = typeChecker.getTypeOfSymbol(classSym);
2967
+ typesToCheck.push([type, node.name]);
2968
+ }
2969
+ } else {
2970
+ ts.forEachChild(node, appendNodeToVisit);
2971
+ continue;
2972
+ }
2973
+ for (const [type, reportAt] of typesToCheck) {
2974
+ yield* pipe(
2975
+ typeParser.contextTag(type, node),
2976
+ map4(() => {
2977
+ report({
2978
+ node: reportAt,
2979
+ category: ts.DiagnosticCategory.Warning,
2980
+ messageText: `Effect Services with type parameters are not supported because they cannot be properly discriminated at runtime, which may cause unexpected behavior.`,
2981
+ fixes: []
2982
+ });
2983
+ }),
2984
+ orElse3(() => sync(() => ts.forEachChild(node, appendNodeToVisit))),
2985
+ ignore
2986
+ );
2987
+ }
2988
+ }
2939
2989
  })
2940
2990
  });
2941
2991
 
@@ -2943,12 +2993,11 @@ var floatingEffect = createDiagnostic({
2943
2993
  var leakingRequirements = createDiagnostic({
2944
2994
  name: "leakingRequirements",
2945
2995
  code: 8,
2946
- apply: fn("leakingRequirements.apply")(function* (sourceFile) {
2996
+ apply: fn("leakingRequirements.apply")(function* (sourceFile, report) {
2947
2997
  const ts = yield* service(TypeScriptApi);
2948
2998
  const typeChecker = yield* service(TypeCheckerApi);
2949
2999
  const typeParser = yield* service(TypeParser);
2950
3000
  const typeOrder = yield* deterministicTypeOrder;
2951
- const effectDiagnostics = [];
2952
3001
  const parseLeakedRequirements = cachedBy(
2953
3002
  fn("leakingServices.checkServiceLeaking")(
2954
3003
  function* (service2, atLocation) {
@@ -2999,7 +3048,7 @@ var leakingRequirements = createDiagnostic({
2999
3048
  );
3000
3049
  function reportLeakingRequirements(node, requirements) {
3001
3050
  if (requirements.length === 0) return;
3002
- effectDiagnostics.push({
3051
+ report({
3003
3052
  node,
3004
3053
  category: ts.DiagnosticCategory.Warning,
3005
3054
  messageText: `This Service is leaking the ${requirements.map((_) => typeChecker.typeToString(_)).join(" | ")} requirement`,
@@ -3017,7 +3066,7 @@ var leakingRequirements = createDiagnostic({
3017
3066
  const typesToCheck = [];
3018
3067
  if (ts.isCallExpression(node)) {
3019
3068
  typesToCheck.push([typeChecker.getTypeAtLocation(node), node]);
3020
- } else if (ts.isClassDeclaration(node) && node.name) {
3069
+ } else if (ts.isClassDeclaration(node) && node.name && node.heritageClauses) {
3021
3070
  const classSym = typeChecker.getSymbolAtLocation(node.name);
3022
3071
  if (classSym) {
3023
3072
  const type = typeChecker.getTypeOfSymbol(classSym);
@@ -3041,7 +3090,6 @@ var leakingRequirements = createDiagnostic({
3041
3090
  );
3042
3091
  }
3043
3092
  }
3044
- return effectDiagnostics;
3045
3093
  })
3046
3094
  });
3047
3095
 
@@ -3049,28 +3097,23 @@ var leakingRequirements = createDiagnostic({
3049
3097
  var missingEffectContext = createDiagnostic({
3050
3098
  name: "missingEffectContext",
3051
3099
  code: 1,
3052
- apply: fn("missingEffectContext.apply")(function* (sourceFile) {
3100
+ apply: fn("missingEffectContext.apply")(function* (sourceFile, report) {
3053
3101
  const ts = yield* service(TypeScriptApi);
3054
3102
  const typeChecker = yield* service(TypeCheckerApi);
3055
3103
  const typeParser = yield* service(TypeParser);
3056
3104
  const typeOrder = yield* deterministicTypeOrder;
3057
- const checkForMissingContextTypes = fn(
3058
- "missingEffectContext.apply.checkForMissingContextTypes"
3059
- )(function* (node, expectedType, valueNode, realType) {
3060
- const expectedEffect = yield* typeParser.effectType(
3061
- expectedType,
3062
- node
3063
- );
3064
- const realEffect = yield* typeParser.effectType(
3065
- realType,
3066
- valueNode
3067
- );
3068
- return yield* getMissingTypeEntriesInTargetType(
3069
- realEffect.R,
3070
- expectedEffect.R
3071
- );
3072
- });
3073
- const effectDiagnostics = [];
3105
+ const checkForMissingContextTypes = (node, expectedType, valueNode, realType) => pipe(
3106
+ all2(
3107
+ typeParser.effectType(expectedType, node),
3108
+ typeParser.effectType(realType, valueNode)
3109
+ ),
3110
+ flatMap3(
3111
+ ([expectedEffect, realEffect]) => getMissingTypeEntriesInTargetType(
3112
+ realEffect.R,
3113
+ expectedEffect.R
3114
+ )
3115
+ )
3116
+ );
3074
3117
  const sortTypes = sort(typeOrder);
3075
3118
  const entries = yield* expectedAndRealType(sourceFile);
3076
3119
  for (const [node, expectedType, valueNode, realType] of entries) {
@@ -3084,7 +3127,7 @@ var missingEffectContext = createDiagnostic({
3084
3127
  orElse3(() => succeed([]))
3085
3128
  );
3086
3129
  if (missingContext.length > 0) {
3087
- effectDiagnostics.push(
3130
+ report(
3088
3131
  {
3089
3132
  node,
3090
3133
  category: ts.DiagnosticCategory.Error,
@@ -3094,7 +3137,6 @@ var missingEffectContext = createDiagnostic({
3094
3137
  );
3095
3138
  }
3096
3139
  }
3097
- return effectDiagnostics;
3098
3140
  })
3099
3141
  });
3100
3142
 
@@ -3102,28 +3144,23 @@ var missingEffectContext = createDiagnostic({
3102
3144
  var missingEffectError = createDiagnostic({
3103
3145
  name: "missingEffectError",
3104
3146
  code: 1,
3105
- apply: fn("missingEffectError.apply")(function* (sourceFile) {
3147
+ apply: fn("missingEffectError.apply")(function* (sourceFile, report) {
3106
3148
  const ts = yield* service(TypeScriptApi);
3107
3149
  const typeChecker = yield* service(TypeCheckerApi);
3108
3150
  const typeParser = yield* service(TypeParser);
3109
3151
  const typeOrder = yield* deterministicTypeOrder;
3110
- const checkForMissingErrorTypes = fn("missingEffectError.apply.checkForMissingErrorTypes")(
3111
- function* (node, expectedType, valueNode, realType) {
3112
- const expectedEffect = yield* typeParser.effectType(
3113
- expectedType,
3114
- node
3115
- );
3116
- const realEffect = yield* typeParser.effectType(
3117
- realType,
3118
- valueNode
3119
- );
3120
- return yield* getMissingTypeEntriesInTargetType(
3152
+ const checkForMissingErrorTypes = (node, expectedType, valueNode, realType) => pipe(
3153
+ all2(
3154
+ typeParser.effectType(expectedType, node),
3155
+ typeParser.effectType(realType, valueNode)
3156
+ ),
3157
+ flatMap3(
3158
+ ([expectedEffect, realEffect]) => getMissingTypeEntriesInTargetType(
3121
3159
  realEffect.E,
3122
3160
  expectedEffect.E
3123
- );
3124
- }
3161
+ )
3162
+ )
3125
3163
  );
3126
- const effectDiagnostics = [];
3127
3164
  const sortTypes = sort(typeOrder);
3128
3165
  const entries = yield* expectedAndRealType(sourceFile);
3129
3166
  for (const [node, expectedType, valueNode, realType] of entries) {
@@ -3137,7 +3174,7 @@ var missingEffectError = createDiagnostic({
3137
3174
  orElse3(() => succeed([]))
3138
3175
  );
3139
3176
  if (missingContext.length > 0) {
3140
- effectDiagnostics.push(
3177
+ report(
3141
3178
  {
3142
3179
  node,
3143
3180
  category: ts.DiagnosticCategory.Error,
@@ -3147,7 +3184,6 @@ var missingEffectError = createDiagnostic({
3147
3184
  );
3148
3185
  }
3149
3186
  }
3150
- return effectDiagnostics;
3151
3187
  })
3152
3188
  });
3153
3189
 
@@ -3155,12 +3191,10 @@ var missingEffectError = createDiagnostic({
3155
3191
  var missingReturnYieldStar = createDiagnostic({
3156
3192
  name: "missingReturnYieldStar",
3157
3193
  code: 7,
3158
- apply: fn("missingReturnYieldStar.apply")(function* (sourceFile) {
3194
+ apply: fn("missingReturnYieldStar.apply")(function* (sourceFile, report) {
3159
3195
  const ts = yield* service(TypeScriptApi);
3160
3196
  const typeChecker = yield* service(TypeCheckerApi);
3161
3197
  const typeParser = yield* service(TypeParser);
3162
- const effectDiagnostics = [];
3163
- const brokenYields = /* @__PURE__ */ new Set();
3164
3198
  const nodeToVisit = [];
3165
3199
  const appendNodeToVisit = (node) => {
3166
3200
  nodeToVisit.push(node);
@@ -3188,36 +3222,32 @@ var missingReturnYieldStar = createDiagnostic({
3188
3222
  option
3189
3223
  );
3190
3224
  if (isSome2(effectGenLike)) {
3191
- brokenYields.add(node);
3225
+ const fix = node.expression ? [{
3226
+ fixName: "missingReturnYieldStar_fix",
3227
+ description: "Add return statement",
3228
+ apply: gen2(function* () {
3229
+ const changeTracker = yield* service(ChangeTracker);
3230
+ changeTracker.replaceNode(
3231
+ sourceFile,
3232
+ node,
3233
+ ts.factory.createReturnStatement(
3234
+ node
3235
+ )
3236
+ );
3237
+ })
3238
+ }] : [];
3239
+ report({
3240
+ node,
3241
+ category: ts.DiagnosticCategory.Error,
3242
+ messageText: `Yielded Effect never succeeds, so it is best to use a 'return yield*' instead.`,
3243
+ fixes: fix
3244
+ });
3192
3245
  }
3193
3246
  }
3194
3247
  }
3195
3248
  }
3196
3249
  }
3197
3250
  }
3198
- brokenYields.forEach((node) => {
3199
- const fix = node.expression ? [{
3200
- fixName: "missingReturnYieldStar_fix",
3201
- description: "Add return statement",
3202
- apply: gen2(function* () {
3203
- const changeTracker = yield* service(ChangeTracker);
3204
- changeTracker.replaceNode(
3205
- sourceFile,
3206
- node,
3207
- ts.factory.createReturnStatement(
3208
- node
3209
- )
3210
- );
3211
- })
3212
- }] : [];
3213
- effectDiagnostics.push({
3214
- node,
3215
- category: ts.DiagnosticCategory.Error,
3216
- messageText: `Yielded Effect never succeeds, so it is best to use a 'return yield*' instead.`,
3217
- fixes: fix
3218
- });
3219
- });
3220
- return effectDiagnostics;
3221
3251
  })
3222
3252
  });
3223
3253
 
@@ -3225,10 +3255,9 @@ var missingReturnYieldStar = createDiagnostic({
3225
3255
  var missingStarInYieldEffectGen = createDiagnostic({
3226
3256
  name: "missingStarInYieldEffectGen",
3227
3257
  code: 4,
3228
- apply: fn("missingStarInYieldEffectGen.apply")(function* (sourceFile) {
3258
+ apply: fn("missingStarInYieldEffectGen.apply")(function* (sourceFile, report) {
3229
3259
  const ts = yield* service(TypeScriptApi);
3230
3260
  const typeParser = yield* service(TypeParser);
3231
- const effectDiagnostics = [];
3232
3261
  const brokenGenerators = /* @__PURE__ */ new Set();
3233
3262
  const brokenYields = /* @__PURE__ */ new Set();
3234
3263
  const nodeToVisit = [];
@@ -3263,7 +3292,7 @@ var missingStarInYieldEffectGen = createDiagnostic({
3263
3292
  }
3264
3293
  }
3265
3294
  brokenGenerators.forEach(
3266
- (node) => effectDiagnostics.push({
3295
+ (node) => report({
3267
3296
  node,
3268
3297
  category: ts.DiagnosticCategory.Error,
3269
3298
  messageText: `Seems like you used yield instead of yield* inside this Effect.gen.`,
@@ -3286,14 +3315,78 @@ var missingStarInYieldEffectGen = createDiagnostic({
3286
3315
  );
3287
3316
  })
3288
3317
  }] : [];
3289
- effectDiagnostics.push({
3318
+ report({
3290
3319
  node,
3291
3320
  category: ts.DiagnosticCategory.Error,
3292
3321
  messageText: `When yielding Effects inside Effect.gen, you should use yield* instead of yield.`,
3293
3322
  fixes: fix
3294
3323
  });
3295
3324
  });
3296
- return effectDiagnostics;
3325
+ })
3326
+ });
3327
+
3328
+ // src/diagnostics/returnEffectInGen.ts
3329
+ var returnEffectInGen = createDiagnostic({
3330
+ name: "returnEffectInGen",
3331
+ code: 11,
3332
+ apply: fn("returnEffectInGen.apply")(function* (sourceFile, report) {
3333
+ const ts = yield* service(TypeScriptApi);
3334
+ const typeChecker = yield* service(TypeCheckerApi);
3335
+ const typeParser = yield* service(TypeParser);
3336
+ const nodeToVisit = [];
3337
+ const appendNodeToVisit = (node) => {
3338
+ nodeToVisit.push(node);
3339
+ return void 0;
3340
+ };
3341
+ ts.forEachChild(sourceFile, appendNodeToVisit);
3342
+ while (nodeToVisit.length > 0) {
3343
+ const node = nodeToVisit.shift();
3344
+ ts.forEachChild(node, appendNodeToVisit);
3345
+ if (ts.isReturnStatement(node) && node.expression) {
3346
+ if (ts.isYieldExpression(node.expression)) continue;
3347
+ const generatorOrRegularFunction = ts.findAncestor(
3348
+ node,
3349
+ (_) => ts.isFunctionExpression(_) || ts.isFunctionDeclaration(_) || ts.isMethodDeclaration(_) || ts.isArrowFunction(_) || ts.isGetAccessor(_)
3350
+ );
3351
+ if (!(generatorOrRegularFunction && "asteriskToken" in generatorOrRegularFunction && generatorOrRegularFunction.asteriskToken)) continue;
3352
+ const type = typeChecker.getTypeAtLocation(node.expression);
3353
+ const maybeEffect = yield* option(typeParser.strictEffectType(type, node.expression));
3354
+ if (isSome2(maybeEffect)) {
3355
+ if (generatorOrRegularFunction && generatorOrRegularFunction.parent) {
3356
+ const effectGenNode = generatorOrRegularFunction.parent;
3357
+ yield* pipe(
3358
+ typeParser.effectGen(effectGenNode),
3359
+ orElse3(() => typeParser.effectFnUntracedGen(effectGenNode)),
3360
+ orElse3(() => typeParser.effectFnGen(effectGenNode)),
3361
+ map4(() => {
3362
+ const fix = node.expression ? [{
3363
+ fixName: "returnEffectInGen_fix",
3364
+ description: "Add yield* statement",
3365
+ apply: gen2(function* () {
3366
+ const changeTracker = yield* service(ChangeTracker);
3367
+ changeTracker.replaceNode(
3368
+ sourceFile,
3369
+ node.expression,
3370
+ ts.factory.createYieldExpression(
3371
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
3372
+ node.expression
3373
+ )
3374
+ );
3375
+ })
3376
+ }] : [];
3377
+ report({
3378
+ node,
3379
+ category: ts.DiagnosticCategory.Suggestion,
3380
+ messageText: `You are returning an Effect-able type inside a generator function, and will result in nested Effect<Effect<...>>. Maybe you wanted to return yield* instead? Nested Effect-able types may be intended if you plan to later manually flatten or unwrap this Effect.`,
3381
+ fixes: fix
3382
+ });
3383
+ }),
3384
+ ignore
3385
+ );
3386
+ }
3387
+ }
3388
+ }
3389
+ }
3297
3390
  })
3298
3391
  });
3299
3392
 
@@ -3301,11 +3394,9 @@ var missingStarInYieldEffectGen = createDiagnostic({
3301
3394
  var unnecessaryEffectGen = createDiagnostic({
3302
3395
  name: "unnecessaryEffectGen",
3303
3396
  code: 5,
3304
- apply: fn("unnecessaryEffectGen.apply")(function* (sourceFile) {
3397
+ apply: fn("unnecessaryEffectGen.apply")(function* (sourceFile, report) {
3305
3398
  const ts = yield* service(TypeScriptApi);
3306
3399
  const typeParser = yield* service(TypeParser);
3307
- const effectDiagnostics = [];
3308
- const unnecessaryGenerators = /* @__PURE__ */ new Map();
3309
3400
  const nodeToVisit = [];
3310
3401
  const appendNodeToVisit = (node) => {
3311
3402
  nodeToVisit.push(node);
@@ -3318,29 +3409,27 @@ var unnecessaryEffectGen = createDiagnostic({
3318
3409
  if (ts.isCallExpression(node)) {
3319
3410
  yield* pipe(
3320
3411
  typeParser.unnecessaryEffectGen(node),
3321
- map4(({ replacementNode }) => unnecessaryGenerators.set(node, replacementNode)),
3412
+ map4(
3413
+ ({ replacementNode }) => report({
3414
+ node,
3415
+ category: ts.DiagnosticCategory.Suggestion,
3416
+ messageText: `This Effect.gen contains a single return statement.`,
3417
+ fixes: [{
3418
+ fixName: "unnecessaryEffectGen_fix",
3419
+ description: "Remove the Effect.gen, and keep the body",
3420
+ apply: gen2(function* () {
3421
+ const textChanges = yield* service(
3422
+ ChangeTracker
3423
+ );
3424
+ textChanges.replaceNode(sourceFile, node, yield* replacementNode);
3425
+ })
3426
+ }]
3427
+ })
3428
+ ),
3322
3429
  ignore
3323
3430
  );
3324
3431
  }
3325
3432
  }
3326
- unnecessaryGenerators.forEach(
3327
- (yieldedResult, effectGenCall) => effectDiagnostics.push({
3328
- node: effectGenCall,
3329
- category: ts.DiagnosticCategory.Suggestion,
3330
- messageText: `This Effect.gen contains a single return statement.`,
3331
- fixes: [{
3332
- fixName: "unnecessaryEffectGen_fix",
3333
- description: "Remove the Effect.gen, and keep the body",
3334
- apply: gen2(function* () {
3335
- const textChanges = yield* service(
3336
- ChangeTracker
3337
- );
3338
- textChanges.replaceNode(sourceFile, effectGenCall, yield* yieldedResult);
3339
- })
3340
- }]
3341
- })
3342
- );
3343
- return effectDiagnostics;
3344
3433
  })
3345
3434
  });
3346
3435
 
@@ -3348,11 +3437,9 @@ var unnecessaryEffectGen = createDiagnostic({
3348
3437
  var unnecessaryPipe = createDiagnostic({
3349
3438
  name: "unnecessaryPipe",
3350
3439
  code: 9,
3351
- apply: fn("unnecessaryPipe.apply")(function* (sourceFile) {
3440
+ apply: fn("unnecessaryPipe.apply")(function* (sourceFile, report) {
3352
3441
  const ts = yield* service(TypeScriptApi);
3353
3442
  const typeParser = yield* service(TypeParser);
3354
- const pipeDiagnostics = [];
3355
- const unnecessaryPipes = /* @__PURE__ */ new Map();
3356
3443
  const nodeToVisit = [];
3357
3444
  const appendNodeToVisit = (node) => {
3358
3445
  nodeToVisit.push(node);
@@ -3365,29 +3452,29 @@ var unnecessaryPipe = createDiagnostic({
3365
3452
  if (ts.isCallExpression(node)) {
3366
3453
  yield* pipe(
3367
3454
  typeParser.pipeCall(node),
3368
- map4(({ args, subject }) => args.length === 0 ? unnecessaryPipes.set(node, subject) : void 0),
3455
+ map4(({ args, subject }) => {
3456
+ if (args.length === 0) {
3457
+ report({
3458
+ node,
3459
+ category: ts.DiagnosticCategory.Suggestion,
3460
+ messageText: `This pipe call contains no arguments.`,
3461
+ fixes: [{
3462
+ fixName: "unnecessaryPipe_fix",
3463
+ description: "Remove the pipe call",
3464
+ apply: gen2(function* () {
3465
+ const textChanges = yield* service(
3466
+ ChangeTracker
3467
+ );
3468
+ textChanges.replaceNode(sourceFile, node, subject);
3469
+ })
3470
+ }]
3471
+ });
3472
+ }
3473
+ }),
3369
3474
  ignore
3370
3475
  );
3371
3476
  }
3372
3477
  }
3373
- unnecessaryPipes.forEach(
3374
- (pipeCall, pipeSubject) => pipeDiagnostics.push({
3375
- node: pipeCall,
3376
- category: ts.DiagnosticCategory.Suggestion,
3377
- messageText: `This pipe call contains no arguments.`,
3378
- fixes: [{
3379
- fixName: "unnecessaryPipe_fix",
3380
- description: "Remove the pipe call",
3381
- apply: gen2(function* () {
3382
- const textChanges = yield* service(
3383
- ChangeTracker
3384
- );
3385
- textChanges.replaceNode(sourceFile, pipeSubject, pipeCall);
3386
- })
3387
- }]
3388
- })
3389
- );
3390
- return pipeDiagnostics;
3391
3478
  })
3392
3479
  });
3393
3480
 
@@ -3401,7 +3488,9 @@ var diagnostics = [
3401
3488
  unnecessaryEffectGen,
3402
3489
  missingReturnYieldStar,
3403
3490
  leakingRequirements,
3404
- unnecessaryPipe
3491
+ unnecessaryPipe,
3492
+ genericEffectServices,
3493
+ returnEffectInGen
3405
3494
  ];
3406
3495
 
3407
3496
  // src/goto/effectRpcDefinition.ts