| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:_fe_analyzer_shared/src/messages/codes.dart' | |
| show LocatedMessage, Message, MessageCode; | |
| import 'package:_fe_analyzer_shared/src/messages/diagnostic.dart' as fe_diag; | |
| import 'package:_fe_analyzer_shared/src/messages/diagnostic.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/parser.dart' | |
| show | |
| Assert, | |
| BlockKind, | |
| ConstructorReferenceContext, | |
| DeclarationHeaderKind, | |
| DeclarationKind, | |
| FormalParameterKind, | |
| IdentifierContext, | |
| MemberKind, | |
| Parser, | |
| boolFromToken, | |
| doubleFromToken, | |
| intFromToken, | |
| optional; | |
| import 'package:_fe_analyzer_shared/src/parser/quote.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/stack_listener.dart' | |
| show NullValues, StackListener; | |
| import 'package:_fe_analyzer_shared/src/scanner/scanner.dart'; | |
| import 'package:_fe_analyzer_shared/src/scanner/token.dart' | |
| show KeywordToken, StringToken, SyntheticToken; | |
| import 'package:_fe_analyzer_shared/src/scanner/token_constants.dart'; | |
| import 'package:_fe_analyzer_shared/src/util/null_value.dart'; | |
| import 'package:analyzer/dart/analysis/features.dart'; | |
| import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/source/line_info.dart'; | |
| import 'package:analyzer/src/dart/analysis/experiments.dart'; | |
| import 'package:analyzer/src/dart/ast/ast.dart'; | |
| import 'package:analyzer/src/dart/scanner/translate_error_token.dart' | |
| show translateErrorToken; | |
| import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag; | |
| import 'package:analyzer/src/error/codes.dart'; | |
| import 'package:analyzer/src/error/listener.dart'; | |
| import 'package:analyzer/src/fasta/doc_comment_builder.dart'; | |
| import 'package:analyzer/src/fasta/error_converter.dart'; | |
| import 'package:analyzer/src/generated/utilities_dart.dart'; | |
| import 'package:analyzer/src/summary2/ast_binary_tokens.dart'; | |
| import 'package:collection/collection.dart'; | |
| import 'package:meta/meta.dart'; | |
| import 'package:pub_semver/pub_semver.dart'; | |
| /// A parser listener that builds the analyzer's AST structure. | |
| class AstBuilder extends StackListener { | |
| final FastaErrorReporter diagnosticReporter; | |
| final Uri fileUri; | |
| ScriptTagImpl? scriptTag; | |
| final List<DirectiveImpl> directives = []; | |
| final List<TopLevelDeclarationV1OrV2Impl> declarations = []; | |
| final List<AstNodeImpl> invalidNodes = []; | |
| @override | |
| final Uri uri; | |
| /// The parser that uses this listener, used to parse optional parts, e.g. | |
| /// `native` support. | |
| late Parser parser; | |
| /// The class like declaration being parsed. | |
| _ClassLikeDeclarationBuilder? _classLikeBuilder; | |
| /// If true, this is building a full AST. Otherwise, only create method | |
| /// bodies. | |
| final bool isFullAst; | |
| /// `true` if the `native` clause is allowed | |
| /// in class, method, and function declarations. | |
| /// | |
| /// This is being replaced by the @native(...) annotation. | |
| // | |
| // TODO(danrubel): Move this flag to a better location | |
| // and should only be true if either: | |
| // * The current library is a platform library | |
| // * The current library has an import that uses the scheme "dart-ext". | |
| bool allowNativeClause = false; | |
| StringLiteralImpl? nativeName; | |
| bool parseFunctionBodies = true; | |
| /// Whether the 'augmentations' feature is enabled. | |
| final bool enableAugmentations; | |
| /// `true` if triple-shift behavior is enabled | |
| final bool enableTripleShift; | |
| /// `true` if nonfunction-type-aliases behavior is enabled | |
| final bool enableNonFunctionTypeAliases; | |
| /// `true` if variance behavior is enabled | |
| final bool enableVariance; | |
| /// `true` if constructor tearoffs are enabled | |
| final bool enableConstructorTearoffs; | |
| /// `true` if named arguments anywhere are enabled | |
| final bool enableNamedArgumentsAnywhere; | |
| /// `true` if super parameters are enabled | |
| final bool enableSuperParameters; | |
| /// `true` if enhanced enums are enabled | |
| final bool enableEnhancedEnums; | |
| /// Whether the 'enhanced_parts' feature is enabled. | |
| final bool enableEnhancedParts; | |
| /// `true` if records are enabled | |
| final bool enableRecords; | |
| /// `true` if unnamed-library behavior is enabled | |
| final bool enableUnnamedLibraries; | |
| /// `true` if inline-class is enabled | |
| final bool enableInlineClass; | |
| /// `true` if sealed-class is enabled | |
| final bool enableSealedClass; | |
| /// `true` if class-modifiers is enabled | |
| final bool enableClassModifiers; | |
| /// `true` if null-aware elements is enabled | |
| final bool enableNullAwareElements; | |
| /// `true` if dot-shorthands is enabled | |
| final bool enabledDotShorthands; | |
| /// `true` if digit-separators is enabled. | |
| final bool _enableDigitSeparators; | |
| final FeatureSet _featureSet; | |
| final LibraryLanguageVersion _languageVersion; | |
| final LineInfo _lineInfo; | |
| AstBuilder( | |
| DiagnosticReporter? errorReporter, | |
| this.fileUri, | |
| this.isFullAst, | |
| this._featureSet, | |
| this._languageVersion, | |
| this._lineInfo, [ | |
| Uri? uri, | |
| ]) : diagnosticReporter = FastaErrorReporter(errorReporter), | |
| enableAugmentations = _featureSet.isEnabled(Feature.augmentations), | |
| enableTripleShift = _featureSet.isEnabled(Feature.triple_shift), | |
| enableNonFunctionTypeAliases = _featureSet.isEnabled( | |
| Feature.nonfunction_type_aliases, | |
| ), | |
| enableVariance = _featureSet.isEnabled(Feature.variance), | |
| enableConstructorTearoffs = _featureSet.isEnabled( | |
| Feature.constructor_tearoffs, | |
| ), | |
| enableNamedArgumentsAnywhere = _featureSet.isEnabled( | |
| Feature.named_arguments_anywhere, | |
| ), | |
| enableSuperParameters = _featureSet.isEnabled(Feature.super_parameters), | |
| enableEnhancedEnums = _featureSet.isEnabled(Feature.enhanced_enums), | |
| enableEnhancedParts = _featureSet.isEnabled(Feature.enhanced_parts), | |
| enableRecords = _featureSet.isEnabled(Feature.records), | |
| enableUnnamedLibraries = _featureSet.isEnabled(Feature.unnamedLibraries), | |
| enableInlineClass = _featureSet.isEnabled(Feature.inline_class), | |
| enableSealedClass = _featureSet.isEnabled(Feature.sealed_class), | |
| enableClassModifiers = _featureSet.isEnabled(Feature.class_modifiers), | |
| enableNullAwareElements = _featureSet.isEnabled( | |
| Feature.null_aware_elements, | |
| ), | |
| enabledDotShorthands = _featureSet.isEnabled(Feature.dot_shorthands), | |
| _enableDigitSeparators = _featureSet.isEnabled(Feature.digit_separators), | |
| uri = uri ?? fileUri; | |
| @override | |
| bool get isDartLibrary => | |
| uri.isScheme("dart") || uri.isScheme("org-dartlang-sdk"); | |
| @override | |
| void addProblem( | |
| Message message, | |
| int charOffset, | |
| int length, { | |
| bool wasHandled = false, | |
| List<LocatedMessage>? context, | |
| }) { | |
| if (directives.isEmpty && | |
| message.code.pseudoSharedCode == | |
| PseudoSharedCode.nonPartOfDirectiveInPart) { | |
| message = fe_diag.directiveAfterDeclaration; | |
| } | |
| diagnosticReporter.reportMessage(message, charOffset, length); | |
| } | |
| @override | |
| void beginAsOperatorType(Token asOperator) {} | |
| @override | |
| void beginCascade(Token token) { | |
| assert(optional('..', token) || optional('?..', token)); | |
| debugEvent("beginCascade"); | |
| var targetOrCascade = pop(); | |
| push(token); | |
| if (targetOrCascade is _CascadeBuilder) { | |
| push(targetOrCascade); | |
| } else { | |
| push(_CascadeBuilder(targetOrCascade as ExpressionImpl)); | |
| } | |
| push(NullValues.CascadeReceiver); | |
| } | |
| @override | |
| void beginClassDeclaration( | |
| Token begin, | |
| Token? abstractToken, | |
| Token? sealedToken, | |
| Token? baseToken, | |
| Token? interfaceToken, | |
| Token? finalToken, | |
| Token? augmentToken, | |
| Token? mixinToken, | |
| Token name, | |
| ) { | |
| assert(_classLikeBuilder == null); | |
| push(_Modifiers()..abstractKeyword = abstractToken); | |
| if (!enableSealedClass) { | |
| if (sealedToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.sealed_class, | |
| startToken: sealedToken, | |
| ); | |
| // Pretend that 'sealed' didn't occur while this feature is incomplete. | |
| sealedToken = null; | |
| } | |
| } | |
| if (!enableClassModifiers) { | |
| if (baseToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.class_modifiers, | |
| startToken: baseToken, | |
| ); | |
| // Pretend that 'base' didn't occur while this feature is incomplete. | |
| baseToken = null; | |
| } | |
| if (interfaceToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.class_modifiers, | |
| startToken: interfaceToken, | |
| ); | |
| // Pretend that 'interface' didn't occur while this feature is | |
| // incomplete. | |
| interfaceToken = null; | |
| } | |
| if (finalToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.class_modifiers, | |
| startToken: finalToken, | |
| ); | |
| // Pretend that 'final' didn't occur while this feature is | |
| // incomplete. | |
| finalToken = null; | |
| } | |
| if (mixinToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.class_modifiers, | |
| startToken: mixinToken, | |
| ); | |
| // Pretend that 'mixin' didn't occur while this feature is incomplete. | |
| mixinToken = null; | |
| } | |
| } | |
| push(sealedToken ?? NullValues.Token); | |
| push(baseToken ?? NullValues.Token); | |
| push(interfaceToken ?? NullValues.Token); | |
| push(finalToken ?? NullValues.Token); | |
| push(augmentToken ?? NullValues.Token); | |
| push(mixinToken ?? NullValues.Token); | |
| } | |
| @override | |
| void beginCompilationUnit(Token token) { | |
| push(token); | |
| } | |
| @override | |
| void beginConstantPattern(Token? constKeyword) {} | |
| @override | |
| void beginConstructor( | |
| DeclarationKind declarationKind, | |
| Token? augmentToken, | |
| Token? externalToken, | |
| Token? staticToken, | |
| Token? covariantToken, | |
| Token? varFinalOrConst, | |
| Token? getOrSet, | |
| Token? newToken, | |
| Token name, | |
| String? enclosingDeclarationName, | |
| ) { | |
| _beginMethod( | |
| declarationKind, | |
| augmentToken, | |
| externalToken, | |
| staticToken, | |
| covariantToken, | |
| varFinalOrConst, | |
| newToken, | |
| getOrSet, | |
| name, | |
| enclosingDeclarationName, | |
| ); | |
| } | |
| @override | |
| void beginEnumDeclaration( | |
| Token beginToken, | |
| Token? augmentToken, | |
| Token enumKeyword, | |
| Token name, | |
| ) {} | |
| @override | |
| void beginEnumDeclarationPrelude(Token enumKeyword) {} | |
| @override | |
| void beginExtensionDeclaration( | |
| Token? augmentKeyword, | |
| Token extensionKeyword, | |
| Token? nameToken, | |
| ) { | |
| assert(optional('extension', extensionKeyword)); | |
| assert(_classLikeBuilder == null); | |
| debugEvent("ExtensionHeader"); | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, extensionKeyword); | |
| _classLikeBuilder = _ExtensionDeclarationBuilder( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| extensionKeyword: extensionKeyword, | |
| name: nameToken, | |
| typeParameters: typeParameters, | |
| leftBracket: Tokens.openCurlyBracket(), | |
| rightBracket: Tokens.closeCurlyBracket(), | |
| ); | |
| } | |
| @override | |
| void beginExtensionTypeDeclaration( | |
| Token? augmentKeyword, | |
| Token extensionKeyword, | |
| Token name, | |
| ) { | |
| assert(optional('extension', extensionKeyword)); | |
| assert(_classLikeBuilder == null); | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, extensionKeyword); | |
| _classLikeBuilder = _ExtensionTypeDeclarationBuilder( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| extensionKeyword: extensionKeyword, | |
| name: name, | |
| typeParameters: typeParameters, | |
| leftBracket: Tokens.openCurlyBracket(), | |
| rightBracket: Tokens.closeCurlyBracket(), | |
| ); | |
| } | |
| @override | |
| void beginFactory( | |
| DeclarationKind declarationKind, | |
| Token lastConsumed, | |
| Token? augmentToken, | |
| Token? externalToken, | |
| Token? constToken, | |
| ) { | |
| push( | |
| _Modifiers() | |
| ..augmentKeyword = augmentToken | |
| ..externalKeyword = externalToken | |
| ..finalConstOrVarKeyword = constToken, | |
| ); | |
| } | |
| @override | |
| void beginFormalParameter( | |
| Token token, | |
| MemberKind kind, | |
| Token? requiredToken, | |
| Token? covariantToken, | |
| Token? varFinalOrConst, | |
| ) { | |
| push( | |
| _Modifiers() | |
| ..covariantKeyword = covariantToken | |
| ..finalConstOrVarKeyword = varFinalOrConst | |
| ..requiredToken = requiredToken, | |
| ); | |
| } | |
| @override | |
| void beginFormalParameterDefaultValueExpression() {} | |
| @override | |
| void beginIfControlFlow(Token ifToken) { | |
| push(ifToken); | |
| } | |
| @override | |
| void beginIsOperatorType(Token asOperator) {} | |
| @override | |
| void beginLibraryAugmentation(Token augmentKeyword, Token libraryKeyword) {} | |
| @override | |
| void beginLiteralString(Token literalString) { | |
| assert(identical(literalString.kind, STRING_TOKEN)); | |
| debugEvent("beginLiteralString"); | |
| push(literalString); | |
| } | |
| @override | |
| void beginMetadataStar(Token token) { | |
| debugEvent("beginMetadataStar"); | |
| } | |
| @override | |
| void beginMethod( | |
| DeclarationKind declarationKind, | |
| Token? augmentToken, | |
| Token? externalToken, | |
| Token? staticToken, | |
| Token? covariantToken, | |
| Token? varFinalOrConst, | |
| Token? getOrSet, | |
| Token name, | |
| String? enclosingDeclarationName, | |
| ) { | |
| _beginMethod( | |
| declarationKind, | |
| augmentToken, | |
| externalToken, | |
| staticToken, | |
| covariantToken, | |
| varFinalOrConst, | |
| null, | |
| getOrSet, | |
| name, | |
| enclosingDeclarationName, | |
| ); | |
| } | |
| @override | |
| void beginMixinDeclaration( | |
| Token beginToken, | |
| Token? augmentToken, | |
| Token? baseToken, | |
| Token mixinKeyword, | |
| Token name, | |
| ) { | |
| assert(_classLikeBuilder == null); | |
| if (!enableClassModifiers) { | |
| if (baseToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.class_modifiers, | |
| startToken: baseToken, | |
| ); | |
| // Pretend that 'base' didn't occur while this feature is incomplete. | |
| baseToken = null; | |
| } | |
| } | |
| push(augmentToken ?? NullValues.Token); | |
| push(baseToken ?? NullValues.Token); | |
| } | |
| @override | |
| void beginNamedMixinApplication( | |
| Token begin, | |
| Token? abstractToken, | |
| Token? sealedToken, | |
| Token? baseToken, | |
| Token? interfaceToken, | |
| Token? finalToken, | |
| Token? augmentToken, | |
| Token? mixinToken, | |
| Token name, | |
| ) { | |
| push(_Modifiers()..abstractKeyword = abstractToken); | |
| if (!enableSealedClass) { | |
| if (sealedToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.sealed_class, | |
| startToken: sealedToken, | |
| ); | |
| // Pretend that 'sealed' didn't occur while this feature is incomplete. | |
| sealedToken = null; | |
| } | |
| } | |
| if (!enableClassModifiers) { | |
| if (baseToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.class_modifiers, | |
| startToken: baseToken, | |
| ); | |
| // Pretend that 'base' didn't occur while this feature is incomplete. | |
| baseToken = null; | |
| } | |
| if (interfaceToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.class_modifiers, | |
| startToken: interfaceToken, | |
| ); | |
| // Pretend that 'interface' didn't occur while this feature is | |
| // incomplete. | |
| interfaceToken = null; | |
| } | |
| if (finalToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.class_modifiers, | |
| startToken: finalToken, | |
| ); | |
| // Pretend that 'final' didn't occur while this feature is incomplete. | |
| finalToken = null; | |
| } | |
| if (mixinToken != null) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.class_modifiers, | |
| startToken: mixinToken, | |
| ); | |
| // Pretend that 'mixin' didn't occur while this feature is incomplete. | |
| mixinToken = null; | |
| } | |
| } | |
| push(sealedToken ?? NullValues.Token); | |
| push(baseToken ?? NullValues.Token); | |
| push(interfaceToken ?? NullValues.Token); | |
| push(finalToken ?? NullValues.Token); | |
| push(augmentToken ?? NullValues.Token); | |
| push(mixinToken ?? NullValues.Token); | |
| } | |
| @override | |
| void beginPattern(Token token) { | |
| debugEvent("Pattern"); | |
| } | |
| @override | |
| void beginPatternGuard(Token when) { | |
| debugEvent("PatternGuard"); | |
| } | |
| @override | |
| void beginPrimaryConstructor(Token beginToken) { | |
| debugEvent("PrimaryConstructor"); | |
| } | |
| @override | |
| void beginPrimaryConstructorBody(Token beginToken, Token? augmentToken) { | |
| debugEvent("PrimaryConstructorBody"); | |
| push(_Modifiers()..augmentKeyword = augmentToken); | |
| } | |
| @override | |
| void beginSwitchCaseWhenClause(Token when) { | |
| debugEvent("PatternSwitchCaseGuard"); | |
| } | |
| @override | |
| void beginTopLevelMethod( | |
| Token lastConsumed, | |
| Token? augmentToken, | |
| Token? externalToken, | |
| ) { | |
| push( | |
| _Modifiers() | |
| ..augmentKeyword = augmentToken | |
| ..externalKeyword = externalToken, | |
| ); | |
| } | |
| @override | |
| void beginTypeVariable(Token token) { | |
| debugEvent("beginTypeVariable"); | |
| var name = pop() as Token; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, name); | |
| var typeParameter = TypeParameterImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| varianceKeyword: null, | |
| name: name, | |
| extendsKeyword: null, | |
| bound: null, | |
| ); | |
| push(typeParameter); | |
| } | |
| @override | |
| void beginVariablesDeclaration( | |
| Token token, | |
| Token? lateToken, | |
| Token? varFinalOrConst, | |
| ) { | |
| debugEvent("beginVariablesDeclaration"); | |
| if (varFinalOrConst != null || lateToken != null) { | |
| push( | |
| _Modifiers() | |
| ..finalConstOrVarKeyword = varFinalOrConst | |
| ..lateToken = lateToken, | |
| ); | |
| } else { | |
| push(NullValues.Modifiers); | |
| } | |
| } | |
| ConstructorInitializerImpl? buildInitializer(Object initializerObject) { | |
| if (initializerObject is ParsedValueArgumentsImpl) { | |
| ExpressionImpl operand = initializerObject.operand; | |
| if (operand is ParsedTypeArgumentsImpl) { | |
| operand = operand.operand; | |
| } | |
| if (operand is ParsedNameAccessImpl) { | |
| var receiver = operand.operand; | |
| if (receiver is SuperReferenceImpl) { | |
| return SuperConstructorInvocationImpl( | |
| superKeyword: receiver.superKeyword, | |
| constructorSelector: ConstructorSelectorImpl.v2( | |
| period: operand.operator, | |
| name2: operand.name, | |
| ), | |
| argumentList: initializerObject.argumentList, | |
| ); | |
| } | |
| if (receiver is ThisExpressionImpl) { | |
| return RedirectingConstructorInvocationImpl( | |
| thisKeyword: receiver.thisKeyword, | |
| constructorSelector: ConstructorSelectorImpl.v2( | |
| period: operand.operator, | |
| name2: operand.name, | |
| ), | |
| argumentList: initializerObject.argumentList, | |
| ); | |
| } | |
| return buildInitializerTargetExpressionRecovery( | |
| receiver, | |
| initializerObject, | |
| ); | |
| } | |
| } | |
| if (initializerObject is ParsedNameAccessImpl) { | |
| return buildInitializerTargetExpressionRecovery( | |
| initializerObject.operand, | |
| initializerObject, | |
| ); | |
| } | |
| if (initializerObject is CallInvocationImpl) { | |
| var function = initializerObject.receiver; | |
| if (function is SuperReferenceImpl) { | |
| return SuperConstructorInvocationImpl( | |
| superKeyword: function.superKeyword, | |
| constructorSelector: null, | |
| argumentList: initializerObject.argumentList, | |
| ); | |
| } | |
| if (function is ThisExpressionImpl) { | |
| return RedirectingConstructorInvocationImpl( | |
| thisKeyword: function.thisKeyword, | |
| constructorSelector: null, | |
| argumentList: initializerObject.argumentList, | |
| ); | |
| } | |
| return null; | |
| } | |
| if (initializerObject is ReceiverPropertyExtractionImpl) { | |
| return buildInitializerTargetExpressionRecovery( | |
| _parsedPropertyReceiver(initializerObject.receiver), | |
| initializerObject, | |
| ); | |
| } | |
| if (initializerObject is DirectAssignmentImpl) { | |
| var target = initializerObject.target; | |
| Token? thisKeyword; | |
| Token? period; | |
| late Token fieldName; | |
| switch (target) { | |
| case ReceiverPropertyAssignmentTargetImpl( | |
| receiver: ThisExpressionImpl(thisKeyword: var writtenThisKeyword), | |
| :var operator, | |
| :var name, | |
| ): | |
| thisKeyword = writtenThisKeyword; | |
| period = operator; | |
| fieldName = name; | |
| case UnqualifiedNameAssignmentTargetImpl(:var name): | |
| fieldName = name; | |
| case ReceiverPropertyAssignmentTargetImpl( | |
| receiver: SuperReferenceImpl(), | |
| :var name, | |
| ): | |
| // The parser already reported a field outside its declaring class. | |
| fieldName = name; | |
| case ParsedUnqualifiedNameAssignmentTargetImpl(:var name): | |
| fieldName = name; | |
| default: | |
| return null; | |
| } | |
| return ConstructorFieldInitializerImpl( | |
| thisKeyword: thisKeyword, | |
| period: period, | |
| fieldName2: fieldName, | |
| equals: initializerObject.operator, | |
| expression2: initializerObject.value, | |
| ); | |
| } | |
| if (initializerObject is AssertInitializerImpl) { | |
| return initializerObject; | |
| } | |
| if (initializerObject is ReceiverIndexExpressionImpl) { | |
| return buildInitializerTargetExpressionRecovery( | |
| initializerObject.receiver, | |
| initializerObject, | |
| ); | |
| } | |
| if (initializerObject is CascadeExpressionImpl) { | |
| return buildInitializerTargetExpressionRecovery( | |
| initializerObject.target2, | |
| initializerObject, | |
| ); | |
| } | |
| return null; | |
| } | |
| ConstructorInitializerImpl? buildInitializerTargetExpressionRecovery( | |
| AstNodeImpl? target, | |
| Object initializerObject, | |
| ) { | |
| ArgumentListImpl? argumentList; | |
| while (true) { | |
| if (target is ParsedValueArgumentsImpl) { | |
| argumentList = target.argumentList; | |
| ExpressionImpl operand = target.operand; | |
| if (operand is ParsedTypeArgumentsImpl) { | |
| operand = operand.operand; | |
| } | |
| target = operand is ParsedNameAccessImpl ? operand.operand : operand; | |
| } else if (target is ParsedNameAccessImpl) { | |
| argumentList = null; | |
| target = target.operand; | |
| } else if (target is CallInvocationImpl) { | |
| argumentList = target.argumentList; | |
| target = target.receiver; | |
| } else if (target is ReceiverPropertyExtractionImpl) { | |
| argumentList = null; | |
| target = _parsedPropertyReceiver(target.receiver); | |
| } else { | |
| break; | |
| } | |
| } | |
| var superReference = switch (target) { | |
| SuperReferenceImpl reference => reference, | |
| InvalidSuperExpressionImpl(:var superReference) => superReference, | |
| _ => null, | |
| }; | |
| if (superReference != null) { | |
| // TODO(danrubel): Consider generating this error in the parser | |
| // This error is also reported in the body builder | |
| handleRecoverableError( | |
| fe_diag.invalidSuperInInitializer, | |
| superReference.superKeyword, | |
| superReference.superKeyword, | |
| ); | |
| return SuperConstructorInvocationImpl( | |
| superKeyword: superReference.superKeyword, | |
| constructorSelector: null, | |
| argumentList: | |
| argumentList ?? _syntheticArgumentList(superReference.superKeyword), | |
| ); | |
| } else if (target is ThisExpressionImpl) { | |
| // TODO(danrubel): Consider generating this error in the parser | |
| // This error is also reported in the body builder | |
| handleRecoverableError( | |
| fe_diag.invalidThisInInitializer, | |
| target.thisKeyword, | |
| target.thisKeyword, | |
| ); | |
| return RedirectingConstructorInvocationImpl( | |
| thisKeyword: target.thisKeyword, | |
| constructorSelector: null, | |
| argumentList: | |
| argumentList ?? _syntheticArgumentList(target.thisKeyword), | |
| ); | |
| } | |
| return null; | |
| } | |
| // TODO(scheglov): We should not do this. | |
| // Ideally, we should not test parsing pieces of class, and instead parse | |
| // the whole unit, and extract pieces that we need to validate. | |
| _ClassDeclarationBuilder createFakeClassDeclarationBuilder(String className) { | |
| return _classLikeBuilder = _ClassDeclarationBuilder( | |
| comment: null, | |
| metadata: null, | |
| abstractKeyword: null, | |
| sealedKeyword: null, | |
| baseKeyword: null, | |
| interfaceKeyword: null, | |
| finalKeyword: null, | |
| augmentKeyword: null, | |
| mixinKeyword: null, | |
| classKeyword: Token(Keyword.CLASS, 0), | |
| name: StringToken(TokenType.STRING, className, -1), | |
| primaryConstructorBuilder: null, | |
| typeParameters: null, | |
| extendsClause: null, | |
| withClause: null, | |
| implementsClause: null, | |
| nativeClause: null, | |
| leftBracket: Tokens.openCurlyBracket(), | |
| rightBracket: Tokens.closeCurlyBracket(), | |
| ); | |
| } | |
| @override | |
| void debugEvent(String name) { | |
| // printEvent('AstBuilder: $name'); | |
| } | |
| void doDotExpression(Token dot) { | |
| var identifierOrInvoke = pop() as ExpressionImpl; | |
| var receiver = pop() as ExpressionImpl?; | |
| assert( | |
| (receiver == null && | |
| (dot.type == TokenType.PERIOD_PERIOD || | |
| dot.type == TokenType.QUESTION_PERIOD_PERIOD)) || | |
| (receiver != null && | |
| (dot.type == TokenType.PERIOD || | |
| dot.type == TokenType.QUESTION_PERIOD)), | |
| ); | |
| if (identifierOrInvoke is ParsedUnqualifiedNameImpl) { | |
| if (receiver is ParsedUnqualifiedNameImpl || | |
| receiver is ParsedNameAccessImpl) { | |
| push( | |
| ParsedNameAccessImpl( | |
| operand: _toInstanceReceiver(receiver!), | |
| operator: dot, | |
| name: identifierOrInvoke.name, | |
| ), | |
| ); | |
| } else if (receiver != null && | |
| _featureSet.isEnabled(Feature.constructor_tearoffs) && | |
| _isSupportedPropertyReceiver(receiver)) { | |
| push( | |
| ReceiverPropertyExtractionImpl( | |
| receiver: receiver, | |
| operator: dot, | |
| name: identifierOrInvoke.name, | |
| ), | |
| ); | |
| } else if (receiver != null) { | |
| push( | |
| ParsedNameAccessImpl( | |
| operand: _toInstanceReceiver(receiver), | |
| operator: dot, | |
| name: identifierOrInvoke.name, | |
| ), | |
| ); | |
| } else { | |
| push(ParsedCascadeNameImpl(name: identifierOrInvoke.name)); | |
| } | |
| } else if (identifierOrInvoke is ParsedValueArgumentsImpl) { | |
| var operand = identifierOrInvoke.operand; | |
| var types = operand is ParsedTypeArgumentsImpl ? operand : null; | |
| // For `a.b<T>()`, [doInvocation] builds `b<T>()` from an identifier, | |
| // optionally wrapped in type arguments. Below we attach `a`. | |
| var name = (types?.operand ?? operand) as ParsedUnqualifiedNameImpl; | |
| var access = receiver == null | |
| ? ParsedCascadeNameImpl(name: name.name) | |
| : ParsedNameAccessImpl( | |
| operand: _toInstanceReceiver(receiver), | |
| operator: dot, | |
| name: name.name, | |
| ); | |
| // The parser completes the selector before attaching its receiver. | |
| // Reuse its argument wrappers and replace only the unqualified name. | |
| if (types != null) { | |
| types.operand = access; | |
| } else { | |
| identifierOrInvoke.operand = access; | |
| } | |
| push(identifierOrInvoke); | |
| } else { | |
| // This same error is reported in BodyBuilder.doDotOrCascadeExpression | |
| Token token = identifierOrInvoke.beginToken; | |
| // TODO(danrubel): Consider specializing the error message based | |
| // upon the type of expression. e.g. "x.this" -> fe_diag.thisAsIdentifier | |
| handleRecoverableError( | |
| fe_diag.expectedIdentifier.withArguments(lexeme: token), | |
| token, | |
| token, | |
| ); | |
| // The expression is dropped. Its first token, e.g. `[` in `a.[0]`, is | |
| // not a name, so the missing name is synthetic. | |
| var name = parser.rewriter.insertSyntheticIdentifier(dot); | |
| if (receiver != null) { | |
| push( | |
| ParsedNameAccessImpl( | |
| operand: _toInstanceReceiver(receiver), | |
| operator: dot, | |
| name: name, | |
| ), | |
| ); | |
| } else { | |
| push(ParsedCascadeNameImpl(name: name)); | |
| } | |
| } | |
| } | |
| void doInvocation( | |
| TypeArgumentListImpl? typeArguments, | |
| ArgumentListImpl argumentList, | |
| ) { | |
| var receiver = pop() as ExpressionImpl; | |
| switch (receiver) { | |
| case ParsedUnqualifiedNameImpl(): | |
| ParsedExpressionImpl operand = receiver; | |
| if (typeArguments != null) { | |
| operand = ParsedTypeArgumentsImpl( | |
| operand: operand, | |
| typeArguments: typeArguments, | |
| ); | |
| } | |
| push( | |
| ParsedValueArgumentsImpl( | |
| operand: operand, | |
| argumentList: argumentList, | |
| ), | |
| ); | |
| default: | |
| push( | |
| CallInvocationImpl( | |
| receiver: _toInstanceReceiver(receiver), | |
| typeArguments: typeArguments, | |
| argumentList: argumentList, | |
| ), | |
| ); | |
| } | |
| } | |
| void doPropertyGet() {} | |
| @override | |
| void endAnonymousMethodInvocation( | |
| Token startToken, | |
| Token? functionDefinition, | |
| Token endToken, { | |
| required bool isExpression, | |
| }) { | |
| debugEvent("AnonymousMethodInvocation"); | |
| var expressionOrBlock = pop(); | |
| var formals = pop(NullValues.FormalParameters) as FormalParameterListImpl?; | |
| var target = pop() as ExpressionImpl?; | |
| if (formals != null) { | |
| var formalParameters = formals.allFormalParameters; | |
| if (formalParameters.isEmpty || | |
| formalParameters.length > 1 || | |
| formalParameters.first.isNamed || | |
| formalParameters.first.isOptional) { | |
| handleRecoverableError( | |
| fe_diag.anonymousMethodWrongParameterList, | |
| formals.leftParenthesis, | |
| formals.rightParenthesis, | |
| ); | |
| formals = null; | |
| } | |
| } | |
| AnonymousMethodBodyImpl methodBody; | |
| if (isExpression) { | |
| expressionOrBlock as ExpressionImpl; | |
| methodBody = AnonymousExpressionBodyImpl( | |
| functionDefinition: functionDefinition!, | |
| expression2: expressionOrBlock, | |
| ); | |
| } else { | |
| expressionOrBlock as BlockImpl; | |
| methodBody = AnonymousBlockBodyImpl(block: expressionOrBlock); | |
| } | |
| push( | |
| AnonymousMethodInvocationImpl( | |
| target2: target, | |
| operator: startToken, | |
| parameters: formals, | |
| body: methodBody, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endArguments(int count, Token leftParenthesis, Token rightParenthesis) { | |
| assert(optional('(', leftParenthesis)); | |
| assert(optional(')', rightParenthesis)); | |
| debugEvent("Arguments"); | |
| var expressions = popTypedList2<ArgumentImpl>(count); | |
| for (var argument in expressions) { | |
| reportErrorIfSuper(argument.argumentExpression2); | |
| } | |
| var argumentList = ArgumentListImpl( | |
| leftParenthesis: leftParenthesis, | |
| arguments2: expressions, | |
| rightParenthesis: rightParenthesis, | |
| ); | |
| if (!enableNamedArgumentsAnywhere) { | |
| bool hasSeenNamedArgument = false; | |
| for (var expression in expressions) { | |
| if (expression is NamedArgumentImpl) { | |
| hasSeenNamedArgument = true; | |
| } else if (hasSeenNamedArgument) { | |
| // Positional argument after named argument. | |
| handleRecoverableError( | |
| fe_diag.positionalAfterNamedArgument, | |
| expression.beginToken, | |
| expression.endToken, | |
| ); | |
| } | |
| } | |
| } | |
| push(argumentList); | |
| } | |
| @override | |
| void endAsOperatorType(Token asOperator) { | |
| debugEvent("AsOperatorType"); | |
| } | |
| @override | |
| void endAssert( | |
| Token assertKeyword, | |
| Assert kind, | |
| Token leftParenthesis, | |
| Token? comma, | |
| Token endToken, | |
| ) { | |
| assert(optional('assert', assertKeyword)); | |
| assert(optional('(', leftParenthesis)); | |
| assert(optionalOrNull(',', comma)); | |
| assert(kind != Assert.Statement || optionalOrNull(';', endToken.next!)); | |
| debugEvent("Assert"); | |
| var message = popIfNotNull(comma) as ExpressionImpl?; | |
| var condition = _popExpression(); | |
| switch (kind) { | |
| case Assert.Expression: | |
| // The parser has already reported an error indicating that assert | |
| // cannot be used in an expression. Insert a placeholder. | |
| var arguments = <ExpressionImpl>[condition]; | |
| if (message != null) { | |
| arguments.add(message); | |
| } | |
| push( | |
| CallInvocationImpl( | |
| receiver: ParsedUnqualifiedNameImpl(name: assertKeyword), | |
| typeArguments: null, | |
| argumentList: ArgumentListImpl( | |
| leftParenthesis: leftParenthesis, | |
| arguments2: arguments, | |
| rightParenthesis: leftParenthesis.endGroup!, | |
| ), | |
| ), | |
| ); | |
| case Assert.Initializer: | |
| push( | |
| AssertInitializerImpl( | |
| assertKeyword: assertKeyword, | |
| leftParenthesis: leftParenthesis, | |
| condition2: condition, | |
| comma: comma, | |
| message2: message, | |
| rightParenthesis: leftParenthesis.endGroup!, | |
| ), | |
| ); | |
| case Assert.Statement: | |
| push( | |
| AssertStatementImpl( | |
| assertKeyword: assertKeyword, | |
| leftParenthesis: leftParenthesis, | |
| condition2: condition, | |
| comma: comma, | |
| message2: message, | |
| rightParenthesis: leftParenthesis.endGroup!, | |
| semicolon: endToken.next!, | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void endAwaitExpression(Token awaitKeyword, Token endToken) { | |
| assert(optional('await', awaitKeyword)); | |
| debugEvent("AwaitExpression"); | |
| var expression = _popExpression(); | |
| reportErrorIfSuper(expression); | |
| push( | |
| AwaitExpressionImpl(awaitKeyword: awaitKeyword, expression2: expression), | |
| ); | |
| } | |
| @override | |
| void endBinaryExpression(Token operatorToken, Token endToken) { | |
| assert( | |
| operatorToken.isOperator || | |
| optional('===', operatorToken) || | |
| optional('!==', operatorToken), | |
| ); | |
| debugEvent("BinaryExpression"); | |
| var right = _popExpression(); | |
| var left = _popExpression(); | |
| reportErrorIfSuper(right); | |
| if (operatorToken.type == TokenType.QUESTION_QUESTION || | |
| operatorToken.type == TokenType.AMPERSAND_AMPERSAND || | |
| operatorToken.type == TokenType.BAR_BAR) { | |
| reportErrorIfSuper(left); | |
| } | |
| var expression = switch (operatorToken.type) { | |
| TokenType.QUESTION_QUESTION => IfNullImpl( | |
| leftOperand: left, | |
| operator: operatorToken, | |
| rightOperand: right, | |
| ), | |
| TokenType.AMPERSAND_AMPERSAND => LogicalAndImpl( | |
| leftOperand: left, | |
| operator: operatorToken, | |
| rightOperand: right, | |
| ), | |
| TokenType.BAR_BAR => LogicalOrImpl( | |
| leftOperand: left, | |
| operator: operatorToken, | |
| rightOperand: right, | |
| ), | |
| _ => BinaryOperatorInvocationImpl( | |
| leftOperand: _toInstanceReceiver(left), | |
| operator: operatorToken, | |
| rightOperand: right, | |
| ), | |
| }; | |
| push(expression); | |
| if (!enableTripleShift && operatorToken.type == TokenType.GT_GT_GT) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.triple_shift, | |
| startToken: operatorToken, | |
| ); | |
| } | |
| } | |
| @override | |
| void endBinaryPattern(Token operatorToken) { | |
| assert(operatorToken.isOperator); | |
| debugEvent("BinaryPattern"); | |
| var right = pop() as DartPatternImpl; | |
| var left = pop() as DartPatternImpl; | |
| if (operatorToken.lexeme == '&&') { | |
| push( | |
| LogicalAndPatternImpl( | |
| leftOperand: left, | |
| operator: operatorToken, | |
| rightOperand: right, | |
| ), | |
| ); | |
| } else if (operatorToken.lexeme == '||') { | |
| push( | |
| LogicalOrPatternImpl( | |
| leftOperand: left, | |
| operator: operatorToken, | |
| rightOperand: right, | |
| ), | |
| ); | |
| } else { | |
| throw UnimplementedError('operatorToken: $operatorToken'); | |
| } | |
| } | |
| @override | |
| void endBlock( | |
| int count, | |
| Token leftBracket, | |
| Token rightBracket, | |
| BlockKind blockKind, | |
| ) { | |
| assert(optional('{', leftBracket)); | |
| assert(optional('}', rightBracket)); | |
| debugEvent("Block"); | |
| var statements = popTypedList2<StatementImpl>(count); | |
| push( | |
| BlockImpl( | |
| leftBracket: leftBracket, | |
| statements: statements, | |
| rightBracket: rightBracket, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endBlockFunctionBody(int count, Token leftBracket, Token rightBracket) { | |
| assert(optional('{', leftBracket)); | |
| assert(optional('}', rightBracket)); | |
| debugEvent("BlockFunctionBody"); | |
| var statements = popTypedList2<StatementImpl>(count); | |
| var block = BlockImpl( | |
| leftBracket: leftBracket, | |
| statements: statements, | |
| rightBracket: rightBracket, | |
| ); | |
| var star = pop() as Token?; | |
| var asyncKeyword = pop() as Token?; | |
| if (parseFunctionBodies) { | |
| push( | |
| BlockFunctionBodyImpl(keyword: asyncKeyword, star: star, block: block), | |
| ); | |
| } else { | |
| // TODO(danrubel): Skip the block rather than parsing it. | |
| push( | |
| EmptyFunctionBodyImpl( | |
| semicolon: SyntheticToken( | |
| TokenType.SEMICOLON, | |
| leftBracket.charOffset, | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void endCascade() { | |
| debugEvent("Cascade"); | |
| var expression = pop() as ExpressionImpl; | |
| var cascade = pop() as _CascadeBuilder; | |
| var operator = pop() as Token; | |
| cascade.sections.add( | |
| CascadeSectionImpl(operator: operator, body: expression), | |
| ); | |
| push(cascade); | |
| } | |
| @override | |
| void endCaseExpression(Token caseKeyword, Token? when, Token colon) { | |
| assert(optional('case', caseKeyword)); | |
| assert(optional(':', colon)); | |
| debugEvent("CaseMatch"); | |
| WhenClauseImpl? whenClause; | |
| if (when != null) { | |
| var expression = _popExpression(); | |
| whenClause = WhenClauseImpl(whenKeyword: when, expression2: expression); | |
| } | |
| if (_featureSet.isEnabled(Feature.patterns)) { | |
| var pattern = pop() as DartPatternImpl; | |
| push( | |
| SwitchPatternCaseImpl( | |
| labels: <LabelImpl>[], | |
| keyword: caseKeyword, | |
| guardedPattern: GuardedPatternImpl( | |
| pattern: pattern, | |
| whenClause: whenClause, | |
| ), | |
| colon: colon, | |
| statements: <StatementImpl>[], | |
| ), | |
| ); | |
| } else { | |
| var expression = pop() as ExpressionImpl; | |
| push( | |
| SwitchCaseImpl( | |
| labels: <LabelImpl>[], | |
| keyword: caseKeyword, | |
| expression2: expression, | |
| colon: colon, | |
| statements: <StatementImpl>[], | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void endClassDeclaration(Token beginToken, Token endToken) { | |
| debugEvent("ClassDeclaration"); | |
| var builder = _classLikeBuilder as _ClassDeclarationBuilder; | |
| declarations.add(builder.build()); | |
| _classLikeBuilder = null; | |
| } | |
| @override | |
| void endClassOrMixinOrExtensionBody( | |
| DeclarationKind kind, | |
| int memberCount, | |
| Token leftBracket, | |
| Token rightBracket, | |
| ) { | |
| // TODO(danrubel): consider renaming endClassOrMixinBody | |
| // to endClassOrMixinOrExtensionBody | |
| assert(optional('{', leftBracket)); | |
| assert(optional('}', rightBracket)); | |
| debugEvent("ClassOrMixinBody"); | |
| var builder = _classLikeBuilder; | |
| if (builder != null) { | |
| builder | |
| ..leftBracket = leftBracket | |
| ..rightBracket = rightBracket; | |
| } | |
| } | |
| @override | |
| void endCombinators(int count) { | |
| debugEvent("Combinators"); | |
| push(popTypedList<CombinatorImpl>(count) ?? NullValues.Combinators); | |
| } | |
| @override | |
| void endCompilationUnit(int count, Token endToken) { | |
| debugEvent("CompilationUnit"); | |
| var beginToken = pop() as Token; | |
| checkEmpty(endToken.charOffset); | |
| CompilationUnitImpl unit = CompilationUnitImpl( | |
| beginToken: beginToken, | |
| scriptTag: scriptTag, | |
| directives: directives, | |
| declarations2: declarations, | |
| endToken: endToken, | |
| featureSet: _featureSet, | |
| lineInfo: _lineInfo, | |
| languageVersion: _languageVersion, | |
| invalidNodes: invalidNodes, | |
| ); | |
| push(unit); | |
| } | |
| @override | |
| void endConditionalExpression(Token question, Token colon, Token endToken) { | |
| assert(optional('?', question)); | |
| assert(optional(':', colon)); | |
| debugEvent("ConditionalExpression"); | |
| var elseExpression = _popExpression(); | |
| var thenExpression = _popExpression(); | |
| var condition = _popExpression(); | |
| reportErrorIfSuper(elseExpression); | |
| reportErrorIfSuper(thenExpression); | |
| reportErrorIfSuper(condition); | |
| push( | |
| ConditionalExpressionImpl( | |
| condition2: condition, | |
| question: question, | |
| thenExpression2: thenExpression, | |
| colon: colon, | |
| elseExpression2: elseExpression, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endConditionalUri(Token ifKeyword, Token leftParen, Token? equalSign) { | |
| assert(optional('if', ifKeyword)); | |
| assert(optionalOrNull('(', leftParen)); | |
| assert(optionalOrNull('==', equalSign)); | |
| debugEvent("ConditionalUri"); | |
| var libraryUri = pop() as StringLiteralImpl; | |
| var value = popIfNotNull(equalSign) as StringLiteralImpl?; | |
| if (value is StringInterpolationImpl) { | |
| for (var child in value.childEntities2) { | |
| if (child is InterpolationExpressionImpl) { | |
| // This error is reported in OutlineBuilder.endLiteralString | |
| handleRecoverableError( | |
| fe_diag.interpolationInUri, | |
| child.beginToken, | |
| child.endToken, | |
| ); | |
| break; | |
| } | |
| } | |
| } | |
| var name = pop() as DottedNameImpl; | |
| push( | |
| ConfigurationImpl( | |
| ifKeyword: ifKeyword, | |
| leftParenthesis: leftParen, | |
| name: name, | |
| equalToken: equalSign, | |
| value: value, | |
| rightParenthesis: leftParen.endGroup!, | |
| uri: libraryUri, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endConditionalUris(int count) { | |
| debugEvent("ConditionalUris"); | |
| push(popTypedList<ConfigurationImpl>(count) ?? NullValues.ConditionalUris); | |
| } | |
| @override | |
| void endConstantPattern(Token? constKeyword) { | |
| var expression = pop() as ExpressionImpl; | |
| push( | |
| ConstantPatternImpl(constKeyword: constKeyword, expression2: expression), | |
| ); | |
| } | |
| @override | |
| void endConstDotShorthand(Token token) { | |
| debugEvent("endConstDotShorthand"); | |
| if (!enabledDotShorthands) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.dot_shorthands, | |
| startToken: token, | |
| ); | |
| } | |
| var dotShorthand = pop() as Expression; | |
| if (dotShorthand is ParsedValueArgumentsImpl) { | |
| var (:head, :typeArguments) = dotShorthand.dotShorthandInvocationParts!; | |
| push( | |
| DotShorthandConstructorInvocation2Impl( | |
| constKeyword: token, | |
| period: head.period, | |
| name: head.name, | |
| typeArguments: typeArguments, | |
| argumentList: dotShorthand.argumentList, | |
| ), | |
| ); | |
| } else if (dotShorthand is ParsedDotShorthandNameImpl) { | |
| push( | |
| DotShorthandConstructorInvocation2Impl( | |
| constKeyword: token, | |
| period: dotShorthand.period, | |
| name: dotShorthand.name, | |
| typeArguments: null, | |
| argumentList: _syntheticArgumentList(dotShorthand.name), | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void endConstExpression(Token constKeyword) { | |
| assert(optional('const', constKeyword)); | |
| debugEvent("ConstExpression"); | |
| _handleConstructorInvocation(constKeyword); | |
| } | |
| @override | |
| void endConstLiteral(Token endToken) { | |
| debugEvent("endConstLiteral"); | |
| } | |
| @override | |
| void endConstructor( | |
| DeclarationKind kind, | |
| Token beginToken, | |
| Token? newToken, | |
| Token beginParam, | |
| Token? beginInitializers, | |
| Token endToken, | |
| ) { | |
| debugEvent("endConstructor"); | |
| switch (kind) { | |
| case DeclarationKind.Class: | |
| case DeclarationKind.ExtensionType: | |
| case DeclarationKind.Enum: | |
| _endClassConstructor( | |
| beginToken, | |
| beginParam, | |
| beginInitializers, | |
| endToken, | |
| ); | |
| case DeclarationKind.Mixin: | |
| case DeclarationKind.Extension: | |
| invalidNodes.add( | |
| _buildConstructorDeclaration( | |
| beginToken: beginToken, | |
| endToken: endToken, | |
| ), | |
| ); | |
| case DeclarationKind.TopLevel: | |
| throw UnsupportedError("Unexpected constructor kind $kind."); | |
| } | |
| } | |
| @override | |
| void endConstructorReference( | |
| Token start, | |
| Token? periodBeforeName, | |
| Token endToken, | |
| ConstructorReferenceContext constructorReferenceContext, | |
| ) { | |
| assert(optionalOrNull('.', periodBeforeName)); | |
| debugEvent("ConstructorReference"); | |
| var constructorName = pop() as Token?; | |
| var typeArguments = pop() as TypeArgumentListImpl?; | |
| var typeName = pop()!; | |
| var selector = switch (periodBeforeName) { | |
| var period? => ConstructorSelectorImpl.v2( | |
| period: period, | |
| name2: constructorName!, | |
| ), | |
| _ => null, | |
| }; | |
| push( | |
| ConstructorReference2Impl( | |
| typeReference: ConstructorTypeReferenceImpl( | |
| importPrefix: typeName is _QualifiedName | |
| ? ImportPrefixReferenceImpl( | |
| name: typeName.prefix, | |
| period: typeName.period, | |
| ) | |
| : null, | |
| name: typeName is _QualifiedName ? typeName.name : typeName as Token, | |
| typeArguments: typeArguments, | |
| ), | |
| selector: selector, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endDoWhileStatement( | |
| Token doKeyword, | |
| Token whileKeyword, | |
| Token semicolon, | |
| ) { | |
| assert(optional('do', doKeyword)); | |
| assert(optional('while', whileKeyword)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("DoWhileStatement"); | |
| var condition = pop() as _ParenthesizedCondition; | |
| var body = pop() as StatementImpl; | |
| push( | |
| DoStatementImpl( | |
| doKeyword: doKeyword, | |
| body: body, | |
| whileKeyword: whileKeyword, | |
| leftParenthesis: condition.leftParenthesis, | |
| condition2: condition.expression, | |
| rightParenthesis: condition.rightParenthesis, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endDoWhileStatementBody(Token token) { | |
| debugEvent("endDoWhileStatementBody"); | |
| } | |
| @override | |
| void endElseStatement(Token beginToken, Token endToken) { | |
| debugEvent("endElseStatement"); | |
| } | |
| @override | |
| void endEnumDeclaration( | |
| Token beginToken, | |
| Token enumKeyword, | |
| Token leftBrace, | |
| int memberCount, | |
| Token endToken, | |
| ) { | |
| assert(optional('enum', enumKeyword)); | |
| assert(optional('{', leftBrace) || optional(';', leftBrace)); | |
| debugEvent("Enum"); | |
| var builder = _classLikeBuilder as _EnumDeclarationBuilder; | |
| declarations.add(builder.build()); | |
| _classLikeBuilder = null; | |
| } | |
| @override | |
| void endExport(Token exportKeyword, Token semicolon) { | |
| assert(optional('export', exportKeyword)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("Export"); | |
| var combinators = pop() as List<CombinatorImpl>?; | |
| var configurations = pop() as List<ConfigurationImpl>?; | |
| var uri = pop() as StringLiteralImpl; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, exportKeyword); | |
| directives.add( | |
| ExportDirectiveImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| exportKeyword: exportKeyword, | |
| uri: uri, | |
| configurations: configurations, | |
| combinators: combinators, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endExtensionDeclaration( | |
| Token beginToken, | |
| Token extensionKeyword, | |
| Token? onKeyword, | |
| Token token, | |
| ) { | |
| var builder = _classLikeBuilder as _ExtensionDeclarationBuilder; | |
| ExtensionOnClauseImpl? onClause; | |
| if (onKeyword != null) { | |
| var extendedType = pop() as TypeAnnotationImpl; | |
| onClause = ExtensionOnClauseImpl( | |
| onKeyword: onKeyword, | |
| extendedType: extendedType, | |
| ); | |
| } | |
| pop(); // Error recovery: Primary constructor. | |
| pop(); // Error recovery: Const token. | |
| declarations.add(builder.build(typeKeyword: null, onClause: onClause)); | |
| _classLikeBuilder = null; | |
| } | |
| @override | |
| void endExtensionTypeDeclaration( | |
| Token beginToken, | |
| Token? augmentToken, | |
| Token extensionKeyword, | |
| Token typeKeyword, | |
| Token endToken, | |
| ) { | |
| var implementsClause = | |
| pop(NullValues.IdentifierList) as ImplementsClauseImpl?; | |
| var primaryConstructorBuilder = | |
| pop(NullValues.PrimaryConstructor) as _PrimaryConstructorBuilder?; | |
| var constKeyword = pop() as Token?; | |
| if (enableInlineClass) { | |
| var builder = _classLikeBuilder as _ExtensionTypeDeclarationBuilder; | |
| if (builder.augmentKeyword == null) { | |
| primaryConstructorBuilder ??= _PrimaryConstructorBuilder( | |
| constKeyword: null, | |
| constructorName: null, | |
| formalParameterList: _syntheticFormalParameterList(builder.name), | |
| ); | |
| } | |
| declarations.add( | |
| builder.build( | |
| typeKeyword: typeKeyword, | |
| constKeyword: constKeyword, | |
| primaryConstructorBuilder: primaryConstructorBuilder, | |
| implementsClause: implementsClause, | |
| ), | |
| ); | |
| } else { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.inline_class, | |
| startToken: typeKeyword, | |
| ); | |
| } | |
| _classLikeBuilder = null; | |
| } | |
| @override | |
| void endFactory( | |
| DeclarationKind kind, | |
| Token beginToken, | |
| Token factoryKeyword, | |
| Token endToken, | |
| ) { | |
| assert(optional('factory', factoryKeyword)); | |
| assert(optional(';', endToken) || optional('}', endToken)); | |
| debugEvent("endFactory"); | |
| switch (kind) { | |
| case DeclarationKind.Class: | |
| case DeclarationKind.Enum: | |
| case DeclarationKind.ExtensionType: | |
| _endFactoryMethod(beginToken, factoryKeyword, endToken); | |
| case DeclarationKind.Mixin: | |
| case DeclarationKind.Extension: | |
| invalidNodes.add( | |
| _buildFactoryConstructorDeclaration( | |
| beginToken: beginToken, | |
| factoryKeyword: factoryKeyword, | |
| endToken: endToken, | |
| ), | |
| ); | |
| case DeclarationKind.TopLevel: | |
| throw UnsupportedError("Unexpected factory kind $kind."); | |
| } | |
| } | |
| @override | |
| void endFieldInitializer(Token equals, Token endToken) { | |
| assert(optional('=', equals)); | |
| debugEvent("FieldInitializer"); | |
| var initializer = _popExpression(); | |
| var name = pop() as Token; | |
| reportErrorIfSuper(initializer); | |
| push( | |
| VariableDeclarationImpl( | |
| comment: null, | |
| metadata: [], | |
| name: name, | |
| equals: equals, | |
| initializer2: initializer, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endFields( | |
| DeclarationKind kind, | |
| Token? abstractToken, | |
| Token? augmentToken, | |
| Token? externalToken, | |
| Token? staticToken, | |
| Token? covariantToken, | |
| Token? lateToken, | |
| Token? varFinalOrConst, | |
| int count, | |
| Token beginToken, | |
| Token endToken, | |
| ) { | |
| debugEvent("endFields"); | |
| _endClassFields( | |
| abstractToken, | |
| augmentToken, | |
| externalToken, | |
| staticToken, | |
| covariantToken, | |
| lateToken, | |
| varFinalOrConst, | |
| count, | |
| beginToken, | |
| endToken, | |
| ); | |
| } | |
| @override | |
| void endForControlFlow(Token token) { | |
| debugEvent("endForControlFlow"); | |
| var body = _popCollectionElement(); | |
| var forLoopParts = pop() as ForPartsImpl; | |
| var leftParenthesis = pop() as Token; | |
| var forToken = pop() as Token; | |
| push( | |
| ForElementImpl( | |
| awaitKeyword: null, | |
| forKeyword: forToken, | |
| leftParenthesis: leftParenthesis, | |
| forLoopParts: forLoopParts, | |
| rightParenthesis: leftParenthesis.endGroup!, | |
| body2: body, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endForIn(Token endToken) { | |
| debugEvent("ForInExpression"); | |
| var body = pop() as StatementImpl; | |
| var forLoopParts = pop() as ForEachPartsImpl; | |
| var leftParenthesis = pop() as Token; | |
| var forToken = pop() as Token; | |
| var awaitToken = pop(NullValues.AwaitToken) as Token?; | |
| push( | |
| ForStatementImpl( | |
| awaitKeyword: awaitToken, | |
| forKeyword: forToken, | |
| leftParenthesis: leftParenthesis, | |
| forLoopParts: forLoopParts, | |
| rightParenthesis: leftParenthesis.endGroup!, | |
| body: body, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endForInBody(Token endToken) { | |
| debugEvent("endForInBody"); | |
| } | |
| @override | |
| void endForInControlFlow(Token token) { | |
| debugEvent("endForInControlFlow"); | |
| var body = _popCollectionElement(); | |
| var forLoopParts = pop() as ForEachPartsImpl; | |
| var leftParenthesis = pop() as Token; | |
| var forToken = pop() as Token; | |
| var awaitToken = pop(NullValues.AwaitToken) as Token?; | |
| push( | |
| ForElementImpl( | |
| awaitKeyword: awaitToken, | |
| forKeyword: forToken, | |
| leftParenthesis: leftParenthesis, | |
| forLoopParts: forLoopParts, | |
| rightParenthesis: leftParenthesis.endGroup!, | |
| body2: body, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endForInExpression(Token token) { | |
| debugEvent("ForInExpression"); | |
| } | |
| @override | |
| void endFormalParameter( | |
| Token? varOrFinal, | |
| Token? thisKeyword, | |
| Token? superKeyword, | |
| Token? periodAfterThisOrSuper, | |
| Token nameToken, | |
| Token? initializerStart, | |
| Token? initializerEnd, | |
| FormalParameterKind kind, | |
| MemberKind memberKind, | |
| ) { | |
| assert(optionalOrNull('this', thisKeyword)); | |
| assert(optionalOrNull('super', superKeyword)); | |
| assert( | |
| thisKeyword == null && superKeyword == null | |
| ? periodAfterThisOrSuper == null | |
| : optional('.', periodAfterThisOrSuper!), | |
| ); | |
| debugEvent("FormalParameter"); | |
| if (superKeyword != null && !enableSuperParameters) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.super_parameters, | |
| startToken: superKeyword, | |
| ); | |
| } | |
| var defaultClause = pop() as FormalParameterDefaultClauseImpl?; | |
| var name = pop() as Token?; | |
| var typeOrFunctionTypedParameter = pop(); | |
| var modifiers = pop() as _Modifiers?; | |
| var keyword = modifiers?.finalConstOrVarKeyword; | |
| var covariantKeyword = modifiers?.covariantKeyword; | |
| var requiredKeyword = modifiers?.requiredToken; | |
| Token? typeOrFunctionTypedBeginToken; | |
| switch (typeOrFunctionTypedParameter) { | |
| case AstNodeImpl self: | |
| typeOrFunctionTypedBeginToken = self.beginToken; | |
| case _FunctionTypedFormalParameterData( | |
| :var returnType, | |
| :var functionTypedSuffix, | |
| ): | |
| typeOrFunctionTypedBeginToken = | |
| returnType?.beginToken ?? functionTypedSuffix.beginToken; | |
| } | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment( | |
| metadata, | |
| modifiers?.beginToken ?? | |
| thisKeyword ?? | |
| typeOrFunctionTypedBeginToken ?? | |
| nameToken, | |
| ); | |
| TypeAnnotationImpl? type; | |
| FunctionTypedFormalParameterSuffixImpl? functionTypedSuffix; | |
| if (typeOrFunctionTypedParameter is _FunctionTypedFormalParameterData) { | |
| type = typeOrFunctionTypedParameter.returnType; | |
| functionTypedSuffix = typeOrFunctionTypedParameter.functionTypedSuffix; | |
| } else { | |
| type = typeOrFunctionTypedParameter as TypeAnnotationImpl?; | |
| } | |
| if (functionTypedSuffix != null && | |
| (thisKeyword != null || superKeyword != null) && | |
| keyword is KeywordToken) { | |
| var keywordKind = keyword.keyword; | |
| if (keywordKind == Keyword.CONST || | |
| keywordKind == Keyword.FINAL || | |
| keywordKind == Keyword.VAR) { | |
| handleRecoverableError( | |
| fe_diag.functionTypedParameterVar, | |
| keyword, | |
| keyword, | |
| ); | |
| } | |
| } | |
| ParameterKind analyzerKind = _toAnalyzerParameterKind(kind); | |
| FormalParameterImpl parameter; | |
| if (superKeyword != null) { | |
| assert( | |
| thisKeyword == null, | |
| "Can't have both 'this' and 'super' in a parameter.", | |
| ); | |
| if (memberKind != MemberKind.PrimaryConstructor) { | |
| // The parser reports a special error for declaring parameters, so we | |
| // avoid emitting this error here for primary constructors. | |
| if (functionTypedSuffix == null && | |
| keyword is KeywordToken && | |
| keyword.keyword == Keyword.VAR) { | |
| handleRecoverableError( | |
| fe_diag.extraneousModifier.withArguments(lexeme: keyword), | |
| keyword, | |
| keyword, | |
| ); | |
| } | |
| } | |
| parameter = SuperFormalParameterImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| kind: analyzerKind, | |
| covariantKeyword: covariantKeyword, | |
| requiredKeyword: requiredKeyword, | |
| constFinalOrVarKeyword: keyword, | |
| type: type, | |
| superKeyword: superKeyword, | |
| period: periodAfterThisOrSuper!, | |
| name: name!, | |
| functionTypedSuffix: functionTypedSuffix, | |
| defaultClause: defaultClause, | |
| ); | |
| } else if (thisKeyword != null) { | |
| assert( | |
| superKeyword == null, | |
| "Can't have both 'this' and 'super' in a parameter.", | |
| ); | |
| parameter = FieldFormalParameterImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| kind: analyzerKind, | |
| covariantKeyword: covariantKeyword, | |
| requiredKeyword: requiredKeyword, | |
| constFinalOrVarKeyword: keyword, | |
| type: type, | |
| thisKeyword: thisKeyword, | |
| period: periodAfterThisOrSuper!, | |
| name: name!, | |
| functionTypedSuffix: functionTypedSuffix, | |
| defaultClause: defaultClause, | |
| ); | |
| } else { | |
| parameter = RegularFormalParameterImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| kind: analyzerKind, | |
| covariantKeyword: covariantKeyword, | |
| requiredKeyword: requiredKeyword, | |
| constFinalOrVarKeyword: keyword, | |
| type: type, | |
| name: name, | |
| functionTypedSuffix: functionTypedSuffix, | |
| defaultClause: defaultClause, | |
| ); | |
| } | |
| push(parameter); | |
| } | |
| @override | |
| void endFormalParameterDefaultValueExpression() { | |
| debugEvent("FormalParameterDefaultValueExpression"); | |
| } | |
| @override | |
| void endFormalParameters( | |
| int count, | |
| Token leftParenthesis, | |
| Token rightParenthesis, | |
| MemberKind kind, | |
| ) { | |
| assert(optional('(', leftParenthesis)); | |
| assert(optional(')', rightParenthesis)); | |
| debugEvent("FormalParameters"); | |
| var rawParameters = popTypedList(count) ?? const <Object>[]; | |
| var requiredPositionalFormalParameters = <FormalParameterImpl>[]; | |
| DelimitedFormalParametersImpl? delimitedFormalParameters; | |
| for (Object raw in rawParameters) { | |
| if (raw is DelimitedFormalParametersImpl) { | |
| delimitedFormalParameters = raw; | |
| } else { | |
| requiredPositionalFormalParameters.add(raw as FormalParameterImpl); | |
| } | |
| } | |
| push( | |
| FormalParameterListImpl( | |
| leftParenthesis: leftParenthesis, | |
| requiredPositionalFormalParameters: requiredPositionalFormalParameters, | |
| delimitedFormalParameters: delimitedFormalParameters, | |
| rightParenthesis: rightParenthesis, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endForStatement(Token endToken) { | |
| debugEvent("ForStatement"); | |
| var body = pop() as StatementImpl; | |
| var forLoopParts = pop() as ForPartsImpl; | |
| var leftParen = pop() as Token; | |
| var forToken = pop() as Token; | |
| push( | |
| ForStatementImpl( | |
| awaitKeyword: null, | |
| forKeyword: forToken, | |
| leftParenthesis: leftParen, | |
| forLoopParts: forLoopParts, | |
| rightParenthesis: leftParen.endGroup!, | |
| body: body, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endForStatementBody(Token endToken) { | |
| debugEvent("endForStatementBody"); | |
| } | |
| @override | |
| void endFunctionExpression(Token beginToken, Token endToken) { | |
| // TODO(paulberry): set up scopes properly to resolve parameters and type | |
| // variables. Note that this is tricky due to the handling of initializers | |
| // in constructors, so the logic should be shared with BodyBuilder as much | |
| // as possible. | |
| debugEvent("FunctionExpression"); | |
| var body = pop() as FunctionBodyImpl; | |
| var parameters = pop() as FormalParameterListImpl?; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| push( | |
| FunctionExpressionImpl( | |
| typeParameters: typeParameters, | |
| parameters: parameters, | |
| body: body, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endFunctionName( | |
| Token beginToken, | |
| Token token, | |
| bool isFunctionExpression, | |
| ) { | |
| debugEvent("FunctionName"); | |
| } | |
| @override | |
| void endFunctionType(Token functionToken, Token? questionMark) { | |
| assert(optional('Function', functionToken)); | |
| debugEvent("FunctionType"); | |
| var parameters = pop() as FormalParameterListImpl; | |
| var returnType = pop() as TypeAnnotationImpl?; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| push( | |
| GenericFunctionTypeImpl( | |
| returnType: returnType, | |
| functionKeyword: functionToken, | |
| typeParameters: typeParameters, | |
| parameters: parameters, | |
| question: questionMark, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endFunctionTypedFormalParameter(Token nameToken, Token? question) { | |
| debugEvent("FunctionTypedFormalParameter"); | |
| var formalParameters = pop() as FormalParameterListImpl; | |
| var returnType = pop() as TypeAnnotationImpl?; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| // Create temporary data that will be attached to the concrete parameter in | |
| // [endFormalParameter]. | |
| push( | |
| _FunctionTypedFormalParameterData( | |
| returnType: returnType, | |
| functionTypedSuffix: FunctionTypedFormalParameterSuffixImpl( | |
| typeParameters: typeParameters, | |
| formalParameters: formalParameters, | |
| question: question, | |
| ), | |
| ), | |
| ); | |
| } | |
| @override | |
| void endHide(Token hideKeyword) { | |
| assert(optional('hide', hideKeyword)); | |
| debugEvent("Hide"); | |
| var names = pop() as List<Token>; | |
| push( | |
| HideCombinatorImpl( | |
| keyword: hideKeyword, | |
| names: [ | |
| for (var identifier in names) CombinatorNameImpl(name: identifier), | |
| ], | |
| ), | |
| ); | |
| } | |
| @override | |
| void endIfControlFlow(Token token) { | |
| var thenElement = _popCollectionElement(); | |
| var condition = pop() as _ParenthesizedCondition; | |
| var ifToken = pop() as Token; | |
| push( | |
| IfElementImpl( | |
| ifKeyword: ifToken, | |
| leftParenthesis: condition.leftParenthesis, | |
| expression2: condition.expression, | |
| caseClause: condition.caseClause, | |
| rightParenthesis: condition.rightParenthesis, | |
| thenElement2: thenElement, | |
| elseKeyword: null, | |
| elseElement2: null, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endIfElseControlFlow(Token token) { | |
| var elseElement = _popCollectionElement(); | |
| var elseToken = pop() as Token; | |
| var thenElement = _popCollectionElement(); | |
| var condition = pop() as _ParenthesizedCondition; | |
| var ifToken = pop() as Token; | |
| push( | |
| IfElementImpl( | |
| ifKeyword: ifToken, | |
| leftParenthesis: condition.leftParenthesis, | |
| expression2: condition.expression, | |
| caseClause: condition.caseClause, | |
| rightParenthesis: condition.rightParenthesis, | |
| thenElement2: thenElement, | |
| elseKeyword: elseToken, | |
| elseElement2: elseElement, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endIfStatement(Token ifToken, Token? elseToken, Token endToken) { | |
| assert(optional('if', ifToken)); | |
| assert(optionalOrNull('else', elseToken)); | |
| var elsePart = popIfNotNull(elseToken) as StatementImpl?; | |
| var thenPart = pop() as StatementImpl; | |
| var condition = pop() as _ParenthesizedCondition; | |
| push( | |
| IfStatementImpl( | |
| ifKeyword: ifToken, | |
| leftParenthesis: condition.leftParenthesis, | |
| expression2: condition.expression, | |
| caseClause: condition.caseClause, | |
| rightParenthesis: condition.rightParenthesis, | |
| thenStatement: thenPart, | |
| elseKeyword: elseToken, | |
| elseStatement: elsePart, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endImplicitCreationExpression(Token token, Token openAngleBracket) { | |
| debugEvent("ImplicitCreationExpression"); | |
| _handleConstructorInvocation(null); | |
| } | |
| @override | |
| void endImport(Token importKeyword, Token? semicolon) { | |
| assert(optional('import', importKeyword)); | |
| assert(optionalOrNull(';', semicolon)); | |
| debugEvent("Import"); | |
| var combinators = pop() as List<CombinatorImpl>?; | |
| var deferredKeyword = pop(NullValues.Deferred) as Token?; | |
| var asKeyword = pop(NullValues.As) as Token?; | |
| var prefixName = pop(NullValues.Prefix) as Token?; | |
| var configurations = pop() as List<ConfigurationImpl>?; | |
| var uri = pop() as StringLiteralImpl; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, importKeyword); | |
| directives.add( | |
| ImportDirectiveImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| importKeyword: importKeyword, | |
| uri: uri, | |
| configurations: configurations, | |
| deferredKeyword: deferredKeyword, | |
| asKeyword: asKeyword, | |
| prefixName: prefixName, | |
| combinators: combinators, | |
| semicolon: semicolon ?? Tokens.semicolon(), | |
| ), | |
| ); | |
| } | |
| @override | |
| void endInitializedIdentifier(Token nameToken) { | |
| debugEvent("InitializedIdentifier"); | |
| var node = pop(); | |
| VariableDeclarationImpl variable; | |
| // TODO(paulberry): This seems kludgy. It would be preferable if we | |
| // could respond to a "handleNoVariableInitializer" event by converting a | |
| // name token into a VariableDeclaration, and then when this code was | |
| // reached, node would always be a VariableDeclaration. | |
| if (node is VariableDeclarationImpl) { | |
| variable = node; | |
| } else if (node is Token) { | |
| variable = VariableDeclarationImpl( | |
| comment: null, | |
| metadata: [], | |
| name: node, | |
| equals: null, | |
| initializer2: null, | |
| ); | |
| } else { | |
| internalProblem( | |
| fe_diag.internalProblemUnhandled.withArguments( | |
| what: "${node.runtimeType}", | |
| where: "identifier", | |
| ), | |
| nameToken.charOffset, | |
| uri, | |
| ); | |
| } | |
| push(variable); | |
| } | |
| @override | |
| void endInitializers(int count, Token colon, Token endToken) { | |
| assert(optional(':', colon)); | |
| debugEvent("Initializers"); | |
| var initializerObjects = popTypedList(count) ?? const []; | |
| if (!isFullAst) return; | |
| push(colon); | |
| var initializers = <ConstructorInitializerImpl>[]; | |
| for (Object initializerObject in initializerObjects) { | |
| var initializer = buildInitializer(initializerObject); | |
| if (initializer != null) { | |
| initializers.add(initializer); | |
| } else { | |
| handleRecoverableError( | |
| fe_diag.invalidInitializer, | |
| initializerObject is AstNodeImpl | |
| ? initializerObject.beginToken | |
| : colon, | |
| initializerObject is AstNodeImpl ? initializerObject.endToken : colon, | |
| ); | |
| } | |
| } | |
| push(initializers); | |
| } | |
| @override | |
| void endInvalidAwaitExpression( | |
| Token awaitKeyword, | |
| Token endToken, | |
| MessageCode errorCode, | |
| ) { | |
| debugEvent("InvalidAwaitExpression"); | |
| endAwaitExpression(awaitKeyword, endToken); | |
| } | |
| @override | |
| void endInvalidYieldStatement( | |
| Token yieldKeyword, | |
| Token? starToken, | |
| Token endToken, | |
| MessageCode errorCode, | |
| ) { | |
| debugEvent("InvalidYieldStatement"); | |
| endYieldStatement(yieldKeyword, starToken, endToken); | |
| } | |
| @override | |
| void endIsOperatorType(Token asOperator) { | |
| debugEvent("IsOperatorType"); | |
| } | |
| @override | |
| void endLabeledStatement(int labelCount) { | |
| debugEvent("LabeledStatement"); | |
| var statement = pop() as StatementImpl; | |
| var labels = popTypedList2<LabelImpl>(labelCount); | |
| push(LabeledStatementImpl(labels: labels, statement: statement)); | |
| } | |
| @override | |
| void endLibraryAugmentation( | |
| Token augmentKeyword, | |
| Token libraryKeyword, | |
| Token semicolon, | |
| ) { | |
| // TODO(scheglov): remove this method | |
| pop() as StringLiteralImpl; // uri | |
| pop() as List<AnnotationImpl>?; // metadata | |
| } | |
| @override | |
| void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { | |
| assert(optional('library', libraryKeyword)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("LibraryName"); | |
| var libraryName = hasName ? pop() as List<Token>? : null; | |
| if (!hasName && !enableUnnamedLibraries) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.unnamed_libraries, | |
| startToken: libraryKeyword, | |
| ); | |
| } | |
| DottedNameImpl? name; | |
| if (libraryName != null) { | |
| name = DottedNameImpl(tokens: libraryName); | |
| } | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, libraryKeyword); | |
| directives.add( | |
| LibraryDirectiveImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| libraryKeyword: libraryKeyword, | |
| name: name, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endLiteralString(int interpolationCount, Token endToken) { | |
| debugEvent("endLiteralString"); | |
| if (interpolationCount == 0) { | |
| var token = pop() as Token; | |
| String value = unescapeString(token.lexeme, token, this); | |
| push(SimpleStringLiteralImpl(literal: token, value: value)); | |
| } else { | |
| var parts = popTypedList(1 + interpolationCount * 2)!; | |
| var first = parts.first as Token; | |
| var last = parts.last as Token; | |
| Quote quote = analyzeQuote(first.lexeme); | |
| var elements = <InterpolationElementImpl>[]; | |
| elements.add( | |
| InterpolationStringImpl( | |
| contents: first, | |
| value: unescapeFirstStringPart(first.lexeme, quote, first, this), | |
| ), | |
| ); | |
| for (int i = 1; i < parts.length - 1; i++) { | |
| var part = parts[i]; | |
| if (part is Token) { | |
| elements.add( | |
| InterpolationStringImpl( | |
| contents: part, | |
| value: unescapeMiddleStringPart(part.lexeme, quote, part, this), | |
| ), | |
| ); | |
| } else if (part is InterpolationExpressionImpl) { | |
| elements.add(part); | |
| } else { | |
| internalProblem( | |
| fe_diag.internalProblemUnhandled.withArguments( | |
| what: "${part.runtimeType}", | |
| where: "string interpolation", | |
| ), | |
| first.charOffset, | |
| uri, | |
| ); | |
| } | |
| } | |
| elements.add( | |
| InterpolationStringImpl( | |
| contents: last, | |
| value: unescapeLastStringPart( | |
| last.lexeme, | |
| quote, | |
| last, | |
| last.isSynthetic, | |
| this, | |
| ), | |
| ), | |
| ); | |
| push(StringInterpolationImpl(elements: elements)); | |
| } | |
| } | |
| @override | |
| void endLiteralSymbol(Token hashToken, int tokenCount) { | |
| assert(optional('#', hashToken)); | |
| debugEvent("LiteralSymbol"); | |
| var components = popTypedList2<Token>(tokenCount); | |
| push(SymbolLiteralImpl(poundSign: hashToken, components: components)); | |
| } | |
| @override | |
| void endLocalFunctionDeclaration(Token token) { | |
| debugEvent("LocalFunctionDeclaration"); | |
| var body = pop() as FunctionBodyImpl; | |
| if (isFullAst) { | |
| pop(); // constructor initializers | |
| pop(); // separator before constructor initializers | |
| } | |
| var parameters = pop() as FormalParameterListImpl; | |
| var name = pop() as Token; | |
| var returnType = pop() as TypeAnnotationImpl?; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var metadata = pop(NullValues.Metadata) as List<AnnotationImpl>?; | |
| var functionExpression = FunctionExpressionImpl( | |
| typeParameters: typeParameters, | |
| parameters: parameters, | |
| body: body, | |
| ); | |
| var functionDeclaration = FunctionDeclarationImpl( | |
| comment: null, | |
| metadata: metadata, | |
| augmentKeyword: null, | |
| externalKeyword: null, | |
| returnType: returnType, | |
| propertyKeyword: null, | |
| name: name, | |
| functionExpression: functionExpression, | |
| ); | |
| push( | |
| FunctionDeclarationStatementImpl( | |
| functionDeclaration: functionDeclaration, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endMember() { | |
| debugEvent("Member"); | |
| } | |
| @override | |
| void endMetadata(Token atSign, Token? periodBeforeName, Token endToken) { | |
| assert(optional('@', atSign)); | |
| assert(optionalOrNull('.', periodBeforeName)); | |
| debugEvent("Metadata"); | |
| var argumentList = pop() as ArgumentListImpl?; | |
| var constructorName = periodBeforeName != null ? pop() as Token : null; | |
| var typeArguments = pop() as TypeArgumentListImpl?; | |
| if (typeArguments != null && | |
| !_featureSet.isEnabled(Feature.generic_metadata)) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.generic_metadata, | |
| startToken: typeArguments.beginToken, | |
| ); | |
| } | |
| var name = pop()!; | |
| if (name is! Token && name is! _QualifiedName) { | |
| throw StateError('Unexpected annotation name: $name'); | |
| } | |
| ExpressionImpl expression; | |
| if (argumentList != null) { | |
| // An annotation with arguments can only invoke a constructor, so it is | |
| // built as if written with `const`, except that there is no keyword. | |
| expression = ConstructorInvocationImpl( | |
| keyword: null, | |
| constructorReference: ConstructorReference2Impl( | |
| typeReference: ConstructorTypeReferenceImpl( | |
| importPrefix: name is _QualifiedName | |
| ? ImportPrefixReferenceImpl( | |
| name: name.prefix, | |
| period: name.period, | |
| ) | |
| : null, | |
| name: name is _QualifiedName ? name.name : name as Token, | |
| typeArguments: typeArguments, | |
| ), | |
| selector: switch (periodBeforeName) { | |
| var period? => ConstructorSelectorImpl.v2( | |
| period: period, | |
| name2: constructorName!, | |
| ), | |
| null => null, | |
| }, | |
| ), | |
| argumentList: argumentList, | |
| typeArguments: null, | |
| ); | |
| } else { | |
| // Without arguments, the annotation is a read that resolution | |
| // interprets like the same keywordless expression. | |
| ParsedExpressionImpl operand = switch (name) { | |
| _QualifiedName() => ParsedNameAccessImpl( | |
| operand: ParsedUnqualifiedNameImpl(name: name.prefix), | |
| operator: name.period, | |
| name: name.name, | |
| ), | |
| _ => ParsedUnqualifiedNameImpl(name: name as Token), | |
| }; | |
| if (typeArguments != null) { | |
| operand = ParsedTypeArgumentsImpl( | |
| operand: operand, | |
| typeArguments: typeArguments, | |
| ); | |
| } | |
| if (periodBeforeName != null) { | |
| operand = ParsedNameAccessImpl( | |
| operand: operand, | |
| operator: periodBeforeName, | |
| name: constructorName!, | |
| ); | |
| } | |
| expression = operand; | |
| } | |
| push(AnnotationImpl(atSign: atSign, expression: expression)); | |
| } | |
| @override | |
| void endMetadataStar(int count) { | |
| debugEvent("MetadataStar"); | |
| push(popTypedList<AnnotationImpl>(count) ?? NullValues.Metadata); | |
| } | |
| @override | |
| void endMethod( | |
| DeclarationKind kind, | |
| Token? getOrSet, | |
| Token beginToken, | |
| Token beginParam, | |
| Token? beginInitializers, | |
| Token endToken, | |
| ) { | |
| debugEvent("endMethod"); | |
| _endClassMethod( | |
| getOrSet, | |
| beginToken, | |
| beginParam, | |
| beginInitializers, | |
| endToken, | |
| ); | |
| } | |
| @override | |
| void endMixinDeclaration(Token beginToken, Token endToken) { | |
| debugEvent("MixinDeclaration"); | |
| var builder = _classLikeBuilder as _MixinDeclarationBuilder; | |
| declarations.add(builder.build()); | |
| _classLikeBuilder = null; | |
| } | |
| @override | |
| void endNamedFunctionExpression(Token endToken) { | |
| debugEvent("NamedFunctionExpression"); | |
| var body = pop() as FunctionBodyImpl; | |
| if (isFullAst) { | |
| pop(); // constructor initializers | |
| pop(); // separator before constructor initializers | |
| } | |
| var parameters = pop() as FormalParameterListImpl; | |
| pop(); // name | |
| pop(); // returnType | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| push( | |
| FunctionExpressionImpl( | |
| typeParameters: typeParameters, | |
| parameters: parameters, | |
| body: body, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endNamedMixinApplication( | |
| Token beginToken, | |
| Token classKeyword, | |
| Token equalsToken, | |
| Token? implementsKeyword, | |
| Token semicolon, | |
| ) { | |
| assert(optional('class', classKeyword)); | |
| assert(optionalOrNull('=', equalsToken)); | |
| assert(optionalOrNull('implements', implementsKeyword)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("NamedMixinApplication"); | |
| ImplementsClauseImpl? implementsClause; | |
| if (implementsKeyword != null) { | |
| var interfaces = _popNamedTypeList( | |
| locatableDiagnostic: diag.expectedNamedTypeImplements, | |
| ); | |
| implementsClause = ImplementsClauseImpl( | |
| implementsKeyword: implementsKeyword, | |
| interfaces: interfaces, | |
| ); | |
| } | |
| var withClause = pop(NullValues.WithClause) as WithClauseImpl; | |
| var superclass = pop() as TypeAnnotationImpl; | |
| if (superclass is! NamedTypeImpl) { | |
| diagnosticReporter.diagnosticReporter?.report( | |
| diag.expectedNamedTypeExtends.at(superclass), | |
| ); | |
| var beginToken = superclass.beginToken; | |
| var endToken = superclass.endToken; | |
| var currentToken = beginToken; | |
| var count = 1; | |
| while (currentToken != endToken) { | |
| count++; | |
| currentToken = currentToken.next!; | |
| } | |
| var nameToken = parser.rewriter.replaceNextTokensWithSyntheticToken( | |
| beginToken.previous!, | |
| count, | |
| TokenType.IDENTIFIER, | |
| ); | |
| superclass = NamedTypeImpl( | |
| importPrefix: null, | |
| name: nameToken, | |
| typeArguments: null, | |
| question: null, | |
| ); | |
| } | |
| var mixinKeyword = pop(NullValues.Token) as Token?; | |
| var augmentKeyword = pop(NullValues.Token) as Token?; | |
| var finalKeyword = pop(NullValues.Token) as Token?; | |
| var interfaceKeyword = pop(NullValues.Token) as Token?; | |
| var baseKeyword = pop(NullValues.Token) as Token?; | |
| var sealedKeyword = pop(NullValues.Token) as Token?; | |
| var modifiers = pop() as _Modifiers?; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var name = pop() as Token; | |
| var abstractKeyword = modifiers?.abstractKeyword; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, beginToken); | |
| declarations.add( | |
| ClassTypeAliasImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| typedefKeyword: classKeyword, | |
| name: name, | |
| typeParameters: typeParameters, | |
| equals: equalsToken, | |
| abstractKeyword: abstractKeyword, | |
| sealedKeyword: sealedKeyword, | |
| baseKeyword: baseKeyword, | |
| interfaceKeyword: interfaceKeyword, | |
| finalKeyword: finalKeyword, | |
| augmentKeyword: augmentKeyword, | |
| mixinKeyword: mixinKeyword, | |
| superclass: superclass, | |
| withClause: withClause, | |
| implementsClause: implementsClause, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endNewExpression(Token newKeyword) { | |
| assert(optional('new', newKeyword)); | |
| debugEvent("NewExpression"); | |
| _handleConstructorInvocation(newKeyword); | |
| } | |
| @override | |
| void endOptionalFormalParameters( | |
| int count, | |
| Token leftDelimiter, | |
| Token rightDelimiter, | |
| MemberKind kind, | |
| ) { | |
| assert( | |
| (optional('[', leftDelimiter) && optional(']', rightDelimiter)) || | |
| (optional('{', leftDelimiter) && optional('}', rightDelimiter)), | |
| ); | |
| debugEvent("OptionalFormalParameters"); | |
| var formalParameters = popTypedList2<FormalParameterImpl>(count); | |
| push( | |
| DelimitedFormalParametersImpl( | |
| leftDelimiter: leftDelimiter, | |
| formalParameters: formalParameters, | |
| rightDelimiter: rightDelimiter, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endParenthesizedExpression(Token leftParenthesis) { | |
| assert(optional('(', leftParenthesis)); | |
| debugEvent("ParenthesizedExpression"); | |
| var expression = _popExpression(); | |
| reportErrorIfSuper(expression); | |
| push( | |
| ParenthesizedExpressionImpl( | |
| leftParenthesis: leftParenthesis, | |
| expression2: expression, | |
| rightParenthesis: leftParenthesis.endGroup!, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endPart(Token partKeyword, Token semicolon) { | |
| assert(optional('part', partKeyword)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("Part"); | |
| var uri = pop() as StringLiteralImpl; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, partKeyword); | |
| directives.add( | |
| PartDirectiveImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| partKeyword: partKeyword, | |
| uri: uri, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endPartOf( | |
| Token partKeyword, | |
| Token ofKeyword, | |
| Token semicolon, | |
| bool hasName, | |
| ) { | |
| assert(optional('part', partKeyword)); | |
| assert(optional('of', ofKeyword)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("PartOf"); | |
| var libraryNameOrUri = pop(); | |
| DottedNameImpl? name; | |
| StringLiteralImpl? uri; | |
| if (libraryNameOrUri is StringLiteralImpl) { | |
| uri = libraryNameOrUri; | |
| } else { | |
| var libraryName = libraryNameOrUri as List<Token>; | |
| name = DottedNameImpl(tokens: libraryName); | |
| if (_featureSet.isEnabled(Feature.enhanced_parts)) { | |
| diagnosticReporter.diagnosticReporter?.report(diag.partOfName.at(name)); | |
| } | |
| } | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, partKeyword); | |
| directives.add( | |
| PartOfDirectiveImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| partKeyword: partKeyword, | |
| ofKeyword: ofKeyword, | |
| uri: uri, | |
| libraryName: name, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endPattern(Token token) { | |
| debugEvent("Pattern"); | |
| } | |
| @override | |
| void endPatternGuard(Token when) { | |
| debugEvent("PatternGuard"); | |
| var expression = _popExpression(); | |
| push(WhenClauseImpl(whenKeyword: when, expression2: expression)); | |
| } | |
| @override | |
| void endPrimaryConstructor( | |
| DeclarationKind kind, | |
| Token beginToken, | |
| Token endToken, | |
| Token? constKeyword, | |
| bool hasConstructorName, | |
| ) { | |
| var formalParameterList = pop() as FormalParameterListImpl?; | |
| if (formalParameterList == null) { | |
| var extensionTypeName = beginToken.previous!; | |
| formalParameterList = _syntheticFormalParameterList(extensionTypeName); | |
| } | |
| switch (kind) { | |
| case DeclarationKind.TopLevel: | |
| case DeclarationKind.Mixin: | |
| case DeclarationKind.Extension: | |
| // Invalid. Error reported in the parser. | |
| break; | |
| case DeclarationKind.ExtensionType: | |
| // Always valid. | |
| break; | |
| case DeclarationKind.Class: | |
| case DeclarationKind.Enum: | |
| if (!_featureSet.isEnabled(Feature.primary_constructors)) { | |
| _reportFeatureNotEnabled( | |
| feature: Feature.primary_constructors, | |
| startToken: beginToken, | |
| ); | |
| } | |
| } | |
| PrimaryConstructorNameImpl? constructorName; | |
| if (hasConstructorName) { | |
| var nameIdentifier = pop() as Token; | |
| constructorName = PrimaryConstructorNameImpl( | |
| period: beginToken, | |
| name: nameIdentifier, | |
| ); | |
| } | |
| push(constKeyword ?? const NullValue("Token")); | |
| push( | |
| _PrimaryConstructorBuilder( | |
| constKeyword: constKeyword, | |
| constructorName: constructorName, | |
| formalParameterList: formalParameterList, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endPrimaryConstructorBody( | |
| Token beginToken, | |
| Token thisToken, | |
| Token? beginInitializers, | |
| Token endToken, | |
| ) { | |
| assert(optional('augment', beginToken) || optional('this', beginToken)); | |
| assert(optional('this', thisToken)); | |
| var body = pop() as FunctionBodyImpl; | |
| var initializers = (pop() as List<ConstructorInitializerImpl>?) ?? const []; | |
| var colon = pop() as Token?; | |
| var modifiers = pop() as _Modifiers; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, beginToken); | |
| _classLikeBuilder!.members.add( | |
| PrimaryConstructorBodyImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: modifiers.augmentKeyword, | |
| thisKeyword: thisToken, | |
| colon: colon, | |
| initializers: initializers, | |
| body: body, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endRecordLiteral(Token leftParenthesis, int count, Token? constKeyword) { | |
| debugEvent("RecordLiteral"); | |
| var fields = popTypedList<RecordLiteralFieldImpl>(count) ?? const []; | |
| var rightParenthesis = leftParenthesis.endGroup!; | |
| if (enableRecords) { | |
| push( | |
| RecordLiteralImpl( | |
| constKeyword: constKeyword, | |
| leftParenthesis: leftParenthesis, | |
| fields2: fields, | |
| rightParenthesis: rightParenthesis, | |
| ), | |
| ); | |
| } else { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.records, | |
| startToken: leftParenthesis, | |
| ); | |
| var expression = fields.firstOrNull?.fieldExpression2; | |
| expression ??= ParsedUnqualifiedNameImpl( | |
| name: parser.rewriter.insertSyntheticIdentifier(leftParenthesis), | |
| ); | |
| push( | |
| ParenthesizedExpressionImpl( | |
| leftParenthesis: leftParenthesis, | |
| expression2: expression, | |
| rightParenthesis: rightParenthesis, | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void endRecordType( | |
| Token leftBracket, | |
| Token? questionMark, | |
| int count, | |
| bool hasNamedFields, | |
| ) { | |
| debugEvent("RecordType"); | |
| RecordTypeAnnotationNamedFieldsImpl? namedFields; | |
| var elements = popTypedList<Object>(count) ?? const []; | |
| var last = elements.lastOrNull; | |
| if (last is RecordTypeAnnotationNamedFieldsImpl) { | |
| elements.removeLast(); | |
| namedFields = last; | |
| } | |
| var positionalFields = <RecordTypeAnnotationPositionalFieldImpl>[]; | |
| for (var elem in elements) { | |
| positionalFields.add(elem as RecordTypeAnnotationPositionalFieldImpl); | |
| } | |
| if (enableRecords) { | |
| push( | |
| RecordTypeAnnotationImpl( | |
| leftParenthesis: leftBracket, | |
| positionalFields: positionalFields, | |
| namedFields: namedFields, | |
| rightParenthesis: leftBracket.endGroup!, | |
| question: questionMark, | |
| ), | |
| ); | |
| } else { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.records, | |
| startToken: leftBracket, | |
| ); | |
| push( | |
| NamedTypeImpl( | |
| importPrefix: null, | |
| name: parser.rewriter.insertSyntheticIdentifier(leftBracket), | |
| typeArguments: null, | |
| question: questionMark, | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void endRecordTypeEntry() { | |
| debugEvent("RecordTypeEntry"); | |
| var name = pop() as Token?; | |
| var type = pop() as TypeAnnotationImpl; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| push( | |
| RecordTypeAnnotationPositionalFieldImpl( | |
| metadata: metadata, | |
| type: type, | |
| name: name, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endRecordTypeNamedFields(int count, Token leftBracket) { | |
| debugEvent("RecordTypeNamedFields"); | |
| var elements = | |
| popTypedList<RecordTypeAnnotationPositionalFieldImpl>(count) ?? | |
| const []; | |
| var fields = <RecordTypeAnnotationNamedFieldImpl>[]; | |
| for (var elem in elements) { | |
| fields.add( | |
| RecordTypeAnnotationNamedFieldImpl( | |
| metadata: elem.metadata, | |
| type: elem.type, | |
| name: elem.name!, | |
| ), | |
| ); | |
| } | |
| push( | |
| RecordTypeAnnotationNamedFieldsImpl( | |
| leftBracket: leftBracket, | |
| fields: fields, | |
| rightBracket: leftBracket.endGroup!, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endRedirectingFactoryBody(Token equalToken, Token endToken) { | |
| assert(optional('=', equalToken)); | |
| debugEvent("RedirectingFactoryBody"); | |
| var constructorReference = pop() as ConstructorReference2Impl; | |
| var starToken = pop() as Token?; | |
| var asyncToken = pop() as Token?; | |
| push( | |
| _RedirectingFactoryBody( | |
| asyncToken, | |
| starToken, | |
| equalToken, | |
| constructorReference, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endRethrowStatement(Token rethrowToken, Token semicolon) { | |
| assert(optional('rethrow', rethrowToken)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("RethrowStatement"); | |
| var expression = RethrowExpressionImpl(rethrowKeyword: rethrowToken); | |
| // TODO(scheglov): According to the specification, 'rethrow' is a statement. | |
| push( | |
| ExpressionStatementImpl(expression2: expression, semicolon: semicolon), | |
| ); | |
| } | |
| @override | |
| void endReturnStatement( | |
| bool hasExpression, | |
| Token returnKeyword, | |
| Token semicolon, | |
| ) { | |
| assert(optional('return', returnKeyword)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("ReturnStatement"); | |
| var expression = hasExpression ? _popExpression() : null; | |
| push( | |
| ReturnStatementImpl( | |
| returnKeyword: returnKeyword, | |
| expression2: expression, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endShow(Token showKeyword) { | |
| assert(optional('show', showKeyword)); | |
| debugEvent("Show"); | |
| var names = pop() as List<Token>; | |
| push( | |
| ShowCombinatorImpl( | |
| keyword: showKeyword, | |
| names: [ | |
| for (var identifier in names) CombinatorNameImpl(name: identifier), | |
| ], | |
| ), | |
| ); | |
| } | |
| @override | |
| void endSwitchBlock(int caseCount, Token leftBracket, Token rightBracket) { | |
| assert(optional('{', leftBracket)); | |
| assert(optional('}', rightBracket)); | |
| debugEvent("SwitchBlock"); | |
| var membersList = popTypedList2<List<SwitchMemberImpl>>(caseCount); | |
| var members = membersList.flattenedToList; | |
| Set<String> labels = <String>{}; | |
| for (var member in members) { | |
| for (var label in member.labels) { | |
| var lexeme = label.name.lexeme; | |
| if (!labels.add(lexeme)) { | |
| handleRecoverableError( | |
| fe_diag.duplicateLabelInSwitchStatement.withArguments( | |
| labelName: lexeme, | |
| ), | |
| label.beginToken, | |
| label.beginToken, | |
| ); | |
| } | |
| } | |
| } | |
| push(leftBracket); | |
| push(members); | |
| push(rightBracket); | |
| } | |
| @override | |
| void endSwitchCase( | |
| int labelCount, | |
| int expressionCount, | |
| Token? defaultKeyword, | |
| Token? colonAfterDefault, | |
| int statementCount, | |
| Token beginToken, | |
| Token endToken, | |
| ) { | |
| assert(optionalOrNull('default', defaultKeyword)); | |
| assert( | |
| defaultKeyword == null | |
| ? colonAfterDefault == null | |
| : optional(':', colonAfterDefault!), | |
| ); | |
| debugEvent("SwitchCase"); | |
| var statements = popTypedList2<StatementImpl>(statementCount); | |
| List<SwitchMemberImpl?> members; | |
| List<LabelImpl> popLabels() { | |
| var labels = <LabelImpl>[]; | |
| while (peek() is LabelImpl) { | |
| labels.insert(0, pop() as LabelImpl); | |
| --labelCount; | |
| } | |
| return labels; | |
| } | |
| SwitchMemberImpl updateSwitchMember({ | |
| required SwitchMemberImpl member, | |
| List<LabelImpl>? labels, | |
| List<StatementImpl>? statements, | |
| }) { | |
| if (member is SwitchCaseImpl) { | |
| return SwitchCaseImpl( | |
| labels: labels ?? member.labels, | |
| keyword: member.keyword, | |
| expression2: member.expression2, | |
| colon: member.colon, | |
| statements: statements ?? member.statements, | |
| ); | |
| } else if (member is SwitchDefaultImpl) { | |
| return SwitchDefaultImpl( | |
| labels: labels ?? member.labels, | |
| keyword: member.keyword, | |
| colon: member.colon, | |
| statements: statements ?? member.statements, | |
| ); | |
| } else if (member is SwitchPatternCaseImpl) { | |
| return SwitchPatternCaseImpl( | |
| labels: labels ?? member.labels, | |
| keyword: member.keyword, | |
| guardedPattern: member.guardedPattern, | |
| colon: member.colon, | |
| statements: statements ?? member.statements, | |
| ); | |
| } else { | |
| throw UnimplementedError('(${member.runtimeType}) $member'); | |
| } | |
| } | |
| if (labelCount == 0 && defaultKeyword == null) { | |
| // Common situation: case with no default and no labels. | |
| members = popTypedList2<SwitchMemberImpl>(expressionCount); | |
| } else { | |
| // Labels and case statements may be intertwined | |
| if (defaultKeyword != null) { | |
| var labels = popLabels(); | |
| var member = SwitchDefaultImpl( | |
| labels: labels, | |
| keyword: defaultKeyword, | |
| colon: colonAfterDefault!, | |
| statements: <StatementImpl>[], | |
| ); | |
| members = List.filled(expressionCount + 1, null); | |
| members[expressionCount] = member; | |
| } else { | |
| members = List.filled(expressionCount, null); | |
| } | |
| for (int index = expressionCount - 1; index >= 0; --index) { | |
| var member = pop() as SwitchMemberImpl; | |
| var labels = popLabels(); | |
| members[index] = updateSwitchMember(member: member, labels: labels); | |
| } | |
| assert(labelCount == 0); | |
| } | |
| var members2 = members.nonNulls.toList(); | |
| if (members2.isNotEmpty) { | |
| members2.last = updateSwitchMember( | |
| member: members2.last, | |
| statements: statements, | |
| ); | |
| } | |
| push(members2); | |
| } | |
| @override | |
| void endSwitchCaseWhenClause(Token token) { | |
| debugEvent("SwitchCaseWhenClause"); | |
| } | |
| @override | |
| void endSwitchExpression(Token switchKeyword, Token endToken) { | |
| assert(optional('switch', switchKeyword)); | |
| debugEvent("SwitchExpression"); | |
| var rightBracket = pop() as Token; | |
| var cases = pop() as List<SwitchExpressionCaseImpl>; | |
| var leftBracket = pop() as Token; | |
| var condition = pop() as _ParenthesizedCondition; | |
| push( | |
| SwitchExpressionImpl( | |
| switchKeyword: switchKeyword, | |
| leftParenthesis: condition.leftParenthesis, | |
| expression2: condition.expression, | |
| rightParenthesis: condition.rightParenthesis, | |
| leftBracket: leftBracket, | |
| cases: cases, | |
| rightBracket: rightBracket, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endSwitchExpressionBlock( | |
| int caseCount, | |
| Token leftBracket, | |
| Token rightBracket, | |
| ) { | |
| assert(optional('{', leftBracket)); | |
| assert(optional('}', rightBracket)); | |
| debugEvent("SwitchExpressionBlock"); | |
| var cases = popTypedList2<SwitchExpressionCaseImpl>(caseCount); | |
| push(leftBracket); | |
| push(cases); | |
| push(rightBracket); | |
| } | |
| @override | |
| void endSwitchExpressionCase( | |
| Token beginToken, | |
| Token? when, | |
| Token arrow, | |
| Token endToken, | |
| ) { | |
| debugEvent("SwitchExpressionCase"); | |
| var expression = _popExpression(); | |
| WhenClauseImpl? whenClause; | |
| if (when != null) { | |
| var expression = _popExpression(); | |
| whenClause = WhenClauseImpl(whenKeyword: when, expression2: expression); | |
| } | |
| var pattern = pop() as DartPatternImpl; | |
| push( | |
| SwitchExpressionCaseImpl( | |
| guardedPattern: GuardedPatternImpl( | |
| pattern: pattern, | |
| whenClause: whenClause, | |
| ), | |
| arrow: arrow, | |
| expression2: expression, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endSwitchStatement(Token switchKeyword, Token endToken) { | |
| assert(optional('switch', switchKeyword)); | |
| debugEvent("SwitchStatement"); | |
| var rightBracket = pop() as Token; | |
| var members = pop() as List<SwitchMemberImpl>; | |
| var leftBracket = pop() as Token; | |
| var condition = pop() as _ParenthesizedCondition; | |
| push( | |
| SwitchStatementImpl( | |
| switchKeyword: switchKeyword, | |
| leftParenthesis: condition.leftParenthesis, | |
| expression2: condition.expression, | |
| rightParenthesis: condition.rightParenthesis, | |
| leftBracket: leftBracket, | |
| members: members, | |
| rightBracket: rightBracket, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endThenStatement(Token beginToken, Token endToken) { | |
| debugEvent("endThenStatement"); | |
| } | |
| @override | |
| void endTopLevelDeclaration(Token endToken) { | |
| debugEvent("TopLevelDeclaration"); | |
| } | |
| @override | |
| void endTopLevelFields( | |
| Token? augmentToken, | |
| Token? abstractToken, | |
| Token? externalToken, | |
| Token? staticToken, | |
| Token? covariantToken, | |
| Token? lateToken, | |
| Token? varFinalOrConst, | |
| int count, | |
| Token beginToken, | |
| Token semicolon, | |
| ) { | |
| assert(optional(';', semicolon)); | |
| debugEvent("TopLevelFields"); | |
| if (externalToken != null) { | |
| if (lateToken != null) { | |
| handleRecoverableError( | |
| fe_diag.externalLateField, | |
| externalToken, | |
| externalToken, | |
| ); | |
| } | |
| } | |
| var variables = popTypedList2<VariableDeclarationImpl>(count); | |
| var type = pop() as TypeAnnotationImpl?; | |
| var variableList = VariableDeclarationListImpl( | |
| comment: null, | |
| metadata: null, | |
| lateKeyword: lateToken, | |
| keyword: varFinalOrConst, | |
| type: type, | |
| variables: variables, | |
| ); | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, beginToken); | |
| declarations.add( | |
| TopLevelVariableDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| abstractKeyword: abstractToken, | |
| augmentKeyword: augmentToken, | |
| externalKeyword: externalToken, | |
| variables: variableList, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endTopLevelMethod(Token beginToken, Token? getOrSet, Token endToken) { | |
| // TODO(paulberry): set up scopes properly to resolve parameters and type | |
| // variables. | |
| assert( | |
| getOrSet == null || | |
| optional('get', getOrSet) || | |
| optional('set', getOrSet), | |
| ); | |
| debugEvent("TopLevelMethod"); | |
| var body = pop() as FunctionBodyImpl; | |
| var formalParameters = pop() as FormalParameterListImpl?; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var name = pop() as Token; | |
| var returnType = pop() as TypeAnnotationImpl?; | |
| var modifiers = pop() as _Modifiers?; | |
| var augmentKeyword = modifiers?.augmentKeyword; | |
| var externalKeyword = modifiers?.externalKeyword; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, beginToken); | |
| if (getOrSet?.keyword == Keyword.SET) { | |
| formalParameters = _ensureSetterFormalParameter(name, formalParameters); | |
| } | |
| if (getOrSet?.keyword == Keyword.GET) { | |
| declarations.add( | |
| TopLevelGetterDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| externalKeyword: externalKeyword, | |
| returnType: returnType, | |
| getKeyword: getOrSet!, | |
| name: name, | |
| recoveryTypeParameters: typeParameters, | |
| recoveryFormalParameters: formalParameters, | |
| body: body, | |
| ), | |
| ); | |
| } else { | |
| declarations.add( | |
| FunctionDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| externalKeyword: externalKeyword, | |
| returnType: returnType, | |
| propertyKeyword: getOrSet, | |
| name: name, | |
| functionExpression: FunctionExpressionImpl( | |
| typeParameters: typeParameters, | |
| parameters: formalParameters, | |
| body: body, | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void endTryStatement( | |
| int catchCount, | |
| Token tryKeyword, | |
| Token? finallyKeyword, | |
| Token endToken, | |
| ) { | |
| assert(optional('try', tryKeyword)); | |
| assert(optionalOrNull('finally', finallyKeyword)); | |
| debugEvent("TryStatement"); | |
| var finallyBlock = popIfNotNull(finallyKeyword) as BlockImpl?; | |
| var catchClauses = popTypedList2<CatchClauseImpl>(catchCount); | |
| var body = pop() as BlockImpl; | |
| push( | |
| TryStatementImpl( | |
| tryKeyword: tryKeyword, | |
| body: body, | |
| catchClauses: catchClauses, | |
| finallyKeyword: finallyKeyword, | |
| finallyBlock: finallyBlock, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endTypeArguments(int count, Token leftBracket, Token rightBracket) { | |
| assert(optional('<', leftBracket)); | |
| assert(optional('>', rightBracket)); | |
| debugEvent("TypeArguments"); | |
| var arguments = popTypedList2<TypeAnnotationImpl>(count); | |
| push( | |
| TypeArgumentListImpl( | |
| leftBracket: leftBracket, | |
| arguments: arguments, | |
| rightBracket: rightBracket, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endTypedef( | |
| Token? augmentToken, | |
| Token typedefKeyword, | |
| Token? equals, | |
| Token semicolon, | |
| ) { | |
| assert(optional('typedef', typedefKeyword)); | |
| assert(optionalOrNull('=', equals)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("FunctionTypeAlias"); | |
| if (equals == null) { | |
| var parameters = pop() as FormalParameterListImpl; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var name = pop() as Token; | |
| var returnType = pop() as TypeAnnotationImpl?; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, typedefKeyword); | |
| declarations.add( | |
| FunctionTypeAliasImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentToken, | |
| typedefKeyword: typedefKeyword, | |
| returnType: returnType, | |
| name: name, | |
| typeParameters: typeParameters, | |
| parameters: parameters, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } else { | |
| var type = pop() as TypeAnnotationImpl; | |
| var codeParameters = pop() as TypeParameterListImpl?; | |
| var name = pop() as Token; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, typedefKeyword); | |
| if (type is! GenericFunctionTypeImpl && !enableNonFunctionTypeAliases) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.nonfunction_type_aliases, | |
| startToken: equals, | |
| ); | |
| } | |
| declarations.add( | |
| GenericTypeAliasImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentToken, | |
| typedefKeyword: typedefKeyword, | |
| name: name, | |
| typeParameters: codeParameters, | |
| equals: equals, | |
| type: type, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void endTypeList(int count) { | |
| debugEvent("TypeList"); | |
| push(popTypedList<TypeAnnotationImpl>(count) ?? NullValues.TypeList); | |
| } | |
| @override | |
| void endTypeVariable( | |
| Token token, | |
| int index, | |
| Token? extendsOrSuper, | |
| Token? variance, | |
| ) { | |
| debugEvent("TypeVariable"); | |
| assert( | |
| extendsOrSuper == null || | |
| optional('extends', extendsOrSuper) || | |
| optional('super', extendsOrSuper), | |
| ); | |
| // TODO(kallentu): Implement variance behaviour for the analyzer. | |
| assert( | |
| variance == null || | |
| optional('in', variance) || | |
| optional('out', variance) || | |
| optional('inout', variance), | |
| ); | |
| if (!enableVariance) { | |
| reportVarianceModifierNotEnabled(variance); | |
| } | |
| var bound = pop() as TypeAnnotationImpl?; | |
| // Peek to leave type parameters on top of stack. | |
| var typeParameters = peek() as List<TypeParameterImpl>; | |
| typeParameters[index] | |
| ..extendsKeyword = extendsOrSuper | |
| ..bound = bound | |
| ..varianceKeyword = variance; | |
| } | |
| @override | |
| void endTypeVariables(Token beginToken, Token endToken) { | |
| assert(optional('<', beginToken)); | |
| assert(optional('>', endToken)); | |
| debugEvent("TypeVariables"); | |
| var typeParameters = pop() as List<TypeParameterImpl>; | |
| push( | |
| TypeParameterListImpl( | |
| leftBracket: beginToken, | |
| typeParameters: typeParameters, | |
| rightBracket: endToken, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endVariableInitializer(Token equals) { | |
| assert(optionalOrNull('=', equals)); | |
| debugEvent("VariableInitializer"); | |
| var initializer = _popExpression(); | |
| var identifier = pop() as Token; | |
| reportErrorIfSuper(initializer); | |
| // TODO(ahe): Don't push initializers, instead install them. | |
| push( | |
| VariableDeclarationImpl( | |
| comment: null, | |
| metadata: [], | |
| name: identifier, | |
| equals: equals, | |
| initializer2: initializer, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endVariablesDeclaration(int count, Token? semicolon) { | |
| assert(optionalOrNull(';', semicolon)); | |
| debugEvent("VariablesDeclaration"); | |
| var variables = popTypedList2<VariableDeclarationImpl>(count); | |
| var modifiers = pop(NullValues.Modifiers) as _Modifiers?; | |
| var type = pop() as TypeAnnotationImpl?; | |
| var keyword = modifiers?.finalConstOrVarKeyword; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, variables[0].beginToken); | |
| // var comment = _findComment(metadata, | |
| // variables[0].beginToken ?? type?.beginToken ?? modifiers.beginToken); | |
| // https://github.com/dart-lang/sdk/issues/53964 | |
| if (semicolon != null && semicolon.isSynthetic) { | |
| if (variables.singleOrNull case var variable?) { | |
| if (type is NamedTypeImpl) { | |
| var importPrefix = type.importPrefix; | |
| if (importPrefix != null) { | |
| // x.^ | |
| // await y.foo(); | |
| { | |
| var awaitToken = type.name; | |
| if (awaitToken.type == Keyword.AWAIT) { | |
| push( | |
| ExpressionStatementImpl( | |
| expression2: ParsedNameAccessImpl( | |
| operand: ParsedUnqualifiedNameImpl( | |
| name: importPrefix.name, | |
| ), | |
| operator: importPrefix.period, | |
| name: parser.rewriter.insertSyntheticIdentifier( | |
| importPrefix.period, | |
| ), | |
| ), | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| parser.rewriter.insertToken(semicolon, awaitToken); | |
| parser.rewriter.insertToken(awaitToken, variable.name); | |
| return; | |
| } | |
| } | |
| // x.foo^ | |
| // await y.bar(); | |
| { | |
| var awaitToken = variable.name; | |
| if (awaitToken.type == Keyword.AWAIT || | |
| awaitToken.type == TokenType.IDENTIFIER) { | |
| // We see `x.foo await;`, where `;` is synthetic. | |
| // It is followed by `y.bar()`. | |
| // Insert a new `;`, and (unfortunately) drop `await;`. | |
| type.name.setNext(semicolon.next!); | |
| var semicolon2 = parser.rewriter.insertSyntheticToken( | |
| type.name, | |
| TokenType.SEMICOLON, | |
| ); | |
| push( | |
| ExpressionStatementImpl( | |
| expression2: ParsedNameAccessImpl( | |
| operand: ParsedUnqualifiedNameImpl( | |
| name: importPrefix.name, | |
| ), | |
| operator: importPrefix.period, | |
| name: type.name, | |
| ), | |
| semicolon: semicolon2, | |
| ), | |
| ); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| push( | |
| VariableDeclarationStatementImpl( | |
| variables: VariableDeclarationListImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| lateKeyword: modifiers?.lateToken, | |
| keyword: keyword, | |
| type: type, | |
| variables: variables, | |
| ), | |
| semicolon: semicolon ?? Tokens.semicolon(), | |
| ), | |
| ); | |
| } | |
| @override | |
| void endWhileStatement(Token whileKeyword, Token endToken) { | |
| assert(optional('while', whileKeyword)); | |
| debugEvent("WhileStatement"); | |
| var body = pop() as StatementImpl; | |
| var condition = pop() as _ParenthesizedCondition; | |
| push( | |
| WhileStatementImpl( | |
| whileKeyword: whileKeyword, | |
| leftParenthesis: condition.leftParenthesis, | |
| condition2: condition.expression, | |
| rightParenthesis: condition.rightParenthesis, | |
| body: body, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endWhileStatementBody(Token endToken) { | |
| debugEvent("endWhileStatementBody"); | |
| } | |
| @override | |
| void endYieldStatement(Token yieldToken, Token? starToken, Token semicolon) { | |
| assert(optional('yield', yieldToken)); | |
| assert(optionalOrNull('*', starToken)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("YieldStatement"); | |
| var expression = _popExpression(); | |
| push( | |
| YieldStatementImpl( | |
| yieldKeyword: yieldToken, | |
| star: starToken, | |
| expression2: expression, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleAdjacentStringLiterals(Token startToken, int literalCount) { | |
| debugEvent("AdjacentStringLiterals"); | |
| var strings = popTypedList2<StringLiteralImpl>(literalCount); | |
| push(AdjacentStringsImpl(strings: strings)); | |
| } | |
| @override | |
| void handleAsOperator(Token asOperator) { | |
| assert(optional('as', asOperator)); | |
| debugEvent("AsOperator"); | |
| var type = pop() as TypeAnnotationImpl; | |
| var expression = _popExpression(); | |
| reportErrorIfSuper(expression); | |
| push( | |
| AsExpressionImpl( | |
| expression2: expression, | |
| asOperator: asOperator, | |
| type: type, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleAssignedVariablePattern(Token variable) { | |
| debugEvent('AssignedVariablePattern'); | |
| assert(_featureSet.isEnabled(Feature.patterns)); | |
| assert(variable.lexeme != '_'); | |
| push(AssignedVariablePatternImpl(name: variable)); | |
| } | |
| @override | |
| void handleAssignmentExpression(Token token, Token endToken) { | |
| assert(token.type.isAssignmentOperator); | |
| debugEvent("AssignmentExpression"); | |
| var rhs = _popExpression(); | |
| var lhs = pop() as ExpressionImpl; | |
| var isAssignable = _isAssignable(lhs); | |
| if (!isAssignable) { | |
| // TODO(danrubel): Update the BodyBuilder to report this error. | |
| handleRecoverableError( | |
| fe_diag.missingAssignableSelector, | |
| lhs.beginToken, | |
| lhs.endToken, | |
| ); | |
| } | |
| reportErrorIfSuper(rhs); | |
| var target = _toAssignmentTarget(lhs); | |
| if (token.type == TokenType.EQ) { | |
| push(DirectAssignmentImpl(target: target, operator: token, value: rhs)); | |
| } else if (token.type == TokenType.QUESTION_QUESTION_EQ) { | |
| push(IfNullAssignmentImpl(target: target, operator: token, value: rhs)); | |
| } else { | |
| push(CompoundAssignmentImpl(target: target, operator: token, value: rhs)); | |
| } | |
| if (!enableTripleShift && token.type == TokenType.GT_GT_GT_EQ) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.triple_shift, | |
| startToken: token, | |
| ); | |
| } | |
| } | |
| @override | |
| void handleAsyncModifier(Token? asyncToken, Token? starToken) { | |
| assert( | |
| asyncToken == null || | |
| optional('async', asyncToken) || | |
| optional('sync', asyncToken), | |
| ); | |
| assert(optionalOrNull('*', starToken)); | |
| debugEvent("AsyncModifier"); | |
| push(asyncToken ?? NullValues.FunctionBodyAsyncToken); | |
| push(starToken ?? NullValues.FunctionBodyStarToken); | |
| } | |
| @override | |
| void handleBreakStatement( | |
| bool hasTarget, | |
| Token breakKeyword, | |
| Token semicolon, | |
| ) { | |
| assert(optional('break', breakKeyword)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("BreakStatement"); | |
| var labelIdentifier = hasTarget ? pop() as Token : null; | |
| var labelReference = labelIdentifier != null | |
| ? LabelReferenceImpl(name: labelIdentifier) | |
| : null; | |
| push( | |
| BreakStatementImpl( | |
| breakKeyword: breakKeyword, | |
| label: labelReference, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleCascadeAccess( | |
| Token operatorToken, | |
| Token endToken, | |
| bool isNullAware, | |
| ) { | |
| assert(optional('..', operatorToken) || optional('?..', operatorToken)); | |
| debugEvent("CascadeAccess"); | |
| doDotExpression(operatorToken); | |
| } | |
| @override | |
| void handleCascadeExpressionEnd(int sectionCount) { | |
| debugEvent("CascadeExpressionEnd"); | |
| var cascade = pop() as _CascadeBuilder; | |
| assert(cascade.sections.length == sectionCount); | |
| push( | |
| CascadeExpressionImpl( | |
| target2: cascade.target, | |
| sections: cascade.sections, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleCastPattern(Token asOperator) { | |
| assert(optional('as', asOperator)); | |
| debugEvent("CastPattern"); | |
| var type = pop() as TypeAnnotationImpl; | |
| var pattern = pop() as DartPatternImpl; | |
| push(CastPatternImpl(pattern: pattern, asToken: asOperator, type: type)); | |
| } | |
| @override | |
| void handleCatchBlock(Token? onKeyword, Token? catchKeyword, Token? comma) { | |
| assert(optionalOrNull('on', onKeyword)); | |
| assert(optionalOrNull('catch', catchKeyword)); | |
| assert(optionalOrNull(',', comma)); | |
| debugEvent("CatchBlock"); | |
| var body = pop() as BlockImpl; | |
| var catchParameterList = | |
| popIfNotNull(catchKeyword) as FormalParameterListImpl?; | |
| var type = popIfNotNull(onKeyword) as TypeAnnotationImpl?; | |
| Token? exception; | |
| Token? stackTrace; | |
| if (catchParameterList != null) { | |
| var catchParameters = catchParameterList.allFormalParameters; | |
| if (catchParameters.isNotEmpty) { | |
| exception = catchParameters[0].name; | |
| } | |
| if (catchParameters.length > 1) { | |
| stackTrace = catchParameters[1].name; | |
| } | |
| } | |
| push( | |
| CatchClauseImpl( | |
| onKeyword: onKeyword, | |
| exceptionType: type, | |
| catchKeyword: catchKeyword, | |
| leftParenthesis: catchParameterList?.leftParenthesis, | |
| exceptionParameter: exception != null | |
| ? CatchClauseParameterImpl(name: exception) | |
| : null, | |
| comma: comma, | |
| stackTraceParameter: stackTrace != null | |
| ? CatchClauseParameterImpl(name: stackTrace) | |
| : null, | |
| rightParenthesis: catchParameterList?.rightParenthesis, | |
| body: body, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleClassExtends(Token? extendsKeyword, int typeCount) { | |
| assert(extendsKeyword == null || extendsKeyword.isKeywordOrIdentifier); | |
| debugEvent("ClassExtends"); | |
| // If more extends clauses was specified (parser has already issued an | |
| // error) throw them away for now and pick the first one. | |
| while (typeCount > 1) { | |
| pop(); | |
| typeCount--; | |
| } | |
| var supertype = pop() as TypeAnnotationImpl?; | |
| if (supertype is NamedTypeImpl) { | |
| push( | |
| ExtendsClauseImpl( | |
| extendsKeyword: extendsKeyword!, | |
| superclass: supertype, | |
| ), | |
| ); | |
| } else { | |
| push(NullValues.ExtendsClause); | |
| // TODO(brianwilkerson): Consider (a) extending `ExtendsClause` to accept | |
| // any type annotation for recovery purposes, and (b) extending the | |
| // parser to parse a generic function type at this location. | |
| if (supertype != null) { | |
| diagnosticReporter.diagnosticReporter?.report( | |
| diag.expectedNamedTypeExtends.at(supertype), | |
| ); | |
| } | |
| } | |
| } | |
| @override | |
| void handleClassHeader(Token begin, Token classKeyword, Token? nativeToken) { | |
| assert(optional('class', classKeyword)); | |
| assert(optionalOrNull('native', nativeToken)); | |
| assert(_classLikeBuilder == null); | |
| debugEvent("ClassHeader"); | |
| NativeClauseImpl? nativeClause; | |
| if (nativeToken != null) { | |
| nativeClause = NativeClauseImpl( | |
| nativeKeyword: nativeToken, | |
| name: nativeName, | |
| ); | |
| } | |
| var implementsClause = | |
| pop(NullValues.IdentifierList) as ImplementsClauseImpl?; | |
| var withClause = pop(NullValues.WithClause) as WithClauseImpl?; | |
| var extendsClause = pop(NullValues.ExtendsClause) as ExtendsClauseImpl?; | |
| var primaryConstructorBuilder = | |
| pop(NullValues.PrimaryConstructor) as _PrimaryConstructorBuilder?; | |
| pop() as Token?; // constKeyword | |
| var mixinKeyword = pop(NullValues.Token) as Token?; | |
| var augmentKeyword = pop(NullValues.Token) as Token?; | |
| var finalKeyword = pop(NullValues.Token) as Token?; | |
| var interfaceKeyword = pop(NullValues.Token) as Token?; | |
| var baseKeyword = pop(NullValues.Token) as Token?; | |
| var sealedKeyword = pop(NullValues.Token) as Token?; | |
| var modifiers = pop() as _Modifiers?; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var name = pop() as Token; | |
| var abstractKeyword = modifiers?.abstractKeyword; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, begin); | |
| // leftBracket, members, and rightBracket | |
| // are set in [endClassOrMixinBody]. | |
| _classLikeBuilder = _ClassDeclarationBuilder( | |
| comment: comment, | |
| metadata: metadata, | |
| abstractKeyword: abstractKeyword, | |
| sealedKeyword: sealedKeyword, | |
| baseKeyword: baseKeyword, | |
| interfaceKeyword: interfaceKeyword, | |
| finalKeyword: finalKeyword, | |
| augmentKeyword: augmentKeyword, | |
| mixinKeyword: mixinKeyword, | |
| classKeyword: classKeyword, | |
| name: name, | |
| primaryConstructorBuilder: primaryConstructorBuilder, | |
| typeParameters: typeParameters, | |
| extendsClause: extendsClause, | |
| withClause: withClause, | |
| implementsClause: implementsClause, | |
| nativeClause: nativeClause, | |
| leftBracket: Tokens.openCurlyBracket(), | |
| rightBracket: Tokens.closeCurlyBracket(), | |
| ); | |
| } | |
| @override | |
| void handleClassNoWithClause() { | |
| push(NullValues.WithClause); | |
| } | |
| @override | |
| void handleClassWithClause(Token withKeyword) { | |
| assert(optional('with', withKeyword)); | |
| var mixinTypes = _popNamedTypeList( | |
| locatableDiagnostic: diag.expectedNamedTypeWith, | |
| ); | |
| push(WithClauseImpl(withKeyword: withKeyword, mixinTypes: mixinTypes)); | |
| } | |
| @override | |
| void handleConstFactory(Token constKeyword) { | |
| debugEvent("ConstFactory"); | |
| // TODO(kallentu): Removal of const factory error for const function feature | |
| } | |
| @override | |
| void handleContinueStatement( | |
| bool hasTarget, | |
| Token continueKeyword, | |
| Token semicolon, | |
| ) { | |
| assert(optional('continue', continueKeyword)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("ContinueStatement"); | |
| var labelIdentifier = hasTarget ? pop() as Token : null; | |
| var labelReference = labelIdentifier != null | |
| ? LabelReferenceImpl(name: labelIdentifier) | |
| : null; | |
| push( | |
| ContinueStatementImpl( | |
| continueKeyword: continueKeyword, | |
| label: labelReference, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleDeclaredVariablePattern( | |
| Token? keyword, | |
| Token variable, { | |
| required bool inAssignmentPattern, | |
| }) { | |
| debugEvent('DeclaredVariablePattern'); | |
| assert(_featureSet.isEnabled(Feature.patterns)); | |
| assert(variable.lexeme != '_'); | |
| var type = pop() as TypeAnnotationImpl?; | |
| push( | |
| DeclaredVariablePatternImpl(keyword: keyword, type: type, name: variable), | |
| ); | |
| } | |
| @override | |
| void handleDotAccess(Token operatorToken, Token endToken, bool isNullAware) { | |
| assert(optional('.', operatorToken) || optional('?.', operatorToken)); | |
| debugEvent("DotAccess"); | |
| doDotExpression(operatorToken); | |
| } | |
| @override | |
| void handleDotShorthandContext(Token token) { | |
| debugEvent("DotShorthandContext"); | |
| if (!enabledDotShorthands) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.dot_shorthands, | |
| startToken: token, | |
| ); | |
| } | |
| var expression = pop() as ExpressionImpl; | |
| push(ParsedDotShorthandExpressionImpl(expression: expression)); | |
| } | |
| @override | |
| void handleDotShorthandHead(Token periodToken) { | |
| debugEvent("DotShorthandHead"); | |
| if (!enabledDotShorthands) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.dot_shorthands, | |
| startToken: periodToken, | |
| ); | |
| } | |
| var operand = pop() as ExpressionImpl; | |
| if (operand is ParsedUnqualifiedNameImpl) { | |
| push(ParsedDotShorthandNameImpl(period: periodToken, name: operand.name)); | |
| } else if (operand is ParsedValueArgumentsImpl) { | |
| var function = operand.operand; | |
| var types = function is ParsedTypeArgumentsImpl ? function : null; | |
| var name = (types?.operand ?? function) as ParsedUnqualifiedNameImpl; | |
| var head = ParsedDotShorthandNameImpl( | |
| period: periodToken, | |
| name: name.name, | |
| ); | |
| if (types != null) { | |
| types.operand = head; | |
| } else { | |
| operand.operand = head; | |
| } | |
| push(operand); | |
| } else { | |
| push(operand); | |
| } | |
| } | |
| @override | |
| void handleDottedName(int count, Token firstIdentifier) { | |
| assert(firstIdentifier.isIdentifier); | |
| debugEvent("DottedName"); | |
| var identifiers = popTypedList2<Token>(count); | |
| var tokens = <Token>[]; | |
| if (identifiers.isNotEmpty) { | |
| // TODO(scheglov): The parser does not use [handleQualified] for | |
| // [handleDottedName], so no periods in [identifiers]. | |
| // We must walk the token stream. | |
| var t = identifiers.first; | |
| var end = identifiers.last; | |
| while (t != end) { | |
| tokens.add(t); | |
| t = t.next!; | |
| } | |
| tokens.add(end); | |
| } | |
| push(DottedNameImpl(tokens: tokens)); | |
| } | |
| @override | |
| void handleElseControlFlow(Token elseToken) { | |
| push(elseToken); | |
| } | |
| @override | |
| void handleEmptyFunctionBody(Token semicolon) { | |
| assert(optional(';', semicolon)); | |
| debugEvent("EmptyFunctionBody"); | |
| // TODO(scheglov): Change the parser to not produce these modifiers. | |
| pop(); // star | |
| pop(); // async | |
| push(EmptyFunctionBodyImpl(semicolon: semicolon)); | |
| } | |
| @override | |
| void handleEmptyStatement(Token semicolon) { | |
| assert(optional(';', semicolon)); | |
| debugEvent("EmptyStatement"); | |
| push(EmptyStatementImpl(semicolon: semicolon)); | |
| } | |
| @override | |
| void handleEnumElement(Token beginToken, Token? augmentToken) { | |
| debugEvent("EnumElement"); | |
| var argumentList = pop() as ArgumentListImpl?; | |
| var tmpConstructor = pop() as ConstructorReference2Impl?; | |
| var constant = pop() as EnumConstantDeclarationImpl; | |
| if (!enableEnhancedEnums && | |
| (argumentList != null || | |
| tmpConstructor != null && | |
| (tmpConstructor.typeReference.typeArguments != null || | |
| tmpConstructor.selector != null))) { | |
| Token token = argumentList != null | |
| ? argumentList.beginToken | |
| : tmpConstructor!.beginToken; | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.enhanced_enums, | |
| startToken: token, | |
| ); | |
| } | |
| TypeArgumentListImpl? typeArguments; | |
| ConstructorSelectorImpl? constructorSelector; | |
| if (tmpConstructor != null) { | |
| typeArguments = tmpConstructor.typeReference.typeArguments; | |
| if (tmpConstructor.selector case var selector?) { | |
| // Enum constant arguments are shared by the V1 and V2 AST views. | |
| // The temporary constructor reference is V2-only, so replace its | |
| // selector with a shared node before attaching it to the enum. | |
| constructorSelector = ConstructorSelectorImpl( | |
| period: selector.period, | |
| name2: selector.name2, | |
| ); | |
| } | |
| } | |
| // Replace the constant to include arguments. | |
| if (argumentList != null) { | |
| constant = EnumConstantDeclarationImpl( | |
| comment: constant.documentationComment, | |
| metadata: constant.metadata, | |
| augmentKeyword: augmentToken, | |
| name: constant.name, | |
| arguments: EnumConstantArgumentsImpl( | |
| typeArguments: typeArguments, | |
| constructorSelector: constructorSelector, | |
| argumentList: argumentList, | |
| ), | |
| ); | |
| } | |
| push(constant); | |
| } | |
| @override | |
| void handleEnumElements(Token elementsEndToken, int elementsCount) { | |
| debugEvent("EnumElements"); | |
| var builder = _classLikeBuilder as _EnumDeclarationBuilder; | |
| var constants = popTypedList2<EnumConstantDeclarationImpl>(elementsCount); | |
| builder.constants.addAll(constants); | |
| if (optional(';', elementsEndToken)) { | |
| builder.semicolon = elementsEndToken; | |
| } | |
| if (!enableEnhancedEnums && optional(';', elementsEndToken)) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.enhanced_enums, | |
| startToken: elementsEndToken, | |
| ); | |
| } | |
| } | |
| @override | |
| void handleEnumHeader( | |
| Token? augmentToken, | |
| Token enumKeyword, | |
| Token leftBrace, | |
| ) { | |
| assert(optional('enum', enumKeyword)); | |
| assert(optional('{', leftBrace) || optional(';', leftBrace)); | |
| debugEvent("EnumHeader"); | |
| var implementsClause = | |
| pop(NullValues.IdentifierList) as ImplementsClauseImpl?; | |
| var withClause = pop(NullValues.WithClause) as WithClauseImpl?; | |
| var primaryConstructorBuilder = | |
| pop(NullValues.PrimaryConstructor) as _PrimaryConstructorBuilder?; | |
| pop() as Token?; // constKeyword | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var name = pop() as Token; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, enumKeyword); | |
| if (!enableEnhancedEnums && | |
| (withClause != null || | |
| implementsClause != null || | |
| typeParameters != null)) { | |
| var token = withClause != null | |
| ? withClause.withKeyword | |
| : implementsClause != null | |
| ? implementsClause.implementsKeyword | |
| : typeParameters!.beginToken; | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.enhanced_enums, | |
| startToken: token, | |
| ); | |
| } | |
| _classLikeBuilder = _EnumDeclarationBuilder( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentToken, | |
| enumKeyword: enumKeyword, | |
| name: name, | |
| typeParameters: typeParameters, | |
| primaryConstructorBuilder: primaryConstructorBuilder, | |
| withClause: withClause, | |
| implementsClause: implementsClause, | |
| leftBracket: leftBrace, | |
| semicolon: null, | |
| rightBracket: leftBrace.endGroup ?? leftBrace, | |
| ); | |
| } | |
| @override | |
| void handleEnumNoWithClause() { | |
| push(NullValues.WithClause); | |
| } | |
| @override | |
| void handleEnumWithClause(Token withKeyword) { | |
| assert(optional('with', withKeyword)); | |
| var mixinTypes = _popNamedTypeList( | |
| locatableDiagnostic: diag.expectedNamedTypeWith, | |
| ); | |
| push(WithClauseImpl(withKeyword: withKeyword, mixinTypes: mixinTypes)); | |
| } | |
| @override | |
| void handleErrorToken(ErrorToken token) { | |
| translateErrorToken(token, diagnosticReporter.reportScannerError); | |
| } | |
| @override | |
| void handleExpressionFunctionBody(Token arrowToken, Token? semicolon) { | |
| assert(optional('=>', arrowToken) || optional('=', arrowToken)); | |
| assert(optionalOrNull(';', semicolon)); | |
| debugEvent("ExpressionFunctionBody"); | |
| var expression = _popExpression(); | |
| reportErrorIfSuper(expression); | |
| var star = pop() as Token?; | |
| var asyncKeyword = pop() as Token?; | |
| if (parseFunctionBodies) { | |
| push( | |
| ExpressionFunctionBodyImpl( | |
| keyword: asyncKeyword, | |
| star: star, | |
| functionDefinition: arrowToken, | |
| expression2: expression, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } else { | |
| push(EmptyFunctionBodyImpl(semicolon: semicolon!)); | |
| } | |
| } | |
| @override | |
| void handleExpressionStatement(Token beginToken, Token semicolon) { | |
| assert(optional(';', semicolon)); | |
| debugEvent("ExpressionStatement"); | |
| var expression = pop() as ExpressionImpl; | |
| reportErrorIfSuper(expression); | |
| if (expression is ParsedUnqualifiedNameImpl && | |
| expression.name.keyword?.isBuiltInOrPseudo == false) { | |
| // This error is also reported by the body builder. | |
| handleRecoverableError( | |
| fe_diag.expectedStatement, | |
| expression.beginToken, | |
| expression.endToken, | |
| ); | |
| } | |
| var invalidAssignmentLeft = switch (expression) { | |
| DirectAssignmentImpl( | |
| target: InvalidSuperAssignmentTargetImpl(:var superReference), | |
| ) || | |
| IfNullAssignmentImpl( | |
| target: InvalidSuperAssignmentTargetImpl(:var superReference), | |
| ) => superReference, | |
| CompoundAssignmentImpl( | |
| target: InvalidExpressionAssignmentTargetImpl(:var expression), | |
| ) => | |
| expression, | |
| DirectAssignmentImpl( | |
| target: InvalidExpressionAssignmentTargetImpl(:var expression), | |
| ) => | |
| expression, | |
| IfNullAssignmentImpl( | |
| target: InvalidExpressionAssignmentTargetImpl(:var expression), | |
| ) => | |
| expression, | |
| _ => null, | |
| }; | |
| if (invalidAssignmentLeft != null) { | |
| // This error is also reported by the body builder. | |
| handleRecoverableError( | |
| fe_diag.illegalAssignmentToNonAssignable, | |
| invalidAssignmentLeft.beginToken, | |
| invalidAssignmentLeft.endToken, | |
| ); | |
| } | |
| push( | |
| ExpressionStatementImpl(expression2: expression, semicolon: semicolon), | |
| ); | |
| } | |
| @override | |
| void handleFinallyBlock(Token finallyKeyword) { | |
| debugEvent("FinallyBlock"); | |
| // The finally block is popped in "endTryStatement". | |
| } | |
| @override | |
| void handleForInitializerEmptyStatement(Token token) { | |
| debugEvent("ForInitializerEmptyStatement"); | |
| push(NullValues.Expression); | |
| } | |
| @override | |
| void handleForInitializerExpressionStatement(Token token, bool forIn) { | |
| debugEvent("ForInitializerExpressionStatement"); | |
| } | |
| @override | |
| void handleForInitializerLocalVariableDeclaration(Token token, bool forIn) { | |
| debugEvent("ForInitializerLocalVariableDeclaration"); | |
| } | |
| @override | |
| void handleForInitializerPatternVariableAssignment( | |
| Token keyword, | |
| Token equals, | |
| ) { | |
| var expression = _popExpression(); | |
| var pattern = pop() as DartPatternImpl; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| push( | |
| PatternVariableDeclarationImpl( | |
| keyword: keyword, | |
| pattern: pattern, | |
| equals: equals, | |
| expression2: expression, | |
| comment: null, | |
| metadata: metadata, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleForInLoopParts( | |
| Token? awaitToken, | |
| Token forToken, | |
| Token leftParenthesis, | |
| Token? patternKeyword, | |
| Token inKeyword, | |
| ) { | |
| assert(optionalOrNull('await', awaitToken)); | |
| assert(optional('for', forToken)); | |
| assert(optional('(', leftParenthesis)); | |
| assert(optional('in', inKeyword) || optional(':', inKeyword)); | |
| var iterable = _popExpression(); | |
| var variableOrDeclaration = pop()!; | |
| reportErrorIfSuper(iterable); | |
| ForEachPartsImpl forLoopParts; | |
| if (patternKeyword != null) { | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| forLoopParts = ForEachPartsWithPatternImpl( | |
| metadata: metadata ?? [], | |
| keyword: patternKeyword, | |
| pattern: variableOrDeclaration as DartPatternImpl, | |
| inKeyword: inKeyword, | |
| iterable2: iterable, | |
| ); | |
| } else if (variableOrDeclaration is VariableDeclarationStatementImpl) { | |
| var variableList = variableOrDeclaration.variables; | |
| forLoopParts = ForEachPartsWithDeclarationImpl( | |
| loopVariable: DeclaredIdentifierImpl( | |
| comment: variableList.documentationComment, | |
| metadata: variableList.metadata, | |
| keyword: variableList.keyword, | |
| type: variableList.type, | |
| name: variableList.variables.first.name, | |
| ), | |
| inKeyword: inKeyword, | |
| iterable2: iterable, | |
| ); | |
| } else { | |
| Token name; | |
| if (variableOrDeclaration is ParsedUnqualifiedNameImpl) { | |
| name = variableOrDeclaration.name; | |
| } else { | |
| // Parser has already reported the error. | |
| if (!leftParenthesis.next!.isIdentifier) { | |
| parser.rewriter.insertSyntheticIdentifier(leftParenthesis); | |
| } | |
| name = leftParenthesis.next!; | |
| } | |
| forLoopParts = ForEachPartsWithIdentifierImpl( | |
| target: UnqualifiedNameAssignmentTargetImpl(name: name), | |
| inKeyword: inKeyword, | |
| iterable2: iterable, | |
| ); | |
| } | |
| push(awaitToken ?? NullValues.AwaitToken); | |
| push(forToken); | |
| push(leftParenthesis); | |
| push(forLoopParts); | |
| } | |
| @override | |
| void handleForLoopParts( | |
| Token forKeyword, | |
| Token leftParen, | |
| Token leftSeparator, | |
| Token rightSeparator, | |
| int updateExpressionCount, | |
| ) { | |
| assert(optional('for', forKeyword)); | |
| assert(optional('(', leftParen)); | |
| assert(optional(';', leftSeparator)); | |
| assert(updateExpressionCount >= 0); | |
| var updates = popTypedList2<ExpressionImpl>(updateExpressionCount); | |
| var conditionStatement = pop() as StatementImpl; | |
| var initializerPart = pop(); | |
| for (var update in updates) { | |
| reportErrorIfSuper(update); | |
| } | |
| ExpressionImpl? condition; | |
| Token rightSeparator; | |
| if (conditionStatement is ExpressionStatementImpl) { | |
| condition = conditionStatement.expression2; | |
| rightSeparator = conditionStatement.semicolon!; | |
| } else { | |
| rightSeparator = (conditionStatement as EmptyStatementImpl).semicolon; | |
| } | |
| ForPartsImpl forLoopParts; | |
| if (initializerPart is VariableDeclarationStatementImpl) { | |
| forLoopParts = ForPartsWithDeclarationsImpl( | |
| variables: initializerPart.variables, | |
| leftSeparator: leftSeparator, | |
| condition2: condition, | |
| rightSeparator: rightSeparator, | |
| updaters2: updates, | |
| ); | |
| } else if (initializerPart is PatternVariableDeclarationImpl) { | |
| forLoopParts = ForPartsWithPatternImpl( | |
| variables: initializerPart, | |
| leftSeparator: leftSeparator, | |
| condition2: condition, | |
| rightSeparator: rightSeparator, | |
| updaters2: updates, | |
| ); | |
| } else { | |
| forLoopParts = ForPartsWithExpressionImpl( | |
| initialization2: initializerPart as ExpressionImpl?, | |
| leftSeparator: leftSeparator, | |
| condition2: condition, | |
| rightSeparator: rightSeparator, | |
| updaters2: updates, | |
| ); | |
| } | |
| push(forKeyword); | |
| push(leftParen); | |
| push(forLoopParts); | |
| } | |
| @override | |
| void handleFormalParameterWithoutValue(Token token) { | |
| debugEvent("FormalParameterWithoutValue"); | |
| push(NullValues.ParameterDefaultValue); | |
| } | |
| @override | |
| void handleIdentifier(Token token, IdentifierContext context) { | |
| assert(token.isKeywordOrIdentifier); | |
| debugEvent("handleIdentifier"); | |
| if (context.inSymbol || | |
| context == IdentifierContext.dottedName || | |
| context == IdentifierContext.dottedNameContinuation || | |
| context == IdentifierContext.labelDeclaration || | |
| context == IdentifierContext.labelReference) { | |
| push(token); | |
| return; | |
| } | |
| if (context.inLibraryOrPartOfDeclaration) { | |
| if (!context.isContinuation) { | |
| push([token]); | |
| } else { | |
| push(token); | |
| } | |
| } else if (context == IdentifierContext.enumValueDeclaration) { | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, token); | |
| Token? augmentKeyword; | |
| if (token.previous case var previous?) { | |
| if (optional('augment', previous)) { | |
| augmentKeyword = previous; | |
| } | |
| } | |
| push( | |
| EnumConstantDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| name: token, | |
| arguments: null, | |
| ), | |
| ); | |
| } else if (context == IdentifierContext.expression || | |
| context == IdentifierContext.expressionContinuation) { | |
| push(ParsedUnqualifiedNameImpl(name: token)); | |
| } else { | |
| push(token); | |
| } | |
| } | |
| @override | |
| void handleIdentifierList(int count) { | |
| debugEvent("IdentifierList"); | |
| push(popTypedList<Token>(count) ?? NullValues.IdentifierList); | |
| } | |
| @override | |
| void handleImplements(Token? implementsKeyword, int interfacesCount) { | |
| assert(optionalOrNull('implements', implementsKeyword)); | |
| debugEvent("Implements"); | |
| if (implementsKeyword != null) { | |
| endTypeList(interfacesCount); | |
| var interfaces = _popNamedTypeList( | |
| locatableDiagnostic: diag.expectedNamedTypeImplements, | |
| ); | |
| push( | |
| ImplementsClauseImpl( | |
| implementsKeyword: implementsKeyword, | |
| interfaces: interfaces, | |
| ), | |
| ); | |
| } else { | |
| push(NullValues.IdentifierList); | |
| } | |
| } | |
| @override | |
| void handleImportPrefix(Token? deferredKeyword, Token? asKeyword) { | |
| assert(optionalOrNull('deferred', deferredKeyword)); | |
| assert(optionalOrNull('as', asKeyword)); | |
| debugEvent("ImportPrefix"); | |
| if (asKeyword == null) { | |
| // If asKeyword is null, then no prefix has been pushed on the stack. | |
| // Push a placeholder indicating that there is no prefix. | |
| push(NullValues.Prefix); | |
| push(NullValues.As); | |
| } else { | |
| push(asKeyword); | |
| } | |
| push(deferredKeyword ?? NullValues.Deferred); | |
| } | |
| @override | |
| void handleIndexedExpression( | |
| Token? question, | |
| Token leftBracket, | |
| Token rightBracket, | |
| ) { | |
| assert(optional('[', leftBracket) || optional('?.[', leftBracket)); | |
| assert(optional(']', rightBracket)); | |
| debugEvent("IndexedExpression"); | |
| var index = _popExpression(); | |
| var target = pop() as ExpressionImpl?; | |
| reportErrorIfSuper(index); | |
| if (target == null) { | |
| var receiver = pop() as _CascadeBuilder; | |
| push(receiver); | |
| var expression = CascadeIndexExpressionImpl( | |
| leftBracket: leftBracket, | |
| index: index, | |
| rightBracket: rightBracket, | |
| ); | |
| push(expression); | |
| } else { | |
| push( | |
| ReceiverIndexExpressionImpl( | |
| receiver: _toInstanceReceiver(target), | |
| question: question, | |
| leftBracket: leftBracket, | |
| index: index, | |
| rightBracket: rightBracket, | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void handleInterpolationExpression(Token leftBracket, Token? rightBracket) { | |
| var expression = _popExpression(); | |
| push( | |
| InterpolationExpressionImpl( | |
| leftBracket: leftBracket, | |
| expression2: expression, | |
| rightBracket: rightBracket, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleInvalidExpression(Token token) { | |
| debugEvent("InvalidExpression"); | |
| } | |
| @override | |
| void handleInvalidFunctionBody(Token leftBracket) { | |
| assert(optional('{', leftBracket)); | |
| assert(optional('}', leftBracket.endGroup!)); | |
| debugEvent("InvalidFunctionBody"); | |
| var block = BlockImpl( | |
| leftBracket: leftBracket, | |
| statements: [], | |
| rightBracket: leftBracket.endGroup!, | |
| ); | |
| var star = pop() as Token?; | |
| var asyncKeyword = pop() as Token?; | |
| push( | |
| BlockFunctionBodyImpl(keyword: asyncKeyword, star: star, block: block), | |
| ); | |
| } | |
| @override | |
| void handleInvalidMember(Token endToken) { | |
| debugEvent("InvalidMember"); | |
| pop(); // metadata star | |
| } | |
| @override | |
| void handleInvalidOperatorName(Token operatorKeyword, Token token) { | |
| assert(optional('operator', operatorKeyword)); | |
| debugEvent("InvalidOperatorName"); | |
| push(_OperatorName(operatorKeyword, token)); | |
| } | |
| @override | |
| void handleInvalidTopLevelBlock(Token token) { | |
| // TODO(danrubel): Consider improved recovery by adding this block | |
| // as part of a synthetic top level function. | |
| pop(); // block | |
| } | |
| @override | |
| void handleInvalidTopLevelDeclaration(Token endToken) { | |
| debugEvent("InvalidTopLevelDeclaration"); | |
| pop(); // metadata star | |
| // TODO(danrubel): consider creating a AST node | |
| // representing the invalid declaration to better support code completion, | |
| // quick fixes, etc, rather than discarding the metadata and token | |
| } | |
| @override | |
| void handleInvalidTypeArguments(Token token) { | |
| var invalidTypeArgs = pop() as TypeArgumentListImpl; | |
| var node = pop(); | |
| if (node is ConstructorReference2Impl) { | |
| push(_ConstructorReferenceWithInvalidTypeArgs(node, invalidTypeArgs)); | |
| } else { | |
| throw UnimplementedError( | |
| 'node is an instance of ${node.runtimeType} in handleInvalidTypeArguments', | |
| ); | |
| } | |
| } | |
| @override | |
| void handleInvocationWithoutTypeArguments(Token beginToken, Token endToken) { | |
| var argumentList = pop() as ArgumentListImpl; | |
| doInvocation(null, argumentList); | |
| } | |
| @override | |
| void handleIsOperator(Token isOperator, Token? not) { | |
| assert(optional('is', isOperator)); | |
| assert(optionalOrNull('!', not)); | |
| debugEvent("IsOperator"); | |
| var type = pop() as TypeAnnotationImpl; | |
| var expression = _popExpression(); | |
| reportErrorIfSuper(expression); | |
| push( | |
| IsExpressionImpl( | |
| expression2: expression, | |
| isOperator: isOperator, | |
| notOperator: not, | |
| type: type, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLabel(Token colon) { | |
| assert(optionalOrNull(':', colon)); | |
| debugEvent("Label"); | |
| var name = pop() as Token; | |
| push(LabelImpl(name: name, colon: colon)); | |
| } | |
| @override | |
| void handleListPattern(int count, Token leftBracket, Token rightBracket) { | |
| assert(optional('[', leftBracket)); | |
| assert(optional(']', rightBracket)); | |
| debugEvent("ListPattern"); | |
| var elements = popTypedList2<ListPatternElementImpl>(count); | |
| var typeArguments = pop() as TypeArgumentListImpl?; | |
| push( | |
| ListPatternImpl( | |
| typeArguments: typeArguments, | |
| leftBracket: leftBracket, | |
| elements: elements, | |
| rightBracket: rightBracket, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLiteralBool(Token token) { | |
| bool value = boolFromToken(token); | |
| debugEvent("LiteralBool"); | |
| push(BooleanLiteralImpl(literal: token, value: value)); | |
| } | |
| @override | |
| void handleLiteralDouble(Token token) { | |
| assert(token.type == TokenType.DOUBLE); | |
| debugEvent("LiteralDouble"); | |
| push(DoubleLiteralImpl(literal: token, value: double.parse(token.lexeme))); | |
| } | |
| @override | |
| void handleLiteralDoubleWithSeparators(Token token) { | |
| assert(token.type == TokenType.DOUBLE_WITH_SEPARATORS); | |
| debugEvent("LiteralDouble"); | |
| if (!_enableDigitSeparators) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.digit_separators, | |
| startToken: token, | |
| ); | |
| } | |
| push( | |
| DoubleLiteralImpl( | |
| literal: token, | |
| value: doubleFromToken(token, hasSeparators: true), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLiteralInt(Token token) { | |
| assert( | |
| identical(token.kind, INT_TOKEN) || | |
| identical(token.kind, HEXADECIMAL_TOKEN), | |
| ); | |
| debugEvent("LiteralInt"); | |
| push( | |
| IntegerLiteralImpl( | |
| literal: token, | |
| value: intFromToken(token, hasSeparators: false), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLiteralIntWithSeparators(Token token) { | |
| assert( | |
| identical(token.kind, INT_TOKEN) || | |
| identical(token.kind, HEXADECIMAL_TOKEN), | |
| ); | |
| debugEvent("LiteralInt"); | |
| if (!_enableDigitSeparators) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.digit_separators, | |
| startToken: token, | |
| ); | |
| } | |
| push( | |
| IntegerLiteralImpl( | |
| literal: token, | |
| value: intFromToken(token, hasSeparators: true), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLiteralList( | |
| int count, | |
| Token leftBracket, | |
| Token? constKeyword, | |
| Token rightBracket, | |
| ) { | |
| assert(optional('[', leftBracket)); | |
| assert(optionalOrNull('const', constKeyword)); | |
| assert(optional(']', rightBracket)); | |
| debugEvent("LiteralList"); | |
| var elements = popCollectionElements(count); | |
| var typeArguments = pop() as TypeArgumentListImpl?; | |
| push( | |
| ListLiteralImpl( | |
| constKeyword: constKeyword, | |
| typeArguments: typeArguments, | |
| leftBracket: leftBracket, | |
| elements2: elements, | |
| rightBracket: rightBracket, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLiteralMapEntry( | |
| Token colon, | |
| Token endToken, { | |
| Token? nullAwareKeyToken, | |
| Token? nullAwareValueToken, | |
| }) { | |
| assert(optional(':', colon)); | |
| debugEvent("LiteralMapEntry"); | |
| if (!enableNullAwareElements && | |
| (nullAwareKeyToken != null || nullAwareValueToken != null)) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.null_aware_elements, | |
| startToken: nullAwareKeyToken ?? nullAwareValueToken!, | |
| ); | |
| nullAwareKeyToken = null; | |
| nullAwareValueToken = null; | |
| } | |
| var value = _popExpression(); | |
| var key = _popExpression(); | |
| push( | |
| MapLiteralEntryImpl( | |
| keyQuestion: nullAwareKeyToken, | |
| key2: key, | |
| separator: colon, | |
| valueQuestion: nullAwareValueToken, | |
| value2: value, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLiteralNull(Token token) { | |
| assert(optional('null', token)); | |
| debugEvent("LiteralNull"); | |
| push(NullLiteralImpl(literal: token)); | |
| } | |
| @override | |
| void handleLiteralSetOrMap( | |
| int count, | |
| Token leftBrace, | |
| Token? constKeyword, | |
| Token rightBrace, | |
| // TODO(danrubel): hasSetEntry parameter exists for replicating existing | |
| // behavior and will be removed once unified collection has been enabled | |
| bool hasSetEntry, | |
| ) { | |
| var elements = popCollectionElements(count); | |
| var typeArguments = pop() as TypeArgumentListImpl?; | |
| push( | |
| SetOrMapLiteralImpl( | |
| constKeyword: constKeyword, | |
| typeArguments: typeArguments, | |
| leftBracket: leftBrace, | |
| elements2: elements, | |
| rightBracket: rightBrace, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleMapPattern(int count, Token leftBrace, Token rightBrace) { | |
| debugEvent('MapPattern'); | |
| var elements = popTypedList2<MapPatternElementImpl>(count); | |
| var typeArguments = pop() as TypeArgumentListImpl?; | |
| push( | |
| MapPatternImpl( | |
| typeArguments: typeArguments, | |
| leftBracket: leftBrace, | |
| elements: elements, | |
| rightBracket: rightBrace, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleMapPatternEntry(Token colon, Token endToken) { | |
| assert(optional(':', colon)); | |
| debugEvent("MapPatternEntry"); | |
| var value = pop() as DartPatternImpl; | |
| var key = _popExpression(); | |
| push(MapPatternEntryImpl(key2: key, separator: colon, value: value)); | |
| } | |
| @override | |
| void handleMixinHeader(Token mixinKeyword) { | |
| assert(optional('mixin', mixinKeyword)); | |
| assert(_classLikeBuilder == null); | |
| debugEvent("MixinHeader"); | |
| var implementsClause = | |
| pop(NullValues.IdentifierList) as ImplementsClauseImpl?; | |
| var onClause = pop(NullValues.IdentifierList) as MixinOnClauseImpl?; | |
| pop(); // Error recovery: Primary constructor. | |
| pop(); // Error recovery: Const token. | |
| var baseKeyword = pop(NullValues.Token) as Token?; | |
| var augmentKeyword = pop(NullValues.Token) as Token?; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var name = pop() as Token; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var begin = baseKeyword ?? mixinKeyword; | |
| var comment = _findComment(metadata, begin); | |
| _classLikeBuilder = _MixinDeclarationBuilder( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| baseKeyword: baseKeyword, | |
| mixinKeyword: mixinKeyword, | |
| name: name, | |
| typeParameters: typeParameters, | |
| onClause: onClause, | |
| implementsClause: implementsClause, | |
| leftBracket: Tokens.openCurlyBracket(), | |
| rightBracket: Tokens.closeCurlyBracket(), | |
| ); | |
| } | |
| @override | |
| void handleMixinOn(Token? onKeyword, int typeCount) { | |
| assert(onKeyword == null || onKeyword.isKeywordOrIdentifier); | |
| debugEvent("MixinOn"); | |
| if (onKeyword != null) { | |
| endTypeList(typeCount); | |
| var onTypes = _popNamedTypeList( | |
| locatableDiagnostic: diag.expectedNamedTypeOn, | |
| ); | |
| push( | |
| MixinOnClauseImpl(onKeyword: onKeyword, superclassConstraints: onTypes), | |
| ); | |
| } else { | |
| push(NullValues.IdentifierList); | |
| } | |
| } | |
| @override | |
| void handleMixinWithClause(Token withKeyword) { | |
| assert(optional('with', withKeyword)); | |
| // This is an error case. An error has been issued already. | |
| // Possibly the data could be used for help though. | |
| _popNamedTypeList(locatableDiagnostic: diag.expectedNamedTypeWith); | |
| } | |
| @override | |
| void handleNamedArgument(Token colon) { | |
| assert(optional(':', colon)); | |
| debugEvent("NamedArgument"); | |
| var expression = _popExpression(); | |
| var name = pop() as Token; | |
| push( | |
| NamedArgumentImpl( | |
| name: name, | |
| colon: colon, | |
| argumentExpression2: expression, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleNamedMixinApplicationWithClause(Token withKeyword) { | |
| assert(optionalOrNull('with', withKeyword)); | |
| var mixinTypes = _popNamedTypeList( | |
| locatableDiagnostic: diag.expectedNamedTypeWith, | |
| ); | |
| push(WithClauseImpl(withKeyword: withKeyword, mixinTypes: mixinTypes)); | |
| } | |
| @override | |
| void handleNamedRecordField(Token colon) { | |
| debugEvent("NamedRecordField"); | |
| var expression = _popExpression(); | |
| var name = pop() as Token; | |
| push( | |
| RecordLiteralNamedFieldImpl( | |
| name: name, | |
| colon: colon, | |
| fieldExpression2: expression, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleNativeClause(Token nativeToken, bool hasName) { | |
| debugEvent("NativeClause"); | |
| if (hasName) { | |
| nativeName = pop() as StringLiteralImpl; | |
| } else { | |
| nativeName = null; | |
| } | |
| } | |
| @override | |
| void handleNativeFunctionBody(Token nativeToken, Token semicolon) { | |
| assert(optional('native', nativeToken)); | |
| assert(optional(';', semicolon)); | |
| debugEvent("NativeFunctionBody"); | |
| // TODO(danrubel): Change the parser to not produce these modifiers. | |
| pop(); // star | |
| pop(); // async | |
| push( | |
| NativeFunctionBodyImpl( | |
| nativeKeyword: nativeToken, | |
| stringLiteral: nativeName, | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleNewAsIdentifier(Token token) { | |
| if (!enableConstructorTearoffs) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.constructor_tearoffs, | |
| startToken: token, | |
| ); | |
| } | |
| } | |
| @override | |
| void handleNoClassBody(Token semicolonToken) { | |
| debugEvent("NoClassBody"); | |
| var builder = _classLikeBuilder; | |
| if (builder != null) { | |
| builder.emptyClassBodySemicolon = semicolonToken; | |
| } | |
| } | |
| @override | |
| void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { | |
| debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); | |
| push(NullValues.ConstructorReferenceContinuationAfterTypeArguments); | |
| } | |
| @override | |
| void handleNoEnumBody(Token semicolonToken) { | |
| debugEvent("NoEnumBody"); | |
| var builder = _classLikeBuilder; | |
| if (builder != null) { | |
| builder.emptyClassBodySemicolon = semicolonToken; | |
| } | |
| } | |
| @override | |
| void handleNoExtensionBody(Token semicolonToken) { | |
| debugEvent("NoExtensionBody"); | |
| var builder = _classLikeBuilder; | |
| if (builder != null) { | |
| builder.emptyClassBodySemicolon = semicolonToken; | |
| } | |
| } | |
| @override | |
| void handleNoExtensionTypeBody(Token semicolonToken) { | |
| debugEvent("NoExtensionTypeBody"); | |
| var builder = _classLikeBuilder; | |
| if (builder != null) { | |
| builder.emptyClassBodySemicolon = semicolonToken; | |
| } | |
| } | |
| @override | |
| void handleNoFieldInitializer(Token token) { | |
| debugEvent("NoFieldInitializer"); | |
| var name = pop() as Token; | |
| push( | |
| VariableDeclarationImpl( | |
| comment: null, | |
| metadata: [], | |
| name: name, | |
| equals: null, | |
| initializer2: null, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleNoIdentifier(Token token, IdentifierContext context) { | |
| debugEvent("handleNoIdentifier"); | |
| push(NullValues.Identifier); | |
| } | |
| @override | |
| void handleNoInitializers() { | |
| debugEvent("NoInitializers"); | |
| if (!isFullAst) return; | |
| push(NullValues.ConstructorInitializerSeparator); | |
| push(NullValues.ConstructorInitializers); | |
| } | |
| @override | |
| void handleNoMixinBody(Token semicolonToken) { | |
| debugEvent("NoMixinBody"); | |
| var builder = _classLikeBuilder; | |
| if (builder != null) { | |
| builder.emptyClassBodySemicolon = semicolonToken; | |
| } | |
| } | |
| @override | |
| void handleNonNullAssertExpression(Token bang) { | |
| debugEvent('NonNullAssertExpression'); | |
| push( | |
| NullAssertionExpressionImpl(operand: _popExpression(), operator: bang), | |
| ); | |
| } | |
| @override | |
| void handleNoPrimaryConstructor( | |
| DeclarationKind kind, | |
| Token token, | |
| Token? constKeyword, | |
| ) { | |
| push(constKeyword ?? const NullValue("Token")); | |
| push(NullValues.PrimaryConstructor); | |
| } | |
| @override | |
| void handleNoTypeNameInConstructorReference(Token token) { | |
| debugEvent("NoTypeNameInConstructorReference"); | |
| var builder = _classLikeBuilder as _EnumDeclarationBuilder; | |
| push(builder.name); | |
| } | |
| @override | |
| void handleNoVariableInitializer(Token token) { | |
| debugEvent("NoVariableInitializer"); | |
| } | |
| @override | |
| void handleNullAssertPattern(Token bang) { | |
| debugEvent("NullAssertPattern"); | |
| push( | |
| NullAssertPatternImpl(pattern: pop() as DartPatternImpl, operator: bang), | |
| ); | |
| } | |
| @override | |
| void handleNullAwareElement(Token nullAwareElement) { | |
| debugEvent('NullAwareElement'); | |
| if (!enableNullAwareElements) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.null_aware_elements, | |
| startToken: nullAwareElement, | |
| ); | |
| } else { | |
| var expression = _popExpression(); | |
| push( | |
| NullAwareElementImpl(question: nullAwareElement, value2: expression), | |
| ); | |
| } | |
| } | |
| @override | |
| void handleNullCheckPattern(Token question) { | |
| debugEvent('NullCheckPattern'); | |
| if (!_featureSet.isEnabled(Feature.patterns)) { | |
| // TODO(paulberry): report the appropriate error | |
| throw UnimplementedError('Patterns not enabled'); | |
| } | |
| push( | |
| NullCheckPatternImpl( | |
| pattern: pop() as DartPatternImpl, | |
| operator: question, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleObjectPattern( | |
| Token firstIdentifierToken, | |
| Token? dot, | |
| Token? secondIdentifierToken, | |
| ) { | |
| debugEvent("ExtractorPattern"); | |
| var arguments = pop() as _ObjectPatternFields; | |
| var typeArguments = pop() as TypeArgumentListImpl?; | |
| NamedTypeImpl namedType; | |
| if (dot != null && secondIdentifierToken != null) { | |
| namedType = NamedTypeImpl( | |
| importPrefix: ImportPrefixReferenceImpl( | |
| name: firstIdentifierToken, | |
| period: dot, | |
| ), | |
| name: secondIdentifierToken, | |
| typeArguments: typeArguments, | |
| question: null, | |
| ); | |
| } else { | |
| namedType = NamedTypeImpl( | |
| importPrefix: null, | |
| name: firstIdentifierToken, | |
| typeArguments: typeArguments, | |
| question: null, | |
| ); | |
| } | |
| push( | |
| ObjectPatternImpl( | |
| type: namedType, | |
| leftParenthesis: arguments.leftParenthesis, | |
| fields: arguments.fields, | |
| rightParenthesis: arguments.rightParenthesis, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleObjectPatternFields(int count, Token beginToken, Token endToken) { | |
| debugEvent("ExtractorPatternFields"); | |
| var fields = popTypedList2<PatternFieldImpl>(count); | |
| push(_ObjectPatternFields(beginToken, endToken, fields)); | |
| } | |
| @override | |
| void handleOperator(Token operatorToken) { | |
| assert(operatorToken.isUserDefinableOperator); | |
| debugEvent("Operator"); | |
| push(operatorToken); | |
| } | |
| @override | |
| void handleOperatorName(Token operatorKeyword, Token token) { | |
| assert(optional('operator', operatorKeyword)); | |
| assert(token.type.isUserDefinableOperator); | |
| debugEvent("OperatorName"); | |
| push(_OperatorName(operatorKeyword, token)); | |
| } | |
| @override | |
| void handleParenthesizedCondition( | |
| Token leftParenthesis, | |
| Token? case_, | |
| Token? when, | |
| ) { | |
| ExpressionImpl condition; | |
| CaseClauseImpl? caseClause; | |
| if (case_ != null) { | |
| var whenClause = when != null ? pop() as WhenClauseImpl : null; | |
| var pattern = pop() as DartPatternImpl; | |
| caseClause = CaseClauseImpl( | |
| caseKeyword: case_, | |
| guardedPattern: GuardedPatternImpl( | |
| pattern: pattern, | |
| whenClause: whenClause, | |
| ), | |
| ); | |
| } | |
| condition = _popExpression(); | |
| reportErrorIfSuper(condition); | |
| push(_ParenthesizedCondition(leftParenthesis, condition, caseClause)); | |
| } | |
| @override | |
| void handleParenthesizedPattern(Token leftParenthesis) { | |
| assert(optional('(', leftParenthesis)); | |
| debugEvent("ParenthesizedPattern"); | |
| var pattern = pop() as DartPatternImpl; | |
| push( | |
| ParenthesizedPatternImpl( | |
| leftParenthesis: leftParenthesis, | |
| pattern: pattern, | |
| rightParenthesis: leftParenthesis.endGroup!, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handlePatternAssignment(Token equals) { | |
| var expression = _popExpression(); | |
| var pattern = pop() as DartPatternImpl; | |
| push( | |
| PatternAssignmentImpl( | |
| pattern: pattern, | |
| equals: equals, | |
| expression2: expression, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handlePatternField(Token? colon) { | |
| debugEvent("PatternField"); | |
| var pattern = pop() as DartPatternImpl; | |
| PatternFieldNameImpl? fieldName; | |
| if (colon != null) { | |
| var name = pop() as Token?; | |
| fieldName = PatternFieldNameImpl(name: name, colon: colon); | |
| } | |
| push(PatternFieldImpl(name: fieldName, pattern: pattern)); | |
| } | |
| @override | |
| void handlePatternVariableDeclarationStatement( | |
| Token keyword, | |
| Token equals, | |
| Token semicolon, | |
| ) { | |
| var expression = _popExpression(); | |
| var pattern = pop() as DartPatternImpl; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, keyword); | |
| push( | |
| PatternVariableDeclarationStatementImpl( | |
| declaration: PatternVariableDeclarationImpl( | |
| keyword: keyword, | |
| pattern: pattern, | |
| equals: equals, | |
| expression2: expression, | |
| comment: comment, | |
| metadata: metadata, | |
| ), | |
| semicolon: semicolon, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleQualified(Token period) { | |
| assert(optional('.', period)); | |
| var identifier = pop(); | |
| var prefix = pop(); | |
| if (prefix is List) { | |
| // We're just accumulating components into a list. | |
| prefix.add(period); | |
| prefix.add(identifier); | |
| push(prefix); | |
| } else if (prefix is Token) { | |
| push(_QualifiedName(prefix, period, identifier as Token)); | |
| } else { | |
| // TODO(paulberry): implement. | |
| logEvent('Qualified with >1 dot'); | |
| } | |
| } | |
| @override | |
| void handleRecordPattern(Token token, int count) { | |
| debugEvent("RecordPattern"); | |
| var fields = popTypedList2<PatternFieldImpl>(count); | |
| push( | |
| RecordPatternImpl( | |
| leftParenthesis: token, | |
| fields: fields, | |
| rightParenthesis: token.endGroup!, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleRecoverableError( | |
| Message message, | |
| Token startToken, | |
| Token endToken, | |
| ) { | |
| // TODO(danrubel): Ignore this error until we deprecate `native` support. | |
| if (message == fe_diag.nativeClauseShouldBeAnnotation && | |
| allowNativeClause) { | |
| return; | |
| } else if (message.code == fe_diag.builtInIdentifierInDeclaration) { | |
| // Allow e.g. 'class Function' in sdk. | |
| if (isDartLibrary) return; | |
| } | |
| debugEvent("Error: ${message.problemMessage}"); | |
| if (message.code.pseudoSharedCode == null && startToken is ErrorToken) { | |
| translateErrorToken(startToken, diagnosticReporter.reportScannerError); | |
| } else { | |
| int offset = startToken.offset; | |
| int length = endToken.end - offset; | |
| addProblem(message, offset, length); | |
| } | |
| } | |
| @override | |
| void handleRecoverDeclarationHeader(DeclarationHeaderKind kind) { | |
| debugEvent("RecoverClassHeader"); | |
| var implementsClause = | |
| pop(NullValues.IdentifierList) as ImplementsClauseImpl?; | |
| var withClause = pop(NullValues.WithClause) as WithClauseImpl?; | |
| var extendsClause = pop(NullValues.ExtendsClause) as ExtendsClauseImpl?; | |
| switch (kind) { | |
| case DeclarationHeaderKind.Class: | |
| var declaration = _classLikeBuilder as _ClassDeclarationBuilder; | |
| if (extendsClause != null) { | |
| if (declaration.extendsClause?.superclass == null) { | |
| declaration.extendsClause = extendsClause; | |
| } | |
| } | |
| if (withClause != null) { | |
| var existingClause = declaration.withClause; | |
| if (existingClause == null) { | |
| declaration.withClause = withClause; | |
| } else { | |
| declaration.withClause = WithClauseImpl( | |
| withKeyword: existingClause.withKeyword, | |
| mixinTypes: [ | |
| ...existingClause.mixinTypes, | |
| ...withClause.mixinTypes, | |
| ], | |
| ); | |
| } | |
| } | |
| if (implementsClause != null) { | |
| var existingClause = declaration.implementsClause; | |
| if (existingClause == null) { | |
| declaration.implementsClause = implementsClause; | |
| } else { | |
| declaration.implementsClause = ImplementsClauseImpl( | |
| implementsKeyword: existingClause.implementsKeyword, | |
| interfaces: [ | |
| ...existingClause.interfaces, | |
| ...implementsClause.interfaces, | |
| ], | |
| ); | |
| } | |
| } | |
| case DeclarationHeaderKind.ExtensionType: | |
| // TODO(scheglov): Support header recovery on extension type | |
| // declaration. | |
| } | |
| } | |
| @override | |
| void handleRecoverImport(Token? semicolon) { | |
| assert(optionalOrNull(';', semicolon)); | |
| debugEvent("RecoverImport"); | |
| var combinators = pop() as List<CombinatorImpl>?; | |
| var deferredKeyword = pop(NullValues.Deferred) as Token?; | |
| var asKeyword = pop(NullValues.As) as Token?; | |
| var prefixName = pop(NullValues.Prefix) as Token?; | |
| var configurations = pop() as List<ConfigurationImpl>?; | |
| var directive = directives.last; | |
| switch (directive) { | |
| case ImportDirectiveImpl(): | |
| // TODO(scheglov): This code would be easier if we used one object. | |
| var mergedAsKeyword = directive.asKeyword; | |
| var mergedPrefixName = directive.prefixName; | |
| if (directive.asKeyword == null && asKeyword != null) { | |
| mergedAsKeyword = asKeyword; | |
| mergedPrefixName = prefixName; | |
| } | |
| directives.last = ImportDirectiveImpl( | |
| comment: directive.documentationComment, | |
| metadata: directive.metadata, | |
| importKeyword: directive.importKeyword, | |
| uri: directive.uri, | |
| configurations: [...directive.configurations, ...?configurations], | |
| deferredKeyword: directive.deferredKeyword ?? deferredKeyword, | |
| asKeyword: mergedAsKeyword, | |
| prefixName: mergedPrefixName, | |
| combinators: [...directive.combinators, ...?combinators], | |
| semicolon: semicolon ?? directive.semicolon, | |
| ); | |
| default: | |
| throw UnimplementedError('${directive.runtimeType}'); | |
| } | |
| } | |
| @override | |
| void handleRecoverMixinHeader() { | |
| var builder = _classLikeBuilder as _MixinDeclarationBuilder; | |
| var implementsClause = | |
| pop(NullValues.IdentifierList) as ImplementsClauseImpl?; | |
| var onClause = pop(NullValues.IdentifierList) as MixinOnClauseImpl?; | |
| if (onClause != null) { | |
| var existingClause = builder.onClause; | |
| if (existingClause == null) { | |
| builder.onClause = onClause; | |
| } else { | |
| builder.onClause = MixinOnClauseImpl( | |
| onKeyword: existingClause.onKeyword, | |
| superclassConstraints: [ | |
| ...existingClause.superclassConstraints, | |
| ...onClause.superclassConstraints, | |
| ], | |
| ); | |
| } | |
| } | |
| if (implementsClause != null) { | |
| var existingClause = builder.implementsClause; | |
| if (existingClause == null) { | |
| builder.implementsClause = implementsClause; | |
| } else { | |
| builder.implementsClause = ImplementsClauseImpl( | |
| implementsKeyword: implementsClause.implementsKeyword, | |
| interfaces: [ | |
| ...existingClause.interfaces, | |
| ...implementsClause.interfaces, | |
| ], | |
| ); | |
| } | |
| } | |
| } | |
| @override | |
| void handleRelationalPattern(Token token) { | |
| debugEvent("RelationalPattern"); | |
| push(RelationalPatternImpl(operator: token, operand2: _popExpression())); | |
| } | |
| @override | |
| void handleRestPattern(Token dots, {required bool hasSubPattern}) { | |
| var subPattern = hasSubPattern ? pop() as DartPatternImpl : null; | |
| push(RestPatternElementImpl(operator: dots, pattern: subPattern)); | |
| } | |
| @override | |
| void handleScript(Token token) { | |
| assert(identical(token.type, TokenType.SCRIPT_TAG)); | |
| debugEvent("Script"); | |
| scriptTag = ScriptTagImpl(scriptTag: token); | |
| } | |
| @override | |
| void handleSend(Token beginToken, Token endToken) { | |
| debugEvent("Send"); | |
| var argumentList = pop() as ArgumentListImpl?; | |
| var typeArguments = pop() as TypeArgumentListImpl?; | |
| if (argumentList != null) { | |
| doInvocation(typeArguments, argumentList); | |
| } else { | |
| doPropertyGet(); | |
| } | |
| } | |
| @override | |
| void handleSendWithoutArguments( | |
| Token beginToken, | |
| Token endToken, | |
| Token nextToken, | |
| ) {} | |
| @override | |
| void handleSpreadExpression(Token spreadToken) { | |
| var expression = _popExpression(); | |
| push( | |
| SpreadElementImpl(spreadOperator: spreadToken, expression2: expression), | |
| ); | |
| } | |
| @override | |
| void handleStringPart(Token literalString) { | |
| assert(identical(literalString.kind, STRING_TOKEN)); | |
| debugEvent("StringPart"); | |
| push(literalString); | |
| } | |
| @override | |
| void handleSuperExpression(Token superKeyword, IdentifierContext context) { | |
| assert(optional('super', superKeyword)); | |
| debugEvent("SuperExpression"); | |
| push( | |
| InvalidSuperExpressionImpl( | |
| superReference: SuperReferenceImpl(superKeyword: superKeyword), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleSwitchCaseNoWhenClause(Token token) { | |
| debugEvent("SwitchCaseNoWhenClause"); | |
| } | |
| @override | |
| void handleSwitchExpressionCasePattern(Token token) { | |
| debugEvent("SwitchExpressionCasePattern"); | |
| } | |
| @override | |
| void handleSymbolVoid(Token voidKeyword) { | |
| assert(optional('void', voidKeyword)); | |
| debugEvent("SymbolVoid"); | |
| push(voidKeyword); | |
| } | |
| @override | |
| void handleThisExpression(Token thisKeyword, IdentifierContext context) { | |
| assert(optional('this', thisKeyword)); | |
| debugEvent("ThisExpression"); | |
| push(ThisExpressionImpl(thisKeyword: thisKeyword)); | |
| } | |
| @override | |
| void handleThrowExpression(Token throwToken, Token endToken) { | |
| assert(optional('throw', throwToken)); | |
| debugEvent("ThrowExpression"); | |
| push( | |
| ThrowExpressionImpl( | |
| throwKeyword: throwToken, | |
| expression2: _popExpression(), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleType(Token beginToken, Token? question) { | |
| debugEvent("Type"); | |
| var arguments = pop() as TypeArgumentListImpl?; | |
| var name = pop()!; | |
| push( | |
| NamedTypeImpl( | |
| importPrefix: name is _QualifiedName | |
| ? ImportPrefixReferenceImpl(name: name.prefix, period: name.period) | |
| : null, | |
| name: name is _QualifiedName ? name.name : name as Token, | |
| typeArguments: arguments, | |
| question: question, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleTypeArgumentApplication(Token openAngleBracket) { | |
| var typeArguments = pop() as TypeArgumentListImpl; | |
| var receiver = pop() as ExpressionImpl; | |
| if (!enableConstructorTearoffs) { | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.constructor_tearoffs, | |
| startToken: typeArguments.leftBracket, | |
| endToken: typeArguments.rightBracket, | |
| ); | |
| } | |
| reportErrorIfSuper(receiver); | |
| push( | |
| ParsedTypeArgumentsImpl(operand: receiver, typeArguments: typeArguments), | |
| ); | |
| } | |
| @override | |
| void handleTypeVariablesDefined(Token token, int count) { | |
| debugEvent("handleTypeVariablesDefined"); | |
| assert(count > 0); | |
| push(popTypedList<TypeParameterImpl>(count)); | |
| } | |
| @override | |
| void handleUnaryPostfixAssignmentExpression(Token operator) { | |
| assert(operator.type.isUnaryPostfixOperator); | |
| debugEvent("UnaryPostfixAssignmentExpression"); | |
| var expression = pop() as ExpressionImpl; | |
| if (!_isAssignable(expression)) { | |
| // This error is also reported by the body builder. | |
| handleRecoverableError( | |
| fe_diag.illegalAssignmentToNonAssignable, | |
| operator, | |
| operator, | |
| ); | |
| } | |
| push( | |
| IncrementOrDecrementExpressionImpl( | |
| position: IncrementOrDecrementPosition.postfix, | |
| operator: operator, | |
| target: _toAssignmentTarget(expression), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleUnaryPrefixAssignmentExpression(Token operator) { | |
| assert(operator.type.isUnaryPrefixOperator); | |
| debugEvent("UnaryPrefixAssignmentExpression"); | |
| var expression = pop() as ExpressionImpl; | |
| if (!_isAssignable(expression)) { | |
| // This error is also reported by the body builder. | |
| handleRecoverableError( | |
| fe_diag.missingAssignableSelector, | |
| expression.endToken, | |
| expression.endToken, | |
| ); | |
| } | |
| push( | |
| IncrementOrDecrementExpressionImpl( | |
| position: IncrementOrDecrementPosition.prefix, | |
| operator: operator, | |
| target: _toAssignmentTarget(expression), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleUnaryPrefixExpression(Token operator) { | |
| assert(operator.type.isUnaryPrefixOperator); | |
| debugEvent("UnaryPrefixExpression"); | |
| var operand = _popExpression(); | |
| if (!(operator.type == TokenType.MINUS || | |
| operator.type == TokenType.TILDE)) { | |
| reportErrorIfSuper(operand); | |
| } | |
| if (operator.type == TokenType.BANG) { | |
| push(LogicalNotImpl(operator: operator, operand: operand)); | |
| } else { | |
| push( | |
| UnaryOperatorInvocationImpl( | |
| operator: operator, | |
| operand: _toInstanceReceiver(operand), | |
| ), | |
| ); | |
| } | |
| } | |
| @override | |
| void handleValuedFormalParameter( | |
| Token equals, | |
| Token token, | |
| FormalParameterKind kind, | |
| ) { | |
| assert(optional('=', equals) || optional(':', equals)); | |
| debugEvent("ValuedFormalParameter"); | |
| var value = _popExpression(); | |
| push(FormalParameterDefaultClauseImpl(separator: equals, value2: value)); | |
| } | |
| @override | |
| void handleVoidKeyword(Token voidKeyword) { | |
| assert(optional('void', voidKeyword)); | |
| debugEvent("VoidKeyword"); | |
| // TODO(paulberry): is this sufficient, or do we need to hook the "void" | |
| // keyword up to an element? | |
| handleIdentifier(voidKeyword, IdentifierContext.typeReference); | |
| handleNoTypeArguments(voidKeyword); | |
| handleType(voidKeyword, null); | |
| } | |
| @override | |
| void handleVoidKeywordWithTypeArguments(Token voidKeyword) { | |
| assert(optional('void', voidKeyword)); | |
| debugEvent("VoidKeywordWithTypeArguments"); | |
| var arguments = pop() as TypeArgumentListImpl; | |
| // TODO(paulberry): is this sufficient, or do we need to hook the "void" | |
| // keyword up to an element? | |
| handleIdentifier(voidKeyword, IdentifierContext.typeReference); | |
| push(arguments); | |
| handleType(voidKeyword, null); | |
| } | |
| @override | |
| void handleWildcardPattern(Token? keyword, Token wildcard) { | |
| debugEvent('WildcardPattern'); | |
| assert(_featureSet.isEnabled(Feature.patterns)); | |
| // Note: if `default` appears in a switch expression, parser error recovery | |
| // treats it as a wildcard pattern. | |
| assert(wildcard.lexeme == '_' || wildcard.lexeme == 'default'); | |
| var type = pop() as TypeAnnotationImpl?; | |
| push(WildcardPatternImpl(keyword: keyword, type: type, name: wildcard)); | |
| } | |
| @override | |
| Never internalProblem(Message message, int charOffset, Uri uri) { | |
| throw UnsupportedError(message.problemMessage); | |
| } | |
| /// Return `true` if [token] is either `null` or is the symbol or keyword | |
| /// [value]. | |
| bool optionalOrNull(String value, Token? token) { | |
| return token == null || identical(value, token.stringValue); | |
| } | |
| /// Parses the comment references in a sequence of comment tokens where | |
| /// [dartdoc] is the first token in the sequence. | |
| @visibleForTesting | |
| CommentImpl parseDocComment(Token dartdoc) { | |
| // Build and return the comment. | |
| return DocCommentBuilder( | |
| parser, | |
| diagnosticReporter.diagnosticReporter, | |
| uri, | |
| _featureSet, | |
| _languageVersion, | |
| _lineInfo, | |
| dartdoc, | |
| ).build(); | |
| } | |
| List<CollectionElementImpl> popCollectionElements(int count) { | |
| // TODO(scheglov): Not efficient. | |
| var elements = <CollectionElementImpl>[]; | |
| for (int index = count - 1; index >= 0; --index) { | |
| elements.add(_popCollectionElement()); | |
| } | |
| return elements.reversed.toList(); | |
| } | |
| List? popList(int n, List list) { | |
| if (n == 0) return null; | |
| return stack.popList(n, list, null); | |
| } | |
| List<T>? popTypedList<T extends Object>(int count) { | |
| if (count == 0) return null; | |
| assert(stack.length >= count); | |
| var tailList = List<T?>.filled(count, null, growable: true); | |
| stack.popList(count, tailList, null); | |
| return tailList.nonNulls.toList(); | |
| } | |
| List<T> popTypedList2<T>(int count) { | |
| return stack.popNonNullableNewList<T>(count); | |
| } | |
| void reportErrorIfNullableType(Token? questionMark) { | |
| if (questionMark != null) { | |
| assert(optional('?', questionMark)); | |
| _reportFeatureNotEnabled( | |
| feature: ExperimentalFeatures.non_nullable, | |
| startToken: questionMark, | |
| ); | |
| } | |
| } | |
| void reportErrorIfSuper(ExpressionImpl expression) { | |
| if (expression is InvalidSuperExpressionImpl) { | |
| // This error is also reported by the body builder. | |
| handleRecoverableError( | |
| fe_diag.missingAssignableSelector, | |
| expression.beginToken, | |
| expression.endToken, | |
| ); | |
| } | |
| } | |
| void _beginMethod( | |
| DeclarationKind declarationKind, | |
| Token? augmentToken, | |
| Token? externalToken, | |
| Token? staticToken, | |
| Token? covariantToken, | |
| Token? varFinalOrConst, | |
| Token? newKeyword, | |
| Token? getOrSet, | |
| Token name, | |
| String? enclosingDeclarationName, | |
| ) { | |
| _Modifiers modifiers = _Modifiers(); | |
| if (augmentToken != null) { | |
| assert(augmentToken.isModifier); | |
| modifiers.augmentKeyword = augmentToken; | |
| } | |
| if (externalToken != null) { | |
| assert(externalToken.isModifier); | |
| modifiers.externalKeyword = externalToken; | |
| } | |
| if (staticToken != null) { | |
| assert(staticToken.isModifier); | |
| var builder = _classLikeBuilder; | |
| if (builder is! _ClassDeclarationBuilder || | |
| builder.name.lexeme != name.lexeme || | |
| getOrSet != null) { | |
| modifiers.staticKeyword = staticToken; | |
| } | |
| } | |
| if (covariantToken != null) { | |
| assert(covariantToken.isModifier); | |
| modifiers.covariantKeyword = covariantToken; | |
| } | |
| if (varFinalOrConst != null) { | |
| assert(varFinalOrConst.isModifier); | |
| modifiers.finalConstOrVarKeyword = varFinalOrConst; | |
| } | |
| if (newKeyword != null) { | |
| modifiers.newKeyword = newKeyword; | |
| } | |
| push(modifiers); | |
| } | |
| ConstructorDeclarationImpl _buildConstructorDeclaration({ | |
| required Token beginToken, | |
| required Token endToken, | |
| }) { | |
| var bodyObject = pop(); | |
| var initializers = (pop() as List<ConstructorInitializerImpl>?) ?? const []; | |
| var separator = pop() as Token?; | |
| var parameters = pop() as FormalParameterListImpl; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var preliminaryName = pop(); | |
| pop(); // return type | |
| var modifiers = pop() as _Modifiers?; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, beginToken); | |
| ConstructorReference2Impl? factoryRedirectionTarget; | |
| FunctionBodyImpl body; | |
| if (bodyObject is FunctionBodyImpl) { | |
| body = bodyObject; | |
| } else if (bodyObject is _RedirectingFactoryBody) { | |
| if (separator != null) { | |
| // Parser encountered a redirecting factory constructor with | |
| // initializers (which is illegal). Leave `separator` pointing to the | |
| // `:` to preserve the invariant that `separator` comes before | |
| // initializers. | |
| } else { | |
| separator ??= bodyObject.equalToken; | |
| } | |
| factoryRedirectionTarget = bodyObject.constructorReference; | |
| body = EmptyFunctionBodyImpl(semicolon: endToken); | |
| } else { | |
| internalProblem( | |
| fe_diag.internalProblemUnhandled.withArguments( | |
| what: "${bodyObject.runtimeType}", | |
| where: "bodyObject", | |
| ), | |
| beginToken.charOffset, | |
| uri, | |
| ); | |
| } | |
| Token? typeName; | |
| Token? period; | |
| Token? constructorNameToken; | |
| var newKeyword = modifiers?.newKeyword; | |
| switch (preliminaryName) { | |
| case null: | |
| break; | |
| case Token(): | |
| if (newKeyword != null) { | |
| constructorNameToken = preliminaryName; | |
| } else { | |
| typeName = preliminaryName; | |
| } | |
| case _QualifiedName(): | |
| typeName = preliminaryName.prefix; | |
| period = preliminaryName.period; | |
| constructorNameToken = preliminaryName.name; | |
| case _OperatorName(): | |
| typeName = preliminaryName.name; | |
| default: | |
| throw UnimplementedError( | |
| 'name is an instance of ${preliminaryName.runtimeType} in endClassConstructor', | |
| ); | |
| } | |
| if (typeParameters != null) { | |
| // Outline builder also reports this error message. | |
| handleRecoverableError( | |
| fe_diag.constructorWithTypeParameters, | |
| typeParameters.beginToken, | |
| typeParameters.endToken, | |
| ); | |
| } | |
| if (modifiers?.externalKeyword != null) { | |
| for (var formalParameter in parameters.allFormalParameters) { | |
| if (formalParameter is FieldFormalParameterImpl) { | |
| diagnosticReporter.diagnosticReporter?.report( | |
| diag.externalConstructorWithFieldInitializers.at( | |
| formalParameter.thisKeyword, | |
| ), | |
| ); | |
| } | |
| } | |
| } | |
| var constructor = ConstructorDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: modifiers?.augmentKeyword, | |
| externalKeyword: modifiers?.externalKeyword, | |
| constKeyword: modifiers?.finalConstOrVarKeyword, | |
| factoryKeyword: null, | |
| newKeyword: modifiers?.newKeyword, | |
| typeName2: typeName, | |
| period: period, | |
| name: constructorNameToken, | |
| parameters: parameters, | |
| separator: separator, | |
| initializers: initializers, | |
| factoryRedirectionTarget: factoryRedirectionTarget, | |
| body: body, | |
| ); | |
| return constructor; | |
| } | |
| ConstructorDeclarationImpl _buildFactoryConstructorDeclaration({ | |
| required Token beginToken, | |
| required Token factoryKeyword, | |
| required Token endToken, | |
| }) { | |
| FunctionBodyImpl body; | |
| Token? separator; | |
| ConstructorReference2Impl? factoryRedirectionTarget; | |
| var bodyObject = pop(); | |
| if (bodyObject is FunctionBodyImpl) { | |
| body = bodyObject; | |
| } else if (bodyObject is _RedirectingFactoryBody) { | |
| separator = bodyObject.equalToken; | |
| factoryRedirectionTarget = bodyObject.constructorReference; | |
| body = EmptyFunctionBodyImpl(semicolon: endToken); | |
| } else { | |
| internalProblem( | |
| fe_diag.internalProblemUnhandled.withArguments( | |
| what: "${bodyObject.runtimeType}", | |
| where: "bodyObject", | |
| ), | |
| beginToken.charOffset, | |
| uri, | |
| ); | |
| } | |
| var parameters = pop() as FormalParameterListImpl; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var preliminaryName = pop(); | |
| var modifiers = pop() as _Modifiers?; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, beginToken); | |
| if (typeParameters != null) { | |
| // TODO(danrubel): Update OutlineBuilder to report this error message. | |
| handleRecoverableError( | |
| fe_diag.constructorWithTypeParameters, | |
| typeParameters.beginToken, | |
| typeParameters.endToken, | |
| ); | |
| } | |
| Token? typeName; | |
| Token? period; | |
| Token? constructorNameToken; | |
| switch (preliminaryName) { | |
| case null: | |
| break; | |
| case Token(): | |
| if (_featureSet.isEnabled(Feature.primary_constructors)) { | |
| // Consider a factory constructor declaration of the form | |
| // `factory C(...` optionally starting with zero or more of the | |
| // modifiers `const`, `augment`, or `external`. Assume that `C` is | |
| // the name of the enclosing class, mixin class, enum, or extension | |
| // type. In this situation, the declaration declares a constructor | |
| // whose name is `C`. | |
| var enclosingClassName = _classLikeBuilder?.name; | |
| if (enclosingClassName?.lexeme == preliminaryName.lexeme) { | |
| typeName = preliminaryName; | |
| } else { | |
| constructorNameToken = preliminaryName; | |
| } | |
| } else { | |
| typeName = preliminaryName; | |
| } | |
| case _QualifiedName(): | |
| typeName = preliminaryName.prefix; | |
| period = preliminaryName.period; | |
| constructorNameToken = preliminaryName.name; | |
| default: | |
| throw StateError('Unexpected constructor name: $preliminaryName'); | |
| } | |
| var constructor = ConstructorDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: modifiers?.augmentKeyword, | |
| externalKeyword: modifiers?.externalKeyword, | |
| constKeyword: modifiers?.finalConstOrVarKeyword, | |
| factoryKeyword: factoryKeyword, | |
| newKeyword: null, | |
| typeName2: typeName, | |
| period: period, | |
| name: constructorNameToken, | |
| parameters: parameters, | |
| separator: separator, | |
| initializers: [], | |
| factoryRedirectionTarget: factoryRedirectionTarget, | |
| body: body, | |
| ); | |
| return constructor; | |
| } | |
| void _endClassConstructor( | |
| Token beginToken, | |
| Token beginParam, | |
| Token? beginInitializers, | |
| Token endToken, | |
| ) { | |
| _classLikeBuilder?.members.add( | |
| _buildConstructorDeclaration(beginToken: beginToken, endToken: endToken), | |
| ); | |
| } | |
| void _endClassFields( | |
| Token? abstractToken, | |
| Token? augmentToken, | |
| Token? externalToken, | |
| Token? staticToken, | |
| Token? covariantToken, | |
| Token? lateToken, | |
| Token? varFinalOrConst, | |
| int count, | |
| Token beginToken, | |
| Token endToken, | |
| ) { | |
| assert(optional(';', endToken)); | |
| if (abstractToken != null) { | |
| if (staticToken != null && !enableAugmentations) { | |
| handleRecoverableError( | |
| fe_diag.abstractStaticField, | |
| abstractToken, | |
| abstractToken, | |
| ); | |
| } | |
| if (lateToken != null) { | |
| handleRecoverableError( | |
| fe_diag.abstractLateField, | |
| abstractToken, | |
| abstractToken, | |
| ); | |
| } | |
| } | |
| if (externalToken != null) { | |
| if (lateToken != null) { | |
| handleRecoverableError( | |
| fe_diag.externalLateField, | |
| externalToken, | |
| externalToken, | |
| ); | |
| } | |
| } | |
| var variables = popTypedList2<VariableDeclarationImpl>(count); | |
| var type = pop() as TypeAnnotationImpl?; | |
| var variableList = VariableDeclarationListImpl( | |
| comment: null, | |
| metadata: null, | |
| lateKeyword: lateToken, | |
| keyword: varFinalOrConst, | |
| type: type, | |
| variables: variables, | |
| ); | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, beginToken); | |
| _classLikeBuilder?.members.add( | |
| FieldDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| abstractKeyword: abstractToken, | |
| augmentKeyword: augmentToken, | |
| covariantKeyword: covariantToken, | |
| externalKeyword: externalToken, | |
| staticKeyword: staticToken, | |
| fields: variableList, | |
| semicolon: endToken, | |
| ), | |
| ); | |
| } | |
| void _endClassMethod( | |
| Token? getOrSet, | |
| Token beginToken, | |
| Token beginParam, | |
| Token? beginInitializers, | |
| Token endToken, | |
| ) { | |
| assert( | |
| getOrSet == null || | |
| optional('get', getOrSet) || | |
| optional('set', getOrSet), | |
| ); | |
| var bodyObject = pop(); | |
| pop(); // initializers | |
| pop(); // separator | |
| var formalParameters = pop() as FormalParameterListImpl?; | |
| var typeParameters = pop() as TypeParameterListImpl?; | |
| var name = pop(); | |
| var returnType = pop() as TypeAnnotationImpl?; | |
| var modifiers = pop() as _Modifiers?; | |
| var metadata = pop() as List<AnnotationImpl>?; | |
| var comment = _findComment(metadata, beginToken); | |
| assert(formalParameters != null || optional('get', getOrSet!)); | |
| Token? operatorKeyword; | |
| Token nameId; | |
| if (name is Token) { | |
| nameId = name; | |
| } else if (name is _OperatorName) { | |
| operatorKeyword = name.operatorKeyword; | |
| nameId = name.name; | |
| if (typeParameters != null) { | |
| handleRecoverableError( | |
| fe_diag.operatorWithTypeParameters, | |
| typeParameters.beginToken, | |
| typeParameters.endToken, | |
| ); | |
| } | |
| } else { | |
| throw UnimplementedError( | |
| 'name is an instance of ${name.runtimeType} in endClassMethod', | |
| ); | |
| } | |
| if (getOrSet?.keyword == Keyword.SET) { | |
| formalParameters = _ensureSetterFormalParameter(nameId, formalParameters); | |
| } | |
| FunctionBodyImpl body; | |
| if (bodyObject is FunctionBodyImpl) { | |
| body = bodyObject; | |
| } else if (bodyObject is _RedirectingFactoryBody) { | |
| body = EmptyFunctionBodyImpl(semicolon: endToken); | |
| } else { | |
| internalProblem( | |
| fe_diag.internalProblemUnhandled.withArguments( | |
| what: "${bodyObject.runtimeType}", | |
| where: "bodyObject", | |
| ), | |
| beginToken.charOffset, | |
| uri, | |
| ); | |
| } | |
| _classLikeBuilder?.members.add( | |
| MethodDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: modifiers?.augmentKeyword, | |
| externalKeyword: modifiers?.externalKeyword, | |
| modifierKeyword: modifiers?.abstractKeyword ?? modifiers?.staticKeyword, | |
| returnType: returnType, | |
| propertyKeyword: getOrSet, | |
| operatorKeyword: operatorKeyword, | |
| name: nameId, | |
| typeParameters: typeParameters, | |
| parameters: formalParameters, | |
| body: body, | |
| ), | |
| ); | |
| } | |
| void _endFactoryMethod( | |
| Token beginToken, | |
| Token factoryKeyword, | |
| Token endToken, | |
| ) { | |
| assert(optional('factory', factoryKeyword)); | |
| assert(optional(';', endToken) || optional('}', endToken)); | |
| _classLikeBuilder?.members.add( | |
| _buildFactoryConstructorDeclaration( | |
| beginToken: beginToken, | |
| factoryKeyword: factoryKeyword, | |
| endToken: endToken, | |
| ), | |
| ); | |
| } | |
| FormalParameterListImpl? _ensureSetterFormalParameter( | |
| Token setterName, | |
| FormalParameterListImpl? formalParameterList, | |
| ) { | |
| formalParameterList ??= throw StateError( | |
| 'Parser has recovery, this never happens.', | |
| ); | |
| var formalParameters = formalParameterList.allFormalParameters; | |
| var valueFormalParameter = formalParameters.firstOrNull; | |
| if (valueFormalParameter == null) { | |
| if (!formalParameterList.leftParenthesis.isSynthetic) { | |
| diagnosticReporter.diagnosticReporter?.report( | |
| diag.wrongNumberOfParametersForSetter.at(setterName), | |
| ); | |
| } | |
| var valueNameToken = parser.rewriter.insertSyntheticIdentifier( | |
| formalParameterList.leftParenthesis, | |
| ); | |
| return FormalParameterListImpl( | |
| leftParenthesis: formalParameterList.leftParenthesis, | |
| requiredPositionalFormalParameters: [ | |
| RegularFormalParameterImpl( | |
| comment: null, | |
| metadata: null, | |
| kind: ParameterKind.REQUIRED, | |
| covariantKeyword: null, | |
| requiredKeyword: null, | |
| constFinalOrVarKeyword: null, | |
| type: null, | |
| name: valueNameToken, | |
| functionTypedSuffix: null, | |
| defaultClause: null, | |
| ), | |
| ], | |
| delimitedFormalParameters: null, | |
| rightParenthesis: formalParameterList.rightParenthesis, | |
| ); | |
| } | |
| if (valueFormalParameter.isRequiredPositional && | |
| formalParameters.length == 1) { | |
| return formalParameterList; | |
| } | |
| diagnosticReporter.diagnosticReporter?.report( | |
| diag.wrongNumberOfParametersForSetter.at(setterName), | |
| ); | |
| return FormalParameterListImpl( | |
| leftParenthesis: formalParameterList.leftParenthesis, | |
| requiredPositionalFormalParameters: [valueFormalParameter], | |
| delimitedFormalParameters: null, | |
| rightParenthesis: formalParameterList.rightParenthesis, | |
| ); | |
| } | |
| CommentImpl? _findComment( | |
| List<AnnotationImpl>? metadata, | |
| Token tokenAfterMetadata, | |
| ) { | |
| // Find the dartdoc tokens. | |
| var dartdoc = parser.findDartDoc(tokenAfterMetadata); | |
| if (dartdoc == null) { | |
| if (metadata == null) { | |
| return null; | |
| } | |
| int index = metadata.length; | |
| while (true) { | |
| if (index == 0) { | |
| return null; | |
| } | |
| --index; | |
| dartdoc = parser.findDartDoc(metadata[index].beginToken); | |
| if (dartdoc != null) { | |
| break; | |
| } | |
| } | |
| } | |
| return parseDocComment(dartdoc); | |
| } | |
| void _handleConstructorInvocation(Token? token) { | |
| var argumentList = pop() as ArgumentListImpl; | |
| ConstructorReference2Impl constructorReference; | |
| TypeArgumentListImpl? typeArguments; | |
| var object = pop(); | |
| if (object is _ConstructorReferenceWithInvalidTypeArgs) { | |
| constructorReference = object.reference; | |
| typeArguments = object.invalidTypeArgs; | |
| } else { | |
| constructorReference = object as ConstructorReference2Impl; | |
| } | |
| if (token == null) { | |
| var type = constructorReference.typeReference; | |
| ParsedExpressionImpl operand; | |
| if (type.importPrefix case var prefix?) { | |
| operand = ParsedNameAccessImpl( | |
| operand: ParsedUnqualifiedNameImpl(name: prefix.name), | |
| operator: prefix.period, | |
| name: type.name, | |
| ); | |
| } else { | |
| operand = ParsedUnqualifiedNameImpl(name: type.name); | |
| } | |
| if (constructorReference.selector case var selector?) { | |
| if (type.typeArguments case var arguments?) { | |
| operand = ParsedTypeArgumentsImpl( | |
| operand: operand, | |
| typeArguments: arguments, | |
| ); | |
| } | |
| operand = ParsedNameAccessImpl( | |
| operand: operand, | |
| operator: selector.period, | |
| name: selector.name2, | |
| ); | |
| } else { | |
| typeArguments ??= type.typeArguments; | |
| } | |
| if (typeArguments != null) { | |
| operand = ParsedTypeArgumentsImpl( | |
| operand: operand, | |
| typeArguments: typeArguments, | |
| ); | |
| } | |
| push( | |
| ParsedValueArgumentsImpl(operand: operand, argumentList: argumentList), | |
| ); | |
| return; | |
| } | |
| push( | |
| ConstructorInvocationImpl( | |
| keyword: token, | |
| constructorReference: constructorReference, | |
| argumentList: argumentList, | |
| typeArguments: typeArguments, | |
| ), | |
| ); | |
| } | |
| /// Whether the written syntax admits an assignment or update, independently | |
| /// of whether resolution will find a writable element. | |
| bool _isAssignable(ExpressionImpl expression) { | |
| return switch (expression) { | |
| ParsedUnqualifiedNameImpl() || | |
| ParsedNameAccessImpl() || | |
| ParsedCascadeNameImpl() || | |
| ReceiverPropertyExtractionImpl() || | |
| CascadePropertyExtractionImpl() || | |
| ReceiverIndexExpressionImpl() || | |
| CascadeIndexExpressionImpl() => true, | |
| ParsedDotShorthandExpressionImpl(:var expression) => _isAssignable( | |
| expression, | |
| ), | |
| _ => false, | |
| }; | |
| } | |
| /// Whether [receiver] is in the property migration slice. | |
| /// | |
| /// Parentheses establish an expression boundary even when the expression | |
| /// inside them requires resolution. Ordinary property accesses preserve a | |
| /// supported root, while other postfix operations remain on their existing | |
| /// AST shapes until they are migrated explicitly. | |
| bool _isSupportedPropertyReceiver(ExpressionImpl receiver) { | |
| switch (receiver) { | |
| case LiteralImpl(): | |
| case ParenthesizedExpressionImpl(): | |
| case ConstructorInvocationImpl(): | |
| case ReceiverIndexExpressionImpl(): | |
| case ThisExpressionImpl(): | |
| return true; | |
| case ReceiverPropertyExtractionImpl(:var receiver): | |
| return _isSupportedPropertyReceiver(_parsedPropertyReceiver(receiver)); | |
| default: | |
| return false; | |
| } | |
| } | |
| /// Parser-built property extractions always have expression receivers. | |
| /// Static qualifiers are introduced later, when resolution lowers a chain. | |
| ExpressionImpl _parsedPropertyReceiver(NamedReceiverImpl receiver) { | |
| return receiver as ExpressionImpl; | |
| } | |
| CollectionElementImpl _popCollectionElement() { | |
| return pop() as CollectionElementImpl; | |
| } | |
| ExpressionImpl _popExpression() { | |
| return pop() as ExpressionImpl; | |
| } | |
| List<NamedTypeImpl> _popNamedTypeList({ | |
| required LocatableDiagnostic locatableDiagnostic, | |
| }) { | |
| var types = pop() as List<TypeAnnotationImpl>; | |
| var namedTypes = <NamedTypeImpl>[]; | |
| for (var type in types) { | |
| if (type is NamedTypeImpl) { | |
| namedTypes.add(type); | |
| } else { | |
| diagnosticReporter.diagnosticReporter?.report( | |
| locatableDiagnostic.at(type), | |
| ); | |
| } | |
| } | |
| return namedTypes; | |
| } | |
| void _reportFeatureNotEnabled({ | |
| required ExperimentalFeature feature, | |
| required Token startToken, | |
| Token? endToken, | |
| }) { | |
| var requiredVersion = | |
| feature.releaseVersion ?? ExperimentStatus.currentVersion; | |
| handleRecoverableError( | |
| fe_diag.experimentNotEnabled.withArguments( | |
| featureName: feature.enableString, | |
| enabledVersion: _versionAsString(requiredVersion), | |
| ), | |
| startToken, | |
| endToken ?? startToken, | |
| ); | |
| } | |
| ArgumentListImpl _syntheticArgumentList(Token precedingToken) { | |
| var left = parser.rewriter.insertParens(precedingToken, false); | |
| var right = left.endGroup!; | |
| return ArgumentListImpl( | |
| leftParenthesis: left, | |
| arguments2: [], | |
| rightParenthesis: right, | |
| ); | |
| } | |
| FormalParameterListImpl _syntheticFormalParameterList(Token precedingToken) { | |
| var left = parser.rewriter.insertParens(precedingToken, false); | |
| var right = left.endGroup!; | |
| return FormalParameterListImpl( | |
| leftParenthesis: left, | |
| requiredPositionalFormalParameters: [], | |
| delimitedFormalParameters: null, | |
| rightParenthesis: right, | |
| ); | |
| } | |
| ParameterKind _toAnalyzerParameterKind(FormalParameterKind type) { | |
| switch (type) { | |
| case FormalParameterKind.requiredPositional: | |
| return ParameterKind.REQUIRED; | |
| case FormalParameterKind.requiredNamed: | |
| return ParameterKind.NAMED_REQUIRED; | |
| case FormalParameterKind.optionalNamed: | |
| return ParameterKind.NAMED; | |
| case FormalParameterKind.optionalPositional: | |
| return ParameterKind.POSITIONAL; | |
| } | |
| } | |
| AssignmentTargetImpl _toAssignmentTarget(ExpressionImpl expression) { | |
| switch (expression) { | |
| case CascadeIndexExpressionImpl(): | |
| return CascadeIndexAssignmentTargetImpl( | |
| leftBracket: expression.leftBracket, | |
| index: expression.index, | |
| rightBracket: expression.rightBracket, | |
| ); | |
| case CascadePropertyExtractionImpl(): | |
| return CascadePropertyAssignmentTargetImpl(name: expression.name); | |
| case InvalidSuperExpressionImpl(): | |
| return InvalidSuperAssignmentTargetImpl( | |
| superReference: expression.superReference, | |
| ); | |
| case ParsedCascadeNameImpl(): | |
| return CascadePropertyAssignmentTargetImpl(name: expression.name); | |
| case ParsedNameAccessImpl(): | |
| var operand = expression.operand; | |
| // Preserve namespace and type-application interpretation for targets. | |
| var head = operand; | |
| while (head is ParsedNameAccessImpl) { | |
| head = head.operand; | |
| } | |
| if (head is ParsedUnqualifiedNameImpl || | |
| head is ParsedTypeArgumentsImpl) { | |
| return ParsedNameAccessAssignmentTargetImpl( | |
| operand: operand as ParsedExpressionImpl, | |
| operator: expression.operator, | |
| name: expression.name, | |
| ); | |
| } | |
| return ReceiverPropertyAssignmentTargetImpl( | |
| receiver: _toInstanceReceiver(operand), | |
| operator: expression.operator, | |
| name: expression.name, | |
| ); | |
| case ParsedUnqualifiedNameImpl(): | |
| return ParsedUnqualifiedNameAssignmentTargetImpl(name: expression.name); | |
| case ReceiverIndexExpressionImpl(): | |
| return ReceiverIndexAssignmentTargetImpl( | |
| receiver: expression.receiver, | |
| question: expression.question, | |
| leftBracket: expression.leftBracket, | |
| index: expression.index, | |
| rightBracket: expression.rightBracket, | |
| ); | |
| case ReceiverPropertyExtractionImpl(): | |
| return ReceiverPropertyAssignmentTargetImpl( | |
| receiver: _parsedPropertyReceiver(expression.receiver), | |
| operator: expression.operator, | |
| name: expression.name, | |
| ); | |
| default: | |
| // Preserve an invalid operand as an expression so later phases can still | |
| // analyze it and provide useful recovery information. | |
| return InvalidExpressionAssignmentTargetImpl(expression: expression); | |
| } | |
| } | |
| InstanceReceiverImpl _toInstanceReceiver(InstanceReceiverImpl expression) { | |
| if (expression is InvalidSuperExpressionImpl) { | |
| return expression.superReference; | |
| } | |
| return expression; | |
| } | |
| static String _versionAsString(Version version) { | |
| return '${version.major}.${version.minor}.${version.patch}'; | |
| } | |
| } | |
| /// Data structure placed on the stack to represent a cascade expression, while | |
| /// the parser reports its sections. | |
| /// | |
| /// The [CascadeExpressionImpl] is built once, after the last section, because | |
| /// building it after each section is quadratic in the number of sections. | |
| class _CascadeBuilder { | |
| final ExpressionImpl target; | |
| final List<CascadeSectionImpl> sections = []; | |
| _CascadeBuilder(this.target); | |
| } | |
| class _ClassDeclarationBuilder extends _ClassLikeDeclarationBuilder { | |
| final Token? augmentKeyword; | |
| final Token? abstractKeyword; | |
| final Token? sealedKeyword; | |
| final Token? baseKeyword; | |
| final Token? interfaceKeyword; | |
| final Token? finalKeyword; | |
| final Token? mixinKeyword; | |
| final Token classKeyword; | |
| final _PrimaryConstructorBuilder? primaryConstructorBuilder; | |
| @override | |
| final Token name; | |
| ExtendsClauseImpl? extendsClause; | |
| WithClauseImpl? withClause; | |
| ImplementsClauseImpl? implementsClause; | |
| final NativeClauseImpl? nativeClause; | |
| _ClassDeclarationBuilder({ | |
| required super.comment, | |
| required super.metadata, | |
| required super.typeParameters, | |
| required super.leftBracket, | |
| required super.rightBracket, | |
| required this.augmentKeyword, | |
| required this.abstractKeyword, | |
| required this.sealedKeyword, | |
| required this.baseKeyword, | |
| required this.interfaceKeyword, | |
| required this.finalKeyword, | |
| required this.mixinKeyword, | |
| required this.classKeyword, | |
| required this.primaryConstructorBuilder, | |
| required this.name, | |
| required this.extendsClause, | |
| required this.withClause, | |
| required this.implementsClause, | |
| required this.nativeClause, | |
| }); | |
| ClassDeclarationImpl build() { | |
| ClassBodyImpl body; | |
| if (emptyClassBodySemicolon case var semicolon?) { | |
| body = EmptyClassBodyImpl(semicolon: semicolon); | |
| } else { | |
| body = BlockClassBodyImpl( | |
| leftBracket: leftBracket, | |
| members: members, | |
| rightBracket: rightBracket, | |
| ); | |
| } | |
| return ClassDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| abstractKeyword: abstractKeyword, | |
| sealedKeyword: sealedKeyword, | |
| baseKeyword: baseKeyword, | |
| interfaceKeyword: interfaceKeyword, | |
| finalKeyword: finalKeyword, | |
| mixinKeyword: mixinKeyword, | |
| classKeyword: classKeyword, | |
| namePart: buildClassNamePart( | |
| typeName: name, | |
| primaryConstructorBuilder: primaryConstructorBuilder, | |
| ), | |
| extendsClause: extendsClause, | |
| withClause: withClause, | |
| implementsClause: implementsClause, | |
| nativeClause: nativeClause, | |
| body: body, | |
| ); | |
| } | |
| } | |
| abstract class _ClassLikeDeclarationBuilder { | |
| final CommentImpl? comment; | |
| final List<AnnotationImpl>? metadata; | |
| final TypeParameterListImpl? typeParameters; | |
| Token? emptyClassBodySemicolon; | |
| Token leftBracket; | |
| final List<ClassMemberImpl> members = []; | |
| Token rightBracket; | |
| _ClassLikeDeclarationBuilder({ | |
| required this.comment, | |
| required this.metadata, | |
| required this.typeParameters, | |
| required this.leftBracket, | |
| required this.rightBracket, | |
| }); | |
| Token? get name; | |
| ClassNamePartImpl buildClassNamePart({ | |
| required Token typeName, | |
| required _PrimaryConstructorBuilder? primaryConstructorBuilder, | |
| }) { | |
| if (primaryConstructorBuilder case var builder?) { | |
| return builder.build(typeName: typeName, typeParameters: typeParameters); | |
| } else { | |
| return NameWithTypeParametersImpl( | |
| typeName: typeName, | |
| typeParameters: typeParameters, | |
| ); | |
| } | |
| } | |
| } | |
| class _ConstructorReferenceWithInvalidTypeArgs { | |
| final ConstructorReference2Impl reference; | |
| final TypeArgumentListImpl invalidTypeArgs; | |
| _ConstructorReferenceWithInvalidTypeArgs( | |
| this.reference, | |
| this.invalidTypeArgs, | |
| ); | |
| } | |
| class _EnumDeclarationBuilder extends _ClassLikeDeclarationBuilder { | |
| final Token? augmentKeyword; | |
| final Token enumKeyword; | |
| final _PrimaryConstructorBuilder? primaryConstructorBuilder; | |
| @override | |
| final Token name; | |
| final WithClauseImpl? withClause; | |
| final ImplementsClauseImpl? implementsClause; | |
| final List<EnumConstantDeclarationImpl> constants = []; | |
| Token? semicolon; | |
| _EnumDeclarationBuilder({ | |
| required super.comment, | |
| required super.metadata, | |
| required super.typeParameters, | |
| required super.leftBracket, | |
| required super.rightBracket, | |
| required this.augmentKeyword, | |
| required this.enumKeyword, | |
| required this.primaryConstructorBuilder, | |
| required this.name, | |
| required this.withClause, | |
| required this.implementsClause, | |
| required this.semicolon, | |
| }); | |
| EnumDeclarationImpl build() { | |
| EnumBodyImpl body; | |
| if (emptyClassBodySemicolon case var semicolon?) { | |
| body = EmptyEnumBodyImpl(semicolon: semicolon); | |
| } else { | |
| body = BlockEnumBodyImpl( | |
| leftBracket: leftBracket, | |
| constants: constants, | |
| semicolon: semicolon, | |
| members: members, | |
| rightBracket: rightBracket, | |
| ); | |
| } | |
| return EnumDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| enumKeyword: enumKeyword, | |
| namePart: buildClassNamePart( | |
| typeName: name, | |
| primaryConstructorBuilder: primaryConstructorBuilder, | |
| ), | |
| withClause: withClause, | |
| implementsClause: implementsClause, | |
| body: body, | |
| ); | |
| } | |
| } | |
| class _ExtensionDeclarationBuilder extends _ClassLikeDeclarationBuilder { | |
| final Token? augmentKeyword; | |
| final Token extensionKeyword; | |
| @override | |
| final Token? name; | |
| _ExtensionDeclarationBuilder({ | |
| required super.comment, | |
| required super.metadata, | |
| required super.typeParameters, | |
| required super.leftBracket, | |
| required super.rightBracket, | |
| required this.augmentKeyword, | |
| required this.extensionKeyword, | |
| required this.name, | |
| }); | |
| ExtensionDeclarationImpl build({ | |
| required Token? typeKeyword, | |
| required ExtensionOnClauseImpl? onClause, | |
| }) { | |
| ClassBodyImpl body; | |
| if (emptyClassBodySemicolon case var semicolon?) { | |
| body = EmptyClassBodyImpl(semicolon: semicolon); | |
| } else { | |
| body = BlockClassBodyImpl( | |
| leftBracket: leftBracket, | |
| members: members, | |
| rightBracket: rightBracket, | |
| ); | |
| } | |
| return ExtensionDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| extensionKeyword: extensionKeyword, | |
| typeKeyword: typeKeyword, | |
| name: name, | |
| typeParameters: typeParameters, | |
| onClause: onClause, | |
| body: body, | |
| ); | |
| } | |
| } | |
| class _ExtensionTypeDeclarationBuilder extends _ClassLikeDeclarationBuilder { | |
| final Token? augmentKeyword; | |
| final Token extensionKeyword; | |
| @override | |
| final Token name; | |
| _ExtensionTypeDeclarationBuilder({ | |
| required super.comment, | |
| required super.metadata, | |
| required super.typeParameters, | |
| required super.leftBracket, | |
| required super.rightBracket, | |
| required this.augmentKeyword, | |
| required this.extensionKeyword, | |
| required this.name, | |
| }); | |
| ExtensionTypeDeclarationImpl build({ | |
| required Token typeKeyword, | |
| required Token? constKeyword, | |
| required _PrimaryConstructorBuilder? primaryConstructorBuilder, | |
| required ImplementsClauseImpl? implementsClause, | |
| }) { | |
| ClassBodyImpl body; | |
| if (emptyClassBodySemicolon case var semicolon?) { | |
| body = EmptyClassBodyImpl(semicolon: semicolon); | |
| } else { | |
| body = BlockClassBodyImpl( | |
| leftBracket: leftBracket, | |
| members: members, | |
| rightBracket: rightBracket, | |
| ); | |
| } | |
| var namePart = buildClassNamePart( | |
| typeName: name, | |
| primaryConstructorBuilder: primaryConstructorBuilder, | |
| ); | |
| return ExtensionTypeDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| extensionKeyword: extensionKeyword, | |
| typeKeyword: typeKeyword, | |
| namePart: namePart, | |
| implementsClause: implementsClause, | |
| body: body, | |
| ); | |
| } | |
| } | |
| /// Data structure placed on the stack to carry function-typed parameter parts | |
| /// until the concrete formal parameter node is built. | |
| class _FunctionTypedFormalParameterData { | |
| final TypeAnnotationImpl? returnType; | |
| final FunctionTypedFormalParameterSuffixImpl functionTypedSuffix; | |
| _FunctionTypedFormalParameterData({ | |
| required this.returnType, | |
| required this.functionTypedSuffix, | |
| }); | |
| } | |
| class _MixinDeclarationBuilder extends _ClassLikeDeclarationBuilder { | |
| final Token? augmentKeyword; | |
| final Token? baseKeyword; | |
| final Token mixinKeyword; | |
| @override | |
| final Token name; | |
| MixinOnClauseImpl? onClause; | |
| ImplementsClauseImpl? implementsClause; | |
| _MixinDeclarationBuilder({ | |
| required super.comment, | |
| required super.metadata, | |
| required super.typeParameters, | |
| required super.leftBracket, | |
| required super.rightBracket, | |
| required this.augmentKeyword, | |
| required this.baseKeyword, | |
| required this.mixinKeyword, | |
| required this.name, | |
| required this.onClause, | |
| required this.implementsClause, | |
| }); | |
| MixinDeclarationImpl build() { | |
| ClassBodyImpl body; | |
| if (emptyClassBodySemicolon case var semicolon?) { | |
| body = EmptyClassBodyImpl(semicolon: semicolon); | |
| } else { | |
| body = BlockClassBodyImpl( | |
| leftBracket: leftBracket, | |
| members: members, | |
| rightBracket: rightBracket, | |
| ); | |
| } | |
| return MixinDeclarationImpl( | |
| comment: comment, | |
| metadata: metadata, | |
| augmentKeyword: augmentKeyword, | |
| baseKeyword: baseKeyword, | |
| mixinKeyword: mixinKeyword, | |
| name: name, | |
| typeParameters: typeParameters, | |
| onClause: onClause, | |
| implementsClause: implementsClause, | |
| body: body, | |
| ); | |
| } | |
| } | |
| /// Data structure placed on the stack to represent a non-empty sequence | |
| /// of modifiers. | |
| class _Modifiers { | |
| Token? abstractKeyword; | |
| Token? augmentKeyword; | |
| Token? externalKeyword; | |
| Token? finalConstOrVarKeyword; | |
| Token? newKeyword; | |
| Token? staticKeyword; | |
| Token? covariantKeyword; | |
| Token? requiredToken; | |
| Token? lateToken; | |
| /// Return the token that is lexically first. | |
| Token? get beginToken { | |
| Token? firstToken; | |
| for (Token? token in [ | |
| abstractKeyword, | |
| externalKeyword, | |
| finalConstOrVarKeyword, | |
| newKeyword, | |
| staticKeyword, | |
| covariantKeyword, | |
| requiredToken, | |
| lateToken, | |
| ]) { | |
| if (firstToken == null) { | |
| firstToken = token; | |
| } else if (token != null) { | |
| if (token.offset < firstToken.offset) { | |
| firstToken = token; | |
| } | |
| } | |
| } | |
| return firstToken; | |
| } | |
| /// Return the `const` keyword or `null`. | |
| Token? get constKeyword { | |
| return identical('const', finalConstOrVarKeyword?.lexeme) | |
| ? finalConstOrVarKeyword | |
| : null; | |
| } | |
| } | |
| /// Temporary representation of the fields of an extractor used internally by | |
| /// the [AstBuilder]. | |
| class _ObjectPatternFields { | |
| final Token leftParenthesis; | |
| final Token rightParenthesis; | |
| final List<PatternFieldImpl> fields; | |
| _ObjectPatternFields( | |
| this.leftParenthesis, | |
| this.rightParenthesis, | |
| this.fields, | |
| ); | |
| } | |
| /// Data structure placed on the stack to represent the keyword "operator" | |
| /// followed by a token. | |
| class _OperatorName { | |
| final Token operatorKeyword; | |
| final Token name; | |
| _OperatorName(this.operatorKeyword, this.name); | |
| } | |
| /// Data structure placed on the stack to represent the parenthesized condition | |
| /// part of an if-statement, if-control-flow, switch-statement, while-statement, | |
| /// or do-while-statement. | |
| class _ParenthesizedCondition { | |
| final Token leftParenthesis; | |
| final ExpressionImpl expression; | |
| final CaseClauseImpl? caseClause; | |
| _ParenthesizedCondition( | |
| this.leftParenthesis, | |
| this.expression, | |
| this.caseClause, | |
| ); | |
| Token get rightParenthesis => leftParenthesis.endGroup!; | |
| } | |
| class _PrimaryConstructorBuilder { | |
| final Token? constKeyword; | |
| final PrimaryConstructorNameImpl? constructorName; | |
| final FormalParameterListImpl formalParameterList; | |
| _PrimaryConstructorBuilder({ | |
| required this.constKeyword, | |
| required this.constructorName, | |
| required this.formalParameterList, | |
| }); | |
| PrimaryConstructorDeclarationImpl build({ | |
| required Token typeName, | |
| required TypeParameterListImpl? typeParameters, | |
| }) { | |
| return PrimaryConstructorDeclarationImpl( | |
| constKeyword: constKeyword, | |
| typeName: typeName, | |
| typeParameters: typeParameters, | |
| constructorName: constructorName, | |
| formalParameters: formalParameterList, | |
| ); | |
| } | |
| } | |
| /// A qualified name before its declaration, type, or annotation owner is built. | |
| class _QualifiedName { | |
| final Token prefix; | |
| final Token period; | |
| final Token name; | |
| _QualifiedName(this.prefix, this.period, this.name); | |
| } | |
| /// Data structure placed on stack to represent the redirected constructor. | |
| class _RedirectingFactoryBody { | |
| final Token? asyncKeyword; | |
| final Token? starKeyword; | |
| final Token equalToken; | |
| final ConstructorReference2Impl constructorReference; | |
| _RedirectingFactoryBody( | |
| this.asyncKeyword, | |
| this.starKeyword, | |
| this.equalToken, | |
| this.constructorReference, | |
| ); | |
| } |