| // 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. |
| |
| library fasta.outline_builder; |
| |
| import 'package:_fe_analyzer_shared/src/parser/parser.dart' |
| show |
| Assert, |
| ConstructorReferenceContext, |
| DeclarationHeaderKind, |
| DeclarationKind, |
| FormalParameterKind, |
| IdentifierContext, |
| MemberKind, |
| lengthOfSpan; |
| import 'package:_fe_analyzer_shared/src/parser/quote.dart' show unescapeString; |
| import 'package:_fe_analyzer_shared/src/parser/stack_listener.dart' |
| show FixedNullableList, NullValues, ParserRecovery; |
| import 'package:_fe_analyzer_shared/src/scanner/token.dart' |
| show Keyword, Token, TokenIsAExtension, TokenType; |
| import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart' |
| show Variance; |
| import 'package:_fe_analyzer_shared/src/util/link.dart'; |
| import 'package:_fe_analyzer_shared/src/util/value_kind.dart'; |
| import 'package:kernel/ast.dart' |
| show AsyncMarker, InvalidType, Nullability, ProcedureKind, TreeNode; |
| |
| import '../api_prototype/experimental_flags.dart'; |
| import '../base/combinator.dart' show CombinatorBuilder; |
| import '../base/configuration.dart' show Configuration; |
| import '../base/identifiers.dart' |
| show Identifier, OperatorIdentifier, SimpleIdentifier, flattenName; |
| import '../base/ignored_parser_errors.dart' show isIgnoredParserError; |
| import '../base/messages.dart'; |
| import '../base/modifiers.dart' show Modifiers; |
| import '../builder/constructor_reference_builder.dart'; |
| import '../builder/declaration_builders.dart'; |
| import '../builder/fixed_type_builder.dart'; |
| import '../builder/formal_parameter_builder.dart'; |
| import '../builder/invalid_type_builder.dart'; |
| import '../builder/library_builder.dart'; |
| import '../builder/metadata_builder.dart'; |
| import '../builder/mixin_application_builder.dart'; |
| import '../builder/named_type_builder.dart'; |
| import '../builder/nullability_builder.dart'; |
| import '../builder/omitted_type_builder.dart'; |
| import '../builder/record_type_builder.dart'; |
| import '../builder/type_builder.dart'; |
| import '../base/operator.dart' show Operator; |
| import '../base/problems.dart' show unhandled; |
| import '../base/uris.dart'; |
| import '../kernel/utils.dart'; |
| import 'builder_factory.dart'; |
| import 'offset_map.dart'; |
| import 'source_enum_builder.dart'; |
| import 'stack_listener_impl.dart'; |
| import 'value_kinds.dart'; |
| |
| enum MethodBody { |
| Abstract, |
| Regular, |
| RedirectingFactoryBody, |
| } |
| |
| /// Enum for the context in which declarations occur. |
| /// |
| /// This is used to determine whether instance type variables access is allowed. |
| enum DeclarationContext { |
| /// In the context of the enclosing library. |
| /// |
| /// This is used for library, import, export, part, and part of declarations |
| /// in libraries and parts, as well as annotations on top level declarations. |
| Library, |
| |
| /// In a typedef declaration |
| /// |
| /// This excludes annotations on the typedef declaration itself, which are |
| /// seen in the [Library] context. |
| Typedef, |
| |
| /// In an enum declaration |
| /// |
| /// This excludes annotations on the enum declaration itself, which are seen |
| /// in the [Library] context. |
| Enum, |
| |
| /// In a top level method declaration. |
| /// |
| /// This includes return type of the declaration but excludes annotations on |
| /// the method declaration itself, which are seen in the [Library] context. |
| TopLevelMethod, |
| |
| /// In a top level field declaration. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [Library] context. |
| TopLevelField, |
| |
| /// In a `class Name<TypeParams>` or `mixin Name<TypeParams>` prefix of a |
| /// class declaration `class Name<TypeParams> ... { ... }`, mixin declaration |
| /// `mixin Name<TypeParams> ... { ... }` or named mixin application |
| /// `class Name<TypeParams> = ...;`. |
| /// |
| /// This is replaced by [Class], [Mixin] or [NamedMixinApplication] after the |
| /// type parameters have been parsed. |
| ClassOrMixinOrNamedMixinApplication, |
| |
| /// In a named mixin application. |
| /// |
| /// This excludes type parameters declared on the named mixin application, |
| /// which are seen in the [ClassOrMixinOrNamedMixinApplication] context, |
| /// and annotations on the named mixin application itself, which are seen in |
| /// the [Library] context. |
| NamedMixinApplication, |
| |
| /// In a class declaration before the class body. |
| /// |
| /// This excludes type parameters declared on the class declaration, which are |
| /// seen in the [ClassOrMixinOrNamedMixinApplication] context, and annotations |
| /// on the class declaration itself, which are seen in the [Library] context. |
| Class, |
| |
| /// In a class declaration body. |
| /// |
| /// This includes annotations on class member declarations. |
| ClassBody, |
| |
| /// In a generative constructor declaration inside a class declaration. |
| /// |
| /// This excludes annotations on the constructor declaration itself, which |
| /// are seen in the [ClassBody] context. |
| ClassConstructor, |
| |
| /// In a factory constructor declaration inside a class declaration. |
| /// |
| /// This excludes annotations on the constructor declaration itself, which |
| /// are seen in the [ClassBody] context. |
| ClassFactory, |
| |
| /// In an instance method declaration inside a class declaration. |
| /// |
| /// This includes return type of the declaration but excludes annotations on |
| /// the method declaration itself, which are seen in the [ClassBody] context. |
| ClassInstanceMethod, |
| |
| /// In an instance field declaration inside a class declaration. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [ClassBody] context. |
| ClassInstanceField, |
| |
| /// In a static method declaration inside a class declaration. |
| /// |
| /// This includes return type of the declaration but excludes annotations on |
| /// the method declaration itself, which are seen in the [ClassBody] context. |
| ClassStaticMethod, |
| |
| /// In a static field declaration inside a class declaration. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [ClassBody] context. |
| ClassStaticField, |
| |
| /// In a mixin declaration before the mixin body. |
| /// |
| /// This excludes type parameters declared on the mixin declaration, which are |
| /// seen in the [ClassOrMixinOrNamedMixinApplication] context, and annotations |
| /// on the mixin declaration itself, which are seen in the [Library] context. |
| Mixin, |
| |
| /// In a mixin declaration body. |
| /// |
| /// This includes annotations on mixin member declarations. |
| MixinBody, |
| |
| /// In a generative constructor declaration inside a mixin declaration. This |
| /// is an error case. |
| /// |
| /// This excludes annotations on the constructor declaration itself, which |
| /// are seen in the [MixinBody] context. |
| MixinConstructor, |
| |
| /// In a factory constructor declaration inside a mixin declaration. This is |
| /// an error case. |
| /// |
| /// This excludes annotations on the constructor declaration itself, which |
| /// are seen in the [MixinBody] context. |
| MixinFactory, |
| |
| /// In an instance method declaration inside a mixin declaration. |
| /// |
| /// This includes return type of the declaration but excludes annotations on |
| /// the method declaration itself, which are seen in the [MixinBody] context. |
| MixinInstanceMethod, |
| |
| /// In an instance field declaration inside a mixin declaration. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [MixinBody] context. |
| MixinInstanceField, |
| |
| /// In a static method declaration inside a mixin declaration. |
| /// |
| /// This includes return type of the declaration but excludes annotations on |
| /// the method declaration itself, which are seen in the [MixinBody] context. |
| MixinStaticMethod, |
| |
| /// In a static field declaration inside a mixin declaration. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [MixinBody] context. |
| MixinStaticField, |
| |
| /// In an extension declaration before the extension body. |
| /// |
| /// This includes type parameters declared on the extension declaration but |
| /// excludes annotations on the extension declaration itself, which are seen |
| /// in the [Library] context. |
| ExtensionOrExtensionType, |
| |
| /// In an extension declaration before the extension body. |
| /// |
| /// This includes type parameters declared on the extension declaration but |
| /// excludes annotations on the extension declaration itself, which are seen |
| /// in the [Library] context. |
| Extension, |
| |
| /// In a extension declaration body. |
| /// |
| /// This includes annotations on extension member declarations. |
| ExtensionBody, |
| |
| /// In a generative constructor declaration inside an extension declaration. |
| /// This is an error case. |
| /// |
| /// This excludes annotations on the constructor declaration itself, which |
| /// are seen in the [ExtensionBody] context. |
| ExtensionConstructor, |
| |
| /// In a factory constructor declaration inside an extension declaration. This |
| /// is an error case. |
| /// |
| /// This excludes annotations on the constructor declaration itself, which |
| /// are seen in the [ExtensionBody] context. |
| ExtensionFactory, |
| |
| /// In an instance method declaration inside an extension declaration. |
| /// |
| /// This includes return type of the declaration but excludes annotations on |
| /// the method declaration itself, which are seen in the [ExtensionBody] |
| /// context. |
| ExtensionInstanceMethod, |
| |
| /// In a non-external instance field declaration inside an extension |
| /// declaration. This is an error case. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [ExtensionBody] context. |
| ExtensionInstanceField, |
| |
| /// In an external instance field declaration inside an extension declaration. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [ExtensionBody] context. |
| ExtensionExternalInstanceField, |
| |
| /// In a static method declaration inside an extension declaration. |
| /// |
| /// This includes return type of the declaration but excludes annotations on |
| /// the method declaration itself, which are seen in the [ExtensionBody] |
| /// context. |
| ExtensionStaticMethod, |
| |
| /// In a static field declaration inside an extension declaration. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [ExtensionBody] context. |
| ExtensionStaticField, |
| |
| /// In an extension type declaration before the extension type body. |
| /// |
| /// This includes type parameters declared on the extension type declaration |
| /// but excludes annotations on the extension type declaration itself, which |
| /// are seen in the [Library] context. |
| ExtensionType, |
| |
| /// In a extension type declaration body. |
| /// |
| /// This includes annotations on extension type member declarations. |
| ExtensionTypeBody, |
| |
| /// In a generative constructor declaration inside an extension type |
| /// declaration. |
| /// |
| /// This excludes annotations on the constructor declaration itself, which |
| /// are seen in the [ExtensionTypeBody] context. |
| ExtensionTypeConstructor, |
| |
| /// In a factory constructor declaration inside an extension type declaration. |
| /// |
| /// This excludes annotations on the constructor declaration itself, which |
| /// are seen in the [ExtensionTypeBody] context. |
| ExtensionTypeFactory, |
| |
| /// In an instance method declaration inside an extension type declaration. |
| /// |
| /// This includes return type of the declaration but excludes annotations on |
| /// the method declaration itself, which are seen in the [ExtensionTypeBody] |
| /// context. |
| ExtensionTypeInstanceMethod, |
| |
| /// In an instance field declaration inside an extension type declaration. |
| /// This is an error case. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [ExtensionTypeBody] |
| /// context. |
| ExtensionTypeInstanceField, |
| |
| /// In a static method declaration inside an extension type declaration. |
| /// |
| /// This includes return type of the declaration but excludes annotations on |
| /// the method declaration itself, which are seen in the [ExtensionTypeBody] |
| /// context. |
| ExtensionTypeStaticMethod, |
| |
| /// In a static field declaration inside an extension type declaration. |
| /// |
| /// This includes type of the declaration but excludes annotations on the |
| /// field declaration itself, which are seen in the [ExtensionTypeBody] |
| /// context. |
| ExtensionTypeStaticField, |
| |
| /// In a generative constructor declaration inside an enum declaration. |
| EnumConstructor, |
| |
| /// In a static method declaration inside an enum declaration. |
| EnumStaticMethod, |
| |
| /// In a static field declaration inside an enum declaration. |
| EnumStaticField, |
| |
| /// In an instance method declaration inside an enum declaration. |
| EnumInstanceMethod, |
| |
| /// In an instance field declaration inside an enum declaration. |
| EnumInstanceField, |
| |
| /// In a factory constructor declaration inside an enum declaration. This |
| /// is an error case. |
| /// |
| /// This excludes annotations on the constructor declaration itself, which |
| /// are seen in the [EnumBody] context. |
| EnumFactory, |
| |
| /// In an enum declaration body. |
| /// |
| /// This includes annotations on extension member declarations. |
| EnumBody, |
| } |
| |
| extension on DeclarationContext { |
| InstanceTypeVariableAccessState get instanceTypeVariableAccessState { |
| switch (this) { |
| case DeclarationContext.Library: |
| case DeclarationContext.Typedef: |
| case DeclarationContext.TopLevelMethod: |
| case DeclarationContext.TopLevelField: |
| return InstanceTypeVariableAccessState.Unexpected; |
| case DeclarationContext.ClassOrMixinOrNamedMixinApplication: |
| case DeclarationContext.NamedMixinApplication: |
| case DeclarationContext.Class: |
| case DeclarationContext.ClassConstructor: |
| case DeclarationContext.ClassFactory: |
| case DeclarationContext.ClassInstanceMethod: |
| case DeclarationContext.ClassInstanceField: |
| case DeclarationContext.Enum: |
| case DeclarationContext.EnumConstructor: |
| case DeclarationContext.EnumInstanceField: |
| case DeclarationContext.EnumInstanceMethod: |
| case DeclarationContext.Mixin: |
| case DeclarationContext.MixinInstanceMethod: |
| case DeclarationContext.MixinInstanceField: |
| case DeclarationContext.ExtensionOrExtensionType: |
| case DeclarationContext.Extension: |
| case DeclarationContext.ExtensionInstanceMethod: |
| case DeclarationContext.ExtensionExternalInstanceField: |
| case DeclarationContext.ExtensionType: |
| case DeclarationContext.ExtensionTypeConstructor: |
| case DeclarationContext.ExtensionTypeFactory: |
| case DeclarationContext.ExtensionTypeInstanceMethod: |
| case DeclarationContext.ExtensionTypeInstanceField: |
| return InstanceTypeVariableAccessState.Allowed; |
| case DeclarationContext.ClassBody: |
| case DeclarationContext.ClassStaticMethod: |
| case DeclarationContext.ClassStaticField: |
| case DeclarationContext.EnumStaticField: |
| case DeclarationContext.EnumStaticMethod: |
| case DeclarationContext.EnumBody: |
| case DeclarationContext.MixinBody: |
| case DeclarationContext.MixinStaticMethod: |
| case DeclarationContext.MixinStaticField: |
| case DeclarationContext.ExtensionBody: |
| case DeclarationContext.ExtensionStaticMethod: |
| case DeclarationContext.ExtensionStaticField: |
| case DeclarationContext.ExtensionTypeBody: |
| case DeclarationContext.ExtensionTypeStaticMethod: |
| case DeclarationContext.ExtensionTypeStaticField: |
| return InstanceTypeVariableAccessState.Disallowed; |
| case DeclarationContext.MixinConstructor: |
| case DeclarationContext.MixinFactory: |
| case DeclarationContext.ExtensionConstructor: |
| case DeclarationContext.ExtensionFactory: |
| case DeclarationContext.ExtensionInstanceField: |
| case DeclarationContext.EnumFactory: |
| return InstanceTypeVariableAccessState.Invalid; |
| } |
| } |
| |
| /// Returns the kind of type variable created in the current context. |
| TypeVariableKind get typeVariableKind { |
| switch (this) { |
| case DeclarationContext.Class: |
| case DeclarationContext.ClassOrMixinOrNamedMixinApplication: |
| case DeclarationContext.Mixin: |
| case DeclarationContext.NamedMixinApplication: |
| return TypeVariableKind.classMixinOrEnum; |
| case DeclarationContext.ExtensionOrExtensionType: |
| case DeclarationContext.Extension: |
| case DeclarationContext.ExtensionBody: |
| case DeclarationContext.ExtensionType: |
| case DeclarationContext.ExtensionTypeBody: |
| return TypeVariableKind.extensionOrExtensionType; |
| case DeclarationContext.ClassBody: |
| case DeclarationContext.ClassConstructor: |
| case DeclarationContext.ClassFactory: |
| case DeclarationContext.ClassInstanceField: |
| case DeclarationContext.ClassInstanceMethod: |
| case DeclarationContext.ClassStaticField: |
| case DeclarationContext.ClassStaticMethod: |
| case DeclarationContext.Enum: |
| case DeclarationContext.EnumBody: |
| case DeclarationContext.EnumConstructor: |
| case DeclarationContext.EnumFactory: |
| case DeclarationContext.EnumInstanceField: |
| case DeclarationContext.EnumInstanceMethod: |
| case DeclarationContext.EnumStaticField: |
| case DeclarationContext.EnumStaticMethod: |
| case DeclarationContext.ExtensionConstructor: |
| case DeclarationContext.ExtensionExternalInstanceField: |
| case DeclarationContext.ExtensionFactory: |
| case DeclarationContext.ExtensionInstanceField: |
| case DeclarationContext.ExtensionInstanceMethod: |
| case DeclarationContext.ExtensionStaticField: |
| case DeclarationContext.ExtensionStaticMethod: |
| case DeclarationContext.ExtensionTypeConstructor: |
| case DeclarationContext.ExtensionTypeFactory: |
| case DeclarationContext.ExtensionTypeInstanceField: |
| case DeclarationContext.ExtensionTypeInstanceMethod: |
| case DeclarationContext.ExtensionTypeStaticField: |
| case DeclarationContext.ExtensionTypeStaticMethod: |
| case DeclarationContext.Library: |
| case DeclarationContext.MixinBody: |
| case DeclarationContext.MixinConstructor: |
| case DeclarationContext.MixinFactory: |
| case DeclarationContext.MixinInstanceField: |
| case DeclarationContext.MixinInstanceMethod: |
| case DeclarationContext.MixinStaticField: |
| case DeclarationContext.MixinStaticMethod: |
| case DeclarationContext.TopLevelField: |
| case DeclarationContext.TopLevelMethod: |
| case DeclarationContext.Typedef: |
| return TypeVariableKind.function; |
| } |
| } |
| } |
| |
| class OutlineBuilder extends StackListenerImpl { |
| final SourceCompilationUnit _compilationUnit; |
| final BuilderFactory _builderFactory; |
| |
| final bool enableNative; |
| bool inAbstractOrSealedClass = false; |
| bool inConstructor = false; |
| bool inConstructorName = false; |
| |
| String? nativeMethodName; |
| |
| Link<DeclarationContext> _declarationContext = const Link(); |
| |
| /// Level of nesting of function-type type parameters |
| /// |
| /// For instance, `X` is at nesting level 1, and `Y` is at nesting level 2 in |
| /// the following: |
| /// |
| /// method() { |
| /// Function<X>(Function<Y extends X>(Y))? f; |
| /// } |
| /// |
| /// For simplicity, non-generic functions are considered generic functions |
| /// with 0 type parameters. |
| int _structuralParameterDepthLevel = 0; |
| |
| /// True if a type of a formal parameter is currently compiled |
| /// |
| /// This variable is needed to distinguish between the type of a formal |
| /// parameter and its initializer because in those two regions of code the |
| /// type variables should be interpreted differently: as structural and |
| /// nominal correspondingly. |
| bool _insideOfFormalParameterType = false; |
| |
| bool get inFunctionType => |
| _structuralParameterDepthLevel > 0 || _insideOfFormalParameterType; |
| |
| OffsetMap _offsetMap; |
| |
| OutlineBuilder(this._compilationUnit, this._builderFactory, this._offsetMap) |
| : enableNative = _compilationUnit.loader.target.backendTarget |
| .enableNative(_compilationUnit.importUri); |
| |
| @override |
| LibraryFeatures get libraryFeatures => _compilationUnit.libraryFeatures; |
| |
| @override |
| bool get isDartLibrary => _compilationUnit.isDartLibrary; |
| |
| @override |
| Message reportFeatureNotEnabled( |
| LibraryFeature feature, int charOffset, int length) { |
| return _compilationUnit.reportFeatureNotEnabled( |
| feature, uri, charOffset, length); |
| } |
| |
| DeclarationContext get declarationContext => _declarationContext.head; |
| |
| void pushDeclarationContext(DeclarationContext value) { |
| _declarationContext = _declarationContext.prepend(value); |
| } |
| |
| void popDeclarationContext([DeclarationContext? expectedContext]) { |
| assert( |
| expectedContext == null || expectedContext == declarationContext, |
| "Unexpected declaration context: " |
| "Expected $expectedContext, actual $declarationContext."); |
| _declarationContext = _declarationContext.tail!; |
| } |
| |
| @override |
| Uri get uri => _compilationUnit.fileUri; |
| |
| int popCharOffset() => pop() as int; |
| |
| List<String>? popIdentifierList(int count) { |
| assert(checkState( |
| null, repeatedKind(ValueKinds.IdentifierOrParserRecovery, count))); |
| if (count == 0) return null; |
| List<String> list = new List<String>.filled(count, /* dummyValue = */ ''); |
| bool isParserRecovery = false; |
| for (int i = count - 1; i >= 0; i--) { |
| Object? identifier = pop(); |
| if (identifier is ParserRecovery) { |
| isParserRecovery = true; |
| } else { |
| list[i] = (identifier as Identifier).name; |
| } |
| } |
| return isParserRecovery ? null : list; |
| } |
| |
| @override |
| void beginCompilationUnit(Token token) { |
| pushDeclarationContext(DeclarationContext.Library); |
| } |
| |
| @override |
| void endCompilationUnit(int count, Token token) { |
| popDeclarationContext(DeclarationContext.Library); |
| _builderFactory.checkStacks(); |
| super.endCompilationUnit(count, token); |
| } |
| |
| @override |
| void endMetadata(Token beginToken, Token? periodBeforeName, Token endToken) { |
| debugEvent("endMetadata"); |
| assert(checkState(beginToken, [ |
| /* arguments */ ValueKinds.ArgumentsTokenOrNull, |
| if (periodBeforeName != null) /* constructor name */ |
| ValueKinds.IdentifierOrParserRecovery, |
| /* type arguments */ ValueKinds.TypeArgumentsOrNull, |
| /* prefix or constructor */ ValueKinds.IdentifierOrParserRecovery, |
| ])); |
| |
| pop(NullValues.Arguments); // arguments |
| if (periodBeforeName != null) { |
| pop(); // constructor name |
| } |
| pop(NullValues.TypeArguments); // type arguments |
| Object? sentinel = pop(); // prefix or constructor |
| push(sentinel is ParserRecovery |
| ? sentinel |
| : new MetadataBuilder(beginToken)); |
| } |
| |
| @override |
| void endMetadataStar(int count) { |
| debugEvent("MetadataStar"); |
| push(const FixedNullableList<MetadataBuilder>() |
| .popNonNullable(stack, count, dummyMetadataBuilder) ?? |
| NullValues.Metadata); |
| } |
| |
| @override |
| void handleInvalidTopLevelDeclaration(Token endToken) { |
| debugEvent("InvalidTopLevelDeclaration"); |
| pop(); // metadata star |
| } |
| |
| @override |
| void endHide(Token hideKeyword) { |
| debugEvent("endHide"); |
| assert(checkState(hideKeyword, [ |
| ValueKinds.NameListOrParserRecovery, |
| ])); |
| |
| Object? names = pop(); |
| if (names is ParserRecovery) { |
| // Coverage-ignore-block(suite): Not run. |
| push(names); |
| } else { |
| push(new CombinatorBuilder.hide(names as Iterable<String>, |
| hideKeyword.charOffset, _compilationUnit.fileUri)); |
| } |
| } |
| |
| @override |
| void endShow(Token showKeyword) { |
| debugEvent("Show"); |
| Object? names = pop(); |
| if (names is ParserRecovery) { |
| // Coverage-ignore-block(suite): Not run. |
| push(names); |
| } else { |
| push(new CombinatorBuilder.show(names as Iterable<String>, |
| showKeyword.charOffset, _compilationUnit.fileUri)); |
| } |
| } |
| |
| @override |
| void endCombinators(int count) { |
| debugEvent("Combinators"); |
| push(const FixedNullableList<CombinatorBuilder>() |
| .popNonNullable(stack, count, dummyCombinator) ?? |
| NullValues.Combinators); |
| } |
| |
| @override |
| void endExport(Token exportKeyword, Token semicolon) { |
| debugEvent("endExport"); |
| assert(checkState(exportKeyword, [ |
| /* show / hide combinators */ ValueKinds.CombinatorListOrNull, |
| /* configurations */ ValueKinds.ConfigurationListOrNull, |
| /* uri offset */ ValueKinds.Integer, |
| /* uri */ ValueKinds.Name, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| |
| List<CombinatorBuilder>? combinators = |
| pop(NullValues.Combinators) as List<CombinatorBuilder>?; |
| List<Configuration>? configurations = |
| pop(NullValues.ConditionalUris) as List<Configuration>?; |
| int uriOffset = popCharOffset(); |
| String uri = pop() as String; |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| _builderFactory.addExport(_offsetMap, exportKeyword, metadata, uri, |
| configurations, combinators, exportKeyword.charOffset, uriOffset); |
| checkEmpty(exportKeyword.charOffset); |
| } |
| |
| @override |
| void handleImportPrefix(Token? deferredKeyword, Token? asKeyword) { |
| debugEvent("handleImportPrefix"); |
| 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(deferredKeyword != null); |
| } |
| |
| @override |
| void endImport(Token importKeyword, Token? augmentToken, Token? semicolon) { |
| debugEvent("endImport"); |
| assert(checkState(importKeyword, [ |
| /* show / hide combinators */ ValueKinds.CombinatorListOrNull, |
| /* is deferred */ ValueKinds.Bool, |
| /* prefix */ ValueKinds.PrefixOrParserRecoveryOrNull, |
| /* configurations */ ValueKinds.ConfigurationListOrNull, |
| /* uri offset */ ValueKinds.Integer, |
| /* uri */ ValueKinds.Name, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| |
| List<CombinatorBuilder>? combinators = |
| pop(NullValues.Combinators) as List<CombinatorBuilder>?; |
| bool isDeferred = pop() as bool; |
| Object? prefix = pop(NullValues.Prefix); |
| List<Configuration>? configurations = |
| pop(NullValues.ConditionalUris) as List<Configuration>?; |
| int uriOffset = popCharOffset(); |
| String uri = |
| pop() as String; // For a conditional import, this is the default URI. |
| List<MetadataBuilder>? metadata = |
| pop(NullValues.Metadata) as List<MetadataBuilder>?; |
| checkEmpty(importKeyword.charOffset); |
| if (prefix is! Identifier?) { |
| assert(prefix is ParserRecovery, |
| "Unexpected prefix $prefix (${prefix.runtimeType})."); |
| return; |
| } |
| |
| if (augmentToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.macros, augmentToken.charOffset, |
| augmentToken.length)) { |
| augmentToken = null; |
| } |
| } |
| bool isAugmentationImport = augmentToken != null; |
| _builderFactory.addImport( |
| offsetMap: _offsetMap, |
| importKeyword: importKeyword, |
| metadata: metadata, |
| isAugmentationImport: isAugmentationImport, |
| uri: uri, |
| configurations: configurations, |
| prefix: prefix?.name, |
| combinators: combinators, |
| deferred: isDeferred, |
| charOffset: importKeyword.charOffset, |
| prefixCharOffset: prefix?.nameOffset ?? TreeNode.noOffset, |
| uriOffset: uriOffset); |
| } |
| |
| @override |
| void endConditionalUris(int count) { |
| debugEvent("endConditionalUris"); |
| push(const FixedNullableList<Configuration>() |
| .popNonNullable(stack, count, dummyConfiguration) ?? |
| NullValues.ConditionalUris); |
| } |
| |
| @override |
| void endConditionalUri(Token ifKeyword, Token leftParen, Token? equalSign) { |
| debugEvent("EndConditionalUri"); |
| int charOffset = popCharOffset(); |
| String uri = pop() as String; |
| if (equalSign != null) { |
| // Coverage-ignore-block(suite): Not run. |
| popCharOffset(); |
| } |
| String condition = popIfNotNull(equalSign) as String? ?? "true"; |
| Object? dottedName = pop(); |
| if (dottedName is ParserRecovery) { |
| // Coverage-ignore-block(suite): Not run. |
| push(dottedName); |
| } else { |
| push(new Configuration(charOffset, dottedName as String, condition, uri)); |
| } |
| } |
| |
| @override |
| void handleDottedName(int count, Token firstIdentifier) { |
| debugEvent("DottedName"); |
| assert(checkState(firstIdentifier, |
| repeatedKind(ValueKinds.IdentifierOrParserRecovery, count))); |
| |
| List<String>? names = popIdentifierList(count); |
| if (names == null) { |
| // Coverage-ignore-block(suite): Not run. |
| push(new ParserRecovery(firstIdentifier.charOffset)); |
| } else { |
| push(names.join('.')); |
| } |
| } |
| |
| @override |
| void handleRecoverImport(Token? semicolon) { |
| debugEvent("handleRecoverImport"); |
| assert(checkState(semicolon, [ |
| /* show / hide combinators */ ValueKinds.CombinatorListOrNull, |
| /* is deferred */ ValueKinds.Bool, |
| /* prefix */ ValueKinds.PrefixOrParserRecoveryOrNull, |
| /* configurations */ ValueKinds.ConfigurationListOrNull, |
| ])); |
| |
| pop(NullValues.Combinators); // combinators |
| pop(NullValues.Deferred); // deferredKeyword |
| pop(NullValues.Prefix); // prefix |
| pop(NullValues.ConditionalUris); // conditionalUris |
| } |
| |
| @override |
| void endPart(Token partKeyword, Token semicolon) { |
| debugEvent("endPart"); |
| assert(checkState(partKeyword, [ |
| /* uri string */ ValueKinds.ConfigurationListOrNull, |
| /* offset */ ValueKinds.Integer, |
| /* uri string */ ValueKinds.String, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| |
| pop(); // configurations |
| int charOffset = popCharOffset(); |
| String uri = pop() as String; |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| _builderFactory.addPart(_offsetMap, partKeyword, metadata, uri, charOffset); |
| checkEmpty(partKeyword.charOffset); |
| } |
| |
| @override |
| void handleOperatorName(Token operatorKeyword, Token token) { |
| debugEvent("handleOperatorName"); |
| push(new OperatorIdentifier(token)); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void handleInvalidOperatorName(Token operatorKeyword, Token token) { |
| debugEvent("handleInvalidOperatorName"); |
| push(new SimpleIdentifier(token)); |
| } |
| |
| @override |
| void handleIdentifier(Token token, IdentifierContext context) { |
| debugEvent("handleIdentifier"); |
| if (context == IdentifierContext.enumValueDeclaration) { |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| if (token.isSynthetic) { |
| push(new ParserRecovery(token.charOffset)); |
| } else { |
| push(new EnumConstantInfo(metadata, token.lexeme, token.charOffset)); |
| } |
| } else { |
| if (!token.isSynthetic) { |
| push(new SimpleIdentifier(token)); |
| } else { |
| // This comes from a synthetic token which is inserted by the parser in |
| // an attempt to recover. This almost always means that the parser has |
| // gotten very confused and we need to ignore the results. |
| push(new ParserRecovery(token.charOffset)); |
| } |
| } |
| if (inConstructor && context == IdentifierContext.methodDeclaration) { |
| inConstructorName = true; |
| } |
| } |
| |
| @override |
| void handleStringPart(Token token) { |
| debugEvent("StringPart"); |
| // Ignore string parts - report error later. |
| } |
| |
| @override |
| void endLiteralString(int interpolationCount, Token endToken) { |
| debugEvent("endLiteralString"); |
| if (interpolationCount == 0) { |
| Token token = pop() as Token; |
| push(unescapeString(token.lexeme, token, this)); |
| push(token.charOffset); |
| } else { |
| Token beginToken = pop() as Token; |
| int charOffset = beginToken.charOffset; |
| push("${MALFORMED_URI_SCHEME}:bad${charOffset}"); |
| push(charOffset); |
| // Point to dollar sign |
| int interpolationOffset = charOffset + beginToken.lexeme.length; |
| addProblem(messageInterpolationInUri, interpolationOffset, 1); |
| } |
| } |
| |
| @override |
| void handleNativeClause(Token nativeToken, bool hasName) { |
| debugEvent("NativeClause"); |
| if (hasName) { |
| // Pop the native clause which in this case is a StringLiteral. |
| pop(); // Char offset. |
| Object? name = pop(); |
| if (name is ParserRecovery) { |
| // Coverage-ignore-block(suite): Not run. |
| nativeMethodName = ''; |
| } else { |
| nativeMethodName = name as String; // String. |
| } |
| } else { |
| nativeMethodName = ''; |
| } |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void handleStringJuxtaposition(Token startToken, int literalCount) { |
| debugEvent("StringJuxtaposition"); |
| List<String> list = |
| new List<String>.filled(literalCount, /* dummyValue = */ ''); |
| int charOffset = -1; |
| for (int i = literalCount - 1; i >= 0; i--) { |
| charOffset = popCharOffset(); |
| list[i] = pop() as String; |
| } |
| push(list.join("")); |
| push(charOffset); |
| } |
| |
| @override |
| void handleIdentifierList(int count) { |
| debugEvent("endIdentifierList"); |
| assert(checkState( |
| null, repeatedKind(ValueKinds.IdentifierOrParserRecovery, count))); |
| push(popIdentifierList(count) ?? |
| // Coverage-ignore(suite): Not run. |
| (count == 0 ? NullValues.IdentifierList : new ParserRecovery(-1))); |
| } |
| |
| @override |
| void handleQualified(Token period) { |
| assert(checkState(period, [ |
| /*suffix*/ ValueKinds.IdentifierOrParserRecovery, |
| /*prefix*/ ValueKinds.IdentifierOrParserRecovery, |
| ])); |
| debugEvent("handleQualified"); |
| Object? suffix = pop(); |
| Object prefix = pop()!; |
| if (prefix is! Identifier) { |
| // Coverage-ignore-block(suite): Not run. |
| assert(prefix is ParserRecovery, |
| "Unexpected prefix $prefix (${prefix.runtimeType})"); |
| push(prefix); |
| } else if (suffix is! SimpleIdentifier) { |
| assert(suffix is ParserRecovery, |
| "Unexpected suffix $suffix (${suffix.runtimeType})"); |
| push(suffix); |
| } else { |
| push(suffix.withIdentifierQualifier(prefix)); |
| } |
| } |
| |
| @override |
| void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) { |
| debugEvent("endLibraryName"); |
| assert(checkState(libraryKeyword, [ |
| if (hasName) ValueKinds.IdentifierOrParserRecovery, |
| ValueKinds.MetadataListOrNull, |
| ])); |
| Object? name = null; |
| if (hasName) { |
| name = pop(); |
| } |
| String? libraryName; |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| if (name != null && name is! ParserRecovery) { |
| libraryName = |
| flattenName(name as Identifier, offsetForToken(libraryKeyword), uri); |
| } else { |
| reportIfNotEnabled( |
| libraryFeatures.unnamedLibraries, semicolon.charOffset, noLength); |
| } |
| _builderFactory.addLibraryDirective( |
| libraryName: libraryName, metadata: metadata, isAugment: false); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void endLibraryAugmentation( |
| Token augmentKeyword, Token libraryKeyword, Token semicolon) { |
| debugEvent("endLibraryAugmentation"); |
| assert(checkState(libraryKeyword, [ |
| /* uri offset */ ValueKinds.Integer, |
| /* uri string */ ValueKinds.String, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| // TODO(johnniwinther): Pass uri to [libraryBuilder] and verify it. |
| pop() as int; |
| pop() as String; |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| _builderFactory.addLibraryDirective( |
| libraryName: null, metadata: metadata, isAugment: true); |
| } |
| |
| @override |
| void beginClassOrMixinOrNamedMixinApplicationPrelude(Token token) { |
| debugEvent("beginClassOrNamedMixinApplicationPrelude"); |
| pushDeclarationContext( |
| DeclarationContext.ClassOrMixinOrNamedMixinApplication); |
| _builderFactory.beginClassOrNamedMixinApplicationHeader(); |
| } |
| |
| @override |
| void beginClassDeclaration( |
| Token begin, |
| Token? abstractToken, |
| Token? macroToken, |
| Token? sealedToken, |
| Token? baseToken, |
| Token? interfaceToken, |
| Token? finalToken, |
| Token? augmentToken, |
| Token? mixinToken, |
| Token name) { |
| debugEvent("beginClassDeclaration"); |
| popDeclarationContext( |
| DeclarationContext.ClassOrMixinOrNamedMixinApplication); |
| pushDeclarationContext(DeclarationContext.Class); |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| push(typeVariables ?? NullValues.NominalVariables); |
| if (macroToken != null) { |
| if (reportIfNotEnabled( |
| libraryFeatures.macros, macroToken.charOffset, macroToken.length)) { |
| macroToken = null; |
| } |
| } |
| if (sealedToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.sealedClass, |
| sealedToken.charOffset, sealedToken.length)) { |
| sealedToken = null; |
| } |
| } |
| if (baseToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.classModifiers, |
| baseToken.charOffset, baseToken.length)) { |
| baseToken = null; |
| } |
| } |
| if (interfaceToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.classModifiers, |
| interfaceToken.charOffset, interfaceToken.length)) { |
| interfaceToken = null; |
| } |
| } |
| if (finalToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.classModifiers, |
| finalToken.charOffset, finalToken.length)) { |
| finalToken = null; |
| } |
| } |
| if (mixinToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.classModifiers, |
| mixinToken.charOffset, mixinToken.length)) { |
| mixinToken = null; |
| } |
| } |
| _builderFactory.beginClassDeclaration( |
| name.lexeme, name.charOffset, typeVariables); |
| Modifiers modifiers = Modifiers.from( |
| abstractToken: abstractToken, |
| macroToken: macroToken, |
| sealedToken: sealedToken, |
| baseToken: baseToken, |
| interfaceToken: interfaceToken, |
| finalToken: finalToken, |
| augmentToken: augmentToken, |
| mixinToken: mixinToken); |
| |
| inAbstractOrSealedClass = modifiers.isAbstract || modifiers.isSealed; |
| push(modifiers); |
| } |
| |
| @override |
| void beginMixinDeclaration(Token beginToken, Token? augmentToken, |
| Token? baseToken, Token mixinKeyword, Token name) { |
| debugEvent("beginMixinDeclaration"); |
| popDeclarationContext( |
| DeclarationContext.ClassOrMixinOrNamedMixinApplication); |
| pushDeclarationContext(DeclarationContext.Mixin); |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| if (baseToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.classModifiers, |
| baseToken.charOffset, baseToken.length)) { |
| baseToken = null; |
| } |
| } |
| Modifiers modifiers = |
| Modifiers.from(augmentToken: augmentToken, baseToken: baseToken); |
| push(modifiers); |
| push(typeVariables ?? NullValues.NominalVariables); |
| _builderFactory.beginMixinDeclaration( |
| name.lexeme, name.charOffset, typeVariables); |
| } |
| |
| @override |
| void beginClassOrMixinOrExtensionBody(DeclarationKind kind, Token token) { |
| DeclarationContext declarationContext; |
| switch (kind) { |
| case DeclarationKind.TopLevel: |
| // Coverage-ignore(suite): Not run. |
| throw new UnsupportedError('Unexpected top level body.'); |
| case DeclarationKind.Class: |
| declarationContext = DeclarationContext.ClassBody; |
| _builderFactory.beginClassBody(); |
| break; |
| case DeclarationKind.Mixin: |
| declarationContext = DeclarationContext.MixinBody; |
| _builderFactory.beginMixinBody(); |
| break; |
| case DeclarationKind.Extension: |
| declarationContext = DeclarationContext.ExtensionBody; |
| assert(checkState(token, [ |
| unionOfKinds([ValueKinds.ParserRecovery, ValueKinds.TypeBuilder]) |
| ])); |
| |
| Object? extensionThisType = peek(); |
| // TODO(johnniwinther): Supply an invalid type as the extension on type. |
| _builderFactory.beginExtensionBody( |
| extensionThisType is TypeBuilder ? extensionThisType : null); |
| break; |
| case DeclarationKind.ExtensionType: |
| declarationContext = DeclarationContext.ExtensionTypeBody; |
| _builderFactory.beginExtensionTypeBody(); |
| break; |
| // Coverage-ignore(suite): Not run. |
| case DeclarationKind.Enum: |
| declarationContext = DeclarationContext.Enum; |
| // [BuilderFactory.beginEnumBody] is called in [handleEnumHeader]. |
| break; |
| } |
| pushDeclarationContext(declarationContext); |
| debugEvent("beginClassOrMixinBody"); |
| } |
| |
| @override |
| void beginNamedMixinApplication( |
| Token begin, |
| Token? abstractToken, |
| Token? macroToken, |
| Token? sealedToken, |
| Token? baseToken, |
| Token? interfaceToken, |
| Token? finalToken, |
| Token? augmentToken, |
| Token? mixinToken, |
| Token name) { |
| debugEvent("beginNamedMixinApplication"); |
| popDeclarationContext( |
| DeclarationContext.ClassOrMixinOrNamedMixinApplication); |
| pushDeclarationContext(DeclarationContext.NamedMixinApplication); |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| push(typeVariables ?? NullValues.NominalVariables); |
| _builderFactory.beginNamedMixinApplication( |
| name.lexeme, name.charOffset, typeVariables); |
| if (macroToken != null) { |
| if (reportIfNotEnabled( |
| libraryFeatures.macros, macroToken.charOffset, macroToken.length)) { |
| macroToken = null; |
| } |
| } |
| if (sealedToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.sealedClass, |
| sealedToken.charOffset, sealedToken.length)) { |
| sealedToken = null; |
| } |
| } |
| if (baseToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.classModifiers, |
| baseToken.charOffset, baseToken.length)) { |
| baseToken = null; |
| } |
| } |
| if (interfaceToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.classModifiers, |
| interfaceToken.charOffset, interfaceToken.length)) { |
| interfaceToken = null; |
| } |
| } |
| if (finalToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.classModifiers, |
| finalToken.charOffset, finalToken.length)) { |
| finalToken = null; |
| } |
| } |
| if (mixinToken != null) { |
| if (reportIfNotEnabled(libraryFeatures.classModifiers, |
| mixinToken.charOffset, mixinToken.length)) { |
| mixinToken = null; |
| } |
| } |
| push(Modifiers.from( |
| abstractToken: abstractToken, |
| macroToken: macroToken, |
| sealedToken: sealedToken, |
| baseToken: baseToken, |
| interfaceToken: interfaceToken, |
| finalToken: finalToken, |
| augmentToken: augmentToken, |
| mixinToken: mixinToken)); |
| } |
| |
| @override |
| void handleImplements(Token? implementsKeyword, int interfacesCount) { |
| debugEvent("Implements"); |
| push(const FixedNullableList<TypeBuilder>() |
| .popNonNullable(stack, interfacesCount, dummyTypeBuilder) ?? |
| NullValues.TypeBuilderList); |
| |
| if (implementsKeyword != null && |
| declarationContext == DeclarationContext.Enum) { |
| reportIfNotEnabled(libraryFeatures.enhancedEnums, |
| implementsKeyword.charOffset, implementsKeyword.length); |
| } |
| } |
| |
| @override |
| void handleRecoverDeclarationHeader(DeclarationHeaderKind kind) { |
| debugEvent("handleRecoverClassHeader"); |
| assert(checkState(null, [ |
| /* interfaces */ ValueKinds.TypeBuilderListOrNull, |
| /* mixins */ unionOfKinds([ |
| ValueKinds.MixinApplicationBuilderOrNull, |
| ValueKinds.ParserRecovery, |
| ]), |
| /* supertype offset */ ValueKinds.Integer, |
| /* supertype */ unionOfKinds([ |
| ValueKinds.TypeBuilderOrNull, |
| ValueKinds.ParserRecovery, |
| ]), |
| ])); |
| // TODO(jensj): Possibly use these instead... E.g. "class A extend B {}" |
| // will get here (because it's 'extends' with an 's') and discard the B... |
| // Also Analyzer actually merges the information meaning that the two could |
| // give different errors (if, say, one later assigns |
| // A to a variable of type B). |
| pop(NullValues.TypeBuilderList); // Interfaces. |
| pop(NullValues.MixinApplicationBuilder); // Mixin applications. |
| pop(); // Supertype offset. |
| pop(NullValues.TypeBuilder); // Supertype. |
| } |
| |
| @override |
| void handleRecoverMixinHeader() { |
| debugEvent("handleRecoverMixinHeader"); |
| // TODO(jensj): Possibly use these instead... |
| // See also handleRecoverClassHeader |
| pop(NullValues.TypeBuilderList); // Interfaces. |
| pop(NullValues.TypeBuilderList); // Supertype constraints. |
| } |
| |
| @override |
| void handleClassExtends(Token? extendsKeyword, int typeCount) { |
| debugEvent("handleClassExtends"); |
| while (typeCount > 1) { |
| pop(); |
| typeCount--; |
| } |
| push(extendsKeyword?.charOffset ?? -1); |
| } |
| |
| @override |
| void handleMixinOn(Token? onKeyword, int typeCount) { |
| debugEvent("handleMixinOn"); |
| push(const FixedNullableList<TypeBuilder>() |
| .popNonNullable(stack, typeCount, dummyTypeBuilder) ?? |
| new ParserRecovery(offsetForToken(onKeyword))); |
| } |
| |
| @override |
| void endClassDeclaration(Token beginToken, Token endToken) { |
| debugEvent("endClassDeclaration"); |
| assert(checkState(beginToken, [ |
| /* interfaces */ ValueKinds.TypeBuilderListOrNull, |
| /* mixins */ unionOfKinds([ |
| ValueKinds.MixinApplicationBuilderOrNull, |
| ValueKinds.ParserRecovery, |
| ]), |
| /* supertype offset */ ValueKinds.Integer, |
| /* supertype */ unionOfKinds([ |
| ValueKinds.TypeBuilderOrNull, |
| ValueKinds.ParserRecovery, |
| ]), |
| /* modifiers */ ValueKinds.Modifiers, |
| /* type variables */ ValueKinds.NominalVariableListOrNull, |
| /* name */ ValueKinds.IdentifierOrParserRecovery, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| |
| List<TypeBuilder>? interfaces = |
| pop(NullValues.TypeBuilderList) as List<TypeBuilder>?; |
| MixinApplicationBuilder? mixinApplication = |
| nullIfParserRecovery(pop(NullValues.MixinApplicationBuilder)) |
| as MixinApplicationBuilder?; |
| int supertypeOffset = popCharOffset(); |
| TypeBuilder? supertype = nullIfParserRecovery(pop()) as TypeBuilder?; |
| Modifiers modifiers = pop() as Modifiers; |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| Object? name = pop(); |
| if (typeVariables != null && mixinApplication != null) { |
| mixinApplication.typeVariables = typeVariables; |
| } |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| inAbstractOrSealedClass = false; |
| checkEmpty(beginToken.charOffset); |
| if (name is ParserRecovery) { |
| // Coverage-ignore-block(suite): Not run. |
| _builderFactory.endClassDeclarationForParserRecovery(typeVariables); |
| } else { |
| Identifier identifier = name as Identifier; |
| final int startCharOffset = |
| metadata == null ? beginToken.charOffset : metadata.first.charOffset; |
| |
| String classNameForErrors = identifier.name; |
| if (supertype != null) { |
| if (supertype.nullabilityBuilder.build() == Nullability.nullable) { |
| _compilationUnit.addProblem( |
| templateNullableSuperclassError |
| .withArguments(supertype.fullNameForErrors), |
| identifier.nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| if (mixinApplication != null) { |
| List<TypeBuilder>? mixins = mixinApplication.mixins; |
| for (TypeBuilder mixin in mixins) { |
| if (mixin.nullabilityBuilder.build() == Nullability.nullable) { |
| _compilationUnit.addProblem( |
| templateNullableMixinError |
| .withArguments(mixin.fullNameForErrors), |
| identifier.nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| if (interfaces != null) { |
| for (TypeBuilder interface in interfaces) { |
| if (interface.nullabilityBuilder.build() == Nullability.nullable) { |
| _compilationUnit.addProblem( |
| templateNullableInterfaceError |
| .withArguments(interface.fullNameForErrors), |
| identifier.nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| |
| if (modifiers.isSealed) { |
| modifiers |= Modifiers.Abstract; |
| } |
| _builderFactory.addClass( |
| _offsetMap, |
| metadata, |
| modifiers, |
| identifier, |
| typeVariables, |
| supertype, |
| mixinApplication, |
| interfaces, |
| startCharOffset, |
| identifier.nameOffset, |
| endToken.charOffset, |
| supertypeOffset); |
| } |
| popDeclarationContext(DeclarationContext.Class); |
| } |
| |
| Object? nullIfParserRecovery(Object? node) { |
| return node is ParserRecovery ? null : node; |
| } |
| |
| @override |
| void endMixinDeclaration(Token beginToken, Token endToken) { |
| debugEvent("endMixinDeclaration"); |
| assert(checkState(beginToken, [ |
| /* interfaces */ ValueKinds.TypeBuilderListOrNull, |
| /* supertypeConstraints */ unionOfKinds([ |
| ValueKinds.TypeBuilderListOrNull, |
| ValueKinds.ParserRecovery, |
| ]), |
| /* type variables */ ValueKinds.NominalVariableListOrNull, |
| /* modifiers */ ValueKinds.Modifiers, |
| /* name */ ValueKinds.IdentifierOrParserRecovery, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| |
| List<TypeBuilder>? interfaces = |
| pop(NullValues.TypeBuilderList) as List<TypeBuilder>?; |
| List<TypeBuilder>? supertypeConstraints = |
| nullIfParserRecovery(pop()) as List<TypeBuilder>?; |
| List<NominalVariableBuilder>? typeVariables = |
| pop(NullValues.NominalVariables) as List<NominalVariableBuilder>?; |
| Modifiers modifiers = pop() as Modifiers; |
| Object? name = pop(); |
| List<MetadataBuilder>? metadata = |
| pop(NullValues.Metadata) as List<MetadataBuilder>?; |
| checkEmpty(beginToken.charOffset); |
| if (name is ParserRecovery) { |
| // Coverage-ignore-block(suite): Not run. |
| _builderFactory.endMixinDeclarationForParserRecovery(typeVariables); |
| } else { |
| Identifier identifier = name as Identifier; |
| int startOffset = metadata == null |
| ? beginToken.charOffset |
| : |
| // Coverage-ignore(suite): Not run. |
| metadata.first.charOffset; |
| String classNameForErrors = identifier.name; |
| if (supertypeConstraints != null) { |
| for (TypeBuilder supertype in supertypeConstraints) { |
| if (supertype.nullabilityBuilder.build() == Nullability.nullable) { |
| _compilationUnit.addProblem( |
| templateNullableSuperclassError |
| .withArguments(supertype.fullNameForErrors), |
| identifier.nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| if (interfaces != null) { |
| for (TypeBuilder interface in interfaces) { |
| if (interface.nullabilityBuilder.build() == Nullability.nullable) { |
| _compilationUnit.addProblem( |
| templateNullableInterfaceError |
| .withArguments(interface.fullNameForErrors), |
| identifier.nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| |
| _builderFactory.addMixinDeclaration( |
| _offsetMap, |
| metadata, |
| modifiers, |
| identifier, |
| typeVariables, |
| supertypeConstraints, |
| interfaces, |
| startOffset, |
| identifier.nameOffset, |
| endToken.charOffset, |
| -1); |
| } |
| popDeclarationContext(DeclarationContext.Mixin); |
| } |
| |
| @override |
| void beginExtensionDeclarationPrelude(Token extensionKeyword) { |
| assert(checkState(extensionKeyword, [ValueKinds.MetadataListOrNull])); |
| debugEvent("beginExtensionDeclaration"); |
| pushDeclarationContext(DeclarationContext.ExtensionOrExtensionType); |
| _builderFactory.beginExtensionOrExtensionTypeHeader(); |
| } |
| |
| @override |
| void beginExtensionDeclaration( |
| Token? augmentToken, Token extensionKeyword, Token? nameToken) { |
| assert(checkState(extensionKeyword, |
| [ValueKinds.NominalVariableListOrNull, ValueKinds.MetadataListOrNull])); |
| debugEvent("beginExtensionDeclaration"); |
| popDeclarationContext(DeclarationContext.ExtensionOrExtensionType); |
| pushDeclarationContext(DeclarationContext.Extension); |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| int offset = nameToken?.charOffset ?? extensionKeyword.charOffset; |
| push(nameToken != null |
| ? new SimpleIdentifier(nameToken) |
| : NullValues.Identifier); |
| push(typeVariables ?? NullValues.NominalVariables); |
| _builderFactory.beginExtensionDeclaration( |
| nameToken?.lexeme, offset, typeVariables); |
| } |
| |
| @override |
| void endExtensionDeclaration(Token beginToken, Token extensionKeyword, |
| Token? onKeyword, Token endToken) { |
| assert(checkState(extensionKeyword, [ |
| unionOfKinds([ValueKinds.ParserRecovery, ValueKinds.TypeBuilder]), |
| ValueKinds.NominalVariableListOrNull, |
| ValueKinds.IdentifierOrNull, |
| ValueKinds.MetadataListOrNull |
| ])); |
| debugEvent("endExtensionDeclaration"); |
| |
| Object? onType = pop(); |
| if (onType is ParserRecovery) { |
| ParserRecovery parserRecovery = onType; |
| onType = new FixedTypeBuilderImpl( |
| const InvalidType(), uri, parserRecovery.charOffset); |
| } |
| List<NominalVariableBuilder>? typeVariables = |
| pop(NullValues.NominalVariables) as List<NominalVariableBuilder>?; |
| Identifier? name = pop(NullValues.Identifier) as Identifier?; |
| int nameOffset = name?.nameOffset ?? extensionKeyword.charOffset; |
| List<MetadataBuilder>? metadata = |
| pop(NullValues.Metadata) as List<MetadataBuilder>?; |
| checkEmpty(extensionKeyword.charOffset); |
| int startOffset = metadata == null |
| ? extensionKeyword.charOffset |
| : metadata.first.charOffset; |
| _builderFactory.addExtensionDeclaration( |
| _offsetMap, |
| beginToken, |
| metadata, |
| // TODO(johnniwinther): Support modifiers on extensions? |
| Modifiers.empty, |
| name, |
| typeVariables, |
| onType as TypeBuilder, |
| startOffset, |
| nameOffset, |
| endToken.charOffset); |
| popDeclarationContext(DeclarationContext.Extension); |
| } |
| |
| @override |
| void beginExtensionTypeDeclaration( |
| Token? augmentToken, Token extensionKeyword, Token nameToken) { |
| assert(checkState(extensionKeyword, |
| [ValueKinds.NominalVariableListOrNull, ValueKinds.MetadataListOrNull])); |
| debugEvent("beginExtensionTypeDeclaration"); |
| popDeclarationContext(DeclarationContext.ExtensionOrExtensionType); |
| pushDeclarationContext(DeclarationContext.ExtensionType); |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| String name = nameToken.lexeme; |
| int offset = nameToken.charOffset; |
| push(new SimpleIdentifier(nameToken)); |
| push(typeVariables ?? NullValues.NominalVariables); |
| _builderFactory.beginExtensionTypeDeclaration(name, offset, typeVariables); |
| } |
| |
| @override |
| void endExtensionTypeDeclaration(Token beginToken, Token? augmentToken, |
| Token extensionKeyword, Token typeKeyword, Token endToken) { |
| assert(checkState(extensionKeyword, [ |
| ValueKinds.TypeBuilderListOrNull, |
| ValueKinds.NominalVariableListOrNull, |
| ValueKinds.Identifier, |
| ValueKinds.MetadataListOrNull, |
| ])); |
| reportIfNotEnabled(libraryFeatures.inlineClass, typeKeyword.charOffset, |
| typeKeyword.length); |
| |
| List<TypeBuilder>? interfaces = |
| pop(NullValues.TypeBuilderList) as List<TypeBuilder>?; |
| List<NominalVariableBuilder>? typeVariables = |
| pop(NullValues.NominalVariables) as List<NominalVariableBuilder>?; |
| Identifier identifier = pop() as Identifier; |
| List<MetadataBuilder>? metadata = |
| pop(NullValues.Metadata) as List<MetadataBuilder>?; |
| checkEmpty(extensionKeyword.charOffset); |
| |
| reportIfNotEnabled(libraryFeatures.inlineClass, |
| extensionKeyword.next!.charOffset, extensionKeyword.next!.length); |
| int startOffset = metadata == null |
| ? extensionKeyword.charOffset |
| : metadata.first.charOffset; |
| _builderFactory.addExtensionTypeDeclaration( |
| _offsetMap, |
| metadata, |
| // TODO(johnniwinther): Support modifiers on extension types? |
| Modifiers.empty, |
| identifier, |
| typeVariables, |
| interfaces, |
| startOffset, |
| endToken.charOffset); |
| |
| popDeclarationContext(DeclarationContext.ExtensionType); |
| } |
| |
| @override |
| void beginPrimaryConstructor(Token beginToken) {} |
| |
| @override |
| void endPrimaryConstructor( |
| Token beginToken, Token? constKeyword, bool hasConstructorName) { |
| assert(checkState(beginToken, [ |
| ValueKinds.FormalListOrNull, |
| /* formals offset */ ValueKinds.Integer, |
| if (hasConstructorName) ValueKinds.IdentifierOrParserRecovery, |
| ])); |
| List<FormalParameterBuilder>? formals = |
| pop(NullValues.FormalParameters) as List<FormalParameterBuilder>?; |
| int charOffset = pop() as int; // Pop formals char offset |
| String constructorName = ''; |
| if (hasConstructorName) { |
| // TODO(johnniwinther): Handle [ParserRecovery]. |
| Identifier identifier = pop() as Identifier; |
| charOffset = identifier.nameOffset; |
| constructorName = identifier.name; |
| } |
| bool inExtensionType = |
| declarationContext == DeclarationContext.ExtensionType; |
| if (formals != null) { |
| int requiredPositionalCount = 0; |
| int? firstNamedParameterOffset; |
| int? firstOptionalPositionalParameterOffset; |
| for (int i = 0; i < formals.length; i++) { |
| FormalParameterBuilder formal = formals[i]; |
| if (inExtensionType) { |
| TypeBuilder type = formal.type; |
| if (type is FunctionTypeBuilder && |
| type.hasFunctionFormalParameterSyntax) { |
| _compilationUnit.addProblem( |
| // ignore: lines_longer_than_80_chars |
| messageExtensionTypePrimaryConstructorFunctionFormalParameterSyntax, |
| formal.charOffset, |
| formal.name.length, |
| formal.fileUri); |
| } |
| if (type is ImplicitTypeBuilder) { |
| _compilationUnit.addProblem(messageExpectedRepresentationType, |
| formal.charOffset, formal.name.length, formal.fileUri); |
| formal.type = |
| new InvalidTypeBuilderImpl(formal.fileUri, formal.charOffset); |
| } |
| if (formal.modifiers.containsSyntacticModifiers( |
| ignoreCovariant: true, ignoreRequired: true)) { |
| _compilationUnit.addProblem(messageRepresentationFieldModifier, |
| formal.charOffset, formal.name.length, formal.fileUri); |
| } |
| if (formal.isInitializingFormal) { |
| _compilationUnit.addProblem( |
| messageExtensionTypePrimaryConstructorWithInitializingFormal, |
| formal.charOffset, |
| formal.name.length, |
| formal.fileUri); |
| } |
| } |
| |
| if (formal.isPositional) { |
| if (formal.isOptionalPositional) { |
| firstOptionalPositionalParameterOffset = formal.charOffset; |
| } else { |
| requiredPositionalCount++; |
| } |
| } |
| if (formal.isNamed) { |
| firstNamedParameterOffset = formal.charOffset; |
| } |
| _builderFactory.addPrimaryConstructorField( |
| // TODO(johnniwinther): Support annotations on annotations on fields |
| // defined through a primary constructor. This is not needed for |
| // extension types where the field is not part of the AST but will |
| // be needed when primary constructors are generally supported. |
| metadata: null, |
| type: formal.type, |
| name: formal.name, |
| charOffset: formal.charOffset); |
| formals[i] = formal.forPrimaryConstructor(_builderFactory); |
| } |
| if (inExtensionType) { |
| if (firstOptionalPositionalParameterOffset != null) { |
| _compilationUnit.addProblem( |
| messageOptionalParametersInExtensionTypeDeclaration, |
| firstOptionalPositionalParameterOffset, |
| 1, |
| uri); |
| } else if (firstNamedParameterOffset != null) { |
| _compilationUnit.addProblem( |
| messageNamedParametersInExtensionTypeDeclaration, |
| firstNamedParameterOffset, |
| 1, |
| uri); |
| } else if (requiredPositionalCount == 0) { |
| _compilationUnit.addProblem( |
| messageExpectedRepresentationField, charOffset, 1, uri); |
| } else if (formals.length > 1) { |
| _compilationUnit.addProblem( |
| messageMultipleRepresentationFields, charOffset, 1, uri); |
| } |
| } |
| } |
| |
| _builderFactory.addPrimaryConstructor( |
| offsetMap: _offsetMap, |
| beginToken: beginToken, |
| constructorName: constructorName == "new" ? "" : constructorName, |
| charOffset: charOffset, |
| formals: formals, |
| isConst: constKeyword != null); |
| } |
| |
| ProcedureKind computeProcedureKind(Token? token) { |
| if (token == null) return ProcedureKind.Method; |
| if (token.isA2(Keyword.GET)) return ProcedureKind.Getter; |
| if (token.isA2(Keyword.SET)) return ProcedureKind.Setter; |
| return unhandled( |
| token.lexeme, "computeProcedureKind", token.charOffset, uri); |
| } |
| |
| @override |
| void beginTopLevelMethod( |
| Token lastConsumed, Token? augmentToken, Token? externalToken) { |
| pushDeclarationContext(DeclarationContext.TopLevelMethod); |
| _builderFactory.beginTopLevelMethod(); |
| Modifiers modifiers = Modifiers.from( |
| augmentToken: augmentToken, externalToken: externalToken); |
| push(modifiers); |
| } |
| |
| @override |
| void endTopLevelMethod(Token beginToken, Token? getOrSet, Token endToken) { |
| debugEvent("endTopLevelMethod"); |
| assert(checkState(beginToken, [ |
| ValueKinds.MethodBody, |
| ValueKinds.AsyncMarker, |
| ValueKinds.FormalListOrNull, |
| /* formalsOffset */ ValueKinds.Integer, |
| ValueKinds.NominalVariableListOrNull, |
| ValueKinds.IdentifierOrParserRecovery, |
| ValueKinds.TypeBuilderOrNull, |
| ValueKinds.Modifiers, |
| ValueKinds.MetadataListOrNull, |
| ])); |
| |
| MethodBody kind = pop() as MethodBody; |
| AsyncMarker asyncModifier = pop() as AsyncMarker; |
| List<FormalParameterBuilder>? formals = |
| pop() as List<FormalParameterBuilder>?; |
| int formalsOffset = popCharOffset(); |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| Object? identifier = pop(); |
| TypeBuilder? returnType = pop() as TypeBuilder?; |
| bool isAbstract = kind == MethodBody.Abstract; |
| if (getOrSet != null && getOrSet.isA(Keyword.SET)) { |
| if (formals == null || formals.length != 1) { |
| // This isn't abstract as we'll add an error-recovery node in |
| // [BodyBuilder.finishFunction]. |
| isAbstract = false; |
| } |
| if (returnType != null && !returnType.isVoidType) { |
| addProblem(messageNonVoidReturnSetter, beginToken.charOffset, noLength); |
| // Use implicit void as recovery. |
| returnType = null; |
| } |
| } |
| Modifiers modifiers = pop() as Modifiers; |
| if (isAbstract && !modifiers.isExternal) { |
| modifiers |= Modifiers.Abstract; |
| } |
| if (nativeMethodName != null) { |
| modifiers |= Modifiers.External; |
| } |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| checkEmpty(beginToken.charOffset); |
| if (identifier is Identifier) { |
| _builderFactory.endTopLevelMethod(); |
| final int startCharOffset = |
| metadata == null ? beginToken.charOffset : metadata.first.charOffset; |
| _builderFactory.addProcedure( |
| _offsetMap, |
| metadata, |
| modifiers, |
| returnType, |
| identifier, |
| identifier.name, |
| typeVariables, |
| formals, |
| computeProcedureKind(getOrSet), |
| startCharOffset, |
| identifier.nameOffset, |
| formalsOffset, |
| endToken.charOffset, |
| nativeMethodName, |
| asyncModifier, |
| isInstanceMember: false, |
| isExtensionMember: false, |
| isExtensionTypeMember: false); |
| nativeMethodName = null; |
| } else { |
| _builderFactory.endTopLevelMethodForParserRecovery(typeVariables); |
| } |
| popDeclarationContext(DeclarationContext.TopLevelMethod); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void handleNativeFunctionBody(Token nativeToken, Token semicolon) { |
| debugEvent("NativeFunctionBody"); |
| if (nativeMethodName != null) { |
| push(MethodBody.Regular); |
| } else { |
| push(MethodBody.Abstract); |
| } |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void handleNativeFunctionBodyIgnored(Token nativeToken, Token semicolon) { |
| debugEvent("NativeFunctionBodyIgnored"); |
| } |
| |
| @override |
| void handleNativeFunctionBodySkipped(Token nativeToken, Token semicolon) { |
| if (!enableNative) { |
| super.handleRecoverableError( |
| messageExpectedBlockToSkip, nativeToken, nativeToken); |
| } |
| push(MethodBody.Regular); |
| } |
| |
| @override |
| void handleNoFunctionBody(Token token) { |
| debugEvent("NoFunctionBody"); |
| if (nativeMethodName != null) { |
| // Coverage-ignore-block(suite): Not run. |
| push(MethodBody.Regular); |
| } else { |
| push(MethodBody.Abstract); |
| } |
| } |
| |
| @override |
| void handleFunctionBodySkipped(Token token, bool isExpressionBody) { |
| debugEvent("handleFunctionBodySkipped"); |
| push(MethodBody.Regular); |
| } |
| |
| @override |
| void beginMethod( |
| DeclarationKind declarationKind, |
| Token? augmentToken, |
| Token? externalToken, |
| Token? staticToken, |
| Token? covariantToken, |
| Token? varFinalOrConst, |
| Token? getOrSet, |
| Token name, |
| String? enclosingDeclarationName) { |
| inConstructor = name.lexeme == enclosingDeclarationName && getOrSet == null; |
| DeclarationContext declarationContext; |
| switch (declarationKind) { |
| case DeclarationKind.TopLevel: |
| // Coverage-ignore(suite): Not run. |
| assert( |
| false, |
| "Expected top level method to be handled by " |
| "`beginTopLevelMethod`."); |
| declarationContext = DeclarationContext.TopLevelMethod; |
| break; |
| case DeclarationKind.Class: |
| if (inConstructor) { |
| declarationContext = DeclarationContext.ClassConstructor; |
| } else if (staticToken != null) { |
| declarationContext = DeclarationContext.ClassStaticMethod; |
| } else { |
| declarationContext = DeclarationContext.ClassInstanceMethod; |
| } |
| break; |
| case DeclarationKind.Mixin: |
| if (inConstructor) { |
| declarationContext = DeclarationContext.MixinConstructor; |
| } else if (staticToken != null) { |
| declarationContext = DeclarationContext.MixinStaticMethod; |
| } else { |
| declarationContext = DeclarationContext.MixinInstanceMethod; |
| } |
| break; |
| case DeclarationKind.Extension: |
| if (inConstructor) { |
| declarationContext = DeclarationContext.ExtensionConstructor; |
| } else if (staticToken != null) { |
| declarationContext = DeclarationContext.ExtensionStaticMethod; |
| } else { |
| declarationContext = DeclarationContext.ExtensionInstanceMethod; |
| } |
| break; |
| case DeclarationKind.ExtensionType: |
| if (inConstructor) { |
| declarationContext = DeclarationContext.ExtensionTypeConstructor; |
| } else if (staticToken != null) { |
| declarationContext = DeclarationContext.ExtensionTypeStaticMethod; |
| } else { |
| declarationContext = DeclarationContext.ExtensionTypeInstanceMethod; |
| } |
| break; |
| case DeclarationKind.Enum: |
| if (inConstructor) { |
| declarationContext = DeclarationContext.EnumConstructor; |
| } else if (staticToken != null) { |
| declarationContext = DeclarationContext.EnumStaticMethod; |
| } else { |
| declarationContext = DeclarationContext.EnumInstanceMethod; |
| } |
| } |
| pushDeclarationContext(declarationContext); |
| |
| Modifiers modifiers = Modifiers.from( |
| augmentToken: augmentToken, |
| externalToken: externalToken, |
| staticToken: !inConstructor ? staticToken : null, |
| covariantToken: covariantToken, |
| varFinalOrConst: varFinalOrConst); |
| push(varFinalOrConst?.charOffset ?? -1); |
| push(modifiers); |
| if (inConstructor) { |
| _builderFactory.beginConstructor(); |
| } else if (staticToken != null) { |
| _builderFactory.beginStaticMethod(); |
| } else { |
| _builderFactory.beginInstanceMethod(); |
| } |
| } |
| |
| @override |
| void endClassMethod(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| debugEvent("endClassMethod"); |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.classMethod); |
| } |
| |
| @override |
| void endClassConstructor(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| debugEvent("endClassConstructor"); |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.classConstructor); |
| } |
| |
| @override |
| void endMixinMethod(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| debugEvent("endMixinMethod"); |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.mixinMethod); |
| } |
| |
| @override |
| void endExtensionMethod(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| debugEvent("endExtensionMethod"); |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.extensionMethod); |
| } |
| |
| @override |
| void endExtensionTypeMethod(Token? getOrSet, Token beginToken, |
| Token beginParam, Token? beginInitializers, Token endToken) { |
| debugEvent("endExtensionTypeMethod"); |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.extensionTypeMethod); |
| } |
| |
| @override |
| void endMixinConstructor(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| debugEvent("endMixinConstructor"); |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.mixinConstructor); |
| } |
| |
| @override |
| void endExtensionConstructor(Token? getOrSet, Token beginToken, |
| Token beginParam, Token? beginInitializers, Token endToken) { |
| debugEvent("endExtensionConstructor"); |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.extensionConstructor); |
| } |
| |
| @override |
| void endExtensionTypeConstructor(Token? getOrSet, Token beginToken, |
| Token beginParam, Token? beginInitializers, Token endToken) { |
| debugEvent("endExtensionTypeConstructor"); |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.extensionTypeConstructor); |
| } |
| |
| void _endClassMethod(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken, _MethodKind methodKind) { |
| assert(checkState(beginToken, [ValueKinds.MethodBody])); |
| MethodBody bodyKind = pop() as MethodBody; |
| if (bodyKind == MethodBody.RedirectingFactoryBody) { |
| // This will cause an error later. |
| pop(); |
| } |
| assert(checkState(beginToken, [ |
| ValueKinds.AsyncModifier, |
| ValueKinds.FormalListOrNull, |
| ValueKinds.Integer, // formals offset |
| ValueKinds.NominalVariableListOrNull, |
| ValueKinds.IdentifierOrParserRecovery, |
| ValueKinds.TypeBuilderOrNull, |
| ValueKinds.Modifiers, |
| ValueKinds.Integer, // var/final/const offset |
| ValueKinds.MetadataListOrNull, |
| ])); |
| |
| AsyncMarker asyncModifier = pop() as AsyncMarker; |
| List<FormalParameterBuilder>? formals = |
| pop() as List<FormalParameterBuilder>?; |
| int formalsOffset = popCharOffset(); |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| Object? identifier = pop(); |
| TypeBuilder? returnType = pop() as TypeBuilder?; |
| Modifiers modifiers = pop() as Modifiers; |
| int varFinalOrConstOffset = popCharOffset(); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| |
| if (identifier is! Identifier) { |
| assert(identifier is ParserRecovery, |
| "Unexpected identifier $identifier (${identifier.runtimeType})"); |
| |
| if (inConstructor) { |
| _builderFactory.endConstructorForParserRecovery(typeVariables); |
| } else if (modifiers.isStatic) { |
| // Coverage-ignore-block(suite): Not run. |
| _builderFactory.endStaticMethodForParserRecovery(typeVariables); |
| } else { |
| _builderFactory.endInstanceMethodForParserRecovery(typeVariables); |
| } |
| |
| nativeMethodName = null; |
| inConstructor = false; |
| popDeclarationContext(); |
| return; |
| } |
| |
| Operator? operator = identifier.operator; |
| // TODO(johnniwinther): Find a uniform way to compute this. |
| bool hasNoFormals = formals == null; |
| if (Operator.subtract == operator && hasNoFormals) { |
| operator = Operator.unaryMinus; |
| } |
| |
| String name; |
| ProcedureKind? kind; |
| int charOffset = identifier.qualifierOffset; |
| if (operator != null) { |
| name = operator.text; |
| kind = ProcedureKind.Operator; |
| int requiredArgumentCount = operator.requiredArgumentCount; |
| if ((formals?.length ?? 0) != requiredArgumentCount) { |
| Template<Message Function(String name)> template; |
| switch (requiredArgumentCount) { |
| case 0: |
| template = templateOperatorParameterMismatch0; |
| break; |
| |
| case 1: |
| if (Operator.subtract == operator) { |
| template = templateOperatorMinusParameterMismatch; |
| } else { |
| template = templateOperatorParameterMismatch1; |
| } |
| break; |
| |
| case 2: |
| template = templateOperatorParameterMismatch2; |
| break; |
| |
| // Coverage-ignore(suite): Not run. |
| default: |
| unhandled("$requiredArgumentCount", "operatorRequiredArgumentCount", |
| identifier.nameOffset, uri); |
| } |
| addProblem(template.withArguments(name), charOffset, name.length); |
| } else { |
| if (formals != null) { |
| for (FormalParameterBuilder formal in formals) { |
| if (!formal.isRequiredPositional) { |
| addProblem(messageOperatorWithOptionalFormals, formal.charOffset, |
| formal.name.length); |
| } |
| } |
| } |
| } |
| if (typeVariables != null) { |
| NominalVariableBuilder typeVariableBuilder = typeVariables.first; |
| addProblem(messageOperatorWithTypeParameters, |
| typeVariableBuilder.charOffset, typeVariableBuilder.name.length); |
| } |
| } else { |
| name = identifier.name; |
| kind = computeProcedureKind(getOrSet); |
| } |
| |
| bool isAbstract = bodyKind == MethodBody.Abstract; |
| if (isAbstract) { |
| // An error has been reported if this wasn't already sync. |
| asyncModifier = AsyncMarker.Sync; |
| } |
| if (getOrSet != null && getOrSet.isA(Keyword.SET)) { |
| if (formals == null || formals.length != 1) { |
| // This isn't abstract as we'll add an error-recovery node in |
| // [BodyBuilder.finishFunction]. |
| isAbstract = false; |
| } |
| if (returnType != null && !returnType.isVoidType) { |
| addProblem( |
| messageNonVoidReturnSetter, |
| returnType.charOffset ?? // Coverage-ignore(suite): Not run. |
| beginToken.charOffset, |
| noLength); |
| // Use implicit void as recovery. |
| returnType = null; |
| } |
| } |
| if (operator == Operator.indexSet && |
| returnType != null && |
| !returnType.isVoidType) { |
| addProblem( |
| messageNonVoidReturnOperator, |
| returnType.charOffset ?? // Coverage-ignore(suite): Not run. |
| beginToken.offset, |
| noLength); |
| // Use implicit void as recovery. |
| returnType = null; |
| } |
| |
| if (isAbstract && !modifiers.isExternal) { |
| modifiers |= Modifiers.Abstract; |
| } |
| if (nativeMethodName != null) { |
| modifiers |= Modifiers.External; |
| } |
| |
| bool isConst = modifiers.isConst; |
| bool isStatic = modifiers.isStatic; |
| |
| bool isConstructor = switch (methodKind) { |
| _MethodKind.classConstructor => true, |
| _MethodKind.mixinConstructor => true, |
| _MethodKind.extensionConstructor => true, |
| _MethodKind.extensionTypeConstructor => true, |
| _MethodKind.enumConstructor => true, |
| _MethodKind.classMethod => false, |
| _MethodKind.mixinMethod => false, |
| _MethodKind.extensionMethod => false, |
| _MethodKind.extensionTypeMethod => false, |
| _MethodKind.enumMethod => false, |
| }; |
| if (isConstructor) { |
| if (isConst && |
| bodyKind != MethodBody.Abstract && |
| !libraryFeatures.constFunctions.isEnabled) { |
| addProblem(messageConstConstructorWithBody, varFinalOrConstOffset, 5); |
| modifiers -= Modifiers.Const; |
| } |
| if (returnType != null) { |
| addProblem( |
| messageConstructorWithReturnType, |
| returnType.charOffset ?? // Coverage-ignore(suite): Not run. |
| beginToken.offset, |
| noLength); |
| returnType = null; |
| } |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| if (isConst) { |
| // TODO(danrubel): consider removing this |
| // because it is an error to have a const method. |
| modifiers -= Modifiers.Const; |
| } |
| } |
| |
| int startCharOffset = |
| metadata == null ? beginToken.charOffset : metadata.first.charOffset; |
| |
| int endCharOffset = endToken.charOffset; |
| |
| bool forAbstractClassOrMixin = |
| inAbstractOrSealedClass || methodKind == _MethodKind.mixinConstructor; |
| |
| bool isExtensionMember = methodKind == _MethodKind.extensionMethod; |
| bool isExtensionTypeMember = methodKind == _MethodKind.extensionTypeMethod; |
| |
| _builderFactory.addClassMethod( |
| offsetMap: _offsetMap, |
| metadata: metadata, |
| identifier: identifier, |
| name: name, |
| returnType: returnType, |
| formals: formals, |
| typeVariables: typeVariables, |
| beginInitializers: beginInitializers, |
| startCharOffset: startCharOffset, |
| endCharOffset: endCharOffset, |
| charOffset: charOffset, |
| formalsOffset: formalsOffset, |
| modifiers: modifiers, |
| inConstructor: inConstructor, |
| isStatic: isStatic, |
| isConstructor: isConstructor, |
| isExtensionMember: isExtensionMember, |
| isExtensionTypeMember: isExtensionTypeMember, |
| forAbstractClassOrMixin: forAbstractClassOrMixin, |
| asyncModifier: asyncModifier, |
| nativeMethodName: nativeMethodName, |
| kind: kind); |
| |
| nativeMethodName = null; |
| inConstructor = false; |
| popDeclarationContext(); |
| } |
| |
| @override |
| void handleNamedMixinApplicationWithClause(Token withKeyword) { |
| debugEvent("NamedMixinApplicationWithClause"); |
| assert(checkState(withKeyword, [ |
| /* mixins */ unionOfKinds([ |
| ValueKinds.ParserRecovery, |
| ValueKinds.TypeBuilderListOrNull, |
| ]), |
| /* supertype */ unionOfKinds([ |
| ValueKinds.ParserRecovery, |
| ValueKinds.TypeBuilder, |
| ]), |
| ])); |
| Object? mixins = pop(); |
| if (mixins is ParserRecovery) { |
| push(mixins); |
| } else { |
| push(_builderFactory.addMixinApplication( |
| mixins as List<TypeBuilder>, withKeyword.charOffset)); |
| } |
| assert(checkState(withKeyword, [ |
| /* mixin application */ unionOfKinds([ |
| ValueKinds.ParserRecovery, |
| ValueKinds.MixinApplicationBuilder, |
| ]), |
| /* supertype */ unionOfKinds([ |
| ValueKinds.ParserRecovery, |
| ValueKinds.TypeBuilder, |
| ]), |
| ])); |
| } |
| |
| @override |
| void handleNamedArgument(Token colon) { |
| debugEvent("handleNamedArgument"); |
| assert(checkState(colon, [ |
| ValueKinds.IdentifierOrParserRecovery, |
| ])); |
| |
| pop(); // Named argument name. |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void handleNamedRecordField(Token colon) { |
| debugEvent("handleNamedRecordField"); |
| assert(checkState(colon, [ |
| ValueKinds.IdentifierOrParserRecovery, |
| ])); |
| |
| pop(); // Named record field name. |
| } |
| |
| @override |
| void endNamedMixinApplication(Token beginToken, Token classKeyword, |
| Token equals, Token? implementsKeyword, Token endToken) { |
| debugEvent("endNamedMixinApplication"); |
| assert(checkState(beginToken, [ |
| if (implementsKeyword != null) |
| /* interfaces */ unionOfKinds([ |
| ValueKinds.ParserRecovery, |
| ValueKinds.TypeBuilderListOrNull, |
| ]), |
| /* mixin application */ unionOfKinds([ |
| ValueKinds.ParserRecovery, |
| ValueKinds.MixinApplicationBuilder, |
| ]), |
| /* supertype */ unionOfKinds([ |
| ValueKinds.ParserRecovery, |
| ValueKinds.TypeBuilder, |
| ]), |
| /* modifiers */ ValueKinds.Modifiers, |
| /* type variables */ ValueKinds.NominalVariableListOrNull, |
| /* name */ ValueKinds.IdentifierOrParserRecovery, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| |
| List<TypeBuilder>? interfaces = |
| nullIfParserRecovery(popIfNotNull(implementsKeyword)) |
| as List<TypeBuilder>?; |
| Object? mixinApplication = pop(); |
| Object? supertype = pop(); |
| Modifiers modifiers = pop() as Modifiers; |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| Object? name = pop(); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| checkEmpty(beginToken.charOffset); |
| if (name is ParserRecovery || |
| supertype is ParserRecovery || |
| mixinApplication is ParserRecovery) { |
| _builderFactory.endNamedMixinApplicationForParserRecovery(typeVariables); |
| } else { |
| Identifier identifier = name as Identifier; |
| String classNameForErrors = identifier.name; |
| MixinApplicationBuilder mixinApplicationBuilder = |
| mixinApplication as MixinApplicationBuilder; |
| List<TypeBuilder> mixins = mixinApplicationBuilder.mixins; |
| if (supertype is TypeBuilder && supertype is! MixinApplicationBuilder) { |
| if (supertype.nullabilityBuilder.build() == Nullability.nullable) { |
| _compilationUnit.addProblem( |
| templateNullableSuperclassError |
| .withArguments(supertype.fullNameForErrors), |
| identifier.nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| for (TypeBuilder mixin in mixins) { |
| if (mixin.nullabilityBuilder.build() == Nullability.nullable) { |
| _compilationUnit.addProblem( |
| templateNullableMixinError.withArguments(mixin.fullNameForErrors), |
| identifier.nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| if (interfaces != null) { |
| for (TypeBuilder interface in interfaces) { |
| if (interface.nullabilityBuilder.build() == Nullability.nullable) { |
| _compilationUnit.addProblem( |
| templateNullableInterfaceError |
| .withArguments(interface.fullNameForErrors), |
| identifier.nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| |
| if (modifiers.isSealed) { |
| modifiers |= Modifiers.Abstract; |
| } |
| |
| int startCharOffset = beginToken.charOffset; |
| int charEndOffset = endToken.charOffset; |
| _builderFactory.addNamedMixinApplication( |
| metadata, |
| identifier.name, |
| typeVariables, |
| modifiers, |
| supertype as TypeBuilder?, |
| mixinApplication, |
| interfaces, |
| startCharOffset, |
| identifier.nameOffset, |
| charEndOffset); |
| } |
| popDeclarationContext(DeclarationContext.NamedMixinApplication); |
| } |
| |
| @override |
| void endTypeArguments(int count, Token beginToken, Token endToken) { |
| debugEvent("endTypeArguments"); |
| push(const FixedNullableList<TypeBuilder>() |
| .popNonNullable(stack, count, dummyTypeBuilder) ?? |
| NullValues.TypeArguments); |
| } |
| |
| @override |
| void endArguments(int count, Token beginToken, Token endToken) { |
| debugEvent("endArguments"); |
| push(beginToken); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void handleInvalidTypeArguments(Token token) { |
| debugEvent("handleInvalidTypeArguments"); |
| pop(NullValues.TypeArguments); |
| } |
| |
| @override |
| void handleScript(Token token) { |
| debugEvent("handleScript"); |
| _builderFactory.addScriptToken(token.charOffset); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void handleNonNullAssertExpression(Token bang) {} |
| |
| @override |
| void handleType(Token beginToken, Token? questionMark) { |
| debugEvent("handleType"); |
| assert(checkState(beginToken, [ |
| /* type arguments = */ ValueKinds.TypeArgumentsOrNull, |
| /* identifier */ ValueKinds.IdentifierOrParserRecovery, |
| ])); |
| bool isMarkedAsNullable = questionMark != null; |
| List<TypeBuilder>? arguments = pop() as List<TypeBuilder>?; |
| Object name = pop()!; |
| if (name is ParserRecovery) { |
| push(name); |
| } else { |
| Identifier identifier = name as Identifier; |
| push(_builderFactory.addNamedType( |
| identifier.typeName, |
| isMarkedAsNullable |
| ? const NullabilityBuilder.nullable() |
| : const NullabilityBuilder.omitted(), |
| arguments, |
| identifier.qualifierOffset, |
| instanceTypeVariableAccess: |
| declarationContext.instanceTypeVariableAccessState)); |
| } |
| } |
| |
| @override |
| void endTypeList(int count) { |
| debugEvent("TypeList"); |
| push(const FixedNullableList<TypeBuilder>() |
| .popNonNullable(stack, count, dummyTypeBuilder) ?? |
| new ParserRecovery(-1)); |
| } |
| |
| @override |
| void handleNoArguments(Token token) { |
| debugEvent("NoArguments"); |
| push(NullValues.Arguments); |
| } |
| |
| @override |
| void handleNoTypeVariables(Token token) { |
| super.handleNoTypeVariables(token); |
| inConstructorName = false; |
| } |
| |
| @override |
| void handleNoTypeArguments(Token token) { |
| debugEvent("NoTypeArguments"); |
| push(NullValues.TypeArguments); |
| } |
| |
| @override |
| void handleNoTypeNameInConstructorReference(Token token) { |
| debugEvent("NoTypeNameInConstructorReference"); |
| push(NullValues.Identifier); |
| } |
| |
| @override |
| void handleVoidKeyword(Token token) { |
| debugEvent("VoidKeyword"); |
| push(_builderFactory.addVoidType(token.charOffset)); |
| } |
| |
| @override |
| void handleVoidKeywordWithTypeArguments(Token token) { |
| debugEvent("VoidKeyword"); |
| /*List<TypeBuilder> arguments =*/ pop(); |
| push(_builderFactory.addVoidType(token.charOffset)); |
| } |
| |
| @override |
| void beginFormalParameter(Token token, MemberKind kind, Token? requiredToken, |
| Token? covariantToken, Token? varFinalOrConst) { |
| _insideOfFormalParameterType = true; |
| push(Modifiers.from( |
| covariantToken: covariantToken, |
| requiredToken: requiredToken, |
| varFinalOrConst: varFinalOrConst)); |
| } |
| |
| @override |
| void endFormalParameter( |
| Token? thisKeyword, |
| Token? superKeyword, |
| Token? periodAfterThisOrSuper, |
| Token nameToken, |
| Token? initializerStart, |
| Token? initializerEnd, |
| FormalParameterKind kind, |
| MemberKind memberKind) { |
| debugEvent("endFormalParameter"); |
| assert(checkState(nameToken, [ |
| ValueKinds.IdentifierOrParserRecoveryOrNull, |
| unionOfKinds([ |
| ValueKinds.TypeBuilderOrNull, |
| ValueKinds.ParserRecovery, |
| ]), |
| ValueKinds.Modifiers, |
| ValueKinds.MetadataListOrNull, |
| ])); |
| |
| _insideOfFormalParameterType = false; |
| |
| if (superKeyword != null) { |
| reportIfNotEnabled(libraryFeatures.superParameters, |
| superKeyword.charOffset, superKeyword.length); |
| } |
| |
| Object? name = pop(NullValues.Identifier); |
| TypeBuilder? type = nullIfParserRecovery(pop()) as TypeBuilder?; |
| Modifiers modifiers = pop() as Modifiers; |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| if (name is ParserRecovery) { |
| push(name); |
| } else { |
| Identifier? identifier = name as Identifier?; |
| push(_builderFactory.addFormalParameter( |
| metadata, |
| kind, |
| modifiers, |
| type ?? |
| (memberKind.isParameterInferable |
| ? _builderFactory.addInferableType() |
| : const ImplicitTypeBuilder()), |
| identifier == null |
| ? FormalParameterBuilder.noNameSentinel |
| : identifier.name, |
| thisKeyword != null, |
| superKeyword != null, |
| identifier?.nameOffset ?? nameToken.charOffset, |
| initializerStart, |
| // Extension type parameters should not have a lowered name for |
| // wildcard variables. |
| lowerWildcard: |
| declarationContext != DeclarationContext.ExtensionType)); |
| } |
| } |
| |
| @override |
| void beginFormalParameterDefaultValueExpression() { |
| _insideOfFormalParameterType = false; |
| } |
| |
| @override |
| void endFormalParameterDefaultValueExpression() { |
| debugEvent("endFormalParameterDefaultValueExpression"); |
| // Ignored for now. |
| } |
| |
| @override |
| void handleValuedFormalParameter( |
| Token equals, Token token, FormalParameterKind kind) { |
| debugEvent("handleValuedFormalParameter"); |
| // Ignored for now. |
| } |
| |
| @override |
| void handleFormalParameterWithoutValue(Token token) { |
| debugEvent("handleFormalParameterWithoutValue"); |
| // Ignored for now. |
| } |
| |
| @override |
| void endOptionalFormalParameters( |
| int count, Token beginToken, Token endToken, MemberKind kind) { |
| debugEvent("endOptionalFormalParameters"); |
| // When recovering from an empty list of optional arguments, count may be |
| // 0. It might be simpler if the parser didn't call this method in that |
| // case, however, then [beginOptionalFormalParameters] wouldn't always be |
| // matched by this method. |
| List<FormalParameterBuilder>? parameters = |
| const FixedNullableList<FormalParameterBuilder>() |
| .popNonNullable(stack, count, dummyFormalParameterBuilder); |
| if (parameters == null) { |
| push(new ParserRecovery(offsetForToken(beginToken))); |
| } else { |
| push(parameters); |
| } |
| } |
| |
| @override |
| void endFormalParameters( |
| int count, Token beginToken, Token endToken, MemberKind kind) { |
| debugEvent("FormalParameters"); |
| List<FormalParameterBuilder>? formals; |
| if (count == 1) { |
| Object? last = pop(); |
| if (last is List<FormalParameterBuilder>) { |
| formals = last; |
| } else if (last is! ParserRecovery) { |
| assert(last != null); |
| formals = [last as FormalParameterBuilder]; |
| } |
| |
| Token? tokenBeforeEnd = endToken.previous; |
| if (tokenBeforeEnd != null && |
| tokenBeforeEnd.isA(TokenType.COMMA) && |
| kind == MemberKind.PrimaryConstructor && |
| declarationContext == DeclarationContext.ExtensionType) { |
| _compilationUnit.addProblem(messageRepresentationFieldTrailingComma, |
| tokenBeforeEnd.charOffset, 1, uri); |
| } |
| } else if (count > 1) { |
| Object? last = pop(); |
| count--; |
| if (last is ParserRecovery) { |
| // Coverage-ignore-block(suite): Not run. |
| discard(count); |
| } else if (last is List<FormalParameterBuilder>) { |
| formals = const FixedNullableList<FormalParameterBuilder>() |
| .popPaddedNonNullable( |
| stack, count, last.length, dummyFormalParameterBuilder); |
| if (formals != null) { |
| formals.setRange(count, formals.length, last); |
| } |
| } else { |
| formals = const FixedNullableList<FormalParameterBuilder>() |
| .popPaddedNonNullable(stack, count, 1, dummyFormalParameterBuilder); |
| if (formals != null) { |
| formals[count] = last as FormalParameterBuilder; |
| } |
| } |
| } |
| if (formals != null) { |
| assert(formals.isNotEmpty); |
| if (formals.length == 2) { |
| // The name may be null for generalized function types. |
| if (formals[0].name != FormalParameterBuilder.noNameSentinel && |
| formals[0].name == formals[1].name && |
| !formals[0].isWildcard) { |
| addProblem( |
| templateDuplicatedParameterName.withArguments(formals[1].name), |
| formals[1].charOffset, |
| formals[1].name.length, |
| context: [ |
| templateDuplicatedParameterNameCause |
| .withArguments(formals[1].name) |
| .withLocation( |
| uri, formals[0].charOffset, formals[0].name.length) |
| ]); |
| } |
| } else if (formals.length > 2) { |
| Map<String, FormalParameterBuilder> seenNames = |
| <String, FormalParameterBuilder>{}; |
| for (FormalParameterBuilder formal in formals) { |
| if (formal.isWildcard) { |
| continue; |
| } |
| if (formal.name == FormalParameterBuilder.noNameSentinel) continue; |
| if (seenNames.containsKey(formal.name)) { |
| // Coverage-ignore-block(suite): Not run. |
| addProblem( |
| templateDuplicatedParameterName.withArguments(formal.name), |
| formal.charOffset, |
| formal.name.length, |
| context: [ |
| templateDuplicatedParameterNameCause |
| .withArguments(formal.name) |
| .withLocation(uri, seenNames[formal.name]!.charOffset, |
| seenNames[formal.name]!.name.length) |
| ]); |
| } else { |
| seenNames[formal.name] = formal; |
| } |
| } |
| } |
| } |
| if (declarationContext == DeclarationContext.ExtensionType && |
| kind == MemberKind.PrimaryConstructor && |
| formals == null) { |
| // In case of primary constructors of extension types, an error is |
| // reported by the parser if the formals together with the parentheses |
| // around them are missing. To distinguish that case from the case of the |
| // formal parameters present, but lacking the representation field, we |
| // pass the empty list further along instead of `null`. |
| formals = const []; |
| } else if ((declarationContext == DeclarationContext.ExtensionType && |
| kind == MemberKind.PrimaryConstructor || |
| declarationContext == |
| DeclarationContext.ExtensionTypeConstructor) && |
| formals != null) { |
| for (FormalParameterBuilder formal in formals) { |
| if (formal.isSuperInitializingFormal) { |
| _compilationUnit.addProblem( |
| messageExtensionTypeConstructorWithSuperFormalParameter, |
| formal.charOffset, |
| formal.name.length, |
| formal.fileUri); |
| } |
| } |
| } |
| push(beginToken.charOffset); |
| push(formals ?? NullValues.FormalParameters); |
| } |
| |
| @override |
| void handleNoFormalParameters(Token token, MemberKind kind) { |
| push(token.charOffset); |
| super.handleNoFormalParameters(token, kind); |
| } |
| |
| @override |
| void endAssert(Token assertKeyword, Assert kind, Token leftParenthesis, |
| Token? commaToken, Token endToken) { |
| debugEvent("Assert"); |
| // Do nothing |
| } |
| |
| @override |
| void beginEnum(Token enumKeyword) { |
| assert(checkState(enumKeyword, [ |
| ValueKinds.IdentifierOrParserRecovery, |
| ])); |
| Object? identifier = peek(); |
| |
| String declarationName; |
| if (identifier is Identifier) { |
| declarationName = identifier.name; |
| } else { |
| declarationName = '#enum'; |
| } |
| pushDeclarationContext(DeclarationContext.Enum); |
| _builderFactory.beginEnumDeclarationHeader(declarationName); |
| } |
| |
| @override |
| void handleEnumElement(Token beginToken, Token? augmentToken) { |
| debugEvent("handleEnumElement"); |
| assert(checkState(beginToken, [ |
| /* argumentsBeginToken */ ValueKinds.ArgumentsTokenOrNull, |
| ValueKinds.ConstructorReferenceBuilderOrNull, |
| ValueKinds.EnumConstantInfoOrParserRecovery, |
| ])); |
| |
| Token? argumentsBeginToken = pop() as Token?; |
| |
| ConstructorReferenceBuilder? constructorReferenceBuilder = |
| pop() as ConstructorReferenceBuilder?; |
| Object? enumConstantInfo = pop(); |
| if (enumConstantInfo is EnumConstantInfo) { |
| push(enumConstantInfo |
| ..constructorReferenceBuilder = constructorReferenceBuilder |
| ..argumentsBeginToken = argumentsBeginToken); |
| } else { |
| assert(enumConstantInfo is ParserRecovery); |
| push(NullValues.EnumConstantInfo); |
| } |
| } |
| |
| @override |
| void handleEnumHeader( |
| Token? augmentToken, Token enumKeyword, Token leftBrace) { |
| assert(checkState(enumKeyword, [ |
| /* interfaces */ ValueKinds.TypeBuilderListOrNull, |
| /* mixins */ unionOfKinds([ |
| ValueKinds.MixinApplicationBuilderOrNull, |
| ValueKinds.ParserRecovery, |
| ]), |
| /* type variables */ ValueKinds.NominalVariableListOrNull, |
| /* name */ ValueKinds.IdentifierOrParserRecovery, |
| ])); |
| debugEvent("EnumHeader"); |
| |
| // We pop more values than needed to reach typeVariables, offset and name. |
| List<TypeBuilder>? interfaces = |
| pop(NullValues.TypeBuilderList) as List<TypeBuilder>?; |
| Object? mixins = pop(NullValues.MixinApplicationBuilder); |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| |
| Object? identifier = peek(); |
| if (identifier is Identifier) { |
| _builderFactory.beginEnumDeclaration( |
| identifier.name, identifier.nameOffset, typeVariables); |
| } else { |
| identifier as ParserRecovery; |
| _builderFactory.beginEnumDeclaration( |
| "<syntax-error>", identifier.charOffset, typeVariables); |
| } |
| _builderFactory.beginEnumBody(); |
| |
| push(typeVariables ?? NullValues.NominalVariables); |
| push(mixins ?? NullValues.MixinApplicationBuilder); |
| push(interfaces ?? NullValues.TypeBuilderList); |
| |
| push(enumKeyword.charOffset); // start char offset. |
| push(leftBrace.endGroup!.charOffset); // end char offset. |
| } |
| |
| @override |
| void handleEnumElements(Token elementsEndToken, int elementsCount) { |
| debugEvent("handleEnumElements"); |
| push(elementsCount); |
| } |
| |
| @override |
| void endEnum(Token beginToken, Token enumKeyword, Token leftBrace, |
| int memberCount, Token endToken) { |
| assert(checkState(beginToken, [ |
| /* element count */ ValueKinds.Integer, |
| ])); |
| debugEvent("endEnum"); |
| |
| int elementsCount = pop() as int; |
| |
| assert(checkState(beginToken, [ |
| /* enum constants */ ...repeatedKind( |
| ValueKinds.EnumConstantInfoOrNull, elementsCount), |
| /* endCharOffset */ ValueKinds.Integer, |
| /* startCharOffset */ ValueKinds.Integer, |
| /* interfaces */ ValueKinds.TypeBuilderListOrNull, |
| /* mixins */ unionOfKinds([ |
| ValueKinds.MixinApplicationBuilderOrNull, |
| ValueKinds.ParserRecovery, |
| ]), |
| /* type variables */ ValueKinds.NominalVariableListOrNull, |
| /* name */ ValueKinds.IdentifierOrParserRecovery, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| |
| List<EnumConstantInfo?>? enumConstantInfos = |
| const FixedNullableList<EnumConstantInfo>().pop(stack, elementsCount); |
| |
| if (enumConstantInfos != null) { |
| List<EnumConstantInfo?>? parsedEnumConstantInfos; |
| for (int index = 0; index < enumConstantInfos.length; index++) { |
| EnumConstantInfo? info = enumConstantInfos[index]; |
| if (info == null) { |
| parsedEnumConstantInfos = enumConstantInfos.take(index).toList(); |
| } |
| // Coverage-ignore(suite): Not run. |
| else if (parsedEnumConstantInfos != null) { |
| parsedEnumConstantInfos.add(info); |
| } |
| } |
| if (parsedEnumConstantInfos != null) { |
| if (parsedEnumConstantInfos.isEmpty) { |
| enumConstantInfos = null; |
| } else { |
| enumConstantInfos = parsedEnumConstantInfos; |
| } |
| } |
| } |
| |
| int endCharOffset = popCharOffset(); |
| int startCharOffset = popCharOffset(); |
| List<TypeBuilder>? interfaces = |
| nullIfParserRecovery(pop()) as List<TypeBuilder>?; |
| MixinApplicationBuilder? mixinBuilder = |
| nullIfParserRecovery(pop()) as MixinApplicationBuilder?; |
| List<NominalVariableBuilder>? typeVariables = |
| pop() as List<NominalVariableBuilder>?; |
| Object? identifier = pop(); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| checkEmpty(startCharOffset); |
| |
| if (identifier is Identifier) { |
| if (enumConstantInfos == null) { |
| if (!leftBrace.isSynthetic) { |
| // Coverage-ignore-block(suite): Not run. |
| addProblem(messageEnumDeclarationEmpty, identifier.token.offset, |
| identifier.token.length); |
| } |
| } |
| if (interfaces != null) { |
| for (TypeBuilder interface in interfaces) { |
| if (interface.nullabilityBuilder.build() == Nullability.nullable) { |
| _compilationUnit.addProblem( |
| templateNullableInterfaceError |
| .withArguments(interface.fullNameForErrors), |
| interface.charOffset ?? startCharOffset, |
| identifier.name.length, |
| uri); |
| } |
| } |
| } |
| |
| _builderFactory.addEnum( |
| _offsetMap, |
| metadata, |
| identifier, |
| typeVariables, |
| mixinBuilder, |
| interfaces, |
| enumConstantInfos, |
| startCharOffset, |
| endCharOffset); |
| } else { |
| _builderFactory.endEnumDeclarationForParserRecovery(typeVariables); |
| } |
| |
| checkEmpty(enumKeyword.charOffset); |
| popDeclarationContext(DeclarationContext.Enum); |
| } |
| |
| @override |
| void beginTypedef(Token token) { |
| pushDeclarationContext(DeclarationContext.Typedef); |
| _builderFactory.beginTypedef(); |
| } |
| |
| @override |
| void beginFunctionType(Token beginToken) { |
| debugEvent("beginFunctionType"); |
| _structuralParameterDepthLevel++; |
| _builderFactory.beginFunctionType(); |
| } |
| |
| @override |
| void beginFunctionTypedFormalParameter(Token token) { |
| debugEvent("beginFunctionTypedFormalParameter"); |
| _insideOfFormalParameterType = false; |
| _builderFactory.beginFunctionType(); |
| } |
| |
| @override |
| void endRecordType( |
| Token leftBracket, Token? questionMark, int count, bool hasNamedFields) { |
| debugEvent("RecordType"); |
| assert(checkState(leftBracket, [ |
| if (hasNamedFields) ValueKinds.RecordTypeFieldBuilderListOrNull, |
| ...repeatedKind(ValueKinds.RecordTypeFieldBuilder, |
| hasNamedFields ? count - 1 : count), |
| ])); |
| |
| if (!libraryFeatures.records.isEnabled) { |
| addProblem( |
| templateExperimentNotEnabledOffByDefault |
| .withArguments(ExperimentalFlag.records.name), |
| leftBracket.offset, |
| noLength); |
| } |
| |
| List<RecordTypeFieldBuilder>? namedFields; |
| if (hasNamedFields) { |
| namedFields = |
| pop(NullValues.RecordTypeFieldList) as List<RecordTypeFieldBuilder>?; |
| } |
| List<RecordTypeFieldBuilder>? positionalFields = |
| const FixedNullableList<RecordTypeFieldBuilder>().popNonNullable(stack, |
| hasNamedFields ? count - 1 : count, dummyRecordTypeFieldBuilder); |
| |
| push(new RecordTypeBuilderImpl( |
| positionalFields, |
| namedFields, |
| questionMark != null |
| ? const NullabilityBuilder.nullable() |
| : const NullabilityBuilder.omitted(), |
| uri, |
| leftBracket.charOffset, |
| )); |
| } |
| |
| @override |
| void endRecordTypeEntry() { |
| debugEvent("endRecordTypeEntry"); |
| assert(checkState(null, [ |
| ValueKinds.IdentifierOrParserRecoveryOrNull, |
| unionOfKinds([ |
| ValueKinds.TypeBuilder, |
| ValueKinds.ParserRecovery, |
| ]), |
| ValueKinds.MetadataListOrNull, |
| ])); |
| |
| // Offset of name of field (or next token if there's no name). |
| Object? identifier = pop(NullValues.Identifier); |
| Object? type = pop(); |
| List<MetadataBuilder>? metadata = |
| pop(NullValues.Metadata) as List<MetadataBuilder>?; |
| |
| String? fieldName = identifier is Identifier ? identifier.name : null; |
| push(new RecordTypeFieldBuilder( |
| metadata, |
| type is ParserRecovery |
| ? new InvalidTypeBuilderImpl(uri, type.charOffset) |
| : type as TypeBuilder, |
| fieldName, |
| identifier is Identifier ? identifier.nameOffset : -1, |
| isWildcard: |
| libraryFeatures.wildcardVariables.isEnabled && fieldName == '_')); |
| } |
| |
| @override |
| void endRecordTypeNamedFields(int count, Token leftBracket) { |
| assert(checkState(leftBracket, [ |
| ...repeatedKind(ValueKinds.RecordTypeFieldBuilder, count), |
| ])); |
| List<RecordTypeFieldBuilder>? fields = |
| const FixedNullableList<RecordTypeFieldBuilder>() |
| .popNonNullable(stack, count, dummyRecordTypeFieldBuilder); |
| push(fields ?? NullValues.RecordTypeFieldList); |
| } |
| |
| @override |
| void endFunctionType(Token functionToken, Token? questionMark) { |
| debugEvent("FunctionType"); |
| _structuralParameterDepthLevel--; |
| List<FormalParameterBuilder>? formals = |
| pop() as List<FormalParameterBuilder>?; |
| pop(); // formals offset |
| TypeBuilder? returnType = pop() as TypeBuilder?; |
| List<StructuralVariableBuilder>? typeVariables = |
| pop() as List<StructuralVariableBuilder>?; |
| push(_builderFactory.addFunctionType( |
| returnType ?? const ImplicitTypeBuilder(), |
| typeVariables, |
| formals, |
| questionMark != null |
| ? const NullabilityBuilder.nullable() |
| : const NullabilityBuilder.omitted(), |
| uri, |
| functionToken.charOffset, |
| hasFunctionFormalParameterSyntax: false)); |
| } |
| |
| @override |
| void endFunctionTypedFormalParameter(Token nameToken, Token? question) { |
| debugEvent("FunctionTypedFormalParameter"); |
| List<FormalParameterBuilder>? formals = |
| pop() as List<FormalParameterBuilder>?; |
| int formalsOffset = popCharOffset(); |
| TypeBuilder? returnType = pop() as TypeBuilder?; |
| List<StructuralVariableBuilder>? typeVariables = |
| pop() as List<StructuralVariableBuilder>?; |
| push(_builderFactory.addFunctionType( |
| returnType ?? const ImplicitTypeBuilder(), |
| typeVariables, |
| formals, |
| question != null |
| ? const NullabilityBuilder.nullable() |
| : const NullabilityBuilder.omitted(), |
| uri, |
| formalsOffset, |
| hasFunctionFormalParameterSyntax: true)); |
| } |
| |
| @override |
| void endTypedef(Token? augmentToken, Token typedefKeyword, Token? equals, |
| Token endToken) { |
|