@@ -7393,7 +7393,11 @@ class Parser {
7393
7393
onlyParseVariableDeclarationStart);
7394
7394
}
7395
7395
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.
7397
7401
Token parseExpressionStatementOrDeclarationAfterModifiers (
7398
7402
Token beforeType,
7399
7403
Token start,
@@ -7418,6 +7422,26 @@ class Parser {
7418
7422
start = context.parseVariableDeclarationModifiers (beforeType);
7419
7423
varFinalOrConst = context.varFinalOrConst;
7420
7424
}
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
+
7421
7445
typeInfo ?? = computeType (beforeType, /* required = */ false );
7422
7446
7423
7447
Token token = typeInfo.skipType (beforeType);
@@ -7438,6 +7462,9 @@ class Parser {
7438
7462
reportRecoverableErrorWithToken (
7439
7463
lateToken, codes.templateExtraneousModifier);
7440
7464
}
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.
7441
7468
if (! optional ('@' , start.next! )) {
7442
7469
listener.beginMetadataStar (start.next! );
7443
7470
listener.endMetadataStar (/* count = */ 0 );
@@ -7551,6 +7578,8 @@ class Parser {
7551
7578
}
7552
7579
}
7553
7580
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.
7554
7583
if (! optional ('@' , start.next! )) {
7555
7584
listener.beginMetadataStar (start.next! );
7556
7585
listener.endMetadataStar (/* count = */ 0 );
@@ -9208,7 +9237,8 @@ class Parser {
9208
9237
listener.endBinaryPattern (next);
9209
9238
break ;
9210
9239
default :
9211
- throw new UnimplementedError ('TODO(paulberry): ${next .lexeme }' );
9240
+ // Some other operator that doesn't belong in a pattern
9241
+ return token;
9212
9242
}
9213
9243
}
9214
9244
}
@@ -9355,10 +9385,15 @@ class Parser {
9355
9385
isRefutableContext: isRefutableContext);
9356
9386
listener.handleExtractorPattern (firstIdentifier, dot, secondIdentifier);
9357
9387
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
+ }
9362
9397
}
9363
9398
// It's not an extractor pattern so parse it as an expression.
9364
9399
token = beforeFirstIdentifier;
@@ -9389,8 +9424,7 @@ class Parser {
9389
9424
typeInfo = computeVariablePatternType (token);
9390
9425
token = typeInfo.parseType (token, this );
9391
9426
} else {
9392
- // Bare wildcard pattern
9393
- assert (next.lexeme == '_' );
9427
+ // Bare identifier pattern
9394
9428
listener.handleNoType (token);
9395
9429
}
9396
9430
}
@@ -9666,6 +9700,88 @@ class Parser {
9666
9700
listener.handleExtractorPatternFields (argumentCount, begin, token);
9667
9701
return token;
9668
9702
}
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
+ }
9669
9785
}
9670
9786
9671
9787
// TODO(ahe): Remove when analyzer supports generalized function syntax.
0 commit comments