Skip to content

Commit 892984f

Browse files
Report floating streams (#711)
1 parent 0372f58 commit 892984f

File tree

5 files changed

+54
-3
lines changed

5 files changed

+54
-3
lines changed

.changeset/calm-hounds-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@effect/language-service": patch
3+
---
4+
5+
Report floating `Stream` values in Effect projects by parsing `Stream` types in the diagnostic type parser and checking them in `floatingEffect` for both v3 and v4 harnesses.
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
no codefixes
1+
floatingEffect_skipNextLine from 134 to 152
2+
floatingEffect_skipFile from 134 to 152
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
// no diagnostics
1+
Stream.succeed(42)
2+
5:2 - 5:20 | 1 | Effect-able Stream<number, never, never> must be yielded or assigned to a variable. effect(floatingEffect)

packages/language-service/src/core/TypeParser.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ export interface TypeParser {
6060
type: ts.Type,
6161
atLocation: ts.Node
6262
) => Nano.Nano<{ A: ts.Type; E: ts.Type; R: ts.Type }, TypeParserIssue>
63+
streamType: (
64+
type: ts.Type,
65+
atLocation: ts.Node
66+
) => Nano.Nano<{ A: ts.Type; E: ts.Type; R: ts.Type }, TypeParserIssue>
6367
strictEffectType: (
6468
type: ts.Type,
6569
atLocation: ts.Node
@@ -764,6 +768,19 @@ export function make(
764768
([A, E, R]) => ({ A, E, R })
765769
)
766770

771+
const streamVarianceStruct = (
772+
type: ts.Type,
773+
atLocation: ts.Node
774+
) =>
775+
Nano.map(
776+
Nano.all(
777+
varianceStructCovariantType(type, atLocation, "_A"),
778+
varianceStructCovariantType(type, atLocation, "_E"),
779+
varianceStructCovariantType(type, atLocation, "_R")
780+
),
781+
([A, E, R]) => ({ A, E, R })
782+
)
783+
767784
const layerVarianceStruct = (
768785
type: ts.Type,
769786
atLocation: ts.Node
@@ -830,6 +847,27 @@ export function make(
830847
(type) => type
831848
)
832849

850+
const streamType = Nano.cachedBy(
851+
Nano.fn("TypeParser.streamType")(function*(
852+
type: ts.Type,
853+
atLocation: ts.Node
854+
) {
855+
if (supportedEffect() === "v3") {
856+
return yield* effectType(type, atLocation)
857+
}
858+
859+
const typeIdSymbol = typeChecker.getPropertyOfType(type, "~effect/Stream")
860+
if (!typeIdSymbol) {
861+
return yield* typeParserIssue("Type is not a stream", type, atLocation)
862+
}
863+
864+
const typeIdType = typeChecker.getTypeOfSymbolAtLocation(typeIdSymbol, atLocation)
865+
return yield* streamVarianceStruct(typeIdType, atLocation)
866+
}),
867+
"TypeParser.streamType",
868+
(type) => type
869+
)
870+
833871
const isEffectTypeSourceFile = Nano.cachedBy(
834872
Nano.fn("TypeParser.isEffectTypeSourceFile")(function*(
835873
sourceFile: ts.SourceFile
@@ -3124,6 +3162,7 @@ export function make(
31243162
isServiceMapTypeSourceFile,
31253163
isNodeReferenceToServiceMapModuleApi,
31263164
effectType,
3165+
streamType,
31273166
strictEffectType,
31283167
layerType,
31293168
fiberType,

packages/language-service/src/diagnostics/floatingEffect.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ export const floatingEffect = LSP.createDiagnostic({
5555
const type = typeCheckerUtils.getTypeAtLocation(node.expression)
5656
if (!type) continue
5757
// if type is an effect
58-
const effect = yield* Nano.option(typeParser.effectType(type, node.expression))
58+
const effect = yield* Nano.option(
59+
pipe(
60+
typeParser.effectType(type, node.expression),
61+
Nano.orElse(() => typeParser.streamType(type, node.expression))
62+
)
63+
)
5964
if (Option.isSome(effect)) {
6065
// and not a fiber (we consider that a valid operation)
6166
const allowedFloatingEffects = yield* pipe(

0 commit comments

Comments
 (0)