Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 27c45cd

Browse files
author
Dart CI
committed
Version 2.19.0-416.0.dev
Merge 6eef2c0 into dev
2 parents 6b8e980 + 6eef2c0 commit 27c45cd

File tree

227 files changed

+8251
-1568
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

227 files changed

+8251
-1568
lines changed

pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,6 +1952,13 @@ class ForwardingListener implements Listener {
19521952
listener?.handleVoidKeywordWithTypeArguments(token);
19531953
}
19541954

1955+
@override
1956+
void handlePatternVariableDeclarationStatement(
1957+
Token keyword, Token equals, Token semicolon) {
1958+
listener?.handlePatternVariableDeclarationStatement(
1959+
keyword, equals, semicolon);
1960+
}
1961+
19551962
@override
19561963
void logEvent(String name) {
19571964
listener?.logEvent(name);

pkg/_fe_analyzer_shared/lib/src/parser/listener.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,4 +2103,13 @@ class Listener implements UnescapeErrorListener {
21032103
/// identifier name. It is the client's responsibility to report an
21042104
/// appropriate error if the "constructor tearoffs" feature is not enabled.
21052105
void handleNewAsIdentifier(Token token) {}
2106+
2107+
/// Called after the parser has processed a variable declaration statement,
2108+
/// consisting of `METADATA KEYWORD PATTERN EQUALS EXPRESSION SEMICOLON`.
2109+
///
2110+
/// KEYWORD is either `var` or `final`, and PATTERN may only be one of the
2111+
/// patterns accepted by the `outerPattern` grammar rule defined in the
2112+
/// patterns spec.
2113+
void handlePatternVariableDeclarationStatement(
2114+
Token keyword, Token equals, Token semicolon) {}
21062115
}

pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart

Lines changed: 124 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7393,7 +7393,11 @@ class Parser {
73937393
onlyParseVariableDeclarationStart);
73947394
}
73957395

