Add stricter Optional Chain node validation (#11250)

* Add stricter Optional Chain node validation

Optional chains cannot start with `optional: false`. Somewhere in the `object`/`callee` tree, there must be an optional with `optional: true`.

* Print current node's type when finding an error.
This commit is contained in:
Justin Ridgewell 2020-03-16 21:17:11 -04:00 committed by GitHub
parent 59976680df
commit 56500603da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 2 deletions

View File

@ -1,5 +1,6 @@
// @flow
import defineType, {
assertOptionalChainStart,
assertEach,
assertNodeType,
assertNodeOrValueType,
@ -105,7 +106,9 @@ defineType("OptionalMemberExpression", {
default: false,
},
optional: {
validate: assertValueType("boolean"),
validate: !process.env.BABEL_TYPES_8_BREAKING
? assertValueType("boolean")
: chain(assertValueType("boolean"), assertOptionalChainStart()),
},
},
});
@ -151,7 +154,9 @@ defineType("OptionalCallExpression", {
),
},
optional: {
validate: assertValueType("boolean"),
validate: !process.env.BABEL_TYPES_8_BREAKING
? assertValueType("boolean")
: chain(assertValueType("boolean"), assertOptionalChainStart()),
},
typeArguments: {
validate: assertNodeType("TypeParameterInstantiation"),

View File

@ -186,6 +186,34 @@ export function assertShape(shape: { [string]: FieldOptions }): Validator {
return validate;
}
export function assertOptionalChainStart(): Validator {
function validate(node) {
let current = node;
while (node) {
const { type } = current;
if (type === "OptionalCallExpression") {
if (current.optional) return;
current = current.callee;
continue;
}
if (type === "OptionalMemberExpression") {
if (current.optional) return;
current = current.object;
continue;
}
break;
}
throw new TypeError(
`Non-optional ${node.type} must chain from an optional OptionalMemberExpression or OptionalCallExpression. Found chain from ${current?.type}`,
);
}
return validate;
}
export function chain(...fns: Array<Validator>): Validator {
function validate(...args) {
for (const fn of fns) {