[cfe] Add OffsetMap
This adds OffsetMap to avoid using names (or other means) to
connect objects/builders created in the OutlineBuilder with the
DietListener. The OffsetMap uses offsets, through Tokens or
Identifiers, as the key for the created objects.
Change-Id: I704d8f8374402463ea741e36ed15b279acb85535
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/366942
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/builder_graph.dart b/pkg/front_end/lib/src/fasta/builder_graph.dart
index fdae514..b36f06d 100644
--- a/pkg/front_end/lib/src/fasta/builder_graph.dart
+++ b/pkg/front_end/lib/src/fasta/builder_graph.dart
@@ -16,7 +16,7 @@
import 'dill/dill_library_builder.dart' show DillLibraryBuilder;
-import 'source/source_library_builder.dart' show SourceLibraryBuilder;
+import 'source/source_library_builder.dart' show Part, SourceLibraryBuilder;
import 'incremental_compiler.dart' show getPartUri;
@@ -52,8 +52,8 @@
neighbors.add(uri);
}
}
- for (LibraryBuilder part in library.parts) {
- Uri uri = part.importUri;
+ for (Part part in library.parts) {
+ Uri uri = part.builder.importUri;
if (builders.containsKey(uri)) {
neighbors.add(uri);
}
diff --git a/pkg/front_end/lib/src/fasta/export.dart b/pkg/front_end/lib/src/fasta/export.dart
index 3eb8ee8..9c0234a 100644
--- a/pkg/front_end/lib/src/fasta/export.dart
+++ b/pkg/front_end/lib/src/fasta/export.dart
@@ -4,6 +4,8 @@
library fasta.export;
+import 'package:kernel/ast.dart';
+
import 'builder/builder.dart';
import 'builder/library_builder.dart';
import 'combinator.dart' show CombinatorBuilder;
@@ -23,6 +25,11 @@
Uri get fileUri => exporter.fileUri;
+ /// The [LibraryDependency] node corresponding to this import.
+ ///
+ /// This set in [SourceLibraryBuilder.addDependencies].
+ late final LibraryDependency libraryDependency;
+
bool addToExportScope(String name, Builder member) {
if (combinators != null) {
for (CombinatorBuilder combinator in combinators!) {
diff --git a/pkg/front_end/lib/src/fasta/import.dart b/pkg/front_end/lib/src/fasta/import.dart
index 310033c..dfb8cfe 100644
--- a/pkg/front_end/lib/src/fasta/import.dart
+++ b/pkg/front_end/lib/src/fasta/import.dart
@@ -46,6 +46,11 @@
// this field is set.
final String? nativeImportPath;
+ /// The [LibraryDependency] node corresponding to this import.
+ ///
+ /// This set in [SourceLibraryBuilder.addDependencies].
+ LibraryDependency? libraryDependency;
+
Import(
this.importer,
this.imported,
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 38a698d..7b9c4d2 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -105,7 +105,7 @@
import 'source/source_class_builder.dart' show SourceClassBuilder;
import 'source/source_extension_builder.dart';
import 'source/source_library_builder.dart'
- show ImplicitLanguageVersion, SourceLibraryBuilder;
+ show ImplicitLanguageVersion, Part, SourceLibraryBuilder;
import 'source/source_loader.dart';
import 'ticker.dart' show Ticker;
import 'uri_translator.dart' show UriTranslator;
@@ -2180,12 +2180,12 @@
}
if (libraryBuilder is SourceLibraryBuilder) {
// TODO(jensj): This shouldn't be possible anymore.
- for (LibraryBuilder part in libraryBuilder.parts) {
- partUriToParent[part.importUri] = libraryBuilder;
- partUriToParent[part.fileUri] = libraryBuilder;
- if (isInvalidated(part.importUri, part.fileUri)) {
- invalidatedImportUris.add(part.importUri);
- builders[part.importUri] = part;
+ for (Part part in libraryBuilder.parts) {
+ partUriToParent[part.builder.importUri] = libraryBuilder;
+ partUriToParent[part.builder.fileUri] = libraryBuilder;
+ if (isInvalidated(part.builder.importUri, part.builder.fileUri)) {
+ invalidatedImportUris.add(part.builder.importUri);
+ builders[part.builder.importUri] = part.builder;
}
}
} else if (libraryBuilder is DillLibraryBuilder) {
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index 2c843a6..15b3bc3 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -11,8 +11,7 @@
DeclarationKind,
IdentifierContext,
MemberKind,
- Parser,
- optional;
+ Parser;
import 'package:_fe_analyzer_shared/src/parser/quote.dart' show unescapeString;
import 'package:_fe_analyzer_shared/src/parser/stack_listener.dart'
show FixedNullableList, NullValues, ParserRecovery;
@@ -26,12 +25,7 @@
import '../builder/declaration_builders.dart';
import '../builder/modifier_builder.dart';
import '../codes/fasta_codes.dart'
- show
- Code,
- LocatedMessage,
- Message,
- messageExpectedBlockToSkip,
- templateInternalProblemNotFound;
+ show Code, LocatedMessage, Message, messageExpectedBlockToSkip;
import '../constant_context.dart' show ConstantContext;
import '../crash.dart' show Crash;
import '../identifiers.dart'
@@ -40,18 +34,16 @@
import '../kernel/benchmarker.dart' show BenchmarkSubdivides, Benchmarker;
import '../kernel/body_builder.dart' show BodyBuilder, FormalParameters;
import '../kernel/body_builder_context.dart';
-import '../operator.dart';
-import '../problems.dart' show DebugAbort, internalProblem, unexpected;
+import '../problems.dart' show DebugAbort;
import '../scope.dart';
import '../source/value_kinds.dart';
import '../type_inference/type_inference_engine.dart'
show InferenceDataForTesting, TypeInferenceEngine;
import '../type_inference/type_inferrer.dart' show TypeInferrer;
import 'diet_parser.dart';
-import 'source_class_builder.dart';
+import 'offset_map.dart';
import 'source_constructor_builder.dart';
import 'source_enum_builder.dart';
-import 'source_extension_type_declaration_builder.dart';
import 'source_field_builder.dart';
import 'source_function_builder.dart';
import 'source_library_builder.dart' show SourceLibraryBuilder;
@@ -69,9 +61,6 @@
final TypeInferenceEngine typeInferenceEngine;
- int importExportDirectiveIndex = 0;
- int partDirectiveIndex = 0;
-
DeclarationBuilder? _currentDeclaration;
ClassBuilder? _currentClass;
bool _inRedirectingFactory = false;
@@ -83,14 +72,16 @@
Scope memberScope;
@override
- Uri uri;
+ final Uri uri;
final Benchmarker? _benchmarker;
+ final OffsetMap _offsetMap;
+
DietListener(SourceLibraryBuilder library, this.hierarchy, this.coreTypes,
- this.typeInferenceEngine)
+ this.typeInferenceEngine, this._offsetMap)
: libraryBuilder = library,
- uri = library.fileUri,
+ uri = _offsetMap.uri,
memberScope = library.scope,
enableNative =
library.loader.target.backendTarget.enableNative(library.importUri),
@@ -355,9 +346,8 @@
if (name is ParserRecovery) return;
Identifier identifier = name as Identifier;
- final BodyBuilder listener = createFunctionListener(
- lookupBuilder(getOrSet, identifier.name, identifier.nameOffset)
- as SourceFunctionBuilderImpl);
+ final BodyBuilder listener =
+ createFunctionListener(_offsetMap.lookupProcedure(identifier));
buildFunctionBody(listener, bodyToken, metadata, MemberKind.TopLevelMethod);
}
@@ -554,9 +544,8 @@
unescapeString(importUriToken.lexeme, importUriToken, this);
if (importUri.startsWith("dart-ext:")) return;
- Library libraryNode = libraryBuilder.library;
LibraryDependency? dependency =
- libraryNode.dependencies[importExportDirectiveIndex++];
+ _offsetMap.lookupImport(importKeyword).libraryDependency;
parseMetadata(libraryBuilder.bodyBuilderContext, libraryBuilder, metadata,
dependency);
}
@@ -571,9 +560,8 @@
debugEvent("Export");
Token? metadata = pop() as Token?;
- Library libraryNode = libraryBuilder.library;
LibraryDependency dependency =
- libraryNode.dependencies[importExportDirectiveIndex++];
+ _offsetMap.lookupExport(exportKeyword).libraryDependency;
parseMetadata(libraryBuilder.bodyBuilderContext, libraryBuilder, metadata,
dependency);
}
@@ -583,16 +571,9 @@
debugEvent("Part");
Token? metadata = pop() as Token?;
- Library libraryNode = libraryBuilder.library;
- if (libraryNode.parts.length > partDirectiveIndex) {
- // If partDirectiveIndex >= libraryNode.parts.length we are in a case of
- // on part having other parts. An error has already been issued.
- // Don't try to parse metadata into other parts that have nothing to do
- // with the one this keyword is talking about.
- LibraryPart part = libraryNode.parts[partDirectiveIndex++];
- parseMetadata(
- libraryBuilder.bodyBuilderContext, libraryBuilder, metadata, part);
- }
+ LibraryPart part = _offsetMap.lookupPart(partKeyword);
+ parseMetadata(
+ libraryBuilder.bodyBuilderContext, libraryBuilder, metadata, part);
}
@override
@@ -635,9 +616,7 @@
if (name is ParserRecovery || currentClassIsParserRecovery) return;
Identifier identifier = name as Identifier;
- SourceFunctionBuilderImpl builder = lookupConstructor(
- _getConstructorName(identifier), identifier.qualifierOffset)
- as SourceFunctionBuilderImpl;
+ SourceFunctionBuilder builder = _offsetMap.lookupConstructor(identifier);
if (_inRedirectingFactory) {
buildRedirectingFactoryMethod(
bodyToken, builder, MemberKind.Factory, metadata);
@@ -755,18 +734,9 @@
SourceFunctionBuilder builder;
if (isConstructor) {
- builder = lookupConstructor(
- _getConstructorName(identifier), identifier.qualifierOffset)
- as SourceFunctionBuilder;
+ builder = _offsetMap.lookupConstructor(identifier);
} else {
- String name = identifier.name;
- // TODO(johnniwinther): Find a uniform way to compute this.
- bool hasNoFormals = identical(beginParam.next, beginParam.endGroup);
- if (Operator.subtract == identifier.operator && hasNoFormals) {
- name = Operator.unaryMinus.text;
- }
- Builder? memberBuilder =
- lookupBuilder(getOrSet, name, identifier.nameOffset);
+ Builder? memberBuilder = _offsetMap.lookupProcedure(identifier);
if (currentClass?.isEnum == true &&
memberBuilder is SourceFieldBuilder &&
memberBuilder.name == "values") {
@@ -857,8 +827,8 @@
inferenceDataForTesting: builder.dataForTesting?.inferenceData);
}
- void buildRedirectingFactoryMethod(Token token,
- SourceFunctionBuilderImpl builder, MemberKind kind, Token? metadata) {
+ void buildRedirectingFactoryMethod(Token token, SourceFunctionBuilder builder,
+ MemberKind kind, Token? metadata) {
_benchmarker?.beginSubdivide(
BenchmarkSubdivides.diet_listener_buildRedirectingFactoryMethod);
final BodyBuilder listener = createFunctionListener(builder);
@@ -896,9 +866,7 @@
if (names == null || currentClassIsParserRecovery) return;
Identifier first = names.first!;
- SourceFieldBuilder declaration =
- lookupBuilder(/*getOrSet*/ null, first.name, first.nameOffset)
- as SourceFieldBuilder;
+ SourceFieldBuilder declaration = _offsetMap.lookupField(first);
// TODO(paulberry): don't re-parse the field if we've already parsed it
// for type inference.
_parseFields(
@@ -949,11 +917,9 @@
return;
}
if (name is Identifier) {
- currentDeclaration =
- lookupBuilder(/*getOrSet*/ null, name.name, name.nameOffset)
- as DeclarationBuilder;
+ currentDeclaration = _offsetMap.lookupNamedDeclaration(name);
} else {
- currentDeclaration = lookupUnnamedExtensionBuilder(beginToken);
+ currentDeclaration = _offsetMap.lookupUnnamedDeclaration(beginToken);
}
memberScope = currentDeclaration!.scope;
}
@@ -1023,7 +989,8 @@
void beginExtensionTypeDeclaration(
Token? augmentToken, Token extensionKeyword, Token nameToken) {
debugEvent("beginExtensionTypeDeclaration");
- push(new SimpleIdentifier(nameToken));
+ Identifier identifier = new SimpleIdentifier(nameToken);
+ push(identifier);
push(extensionKeyword);
// The current declaration is set in [beginClassOrMixinOrExtensionBody] but
@@ -1032,9 +999,7 @@
assert(currentDeclaration == null);
assert(memberScope == libraryBuilder.scope);
- currentDeclaration =
- lookupBuilder(/*getOrSet*/ null, nameToken.lexeme, nameToken.charOffset)
- as DeclarationBuilder;
+ currentDeclaration = _offsetMap.lookupNamedDeclaration(identifier);
memberScope = currentDeclaration!.scope;
}
@@ -1052,16 +1017,12 @@
]));
debugEvent("endPrimaryConstructor");
Token formalsToken = pop() as Token; // Pop formals begin token.
- int charOffset = formalsToken.charOffset;
- String constructorName = '';
if (hasConstructorName) {
// TODO(johnniwinther): Handle [ParserRecovery].
- Identifier identifier = pop() as Identifier;
- constructorName = identifier.name;
- charOffset = identifier.nameOffset;
+ pop() as Identifier;
}
SourceFunctionBuilder builder =
- lookupConstructor(constructorName, charOffset) as SourceFunctionBuilder;
+ _offsetMap.lookupPrimaryConstructor(beginToken);
if (!builder.isConst) {
buildPrimaryConstructor(createFunctionListener(builder), formalsToken);
}
@@ -1104,9 +1065,7 @@
}
Identifier identifier = name as Identifier;
- currentDeclaration =
- lookupBuilder(/*getOrSet*/ null, identifier.name, identifier.nameOffset)
- as DeclarationBuilder;
+ currentDeclaration = _offsetMap.lookupNamedDeclaration(identifier);
memberScope = currentDeclaration!.scope;
}
@@ -1278,70 +1237,6 @@
bodyBuilder.checkEmpty(token.charOffset);
}
- Builder? lookupBuilder(Token? getOrSet, String name, int charOffset) {
- // TODO(ahe): Can I move this to Scope or ScopeBuilder?
- Builder? declaration;
- DeclarationBuilder? currentDeclaration = this.currentDeclaration;
- if (currentDeclaration != null) {
- if (uri != currentDeclaration.fileUri) {
- unexpected("$uri", "${currentDeclaration.fileUri}",
- currentDeclaration.charOffset, currentDeclaration.fileUri);
- }
-
- if (getOrSet != null && optional("set", getOrSet)) {
- declaration =
- currentDeclaration.scope.lookupLocalMember(name, setter: true);
- } else {
- declaration =
- currentDeclaration.scope.lookupLocalMember(name, setter: false);
- }
- } else if (getOrSet != null && optional("set", getOrSet)) {
- declaration = libraryBuilder.scope.lookupLocalMember(name, setter: true);
- } else {
- declaration = libraryBuilder.scope.lookupLocalMember(name, setter: false);
- }
- declaration = handleDuplicatedName(declaration, charOffset);
- checkBuilder(declaration, name, charOffset);
- return declaration;
- }
-
- ExtensionBuilder? lookupUnnamedExtensionBuilder(Token extensionToken) {
- return libraryBuilder.scope
- .lookupLocalUnnamedExtension(uri, extensionToken.charOffset);
- }
-
- String _getConstructorName(Identifier nameOrQualified) {
- String suffix;
- if (nameOrQualified is QualifiedName) {
- suffix = nameOrQualified.name;
- } else {
- suffix = nameOrQualified.name == currentDeclaration!.name
- ? ""
- : nameOrQualified.name;
- }
- return suffix;
- }
-
- Builder? lookupConstructor(String constructorName, int charOffset) {
- assert(currentDeclaration != null);
- assert(currentDeclaration is SourceClassBuilder ||
- currentDeclaration is SourceExtensionTypeDeclarationBuilder);
- Builder? declaration;
- if (libraryFeatures.constructorTearoffs.isEnabled) {
- constructorName = constructorName == "new" ? "" : constructorName;
- }
- declaration =
- currentDeclaration!.constructorScope.lookupLocalMember(constructorName);
- declaration = handleDuplicatedName(declaration, charOffset);
- checkBuilder(
- declaration,
- constructorName.isEmpty
- ? currentDeclaration!.name
- : '${currentDeclaration!.name}.$constructorName',
- charOffset);
- return declaration;
- }
-
Builder? handleDuplicatedName(Builder? declaration, int charOffset) {
if (declaration == null) {
return null;
@@ -1376,7 +1271,7 @@
}
}
- void checkBuilder(Builder? declaration, String name, int charOffset) {
+ /*void checkBuilder(Builder? declaration, String name, int charOffset) {
if (declaration == null) {
internalProblem(
templateInternalProblemNotFound.withArguments(name), charOffset, uri);
@@ -1385,7 +1280,7 @@
unexpected("$uri", "${declaration.fileUri}", declaration.charOffset,
declaration.fileUri);
}
- }
+ }*/
@override
void addProblem(Message message, int charOffset, int length,
diff --git a/pkg/front_end/lib/src/fasta/source/offset_map.dart b/pkg/front_end/lib/src/fasta/source/offset_map.dart
new file mode 100644
index 0000000..363c056
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/source/offset_map.dart
@@ -0,0 +1,150 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/scanner/token.dart';
+import 'package:kernel/ast.dart';
+import '../builder/builder.dart';
+import '../builder/declaration_builders.dart';
+import '../codes/fasta_codes.dart';
+import '../export.dart';
+import '../identifiers.dart';
+import '../import.dart';
+import '../problems.dart';
+import 'source_field_builder.dart';
+import 'source_function_builder.dart';
+
+/// Map from offsets of directives and declarations to the objects the define.
+///
+/// This is used to connect parsing of the [OutlineBuilder], where the objects
+/// are created, with the [DietListener], where the objects are looked up.
+class OffsetMap {
+ final Uri uri;
+ final Map<int, DeclarationBuilder> _declarations = {};
+ final Map<int, SourceFieldBuilder> _fields = {};
+ final Map<int, SourceFunctionBuilder> _constructors = {};
+ final Map<int, SourceFunctionBuilder> _procedures = {};
+ final Map<int, LibraryPart> _parts = {};
+ final Map<int, Import> _imports = {};
+ final Map<int, Export> _exports = {};
+
+ OffsetMap(this.uri);
+
+ void registerImport(Token importKeyword, Import import) {
+ assert(importKeyword.lexeme == 'import',
+ "Invalid token for import: $importKeyword.");
+ _imports[importKeyword.charOffset] = import;
+ }
+
+ Import lookupImport(Token importKeyword) {
+ assert(importKeyword.lexeme == 'import',
+ "Invalid token for import: $importKeyword.");
+ return _checkDirective(_imports[importKeyword.charOffset], '<import>',
+ importKeyword.charOffset);
+ }
+
+ void registerExport(Token exportKeyword, Export export) {
+ assert(exportKeyword.lexeme == 'export',
+ "Invalid token for export: $exportKeyword.");
+ _exports[exportKeyword.charOffset] = export;
+ }
+
+ Export lookupExport(Token exportKeyword) {
+ assert(exportKeyword.lexeme == 'export',
+ "Invalid token for export: $exportKeyword.");
+ return _checkDirective(_exports[exportKeyword.charOffset], '<export>',
+ exportKeyword.charOffset);
+ }
+
+ void registerPart(Token partKeyword, LibraryPart part) {
+ assert(
+ partKeyword.lexeme == 'part', "Invalid token for part: $partKeyword.");
+ _parts[partKeyword.charOffset] = part;
+ }
+
+ LibraryPart lookupPart(Token partKeyword) {
+ assert(
+ partKeyword.lexeme == 'part', "Invalid token for part: $partKeyword.");
+ return _checkDirective(
+ _parts[partKeyword.charOffset], '<part>', partKeyword.charOffset);
+ }
+
+ void registerNamedDeclaration(
+ Identifier identifier, DeclarationBuilder builder) {
+ _declarations[identifier.nameOffset] = builder;
+ }
+
+ DeclarationBuilder lookupNamedDeclaration(Identifier identifier) {
+ return _checkBuilder(_declarations[identifier.nameOffset], identifier.name,
+ identifier.nameOffset);
+ }
+
+ void registerUnnamedDeclaration(
+ Token beginToken, DeclarationBuilder builder) {
+ _declarations[beginToken.charOffset] = builder;
+ }
+
+ DeclarationBuilder lookupUnnamedDeclaration(Token beginToken) {
+ return _checkBuilder(_declarations[beginToken.charOffset],
+ '<unnamed-declaration>', beginToken.charOffset);
+ }
+
+ void registerField(Identifier identifier, SourceFieldBuilder builder) {
+ _fields[identifier.nameOffset] = builder;
+ }
+
+ SourceFieldBuilder lookupField(Identifier identifier) {
+ return _checkBuilder(
+ _fields[identifier.nameOffset], identifier.name, identifier.nameOffset);
+ }
+
+ void registerPrimaryConstructor(
+ Token beginToken, SourceFunctionBuilder builder) {
+ _constructors[beginToken.charOffset] = builder;
+ }
+
+ SourceFunctionBuilder lookupPrimaryConstructor(Token beginToken) {
+ return _checkBuilder(_constructors[beginToken.charOffset],
+ '<primary-constructor>', beginToken.charOffset);
+ }
+
+ void registerConstructor(
+ Identifier identifier, SourceFunctionBuilder builder) {
+ _constructors[identifier.nameOffset] = builder;
+ }
+
+ SourceFunctionBuilder lookupConstructor(Identifier identifier) {
+ return _checkBuilder(_constructors[identifier.nameOffset], identifier.name,
+ identifier.nameOffset);
+ }
+
+ void registerProcedure(Identifier identifier, SourceFunctionBuilder builder) {
+ _procedures[identifier.nameOffset] = builder;
+ }
+
+ SourceFunctionBuilder lookupProcedure(Identifier identifier) {
+ return _checkBuilder(_procedures[identifier.nameOffset], identifier.name,
+ identifier.nameOffset);
+ }
+
+ T _checkDirective<T>(T? directive, String name, int charOffset) {
+ if (directive == null) {
+ internalProblem(
+ templateInternalProblemNotFound.withArguments(name), charOffset, uri);
+ }
+ return directive;
+ }
+
+ T _checkBuilder<T extends Builder>(
+ T? declaration, String name, int charOffset) {
+ if (declaration == null) {
+ internalProblem(
+ templateInternalProblemNotFound.withArguments(name), charOffset, uri);
+ }
+ if (uri != declaration.fileUri) {
+ unexpected("$uri", "${declaration.fileUri}", declaration.charOffset,
+ declaration.fileUri);
+ }
+ return declaration;
+ }
+}
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index 8d2d77e..ff08984 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -70,6 +70,7 @@
staticMask;
import '../operator.dart' show Operator;
import '../problems.dart' show unhandled;
+import 'offset_map.dart';
import 'source_enum_builder.dart';
import 'source_library_builder.dart'
show
@@ -537,10 +538,11 @@
bool get inFunctionType =>
_structuralParameterDepthLevel > 0 || _insideOfFormalParameterType;
- OutlineBuilder(SourceLibraryBuilder library)
- : libraryBuilder = library,
- enableNative =
- library.loader.target.backendTarget.enableNative(library.importUri);
+ OffsetMap _offsetMap;
+
+ OutlineBuilder(this.libraryBuilder, this._offsetMap)
+ : enableNative = libraryBuilder.loader.target.backendTarget
+ .enableNative(libraryBuilder.importUri);
DeclarationContext get declarationContext => _declarationContext.head;
@@ -678,8 +680,8 @@
int uriOffset = popCharOffset();
String uri = pop() as String;
List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?;
- libraryBuilder.addExport(metadata, uri, configurations, combinators,
- exportKeyword.charOffset, uriOffset);
+ libraryBuilder.addExport(_offsetMap, exportKeyword, metadata, uri,
+ configurations, combinators, exportKeyword.charOffset, uriOffset);
checkEmpty(exportKeyword.charOffset);
}
@@ -733,6 +735,8 @@
}
bool isAugmentationImport = augmentToken != null;
libraryBuilder.addImport(
+ offsetMap: _offsetMap,
+ importKeyword: importKeyword,
metadata: metadata,
isAugmentationImport: isAugmentationImport,
uri: uri,
@@ -811,7 +815,7 @@
int charOffset = popCharOffset();
String uri = pop() as String;
List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?;
- libraryBuilder.addPart(metadata, uri, charOffset);
+ libraryBuilder.addPart(_offsetMap, partKeyword, metadata, uri, charOffset);
checkEmpty(partKeyword.charOffset);
}
@@ -1365,9 +1369,10 @@
modifiers |= abstractMask;
}
libraryBuilder.addClass(
+ _offsetMap,
metadata,
modifiers,
- identifier.name,
+ identifier,
typeVariables,
supertype,
mixinApplication,
@@ -1458,9 +1463,10 @@
}
libraryBuilder.addMixinDeclaration(
+ _offsetMap,
metadata,
mixinDeclarationMask,
- identifier.name,
+ identifier,
typeVariables,
supertypeConstraints,
interfaces,
@@ -1496,8 +1502,9 @@
List<NominalVariableBuilder>? typeVariables =
pop() as List<NominalVariableBuilder>?;
int offset = nameToken?.charOffset ?? extensionKeyword.charOffset;
- push(nameToken?.lexeme ?? NullValues.Name);
- push(offset);
+ push(nameToken != null
+ ? new SimpleIdentifier(nameToken)
+ : NullValues.Identifier);
push(typeVariables ?? NullValues.NominalVariables);
libraryBuilder.currentTypeParameterScopeBuilder
.markAsExtensionDeclaration(nameToken?.lexeme, offset, typeVariables);
@@ -1509,8 +1516,7 @@
assert(checkState(extensionKeyword, [
unionOfKinds([ValueKinds.ParserRecovery, ValueKinds.TypeBuilder]),
ValueKinds.NominalVariableListOrNull,
- ValueKinds.Integer,
- ValueKinds.NameOrNull,
+ ValueKinds.IdentifierOrNull,
ValueKinds.MetadataListOrNull
]));
debugEvent("endExtensionDeclaration");
@@ -1523,11 +1529,8 @@
}
List<NominalVariableBuilder>? typeVariables =
pop(NullValues.NominalVariables) as List<NominalVariableBuilder>?;
- int nameOffset = popCharOffset();
- String? name = pop(NullValues.Name) as String?;
- if (name == null) {
- nameOffset = extensionKeyword.charOffset;
- }
+ Identifier? name = pop(NullValues.Identifier) as Identifier?;
+ int nameOffset = name?.nameOffset ?? extensionKeyword.charOffset;
List<MetadataBuilder>? metadata =
pop(NullValues.Metadata) as List<MetadataBuilder>?;
checkEmpty(extensionKeyword.charOffset);
@@ -1535,6 +1538,8 @@
? extensionKeyword.charOffset
: metadata.first.charOffset;
libraryBuilder.addExtensionDeclaration(
+ _offsetMap,
+ beginToken,
metadata,
// TODO(johnniwinther): Support modifiers on extensions?
0,
@@ -1559,8 +1564,7 @@
pop() as List<NominalVariableBuilder>?;
String name = nameToken.lexeme;
int offset = nameToken.charOffset;
- push(name);
- push(offset);
+ push(new SimpleIdentifier(nameToken));
push(typeVariables ?? NullValues.NominalVariables);
libraryBuilder.currentTypeParameterScopeBuilder
.markAsExtensionTypeDeclaration(name, offset, typeVariables);
@@ -1574,8 +1578,7 @@
assert(checkState(extensionKeyword, [
ValueKinds.TypeBuilderListOrNull,
ValueKinds.NominalVariableListOrNull,
- ValueKinds.Integer,
- ValueKinds.Name,
+ ValueKinds.Identifier,
ValueKinds.MetadataListOrNull,
]));
reportIfNotEnabled(libraryFeatures.inlineClass, typeKeyword.charOffset,
@@ -1585,8 +1588,7 @@
pop(NullValues.TypeBuilderList) as List<TypeBuilder>?;
List<NominalVariableBuilder>? typeVariables =
pop(NullValues.NominalVariables) as List<NominalVariableBuilder>?;
- int nameOffset = popCharOffset();
- String name = pop() as String;
+ Identifier identifier = pop() as Identifier;
List<MetadataBuilder>? metadata =
pop(NullValues.Metadata) as List<MetadataBuilder>?;
checkEmpty(extensionKeyword.charOffset);
@@ -1597,14 +1599,14 @@
? extensionKeyword.charOffset
: metadata.first.charOffset;
libraryBuilder.addExtensionTypeDeclaration(
+ _offsetMap,
metadata,
// TODO(johnniwinther): Support modifiers on extension types?
0,
- name,
+ identifier,
typeVariables,
interfaces,
startOffset,
- nameOffset,
endToken.charOffset);
libraryBuilder.endIndexedContainer();
@@ -1731,6 +1733,8 @@
scopeBuilder.resolveNamedTypes(typeVariables, libraryBuilder);
libraryBuilder.addPrimaryConstructor(
+ offsetMap: _offsetMap,
+ beginToken: beginToken,
constructorName: constructorName == "new" ? "" : constructorName,
charOffset: charOffset,
formals: formals,
@@ -1814,9 +1818,11 @@
final int startCharOffset =
metadata == null ? beginToken.charOffset : metadata.first.charOffset;
libraryBuilder.addProcedure(
+ _offsetMap,
metadata,
modifiers,
returnType,
+ identifier,
identifier.name,
typeVariables,
formals,
@@ -2343,9 +2349,10 @@
final int startCharOffset =
metadata == null ? beginToken.charOffset : metadata.first.charOffset;
libraryBuilder.addConstructor(
+ _offsetMap,
metadata,
modifiers,
- name,
+ identifier,
constructorName,
typeVariables,
formals,
@@ -2369,9 +2376,11 @@
bool isExtensionTypeMember =
methodKind == _MethodKind.extensionTypeMethod;
libraryBuilder.addProcedure(
+ _offsetMap,
metadata,
modifiers,
returnType,
+ identifier,
name,
typeVariables,
formals,
@@ -3063,14 +3072,14 @@
}
libraryBuilder.addEnum(
+ _offsetMap,
metadata,
- identifier.name,
+ identifier,
typeVariables,
mixinBuilder,
interfaces,
enumConstantInfos,
startCharOffset,
- identifier.nameOffset,
endCharOffset);
} else {
libraryBuilder
@@ -3369,6 +3378,7 @@
List<MetadataBuilder>? metadata =
pop(NullValues.Metadata) as List<MetadataBuilder>?;
checkEmpty(typedefKeyword.charOffset);
+
libraryBuilder.addFunctionTypeAlias(metadata, identifier.name,
typeVariables, aliasedType, identifier.nameOffset);
popDeclarationContext(DeclarationContext.Typedef);
@@ -3472,8 +3482,8 @@
List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?;
checkEmpty(beginToken.charOffset);
if (fieldInfos != null) {
- libraryBuilder.addFields(
- metadata, modifiers, /* isTopLevel = */ true, type, fieldInfos);
+ libraryBuilder.addFields(_offsetMap, metadata, modifiers,
+ /* isTopLevel = */ true, type, fieldInfos);
}
popDeclarationContext();
}
@@ -3535,8 +3545,8 @@
}
List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?;
if (fieldInfos != null) {
- libraryBuilder.addFields(
- metadata, modifiers, /* isTopLevel = */ false, type, fieldInfos);
+ libraryBuilder.addFields(_offsetMap, metadata, modifiers,
+ /* isTopLevel = */ false, type, fieldInfos);
}
popDeclarationContext();
}
@@ -3810,6 +3820,7 @@
TypeParameterScopeKind.factoryMethod, "<syntax-error>");
} else {
libraryBuilder.addFactoryMethod(
+ _offsetMap,
metadata,
modifiers,
name,
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 1672f47..7e60e542 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -105,6 +105,7 @@
import '../util/helpers.dart';
import 'class_declaration.dart';
import 'name_scheme.dart';
+import 'offset_map.dart';
import 'source_class_builder.dart' show SourceClassBuilder;
import 'source_constructor_builder.dart';
import 'source_enum_builder.dart';
@@ -124,15 +125,18 @@
@override
final SourceLoader loader;
+ /// Map used to find objects created in the [OutlineBuilder] from within
+ /// the [DietListener].
+ ///
+ /// This is meant to be written once and read once.
+ OffsetMap? _offsetMap;
+
final TypeParameterScopeBuilder _libraryTypeParameterScopeBuilder;
final List<ConstructorReferenceBuilder> constructorReferences =
<ConstructorReferenceBuilder>[];
- final List<LibraryBuilder> parts = <LibraryBuilder>[];
-
- // Can I use library.parts instead? See SourceLibraryBuilder.addPart.
- final List<int> partOffsets = <int>[];
+ final List<Part> parts = [];
final List<Import> imports = <Import>[];
@@ -353,6 +357,24 @@
"'${importUri}'.");
}
+ /// Returns the map of objects created in the [OutlineBuilder].
+ ///
+ /// This should only be called once.
+ OffsetMap get offsetMap {
+ assert(_offsetMap != null, "No OffsetMap for $this");
+ OffsetMap map = _offsetMap!;
+ _offsetMap = null;
+ return map;
+ }
+
+ /// Registers the map of objects created in the [OutlineBuilder].
+ ///
+ /// This should only be called once.
+ void set offsetMap(OffsetMap value) {
+ assert(_offsetMap == null, "OffsetMap has already been set for $this");
+ _offsetMap = value;
+ }
+
MergedLibraryScope get mergedScope {
return _mergedScope ??=
isAugmenting ? origin.mergedScope : new MergedLibraryScope(this);
@@ -740,6 +762,8 @@
}
void addExport(
+ OffsetMap offsetMap,
+ Token exportKeyword,
List<MetadataBuilder>? metadata,
String uri,
List<Configuration>? configurations,
@@ -760,11 +784,15 @@
resolve(this.importUri, uri, uriOffset), charOffset,
accessor: this);
exportedLibrary.addExporter(this, combinators, charOffset);
- exports.add(new Export(this, exportedLibrary, combinators, charOffset));
+ Export export = new Export(this, exportedLibrary, combinators, charOffset);
+ exports.add(export);
+ offsetMap.registerExport(exportKeyword, export);
}
void addImport(
- {required List<MetadataBuilder>? metadata,
+ {OffsetMap? offsetMap,
+ Token? importKeyword,
+ required List<MetadataBuilder>? metadata,
required bool isAugmentationImport,
required String uri,
required List<Configuration>? configurations,
@@ -810,7 +838,7 @@
referencesFromIndex: isAugmentationImport ? indexedLibrary : null);
}
- imports.add(new Import(
+ Import import = new Import(
this,
builder,
isAugmentationImport,
@@ -821,10 +849,13 @@
charOffset,
prefixCharOffset,
importIndex,
- nativeImportPath: nativePath));
+ nativeImportPath: nativePath);
+ imports.add(import);
+ offsetMap?.registerImport(importKeyword!, import);
}
- void addPart(List<MetadataBuilder>? metadata, String uri, int charOffset) {
+ void addPart(OffsetMap offsetMap, Token partKeyword,
+ List<MetadataBuilder>? metadata, String uri, int charOffset) {
Uri resolvedUri = resolve(this.importUri, uri, charOffset, isPart: true);
// To support absolute paths from within packages in the part uri, we try to
// translate the file uri from the resolved import uri before resolving
@@ -833,17 +864,18 @@
resolve(fileUri, uri, charOffset);
// TODO(johnniwinther): Add a LibraryPartBuilder instead of using
// [LibraryBuilder] to represent both libraries and parts.
- parts.add(loader.read(resolvedUri, charOffset,
+ LibraryBuilder builder = loader.read(resolvedUri, charOffset,
origin: isAugmenting ? origin : null,
fileUri: newFileUri,
accessor: this,
- isPatch: isAugmenting));
- partOffsets.add(charOffset);
+ isPatch: isAugmenting);
+ parts.add(new Part(charOffset, builder));
// TODO(ahe): [metadata] should be stored, evaluated, and added to [part].
LibraryPart part = new LibraryPart(<Expression>[], uri)
..fileOffset = charOffset;
library.addPart(part);
+ offsetMap.registerPart(partKeyword, part);
}
void addPartOf(List<MetadataBuilder>? metadata, String? name, String? uri,
@@ -868,8 +900,13 @@
_scriptTokenOffset ??= charOffset;
}
- void addFields(List<MetadataBuilder>? metadata, int modifiers,
- bool isTopLevel, TypeBuilder? type, List<FieldInfo> fieldInfos) {
+ void addFields(
+ OffsetMap offsetMap,
+ List<MetadataBuilder>? metadata,
+ int modifiers,
+ bool isTopLevel,
+ TypeBuilder? type,
+ List<FieldInfo> fieldInfos) {
for (FieldInfo info in fieldInfos) {
bool isConst = modifiers & constMask != 0;
bool isFinal = modifiers & finalMask != 0;
@@ -886,22 +923,24 @@
new Token.eof(startToken.previous!.offset).setNext(startToken);
}
bool hasInitializer = info.initializerToken != null;
- addField(
- metadata,
- modifiers,
- isTopLevel,
- type ?? addInferableType(),
- info.identifier.name,
- info.identifier.nameOffset,
- info.charEndOffset,
- startToken,
- hasInitializer,
- constInitializerToken:
- potentiallyNeedInitializerInOutline ? startToken : null);
+ offsetMap.registerField(
+ info.identifier,
+ addField(
+ metadata,
+ modifiers,
+ isTopLevel,
+ type ?? addInferableType(),
+ info.identifier.name,
+ info.identifier.nameOffset,
+ info.charEndOffset,
+ startToken,
+ hasInitializer,
+ constInitializerToken:
+ potentiallyNeedInitializerInOutline ? startToken : null));
}
}
- Builder? addBuilder(String name, Builder declaration, int charOffset,
+ Builder addBuilder(String name, Builder declaration, int charOffset,
{Reference? getterReference, Reference? setterReference}) {
// TODO(ahe): Set the parent correctly here. Could then change the
// implementation of MemberBuilder.isTopLevel to test explicitly for a
@@ -948,7 +987,7 @@
: currentTypeParameterScopeBuilder.members!);
Builder? existing = members[name];
- if (existing == declaration) return existing;
+ if (existing == declaration) return declaration;
if (declaration.next != null && declaration.next != existing) {
unexpected(
@@ -1171,13 +1210,13 @@
List<LocatedMessage> context = <LocatedMessage>[
messagePartInPartLibraryContext.withLocation(library.fileUri, -1, 1),
];
- for (int offset in partOffsets) {
- addProblem(messagePartInPart, offset, noLength, fileUri,
+ for (Part part in parts) {
+ addProblem(messagePartInPart, part.offset, noLength, fileUri,
context: context);
}
- for (LibraryBuilder part in parts) {
+ for (Part part in parts) {
// Mark this part as used so we don't report it as orphaned.
- usedParts!.add(part.importUri);
+ usedParts!.add(part.builder.importUri);
}
}
parts.clear();
@@ -1206,32 +1245,43 @@
void includeParts(Set<Uri> usedParts) {
Set<Uri> seenParts = new Set<Uri>();
- for (int i = 0; i < parts.length; i++) {
- LibraryBuilder part = parts[i];
- int partOffset = partOffsets[i];
- if (part == this) {
+ int index = 0;
+ while (index < parts.length) {
+ Part part = parts[index];
+ bool keepPart = true;
+ // TODO(johnniwinther): Use [part.offset] in messages.
+ if (part.builder == this) {
addProblem(messagePartOfSelf, -1, noLength, fileUri);
- } else if (seenParts.add(part.fileUri)) {
- if (part.partOfLibrary != null) {
- addProblem(messagePartOfTwoLibraries, -1, noLength, part.fileUri,
+ keepPart = false;
+ } else if (seenParts.add(part.builder.fileUri)) {
+ if (part.builder.partOfLibrary != null) {
+ addProblem(
+ messagePartOfTwoLibraries, -1, noLength, part.builder.fileUri,
context: [
messagePartOfTwoLibrariesContext.withLocation(
- part.partOfLibrary!.fileUri, -1, noLength),
+ part.builder.partOfLibrary!.fileUri, -1, noLength),
messagePartOfTwoLibrariesContext.withLocation(
this.fileUri, -1, noLength)
]);
+ keepPart = false;
} else {
- usedParts.add(part.importUri);
- includePart(part, usedParts, partOffset);
+ usedParts.add(part.builder.importUri);
+ keepPart = _includePart(part.builder, usedParts, part.offset);
}
} else {
- addProblem(templatePartTwice.withArguments(part.fileUri), -1, noLength,
- fileUri);
+ addProblem(templatePartTwice.withArguments(part.builder.fileUri), -1,
+ noLength, fileUri);
+ keepPart = false;
+ }
+ if (keepPart) {
+ index++;
+ } else {
+ parts.removeAt(index);
}
}
}
- bool includePart(LibraryBuilder part, Set<Uri> usedParts, int partOffset) {
+ bool _includePart(LibraryBuilder part, Set<Uri> usedParts, int partOffset) {
if (part is SourceLibraryBuilder) {
if (part.partOfUri != null) {
if (uriIsValid(part.partOfUri!) && part.partOfUri != importUri) {
@@ -1902,9 +1952,10 @@
}
void addClass(
+ OffsetMap offsetMap,
List<MetadataBuilder>? metadata,
int modifiers,
- String className,
+ Identifier identifier,
List<NominalVariableBuilder>? typeVariables,
TypeBuilder? supertype,
MixinApplicationBuilder? mixins,
@@ -1921,10 +1972,11 @@
required bool isAugmentation,
required bool isMixinClass}) {
_addClass(
+ offsetMap,
TypeParameterScopeKind.classDeclaration,
metadata,
modifiers,
- className,
+ identifier,
typeVariables,
supertype,
mixins,
@@ -1943,9 +1995,10 @@
}
void addMixinDeclaration(
+ OffsetMap offsetMap,
List<MetadataBuilder>? metadata,
int modifiers,
- String className,
+ Identifier identifier,
List<NominalVariableBuilder>? typeVariables,
List<TypeBuilder>? supertypeConstraints,
List<TypeBuilder>? interfaces,
@@ -1967,10 +2020,11 @@
}
}
_addClass(
+ offsetMap,
TypeParameterScopeKind.mixinDeclaration,
metadata,
modifiers,
- className,
+ identifier,
typeVariables,
supertype,
mixinApplication,
@@ -1989,10 +2043,11 @@
}
void _addClass(
+ OffsetMap offsetMap,
TypeParameterScopeKind kind,
List<MetadataBuilder>? metadata,
int modifiers,
- String className,
+ Identifier identifier,
List<NominalVariableBuilder>? typeVariables,
TypeBuilder? supertype,
MixinApplicationBuilder? mixins,
@@ -2008,6 +2063,7 @@
required bool isFinal,
required bool isAugmentation,
required bool isMixinClass}) {
+ String className = identifier.name;
// Nested declaration began in `OutlineBuilder.beginClassDeclaration`.
TypeParameterScopeBuilder declaration =
endNestedDeclaration(kind, className)
@@ -2106,6 +2162,7 @@
setters.forEach(setParentAndCheckConflicts);
addBuilder(className, classBuilder, nameOffset,
getterReference: _indexedContainer?.reference);
+ offsetMap.registerNamedDeclaration(identifier, classBuilder);
}
Map<String, NominalVariableBuilder>? checkTypeVariables(
@@ -2279,14 +2336,17 @@
}
void addExtensionDeclaration(
+ OffsetMap offsetMap,
+ Token beginToken,
List<MetadataBuilder>? metadata,
int modifiers,
- String? name,
+ Identifier? identifier,
List<NominalVariableBuilder>? typeVariables,
TypeBuilder type,
int startOffset,
int nameOffset,
int endOffset) {
+ String? name = identifier?.name;
// Nested declaration began in
// `OutlineBuilder.beginExtensionDeclarationPrelude`.
TypeParameterScopeBuilder declaration =
@@ -2355,17 +2415,23 @@
setters.forEach(setParentAndCheckConflicts);
addBuilder(extensionBuilder.name, extensionBuilder, nameOffset,
getterReference: referenceFrom?.reference);
+ if (identifier != null) {
+ offsetMap.registerNamedDeclaration(identifier, extensionBuilder);
+ } else {
+ offsetMap.registerUnnamedDeclaration(beginToken, extensionBuilder);
+ }
}
void addExtensionTypeDeclaration(
+ OffsetMap offsetMap,
List<MetadataBuilder>? metadata,
int modifiers,
- String name,
+ Identifier identifier,
List<NominalVariableBuilder>? typeVariables,
List<TypeBuilder>? interfaces,
int startOffset,
- int nameOffset,
int endOffset) {
+ String name = identifier.name;
// Nested declaration began in `OutlineBuilder.beginExtensionDeclaration`.
TypeParameterScopeBuilder declaration = endNestedDeclaration(
TypeParameterScopeKind.extensionTypeDeclaration, name)
@@ -2414,7 +2480,7 @@
this,
new List<ConstructorReferenceBuilder>.of(constructorReferences),
startOffset,
- nameOffset,
+ identifier.nameOffset,
endOffset,
indexedContainer,
representationFieldBuilder);
@@ -2449,8 +2515,10 @@
constructors.forEach(setParentAndCheckConflicts);
setters.forEach(setParentAndCheckConflicts);
addBuilder(extensionTypeDeclarationBuilder.name,
- extensionTypeDeclarationBuilder, nameOffset,
+ extensionTypeDeclarationBuilder, identifier.nameOffset,
getterReference: indexedContainer?.reference);
+ offsetMap.registerNamedDeclaration(
+ identifier, extensionTypeDeclarationBuilder);
}
TypeBuilder? _applyMixins(
@@ -2986,15 +3054,16 @@
}
void addPrimaryConstructor(
- {required String constructorName,
+ {required OffsetMap offsetMap,
+ required Token beginToken,
+ required String constructorName,
required List<NominalVariableBuilder>? typeVariables,
required List<FormalParameterBuilder>? formals,
required int charOffset,
required bool isConst}) {
- addConstructor(
+ SourceFunctionBuilder builder = _addConstructor(
null,
isConst ? constMask : 0,
- null,
constructorName,
typeVariables,
formals,
@@ -3004,12 +3073,43 @@
/* charEndOffset = */ charOffset,
/* nativeMethodName = */ null,
forAbstractClassOrMixin: false);
+ offsetMap.registerPrimaryConstructor(beginToken, builder);
}
void addConstructor(
+ OffsetMap offsetMap,
List<MetadataBuilder>? metadata,
int modifiers,
- final Object? name,
+ Identifier identifier,
+ String constructorName,
+ List<NominalVariableBuilder>? typeVariables,
+ List<FormalParameterBuilder>? formals,
+ int startCharOffset,
+ int charOffset,
+ int charOpenParenOffset,
+ int charEndOffset,
+ String? nativeMethodName,
+ {Token? beginInitializers,
+ required bool forAbstractClassOrMixin}) {
+ SourceFunctionBuilder builder = _addConstructor(
+ metadata,
+ modifiers,
+ constructorName,
+ typeVariables,
+ formals,
+ startCharOffset,
+ charOffset,
+ charOpenParenOffset,
+ charEndOffset,
+ nativeMethodName,
+ beginInitializers: beginInitializers,
+ forAbstractClassOrMixin: forAbstractClassOrMixin);
+ offsetMap.registerConstructor(identifier, builder);
+ }
+
+ SourceFunctionBuilder _addConstructor(
+ List<MetadataBuilder>? metadata,
+ int modifiers,
String constructorName,
List<NominalVariableBuilder>? typeVariables,
List<FormalParameterBuilder>? formals,
@@ -3103,12 +3203,15 @@
constructorBuilder.beginInitializers =
beginInitializers ?? new Token.eof(-1);
}
+ return constructorBuilder;
}
void addProcedure(
+ OffsetMap offsetMap,
List<MetadataBuilder>? metadata,
int modifiers,
TypeBuilder? returnType,
+ Identifier identifier,
String name,
List<NominalVariableBuilder>? typeVariables,
List<FormalParameterBuilder>? formals,
@@ -3197,9 +3300,11 @@
if (nativeMethodName != null) {
addNativeMethod(procedureBuilder);
}
+ offsetMap.registerProcedure(identifier, procedureBuilder);
}
void addFactoryMethod(
+ OffsetMap offsetMap,
List<MetadataBuilder>? metadata,
int modifiers,
Identifier identifier,
@@ -3352,18 +3457,22 @@
if (nativeMethodName != null) {
addNativeMethod(procedureBuilder);
}
+ offsetMap.registerConstructor(identifier, procedureBuilder);
}
void addEnum(
+ OffsetMap offsetMap,
List<MetadataBuilder>? metadata,
- String name,
+ Identifier identifier,
List<NominalVariableBuilder>? typeVariables,
MixinApplicationBuilder? supertypeBuilder,
List<TypeBuilder>? interfaceBuilders,
List<EnumConstantInfo?>? enumConstantInfos,
int startCharOffset,
- int charOffset,
int charEndOffset) {
+ String name = identifier.name;
+ int charOffset = identifier.nameOffset;
+
IndexedClass? referencesFromIndexedClass;
if (indexedLibrary != null) {
referencesFromIndexedClass = indexedLibrary!.lookupIndexedClass(name);
@@ -3448,6 +3557,8 @@
setters.forEach(setParentAndCheckConflicts);
addBuilder(name, enumBuilder, charOffset,
getterReference: referencesFromIndexedClass?.cls.reference);
+
+ offsetMap.registerNamedDeclaration(identifier, enumBuilder);
}
void addFunctionTypeAlias(
@@ -3736,29 +3847,35 @@
continue;
}
+ LibraryDependency libraryDependency;
if (import.deferred && import.prefixBuilder?.dependency != null) {
- library.addDependency(import.prefixBuilder!.dependency!);
+ libraryDependency = import.prefixBuilder!.dependency!;
} else {
LibraryBuilder imported = import.imported!.origin;
Library targetLibrary = imported.library;
- library.addDependency(new LibraryDependency.import(targetLibrary,
+ libraryDependency = new LibraryDependency.import(targetLibrary,
name: import.prefix,
combinators: toKernelCombinators(import.combinators))
- ..fileOffset = import.charOffset);
+ ..fileOffset = import.charOffset;
}
+ library.addDependency(libraryDependency);
+ import.libraryDependency = libraryDependency;
} else {
// Add export
Export export = exports[exportIndex++];
- library.addDependency(new LibraryDependency.export(
+ LibraryDependency libraryDependency = new LibraryDependency.export(
export.exported.library,
combinators: toKernelCombinators(export.combinators))
- ..fileOffset = export.charOffset);
+ ..fileOffset = export.charOffset;
+ library.addDependency(libraryDependency);
+ export.libraryDependency = libraryDependency;
}
}
- for (LibraryBuilder part in parts) {
- if (part is SourceLibraryBuilder) {
- part.addDependencies(library, seen);
+ for (Part part in parts) {
+ LibraryBuilder builder = part.builder;
+ if (builder is SourceLibraryBuilder) {
+ builder.addDependencies(library, seen);
}
}
}
@@ -6187,3 +6304,18 @@
@override
String get name => _iterator?.name ?? (throw new StateError('No element'));
}
+
+class Part {
+ final int offset;
+ final LibraryBuilder builder;
+
+ Part(this.offset, this.builder);
+
+ OffsetMap get offsetMap {
+ if (builder is SourceLibraryBuilder) {
+ return (builder as SourceLibraryBuilder).offsetMap;
+ }
+ assert(false, "No offset map for $builder.");
+ return new OffsetMap(builder.fileUri);
+ }
+}
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 7e8fae6..7f42382 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -81,6 +81,7 @@
import 'diet_listener.dart' show DietListener;
import 'diet_parser.dart' show DietParser, useImplicitCreationExpressionInCfe;
import 'name_scheme.dart';
+import 'offset_map.dart';
import 'outline_builder.dart' show OutlineBuilder;
import 'source_class_builder.dart' show SourceClassBuilder;
import 'source_constructor_builder.dart';
@@ -92,6 +93,7 @@
InvalidLanguageVersion,
LanguageVersion,
LibraryAccess,
+ Part,
SourceLibraryBuilder;
import 'source_procedure_builder.dart';
import 'stack_listener_impl.dart' show offsetForToken;
@@ -1122,10 +1124,12 @@
Future<Null> buildOutline(SourceLibraryBuilder library) async {
Token tokens = await tokenize(library);
- OutlineBuilder listener = new OutlineBuilder(library);
+ OffsetMap offsetMap = new OffsetMap(library.fileUri);
+ OutlineBuilder listener = new OutlineBuilder(library, offsetMap);
new ClassMemberParser(listener,
allowPatterns: library.libraryFeatures.patterns.isEnabled)
.parseUnit(tokens);
+ library.offsetMap = offsetMap;
}
/// Builds all the method bodies found in the given [library].
@@ -1172,18 +1176,18 @@
}
}
- DietListener listener = createDietListener(library);
+ DietListener listener = createDietListener(library, library.offsetMap);
DietParser parser = new DietParser(listener,
allowPatterns: library.libraryFeatures.patterns.isEnabled);
parser.parseUnit(tokens);
- for (LibraryBuilder part in library.parts) {
- if (part.partOfLibrary != library) {
- // Part was included in multiple libraries. Skip it here.
- continue;
- }
- Token tokens = await tokenize(part as SourceLibraryBuilder,
+ for (Part part in library.parts) {
+ assert(part.builder.partOfLibrary == library,
+ "Part ${part.builder} is not part of ${library}.");
+ Token tokens = await tokenize(part.builder as SourceLibraryBuilder,
suppressLexicalErrors: true);
- listener.uri = part.fileUri;
+ DietListener listener = createDietListener(library, part.offsetMap);
+ DietParser parser = new DietParser(listener,
+ allowPatterns: library.libraryFeatures.patterns.isEnabled);
parser.parseUnit(tokens);
}
}
@@ -1195,7 +1199,11 @@
FunctionNode parameters,
VariableDeclaration? extensionThis) async {
Token token = await tokenize(libraryBuilder, suppressLexicalErrors: false);
- DietListener dietListener = createDietListener(libraryBuilder);
+ DietListener dietListener = createDietListener(
+ libraryBuilder,
+ // Expression compilation doesn't build an outline, and thus doesn't
+ // support members from source, so we provide an empty [DeclarationMap].
+ new OffsetMap(libraryBuilder.fileUri));
Builder parent = libraryBuilder;
if (enclosingClassOrExtension != null) {
@@ -1271,8 +1279,10 @@
parameters);
}
- DietListener createDietListener(SourceLibraryBuilder library) {
- return new DietListener(library, hierarchy, coreTypes, typeInferenceEngine);
+ DietListener createDietListener(
+ SourceLibraryBuilder library, OffsetMap offsetMap) {
+ return new DietListener(
+ library, hierarchy, coreTypes, typeInferenceEngine, offsetMap);
}
void resolveParts() {
diff --git a/pkg/front_end/test/compiler_test_helper.dart b/pkg/front_end/test/compiler_test_helper.dart
index e6be3e5..6c28995 100644
--- a/pkg/front_end/test/compiler_test_helper.dart
+++ b/pkg/front_end/test/compiler_test_helper.dart
@@ -19,10 +19,10 @@
import 'package:front_end/src/fasta/kernel/kernel_target.dart';
import 'package:front_end/src/fasta/scope.dart';
import 'package:front_end/src/fasta/source/diet_listener.dart';
+import 'package:front_end/src/fasta/source/offset_map.dart';
import 'package:front_end/src/fasta/source/source_library_builder.dart';
import 'package:front_end/src/fasta/source/source_loader.dart';
import 'package:front_end/src/fasta/ticker.dart';
-import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart';
import 'package:front_end/src/fasta/type_inference/type_inferrer.dart';
import 'package:front_end/src/fasta/uri_translator.dart';
import 'package:kernel/class_hierarchy.dart';
@@ -214,9 +214,10 @@
: super(fileSystem, includeComments, target);
@override
- DietListener createDietListener(SourceLibraryBuilder library) {
- return new DietListenerTest(
- library, hierarchy, coreTypes, typeInferenceEngine, bodyBuilderCreator);
+ DietListener createDietListener(
+ SourceLibraryBuilder library, OffsetMap offsetMap) {
+ return new DietListenerTest(library, hierarchy, coreTypes,
+ typeInferenceEngine, offsetMap, bodyBuilderCreator);
}
@override
@@ -246,13 +247,8 @@
class DietListenerTest extends DietListener {
final BodyBuilderCreator bodyBuilderCreator;
- DietListenerTest(
- SourceLibraryBuilder library,
- ClassHierarchy hierarchy,
- CoreTypes coreTypes,
- TypeInferenceEngine typeInferenceEngine,
- this.bodyBuilderCreator)
- : super(library, hierarchy, coreTypes, typeInferenceEngine);
+ DietListenerTest(super.library, super.hierarchy, super.coreTypes,
+ super.typeInferenceEngine, super.offsetMap, this.bodyBuilderCreator);
@override
BodyBuilder createListenerInternal(
diff --git a/pkg/front_end/test/coverage_suite.dart b/pkg/front_end/test/coverage_suite.dart
index 0788206..6dc69d8 100644
--- a/pkg/front_end/test/coverage_suite.dart
+++ b/pkg/front_end/test/coverage_suite.dart
@@ -97,7 +97,7 @@
78.42227378190255,
"package:front_end/src/fasta/builder/void_type_declaration_builder.dart":
100.0,
- "package:front_end/src/fasta/builder_graph.dart": 55.769230769230774,
+ "package:front_end/src/fasta/builder_graph.dart": 54.0,
"package:front_end/src/fasta/codes/fasta_codes_cfe_generated.dart":
73.0892742453436,
"package:front_end/src/fasta/codes/type_labeler.dart": 83.68336025848141,
@@ -205,10 +205,10 @@
"package:front_end/src/fasta/modifier.dart": 100.0,
"package:front_end/src/fasta/operator.dart": 100.0,
"package:front_end/src/fasta/problems.dart": 0.0,
- "package:front_end/src/fasta/scope.dart": 80.14256619144604,
+ "package:front_end/src/fasta/scope.dart": 79.0,
"package:front_end/src/fasta/source/class_declaration.dart":
80.29556650246306,
- "package:front_end/src/fasta/source/diet_listener.dart": 91.22807017543859,
+ "package:front_end/src/fasta/source/diet_listener.dart": 89.0,
"package:front_end/src/fasta/source/diet_parser.dart": 100.0,
"package:front_end/src/fasta/source/name_scheme.dart": 93.19148936170212,
"package:front_end/src/fasta/source/outline_builder.dart": 91.54411764705883,
diff --git a/pkg/front_end/testcases/general/part_of_multiple.dart b/pkg/front_end/testcases/general/part_of_multiple.dart
new file mode 100644
index 0000000..5e76378
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple.dart
@@ -0,0 +1,6 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'part_of_multiple_lib1.dart';
+import 'part_of_multiple_lib2.dart';
diff --git a/pkg/front_end/testcases/general/part_of_multiple.dart.strong.expect b/pkg/front_end/testcases/general/part_of_multiple.dart.strong.expect
new file mode 100644
index 0000000..0a4a719
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple.dart.strong.expect
@@ -0,0 +1,24 @@
+library;
+import self as self;
+
+import "org-dartlang-testcase:///part_of_multiple_lib1.dart";
+import "org-dartlang-testcase:///part_of_multiple_lib2.dart";
+
+
+library part_of_multiple;
+import self as self2;
+
+part part_of_multiple_part.dart;
+
+library part_of_multiple;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_of_multiple_part.dart: Error: A file can't be part of more than one library.
+// Try moving the shared declarations into the libraries, or into a new library.
+// pkg/front_end/testcases/general/part_of_multiple_lib1.dart: Context: Used as a part in this library.
+// pkg/front_end/testcases/general/part_of_multiple_lib2.dart: Context: Used as a part in this library.
+//
+import self as self3;
+
+part part_of_multiple_part.dart;
diff --git a/pkg/front_end/testcases/general/part_of_multiple.dart.strong.modular.expect b/pkg/front_end/testcases/general/part_of_multiple.dart.strong.modular.expect
new file mode 100644
index 0000000..0a4a719
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple.dart.strong.modular.expect
@@ -0,0 +1,24 @@
+library;
+import self as self;
+
+import "org-dartlang-testcase:///part_of_multiple_lib1.dart";
+import "org-dartlang-testcase:///part_of_multiple_lib2.dart";
+
+
+library part_of_multiple;
+import self as self2;
+
+part part_of_multiple_part.dart;
+
+library part_of_multiple;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_of_multiple_part.dart: Error: A file can't be part of more than one library.
+// Try moving the shared declarations into the libraries, or into a new library.
+// pkg/front_end/testcases/general/part_of_multiple_lib1.dart: Context: Used as a part in this library.
+// pkg/front_end/testcases/general/part_of_multiple_lib2.dart: Context: Used as a part in this library.
+//
+import self as self3;
+
+part part_of_multiple_part.dart;
diff --git a/pkg/front_end/testcases/general/part_of_multiple.dart.strong.outline.expect b/pkg/front_end/testcases/general/part_of_multiple.dart.strong.outline.expect
new file mode 100644
index 0000000..0a4a719
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple.dart.strong.outline.expect
@@ -0,0 +1,24 @@
+library;
+import self as self;
+
+import "org-dartlang-testcase:///part_of_multiple_lib1.dart";
+import "org-dartlang-testcase:///part_of_multiple_lib2.dart";
+
+
+library part_of_multiple;
+import self as self2;
+
+part part_of_multiple_part.dart;
+
+library part_of_multiple;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_of_multiple_part.dart: Error: A file can't be part of more than one library.
+// Try moving the shared declarations into the libraries, or into a new library.
+// pkg/front_end/testcases/general/part_of_multiple_lib1.dart: Context: Used as a part in this library.
+// pkg/front_end/testcases/general/part_of_multiple_lib2.dart: Context: Used as a part in this library.
+//
+import self as self3;
+
+part part_of_multiple_part.dart;
diff --git a/pkg/front_end/testcases/general/part_of_multiple.dart.strong.transformed.expect b/pkg/front_end/testcases/general/part_of_multiple.dart.strong.transformed.expect
new file mode 100644
index 0000000..0a4a719
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple.dart.strong.transformed.expect
@@ -0,0 +1,24 @@
+library;
+import self as self;
+
+import "org-dartlang-testcase:///part_of_multiple_lib1.dart";
+import "org-dartlang-testcase:///part_of_multiple_lib2.dart";
+
+
+library part_of_multiple;
+import self as self2;
+
+part part_of_multiple_part.dart;
+
+library part_of_multiple;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_of_multiple_part.dart: Error: A file can't be part of more than one library.
+// Try moving the shared declarations into the libraries, or into a new library.
+// pkg/front_end/testcases/general/part_of_multiple_lib1.dart: Context: Used as a part in this library.
+// pkg/front_end/testcases/general/part_of_multiple_lib2.dart: Context: Used as a part in this library.
+//
+import self as self3;
+
+part part_of_multiple_part.dart;
diff --git a/pkg/front_end/testcases/general/part_of_multiple.dart.textual_outline.expect b/pkg/front_end/testcases/general/part_of_multiple.dart.textual_outline.expect
new file mode 100644
index 0000000..0370ff6
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple.dart.textual_outline.expect
@@ -0,0 +1,3 @@
+import 'part_of_multiple_lib1.dart';
+
+import 'part_of_multiple_lib2.dart';
diff --git a/pkg/front_end/testcases/general/part_of_multiple.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/part_of_multiple.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..03c393e
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple.dart.textual_outline_modelled.expect
@@ -0,0 +1,2 @@
+import 'part_of_multiple_lib1.dart';
+import 'part_of_multiple_lib2.dart';
diff --git a/pkg/front_end/testcases/general/part_of_multiple_lib1.dart b/pkg/front_end/testcases/general/part_of_multiple_lib1.dart
new file mode 100644
index 0000000..4322291
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple_lib1.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2024, 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 part_of_multiple;
+
+part 'part_of_multiple_part.dart';
diff --git a/pkg/front_end/testcases/general/part_of_multiple_lib2.dart b/pkg/front_end/testcases/general/part_of_multiple_lib2.dart
new file mode 100644
index 0000000..4322291
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple_lib2.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2024, 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 part_of_multiple;
+
+part 'part_of_multiple_part.dart';
diff --git a/pkg/front_end/testcases/general/part_of_multiple_part.dart b/pkg/front_end/testcases/general/part_of_multiple_part.dart
new file mode 100644
index 0000000..8d76ba4
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_of_multiple_part.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2024, 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.
+
+part of part_of_multiple;
diff --git a/pkg/front_end/testcases/general/part_self.dart b/pkg/front_end/testcases/general/part_self.dart
new file mode 100644
index 0000000..dc0f39a
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_self.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2024, 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.
+
+part 'part_self.dart';
diff --git a/pkg/front_end/testcases/general/part_self.dart.strong.expect b/pkg/front_end/testcases/general/part_self.dart.strong.expect
new file mode 100644
index 0000000..d591432
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_self.dart.strong.expect
@@ -0,0 +1,9 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_self.dart: Error: A file can't be a part of itself.
+//
+import self as self;
+
+part part_self.dart;
diff --git a/pkg/front_end/testcases/general/part_self.dart.strong.modular.expect b/pkg/front_end/testcases/general/part_self.dart.strong.modular.expect
new file mode 100644
index 0000000..d591432
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_self.dart.strong.modular.expect
@@ -0,0 +1,9 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_self.dart: Error: A file can't be a part of itself.
+//
+import self as self;
+
+part part_self.dart;
diff --git a/pkg/front_end/testcases/general/part_self.dart.strong.outline.expect b/pkg/front_end/testcases/general/part_self.dart.strong.outline.expect
new file mode 100644
index 0000000..d591432
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_self.dart.strong.outline.expect
@@ -0,0 +1,9 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_self.dart: Error: A file can't be a part of itself.
+//
+import self as self;
+
+part part_self.dart;
diff --git a/pkg/front_end/testcases/general/part_self.dart.strong.transformed.expect b/pkg/front_end/testcases/general/part_self.dart.strong.transformed.expect
new file mode 100644
index 0000000..d591432
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_self.dart.strong.transformed.expect
@@ -0,0 +1,9 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_self.dart: Error: A file can't be a part of itself.
+//
+import self as self;
+
+part part_self.dart;
diff --git a/pkg/front_end/testcases/general/part_self.dart.textual_outline.expect b/pkg/front_end/testcases/general/part_self.dart.textual_outline.expect
new file mode 100644
index 0000000..347608e
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_self.dart.textual_outline.expect
@@ -0,0 +1 @@
+part 'part_self.dart';
diff --git a/pkg/front_end/testcases/general/part_self.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/part_self.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..347608e
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_self.dart.textual_outline_modelled.expect
@@ -0,0 +1 @@
+part 'part_self.dart';
diff --git a/pkg/front_end/testcases/general/part_twice.dart b/pkg/front_end/testcases/general/part_twice.dart
new file mode 100644
index 0000000..8ce116f
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_twice.dart
@@ -0,0 +1,6 @@
+// Copyright (c) 2024, 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.
+
+part 'part_twice_part.dart';
+part 'part_twice_part.dart';
diff --git a/pkg/front_end/testcases/general/part_twice.dart.strong.expect b/pkg/front_end/testcases/general/part_twice.dart.strong.expect
new file mode 100644
index 0000000..1402ce5
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_twice.dart.strong.expect
@@ -0,0 +1,10 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_twice.dart: Error: Can't use 'pkg/front_end/testcases/general/part_twice_part.dart' as a part more than once.
+//
+import self as self;
+
+part part_twice_part.dart;
+part part_twice_part.dart;
diff --git a/pkg/front_end/testcases/general/part_twice.dart.strong.modular.expect b/pkg/front_end/testcases/general/part_twice.dart.strong.modular.expect
new file mode 100644
index 0000000..1402ce5
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_twice.dart.strong.modular.expect
@@ -0,0 +1,10 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_twice.dart: Error: Can't use 'pkg/front_end/testcases/general/part_twice_part.dart' as a part more than once.
+//
+import self as self;
+
+part part_twice_part.dart;
+part part_twice_part.dart;
diff --git a/pkg/front_end/testcases/general/part_twice.dart.strong.outline.expect b/pkg/front_end/testcases/general/part_twice.dart.strong.outline.expect
new file mode 100644
index 0000000..1402ce5
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_twice.dart.strong.outline.expect
@@ -0,0 +1,10 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_twice.dart: Error: Can't use 'pkg/front_end/testcases/general/part_twice_part.dart' as a part more than once.
+//
+import self as self;
+
+part part_twice_part.dart;
+part part_twice_part.dart;
diff --git a/pkg/front_end/testcases/general/part_twice.dart.strong.transformed.expect b/pkg/front_end/testcases/general/part_twice.dart.strong.transformed.expect
new file mode 100644
index 0000000..1402ce5
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_twice.dart.strong.transformed.expect
@@ -0,0 +1,10 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/part_twice.dart: Error: Can't use 'pkg/front_end/testcases/general/part_twice_part.dart' as a part more than once.
+//
+import self as self;
+
+part part_twice_part.dart;
+part part_twice_part.dart;
diff --git a/pkg/front_end/testcases/general/part_twice.dart.textual_outline.expect b/pkg/front_end/testcases/general/part_twice.dart.textual_outline.expect
new file mode 100644
index 0000000..1819cdd6
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_twice.dart.textual_outline.expect
@@ -0,0 +1,3 @@
+part 'part_twice_part.dart';
+
+part 'part_twice_part.dart';
diff --git a/pkg/front_end/testcases/general/part_twice.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/part_twice.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..1819cdd6
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_twice.dart.textual_outline_modelled.expect
@@ -0,0 +1,3 @@
+part 'part_twice_part.dart';
+
+part 'part_twice_part.dart';
diff --git a/pkg/front_end/testcases/general/part_twice_part.dart b/pkg/front_end/testcases/general/part_twice_part.dart
new file mode 100644
index 0000000..93a8918
--- /dev/null
+++ b/pkg/front_end/testcases/general/part_twice_part.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2024, 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.
+
+part of 'part_twice.dart';
diff --git a/pkg/front_end/testcases/macros/multiple_imports.dart.strong.expect b/pkg/front_end/testcases/macros/multiple_imports.dart.strong.expect
index b7fd881..b6f612e 100644
--- a/pkg/front_end/testcases/macros/multiple_imports.dart.strong.expect
+++ b/pkg/front_end/testcases/macros/multiple_imports.dart.strong.expect
@@ -5,12 +5,12 @@
import "dart:convert" as con;
@#C2
-@#C4
import "dart:math";
-@#C6
-@#C8
+@#C4
import "dart:convert";
+@#C6
import "dart:async";
+@#C8
import "org-dartlang-testcase:///multiple_imports.dart";
class Const extends core::Object /*hasConstConstructor*/ { // from org-dartlang-testcase:///multiple_imports_lib.dart
@@ -30,9 +30,9 @@
constants {
#C1 = 0
#C2 = self::Const {field:#C1}
- #C3 = 2
+ #C3 = 1
#C4 = self::Const {field:#C3}
- #C5 = 1
+ #C5 = 2
#C6 = self::Const {field:#C5}
#C7 = 3
#C8 = self::Const {field:#C7}
diff --git a/pkg/front_end/testcases/macros/multiple_imports.dart.strong.modular.expect b/pkg/front_end/testcases/macros/multiple_imports.dart.strong.modular.expect
index b7fd881..b6f612e 100644
--- a/pkg/front_end/testcases/macros/multiple_imports.dart.strong.modular.expect
+++ b/pkg/front_end/testcases/macros/multiple_imports.dart.strong.modular.expect
@@ -5,12 +5,12 @@
import "dart:convert" as con;
@#C2
-@#C4
import "dart:math";
-@#C6
-@#C8
+@#C4
import "dart:convert";
+@#C6
import "dart:async";
+@#C8
import "org-dartlang-testcase:///multiple_imports.dart";
class Const extends core::Object /*hasConstConstructor*/ { // from org-dartlang-testcase:///multiple_imports_lib.dart
@@ -30,9 +30,9 @@
constants {
#C1 = 0
#C2 = self::Const {field:#C1}
- #C3 = 2
+ #C3 = 1
#C4 = self::Const {field:#C3}
- #C5 = 1
+ #C5 = 2
#C6 = self::Const {field:#C5}
#C7 = 3
#C8 = self::Const {field:#C7}
diff --git a/pkg/front_end/testcases/macros/multiple_imports.dart.strong.transformed.expect b/pkg/front_end/testcases/macros/multiple_imports.dart.strong.transformed.expect
index b7fd881..b6f612e 100644
--- a/pkg/front_end/testcases/macros/multiple_imports.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/macros/multiple_imports.dart.strong.transformed.expect
@@ -5,12 +5,12 @@
import "dart:convert" as con;
@#C2
-@#C4
import "dart:math";
-@#C6
-@#C8
+@#C4
import "dart:convert";
+@#C6
import "dart:async";
+@#C8
import "org-dartlang-testcase:///multiple_imports.dart";
class Const extends core::Object /*hasConstConstructor*/ { // from org-dartlang-testcase:///multiple_imports_lib.dart
@@ -30,9 +30,9 @@
constants {
#C1 = 0
#C2 = self::Const {field:#C1}
- #C3 = 2
+ #C3 = 1
#C4 = self::Const {field:#C3}
- #C5 = 1
+ #C5 = 2
#C6 = self::Const {field:#C5}
#C7 = 3
#C8 = self::Const {field:#C7}