7396-
/// See [parseExpressionStatementOrDeclaration]
7396+
/// See [parseExpressionStatementOrDeclaration].
7397+
///
7398+
/// If `start.next` is an `@` token (i.e. this is a declaration with metadata)
7399+
/// then the caller should parse it before calling this method; otherwise,
7400+
/// this method will handle the lack of metadata appropriately.
73977401
Token parseExpressionStatementOrDeclarationAfterModifiers(
73987402
Token beforeType,
73997403
Token start,
@@ -7418,6 +7422,26 @@ class Parser {
74187422
start = context.parseVariableDeclarationModifiers(beforeType);
74197423
varFinalOrConst = context.varFinalOrConst;
74207424
}
7425+
7426+
// TODO(paulberry): maybe some of the conditions in this `if` test should be
7427+
// removed to allow for better error recovery.
7428+
if (allowPatterns &&
7429+
lateToken == null &&
7430+
varFinalOrConst != null &&
7431+
(optional('var', varFinalOrConst) ||
7432+
optional('final', varFinalOrConst)) &&
7433+
!onlyParseVariableDeclarationStart &&
7434+
looksLikePatternVariableDeclaration(beforeType)) {
7435+
// If there was any metadata, then the caller was responsible for parsing
7436+
// it; if not, then we need to let the listener know there wasn't any.
7437+
if (!optional('@', start.next!)) {
7438+
listener.beginMetadataStar(start.next!);
7439+
listener.endMetadataStar(/* count = */ 0);
7440+
}
7441+
return parsePatternVariableDeclarationStatement(
7442+
beforeType, start, varFinalOrConst);
7443+
}
7444+
74217445
typeInfo ??= computeType(beforeType, /* required = */ false);
74227446

74237447
Token token = typeInfo.skipType(beforeType);
@@ -7438,6 +7462,9 @@ class Parser {
74387462
reportRecoverableErrorWithToken(
74397463
lateToken, codes.templateExtraneousModifier);
74407464
}
7465+
// If there was any metadata, then the caller was responsible for
7466+
// parsing it; if not, then we need to let the listener know there
7467+
// wasn't any.
74417468
if (!optional('@', start.next!)) {
74427469
listener.beginMetadataStar(start.next!);
74437470
listener.endMetadataStar(/* count = */ 0);
@@ -7551,6 +7578,8 @@ class Parser {
75517578
}
75527579
}
75537580

7581+
// If there was any metadata, then the caller was responsible for parsing
7582+
// it; if not, then we need to let the listener know there wasn't any.
75547583
if (!optional('@', start.next!)) {
75557584
listener.beginMetadataStar(start.next!);
75567585
listener.endMetadataStar(/* count = */ 0);
@@ -9208,7 +9237,8 @@ class Parser {
92089237
listener.endBinaryPattern(next);
92099238
break;
92109239
default:
9211-
throw new UnimplementedError('TODO(paulberry): ${next.lexeme}');
9240+
// Some other operator that doesn't belong in a pattern
9241+
return token;
92129242
}
92139243
}
92149244
}
@@ -9355,10 +9385,15 @@ class Parser {
93559385
isRefutableContext: isRefutableContext);
93569386
listener.handleExtractorPattern(firstIdentifier, dot, secondIdentifier);
93579387
return token;
9358-
} else if (firstIdentifier.lexeme == '_' && dot == null) {
9359-
// It's a wildcard pattern with no preceding type, so parse it as a
9360-
// variable pattern.
9361-
return parseVariablePattern(beforeFirstIdentifier, typeInfo: typeInfo);
9388+
} else if (dot == null) {
9389+
// It's a single identifier. If it's a wildcard pattern or we're in an
9390+
// irrefutable context, parse it as a variable pattern.
9391+
if (!isRefutableContext || firstIdentifier.lexeme == '_') {
9392+
// It's a wildcard pattern with no preceding type, so parse it as a
9393+
// variable pattern.
9394+
return parseVariablePattern(beforeFirstIdentifier,
9395+
typeInfo: typeInfo);
9396+
}
93629397
}
93639398
// It's not an extractor pattern so parse it as an expression.
93649399
token = beforeFirstIdentifier;
@@ -9389,8 +9424,7 @@ class Parser {
93899424
typeInfo = computeVariablePatternType(token);
93909425
token = typeInfo.parseType(token, this);
93919426
} else {
9392-
// Bare wildcard pattern
9393-
assert(next.lexeme == '_');
9427+
// Bare identifier pattern
93949428
listener.handleNoType(token);
93959429
}
93969430
}
@@ -9666,6 +9700,88 @@ class Parser {
96669700
listener.handleExtractorPatternFields(argumentCount, begin, token);
96679701
return token;
96689702
}
9703+
9704+
/// Returns `true` if the given [token] should be treated like the start of
9705+
/// a pattern variable declaration.
9706+
///
9707+
/// patternVariableDeclaration ::= ( 'final' | 'var' ) outerPattern '='
9708+
/// expression
9709+
bool looksLikePatternVariableDeclaration(Token token) {
9710+
Token? afterOuterPattern = skipOuterPattern(token);
9711+
if (afterOuterPattern == null) return false;
9712+
return optional('=', afterOuterPattern.next!);
9713+
}
9714+
9715+
/// Tries to advance beyond an "outer pattern" starting from [token]. If the
9716+
/// next construct after [token] is not an outer pattern, returns `null`.
9717+
///
9718+
/// outerPattern ::= parenthesizedPattern
9719+
/// | listPattern
9720+
/// | mapPattern
9721+
/// | recordPattern
9722+
/// | extractorPattern
9723+
Token? skipOuterPattern(Token token) {
9724+
Token next = token.next!;
9725+
if (next.isIdentifier) {
9726+
token = next;
9727+
next = token.next!;
9728+
if (!optional('.', next)) {
9729+
return skipExtractorPatternRest(token);
9730+
}
9731+
token = next;
9732+
next = token.next!;
9733+
if (next.isIdentifier) {
9734+
return skipExtractorPatternRest(next);
9735+
} else {
9736+
throw new UnimplementedError('TODO(paulberry)');
9737+
}
9738+
}
9739+
TypeParamOrArgInfo typeParamOrArg = computeTypeParamOrArg(token);
9740+
token = typeParamOrArg.skip(token);
9741+
next = token.next!;
9742+
if (optional('[]', next)) {
9743+
// Empty list pattern
9744+
return next;
9745+
}
9746+
if (optional('[', next) || optional('{', next)) {
9747+
// List or map pattern
9748+
return next.endGroup;
9749+
}
9750+
if (typeParamOrArg == noTypeParamOrArg && optional('(', next)) {
9751+
// Record or parenthesized pattern
9752+
return next.endGroup;
9753+
}
9754+
throw new UnimplementedError('TODO(paulberry)');
9755+
}
9756+
9757+
/// Tries to advance through an extractor pattern, where [token] is the last
9758+
/// token of the extractor pattern's type name. If the tokens following
9759+
/// [token] don't look like the rest of an extractor pattern, returns `null`.
9760+
///
9761+
/// extractorPattern ::= typeName typeArguments? '(' patternFields? ')'
9762+
Token? skipExtractorPatternRest(Token token) {
9763+
TypeParamOrArgInfo typeParamOrArg = computeTypeParamOrArg(token);
9764+
token = typeParamOrArg.skip(token);
9765+
Token? next = token.next;
9766+
if (next == null) return null;
9767+
if (!optional('(', next)) return null;
9768+
return next.endGroup;
9769+
}
9770+
9771+
/// patternVariableDeclaration ::= ( 'final' | 'var' ) outerPattern '='
9772+
/// expression
9773+
Token parsePatternVariableDeclarationStatement(
9774+
Token keyword, Token start, Token varOrFinal) {
9775+
Token token = parsePattern(keyword, isRefutableContext: false);
9776+
Token equals = token.next!;
9777+
// Caller should have assured that the pattern was followed by an `=`.
9778+
assert(optional('=', equals));
9779+
token = parseExpression(equals);
9780+
Token semicolon = ensureSemicolon(token);
9781+
listener.handlePatternVariableDeclarationStatement(
9782+
keyword, equals, semicolon);
9783+
return semicolon;
9784+
}
96699785
}
96709786

96719787
// TODO(ahe): Remove when analyzer supports generalized function syntax.

0 commit comments

Comments
 (0)