| // 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, |
| DeclarationKind, |
| FormalParameterKind, |
| IdentifierContext, |
| lengthOfSpan, |
| MemberKind, |
| optional; |
| import 'package:_fe_analyzer_shared/src/parser/quote.dart' show unescapeString; |
| import 'package:_fe_analyzer_shared/src/parser/stack_listener.dart' |
| show FixedNullableList, NullValue, ParserRecovery; |
| import 'package:_fe_analyzer_shared/src/parser/value_kind.dart'; |
| import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token; |
| import 'package:_fe_analyzer_shared/src/util/link.dart'; |
| import 'package:kernel/ast.dart' |
| show AsyncMarker, InvalidType, Nullability, ProcedureKind, Variance; |
| |
| import '../builder/constructor_reference_builder.dart'; |
| import '../builder/fixed_type_builder.dart'; |
| import '../builder/formal_parameter_builder.dart'; |
| import '../builder/function_type_builder.dart'; |
| import '../builder/invalid_type_declaration_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/type_builder.dart'; |
| import '../builder/type_variable_builder.dart'; |
| import '../combinator.dart' show CombinatorBuilder; |
| import '../configuration.dart' show Configuration; |
| import '../fasta_codes.dart'; |
| import '../identifiers.dart' show QualifiedName, flattenName; |
| import '../ignored_parser_errors.dart' show isIgnoredParserError; |
| import '../kernel/type_algorithms.dart'; |
| import '../kernel/utils.dart'; |
| import '../modifier.dart' |
| show |
| Const, |
| Covariant, |
| External, |
| Final, |
| Modifier, |
| Static, |
| Var, |
| abstractMask, |
| constMask, |
| covariantMask, |
| externalMask, |
| finalMask, |
| lateMask, |
| mixinDeclarationMask, |
| requiredMask, |
| staticMask; |
| import '../operator.dart' |
| show |
| Operator, |
| operatorFromString, |
| operatorToString, |
| operatorRequiredArgumentCount; |
| import '../problems.dart' show unhandled; |
| import 'source_enum_builder.dart'; |
| import 'source_extension_builder.dart'; |
| import 'source_library_builder.dart' |
| show |
| TypeParameterScopeBuilder, |
| TypeParameterScopeKind, |
| FieldInfo, |
| SourceLibraryBuilder; |
| 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 class 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 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.Extension: |
| case DeclarationContext.ExtensionInstanceMethod: |
| case DeclarationContext.ExtensionExternalInstanceField: |
| 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: |
| return InstanceTypeVariableAccessState.Disallowed; |
| case DeclarationContext.MixinConstructor: |
| case DeclarationContext.MixinFactory: |
| case DeclarationContext.ExtensionConstructor: |
| case DeclarationContext.ExtensionFactory: |
| case DeclarationContext.ExtensionInstanceField: |
| case DeclarationContext.EnumFactory: |
| return InstanceTypeVariableAccessState.Invalid; |
| } |
| } |
| } |
| |
| class OutlineBuilder extends StackListenerImpl { |
| @override |
| final SourceLibraryBuilder libraryBuilder; |
| |
| final bool enableNative; |
| final bool stringExpectedAfterNative; |
| bool inAbstractClass = false; |
| bool inConstructor = false; |
| bool inConstructorName = false; |
| int importIndex = 0; |
| |
| String? nativeMethodName; |
| |
| /// Counter used for naming unnamed extension declarations. |
| int unnamedExtensionCounter = 0; |
| |
| Link<DeclarationContext> _declarationContext = const Link(); |
| |
| OutlineBuilder(SourceLibraryBuilder library) |
| : libraryBuilder = library, |
| enableNative = |
| library.loader.target.backendTarget.enableNative(library.importUri), |
| stringExpectedAfterNative = |
| library.loader.target.backendTarget.nativeExtensionExpectsString; |
| |
| 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 => libraryBuilder.fileUri; |
| |
| int popCharOffset() => pop() as int; |
| |
| List<String>? popIdentifierList(int 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--) { |
| popCharOffset(); |
| Object? identifier = pop(); |
| if (identifier is ParserRecovery) { |
| isParserRecovery = true; |
| } else { |
| list[i] = identifier as String; |
| } |
| } |
| return isParserRecovery ? null : list; |
| } |
| |
| @override |
| void beginCompilationUnit(Token token) { |
| pushDeclarationContext(DeclarationContext.Library); |
| } |
| |
| @override |
| void endCompilationUnit(int count, Token token) { |
| popDeclarationContext(DeclarationContext.Library); |
| } |
| |
| @override |
| void endMetadata(Token beginToken, Token? periodBeforeName, Token endToken) { |
| debugEvent("Metadata"); |
| pop(); // arguments |
| if (periodBeforeName != null) { |
| pop(); // offset |
| pop(); // constructor name |
| } |
| pop(); // type arguments |
| pop(); // offset |
| 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) ?? |
| NullValue.Metadata); |
| } |
| |
| @override |
| void handleInvalidTopLevelDeclaration(Token endToken) { |
| debugEvent("InvalidTopLevelDeclaration"); |
| pop(); // metadata star |
| } |
| |
| @override |
| void endHide(Token hideKeyword) { |
| debugEvent("Hide"); |
| Object? names = pop(); |
| if (names is ParserRecovery) { |
| push(names); |
| } else { |
| push(new CombinatorBuilder.hide(names as Iterable<String>, |
| hideKeyword.charOffset, libraryBuilder.fileUri)); |
| } |
| } |
| |
| @override |
| void endShow(Token showKeyword) { |
| debugEvent("Show"); |
| Object? names = pop(); |
| if (names is ParserRecovery) { |
| push(names); |
| } else { |
| push(new CombinatorBuilder.show(names as Iterable<String>, |
| showKeyword.charOffset, libraryBuilder.fileUri)); |
| } |
| } |
| |
| @override |
| void endCombinators(int count) { |
| debugEvent("Combinators"); |
| push(const FixedNullableList<CombinatorBuilder>() |
| .popNonNullable(stack, count, dummyCombinator) ?? |
| NullValue.Combinators); |
| } |
| |
| @override |
| void endExport(Token exportKeyword, Token semicolon) { |
| debugEvent("Export"); |
| List<CombinatorBuilder>? combinators = pop() as List<CombinatorBuilder>?; |
| List<Configuration>? configurations = pop() as List<Configuration>?; |
| int uriOffset = popCharOffset(); |
| String uri = pop() as String; |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| libraryBuilder.addExport(metadata, uri, configurations, combinators, |
| exportKeyword.charOffset, uriOffset); |
| checkEmpty(exportKeyword.charOffset); |
| } |
| |
| @override |
| void handleImportPrefix(Token? deferredKeyword, Token? asKeyword) { |
| debugEvent("ImportPrefix"); |
| if (asKeyword == null) { |
| // If asKeyword is null, then no prefix has been pushed on the stack. |
| // Push a placeholder indicating that there is no prefix. |
| push(NullValue.Prefix); |
| push(-1); |
| } |
| push(deferredKeyword != null); |
| } |
| |
| @override |
| void endImport(Token importKeyword, Token? semicolon) { |
| debugEvent("EndImport"); |
| List<CombinatorBuilder>? combinators = pop() as List<CombinatorBuilder>?; |
| bool isDeferred = pop() as bool; |
| int prefixOffset = popCharOffset(); |
| Object? prefix = pop(NullValue.Prefix); |
| List<Configuration>? configurations = pop() as List<Configuration>?; |
| int uriOffset = popCharOffset(); |
| String uri = |
| pop() as String; // For a conditional import, this is the default URI. |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| checkEmpty(importKeyword.charOffset); |
| if (prefix is ParserRecovery) return; |
| libraryBuilder.addImport( |
| metadata, |
| uri, |
| configurations, |
| prefix as String?, |
| combinators, |
| isDeferred, |
| importKeyword.charOffset, |
| prefixOffset, |
| uriOffset, |
| importIndex++); |
| } |
| |
| @override |
| void endConditionalUris(int count) { |
| debugEvent("EndConditionalUris"); |
| push(const FixedNullableList<Configuration>() |
| .popNonNullable(stack, count, dummyConfiguration) ?? |
| NullValue.ConditionalUris); |
| } |
| |
| @override |
| void endConditionalUri(Token ifKeyword, Token leftParen, Token? equalSign) { |
| debugEvent("EndConditionalUri"); |
| int charOffset = popCharOffset(); |
| String uri = pop() as String; |
| if (equalSign != null) popCharOffset(); |
| String condition = popIfNotNull(equalSign) as String? ?? "true"; |
| Object? dottedName = pop(); |
| if (dottedName is ParserRecovery) { |
| push(dottedName); |
| } else { |
| push(new Configuration(charOffset, dottedName as String, condition, uri)); |
| } |
| } |
| |
| @override |
| void handleDottedName(int count, Token firstIdentifier) { |
| debugEvent("DottedName"); |
| List<String>? names = popIdentifierList(count); |
| if (names == null) { |
| push(new ParserRecovery(firstIdentifier.charOffset)); |
| } else { |
| push(names.join('.')); |
| } |
| } |
| |
| @override |
| void handleRecoverImport(Token? semicolon) { |
| debugEvent("RecoverImport"); |
| pop(); // combinators |
| pop(NullValue.Deferred); // deferredKeyword |
| pop(); // prefixOffset |
| pop(NullValue.Prefix); // prefix |
| pop(); // conditionalUris |
| } |
| |
| @override |
| void endPart(Token partKeyword, Token semicolon) { |
| debugEvent("Part"); |
| int charOffset = popCharOffset(); |
| String uri = pop() as String; |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| libraryBuilder.addPart(metadata, uri, charOffset); |
| checkEmpty(partKeyword.charOffset); |
| } |
| |
| @override |
| void handleOperatorName(Token operatorKeyword, Token token) { |
| debugEvent("OperatorName"); |
| push(operatorFromString(token.stringValue!)); |
| push(token.charOffset); |
| } |
| |
| @override |
| void handleInvalidOperatorName(Token operatorKeyword, Token token) { |
| debugEvent("InvalidOperatorName"); |
| push('invalid'); |
| push(token.charOffset); |
| } |
| |
| @override |
| void handleShowHideIdentifier(Token? modifier, Token identifier) { |
| debugEvent("ShowHideIdentifier"); |
| |
| assert(modifier == null || |
| modifier.stringValue! == "get" || |
| modifier.stringValue! == "set" || |
| modifier.stringValue! == "operator"); |
| |
| if (modifier == null) { |
| handleIdentifier( |
| identifier, IdentifierContext.extensionShowHideElementMemberOrType); |
| } else if (modifier.stringValue! == "get") { |
| handleIdentifier( |
| identifier, IdentifierContext.extensionShowHideElementGetter); |
| } else if (modifier.stringValue! == "set") { |
| handleIdentifier( |
| identifier, IdentifierContext.extensionShowHideElementSetter); |
| } else if (modifier.stringValue! == "operator") { |
| handleIdentifier( |
| identifier, IdentifierContext.extensionShowHideElementOperator); |
| } |
| } |
| |
| @override |
| void handleIdentifier(Token token, IdentifierContext context) { |
| if (context == IdentifierContext.enumValueDeclaration) { |
| debugEvent("handleIdentifier"); |
| 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 (context == IdentifierContext.extensionShowHideElementGetter || |
| context == IdentifierContext.extensionShowHideElementMemberOrType || |
| context == IdentifierContext.extensionShowHideElementSetter) { |
| push(context); |
| super.handleIdentifier(token, context); |
| push(token.charOffset); |
| } else if (context == IdentifierContext.extensionShowHideElementOperator) { |
| push(context); |
| push(operatorFromString(token.stringValue!)); |
| push(token.charOffset); |
| } else { |
| super.handleIdentifier(token, context); |
| push(token.charOffset); |
| } |
| if (inConstructor && context == IdentifierContext.methodDeclaration) { |
| inConstructorName = true; |
| } |
| } |
| |
| @override |
| void handleNoName(Token token) { |
| super.handleNoName(token); |
| push(token.charOffset); |
| } |
| |
| @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("${SourceLibraryBuilder.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) { |
| nativeMethodName = ''; |
| } else { |
| nativeMethodName = name as String; // String. |
| } |
| } else { |
| nativeMethodName = ''; |
| } |
| } |
| |
| @override |
| 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"); |
| push(popIdentifierList(count) ?? |
| (count == 0 ? NullValue.IdentifierList : new ParserRecovery(-1))); |
| } |
| |
| @override |
| void handleQualified(Token period) { |
| assert(checkState(period, [ |
| /*suffix offset*/ ValueKinds.Integer, |
| /*suffix*/ ValueKinds.NameOrParserRecovery, |
| /*prefix offset*/ ValueKinds.Integer, |
| /*prefix*/ unionOfKinds([ |
| ValueKinds.Name, |
| ValueKinds.ParserRecovery, |
| ValueKinds.QualifiedName |
| ]), |
| ])); |
| debugEvent("handleQualified"); |
| int suffixOffset = popCharOffset(); |
| Object? suffix = pop(); |
| int offset = popCharOffset(); |
| Object prefix = pop()!; |
| if (prefix is ParserRecovery) { |
| push(prefix); |
| } else if (suffix is ParserRecovery) { |
| push(suffix); |
| } else { |
| assert(identical(suffix, period.next!.lexeme)); |
| assert(suffixOffset == period.next!.charOffset); |
| push(new QualifiedName(prefix, period.next!)); |
| } |
| push(offset); |
| } |
| |
| @override |
| void endLibraryName(Token libraryKeyword, Token semicolon) { |
| debugEvent("endLibraryName"); |
| popCharOffset(); |
| Object? name = pop(); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| if (name is! ParserRecovery) { |
| libraryBuilder.name = |
| flattenName(name!, offsetForToken(libraryKeyword), uri); |
| } |
| libraryBuilder.metadata = metadata; |
| } |
| |
| @override |
| void beginClassOrMixinOrNamedMixinApplicationPrelude(Token token) { |
| debugEvent("beginClassOrNamedMixinApplicationPrelude"); |
| pushDeclarationContext( |
| DeclarationContext.ClassOrMixinOrNamedMixinApplication); |
| libraryBuilder.beginNestedDeclaration( |
| TypeParameterScopeKind.classOrNamedMixinApplication, |
| "class or mixin application"); |
| } |
| |
| @override |
| void beginClassDeclaration( |
| Token begin, Token? abstractToken, Token? macroToken, Token name) { |
| debugEvent("beginClassDeclaration"); |
| popDeclarationContext( |
| DeclarationContext.ClassOrMixinOrNamedMixinApplication); |
| pushDeclarationContext(DeclarationContext.Class); |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| push(typeVariables ?? NullValue.TypeVariables); |
| libraryBuilder.currentTypeParameterScopeBuilder |
| .markAsClassDeclaration(name.lexeme, name.charOffset, typeVariables); |
| libraryBuilder.setCurrentClassName(name.lexeme); |
| inAbstractClass = abstractToken != null; |
| push(abstractToken != null ? abstractMask : 0); |
| if (macroToken != null && !libraryBuilder.enableMacrosInLibrary) { |
| // TODO(johnniwinther): We should emit a different message when the |
| // experiment is not released yet. The current message indicates that |
| // changing the sdk version can solve the problem. |
| addProblem( |
| templateExperimentNotEnabled.withArguments( |
| 'macros', libraryBuilder.enableMacrosVersionInLibrary.toText()), |
| macroToken.next!.charOffset, |
| macroToken.next!.length); |
| macroToken = null; |
| } |
| push(macroToken ?? NullValue.Token); |
| } |
| |
| @override |
| void beginMixinDeclaration(Token mixinKeyword, Token name) { |
| debugEvent("beginMixinDeclaration"); |
| popDeclarationContext( |
| DeclarationContext.ClassOrMixinOrNamedMixinApplication); |
| pushDeclarationContext(DeclarationContext.Mixin); |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| push(typeVariables ?? NullValue.TypeVariables); |
| libraryBuilder.currentTypeParameterScopeBuilder |
| .markAsMixinDeclaration(name.lexeme, name.charOffset, typeVariables); |
| libraryBuilder.setCurrentClassName(name.lexeme); |
| } |
| |
| @override |
| void beginClassOrMixinOrExtensionBody(DeclarationKind kind, Token token) { |
| DeclarationContext declarationContext; |
| switch (kind) { |
| case DeclarationKind.TopLevel: |
| throw new UnsupportedError('Unexpected top level body.'); |
| case DeclarationKind.Class: |
| declarationContext = DeclarationContext.ClassBody; |
| break; |
| case DeclarationKind.Mixin: |
| declarationContext = DeclarationContext.MixinBody; |
| break; |
| case DeclarationKind.Extension: |
| declarationContext = DeclarationContext.ExtensionBody; |
| break; |
| case DeclarationKind.Enum: |
| declarationContext = DeclarationContext.Enum; |
| break; |
| } |
| pushDeclarationContext(declarationContext); |
| if (kind == DeclarationKind.Extension) { |
| assert(checkState(token, [ |
| /* hide type elements = */ ValueKinds.TypeBuilderListOrNull, |
| /* hide get elements = */ ValueKinds.NameListOrNull, |
| /* hide member or type elements = */ ValueKinds.NameListOrNull, |
| /* hide set elements = */ ValueKinds.NameListOrNull, |
| /* hide operator elements = */ ValueKinds.OperatorListOrNull, |
| /* show type elements = */ ValueKinds.TypeBuilderListOrNull, |
| /* show get elements = */ ValueKinds.NameListOrNull, |
| /* show member or type elements = */ ValueKinds.NameListOrNull, |
| /* show set elements */ ValueKinds.NameListOrNull, |
| /* show operator elements*/ ValueKinds.OperatorListOrNull, |
| unionOfKinds([ValueKinds.ParserRecovery, ValueKinds.TypeBuilder]) |
| ])); |
| |
| // We peek into 10th frame on the stack for the extension 'this' type. |
| Object? extensionThisType = stack[10]; |
| |
| if (extensionThisType is TypeBuilder) { |
| libraryBuilder.currentTypeParameterScopeBuilder |
| .registerExtensionThisType(extensionThisType); |
| } else { |
| // TODO(johnniwinther): Supply an invalid type as the extension on type. |
| } |
| } |
| debugEvent("beginClassOrMixinBody"); |
| // Resolve unresolved types from the class header (i.e., superclass, mixins, |
| // and implemented types) before adding members from the class body which |
| // should not shadow these unresolved types. |
| libraryBuilder.currentTypeParameterScopeBuilder.resolveNamedTypes( |
| libraryBuilder.currentTypeParameterScopeBuilder.typeVariables, |
| libraryBuilder); |
| } |
| |
| @override |
| void beginNamedMixinApplication( |
| Token begin, Token? abstractToken, Token? macroToken, Token name) { |
| debugEvent("beginNamedMixinApplication"); |
| popDeclarationContext( |
| DeclarationContext.ClassOrMixinOrNamedMixinApplication); |
| pushDeclarationContext(DeclarationContext.NamedMixinApplication); |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| push(typeVariables ?? NullValue.TypeVariables); |
| libraryBuilder.currentTypeParameterScopeBuilder.markAsNamedMixinApplication( |
| name.lexeme, name.charOffset, typeVariables); |
| push(abstractToken != null ? abstractMask : 0); |
| if (macroToken != null && !libraryBuilder.enableMacrosInLibrary) { |
| addProblem( |
| templateExperimentNotEnabled.withArguments( |
| 'macros', libraryBuilder.enableMacrosVersionInLibrary.toText()), |
| macroToken.next!.charOffset, |
| macroToken.next!.length); |
| macroToken = null; |
| } |
| push(macroToken ?? NullValue.Token); |
| } |
| |
| @override |
| void handleImplements(Token? implementsKeyword, int interfacesCount) { |
| debugEvent("Implements"); |
| push(const FixedNullableList<TypeBuilder>() |
| .popNonNullable(stack, interfacesCount, dummyTypeBuilder) ?? |
| NullValue.TypeBuilderList); |
| |
| if (!libraryBuilder.enableEnhancedEnumsInLibrary && |
| implementsKeyword != null && |
| declarationContext == DeclarationContext.Enum) { |
| addProblem( |
| templateExperimentNotEnabled.withArguments('enhanced-enums', |
| libraryBuilder.enableEnhancedEnumsVersionInLibrary.toText()), |
| implementsKeyword.charOffset, |
| -1); |
| } |
| } |
| |
| @override |
| void handleExtensionShowHide(Token? showKeyword, int showElementCount, |
| Token? hideKeyword, int hideElementCount) { |
| debugEvent("ExtensionShow"); |
| |
| List<dynamic> toBePushed = <dynamic>[]; |
| void handleShowHideElements(int elementCount) { |
| if (elementCount == 0) { |
| toBePushed.add(NullValue.TypeBuilderList); |
| toBePushed.add(NullValue.IdentifierList); |
| toBePushed.add(NullValue.IdentifierList); |
| toBePushed.add(NullValue.IdentifierList); |
| toBePushed.add(NullValue.OperatorList); |
| } else { |
| List<TypeBuilder> typeElements = <TypeBuilder>[]; |
| List<String> getElements = <String>[]; |
| List<String> ambiguousMemberOrTypeElements = <String>[]; |
| List<String> setElements = <String>[]; |
| List<Operator> operatorElements = <Operator>[]; |
| |
| for (int i = 0; i < elementCount; ++i) { |
| Object leadingElementPart = pop()!; |
| if (leadingElementPart is TypeBuilder) { |
| typeElements.add(leadingElementPart); |
| } else { |
| leadingElementPart as int; // Offset. |
| Object name = pop()!; |
| IdentifierContext context = pop() as IdentifierContext; |
| |
| if (name is! ParserRecovery) { |
| assert(context == |
| IdentifierContext.extensionShowHideElementGetter || |
| context == |
| IdentifierContext.extensionShowHideElementMemberOrType || |
| context == |
| IdentifierContext.extensionShowHideElementOperator || |
| context == IdentifierContext.extensionShowHideElementSetter); |
| |
| if (context == IdentifierContext.extensionShowHideElementGetter) { |
| getElements.add(name as String); |
| } else if (context == |
| IdentifierContext.extensionShowHideElementMemberOrType) { |
| ambiguousMemberOrTypeElements.add(name as String); |
| } else if (context == |
| IdentifierContext.extensionShowHideElementOperator) { |
| operatorElements.add(name as Operator); |
| } else if (context == |
| IdentifierContext.extensionShowHideElementSetter) { |
| setElements.add(name as String); |
| } |
| } |
| } |
| } |
| |
| toBePushed.add(typeElements); |
| toBePushed.add(getElements); |
| toBePushed.add(ambiguousMemberOrTypeElements); |
| toBePushed.add(setElements); |
| toBePushed.add(operatorElements); |
| } |
| } |
| |
| handleShowHideElements(hideElementCount); |
| handleShowHideElements(showElementCount); |
| for (int i = toBePushed.length - 1; i >= 0; --i) { |
| push(toBePushed[i]); |
| } |
| } |
| |
| @override |
| void handleRecoverClassHeader() { |
| debugEvent("handleRecoverClassHeader"); |
| // 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(NullValue.TypeBuilderList); // Interfaces. |
| pop(); // Supertype offset. |
| pop(); // Supertype. |
| } |
| |
| @override |
| void handleRecoverMixinHeader() { |
| debugEvent("handleRecoverMixinHeader"); |
| // TODO(jensj): Possibly use these instead... |
| // See also handleRecoverClassHeader |
| pop(NullValue.TypeBuilderList); // Interfaces. |
| pop(NullValue.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, |
| /* supertype offset */ ValueKinds.Integer, |
| /* supertype */ unionOfKinds([ |
| ValueKinds.TypeBuilderOrNull, |
| ValueKinds.ParserRecovery, |
| ]), |
| /* macro token */ ValueKinds.TokenOrNull, |
| /* modifiers */ ValueKinds.Integer, |
| /* type variables */ ValueKinds.TypeVariableListOrNull, |
| /* name offset */ ValueKinds.Integer, |
| /* name */ ValueKinds.NameOrParserRecovery, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| |
| List<TypeBuilder>? interfaces = |
| pop(NullValue.TypeBuilderList) as List<TypeBuilder>?; |
| int supertypeOffset = popCharOffset(); |
| TypeBuilder? supertype = nullIfParserRecovery(pop()) as TypeBuilder?; |
| Token? macroToken = pop(NullValue.Token) as Token?; |
| int modifiers = pop() as int; |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| int nameOffset = popCharOffset(); |
| Object? name = pop(); |
| if (typeVariables != null && supertype is MixinApplicationBuilder) { |
| supertype.typeVariables = typeVariables; |
| } |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| inAbstractClass = false; |
| checkEmpty(beginToken.charOffset); |
| if (name is ParserRecovery) { |
| libraryBuilder |
| .endNestedDeclaration( |
| TypeParameterScopeKind.classDeclaration, "<syntax-error>") |
| .resolveNamedTypes(typeVariables, libraryBuilder); |
| } else { |
| final int startCharOffset = |
| metadata == null ? beginToken.charOffset : metadata.first.charOffset; |
| |
| if (libraryBuilder.isNonNullableByDefault) { |
| String classNameForErrors = "${name}"; |
| TypeBuilder? supertypeForErrors = supertype is MixinApplicationBuilder |
| ? supertype.supertype |
| : supertype; |
| List<TypeBuilder>? mixins = |
| supertype is MixinApplicationBuilder ? supertype.mixins : null; |
| if (supertypeForErrors != null) { |
| if (supertypeForErrors.nullabilityBuilder.build(libraryBuilder) == |
| Nullability.nullable) { |
| libraryBuilder.addProblem( |
| templateNullableSuperclassError |
| .withArguments(supertypeForErrors.fullNameForErrors), |
| nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| if (mixins != null) { |
| for (TypeBuilder mixin in mixins) { |
| if (mixin.nullabilityBuilder.build(libraryBuilder) == |
| Nullability.nullable) { |
| libraryBuilder.addProblem( |
| templateNullableMixinError |
| .withArguments(mixin.fullNameForErrors), |
| nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| if (interfaces != null) { |
| for (TypeBuilder interface in interfaces) { |
| if (interface.nullabilityBuilder.build(libraryBuilder) == |
| Nullability.nullable) { |
| libraryBuilder.addProblem( |
| templateNullableInterfaceError |
| .withArguments(interface.fullNameForErrors), |
| nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| } |
| |
| libraryBuilder.addClass( |
| metadata, |
| modifiers, |
| name as String, |
| typeVariables, |
| supertype, |
| interfaces, |
| startCharOffset, |
| nameOffset, |
| endToken.charOffset, |
| supertypeOffset, |
| isMacro: macroToken != null); |
| } |
| libraryBuilder.setCurrentClassName(null); |
| popDeclarationContext(DeclarationContext.Class); |
| } |
| |
| Object? nullIfParserRecovery(Object? node) { |
| return node is ParserRecovery ? null : node; |
| } |
| |
| @override |
| void endMixinDeclaration(Token mixinToken, Token endToken) { |
| debugEvent("endMixinDeclaration"); |
| List<TypeBuilder>? interfaces = |
| pop(NullValue.TypeBuilderList) as List<TypeBuilder>?; |
| List<TypeBuilder>? supertypeConstraints = |
| nullIfParserRecovery(pop()) as List<TypeBuilder>?; |
| List<TypeVariableBuilder>? typeVariables = |
| pop(NullValue.TypeVariables) as List<TypeVariableBuilder>?; |
| int nameOffset = popCharOffset(); |
| Object? name = pop(); |
| List<MetadataBuilder>? metadata = |
| pop(NullValue.Metadata) as List<MetadataBuilder>?; |
| checkEmpty(mixinToken.charOffset); |
| if (name is ParserRecovery) { |
| libraryBuilder |
| .endNestedDeclaration( |
| TypeParameterScopeKind.mixinDeclaration, "<syntax-error>") |
| .resolveNamedTypes(typeVariables, libraryBuilder); |
| } else { |
| int startOffset = |
| metadata == null ? mixinToken.charOffset : metadata.first.charOffset; |
| TypeBuilder? supertype; |
| if (supertypeConstraints != null && supertypeConstraints.isNotEmpty) { |
| if (supertypeConstraints.length == 1) { |
| supertype = supertypeConstraints.first; |
| } else { |
| supertype = new MixinApplicationBuilder( |
| supertypeConstraints.first, |
| supertypeConstraints.skip(1).toList(), |
| supertypeConstraints.first.fileUri!, |
| supertypeConstraints.first.charOffset!); |
| } |
| } |
| |
| if (libraryBuilder.isNonNullableByDefault) { |
| String classNameForErrors = "${name}"; |
| if (supertypeConstraints != null) { |
| for (TypeBuilder supertype in supertypeConstraints) { |
| if (supertype.nullabilityBuilder.build(libraryBuilder) == |
| Nullability.nullable) { |
| libraryBuilder.addProblem( |
| templateNullableSuperclassError |
| .withArguments(supertype.fullNameForErrors), |
| nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| if (interfaces != null) { |
| for (TypeBuilder interface in interfaces) { |
| if (interface.nullabilityBuilder.build(libraryBuilder) == |
| Nullability.nullable) { |
| libraryBuilder.addProblem( |
| templateNullableInterfaceError |
| .withArguments(interface.fullNameForErrors), |
| nameOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| } |
| |
| libraryBuilder.addMixinDeclaration( |
| metadata, |
| mixinDeclarationMask, |
| name as String, |
| typeVariables, |
| supertype, |
| interfaces, |
| startOffset, |
| nameOffset, |
| endToken.charOffset, |
| -1); |
| } |
| libraryBuilder.setCurrentClassName(null); |
| popDeclarationContext(DeclarationContext.Mixin); |
| } |
| |
| @override |
| void beginExtensionDeclarationPrelude(Token extensionKeyword) { |
| assert(checkState(extensionKeyword, [ValueKinds.MetadataListOrNull])); |
| debugEvent("beginExtensionDeclaration"); |
| pushDeclarationContext(DeclarationContext.Extension); |
| libraryBuilder.beginNestedDeclaration( |
| TypeParameterScopeKind.extensionDeclaration, "extension"); |
| } |
| |
| @override |
| void beginExtensionDeclaration(Token extensionKeyword, Token? nameToken) { |
| assert(checkState(extensionKeyword, |
| [ValueKinds.TypeVariableListOrNull, ValueKinds.MetadataListOrNull])); |
| debugEvent("beginExtensionDeclaration"); |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| int offset = nameToken?.charOffset ?? extensionKeyword.charOffset; |
| String name = nameToken?.lexeme ?? |
| // Synthesized name used internally. |
| '_extension#${unnamedExtensionCounter++}'; |
| push(name); |
| push(offset); |
| push(typeVariables ?? NullValue.TypeVariables); |
| libraryBuilder.currentTypeParameterScopeBuilder |
| .markAsExtensionDeclaration(name, offset, typeVariables); |
| } |
| |
| @override |
| void endExtensionDeclaration(Token extensionKeyword, Token? typeKeyword, |
| Token onKeyword, Token? showKeyword, Token? hideKeyword, Token endToken) { |
| assert(checkState(extensionKeyword, [ |
| /* hide type elements = */ ValueKinds.TypeBuilderListOrNull, |
| /* hide get elements = */ ValueKinds.NameListOrNull, |
| /* hide member or type elements = */ ValueKinds.NameListOrNull, |
| /* hide set elements = */ ValueKinds.NameListOrNull, |
| /* hide operator elements = */ ValueKinds.OperatorListOrNull, |
| /* show type elements = */ ValueKinds.TypeBuilderListOrNull, |
| /* show get elements = */ ValueKinds.NameListOrNull, |
| /* show member or type elements = */ ValueKinds.NameListOrNull, |
| /* show set elements = */ ValueKinds.NameListOrNull, |
| /* show operator elements = */ ValueKinds.OperatorListOrNull, |
| unionOfKinds([ValueKinds.ParserRecovery, ValueKinds.TypeBuilder]), |
| ValueKinds.TypeVariableListOrNull, |
| ValueKinds.Integer, |
| ValueKinds.NameOrNull, |
| ValueKinds.MetadataListOrNull |
| ])); |
| debugEvent("endExtensionDeclaration"); |
| |
| List<TypeBuilder>? hiddenSupertypes = pop() as List<TypeBuilder>?; |
| List<String>? hiddenGetters = pop() as List<String>?; |
| List<String>? hiddenMembersOrTypes = pop() as List<String>?; |
| List<String>? hiddenSetters = pop() as List<String>?; |
| List<Operator>? hiddenOperators = pop() as List<Operator>?; |
| |
| List<TypeBuilder>? shownSupertypes = pop() as List<TypeBuilder>?; |
| List<String>? shownGetters = pop() as List<String>?; |
| List<String>? shownMembersOrTypes = pop() as List<String>?; |
| List<String>? shownSetters = pop() as List<String>?; |
| List<Operator>? shownOperators = pop() as List<Operator>?; |
| |
| ExtensionTypeShowHideClauseBuilder extensionTypeShowHideClauseBuilder = |
| new ExtensionTypeShowHideClauseBuilder( |
| shownSupertypes: shownSupertypes ?? const <TypeBuilder>[], |
| shownGetters: shownGetters ?? const <String>[], |
| shownSetters: shownSetters ?? const <String>[], |
| shownMembersOrTypes: shownMembersOrTypes ?? const <String>[], |
| shownOperators: shownOperators ?? const <Operator>[], |
| hiddenSupertypes: hiddenSupertypes ?? const <TypeBuilder>[], |
| hiddenGetters: hiddenGetters ?? const <String>[], |
| hiddenSetters: hiddenSetters ?? const <String>[], |
| hiddenMembersOrTypes: hiddenMembersOrTypes ?? const <String>[], |
| hiddenOperators: hiddenOperators ?? const <Operator>[]); |
| |
| if (showKeyword != null && !libraryBuilder.enableExtensionTypesInLibrary) { |
| addProblem( |
| templateExperimentNotEnabled.withArguments('extension-types', |
| libraryBuilder.enableExtensionTypesVersionInLibrary.toText()), |
| showKeyword.charOffset, |
| showKeyword.length); |
| } |
| Object? onType = pop(); |
| if (onType is ParserRecovery) { |
| ParserRecovery parserRecovery = onType; |
| onType = new FixedTypeBuilder( |
| const InvalidType(), uri, parserRecovery.charOffset); |
| } |
| List<TypeVariableBuilder>? typeVariables = |
| pop(NullValue.TypeVariables) as List<TypeVariableBuilder>?; |
| int nameOffset = popCharOffset(); |
| String? name = pop(NullValue.Name) as String?; |
| if (name == null) { |
| nameOffset = extensionKeyword.charOffset; |
| name = '$nameOffset'; |
| } |
| List<MetadataBuilder>? metadata = |
| pop(NullValue.Metadata) as List<MetadataBuilder>?; |
| checkEmpty(extensionKeyword.charOffset); |
| int startOffset = metadata == null |
| ? extensionKeyword.charOffset |
| : metadata.first.charOffset; |
| bool isExtensionTypeDeclaration = typeKeyword != null; |
| if (!libraryBuilder.enableExtensionTypesInLibrary && |
| isExtensionTypeDeclaration) { |
| addProblem( |
| templateExperimentNotEnabled.withArguments('extension-types', |
| libraryBuilder.enableExtensionTypesVersionInLibrary.toText()), |
| extensionKeyword.next!.charOffset, |
| extensionKeyword.next!.length); |
| } |
| libraryBuilder.addExtensionDeclaration( |
| metadata, |
| // TODO(johnniwinther): Support modifiers on extensions? |
| 0, |
| name, |
| typeVariables, |
| onType as TypeBuilder, |
| extensionTypeShowHideClauseBuilder, |
| isExtensionTypeDeclaration, |
| startOffset, |
| nameOffset, |
| endToken.charOffset); |
| popDeclarationContext(DeclarationContext.Extension); |
| } |
| |
| ProcedureKind computeProcedureKind(Token? token) { |
| if (token == null) return ProcedureKind.Method; |
| if (optional("get", token)) return ProcedureKind.Getter; |
| if (optional("set", token)) return ProcedureKind.Setter; |
| return unhandled( |
| token.lexeme, "computeProcedureKind", token.charOffset, uri); |
| } |
| |
| @override |
| void beginTopLevelMethod(Token lastConsumed, Token? externalToken) { |
| pushDeclarationContext(DeclarationContext.TopLevelMethod); |
| libraryBuilder.beginNestedDeclaration( |
| TypeParameterScopeKind.topLevelMethod, "#method", |
| hasMembers: false); |
| push(externalToken != null ? externalMask : 0); |
| } |
| |
| @override |
| void endTopLevelMethod(Token beginToken, Token? getOrSet, Token endToken) { |
| debugEvent("endTopLevelMethod"); |
| MethodBody kind = pop() as MethodBody; |
| AsyncMarker asyncModifier = pop() as AsyncMarker; |
| List<FormalParameterBuilder>? formals = |
| pop() as List<FormalParameterBuilder>?; |
| int formalsOffset = popCharOffset(); |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| int charOffset = popCharOffset(); |
| Object? name = pop(); |
| TypeBuilder? returnType = pop() as TypeBuilder?; |
| bool isAbstract = kind == MethodBody.Abstract; |
| if (getOrSet != null && optional("set", getOrSet)) { |
| 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; |
| } |
| } |
| int modifiers = pop() as int; |
| modifiers = Modifier.addAbstractMask(modifiers, isAbstract: isAbstract); |
| if (nativeMethodName != null) { |
| modifiers |= externalMask; |
| } |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| checkEmpty(beginToken.charOffset); |
| libraryBuilder |
| .endNestedDeclaration(TypeParameterScopeKind.topLevelMethod, "#method") |
| .resolveNamedTypes(typeVariables, libraryBuilder); |
| if (name is! ParserRecovery) { |
| final int startCharOffset = |
| metadata == null ? beginToken.charOffset : metadata.first.charOffset; |
| libraryBuilder.addProcedure( |
| metadata, |
| modifiers, |
| returnType, |
| name as String, |
| typeVariables, |
| formals, |
| computeProcedureKind(getOrSet), |
| startCharOffset, |
| charOffset, |
| formalsOffset, |
| endToken.charOffset, |
| nativeMethodName, |
| asyncModifier, |
| isInstanceMember: false, |
| isExtensionMember: false); |
| nativeMethodName = null; |
| } |
| popDeclarationContext(DeclarationContext.TopLevelMethod); |
| } |
| |
| @override |
| void handleNativeFunctionBody(Token nativeToken, Token semicolon) { |
| debugEvent("NativeFunctionBody"); |
| if (nativeMethodName != null) { |
| push(MethodBody.Regular); |
| } else { |
| push(MethodBody.Abstract); |
| } |
| } |
| |
| @override |
| 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) { |
| 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? externalToken, |
| Token? staticToken, |
| Token? covariantToken, |
| Token? varFinalOrConst, |
| Token? getOrSet, |
| Token name) { |
| inConstructor = |
| name.lexeme == libraryBuilder.currentTypeParameterScopeBuilder.name && |
| getOrSet == null; |
| DeclarationContext declarationContext; |
| switch (declarationKind) { |
| case DeclarationKind.TopLevel: |
| 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.Enum: |
| if (inConstructor) { |
| declarationContext = DeclarationContext.EnumConstructor; |
| } else if (staticToken != null) { |
| declarationContext = DeclarationContext.EnumStaticMethod; |
| } else { |
| declarationContext = DeclarationContext.EnumInstanceMethod; |
| } |
| } |
| pushDeclarationContext(declarationContext); |
| |
| List<Modifier>? modifiers; |
| if (externalToken != null) { |
| modifiers ??= <Modifier>[]; |
| modifiers.add(External); |
| } |
| if (staticToken != null) { |
| if (!inConstructor) { |
| modifiers ??= <Modifier>[]; |
| modifiers.add(Static); |
| } |
| } |
| if (covariantToken != null) { |
| modifiers ??= <Modifier>[]; |
| modifiers.add(Covariant); |
| } |
| if (varFinalOrConst != null) { |
| String lexeme = varFinalOrConst.lexeme; |
| if (identical('var', lexeme)) { |
| modifiers ??= <Modifier>[]; |
| modifiers.add(Var); |
| } else if (identical('final', lexeme)) { |
| modifiers ??= <Modifier>[]; |
| modifiers.add(Final); |
| } else { |
| modifiers ??= <Modifier>[]; |
| modifiers.add(Const); |
| } |
| } |
| push(varFinalOrConst?.charOffset ?? -1); |
| push(modifiers ?? NullValue.Modifiers); |
| TypeParameterScopeKind kind; |
| if (inConstructor) { |
| kind = TypeParameterScopeKind.constructor; |
| } else if (staticToken != null) { |
| kind = TypeParameterScopeKind.staticMethod; |
| } else { |
| kind = TypeParameterScopeKind.instanceMethod; |
| } |
| libraryBuilder.beginNestedDeclaration(kind, "#method", hasMembers: false); |
| } |
| |
| @override |
| void endClassMethod(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.classMethod); |
| } |
| |
| @override |
| void endClassConstructor(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.classConstructor); |
| } |
| |
| @override |
| void endMixinMethod(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.mixinMethod); |
| } |
| |
| @override |
| void endExtensionMethod(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.extensionMethod); |
| } |
| |
| @override |
| void endMixinConstructor(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken) { |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.mixinConstructor); |
| } |
| |
| @override |
| void endExtensionConstructor(Token? getOrSet, Token beginToken, |
| Token beginParam, Token? beginInitializers, Token endToken) { |
| _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers, |
| endToken, _MethodKind.extensionConstructor); |
| } |
| |
| void _endClassMethod(Token? getOrSet, Token beginToken, Token beginParam, |
| Token? beginInitializers, Token endToken, _MethodKind methodKind) { |
| assert(checkState(beginToken, [ValueKinds.MethodBody])); |
| debugEvent("Method"); |
| 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.TypeVariableListOrNull, |
| ValueKinds.Integer, // name offset |
| unionOfKinds([ |
| ValueKinds.Name, |
| ValueKinds.QualifiedName, |
| ValueKinds.Operator, |
| ValueKinds.ParserRecovery, |
| ]), |
| ValueKinds.TypeBuilderOrNull, |
| ValueKinds.ModifiersOrNull, |
| ValueKinds.Integer, // var/final/const offset |
| ValueKinds.MetadataListOrNull, |
| ])); |
| AsyncMarker asyncModifier = pop() as AsyncMarker; |
| List<FormalParameterBuilder>? formals = |
| pop() as List<FormalParameterBuilder>?; |
| int formalsOffset = popCharOffset(); |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| int charOffset = popCharOffset(); |
| Object? nameOrOperator = pop(); |
| if (Operator.subtract == nameOrOperator && formals == null) { |
| nameOrOperator = Operator.unaryMinus; |
| } |
| Object? name; |
| ProcedureKind kind; |
| if (nameOrOperator is Operator) { |
| name = operatorToString(nameOrOperator); |
| kind = ProcedureKind.Operator; |
| int requiredArgumentCount = operatorRequiredArgumentCount(nameOrOperator); |
| if ((formals?.length ?? 0) != requiredArgumentCount) { |
| Template<Message Function(String name)> template; |
| switch (requiredArgumentCount) { |
| case 0: |
| template = templateOperatorParameterMismatch0; |
| break; |
| |
| case 1: |
| if (Operator.subtract == nameOrOperator) { |
| template = templateOperatorMinusParameterMismatch; |
| } else { |
| template = templateOperatorParameterMismatch1; |
| } |
| break; |
| |
| case 2: |
| template = templateOperatorParameterMismatch2; |
| break; |
| |
| default: |
| unhandled("$requiredArgumentCount", "operatorRequiredArgumentCount", |
| charOffset, uri); |
| } |
| String string = name as String; |
| addProblem(template.withArguments(string), charOffset, string.length); |
| } else { |
| if (formals != null) { |
| for (FormalParameterBuilder formal in formals) { |
| if (!formal.isRequired) { |
| addProblem(messageOperatorWithOptionalFormals, formal.charOffset, |
| formal.name.length); |
| } |
| } |
| } |
| } |
| if (typeVariables != null) { |
| TypeVariableBuilder typeVariableBuilder = typeVariables.first; |
| addProblem(messageOperatorWithTypeParameters, |
| typeVariableBuilder.charOffset, typeVariableBuilder.name.length); |
| } |
| } else { |
| name = nameOrOperator; |
| kind = computeProcedureKind(getOrSet); |
| } |
| TypeBuilder? returnType = pop() as TypeBuilder?; |
| bool isAbstract = bodyKind == MethodBody.Abstract; |
| if (getOrSet != null && optional("set", getOrSet)) { |
| 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 ?? beginToken.charOffset, noLength); |
| // Use implicit void as recovery. |
| returnType = null; |
| } |
| } |
| if (nameOrOperator == Operator.indexSet && |
| returnType != null && |
| !returnType.isVoidType) { |
| addProblem(messageNonVoidReturnOperator, |
| returnType.charOffset ?? beginToken.offset, noLength); |
| // Use implicit void as recovery. |
| returnType = null; |
| } |
| int modifiers = Modifier.toMask(pop() as List<Modifier>?); |
| modifiers = Modifier.addAbstractMask(modifiers, isAbstract: isAbstract); |
| if (nativeMethodName != null) { |
| modifiers |= externalMask; |
| } |
| bool isConst = (modifiers & constMask) != 0; |
| int varFinalOrConstOffset = popCharOffset(); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| |
| TypeParameterScopeKind scopeKind; |
| if (inConstructor) { |
| scopeKind = TypeParameterScopeKind.constructor; |
| } else if ((modifiers & staticMask) != 0) { |
| scopeKind = TypeParameterScopeKind.staticMethod; |
| } else { |
| scopeKind = TypeParameterScopeKind.instanceMethod; |
| } |
| TypeParameterScopeBuilder declarationBuilder = |
| libraryBuilder.endNestedDeclaration(scopeKind, "#method"); |
| if (name is ParserRecovery) { |
| nativeMethodName = null; |
| inConstructor = false; |
| declarationBuilder.resolveNamedTypes(typeVariables, libraryBuilder); |
| } else { |
| String? constructorName; |
| switch (methodKind) { |
| case _MethodKind.classConstructor: |
| case _MethodKind.mixinConstructor: |
| case _MethodKind.extensionConstructor: |
| case _MethodKind.enumConstructor: |
| constructorName = libraryBuilder.computeAndValidateConstructorName( |
| name, charOffset) ?? |
| name as String?; |
| break; |
| case _MethodKind.classMethod: |
| case _MethodKind.mixinMethod: |
| case _MethodKind.extensionMethod: |
| break; |
| } |
| bool isStatic = (modifiers & staticMask) != 0; |
| if (constructorName == null && |
| !isStatic && |
| libraryBuilder.currentTypeParameterScopeBuilder.kind == |
| TypeParameterScopeKind.extensionDeclaration) { |
| TypeParameterScopeBuilder extension = |
| libraryBuilder.currentTypeParameterScopeBuilder; |
| Map<TypeVariableBuilder, TypeBuilder>? substitution; |
| if (extension.typeVariables != null) { |
| // We synthesize the names of the generated [TypeParameter]s, i.e. |
| // rename 'T' to '#T'. We cannot do it on the builders because their |
| // names are used to create the scope. |
| List<TypeVariableBuilder> synthesizedTypeVariables = libraryBuilder |
| .copyTypeVariables(extension.typeVariables!, declarationBuilder, |
| isExtensionTypeParameter: true); |
| substitution = {}; |
| for (int i = 0; i < synthesizedTypeVariables.length; i++) { |
| substitution[extension.typeVariables![i]] = |
| new NamedTypeBuilder.fromTypeDeclarationBuilder( |
| synthesizedTypeVariables[i], |
| const NullabilityBuilder.omitted(), |
| instanceTypeVariableAccess: |
| declarationContext.instanceTypeVariableAccessState); |
| } |
| if (typeVariables != null) { |
| typeVariables = synthesizedTypeVariables..addAll(typeVariables); |
| } else { |
| typeVariables = synthesizedTypeVariables; |
| } |
| } |
| List<FormalParameterBuilder> synthesizedFormals = []; |
| TypeBuilder thisType = extension.extensionThisType; |
| if (substitution != null) { |
| List<NamedTypeBuilder> unboundTypes = []; |
| List<TypeVariableBuilder> unboundTypeVariables = []; |
| thisType = substitute(thisType, substitution, |
| unboundTypes: unboundTypes, |
| unboundTypeVariables: unboundTypeVariables)!; |
| for (NamedTypeBuilder unboundType in unboundTypes) { |
| extension.registerUnresolvedNamedType(unboundType); |
| } |
| libraryBuilder.unboundTypeVariables.addAll(unboundTypeVariables); |
| } |
| synthesizedFormals.add(new FormalParameterBuilder( |
| null, finalMask, thisType, extensionThisName, null, charOffset, |
| fileUri: uri, isExtensionThis: true)); |
| if (formals != null) { |
| synthesizedFormals.addAll(formals); |
| } |
| formals = synthesizedFormals; |
| } |
| |
| declarationBuilder.resolveNamedTypes(typeVariables, libraryBuilder); |
| if (constructorName != null) { |
| if (isConst && |
| bodyKind != MethodBody.Abstract && |
| !libraryBuilder.enableConstFunctionsInLibrary) { |
| addProblem(messageConstConstructorWithBody, varFinalOrConstOffset, 5); |
| modifiers &= ~constMask; |
| } |
| if (returnType != null) { |
| addProblem(messageConstructorWithReturnType, |
| returnType.charOffset ?? beginToken.offset, noLength); |
| returnType = null; |
| } |
| final int startCharOffset = metadata == null |
| ? beginToken.charOffset |
| : metadata.first.charOffset; |
| libraryBuilder.addConstructor( |
| metadata, |
| modifiers, |
| returnType, |
| name, |
| constructorName, |
| typeVariables, |
| formals, |
| startCharOffset, |
| charOffset, |
| formalsOffset, |
| endToken.charOffset, |
| nativeMethodName, |
| beginInitializers: beginInitializers, |
| forAbstractClass: inAbstractClass); |
| } else { |
| if (isConst) { |
| // TODO(danrubel): consider removing this |
| // because it is an error to have a const method. |
| modifiers &= ~constMask; |
| } |
| final int startCharOffset = metadata == null |
| ? beginToken.charOffset |
| : metadata.first.charOffset; |
| bool isExtensionMember = methodKind == _MethodKind.extensionMethod; |
| libraryBuilder.addProcedure( |
| metadata, |
| modifiers, |
| returnType, |
| name as String, |
| typeVariables, |
| formals, |
| kind, |
| startCharOffset, |
| charOffset, |
| formalsOffset, |
| endToken.charOffset, |
| nativeMethodName, |
| asyncModifier, |
| isInstanceMember: !isStatic, |
| isExtensionMember: isExtensionMember); |
| } |
| } |
| nativeMethodName = null; |
| inConstructor = false; |
| popDeclarationContext(); |
| } |
| |
| @override |
| void handleNamedMixinApplicationWithClause(Token withKeyword) { |
| debugEvent("NamedMixinApplicationWithClause"); |
| Object? mixins = pop(); |
| Object? supertype = pop(); |
| if (mixins is ParserRecovery) { |
| push(mixins); |
| } else if (supertype is ParserRecovery) { |
| push(supertype); |
| } else { |
| push(libraryBuilder.addMixinApplication(supertype as TypeBuilder?, |
| mixins as List<TypeBuilder>, withKeyword.charOffset)); |
| } |
| } |
| |
| @override |
| void endNamedMixinApplication(Token beginToken, Token classKeyword, |
| Token equals, Token? implementsKeyword, Token endToken) { |
| debugEvent("endNamedMixinApplication"); |
| assert(checkState(beginToken, [ |
| if (implementsKeyword != null) |
| /* interfaces */ ValueKinds.TypeBuilderListOrNull, |
| /* mixin application */ unionOfKinds([ |
| ValueKinds.ParserRecovery, |
| ValueKinds.TypeBuilder, |
| ]), |
| /* macro token */ ValueKinds.TokenOrNull, |
| /* modifiers */ ValueKinds.Integer, |
| /* type variables */ ValueKinds.TypeVariableListOrNull, |
| /* name offset */ ValueKinds.Integer, |
| /* name */ ValueKinds.NameOrParserRecovery, |
| /* metadata */ ValueKinds.MetadataListOrNull, |
| ])); |
| |
| List<TypeBuilder>? interfaces = |
| popIfNotNull(implementsKeyword) as List<TypeBuilder>?; |
| Object? mixinApplication = pop(); |
| Token? macroToken = pop(NullValue.Token) as Token?; |
| int modifiers = pop() as int; |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| int charOffset = popCharOffset(); |
| Object? name = pop(); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| checkEmpty(beginToken.charOffset); |
| if (name is ParserRecovery || mixinApplication is ParserRecovery) { |
| libraryBuilder |
| .endNestedDeclaration( |
| TypeParameterScopeKind.namedMixinApplication, "<syntax-error>") |
| .resolveNamedTypes(typeVariables, libraryBuilder); |
| } else { |
| if (libraryBuilder.isNonNullableByDefault) { |
| String classNameForErrors = "${name}"; |
| MixinApplicationBuilder mixinApplicationBuilder = |
| mixinApplication as MixinApplicationBuilder; |
| TypeBuilder? supertype = mixinApplicationBuilder.supertype; |
| List<TypeBuilder> mixins = mixinApplicationBuilder.mixins; |
| if (supertype != null && supertype is! MixinApplicationBuilder) { |
| if (supertype.nullabilityBuilder.build(libraryBuilder) == |
| Nullability.nullable) { |
| libraryBuilder.addProblem( |
| templateNullableSuperclassError |
| .withArguments(supertype.fullNameForErrors), |
| charOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| for (TypeBuilder mixin in mixins) { |
| if (mixin.nullabilityBuilder.build(libraryBuilder) == |
| Nullability.nullable) { |
| libraryBuilder.addProblem( |
| templateNullableMixinError |
| .withArguments(mixin.fullNameForErrors), |
| charOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| if (interfaces != null) { |
| for (TypeBuilder interface in interfaces) { |
| if (interface.nullabilityBuilder.build(libraryBuilder) == |
| Nullability.nullable) { |
| libraryBuilder.addProblem( |
| templateNullableInterfaceError |
| .withArguments(interface.fullNameForErrors), |
| charOffset, |
| classNameForErrors.length, |
| uri); |
| } |
| } |
| } |
| } |
| |
| int startCharOffset = beginToken.charOffset; |
| int charEndOffset = endToken.charOffset; |
| libraryBuilder.addNamedMixinApplication( |
| metadata, |
| name as String, |
| typeVariables, |
| modifiers, |
| mixinApplication as TypeBuilder?, |
| interfaces, |
| startCharOffset, |
| charOffset, |
| charEndOffset, |
| isMacro: macroToken != null); |
| } |
| popDeclarationContext(DeclarationContext.NamedMixinApplication); |
| } |
| |
| @override |
| void endTypeArguments(int count, Token beginToken, Token endToken) { |
| debugEvent("TypeArguments"); |
| push(const FixedNullableList<TypeBuilder>() |
| .popNonNullable(stack, count, dummyTypeBuilder) ?? |
| NullValue.TypeArguments); |
| } |
| |
| @override |
| void endArguments(int count, Token beginToken, Token endToken) { |
| debugEvent("Arguments"); |
| push(NullValue.Arguments); |
| } |
| |
| @override |
| void handleInvalidTypeArguments(Token token) { |
| debugEvent("InvalidTypeArguments"); |
| pop(NullValue.TypeArguments); |
| } |
| |
| @override |
| void handleScript(Token token) { |
| debugEvent("Script"); |
| } |
| |
| @override |
| void handleNonNullAssertExpression(Token bang) { |
| if (!libraryBuilder.isNonNullableByDefault) { |
| reportNonNullAssertExpressionNotEnabled(bang); |
| } |
| } |
| |
| @override |
| void handleType(Token beginToken, Token? questionMark) { |
| debugEvent("Type"); |
| if (!libraryBuilder.isNonNullableByDefault) { |
| reportErrorIfNullableType(questionMark); |
| } |
| bool isMarkedAsNullable = questionMark != null; |
| List<TypeBuilder>? arguments = pop() as List<TypeBuilder>?; |
| int charOffset = popCharOffset(); |
| Object name = pop()!; |
| if (name is ParserRecovery) { |
| push(name); |
| } else { |
| push(libraryBuilder.addNamedType( |
| name, |
| libraryBuilder.nullableBuilderIfTrue(isMarkedAsNullable), |
| arguments, |
| charOffset, |
| 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(NullValue.Arguments); |
| } |
| |
| @override |
| void handleNoTypeVariables(Token token) { |
| super.handleNoTypeVariables(token); |
| inConstructorName = false; |
| } |
| |
| @override |
| void handleNoTypeArguments(Token token) { |
| debugEvent("NoTypeArguments"); |
| push(NullValue.TypeArguments); |
| } |
| |
| @override |
| void handleNoTypeNameInConstructorReference(Token token) { |
| debugEvent("NoTypeNameInConstructorReference"); |
| push(NullValue.Name); |
| push(token.charOffset); |
| } |
| |
| @override |
| void handleVoidKeyword(Token token) { |
| debugEvent("VoidKeyword"); |
| push(libraryBuilder.addVoidType(token.charOffset)); |
| } |
| |
| @override |
| void handleVoidKeywordWithTypeArguments(Token token) { |
| debugEvent("VoidKeyword"); |
| /*List<TypeBuilder> arguments =*/ pop(); |
| push(libraryBuilder.addVoidType(token.charOffset)); |
| } |
| |
| @override |
| void beginFormalParameter(Token token, MemberKind kind, Token? requiredToken, |
| Token? covariantToken, Token? varFinalOrConst) { |
| if (requiredToken != null && !libraryBuilder.isNonNullableByDefault) { |
| reportNonNullableModifierError(requiredToken); |
| } |
| push((covariantToken != null ? covariantMask : 0) | |
| (requiredToken != null ? requiredMask : 0) | |
| Modifier.validateVarFinalOrConst(varFinalOrConst?.lexeme)); |
| } |
| |
| @override |
| void endFormalParameter( |
| Token? thisKeyword, |
| Token? superKeyword, |
| Token? periodAfterThisOrSuper, |
| Token nameToken, |
| Token? initializerStart, |
| Token? initializerEnd, |
| FormalParameterKind kind, |
| MemberKind memberKind) { |
| debugEvent("FormalParameter"); |
| |
| if (superKeyword != null && |
| !libraryBuilder.enableSuperParametersInLibrary) { |
| addProblem( |
| templateExperimentNotEnabled.withArguments( |
| 'super-parameters', |
| libraryBuilder.enableConstructorTearOffsVersionInLibrary |
| .toText()), |
| superKeyword.charOffset, |
| superKeyword.length); |
| } |
| |
| int charOffset = popCharOffset(); |
| Object? name = pop(); |
| TypeBuilder? type = nullIfParserRecovery(pop()) as TypeBuilder?; |
| int modifiers = pop() as int; |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| if (name is ParserRecovery) { |
| push(name); |
| } else { |
| push(libraryBuilder.addFormalParameter( |
| metadata, |
| modifiers, |
| type, |
| name == null ? FormalParameterBuilder.noNameSentinel : name as String, |
| thisKeyword != null, |
| superKeyword != null, |
| charOffset, |
| initializerStart)); |
| } |
| } |
| |
| @override |
| void beginFormalParameterDefaultValueExpression() { |
| // Ignored for now. |
| } |
| |
| @override |
| void endFormalParameterDefaultValueExpression() { |
| debugEvent("FormalParameterDefaultValueExpression"); |
| // Ignored for now. |
| } |
| |
| @override |
| void handleValuedFormalParameter(Token equals, Token token) { |
| debugEvent("ValuedFormalParameter"); |
| // Ignored for now. |
| } |
| |
| @override |
| void handleFormalParameterWithoutValue(Token token) { |
| debugEvent("FormalParameterWithoutValue"); |
| // Ignored for now. |
| } |
| |
| @override |
| void endOptionalFormalParameters( |
| int count, Token beginToken, Token endToken) { |
| debugEvent("OptionalFormalParameters"); |
| FormalParameterKind kind = optional("{", beginToken) |
| ? FormalParameterKind.optionalNamed |
| : FormalParameterKind.optionalPositional; |
| // 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 { |
| for (FormalParameterBuilder parameter in parameters) { |
| parameter.kind = kind; |
| } |
| 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]; |
| } |
| } else if (count > 1) { |
| Object? last = pop(); |
| count--; |
| if (last is ParserRecovery) { |
| 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) { |
| 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.name == FormalParameterBuilder.noNameSentinel) continue; |
| if (seenNames.containsKey(formal.name)) { |
| 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; |
| } |
| } |
| } |
| } |
| push(beginToken.charOffset); |
| push(formals ?? NullValue.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 semicolonToken) { |
| debugEvent("Assert"); |
| // Do nothing |
| } |
| |
| @override |
| void beginEnum(Token enumKeyword) { |
| assert(checkState( |
| enumKeyword, [ValueKinds.Integer, ValueKinds.NameOrParserRecovery])); |
| int offset = pop() as int; |
| Object? name = pop(); |
| push(name); |
| push(offset); |
| |
| String declarationName; |
| if (name is String) { |
| declarationName = name; |
| } else { |
| declarationName = '#enum'; |
| } |
| pushDeclarationContext(DeclarationContext.Enum); |
| libraryBuilder.beginNestedDeclaration( |
| TypeParameterScopeKind.enumDeclaration, declarationName); |
| } |
| |
| @override |
| void handleEnumElement(Token beginToken) { |
| debugEvent("EnumElements"); |
| pop(); // arguments. |
| pop(); // constructor reference. |
| // Keep on the stack the EnumConstantInfo created in handleIdentifier. |
| } |
| |
| @override |
| void handleEnumHeader(Token enumKeyword, Token leftBrace) { |
| debugEvent("EnumHeader"); |
| push(enumKeyword.charOffset); // start char offset. |
| push(leftBrace.endGroup!.charOffset); // end char offset. |
| } |
| |
| @override |
| void handleEnumElements(Token elementsEndToken, int elementsCount) { |
| debugEvent("EnumElements"); |
| push(elementsCount); |
| } |
| |
| @override |
| void endEnum(Token enumKeyword, Token leftBrace, int memberCount) { |
| debugEvent("Enum"); |
| |
| int elementsCount = pop() as int; |
| List<EnumConstantInfo?>? enumConstantInfos = |
| const FixedNullableList<EnumConstantInfo>().pop(stack, elementsCount); |
| int endCharOffset = popCharOffset(); |
| int startCharOffset = popCharOffset(); |
| pop() as List<TypeBuilder>?; // interfaces. |
| pop() as List<TypeBuilder>?; // mixins. |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| int charOffset = popCharOffset(); // identifier char offset. |
| Object? name = pop(); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| checkEmpty(startCharOffset); |
| |
| if (name is! ParserRecovery) { |
| libraryBuilder.addEnum(metadata, name as String, typeVariables, |
| enumConstantInfos, startCharOffset, charOffset, endCharOffset); |
| } else { |
| libraryBuilder |
| .endNestedDeclaration( |
| TypeParameterScopeKind.enumDeclaration, "<syntax-error>") |
| .resolveNamedTypes(typeVariables, libraryBuilder); |
| } |
| |
| checkEmpty(enumKeyword.charOffset); |
| popDeclarationContext(DeclarationContext.Enum); |
| } |
| |
| @override |
| void beginTypedef(Token token) { |
| pushDeclarationContext(DeclarationContext.Typedef); |
| libraryBuilder.beginNestedDeclaration( |
| TypeParameterScopeKind.typedef, "#typedef", |
| hasMembers: false); |
| } |
| |
| @override |
| void beginFunctionType(Token beginToken) { |
| debugEvent("beginFunctionType"); |
| libraryBuilder.beginNestedDeclaration( |
| TypeParameterScopeKind.functionType, "#function_type", |
| hasMembers: false); |
| } |
| |
| @override |
| void beginFunctionTypedFormalParameter(Token token) { |
| debugEvent("beginFunctionTypedFormalParameter"); |
| libraryBuilder.beginNestedDeclaration( |
| TypeParameterScopeKind.functionType, "#function_type", |
| hasMembers: false); |
| } |
| |
| @override |
| void endFunctionType(Token functionToken, Token? questionMark) { |
| debugEvent("FunctionType"); |
| if (!libraryBuilder.isNonNullableByDefault) { |
| reportErrorIfNullableType(questionMark); |
| } |
| List<FormalParameterBuilder>? formals = |
| pop() as List<FormalParameterBuilder>?; |
| pop(); // formals offset |
| TypeBuilder? returnType = pop() as TypeBuilder?; |
| List<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| push(libraryBuilder.addFunctionType( |
| returnType, |
| typeVariables, |
| formals, |
| libraryBuilder.nullableBuilderIfTrue(questionMark != null), |
| uri, |
| functionToken.charOffset)); |
| } |
| |
| @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<TypeVariableBuilder>? typeVariables = |
| pop() as List<TypeVariableBuilder>?; |
| if (!libraryBuilder.isNonNullableByDefault) { |
| reportErrorIfNullableType(question); |
| } |
| push(libraryBuilder.addFunctionType( |
| returnType, |
| typeVariables, |
| formals, |
| libraryBuilder.nullableBuilderIfTrue(question != null), |
| uri, |
| formalsOffset)); |
| } |
| |
| @override |
| void endTypedef(Token typedefKeyword, Token? equals, Token endToken) { |
| debugEvent("endFunctionTypeAlias"); |
| List<TypeVariableBuilder>? typeVariables; |
| Object? name; |
| int charOffset; |
| TypeBuilder aliasedType; |
| if (equals == null) { |
| List<FormalParameterBuilder>? formals = |
| pop() as List<FormalParameterBuilder>?; |
| pop(); // formals offset |
| typeVariables = pop() as List<TypeVariableBuilder>?; |
| charOffset = popCharOffset(); |
| name = pop(); |
| TypeBuilder? returnType = pop() as TypeBuilder?; |
| // Create a nested declaration that is ended below by |
| // `library.addFunctionType`. |
| if (name is ParserRecovery) { |
| pop(); // Metadata. |
| libraryBuilder |
| .endNestedDeclaration( |
| TypeParameterScopeKind.typedef, "<syntax-error>") |
| .resolveNamedTypes(typeVariables, libraryBuilder); |
| popDeclarationContext(DeclarationContext.Typedef); |
| return; |
| } |
| libraryBuilder.beginNestedDeclaration( |
| TypeParameterScopeKind.functionType, "#function_type", |
| hasMembers: false); |
| // TODO(dmitryas): Make sure that RHS of typedefs can't have '?'. |
| aliasedType = libraryBuilder.addFunctionType(returnType, null, formals, |
| const NullabilityBuilder.omitted(), uri, charOffset); |
| } else { |
| Object? type = pop(); |
| typeVariables = pop() as List<TypeVariableBuilder>?; |
| charOffset = popCharOffset(); |
| name = pop(); |
| if (name is ParserRecovery) { |
| pop(); // Metadata. |
| libraryBuilder |
| .endNestedDeclaration( |
| TypeParameterScopeKind.functionType, "<syntax-error>") |
| .resolveNamedTypes(typeVariables, libraryBuilder); |
| popDeclarationContext(DeclarationContext.Typedef); |
| return; |
| } |
| if (type is FunctionTypeBuilder && |
| !libraryBuilder.enableNonfunctionTypeAliasesInLibrary) { |
| if (type.nullabilityBuilder.build(libraryBuilder) == |
| Nullability.nullable && |
| libraryBuilder.isNonNullableByDefault) { |
| // The error is reported when the non-nullable experiment is enabled. |
| // Otherwise, the attempt to use a nullable type will be reported |
| // elsewhere. |
| addProblem( |
| messageTypedefNullableType, equals.charOffset, equals.length); |
| aliasedType = new NamedTypeBuilder.fromTypeDeclarationBuilder( |
| new InvalidTypeDeclarationBuilder( |
| "${name}", |
| messageTypedefNullableType.withLocation( |
| uri, equals.charOffset, equals.length)), |
| const NullabilityBuilder.omitted(), |
| instanceTypeVariableAccess: |
| InstanceTypeVariableAccessState.Allowed); |
| } else { |
| // TODO(ahe): We need to start a nested declaration when parsing the |
| // formals and return type so we can correctly bind |
| // `type.typeVariables`. A typedef can have type variables, and a new |
| // function type can also have type variables (representing the type |
| // of a generic function). |
| aliasedType = type; |
| } |
| } else if (libraryBuilder.enableNonfunctionTypeAliasesInLibrary) { |
| if (type is TypeBuilder) { |
| aliasedType = type; |
| } else { |
| addProblem(messageTypedefNotType, equals.charOffset, equals.length); |
| aliasedType = new NamedTypeBuilder.fromTypeDeclarationBuilder( |
| new InvalidTypeDeclarationBuilder( |
| "${name}", |
| messageTypedefNotType.withLocation( |
| uri, equals.charOffset, equals.length)), |
| const NullabilityBuilder.omitted(), |
| instanceTypeVariableAccess: |
| InstanceTypeVariableAccessState.Allowed); |
| } |
| } else { |
| assert(type is! FunctionTypeBuilder); |
| // TODO(ahe): Improve this error message. |
| if (type is TypeBuilder) { |
| addProblem( |
| messageTypedefNotFunction, equals.charOffset, equals.length); |
| aliasedType = new NamedTypeBuilder.fromTypeDeclarationBuilder( |
| new InvalidTypeDeclarationBuilder( |
| "${name}", |
| messageTypedefNotFunction.withLocation( |
| uri, equals.charOffset, equals.length)), |
| const NullabilityBuilder.omitted(), |
| instanceTypeVariableAccess: |
| InstanceTypeVariableAccessState.Allowed); |
| } else { |
| addProblem(messageTypedefNotType, equals.charOffset, equals.length); |
| aliasedType = new NamedTypeBuilder.fromTypeDeclarationBuilder( |
| new InvalidTypeDeclarationBuilder( |
| "${name}", |
| messageTypedefNotType.withLocation( |
| uri, equals.charOffset, equals.length)), |
| const NullabilityBuilder.omitted(), |
| instanceTypeVariableAccess: |
| InstanceTypeVariableAccessState.Allowed); |
| } |
| } |
| } |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| checkEmpty(typedefKeyword.charOffset); |
| libraryBuilder.addFunctionTypeAlias( |
| metadata, name as String, typeVariables, aliasedType, charOffset); |
| popDeclarationContext(DeclarationContext.Typedef); |
| } |
| |
| @override |
| void beginFields( |
| DeclarationKind declarationKind, |
| Token? abstractToken, |
| Token? externalToken, |
| Token? staticToken, |
| Token? covariantToken, |
| Token? lateToken, |
| Token? varFinalOrConst, |
| Token lastConsumed) { |
| DeclarationContext declarationContext; |
| switch (declarationKind) { |
| case DeclarationKind.TopLevel: |
| declarationContext = DeclarationContext.TopLevelField; |
| break; |
| case DeclarationKind.Class: |
| if (staticToken != null) { |
| declarationContext = DeclarationContext.ClassStaticField; |
| } else { |
| declarationContext = DeclarationContext.ClassInstanceField; |
| } |
| break; |
| case DeclarationKind.Mixin: |
| if (staticToken != null) { |
| declarationContext = DeclarationContext.MixinStaticField; |
| } else { |
| declarationContext = DeclarationContext.MixinInstanceField; |
| } |
| break; |
| case DeclarationKind.Extension: |
| if (staticToken != null) { |
| declarationContext = DeclarationContext.ExtensionStaticField; |
| } else if (externalToken != null) { |
| declarationContext = |
| DeclarationContext.ExtensionExternalInstanceField; |
| } else { |
| declarationContext = DeclarationContext.ExtensionInstanceField; |
| } |
| break; |
| case DeclarationKind.Enum: |
| if (staticToken != null) { |
| declarationContext = DeclarationContext.EnumStaticMethod; |
| } else { |
| declarationContext = DeclarationContext.EnumInstanceMethod; |
| } |
| } |
| pushDeclarationContext(declarationContext); |
| } |
| |
| @override |
| void endTopLevelFields( |
| Token? externalToken, |
| Token? staticToken, |
| Token? covariantToken, |
| Token? lateToken, |
| Token? varFinalOrConst, |
| int count, |
| Token beginToken, |
| Token endToken) { |
| debugEvent("endTopLevelFields"); |
| if (!libraryBuilder.isNonNullableByDefault) { |
| reportNonNullableModifierError(lateToken); |
| if (externalToken != null) { |
| handleRecoverableError( |
| messageExternalField, externalToken, externalToken); |
| externalToken = null; |
| } |
| } else { |
| if (externalToken != null && lateToken != null) { |
| handleRecoverableError( |
| messageExternalLateField, externalToken, externalToken); |
| externalToken = null; |
| } |
| } |
| List<FieldInfo>? fieldInfos = popFieldInfos(count); |
| TypeBuilder? type = nullIfParserRecovery(pop()) as TypeBuilder?; |
| int modifiers = (externalToken != null ? externalMask : 0) | |
| (staticToken != null ? staticMask : 0) | |
| (covariantToken != null ? covariantMask : 0) | |
| (lateToken != null ? lateMask : 0) | |
| Modifier.validateVarFinalOrConst(varFinalOrConst?.lexeme); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| checkEmpty(beginToken.charOffset); |
| if (fieldInfos != null) { |
| libraryBuilder.addFields( |
| metadata, modifiers, /* isTopLevel = */ true, type, fieldInfos); |
| } |
| popDeclarationContext(); |
| } |
| |
| @override |
| void endClassFields( |
| Token? abstractToken, |
| Token? externalToken, |
| Token? staticToken, |
| Token? covariantToken, |
| Token? lateToken, |
| Token? varFinalOrConst, |
| int count, |
| Token beginToken, |
| Token endToken) { |
| debugEvent("Fields"); |
| if (!libraryBuilder.isNonNullableByDefault) { |
| reportNonNullableModifierError(lateToken); |
| if (abstractToken != null) { |
| handleRecoverableError( |
| messageAbstractClassMember, abstractToken, abstractToken); |
| abstractToken = null; |
| } |
| if (externalToken != null) { |
| handleRecoverableError( |
| messageExternalField, externalToken, externalToken); |
| externalToken = null; |
| } |
| } else { |
| if (staticToken != null && abstractToken != null) { |
| handleRecoverableError( |
| messageAbstractStaticField, abstractToken, abstractToken); |
| abstractToken = null; |
| } |
| if (abstractToken != null && lateToken != null) { |
| handleRecoverableError( |
| messageAbstractLateField, abstractToken, abstractToken); |
| abstractToken = null; |
| } else if (externalToken != null && lateToken != null) { |
| handleRecoverableError( |
| messageExternalLateField, externalToken, externalToken); |
| externalToken = null; |
| } |
| } |
| List<FieldInfo>? fieldInfos = popFieldInfos(count); |
| TypeBuilder? type = pop() as TypeBuilder?; |
| int modifiers = (abstractToken != null ? abstractMask : 0) | |
| (externalToken != null ? externalMask : 0) | |
| (staticToken != null ? staticMask : 0) | |
| (covariantToken != null ? covariantMask : 0) | |
| (lateToken != null ? lateMask : 0) | |
| Modifier.validateVarFinalOrConst(varFinalOrConst?.lexeme); |
| if (staticToken == null && modifiers & constMask != 0) { |
| // It is a compile-time error if an instance variable is declared to be |
| // constant. |
| addProblem(messageConstInstanceField, varFinalOrConst!.charOffset, |
| varFinalOrConst.length); |
| modifiers &= ~constMask; |
| } |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| if (fieldInfos != null) { |
| libraryBuilder.addFields( |
| metadata, modifiers, /* isTopLevel = */ false, type, fieldInfos); |
| } |
| popDeclarationContext(); |
| } |
| |
| List<FieldInfo>? popFieldInfos(int count) { |
| if (count == 0) return null; |
| List<FieldInfo> fieldInfos = |
| new List<FieldInfo>.filled(count, dummyFieldInfo); |
| bool isParserRecovery = false; |
| for (int i = count - 1; i != -1; i--) { |
| int charEndOffset = popCharOffset(); |
| Token? beforeLast = pop() as Token?; |
| Token? initializerTokenForInference = pop() as Token?; |
| int charOffset = popCharOffset(); |
| Object? name = pop(NullValue.Identifier); |
| if (name is ParserRecovery) { |
| isParserRecovery = true; |
| } else { |
| fieldInfos[i] = new FieldInfo(name as String, charOffset, |
| initializerTokenForInference, beforeLast, charEndOffset); |
| } |
| } |
| return isParserRecovery ? null : fieldInfos; |
| } |
| |
| @override |
| void beginTypeVariable(Token token) { |
| debugEvent("beginTypeVariable"); |
| int charOffset = popCharOffset(); |
| Object? name = pop(); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| if (name is ParserRecovery) { |
| push(name); |
| } else { |
| push(libraryBuilder.addTypeVariable( |
| metadata, name as String, null, charOffset, uri)); |
| } |
| } |
| |
| @override |
| void handleTypeVariablesDefined(Token token, int count) { |
| debugEvent("TypeVariablesDefined"); |
| assert(count > 0); |
| push(const FixedNullableList<TypeVariableBuilder>() |
| .popNonNullable(stack, count, dummyTypeVariableBuilder) ?? |
| NullValue.TypeVariables); |
| } |
| |
| @override |
| void endTypeVariable( |
| Token token, int index, Token? extendsOrSuper, Token? variance) { |
| debugEvent("endTypeVariable"); |
| TypeBuilder? bound = nullIfParserRecovery(pop()) as TypeBuilder?; |
| // Peek to leave type parameters on top of stack. |
| List<TypeVariableBuilder>? typeParameters = |
| peek() as List<TypeVariableBuilder>?; |
| if (typeParameters != null) { |
| typeParameters[index].bound = bound; |
| if (variance != null) { |
| if (!libraryBuilder.enableVarianceInLibrary) { |
| reportVarianceModifierNotEnabled(variance); |
| } |
| typeParameters[index].variance = Variance.fromString(variance.lexeme); |
| } |
| } |
| } |
| |
| @override |
| void endTypeVariables(Token beginToken, Token endToken) { |
| debugEvent("endTypeVariables"); |
| |
| if (!libraryBuilder.enableEnhancedEnumsInLibrary && |
| declarationContext == DeclarationContext.Enum) { |
| addProblem( |
| templateExperimentNotEnabled.withArguments('enhanced-enums', |
| libraryBuilder.enableEnhancedEnumsVersionInLibrary.toText()), |
| beginToken.charOffset, |
| -1); |
| } |
| |
| // Peek to leave type parameters on top of stack. |
| List<TypeVariableBuilder>? typeParameters = |
| peek() as List<TypeVariableBuilder>?; |
| |
| Map<String, TypeVariableBuilder>? typeVariablesByName; |
| if (typeParameters != null) { |
| for (TypeVariableBuilder builder in typeParameters) { |
| if (builder.bound != null) { |
| if (typeVariablesByName == null) { |
| typeVariablesByName = new Map<String, TypeVariableBuilder>(); |
| for (TypeVariableBuilder builder in typeParameters) { |
| typeVariablesByName[builder.name] = builder; |
| } |
| } |
| |
| // Find cycle: If there's no cycle we can at most step through all |
| // `typeParameters` (at which point the last builders bound will be |
| // null). |
| // If there is a cycle with `builder` 'inside' the steps to get back |
| // to it will also be bound by `typeParameters.length`. |
| // If there is a cycle without `builder` 'inside' we will just ignore |
| // it for now. It will be reported when processing one of the |
| // `builder`s that is in fact `inside` the cycle. This matches the |
| // cyclic class hierarchy error. |
| TypeVariableBuilder? bound = builder; |
| for (int steps = 0; |
| bound!.bound != null && steps < typeParameters.length; |
| ++steps) { |
| bound = typeVariablesByName[bound.bound!.name]; |
| if (bound == null || bound == builder) break; |
| } |
| if (bound == builder && bound!.bound != null) { |
| // Write out cycle. |
| List<String> via = <String>[]; |
| bound = typeVariablesByName[builder.bound!.name]; |
| while (bound != builder) { |
| via.add(bound!.name); |
| bound = typeVariablesByName[bound.bound!.name]; |
| } |
| Message message = via.isEmpty |
| ? templateDirectCycleInTypeVariables.withArguments(builder.name) |
| : templateCycleInTypeVariables.withArguments( |
| builder.name, via.join("', '")); |
| addProblem(message, builder.charOffset, builder.name.length); |
| builder.bound = new NamedTypeBuilder( |
| builder.name, |
| const NullabilityBuilder.omitted(), |
| null, |
| uri, |
| builder.charOffset, |
| instanceTypeVariableAccess: |
| //InstanceTypeVariableAccessState.Unexpected |
| declarationContext.instanceTypeVariableAccessState) |
| ..bind(new InvalidTypeDeclarationBuilder( |
| builder.name, |
| message.withLocation( |
| uri, builder.charOffset, builder.name.length))); |
| } |
| } |
| } |
| } |
| |
| if (inConstructorName) { |
| addProblem(messageConstructorWithTypeParameters, |
| offsetForToken(beginToken), lengthOfSpan(beginToken, endToken)); |
| inConstructorName = false; |
| } |
| } |
| |
| @override |
| void endPartOf( |
| Token partKeyword, Token ofKeyword, Token semicolon, bool hasName) { |
| debugEvent("endPartOf"); |
| int charOffset = popCharOffset(); |
| Object? containingLibrary = pop(); |
| List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?; |
| if (hasName) { |
| libraryBuilder.addPartOf(metadata, |
| flattenName(containingLibrary!, charOffset, uri), null, charOffset); |
| } else { |
| libraryBuilder.addPartOf( |
| metadata, null, containingLibrary as String?, charOffset); |
| } |
| } |
| |
| @override |
| void endConstructorReference(Token start, Token? periodBeforeName, |
| Token endToken, ConstructorReferenceContext constructorReferenceContext) { |
| debugEvent("ConstructorReference"); |
| popIfNotNull(periodBeforeName); // charOffset. |
| String? suffix = popIfNotNull(periodBeforeName) as String?; |
| List<TypeBuilder>? typeArguments = pop() as List<TypeBuilder>?; |
| int charOffset = popCharOffset(); |
| Object? name = pop(); |
| if (name is ParserRecovery) { |
| push(name); |
| } else if (name != null) { |
| push(libraryBuilder.addConstructorReference( |
| name, typeArguments, suffix, charOffset)); |
| } else { |
| assert(name == null); |
| // At the moment, the name of the type in a constructor reference can be |
| // omitted only within an enum element declaration. |
| if (libraryBuilder.currentTypeParameterScopeBuilder.kind == |
| TypeParameterScopeKind.enumDeclaration) { |
| if (libraryBuilder.enableEnhancedEnumsInLibrary) { |
| push(libraryBuilder.addConstructorReference( |
| libraryBuilder.currentTypeParameterScopeBuilder.name, |
| typeArguments, |
| suffix, |
| charOffset)); |
| } else { |
| // For entries that consist of their name only, all of the elements |
| // of the constructor reference should be null. |
| if (typeArguments != null || suffix != null) { |
| addProblem( |
| templateExperimentNotEnabled.withArguments( |
| 'enhanced-enums', |
| libraryBuilder.enableEnhancedEnumsVersionInLibrary |
| .toText()), |
| charOffset, |
| -1); |
| } |
| push(NullValue.ConstructorReference); |
| } |
| } else { |
| internalProblem( |
| messageInternalProblemOmittedTypeNameInConstructorReference, |
| charOffset, |
| uri); |
| } |
| } |
| } |
| |
| @override |
| void beginFactoryMethod(DeclarationKind declarationKind, Token lastConsumed, |
| Token? externalToken, Token? constToken) { |
| DeclarationContext declarationContext; |
| switch (declarationKind) { |
| case DeclarationKind.TopLevel: |
| throw new UnsupportedError("Unexpected top level factory method."); |
| case DeclarationKind.Class: |
| declarationContext = DeclarationContext.ClassFactory; |
| break; |
| case DeclarationKind.Mixin: |
| declarationContext = DeclarationContext.MixinFactory; |
| break; |
| case DeclarationKind.Extension: |
| declarationContext = DeclarationContext.ExtensionFactory; |
| break; |
| case DeclarationKind.Enum: |
| declarationContext = DeclarationContext.EnumFactory; |
| break; |
| } |
| |
| pushDeclarationContext(declarationContext); |
| inConstructor = true; |
| libraryBuilder.beginNestedDeclaration( |
| TypeParameterScopeKind.factoryMethod, "#factory_method", |
| hasMembers: false); |
| push((externalToken != null ? externalMask : 0) | |
| (constToken != null ? constMask : 0)); |
| } |
| |
| void _endFactoryMethod( |
| Token beginToken, Token factoryKeyword, Token endToken) { |
| debugEvent("ClassFactoryMethod"); |
| MethodBody kind = pop() as MethodBody; |
| ConstructorReferenceBuilder? redirectionTarget; |
| if (kind == MethodBody.RedirectingFactoryBody) { |
| redirectionTarget = |
| |