Compare commits
2 Commits
asserts-st
...
pr34125
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61b605f764 | ||
|
|
fa992fdc10 |
@@ -9,6 +9,7 @@ import {
|
|||||||
import * as t from '@babel/types';
|
import * as t from '@babel/types';
|
||||||
import * as TypeErrors from './TypeErrors';
|
import * as TypeErrors from './TypeErrors';
|
||||||
import {assertExhaustive} from '../Utils/utils';
|
import {assertExhaustive} from '../Utils/utils';
|
||||||
|
import {FlowType} from './FlowTypes';
|
||||||
|
|
||||||
export const DEBUG = false;
|
export const DEBUG = false;
|
||||||
|
|
||||||
@@ -196,8 +197,6 @@ export function makeVariableId(id: number): VariableId {
|
|||||||
return id as VariableId;
|
return id as VariableId;
|
||||||
}
|
}
|
||||||
|
|
||||||
import {inspect} from 'util';
|
|
||||||
import {FlowType} from './FlowTypes';
|
|
||||||
export function printConcrete<T>(
|
export function printConcrete<T>(
|
||||||
type: ConcreteType<T>,
|
type: ConcreteType<T>,
|
||||||
printType: (_: T) => string,
|
printType: (_: T) => string,
|
||||||
@@ -241,7 +240,7 @@ export function printConcrete<T>(
|
|||||||
case 'Generic':
|
case 'Generic':
|
||||||
return `T${type.id}`;
|
return `T${type.id}`;
|
||||||
case 'Object': {
|
case 'Object': {
|
||||||
const name = `Object ${inspect([...type.members.keys()])}`;
|
const name = `Object [${[...type.members.keys()].map(key => JSON.stringify(key)).join(', ')}]`;
|
||||||
return `${name}`;
|
return `${name}`;
|
||||||
}
|
}
|
||||||
case 'Tuple': {
|
case 'Tuple': {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export type AliasingEffect =
|
|||||||
/**
|
/**
|
||||||
* Mutate the value and any direct aliases (not captures). Errors if the value is not mutable.
|
* Mutate the value and any direct aliases (not captures). Errors if the value is not mutable.
|
||||||
*/
|
*/
|
||||||
| {kind: 'Mutate'; value: Place}
|
| {kind: 'Mutate'; value: Place; reason?: MutationReason | null}
|
||||||
/**
|
/**
|
||||||
* Mutate the value and any direct aliases (not captures), but only if the value is known mutable.
|
* Mutate the value and any direct aliases (not captures), but only if the value is known mutable.
|
||||||
* This should be rare.
|
* This should be rare.
|
||||||
@@ -174,6 +174,8 @@ export type AliasingEffect =
|
|||||||
place: Place;
|
place: Place;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MutationReason = {kind: 'AssignCurrentProperty'};
|
||||||
|
|
||||||
export function hashEffect(effect: AliasingEffect): string {
|
export function hashEffect(effect: AliasingEffect): string {
|
||||||
switch (effect.kind) {
|
switch (effect.kind) {
|
||||||
case 'Apply': {
|
case 'Apply': {
|
||||||
|
|||||||
@@ -68,7 +68,12 @@ import {FunctionSignature} from '../HIR/ObjectShape';
|
|||||||
import {getWriteErrorReason} from './InferFunctionEffects';
|
import {getWriteErrorReason} from './InferFunctionEffects';
|
||||||
import prettyFormat from 'pretty-format';
|
import prettyFormat from 'pretty-format';
|
||||||
import {createTemporaryPlace} from '../HIR/HIRBuilder';
|
import {createTemporaryPlace} from '../HIR/HIRBuilder';
|
||||||
import {AliasingEffect, AliasingSignature, hashEffect} from './AliasingEffects';
|
import {
|
||||||
|
AliasingEffect,
|
||||||
|
AliasingSignature,
|
||||||
|
hashEffect,
|
||||||
|
MutationReason,
|
||||||
|
} from './AliasingEffects';
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
|
|
||||||
@@ -452,10 +457,7 @@ function applySignature(
|
|||||||
effect.value.identifier.name.kind === 'named'
|
effect.value.identifier.name.kind === 'named'
|
||||||
? `\`${effect.value.identifier.name.value}\``
|
? `\`${effect.value.identifier.name.value}\``
|
||||||
: 'value';
|
: 'value';
|
||||||
effects.push({
|
const diagnostic = CompilerDiagnostic.create({
|
||||||
kind: 'MutateFrozen',
|
|
||||||
place: effect.value,
|
|
||||||
error: CompilerDiagnostic.create({
|
|
||||||
severity: ErrorSeverity.InvalidReact,
|
severity: ErrorSeverity.InvalidReact,
|
||||||
category: 'This value cannot be modified',
|
category: 'This value cannot be modified',
|
||||||
description: `${reason}.`,
|
description: `${reason}.`,
|
||||||
@@ -463,7 +465,21 @@ function applySignature(
|
|||||||
kind: 'error',
|
kind: 'error',
|
||||||
loc: effect.value.loc,
|
loc: effect.value.loc,
|
||||||
message: `${variable} cannot be modified`,
|
message: `${variable} cannot be modified`,
|
||||||
}),
|
});
|
||||||
|
if (
|
||||||
|
effect.kind === 'Mutate' &&
|
||||||
|
effect.reason?.kind === 'AssignCurrentProperty'
|
||||||
|
) {
|
||||||
|
diagnostic.withDetail({
|
||||||
|
kind: 'error',
|
||||||
|
loc: effect.value.loc,
|
||||||
|
message: `Hint: If this value is a Ref (value returned by \`useRef()\`), rename the variable to end in "Ref".`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
effects.push({
|
||||||
|
kind: 'MutateFrozen',
|
||||||
|
place: effect.value,
|
||||||
|
error: diagnostic,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1066,6 +1082,25 @@ function applyEffect(
|
|||||||
effect.value.identifier.name.kind === 'named'
|
effect.value.identifier.name.kind === 'named'
|
||||||
? `\`${effect.value.identifier.name.value}\``
|
? `\`${effect.value.identifier.name.value}\``
|
||||||
: 'value';
|
: 'value';
|
||||||
|
const diagnostic = CompilerDiagnostic.create({
|
||||||
|
severity: ErrorSeverity.InvalidReact,
|
||||||
|
category: 'This value cannot be modified',
|
||||||
|
description: `${reason}.`,
|
||||||
|
}).withDetail({
|
||||||
|
kind: 'error',
|
||||||
|
loc: effect.value.loc,
|
||||||
|
message: `${variable} cannot be modified`,
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
effect.kind === 'Mutate' &&
|
||||||
|
effect.reason?.kind === 'AssignCurrentProperty'
|
||||||
|
) {
|
||||||
|
diagnostic.withDetail({
|
||||||
|
kind: 'error',
|
||||||
|
loc: effect.value.loc,
|
||||||
|
message: `Hint: If this value is a Ref (value returned by \`useRef()\`), rename the variable to end in "Ref".`,
|
||||||
|
});
|
||||||
|
}
|
||||||
applyEffect(
|
applyEffect(
|
||||||
context,
|
context,
|
||||||
state,
|
state,
|
||||||
@@ -1075,15 +1110,7 @@ function applyEffect(
|
|||||||
? 'MutateFrozen'
|
? 'MutateFrozen'
|
||||||
: 'MutateGlobal',
|
: 'MutateGlobal',
|
||||||
place: effect.value,
|
place: effect.value,
|
||||||
error: CompilerDiagnostic.create({
|
error: diagnostic,
|
||||||
severity: ErrorSeverity.InvalidReact,
|
|
||||||
category: 'This value cannot be modified',
|
|
||||||
description: `${reason}.`,
|
|
||||||
}).withDetail({
|
|
||||||
kind: 'error',
|
|
||||||
loc: effect.value.loc,
|
|
||||||
message: `${variable} cannot be modified`,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
initialized,
|
initialized,
|
||||||
effects,
|
effects,
|
||||||
@@ -1680,7 +1707,15 @@ function computeSignatureForInstruction(
|
|||||||
}
|
}
|
||||||
case 'PropertyStore':
|
case 'PropertyStore':
|
||||||
case 'ComputedStore': {
|
case 'ComputedStore': {
|
||||||
effects.push({kind: 'Mutate', value: value.object});
|
const mutationReason: MutationReason | null =
|
||||||
|
value.kind === 'PropertyStore' && value.property === 'current'
|
||||||
|
? {kind: 'AssignCurrentProperty'}
|
||||||
|
: null;
|
||||||
|
effects.push({
|
||||||
|
kind: 'Mutate',
|
||||||
|
value: value.object,
|
||||||
|
reason: mutationReason,
|
||||||
|
});
|
||||||
effects.push({
|
effects.push({
|
||||||
kind: 'Capture',
|
kind: 'Capture',
|
||||||
from: value.value,
|
from: value.value,
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
component Foo() {
|
||||||
|
const foo = useFoo();
|
||||||
|
foo.current = true;
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Error
|
||||||
|
|
||||||
|
```
|
||||||
|
Found 1 error:
|
||||||
|
|
||||||
|
Error: This value cannot be modified
|
||||||
|
|
||||||
|
Modifying a value returned from a hook is not allowed. Consider moving the modification into the hook where the value is constructed.
|
||||||
|
|
||||||
|
3 | component Foo() {
|
||||||
|
4 | const foo = useFoo();
|
||||||
|
> 5 | foo.current = true;
|
||||||
|
| ^^^ value cannot be modified
|
||||||
|
6 | return <div />;
|
||||||
|
7 | }
|
||||||
|
8 |
|
||||||
|
|
||||||
|
3 | component Foo() {
|
||||||
|
4 | const foo = useFoo();
|
||||||
|
> 5 | foo.current = true;
|
||||||
|
| ^^^ Hint: If this value is a Ref (value returned by `useRef()`), rename the variable to end in "Ref".
|
||||||
|
6 | return <div />;
|
||||||
|
7 | }
|
||||||
|
8 |
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
component Foo() {
|
||||||
|
const foo = useFoo();
|
||||||
|
foo.current = true;
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user