Support flow "match" syntax (#17681)

This commit is contained in:
fisker Cheung authored and GitHub committed 2025-08-04 15:27:10 +08:00
1 parent 6c0c37a00c
commit acd25a9aa6
41 files changed
+4824 -34

No files matched your search

+26
View File
@@ -0,0 +1,26 @@
#### Add basic support for "match" syntax (#17681 by @fisker)
<!-- prettier-ignore -->
```jsx
// Input
const e = match (a) {
1 => true,
'foo' => false,
2 => {obj: 'literal'},
};
// Prettier stable
SyntaxError: Unexpected token `{`, expected the token `;` (1:21)
> 1 | const e = match (a) {
| ^
2 | 1 => true,
3 | 'foo' => false,
4 | 2 => {obj: 'literal'},
// Prettier main
const e = match (a) {
1 => true,
"foo" => false,
2 => { obj: "literal" },
};
```
@@ -61,6 +61,7 @@ function handleOwnLineComment(context) {
handleClassComments, handleClassComments,
handleForComments, handleForComments,
handleUnionTypeComments, handleUnionTypeComments,
handleMatchOrPatternComments,
handleOnlyComments, handleOnlyComments,
handleModuleSpecifiersComments, handleModuleSpecifiersComments,
handleAssignmentPatternComments, handleAssignmentPatternComments,
@@ -770,6 +771,36 @@ function handleUnionTypeComments({
return false; return false;
} }
function handleMatchOrPatternComments({
comment,
precedingNode,
enclosingNode,
followingNode,
}) {
if (enclosingNode && enclosingNode.type === "MatchOrPattern") {
if (isPrettierIgnoreComment(comment)) {
followingNode.prettierIgnore = true;
comment.unignore = true;
}
if (precedingNode) {
addTrailingComment(precedingNode, comment);
return true;
}
return false;
}
if (
followingNode &&
followingNode.type === "MatchOrPattern" &&
isPrettierIgnoreComment(comment)
) {
followingNode.types[0].prettierIgnore = true;
comment.unignore = true;
}
return false;
}
function handlePropertyComments({ comment, enclosingNode }) { function handlePropertyComments({ comment, enclosingNode }) {
if (isObjectProperty(enclosingNode)) { if (isObjectProperty(enclosingNode)) {
addLeadingComment(enclosingNode, comment); addLeadingComment(enclosingNode, comment);
@@ -74,6 +74,7 @@ function willPrintOwnComments(path) {
(parent.type === "JSXSpreadAttribute" || (parent.type === "JSXSpreadAttribute" ||
parent.type === "JSXSpreadChild" || parent.type === "JSXSpreadChild" ||
isUnionType(parent) || isUnionType(parent) ||
parent.type === "MatchOrPattern" ||
((parent.type === "ClassDeclaration" || ((parent.type === "ClassDeclaration" ||
parent.type === "ClassExpression") && parent.type === "ClassExpression") &&
parent.superClass === node)))) && parent.superClass === node)))) &&
+7 -1
View File
@@ -621,6 +621,7 @@ function needsParens(path, options) {
case "UnionTypeAnnotation": case "UnionTypeAnnotation":
return ( return (
parent.type === "TypeOperator" || parent.type === "TypeOperator" ||
parent.type === "KeyofTypeAnnotation" ||
parent.type === "ArrayTypeAnnotation" || parent.type === "ArrayTypeAnnotation" ||
parent.type === "NullableTypeAnnotation" || parent.type === "NullableTypeAnnotation" ||
parent.type === "IntersectionTypeAnnotation" || parent.type === "IntersectionTypeAnnotation" ||
@@ -877,6 +878,7 @@ function needsParens(path, options) {
case "LogicalExpression": case "LogicalExpression":
case "AwaitExpression": case "AwaitExpression":
case "TSTypeAssertion": case "TSTypeAssertion":
case "MatchExpressionCase":
return true; return true;
case "ConditionalExpression": case "ConditionalExpression":
@@ -985,11 +987,15 @@ function needsParens(path, options) {
parent.type !== "ThrowStatement" && parent.type !== "ThrowStatement" &&
parent.type !== "TypeCastExpression" && parent.type !== "TypeCastExpression" &&
parent.type !== "VariableDeclarator" && parent.type !== "VariableDeclarator" &&
parent.type !== "YieldExpression") parent.type !== "YieldExpression" &&
parent.type !== "MatchExpressionCase")
); );
case "TSInstantiationExpression": case "TSInstantiationExpression":
return key === "object" && isMemberExpression(parent); return key === "object" && isMemberExpression(parent);
case "MatchOrPattern":
return parent.type === "MatchAsPattern";
} }
return false; return false;
+2
View File
@@ -18,6 +18,8 @@ const parseOptions = {
esproposal_decorators: true, esproposal_decorators: true,
// `esproposal_export_star_as` (boolean, default `false`) - enable parsing of `export * as` syntax // `esproposal_export_star_as` (boolean, default `false`) - enable parsing of `export * as` syntax
esproposal_export_star_as: true, esproposal_export_star_as: true,
// Undocumented
pattern_matching: true,
// `types` (boolean, default `true`) - enable parsing of Flow types // `types` (boolean, default `true`) - enable parsing of Flow types
// types: true, // types: true,
// `use_strict` (boolean, default `false`) - treat the file as strict, without needing a "use strict" directive // `use_strict` (boolean, default `false`) - treat the file as strict, without needing a "use strict" directive
+2 -2
View File
@@ -31,8 +31,8 @@ function createParseError(error) {
const parseOptions = { const parseOptions = {
flow: "all", flow: "all",
babel: false, babel: false,
// Not supported yet // enableExperimentalComponentSyntax: true, // Enable by default
enableExperimentalFlowMatchSyntax: false, // enableExperimentalFlowMatchSyntax: true, // Enable by default
tokens: false, tokens: false,
allowReturnOutsideFunction: true, allowReturnOutsideFunction: true,
}; };
+16 -7
View File
@@ -175,15 +175,24 @@ function isConciselyPrintedArray(node, options) {
} }
function isLineAfterElementEmpty({ node }, { originalText: text }) { function isLineAfterElementEmpty({ node }, { originalText: text }) {
const skipComment = (idx) => let currentIdx = locEnd(node);
skipInlineComment(text, skipTrailingComment(text, idx)); if (currentIdx === locStart(node)) {
return false;
}
const skipToComma = (currentIdx) => const { length } = text;
text[currentIdx] === "," while (currentIdx < length) {
? currentIdx if (text[currentIdx] === ",") {
: skipToComma(skipComment(currentIdx + 1)); break;
}
return isNextLineEmptyAfterIndex(text, skipToComma(locEnd(node))); currentIdx = skipInlineComment(
text,
skipTrailingComment(text, currentIdx + 1),
);
}
return isNextLineEmptyAfterIndex(text, currentIdx);
} }
function printArrayElements(path, options, print, elementsProperty, inexact) { function printArrayElements(path, options, print, elementsProperty, inexact) {
+1
View File
@@ -52,6 +52,7 @@ function printBlock(path, options, print) {
parent.type === "ModuleExpression" || parent.type === "ModuleExpression" ||
(parent.type === "CatchClause" && !parentParent.finalizer) || (parent.type === "CatchClause" && !parentParent.finalizer) ||
parent.type === "TSModuleDeclaration" || parent.type === "TSModuleDeclaration" ||
parent.type === "MatchStatementCase" ||
node.type === "StaticBlock" node.type === "StaticBlock"
) )
) { ) {
+21
View File
@@ -28,6 +28,7 @@ import {
import { printInterface } from "./interface.js"; import { printInterface } from "./interface.js";
import { printBigInt } from "./literal.js"; import { printBigInt } from "./literal.js";
import { printFlowMappedTypeProperty } from "./mapped-type.js"; import { printFlowMappedTypeProperty } from "./mapped-type.js";
import { printMatch, printMatchCase, printMatchPattern } from "./match.js";
import { import {
printDeclareToken, printDeclareToken,
printOptionalToken, printOptionalToken,
@@ -323,6 +324,26 @@ function printFlow(path, options, print) {
case "AsConstExpression": case "AsConstExpression":
case "SatisfiesExpression": case "SatisfiesExpression":
return printBinaryCastExpression(path, options, print); return printBinaryCastExpression(path, options, print);
case "MatchExpression":
case "MatchStatement":
return printMatch(path, options, print);
case "MatchExpressionCase":
case "MatchStatementCase":
return printMatchCase(path, options, print);
case "MatchOrPattern":
case "MatchAsPattern":
case "MatchWildcardPattern":
case "MatchLiteralPattern":
case "MatchUnaryPattern":
case "MatchIdentifierPattern":
case "MatchMemberPattern":
case "MatchBindingPattern":
case "MatchObjectPattern":
case "MatchObjectPatternProperty":
case "MatchRestPattern":
case "MatchArrayPattern":
return printMatchPattern(path, options, print);
} }
} }
+1
View File
@@ -496,6 +496,7 @@ const NO_WRAP_PARENTS = new Set([
"OptionalCallExpression", "OptionalCallExpression",
"ConditionalExpression", "ConditionalExpression",
"JsExpressionRoot", "JsExpressionRoot",
"MatchExpressionCase",
]); ]);
function maybeWrapJsxElementInParens(path, elem, options) { function maybeWrapJsxElementInParens(path, elem, options) {
const { parent } = path; const { parent } = path;
+249
View File
@@ -0,0 +1,249 @@
import {
align,
group,
hardline,
ifBreak,
indent,
join,
line,
softline,
} from "../../document/builders.js";
import {
printComments,
printDanglingComments,
} from "../../main/comments/print.js";
import pathNeedsParens from "../needs-parens.js";
import {
CommentCheckFlags,
createTypeCheckFunction,
hasComment,
hasLeadingOwnLineComment,
isNextLineEmpty,
} from "../utils/index.js";
/*
- `MatchExpression` (Flow)
- `MatchStatement` (Flow)
*/
export function printMatch(path, options, print) {
const { node } = path;
return [
group(["match (", indent([softline, print("argument")]), softline, ")"]),
" {",
node.cases.length > 0
? indent([
hardline,
join(
hardline,
path.map(
({ node, isLast }) => [
print(),
!isLast && isNextLineEmpty(node, options) ? hardline : "",
],
"cases",
),
),
])
: "",
hardline,
"}",
];
}
/*
- `MatchExpressionCase` (Flow)
- `MatchStatementCase` (Flow)
*/
export function printMatchCase(path, options, print) {
const { node } = path;
const comment = hasComment(node, CommentCheckFlags.Dangling)
? [" ", printDanglingComments(path, options)]
: [];
const body =
node.type === "MatchStatementCase"
? [" ", print("body")]
: indent([line, print("body"), ","]);
return [
print("pattern"),
node.guard ? group([indent([line, "if (", print("guard"), ")"])]) : "",
group([" =>", comment, body]),
];
}
/*
- `MatchOrPattern` (Flow)
- `MatchAsPattern` (Flow)
- `MatchWildcardPattern` (Flow)
- `MatchLiteralPattern` (Flow)
- `MatchUnaryPattern` (Flow)
- `MatchIdentifierPattern` (Flow)
- `MatchMemberPattern` (Flow)
- `MatchBindingPattern` (Flow)
- `MatchObjectPattern` (Flow)
- `MatchArrayPattern` (Flow)
- `MatchObjectPatternProperty` (Flow)
- `MatchRestPattern` (Flow)
*/
export function printMatchPattern(path, options, print) {
const { node } = path;
switch (node.type) {
case "MatchOrPattern":
return printMatchOrPattern(path, options, print);
case "MatchAsPattern":
return [print("pattern"), " as ", print("target")];
case "MatchWildcardPattern":
return ["_"];
case "MatchLiteralPattern":
return print("literal");
case "MatchUnaryPattern":
return [node.operator, print("argument")];
case "MatchIdentifierPattern":
return print("id");
case "MatchMemberPattern": {
const property =
node.property.type === "Identifier"
? [".", print("property")]
: ["[", indent([softline, print("property")]), softline, "]"];
return group([print("base"), property]);
}
case "MatchBindingPattern":
return [node.kind, " ", print("id")];
case "MatchObjectPattern": {
const properties = path.map(print, "properties");
if (node.rest) {
properties.push(print("rest"));
}
return group([
"{",
indent([softline, join([",", line], properties)]),
node.rest ? "" : ifBreak(","),
softline,
"}",
]);
}
case "MatchArrayPattern": {
const elements = path.map(print, "elements");
if (node.rest) {
elements.push(print("rest"));
}
return group([
"[",
indent([softline, join([",", line], elements)]),
node.rest ? "" : ifBreak(","),
softline,
"]",
]);
}
case "MatchObjectPatternProperty":
if (node.shorthand) {
return print("pattern");
}
return group([print("key"), ":", indent([line, print("pattern")])]);
case "MatchRestPattern": {
const parts = ["..."];
if (node.argument) {
parts.push(print("argument"));
}
return parts;
}
}
}
const isSimpleMatchPattern = createTypeCheckFunction([
"MatchWildcardPattern",
"MatchLiteralPattern",
"MatchUnaryPattern",
"MatchIdentifierPattern",
]);
function shouldHugMatchOrPattern(node) {
const { patterns } = node;
if (patterns.some((node) => hasComment(node))) {
return false;
}
const objectPattern = patterns.find(
(node) => node.type === "MatchObjectPattern",
);
if (!objectPattern) {
return false;
}
return patterns.every(
(node) => node === objectPattern || isSimpleMatchPattern(node),
);
}
function shouldHugMatchPattern(node) {
if (isSimpleMatchPattern(node) || node.type === "MatchObjectPattern") {
return true;
}
if (node.type === "MatchOrPattern") {
return shouldHugMatchOrPattern(node);
}
return false;
}
function printMatchOrPattern(path, options, print) {
const { node } = path;
// single-line variation
// A | B | C
// multi-line variation
// | A
// | B
// | C
const { parent } = path;
const shouldIndent =
parent.type !== "MatchStatementCase" &&
parent.type !== "MatchExpressionCase" &&
parent.type !== "MatchArrayPattern" &&
parent.type !== "MatchObjectPatternProperty" &&
!hasLeadingOwnLineComment(options.originalText, node);
// {
// a: foo
// } | null | void
// should be inlined and not be printed in the multi-line variant
const shouldHug = shouldHugMatchPattern(node);
// We want to align the children but without its comment, so it looks like
// | child1
// // comment
// | child2
const printed = path.map((patternPath) => {
let printedPattern = print();
if (!shouldHug) {
printedPattern = align(2, printedPattern);
}
return printComments(patternPath, printedPattern, options);
}, "patterns");
if (shouldHug) {
return join(" | ", printed);
}
const code = [ifBreak(["| "]), join([line, "| "], printed)];
if (pathNeedsParens(path, options)) {
return group([indent([ifBreak([softline]), code]), softline]);
}
if (parent.type === "MatchArrayPattern" && parent.elements.length > 1) {
return group([
indent([ifBreak(["(", softline]), code]),
softline,
ifBreak(")"),
]);
}
return group(shouldIndent ? indent(code) : code);
}
@@ -67,24 +67,6 @@ const excludeNodeTypes = new Set([
// Babel, Won't exist since we use `createImportExpressions` when parsing with babel // Babel, Won't exist since we use `createImportExpressions` when parsing with babel
"Import", "Import",
// Flow, not supported
"MatchArrayPattern",
"MatchAsPattern",
"MatchBindingPattern",
"MatchExpression",
"MatchExpressionCase",
"MatchIdentifierPattern",
"MatchLiteralPattern",
"MatchMemberPattern",
"MatchObjectPattern",
"MatchObjectPatternProperty",
"MatchOrPattern",
"MatchRestPattern",
"MatchStatement",
"MatchStatementCase",
"MatchUnaryPattern",
"MatchWildcardPattern",
// Babel v7.28.0 // Babel v7.28.0
// https://github.com/prettier/prettier/pull/17696 // https://github.com/prettier/prettier/pull/17696
"VoidPattern", "VoidPattern",
File diff suppressed because it is too large. Load diff
+153
View File
@@ -0,0 +1,153 @@
// Type of body and abnormal handling
{
declare const x: empty;
const out = match (x) {};
out as empty; // OK
}
declare const x: 1 | 2;
{
const out = match (x) {
1 => true,
2 => false,
};
out as boolean; // OK
out as empty; // ERROR
}
{
const out = match (x) {
1 => true,
2 => 's',
};
out as boolean | string; // OK
out as empty; // ERROR
}
declare function invariant(boolean): empty;
{
const out = match (x) {
1 => true,
2 => invariant(false),
};
out as boolean; // OK
out as empty; // ERROR
}
function f1() {
const out = match (x) {
1 => invariant(false),
2 => invariant(false),
};
out; // ERROR: unreachable
}
// Throws in guards
function f2() {
const out = match (x) {
1 if (invariant(false)) => true,
_ => 's',
};
out as string; // OK
out as empty; // ERROR
}
// Nested matches
{
const out = match (x) {
1 => 1,
const a => match (a) {
const a => a,
},
};
out as number; // OK
}
// Guards can refine values which are not the argument
{
declare const y: number | string;
const out = match (x) {
1 if (typeof y === 'number') => y as number, // OK
const a if (a === 1) => a as 1,
_ => 0,
};
}
// Case body provider and refinement analysis
{
let target;
const out = match (x) {
1 => target = "foo",
2 => target = true,
};
target as string | boolean; // OK
}
{
let target;
const out = match (x) {
const a => target = "foo" as const,
};
a; // ERROR
target as "foo"; // OK
}
{
declare const x: [number] | number;
let target = null;
const out = match (x) {
[const a] => target = "foo",
const a => target = true,
};
a; // ERROR
target as string | boolean; // OK
}
{
let target;
const out = match (x) {
1 => target = "foo",
2 => invariant(false),
};
target as string; // OK
}
{
declare const o: {prop: number};
const out = match (x) {
1 => o.prop = 1,
2 => o.prop = 2,
};
o.prop as 1 | 2; // OK
}
{
const a = [];
const out = match (x) {
1 => a.push(1),
2 => a.push(2),
};
a as Array<number>; // OK
}
// Natural inference hint
{
const out: 'a' | 'b' = match (x) {
1 => 'b', // OK
2 => 'xxx', // ERROR: 'xxx' is not a subtype of 'a' | 'b'
};
}
{
declare const dict: {['a' | 'b']: number};
dict[match (x) {
1 => 'b', // OK
2 => 'xxx', // ERROR: 'xxx' is not a subtype of 'a' | 'b'
}];
}
@@ -0,0 +1 @@
runFormatTest(import.meta, ["flow"]);
+83
View File
@@ -0,0 +1,83 @@
// Annotation hint
type F = string => boolean;
{
declare const x: 'a';
const out: F = match (x) {
'a' => (y => true), // OK
};
}
// Sibling before
{
declare const x: 'a' | 'b';
const out = match (x) {
'a' => [1],
'b' => [], // Should be `Array<number>`
};
out as Array<number>; // OK
}
// Sibling after
{
declare const x: 'a' | 'b';
const out = match (x) {
'a' => [], // Should be `Array<number>`
'b' => [1],
};
out as Array<number>; // OK
}
// Multiple siblings, one valid
{
declare const x: 'a' | 'b' | 'c' | 'd';
const out = match (x) {
'a' => 1,
'b' => {},
'c' => [1],
'd' => [], // Should be `Array<number>`
};
out as number | {} | Array<number>; // OK
}
// Multiple siblings, multiple valid
{
declare const x: 'a' | 'b' | 'c';
const out = match (x) {
'a' => [true],
'b' => [1],
'c' => [],
};
out as Array<boolean> | Array<number>; // OK
}
{
declare const x: 'a' | 'b' | 'c';
const out = match (x) {
'a' => ((x: number) => 1),
'b' => ((x: string) => true),
'c' => (x => x as number), // OK
};
}
// Cycles avoided
{
declare const x: 'a' | 'b';
const out: (x: number) => void = match (x) { // OK
'a' => ((x) => {}),
'b' => ((x) => {}),
};
}
{
declare const x: 'a' | 'b';
const out: Array<number> = match (x) { // OK
'a' => [],
'b' => [],
};
}
+399
View File
@@ -0,0 +1,399 @@
// Primitive value union
{
declare const x: 1 | -2 | 3n | 's' | false | null;
const e1 = match (x) {
1 => 0,
-2 => 0,
3n => 0,
's' => 0,
false => 0,
null => 0,
};
const e2 = match (x) { // ERROR: not all members checked
1 => 0,
false => 0,
3n => 0,
null => 0,
};
}
// Identifier patterns
{
declare const x: 1 | 2;
declare const one: 1;
declare const two: 2;
const e1 = match (x) {
one => 0,
two => 0,
};
const e2 = match (x) { // ERROR: `2` not checked
one => 0,
};
}
// `undefined`
{
declare const x: 1 | void;
const e1 = match (x) {
1 => 0,
undefined => 0,
};
const e2 = match (x) { // ERROR: `undefined` not checked
1 => 0,
};
}
// Maybe types
{
declare const x: ?1;
const e1 = match (x) {
1 => 0,
undefined => 0,
null => 0,
};
const e2 = match (x) { // ERROR: `null` and `undefined` not checked
1 => 0,
};
}
// Member patterns
{
declare const x: 1 | 2;
declare const o: {
one: 1,
two: 2,
};
const e1 = match (x) {
o.one => 0,
o.two => 0,
};
const e2 = match (x) { // ERROR: `2` not checked
o.one => 0,
};
}
// `as` pattern refines using its pattern
{
declare const x: 1 | 2;
const e1 = match (x) {
1 as a => a as 1, // OK
2 as a => a as 2, // OK
};
}
{
declare const x: {type: 'foo', value: 1}
| {type: 'bar', value: 2};
// Nested `as` pattern
const e1 = match (x) {
{type: 'foo' as a, const value} => (a as 'foo', value as 1), // OK
{type: 'bar' as a, const value} => (a as 'bar', value as 2), // OK
};
}
// Top level binding and wildcard
{
declare const x: 1 | 2;
const e1 = match (x) {
1 => 0,
const a => a as 2, // OK
};
const e2 = match (x) {
1 => 0,
_ => 0, // OK
};
}
// Non-ident/member argument still works
{
declare const f: () => 1 | 2;
const e1 = match (f()) {
1 => 0,
2 => 0,
};
const e2 = match (f()) { // ERROR: `2` not checked
1 => 0,
};
}
// Or pattern
{
declare const x: 1 | 2 | 3;
const e1 = match (x) {
1 | 2 | 3 => true,
};
const e2 = match (x) { // ERROR: `3` not checked
1 | 2 => true,
};
}
// Patterns with guard could match or not match
{
declare const x: 1 | 2;
declare function f(): boolean;
const e1 = match (x) { // ERROR: `2` not checked
1 => 0,
2 if (f()) => 0,
};
const e2 = match (x) {
1 => 0,
2 if (f()) => 0,
2 => 0,
};
}
// Property exists
{
declare const x: {foo: void, a: 0} | {bar: void, a: 1};
const e1 = match (x) {
{foo: _, const a} => a as 0, // OK
{bar: _, const a} => a as 1, // OK
};
}
{
declare const x:
| {foo: void, a: 0}
| {bar: void, a: 1}
| {baz: void, a: 2}
| {zap: void, a: 3};
declare const u: void;
declare const o: {u: void};
const e1 = match (x) {
{foo: u, const a} => a as 0, // OK
{bar: o.u, const a} => a as 1, // OK
{baz: undefined as v, const a} => a as 2, // OK
{zap: u, const a} => a as 3, // OK
};
}
// Disjoint object union
{
declare const x: {type: 'foo', val: number}
| {type: 'bar', val: string}
| {type: 'baz', val: boolean};
const e1 = match (x) {
{type: 'foo', val: const a} => a as number, // OK
{type: 'bar', val: const a} => a as string, // OK
{type: 'baz', val: const a} => a as boolean, // OK
};
const e2 = match (x) { // ERROR: `type: 'baz'` not checked
{type: 'foo', val: const a} => a as number, // OK
{type: 'bar', val: const a} => a as string, // OK
};
// Using idents as pattern
declare const foo: 'foo';
declare const bar: 'bar';
declare const baz: 'baz';
const e3 = match (x) {
{type: foo, val: const a} => a as number, // OK
{type: bar, val: const a} => a as string, // OK
{type: baz, val: const a} => a as boolean, // OK
};
// Using members as pattern
declare const o: {
foo: 'foo',
bar: 'bar',
baz: 'baz',
};
const e4 = match (x) {
{type: o.foo, val: const a} => a as number, // OK
{type: o.bar, val: const a} => a as string, // OK
{type: o.baz, val: const a} => a as boolean, // OK
};
}
// Disjoint object union with multiple pivot props
{
declare const x: {type: 'foo', val: number}
| {type: 'bar', n: 1, val: string}
| {type: 'bar', n: 2, val: boolean};
const e1 = match (x) {
{type: 'foo', val: const a} => a as number, // OK
{type: 'bar', val: const a, ...} => a as string | boolean, // OK
};
const e2 = match (x) {
{type: 'foo', val: const a} => a as number, // OK
{type: 'bar', n: 1, val: const a} => a as string, // OK
{type: 'bar', n: 2, val: const a} => a as boolean, // OK
};
const e3 = match (x) { // ERROR: `type: 'bar', n: 2` not checked
{type: 'foo', val: const a} => a as number, // OK
{type: 'bar', n: 1, val: const a} => a as string, // OK
};
}
// Combo union of object with sentinel property and primitive value
{
declare const x: null | {type: 'bar', val: number};
const e1 = match (x) {
{type: 'bar', val: const a} => a as number, // OK
null => 0,
};
}
// Or pattern: objects
{
declare const x: {type: 'foo', val: number}
| {type: 'bar', val: string}
| {type: 'baz', val: boolean};
const e1 = match (x) {
{type: 'foo', ...} | {type: 'bar', ...} | {type: 'baz', ...} => 0,
};
const e2 = match (x) { // ERROR: `type: 'bar'` not checked
{type: 'foo', ...} | {type: 'baz', ...} => 0,
};
}
// Disjoint tuple union
{
declare const x: ['foo', number]
| ['bar', string]
| ['baz', boolean];
const e1 = match (x) {
['foo', const a] => a as number, // OK
['bar', const a] => a as string, // OK
['baz', const a] => a as boolean, // OK
};
const e2 = match (x) { // ERROR: `'baz'` element not checked
['foo', const a] => a as number, // OK
['bar', const a] => a as string, // OK
};
// Using idents as pattern
declare const foo: 'foo';
declare const bar: 'bar';
declare const baz: 'baz';
const e3 = match (x) {
[foo, const a] => a as number, // OK
[bar, const a] => a as string, // OK
[baz, const a] => a as boolean, // OK
};
}
// Combo union of tuples with sentinel property and primitive value
{
declare const x: null | ['bar', number] | ['foo', string];
const e1 = match (x) {
['bar', const a] => a as number, // OK
['foo', const a] => a as string, // OK
null => 0,
};
}
// Tuple length refinements
{
declare const x: [number]
| [string, string]
| [boolean, boolean, boolean];
const e1 = match (x) {
[const a] => a as number, // OK
[const a, _] => a as string, // OK
[const a, _, _] => a as empty, // ERROR: `boolean` is not `empty`
};
const e2 = match (x) {
[...] => 0, // OK: matches all
}
const e3 = match (x) {
[const a, _, ...] => a as string | boolean, // OK
[const a] => a as number, // OK
}
}
{
declare const x: [number] | Array<string>;
const e1 = match (x) {
[] => 0, // OK
[const a] => a as string, // ERROR: `number` is not `string`
[const a, _] => a as string, // OK
const d => d as Array<string>, // OK: tuple checked, but array could have other lengths
};
const e2 = match (x) {
[...] => 0, // OK: matches all
}
}
// Optional tuple elements
{
declare const x: [a: 0, b?: 1, c?: 2];
const e1 = match (x) {
[_, ...] => 0,
};
const e2 = match (x) { // ERROR: does not match all possibilities
[_, _, ...] => 0,
};
const e3 = match (x) { // ERROR: does not match all possibilities
[_] => 0,
};
const e4 = match (x) { // ERROR: does not match all possibilities
[_, _, _] => 0,
};
}
// Inexact tuple types
{
declare const x: [a: 0, ...];
const e1 = match (x) {
[_, ...] => 0,
};
const e2 = match (x) { // ERROR: does not match all elements
[_] => 0,
};
}
// Exhaustive checking error points to definition
{
type T = {foo: 1} | {foo: 2};
declare const x: T;
const e1 = match (x) {
{foo: 1} => 0,
};
}
@@ -0,0 +1,191 @@
// Match expressions only allow `const` bindings
{
declare const x: 0 | 1 | [2] | {a: 3, b: 4};
const e1 = match (x) {
[...let a] => 0, // ERROR
{let a, ...let b} => 0, // ERROR
0 as let a => 0, // ERROR
let a => 0, // ERROR
};
const e2 = match (x) {
[...var a] => 0, // ERROR
{var a, ...var b} => 0, // ERROR
0 as var a => 0, // ERROR
var a => 0, // ERROR
};
}
// Invalid numeric property
{
declare const x: {1: true};
const e1 = match (x) {
{1.1: _} => 0, // ERROR
_ => 0,
};
}
// Unary pattern on `0` banned
{
declare const x: 0;
const e1 = match (x) {
-0 => true, // ERROR
+0 => true, // ERROR
0 => true, // OK
};
}
// Unary `+` on bigint banned
{
declare const x: 1n;
const e1 = match (x) {
+1n => true, // ERROR
1n => true, // OK
};
}
// Duplicate object keys banned
{
declare const x: {foo: boolean};
const e1 = match (x) {
{foo: true, foo: false} => 0, // ERROR
_ => 0,
};
const e2 = match (x) {
{foo: true, const foo} => 0, // ERROR
_ => 0,
};
const e3 = match (x) {
{foo: true, 'foo': false} => 0, // ERROR
_ => 0,
};
}
// Duplicate binding names
{
declare const x: [boolean, boolean];
const e1 = match (x) {
[const a, true as a] => 0, // ERROR
[const a, const a] => 0, // ERROR
_ => 0,
};
const e2 = match (x) {
[const a, true as const a] => 0, // ERROR
[const a, ...const a] => 0, // ERROR
_ => 0,
};
}
{
declare const x: {a: boolean, b: boolean};
const e1 = match (x) {
{const a, b: true as a} => 0, // ERROR
{const a, ...const a} => 0, // ERROR
_ => 0,
};
}
// Bindings in 'or' patterns are not yet supported
{
declare const x: [boolean] | [string];
const e1 = match (x) {
[true as a] | ['s'] => 0, // ERROR
[false as const a] | ['s'] => 0, // ERROR
_ => 0,
};
const e2 = match (x) {
[true as const a] | ['s'] => 0, // ERROR
['t'] | [...const a] => 0, // ERROR
_ => 0,
};
}
{
declare const x: {a: boolean} | {a: string};
const e1 = match (x) {
{a: true as a} | {a: 's'} => 0, // ERROR
{a: false as const a} | {a: 's'} => 0, // ERROR
_ => 0,
};
}
// As pattern on binding pattern
{
declare const x: [boolean];
const e1 = match (x) {
[const a as b] => 0, // ERROR
_ => 0,
};
const e2 = match (x) {
const a as const b => 0, // ERROR
};
}
// Reference before declaration
{
declare const x: [number, number];
const out = match (x) {
[a, const a] => a, // ERROR: reference before declaration
_ => 0,
};
}
{
declare const x: [number, {foo: number}];
const out = match (x) {
[a.foo, const a] => a, // ERROR: reference before declaration
_ => 0,
};
}
// Reference binding introduced in same pattern
{
declare const x: [number, number];
const out = match (x) {
[const a, a] => a, // ERROR
_ => 0,
};
}
{
declare const x: [{foo: number}, number];
const out = match (x) {
[const a, a.foo] => a, // ERROR
_ => 0,
};
}
// // Invalid object property shorthand
// {
// declare const x: {foo: 1, bar: boolean};
// const out = match (x) {
// {foo} => 0, // ERROR
// {foo, bar: true} => 0, // ERROR
// _ => 0,
// };
// }
// BigInt property usage
{
declare const x: mixed;
const out = match (x) {
{1n: 1, ...} => 0, // ERROR
_ => 0,
};
}
+197
View File
@@ -0,0 +1,197 @@
// Top level binding with `const`
{
declare const x: number;
const out = match (x) {
const a => a,
};
out as number; // OK
out as empty; // ERROR
}
// Binding doesn't leak to outer scope
{
declare const x: number;
const out = match (x) {
const a => a,
};
a; // ERROR: cannot resolve name
}
// Array pattern
{
declare const x: [number];
const out = match (x) {
[const a] => a,
};
out as number; // OK
out as empty; // ERROR
}
// Object pattern
{
declare const x: {foo: number};
const out = match (x) {
{foo: const a} => a,
};
out as number; // OK
out as empty; // ERROR
}
// Object shorthand pattern
{
declare const x: {foo: number};
const out = match (x) {
{const foo} => foo,
};
out as number; // OK
out as empty; // ERROR
}
// Nested patterns
{
declare const x: {foo: [{bar: number}]};
const out1 = match (x) {
{foo: [{bar: const a}]} => a,
_ => 0,
};
out1 as number; // OK
out1 as empty; // ERROR
const out2 = match (x) {
{foo: const a} => a[0].bar,
};
out2 as number; // OK
out2 as empty; // ERROR
}
// Guards
{
declare const x: {foo: number};
declare const f: number => boolean;
const out = match (x) {
{foo: const n} if (n === 0) => n, // OK
{foo: const n} if (f(n)) => n, // OK
{foo: const n} if (n as empty) => n, // ERROR
_ => 0,
};
}
// As pattern
{
declare const x: {foo: [number]};
const out = match (x) {
{foo: [1] as n} => n as [number], // OK
{foo: [1 as n]} => n as number, // OK
_ => 0,
};
}
{
declare const x: {foo: [number]};
const out = match (x) {
{foo: [1] as const n} => n as [number], // OK
{foo: [1 as const n]} => n as number, // OK
_ => 0,
};
}
{
declare const x: {foo: {bar: number}};
const out = match (x) {
{foo: {bar: 1}} => 0, // OK
{foo: {bar: 2} as const a} => 0, // OK
{foo: {bar: 3 as const a}} => 0, // OK
{foo: {bar: const a}} => 0, // OK
_ => 0,
};
}
// Array rest
{
declare const x: [1, 2, 3];
const out1 = match (x) {
[1, 2, ...const xs] => xs as [3], // OK
_ => 0,
};
const out2 = match (x) {
[1, ...const xs] => xs as [2, 3], // OK
_ => 0,
};
const out3 = match (x) {
[...const xs] => xs as [1, 2, 3], // OK
_ => 0,
};
}
// Object rest
{
declare const x: {foo: 1, bar: 2, baz: 3};
const out1 = match (x) {
{foo: _, bar: _, ...const xs} => xs as {baz: 3}, // OK
};
const out2 = match (x) {
{bar: _, ...const xs} => xs as {foo: 1, baz: 3}, // OK
};
const out3 = match (x) {
{...const xs} => xs as {foo: 1, bar: 2, baz: 3}, // OK
};
}
// Identifier, member, literal
{
declare const A: 3;
declare const O: {B: 2};
declare const x: number;
const out = match (x) {
0 => 0,
-1 => 0,
+1 => 0,
A => 0,
O.B => 0,
_ => 0,
};
}
// Or pattern
{
declare const x: number;
const out = match (x) {
1 | 2 | 3 => 0,
_ => 0,
};
}
// Array pattern applied to array literal
{
const x = [1, 'foo'] as const;
const out = match (x) {
[_, const b] => b as string, // OK
};
}
// BigInt member pattern
{
declare const x: string;
declare const O: {[bigint]: 'foo'};
const out = match (x) {
O[1n] => 0, // OK
_ => 0,
};
}
@@ -0,0 +1,30 @@
declare const x: 1 | 2;
hook useHook() {}
component WithMatchStatement() {
match (x) {
1 => {
useHook(); // ERROR
}
2 => {}
}
return null
}
component WithMatchExpression() {
return match (x) {
1 => useHook(), // ERROR
2 => undefined,
};
}
match (x) {
1 => {
component NestedInMatch() {
useHook(); // OK
return null;
}
}
2 => {}
}
+180
View File
@@ -0,0 +1,180 @@
{
declare const x: empty;
match (x) {}; // OK
}
declare const x: 1 | 2;
match (x) {
1 => {}
2 => {}
}
function f1() {
match (x) {
1 => {
throw 0;
}
2 => {
throw 0;
}
};
x; // ERROR: unreachable
}
// Throws in guards
function f2() {
declare function invariant(boolean): empty;
match (x) {
1 => {}
2 if (invariant(false)) => {}
};
x; // OK: not unreachable
}
// Nested matches
{
match (x) {
1 => {}
const a => {
match (a) {
_ => {}
}
}
}
}
// Body makes refinements
{
let target;
match (x) {
1 => {
target = "foo";
}
2 => {
target = true;
}
}
target as string | boolean; // OK
}
{
let target;
match (x) {
const a => {
target = "foo" as const;
}
}
a; // ERROR
target as "foo"; // OK
}
{
declare const x: [number] | number;
let target = null;
match (x) {
[const a] => {
target = "foo";
}
const a => {
target = true;
}
}
a; // ERROR
target as string | boolean; // OK
}
{
let target;
match (x) {
1 => {
target = "foo";
}
2 => {
throw 0;
}
}
target as string; // OK
}
{
declare const o: {prop: number};
match (x) {
1 => {
o.prop = 1;
}
2 => {
o.prop = 2;
}
}
o.prop as 1 | 2; // OK
}
{
const a = [];
match (x) {
1 => {
a.push(1);
}
2 => {
a.push(2);
}
}
a as Array<number>; // OK
}
// Abnormal exits functions
function t1(): boolean {
match (x) {
1 => {
return true;
}
2 => {
return false;
}
}
}
function t2(): boolean {
match (x) {
1 => {
throw 0;
}
2 => {
return false;
}
}
}
// Abnormals in loops
{
for (let i = 0; i < 2; i++) {
match (i) {
0 => {
break;
}
_ => {}
}
}
}
{
label: for (let i = 0; i < 2; i++) {
match (i) {
0 => {
break label;
}
_ => {}
}
}
}
// // Invalid case body
// function t3() {
// match (x) {
// 1 => return 1; // ERROR
// 2 => throw false; // ERROR
// }
// }
@@ -0,0 +1,416 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`export.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
export default match (a) {}
=====================================output=====================================
export default match (a) {
};
================================================================================
`;
exports[`expression.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
const e = match (a) {
1 => true,
'foo' => false,
2 => {obj: 'literal'},
};
=====================================output=====================================
const e = match (a) {
1 => true,
"foo" => false,
2 => { obj: "literal" },
};
================================================================================
`;
exports[`expression-empty.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
const e = match (a) {};
=====================================output=====================================
const e = match (a) {
};
================================================================================
`;
exports[`expression-guards.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
const e = match (a) {
1 if (b) => true,
};
=====================================output=====================================
const e = match (a) {
1 if (b) => true,
};
================================================================================
`;
exports[`pattern-array.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
const e = match (x) {
[10] => 1,
[const y, 1] => y,
[1, ...] => 1,
[1, 2, ...const rest] => rest,
[...let rest] => rest,
[...var rest] => rest,
[{nested: [1, const x]}] => x,
};
=====================================output=====================================
const e = match (x) {
[10] => 1,
[const y, 1] => y,
[1, ...] => 1,
[1, 2, ...const rest] => rest,
[...let rest] => rest,
[...var rest] => rest,
[{nested: [1, const x]}] => x,
};
================================================================================
`;
exports[`pattern-core.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
const e = match (x) {
"s" => 1,
true => 1,
null => 1,
3 => 1,
4n => 1,
+5 => 1,
-6 => 1,
+7n => 1,
-8n => 1,
y => 1,
const y => y,
let y => y,
var y => y,
('s') => 1,
_ => 1,
};
=====================================output=====================================
const e = match (x) {
"s" => 1,
true => 1,
null => 1,
3 => 1,
4n => 1,
+5 => 1,
-6 => 1,
+7n => 1,
-8n => 1,
y => 1,
const y => y,
let y => y,
var y => y,
"s" => 1,
_ => 1,
};
================================================================================
`;
exports[`pattern-member.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
const e = match (x) {
foo.bar => true,
foo[1] => true,
foo[1n] => true,
foo["bar"] => true,
foo.bar[1] => true,
foo[1].bar["baz"] => true,
};
=====================================output=====================================
const e = match (x) {
foo.bar => true,
foo[1] => true,
foo[1n] => true,
foo["bar"] => true,
foo.bar[1] => true,
foo[1].bar["baz"] => true,
};
================================================================================
`;
exports[`pattern-object.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
const e = match (x) {
{foo: 1, bar: 2} => 1,
{'foo': 1} => 1,
{111: true} => 1,
{foo: const y} => y,
{const x, let y, var z} => y,
{const x, ...const y} => y,
{const x, ...let y} => y,
{const x, ...} => x,
};
=====================================output=====================================
const e = match (x) {
{foo: 1, bar: 2} => 1,
{"foo": 1} => 1,
{111: true} => 1,
{foo: const y} => y,
{const x, let y, var z} => y,
{const x, ...const y} => y,
{const x, ...let y} => y,
{const x, ...} => x,
};
================================================================================
`;
exports[`pattern-or-as.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
const e = match (x) {
"s" | true | null => 1,
| "s"
| true
| null => 1,
{foo: 1 | 2} => 2,
{foo: | 1
| 2} => 2,
{foo: [1] as y} => y,
{foo: 1 | 2 | 3 as y} => y,
{foo: (1 | 2 | 3) as y} => y,
{foo: [1] as const y} => y,
};
=====================================output=====================================
const e = match (x) {
"s" | true | null => 1,
"s" | true | null => 1,
{foo: 1 | 2} => 2,
{foo: 1 | 2} => 2,
{foo: [1] as y} => y,
{foo: (1 | 2 | 3) as y} => y,
{foo: (1 | 2 | 3) as y} => y,
{foo: [1] as const y} => y,
};
================================================================================
`;
exports[`regression.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
type match = number;
{
const match = 1;
}
function match(match: match) {}
const a = match(1);
const b = match(1).f();
const c = match(1)
{
// block statement
}
const d = match(1) + 2;
const e = match(1)[2];
const f = match(1) ? 2 : 3;
const g = match(1) < 2;
const h = match(1) && 2;
const i = match();
const j = match(...b);
match(1);
match(1).f();
match(1)
{
// block statement
}
match(1) + 2;
match(1)[2];
match(1) ? 2 : 3;
match(1) < 2;
match(1) && 2;
match();
match(...b);
=====================================output=====================================
type match = number;
{
const match = 1;
}
function match(match: match) {}
const a = match(1);
const b = match(1).f();
const c = match(1);
{
// block statement
}
const d = match(1) + 2;
const e = match(1)[2];
const f = match(1) ? 2 : 3;
const g = match(1) < 2;
const h = match(1) && 2;
const i = match();
const j = match(...b);
match(1);
match(1).f();
match(1);
{
// block statement
}
match(1) + 2;
match(1)[2];
match(1) ? 2 : 3;
match(1) < 2;
match(1) && 2;
match();
match(...b);
================================================================================
`;
exports[`statement.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
match (a) {
1 => {
const x = 1;
f(x);
},
'foo' => {
// empty
},
2 => {
label: 'literal'
},
}
=====================================output=====================================
match (a) {
1 => {
const x = 1;
f(x);
}
"foo" => {
// empty
}
2 => {
label: "literal";
}
}
================================================================================
`;
exports[`statement-empty.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
match (a) {}
=====================================output=====================================
match (a) {
}
================================================================================
`;
exports[`statement-guards.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
match (a) {
1 if (b) => {
const x = 1;
},
'foo' if (f()) => {
const x = 2;
},
2 if (x < y) => {
const x = 3;
},
}
=====================================output=====================================
match (a) {
1 if (b) => {
const x = 1;
}
"foo" if (f()) => {
const x = 2;
}
2 if (x < y) => {
const x = 3;
}
}
================================================================================
`;
exports[`statement-sequence-arg.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
match (a, b) {}
=====================================output=====================================
match ((a, b)) {
}
================================================================================
`;
@@ -0,0 +1 @@
export default match (a) {}
@@ -0,0 +1 @@
const e = match (a) {};
@@ -0,0 +1,3 @@
const e = match (a) {
1 if (b) => true,
};
@@ -0,0 +1,5 @@
const e = match (a) {
1 => true,
'foo' => false,
2 => {obj: 'literal'},
};
@@ -0,0 +1,2 @@
// Tests taken from https://github.com/facebook/hermes/tree/83c8115/test/Parser/flow/match
runFormatTest(import.meta, ["flow"]);
@@ -0,0 +1,9 @@
const e = match (x) {
[10] => 1,
[const y, 1] => y,
[1, ...] => 1,
[1, 2, ...const rest] => rest,
[...let rest] => rest,
[...var rest] => rest,
[{nested: [1, const x]}] => x,
};
@@ -0,0 +1,17 @@
const e = match (x) {
"s" => 1,
true => 1,
null => 1,
3 => 1,
4n => 1,
+5 => 1,
-6 => 1,
+7n => 1,
-8n => 1,
y => 1,
const y => y,
let y => y,
var y => y,
('s') => 1,
_ => 1,
};
@@ -0,0 +1,8 @@
const e = match (x) {
foo.bar => true,
foo[1] => true,
foo[1n] => true,
foo["bar"] => true,
foo.bar[1] => true,
foo[1].bar["baz"] => true,
};
@@ -0,0 +1,10 @@
const e = match (x) {
{foo: 1, bar: 2} => 1,
{'foo': 1} => 1,
{111: true} => 1,
{foo: const y} => y,
{const x, let y, var z} => y,
{const x, ...const y} => y,
{const x, ...let y} => y,
{const x, ...} => x,
};
@@ -0,0 +1,13 @@
const e = match (x) {
"s" | true | null => 1,
| "s"
| true
| null => 1,
{foo: 1 | 2} => 2,
{foo: | 1
| 2} => 2,
{foo: [1] as y} => y,
{foo: 1 | 2 | 3 as y} => y,
{foo: (1 | 2 | 3) as y} => y,
{foo: [1] as const y} => y,
};
@@ -0,0 +1,31 @@
type match = number;
{
const match = 1;
}
function match(match: match) {}
const a = match(1);
const b = match(1).f();
const c = match(1)
{
// block statement
}
const d = match(1) + 2;
const e = match(1)[2];
const f = match(1) ? 2 : 3;
const g = match(1) < 2;
const h = match(1) && 2;
const i = match();
const j = match(...b);
match(1);
match(1).f();
match(1)
{
// block statement
}
match(1) + 2;
match(1)[2];
match(1) ? 2 : 3;
match(1) < 2;
match(1) && 2;
match();
match(...b);
@@ -0,0 +1 @@
match (a) {}
@@ -0,0 +1,11 @@
match (a) {
1 if (b) => {
const x = 1;
},
'foo' if (f()) => {
const x = 2;
},
2 if (x < y) => {
const x = 3;
},
}
@@ -0,0 +1 @@
match (a, b) {}
@@ -0,0 +1,12 @@
match (a) {
1 => {
const x = 1;
f(x);
},
'foo' => {
// empty
},
2 => {
label: 'literal'
},
}
@@ -1,5 +1 @@
runFormatTest(import.meta, ["flow"], { runFormatTest(import.meta, ["flow"]);
errors: {
"babel-flow": ["const-type-params.js"],
},
});
@@ -0,0 +1,31 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`snippet: #0 [flow] format 1`] = `
"\`match\` argument must not be empty (1:7)
> 1 | match () {}
| ^^"
`;
exports[`snippet: #0 [hermes] format 1`] = `
"'match' argument must not be empty (1:6)
> 1 | match () {}
| ^
Cause: 'match' argument must not be empty (1:6)
match () {}
^~"
`;
exports[`snippet: #1 [flow] format 1`] = `
"\`match\` argument cannot contain spread elements (1:8)
> 1 | match (...b) {}
| ^^^^"
`;
exports[`snippet: #1 [hermes] format 1`] = `
"'match' argument cannot contain spread elements (1:7)
> 1 | match (...b) {}
| ^
Cause: 'match' argument cannot contain spread elements (1:7)
match (...b) {}
^~~~"
`;
@@ -0,0 +1,12 @@
runFormatTest(
{
importMeta: import.meta,
snippets: [
// https://github.com/facebook/hermes/blob/83c811545b35dc2284ba27786314936e4c80ee6a/test/Parser/flow/match/statement-no-arg.js
"match () {}",
// https://github.com/facebook/hermes/blob/83c811545b35dc2284ba27786314936e4c80ee6a/test/Parser/flow/match/statement-spread-arg.js
"match (...b) {}",
],
},
["flow", "hermes"],
);
+58 -1
View File
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`visitor keys estree 1`] = ` exports[`visitor keys estree 1`] = `
{ {
@@ -523,6 +523,63 @@ exports[`visitor keys estree 1`] = `
"left", "left",
"right", "right",
], ],
"MatchArrayPattern": [
"elements",
"rest",
],
"MatchAsPattern": [
"pattern",
"target",
],
"MatchBindingPattern": [
"id",
],
"MatchExpression": [
"argument",
"cases",
],
"MatchExpressionCase": [
"body",
"guard",
"pattern",
],
"MatchIdentifierPattern": [
"id",
],
"MatchLiteralPattern": [
"literal",
],
"MatchMemberPattern": [
"base",
"property",
],
"MatchObjectPattern": [
"properties",
"rest",
],
"MatchObjectPatternProperty": [
"key",
"pattern",
],
"MatchOrPattern": [
"patterns",
],
"MatchRestPattern": [
"argument",
],
"MatchStatement": [
"argument",
"cases",
],
"MatchStatementCase": [
"body",
"guard",
"pattern",
],
"MatchUnaryPattern": [
"argument",
],
"MatchWildcardPattern": [],
"MemberExpression": [ "MemberExpression": [
"object", "object",
"property", "property",