Version 2.16.0-54.0.dev
Merge commit 'ad6ffaab952ae0ad6816b122d4340c5fd43fd587' into 'dev'
diff --git a/pkg/front_end/analysis_options_no_lints.yaml b/pkg/front_end/analysis_options_no_lints.yaml
index aca6b61..cc832b8 100644
--- a/pkg/front_end/analysis_options_no_lints.yaml
+++ b/pkg/front_end/analysis_options_no_lints.yaml
@@ -4,6 +4,7 @@
analyzer:
exclude:
+ - outline_extraction_testcases/**
- parser_testcases/**
- test/analyser_ignored/**
- test/class_hierarchy/data/**
diff --git a/pkg/front_end/lib/src/fasta/util/abstracted_ast_nodes.dart b/pkg/front_end/lib/src/fasta/util/abstracted_ast_nodes.dart
new file mode 100644
index 0000000..57ae59a
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/util/abstracted_ast_nodes.dart
@@ -0,0 +1,864 @@
+// Copyright (c) 2021, 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:front_end/src/fasta/util/direct_parser_ast_helper.dart';
+
+enum Coloring { Untouched, Marked }
+
+abstract class AstNode {
+ Map<String, List<AstNode>> scope = {};
+ Container? parent;
+ DirectParserASTContent get node;
+ Token get startInclusive;
+ Token get endInclusive;
+
+ Coloring marked = Coloring.Untouched;
+
+ StringBuffer toStringInternal(StringBuffer sb, int indent);
+
+ void buildScope();
+ Map<String, AstNode> selfScope();
+
+ List<AstNode>? findInScope(String name) {
+ return scope[name] ?? parent?.findInScope(name);
+ }
+}
+
+abstract class Container extends AstNode {
+ List<AstNode> _children = [];
+ Iterable<AstNode> get children => _children;
+
+ void addChild(AstNode child, Map<DirectParserASTContent, AstNode> map) {
+ child.parent = this;
+ _children.add(child);
+ map[child.node] = child;
+ }
+}
+
+class TopLevel extends Container {
+ final String sourceText;
+ final Uri uri;
+
+ @override
+ final DirectParserASTContent node;
+
+ final Map<DirectParserASTContent, AstNode> map;
+
+ TopLevel(this.sourceText, this.uri, this.node, this.map);
+
+ @override
+ String toString() => toStringInternal(new StringBuffer(), 0).toString();
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ if (_children.isEmpty) {
+ String stringIndent = " " * ((indent + 1) * 2);
+ sb.write(stringIndent);
+ sb.writeln("(empty)");
+ } else {
+ for (AstNode node in _children) {
+ sb.write(stringIndent);
+ node.toStringInternal(sb, indent + 1);
+ }
+ }
+ return sb;
+ }
+
+ @override
+ void buildScope() {
+ for (AstNode child in _children) {
+ child.buildScope();
+ for (MapEntry<String, AstNode> entry in child.selfScope().entries) {
+ (scope[entry.key] ??= []).add(entry.value);
+ }
+ }
+ }
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return const {};
+ }
+
+ @override
+ Token get endInclusive => throw new UnimplementedError();
+
+ @override
+ Token get startInclusive => throw new UnimplementedError();
+}
+
+class Class extends Container {
+ @override
+ final DirectParserASTContentTopLevelDeclarationEnd node;
+ final String name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ Class(this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Class $name");
+ if (_children.isEmpty) {
+ String stringIndent = " " * ((indent + 1) * 2);
+ sb.write(stringIndent);
+ sb.writeln("(empty)");
+ } else {
+ for (AstNode node in _children) {
+ node.toStringInternal(sb, indent + 1);
+ }
+ }
+ return sb;
+ }
+
+ @override
+ void buildScope() {
+ for (AstNode child in _children) {
+ child.buildScope();
+ for (MapEntry<String, AstNode> entry in child.selfScope().entries) {
+ (scope[entry.key] ??= []).add(entry.value);
+ }
+ }
+ }
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return {name: this};
+ }
+}
+
+class Mixin extends Container {
+ @override
+ final DirectParserASTContentTopLevelDeclarationEnd node;
+ final String name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ Mixin(this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Mixin $name");
+ if (_children.isEmpty) {
+ String stringIndent = " " * ((indent + 1) * 2);
+ sb.write(stringIndent);
+ sb.writeln("(empty)");
+ } else {
+ for (AstNode node in _children) {
+ node.toStringInternal(sb, indent + 1);
+ }
+ }
+ return sb;
+ }
+
+ @override
+ void buildScope() {
+ for (AstNode child in _children) {
+ child.buildScope();
+ for (MapEntry<String, AstNode> entry in child.selfScope().entries) {
+ (scope[entry.key] ??= []).add(entry.value);
+ }
+ }
+ }
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return {name: this};
+ }
+}
+
+class Extension extends Container {
+ @override
+ final DirectParserASTContentTopLevelDeclarationEnd node;
+ final String? name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ Extension(this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Extension $name");
+ if (_children.isEmpty) {
+ String stringIndent = " " * ((indent + 1) * 2);
+ sb.write(stringIndent);
+ sb.writeln("(empty)");
+ } else {
+ for (AstNode node in _children) {
+ node.toStringInternal(sb, indent + 1);
+ }
+ }
+ return sb;
+ }
+
+ @override
+ void buildScope() {
+ for (AstNode child in _children) {
+ child.buildScope();
+ for (MapEntry<String, AstNode> entry in child.selfScope().entries) {
+ (scope[entry.key] ??= []).add(entry.value);
+ }
+ }
+ }
+
+ @override
+ Map<String, AstNode> selfScope() {
+ if (name != null) {
+ return {name!: this};
+ } else {
+ return const {};
+ }
+ }
+}
+
+class ClassConstructor extends AstNode {
+ @override
+ final DirectParserASTContentClassConstructorEnd node;
+ final String name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ ClassConstructor(
+ this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Class constructor $name");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ // TODO: Possibly this should be different...
+ return {name: this};
+ }
+}
+
+class ClassFactoryMethod extends AstNode {
+ @override
+ final DirectParserASTContentClassFactoryMethodEnd node;
+ final String name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ ClassFactoryMethod(
+ this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Class factory constructor $name");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ // TODO: Possibly this should be different...
+ return {name: this};
+ }
+}
+
+class ClassMethod extends AstNode {
+ @override
+ final DirectParserASTContentClassMethodEnd node;
+ final String name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ ClassMethod(this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Class method $name");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return {name: this};
+ }
+}
+
+class ExtensionMethod extends AstNode {
+ @override
+ final DirectParserASTContentExtensionMethodEnd node;
+ final String name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ ExtensionMethod(this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Extension method $name");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return {name: this};
+ }
+}
+
+class MixinMethod extends AstNode {
+ @override
+ final DirectParserASTContentMixinMethodEnd node;
+ final String name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ MixinMethod(this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Mixin method $name");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return {name: this};
+ }
+}
+
+class Enum extends AstNode {
+ @override
+ final DirectParserASTContentEnumEnd node;
+ final String name;
+ final List<String> members;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ Enum(this.node, this.name, this.members, this.startInclusive,
+ this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Enum $name with members $members");
+ return sb;
+ }
+
+ @override
+ void buildScope() {
+ for (String child in members) {
+ scope[child] = [this];
+ }
+ }
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return {name: this};
+ }
+}
+
+class Import extends AstNode {
+ @override
+ final DirectParserASTContentImportEnd node;
+ final Uri firstUri;
+ final List<Uri>? conditionalUris;
+ final String? asName;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ Import(this.node, this.firstUri, this.conditionalUris, this.asName,
+ this.startInclusive, this.endInclusive);
+
+ List<Uri> get uris => [firstUri, ...?conditionalUris];
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ if (asName == null) {
+ sb.writeln("Import of $uris");
+ } else {
+ sb.writeln("Import of $uris as '$asName'");
+ }
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ if (asName != null) {
+ return {asName!: this};
+ }
+ return const {};
+ }
+}
+
+class Export extends AstNode {
+ @override
+ final DirectParserASTContentExportEnd node;
+ final Uri firstUri;
+ final List<Uri>? conditionalUris;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ Export(this.node, this.firstUri, this.conditionalUris, this.startInclusive,
+ this.endInclusive);
+
+ List<Uri> get uris => [firstUri, ...?conditionalUris];
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Export of $uris");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return const {};
+ }
+}
+
+class Part extends AstNode {
+ @override
+ final DirectParserASTContentPartEnd node;
+ final Uri uri;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ Part(this.node, this.uri, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Part $uri");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return const {};
+ }
+}
+
+class TopLevelFields extends AstNode {
+ @override
+ final DirectParserASTContentTopLevelFieldsEnd node;
+ final List<String> names;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ TopLevelFields(this.node, this.names, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Top level field(s) ${names.join(", ")}");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ Map<String, AstNode> scope = {};
+ for (String name in names) {
+ scope[name] = this;
+ }
+ return scope;
+ }
+}
+
+class TopLevelMethod extends AstNode {
+ @override
+ final DirectParserASTContentTopLevelMethodEnd node;
+ final String name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ TopLevelMethod(this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Top level method $name");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return {name: this};
+ }
+}
+
+class Typedef extends AstNode {
+ @override
+ final DirectParserASTContentTypedefEnd node;
+ final String name;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ Typedef(this.node, this.name, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Top level method $name");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return {name: this};
+ }
+}
+
+class ClassFields extends AstNode {
+ @override
+ final DirectParserASTContentClassFieldsEnd node;
+ final List<String> names;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ ClassFields(this.node, this.names, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Class field(s) ${names.join(", ")}");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ Map<String, AstNode> scope = {};
+ for (String name in names) {
+ scope[name] = this;
+ }
+ return scope;
+ }
+}
+
+class MixinFields extends AstNode {
+ @override
+ final DirectParserASTContentMixinFieldsEnd node;
+ final List<String> names;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ MixinFields(this.node, this.names, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Mixin field(s) ${names.join(", ")}");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ Map<String, AstNode> scope = {};
+ for (String name in names) {
+ scope[name] = this;
+ }
+ return scope;
+ }
+}
+
+class ExtensionFields extends AstNode {
+ @override
+ final DirectParserASTContentExtensionFieldsEnd node;
+ final List<String> names;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ ExtensionFields(
+ this.node, this.names, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("Extension field(s) ${names.join(", ")}");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ Map<String, AstNode> scope = {};
+ for (String name in names) {
+ scope[name] = this;
+ }
+ return scope;
+ }
+}
+
+class Metadata extends AstNode {
+ @override
+ final DirectParserASTContentMetadataEnd node;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ Metadata(this.node, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("metadata");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return const {};
+ }
+}
+
+class LibraryName extends AstNode {
+ @override
+ final DirectParserASTContentLibraryNameEnd node;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ LibraryName(this.node, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("library name");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return const {};
+ }
+}
+
+class PartOf extends AstNode {
+ @override
+ final DirectParserASTContentPartOfEnd node;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+ final Uri partOfUri;
+
+ PartOf(this.node, this.partOfUri, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("part of");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return const {};
+ }
+}
+
+class LanguageVersion extends AstNode {
+ @override
+ final DirectParserASTContent node;
+ @override
+ final Token startInclusive;
+ @override
+ final Token endInclusive;
+
+ LanguageVersion(this.node, this.startInclusive, this.endInclusive);
+
+ @override
+ StringBuffer toStringInternal(StringBuffer sb, int indent) {
+ String stringIndent = " " * (indent * 2);
+ sb.write(stringIndent);
+ if (marked != Coloring.Untouched) {
+ sb.write("(marked) ");
+ }
+ sb.writeln("$startInclusive");
+ return sb;
+ }
+
+ @override
+ void buildScope() {}
+
+ @override
+ Map<String, AstNode> selfScope() {
+ return const {};
+ }
+}
diff --git a/pkg/front_end/lib/src/fasta/util/direct_parser_ast.dart b/pkg/front_end/lib/src/fasta/util/direct_parser_ast.dart
index 94a1865..a5bb20a 100644
--- a/pkg/front_end/lib/src/fasta/util/direct_parser_ast.dart
+++ b/pkg/front_end/lib/src/fasta/util/direct_parser_ast.dart
@@ -6,6 +6,7 @@
import 'dart:io' show File;
+import 'package:_fe_analyzer_shared/src/messages/codes.dart';
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart'
show ScannerConfiguration;
@@ -17,6 +18,13 @@
import 'package:_fe_analyzer_shared/src/scanner/token.dart' show Token;
+import 'package:_fe_analyzer_shared/src/parser/listener.dart'
+ show UnescapeErrorListener;
+
+import 'package:_fe_analyzer_shared/src/parser/identifier_context.dart';
+
+import 'package:_fe_analyzer_shared/src/parser/quote.dart' show unescapeString;
+
import '../source/diet_parser.dart';
import 'direct_parser_ast_helper.dart';
@@ -26,7 +34,8 @@
bool includeComments: false,
bool enableExtensionMethods: false,
bool enableNonNullable: false,
- bool enableTripleShift: false}) {
+ bool enableTripleShift: false,
+ List<Token>? languageVersionsSeen}) {
Uint8List bytes = new Uint8List(rawBytes.length + 1);
bytes.setRange(0, rawBytes.length, rawBytes);
@@ -42,6 +51,7 @@
languageVersionChanged: (scanner, languageVersion) {
// For now don't do anything, but having it (making it non-null) means the
// configuration won't be reset.
+ languageVersionsSeen?.add(languageVersion);
},
);
Token firstToken = scanner.tokenize();
@@ -1018,15 +1028,60 @@
}
}
-extension ClassFieldsExtension on DirectParserASTContentClassFieldsEnd {
- List<DirectParserASTContentIdentifierHandle?> getFieldIdentifiers() {
- // For now blindly assume that the last count identifiers are the names
- // of the fields.
+extension MixinFieldsExtension on DirectParserASTContentMixinFieldsEnd {
+ List<DirectParserASTContentIdentifierHandle> getFieldIdentifiers() {
int countLeft = count;
List<DirectParserASTContentIdentifierHandle>? identifiers;
for (int i = children!.length - 1; i >= 0; i--) {
DirectParserASTContent child = children![i];
- if (child is DirectParserASTContentIdentifierHandle) {
+ if (child is DirectParserASTContentIdentifierHandle &&
+ child.context == IdentifierContext.fieldDeclaration) {
+ countLeft--;
+ if (identifiers == null) {
+ identifiers = new List<DirectParserASTContentIdentifierHandle>.filled(
+ count, child);
+ } else {
+ identifiers[countLeft] = child;
+ }
+ if (countLeft == 0) break;
+ }
+ }
+ if (countLeft != 0) throw "Didn't find the expected number of identifiers";
+ return identifiers ?? [];
+ }
+}
+
+extension ExtensionFieldsExtension on DirectParserASTContentExtensionFieldsEnd {
+ List<DirectParserASTContentIdentifierHandle> getFieldIdentifiers() {
+ int countLeft = count;
+ List<DirectParserASTContentIdentifierHandle>? identifiers;
+ for (int i = children!.length - 1; i >= 0; i--) {
+ DirectParserASTContent child = children![i];
+ if (child is DirectParserASTContentIdentifierHandle &&
+ child.context == IdentifierContext.fieldDeclaration) {
+ countLeft--;
+ if (identifiers == null) {
+ identifiers = new List<DirectParserASTContentIdentifierHandle>.filled(
+ count, child);
+ } else {
+ identifiers[countLeft] = child;
+ }
+ if (countLeft == 0) break;
+ }
+ }
+ if (countLeft != 0) throw "Didn't find the expected number of identifiers";
+ return identifiers ?? [];
+ }
+}
+
+extension ClassFieldsExtension on DirectParserASTContentClassFieldsEnd {
+ List<DirectParserASTContentIdentifierHandle> getFieldIdentifiers() {
+ int countLeft = count;
+ List<DirectParserASTContentIdentifierHandle>? identifiers;
+ for (int i = children!.length - 1; i >= 0; i--) {
+ DirectParserASTContent child = children![i];
+ if (child is DirectParserASTContentIdentifierHandle &&
+ child.context == IdentifierContext.fieldDeclaration) {
countLeft--;
if (identifiers == null) {
identifiers = new List<DirectParserASTContentIdentifierHandle>.filled(
@@ -1056,6 +1111,190 @@
}
}
+extension EnumExtension on DirectParserASTContentEnumEnd {
+ List<DirectParserASTContentIdentifierHandle> getIdentifiers() {
+ List<DirectParserASTContentIdentifierHandle> ids = [];
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentIdentifierHandle) ids.add(child);
+ }
+ return ids;
+ }
+}
+
+extension ExtensionDeclarationExtension
+ on DirectParserASTContentExtensionDeclarationEnd {
+ List<DirectParserASTContentIdentifierHandle> getIdentifiers() {
+ List<DirectParserASTContentIdentifierHandle> ids = [];
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentIdentifierHandle) ids.add(child);
+ }
+ return ids;
+ }
+}
+
+extension TopLevelMethodExtension on DirectParserASTContentTopLevelMethodEnd {
+ DirectParserASTContentIdentifierHandle getNameIdentifier() {
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentIdentifierHandle) {
+ if (child.context == IdentifierContext.topLevelFunctionDeclaration) {
+ return child;
+ }
+ }
+ }
+ throw "Didn't find the name identifier!";
+ }
+}
+
+extension TypedefExtension on DirectParserASTContentTypedefEnd {
+ DirectParserASTContentIdentifierHandle getNameIdentifier() {
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentIdentifierHandle) {
+ if (child.context == IdentifierContext.typedefDeclaration) {
+ return child;
+ }
+ }
+ }
+ throw "Didn't find the name identifier!";
+ }
+}
+
+extension ImportExtension on DirectParserASTContentImportEnd {
+ DirectParserASTContentIdentifierHandle? getImportPrefix() {
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentIdentifierHandle) {
+ if (child.context == IdentifierContext.importPrefixDeclaration) {
+ return child;
+ }
+ }
+ }
+ }
+
+ String getImportUriString() {
+ StringBuffer sb = new StringBuffer();
+ bool foundOne = false;
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentLiteralStringEnd) {
+ DirectParserASTContentLiteralStringBegin uri =
+ child.children!.single as DirectParserASTContentLiteralStringBegin;
+ sb.write(unescapeString(
+ uri.token.lexeme, uri.token, const UnescapeErrorListenerDummy()));
+ foundOne = true;
+ }
+ }
+ if (!foundOne) throw "Didn't find any";
+ return sb.toString();
+ }
+
+ List<String>? getConditionalImportUriStrings() {
+ List<String>? result;
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentConditionalUrisEnd) {
+ for (DirectParserASTContent child2 in child.children!) {
+ if (child2 is DirectParserASTContentConditionalUriEnd) {
+ DirectParserASTContentLiteralStringEnd end =
+ child2.children!.last as DirectParserASTContentLiteralStringEnd;
+ DirectParserASTContentLiteralStringBegin uri = end.children!.single
+ as DirectParserASTContentLiteralStringBegin;
+ (result ??= []).add(unescapeString(uri.token.lexeme, uri.token,
+ const UnescapeErrorListenerDummy()));
+ }
+ }
+ return result;
+ }
+ }
+ return result;
+ }
+}
+
+extension ExportExtension on DirectParserASTContentExportEnd {
+ String getExportUriString() {
+ StringBuffer sb = new StringBuffer();
+ bool foundOne = false;
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentLiteralStringEnd) {
+ DirectParserASTContentLiteralStringBegin uri =
+ child.children!.single as DirectParserASTContentLiteralStringBegin;
+ sb.write(unescapeString(
+ uri.token.lexeme, uri.token, const UnescapeErrorListenerDummy()));
+ foundOne = true;
+ }
+ }
+ if (!foundOne) throw "Didn't find any";
+ return sb.toString();
+ }
+
+ List<String>? getConditionalExportUriStrings() {
+ List<String>? result;
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentConditionalUrisEnd) {
+ for (DirectParserASTContent child2 in child.children!) {
+ if (child2 is DirectParserASTContentConditionalUriEnd) {
+ DirectParserASTContentLiteralStringEnd end =
+ child2.children!.last as DirectParserASTContentLiteralStringEnd;
+ DirectParserASTContentLiteralStringBegin uri = end.children!.single
+ as DirectParserASTContentLiteralStringBegin;
+ (result ??= []).add(unescapeString(uri.token.lexeme, uri.token,
+ const UnescapeErrorListenerDummy()));
+ }
+ }
+ return result;
+ }
+ }
+ return result;
+ }
+}
+
+extension PartExtension on DirectParserASTContentPartEnd {
+ String getPartUriString() {
+ StringBuffer sb = new StringBuffer();
+ bool foundOne = false;
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentLiteralStringEnd) {
+ DirectParserASTContentLiteralStringBegin uri =
+ child.children!.single as DirectParserASTContentLiteralStringBegin;
+ sb.write(unescapeString(
+ uri.token.lexeme, uri.token, const UnescapeErrorListenerDummy()));
+ foundOne = true;
+ }
+ }
+ if (!foundOne) throw "Didn't find any";
+ return sb.toString();
+ }
+}
+
+class UnescapeErrorListenerDummy implements UnescapeErrorListener {
+ const UnescapeErrorListenerDummy();
+
+ @override
+ void handleUnescapeError(
+ Message message, covariant location, int offset, int length) {
+ // Purposely doesn't do anything.
+ }
+}
+
+extension TopLevelFieldsExtension on DirectParserASTContentTopLevelFieldsEnd {
+ List<DirectParserASTContentIdentifierHandle> getFieldIdentifiers() {
+ int countLeft = count;
+ List<DirectParserASTContentIdentifierHandle>? identifiers;
+ for (int i = children!.length - 1; i >= 0; i--) {
+ DirectParserASTContent child = children![i];
+ if (child is DirectParserASTContentIdentifierHandle &&
+ child.context == IdentifierContext.topLevelVariableDeclaration) {
+ countLeft--;
+ if (identifiers == null) {
+ identifiers = new List<DirectParserASTContentIdentifierHandle>.filled(
+ count, child);
+ } else {
+ identifiers[countLeft] = child;
+ }
+ if (countLeft == 0) break;
+ }
+ }
+ if (countLeft != 0) throw "Didn't find the expected number of identifiers";
+ return identifiers ?? [];
+ }
+}
+
extension ClassMethodExtension on DirectParserASTContentClassMethodEnd {
DirectParserASTContentBlockFunctionBodyEnd? getBlockFunctionBody() {
for (DirectParserASTContent child in children!) {
@@ -1065,6 +1304,80 @@
}
return null;
}
+
+ String getNameIdentifier() {
+ bool foundType = false;
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentTypeHandle ||
+ child is DirectParserASTContentNoTypeHandle ||
+ child is DirectParserASTContentVoidKeywordHandle ||
+ child is DirectParserASTContentFunctionTypeEnd) {
+ foundType = true;
+ }
+ if (foundType && child is DirectParserASTContentIdentifierHandle) {
+ return child.token.lexeme;
+ } else if (foundType &&
+ child is DirectParserASTContentOperatorNameHandle) {
+ return child.token.lexeme;
+ }
+ }
+ throw "No identifier found: $children";
+ }
+}
+
+extension MixinMethodExtension on DirectParserASTContentMixinMethodEnd {
+ String getNameIdentifier() {
+ bool foundType = false;
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentTypeHandle ||
+ child is DirectParserASTContentNoTypeHandle ||
+ child is DirectParserASTContentVoidKeywordHandle) {
+ foundType = true;
+ }
+ if (foundType && child is DirectParserASTContentIdentifierHandle) {
+ return child.token.lexeme;
+ } else if (foundType &&
+ child is DirectParserASTContentOperatorNameHandle) {
+ return child.token.lexeme;
+ }
+ }
+ throw "No identifier found: $children";
+ }
+}
+
+extension ExtensionMethodExtension on DirectParserASTContentExtensionMethodEnd {
+ String getNameIdentifier() {
+ bool foundType = false;
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentTypeHandle ||
+ child is DirectParserASTContentNoTypeHandle ||
+ child is DirectParserASTContentVoidKeywordHandle) {
+ foundType = true;
+ }
+ if (foundType && child is DirectParserASTContentIdentifierHandle) {
+ return child.token.lexeme;
+ } else if (foundType &&
+ child is DirectParserASTContentOperatorNameHandle) {
+ return child.token.lexeme;
+ }
+ }
+ throw "No identifier found: $children";
+ }
+}
+
+extension ClassFactoryMethodExtension
+ on DirectParserASTContentClassFactoryMethodEnd {
+ List<DirectParserASTContentIdentifierHandle> getIdentifiers() {
+ List<DirectParserASTContentIdentifierHandle> result = [];
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentIdentifierHandle) {
+ result.add(child);
+ } else if (child is DirectParserASTContentFormalParametersEnd) {
+ break;
+ }
+ }
+ return result;
+ }
}
extension ClassConstructorExtension
@@ -1095,6 +1408,16 @@
}
return null;
}
+
+ List<DirectParserASTContentIdentifierHandle> getIdentifiers() {
+ List<DirectParserASTContentIdentifierHandle> result = [];
+ for (DirectParserASTContent child in children!) {
+ if (child is DirectParserASTContentIdentifierHandle) {
+ result.add(child);
+ }
+ }
+ return result;
+ }
}
extension FormalParametersExtension
@@ -1271,6 +1594,9 @@
throw "Unknown combination: begin$begin and end$end";
}
List<DirectParserASTContent> children = data.sublist(beginIndex);
+ for (DirectParserASTContent child in children) {
+ child.parent = entry;
+ }
data.length = beginIndex;
data.add(entry..children = children);
break;
diff --git a/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart b/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart
index 2d73710..b0a2688 100644
--- a/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart
+++ b/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart
@@ -26,6 +26,7 @@
final DirectParserASTType type;
Map<String, Object?> get deprecatedArguments;
List<DirectParserASTContent>? children;
+ DirectParserASTContent? parent;
DirectParserASTContent(this.what, this.type);
diff --git a/pkg/front_end/lib/src/fasta/util/outline_extractor.dart b/pkg/front_end/lib/src/fasta/util/outline_extractor.dart
new file mode 100644
index 0000000..e8ba2cc
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/util/outline_extractor.dart
@@ -0,0 +1,964 @@
+// Copyright (c) 2021, 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 'dart:convert';
+
+import 'package:_fe_analyzer_shared/src/scanner/token.dart';
+import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
+ show ScannerConfiguration;
+import 'package:front_end/src/api_prototype/compiler_options.dart';
+import 'package:front_end/src/api_prototype/file_system.dart';
+import 'package:front_end/src/base/processed_options.dart';
+import 'package:front_end/src/fasta/compiler_context.dart';
+import 'package:front_end/src/fasta/uri_translator.dart';
+import 'package:front_end/src/fasta/util/direct_parser_ast_helper.dart';
+import 'package:front_end/src/fasta/util/textual_outline.dart';
+import 'package:_fe_analyzer_shared/src/parser/identifier_context.dart';
+import 'package:kernel/target/targets.dart';
+
+import "direct_parser_ast.dart";
+import "abstracted_ast_nodes.dart";
+
+// Overall TODO(s):
+// * If entry is given as fileuri but exists as different import uri...
+// Does that matter?
+// * Setters vs non-setters with naming conflicts.
+// * -> also these might be found on "different levels", e.g. the setter might
+// be in the class and the getter might be in an import.
+// * show/hide on imports and exports.
+// * Handle importing/exporting non-existing files.
+// * Tests.
+// * Maybe bypass the direct-from-parser-ast stuff for speed?
+// * Probably some of the special classes can be combined if we want to
+// (e.g. Class and Mixin).
+// * Extensions --- we currently basically mark all we see.
+// => Could be perhaps only include them if the class they're talking about
+// is included? (or we don't know).
+// * E.g. "factory Abc.b() => Abc3();" is the same as
+// "factory Abc.b() { return Abc3(); }" and Abc3 shouldn't be marked by it.
+// -> This is basically a rough edge on the textual outline though.
+// -> Also, the same applies to other instances of "=>".
+// * It shouldn't lookup private stuff in other libraries.
+// * Could there be made a distinction between for instance
+// `IdentifierContext.typeReference` and `IdentifierContext.expression`?
+// => one might not have to include content of classes that only talk about
+// typeReference I think.
+
+Future<void> main(List<String> args) async {
+ if (args.length != 2) {
+ throw "Needs 2 arguments: packages file/dir and file to process.";
+ }
+ Uri packages = Uri.base.resolve(args[0]);
+ Uri file = Uri.base.resolve(args[1]);
+ for (int i = 0; i < 1; i++) {
+ Stopwatch stopwatch = new Stopwatch()..start();
+ await extractOutline([file], packages: packages, verbosityLevel: 40);
+ print("Finished in ${stopwatch.elapsedMilliseconds} ms "
+ "(textual outline was "
+ "${latestProcessor!.textualOutlineStopwatch.elapsedMilliseconds} ms)"
+ "(get ast was "
+ "${latestProcessor!.getAstStopwatch.elapsedMilliseconds} ms)"
+ "(extract identifier was "
+ "${latestProcessor!.extractIdentifierStopwatch.elapsedMilliseconds} ms)"
+ "");
+ }
+}
+
+_Processor? latestProcessor;
+
+Future<Map<Uri, String>> extractOutline(List<Uri> entryPointUris,
+ {Uri? sdk,
+ required Uri? packages,
+ Uri? platform,
+ Target? target,
+ int verbosityLevel: 0}) {
+ CompilerOptions options = new CompilerOptions()
+ ..target = target
+ ..packagesFileUri = packages
+ ..sdkSummary = platform
+ ..sdkRoot = sdk;
+ ProcessedOptions pOptions =
+ new ProcessedOptions(options: options, inputs: entryPointUris);
+ return CompilerContext.runWithOptions(pOptions, (CompilerContext c) async {
+ FileSystem fileSystem = c.options.fileSystem;
+ UriTranslator uriTranslator = await c.options.getUriTranslator();
+ _Processor processor =
+ new _Processor(verbosityLevel, fileSystem, uriTranslator);
+ latestProcessor = processor;
+ List<TopLevel> entryPoints = [];
+ for (Uri entryPointUri in entryPointUris) {
+ TopLevel entryPoint = await processor.preprocessUri(entryPointUri);
+ entryPoints.add(entryPoint);
+ }
+ return await processor.calculate(entryPoints);
+ });
+}
+
+class _Processor {
+ final FileSystem fileSystem;
+ final UriTranslator uriTranslator;
+ final int verbosityLevel;
+
+ final Stopwatch textualOutlineStopwatch = new Stopwatch();
+ final Stopwatch getAstStopwatch = new Stopwatch();
+ final Stopwatch extractIdentifierStopwatch = new Stopwatch();
+
+ Map<Uri, TopLevel> parsed = {};
+
+ _Processor(this.verbosityLevel, this.fileSystem, this.uriTranslator);
+
+ void log(String s) {
+ if (verbosityLevel <= 0) return;
+ print(s);
+ }
+
+ Future<TopLevel> preprocessUri(Uri importUri, {Uri? partOf}) async {
+ if (verbosityLevel >= 20) log("$importUri =>");
+ Uri fileUri = importUri;
+ if (importUri.scheme == "package") {
+ fileUri = uriTranslator.translate(importUri)!;
+ }
+ if (verbosityLevel >= 20) log("$fileUri");
+ final List<int> bytes =
+ await fileSystem.entityForUri(fileUri).readAsBytes();
+ // TODO: Support updating the configuration; also default it to match
+ // the package version.
+ final ScannerConfiguration configuration = new ScannerConfiguration(
+ enableExtensionMethods: true,
+ enableNonNullable: true,
+ enableTripleShift: true);
+ textualOutlineStopwatch.start();
+ final String? outlined = textualOutline(bytes, configuration);
+ textualOutlineStopwatch.stop();
+ if (outlined == null) throw "Textual outline returned null";
+ final List<int> bytes2 = utf8.encode(outlined);
+ getAstStopwatch.start();
+ List<Token> languageVersionsSeen = [];
+ final DirectParserASTContent ast = getAST(bytes2,
+ enableExtensionMethods: configuration.enableExtensionMethods,
+ enableNonNullable: configuration.enableNonNullable,
+ enableTripleShift: configuration.enableTripleShift,
+ languageVersionsSeen: languageVersionsSeen);
+ getAstStopwatch.stop();
+
+ _ParserAstVisitor visitor = new _ParserAstVisitor(
+ verbosityLevel, outlined, importUri, partOf, ast, languageVersionsSeen);
+ TopLevel topLevel = visitor.currentContainer as TopLevel;
+ if (parsed[importUri] != null) throw "$importUri already set?!?";
+ parsed[importUri] = topLevel;
+ visitor.accept(ast);
+ topLevel.buildScope();
+
+ _IdentifierExtractor identifierExtractor = new _IdentifierExtractor();
+ extractIdentifierStopwatch.start();
+ identifierExtractor.extract(ast);
+ extractIdentifierStopwatch.stop();
+ for (DirectParserASTContentIdentifierHandle identifier
+ in identifierExtractor.identifiers) {
+ if (identifier.context == IdentifierContext.typeVariableDeclaration) {
+ // Hack: Put type variable declarations into scope so any overlap in
+ // name doesn't mark usages (e.g. a class E shouldn't be marked if we're
+ // talking about the type variable E).
+ DirectParserASTContent content = identifier;
+ AstNode? nearestAstNode = visitor.map[content];
+ while (nearestAstNode == null && content.parent != null) {
+ content = content.parent!;
+ nearestAstNode = visitor.map[content];
+ }
+ if (nearestAstNode == null) {
+ content = identifier;
+ nearestAstNode = visitor.map[content];
+ while (nearestAstNode == null && content.parent != null) {
+ content = content.parent!;
+ nearestAstNode = visitor.map[content];
+ }
+
+ StringBuffer sb = new StringBuffer();
+ Token t = identifier.token;
+ // for(int i = 0; i < 10; i++) {
+ // t = t.previous!;
+ // }
+ for (int i = 0; i < 20; i++) {
+ sb.write("$t ");
+ t = t.next!;
+ }
+ throw "$fileUri --- couldn't even find nearest ast node for "
+ "${identifier.token} :( -- context $sb";
+ }
+ (nearestAstNode.scope[identifier.token.lexeme] ??= [])
+ .add(nearestAstNode);
+ }
+ }
+
+ return topLevel;
+ }
+
+ Future<void> _premarkTopLevel(List<_TopLevelAndAstNode> worklist,
+ Set<TopLevel> closed, TopLevel entrypointish) async {
+ if (!closed.add(entrypointish)) return;
+
+ for (AstNode child in entrypointish.children) {
+ child.marked = Coloring.Marked;
+ worklist.add(new _TopLevelAndAstNode(entrypointish, child));
+
+ if (child is Part) {
+ if (child.uri.scheme != "dart") {
+ TopLevel partTopLevel = parsed[child.uri] ??
+ await preprocessUri(child.uri, partOf: entrypointish.uri);
+ await _premarkTopLevel(worklist, closed, partTopLevel);
+ }
+ } else if (child is Export) {
+ for (Uri importedUri in child.uris) {
+ if (importedUri.scheme != "dart") {
+ TopLevel exportTopLevel =
+ parsed[importedUri] ?? await preprocessUri(importedUri);
+ await _premarkTopLevel(worklist, closed, exportTopLevel);
+ }
+ }
+ }
+ }
+ }
+
+ Future<List<TopLevel>> _preprocessImportsAsNeeded(
+ Map<TopLevel, List<TopLevel>> imports, TopLevel topLevel) async {
+ List<TopLevel>? imported = imports[topLevel];
+ if (imported == null) {
+ // Process all imports.
+ imported = [];
+ imports[topLevel] = imported;
+ for (AstNode child in topLevel.children) {
+ if (child is Import) {
+ child.marked = Coloring.Marked;
+ for (Uri importedUri in child.uris) {
+ if (importedUri.scheme != "dart") {
+ TopLevel importedTopLevel =
+ parsed[importedUri] ?? await preprocessUri(importedUri);
+ imported.add(importedTopLevel);
+ }
+ }
+ } else if (child is PartOf) {
+ child.marked = Coloring.Marked;
+ if (child.partOfUri.scheme != "dart") {
+ TopLevel part = parsed[child.partOfUri]!;
+ List<TopLevel> importsFromPart =
+ await _preprocessImportsAsNeeded(imports, part);
+ imported.addAll(importsFromPart);
+ }
+ }
+ }
+ }
+ return imported;
+ }
+
+ Future<Map<Uri, String>> calculate(List<TopLevel> entryPoints) async {
+ List<_TopLevelAndAstNode> worklist = [];
+ Map<TopLevel, List<TopLevel>> imports = {};
+
+ // Mark all top-level in entry point. Also include parts and exports (and
+ // exports exports etc) of the entry point.
+ Set<TopLevel> closed = {};
+ for (TopLevel entryPoint in entryPoints) {
+ await _premarkTopLevel(worklist, closed, entryPoint);
+ }
+
+ Map<TopLevel, Set<String>> lookupsAll = {};
+ Map<TopLevel, List<String>> lookupsWorklist = {};
+ while (worklist.isNotEmpty || lookupsWorklist.isNotEmpty) {
+ while (worklist.isNotEmpty) {
+ _TopLevelAndAstNode entry = worklist.removeLast();
+ if (verbosityLevel >= 20) {
+ log("\n-----\nProcessing ${entry.entry.node.toString()}");
+ }
+ _IdentifierExtractor identifierExtractor = new _IdentifierExtractor();
+ identifierExtractor.extract(entry.entry.node);
+ if (verbosityLevel >= 20) {
+ log("Found ${identifierExtractor.identifiers}");
+ }
+ List<AstNode>? prevLookupResult;
+ nextIdentifier:
+ for (DirectParserASTContentIdentifierHandle identifier
+ in identifierExtractor.identifiers) {
+ DirectParserASTContent content = identifier;
+ AstNode? nearestAstNode = entry.topLevel.map[content];
+ while (nearestAstNode == null && content.parent != null) {
+ content = content.parent!;
+ nearestAstNode = entry.topLevel.map[content];
+ }
+ if (nearestAstNode == null) {
+ throw "couldn't even find nearest ast node for "
+ "${identifier.token} :(";
+ }
+
+ if (identifier.context == IdentifierContext.typeReference ||
+ identifier.context == IdentifierContext.prefixedTypeReference ||
+ identifier.context ==
+ IdentifierContext.typeReferenceContinuation ||
+ identifier.context == IdentifierContext.constructorReference ||
+ identifier.context ==
+ IdentifierContext.constructorReferenceContinuation ||
+ identifier.context == IdentifierContext.expression ||
+ identifier.context == IdentifierContext.expressionContinuation ||
+ identifier.context == IdentifierContext.metadataReference ||
+ identifier.context == IdentifierContext.metadataContinuation) {
+ bool lookupInThisScope = true;
+ if (!identifier.context.isContinuation) {
+ prevLookupResult = null;
+ } else if (prevLookupResult != null) {
+ // In continuation.
+ // either 0 or all should be imports.
+ for (AstNode prevResult in prevLookupResult) {
+ if (prevResult is Import) {
+ lookupInThisScope = false;
+ } else {
+ continue nextIdentifier;
+ }
+ }
+ } else {
+ // Still in continuation --- but prev lookup didn't yield
+ // anything. We shouldn't search for the continuation part in this
+ // scope (and thus skip looking in imports).
+ lookupInThisScope = false;
+ }
+ if (verbosityLevel >= 20) {
+ log("${identifier.token} (${identifier.context})");
+ }
+
+ // Now we need parts at this point. Either we're in the entry point
+ // in which case parts was read by [_premarkTopLevel], or we're here
+ // via lookups on an import, where parts were read too.
+ List<AstNode>? lookedUp;
+ if (lookupInThisScope) {
+ lookedUp = findInScope(
+ identifier.token.lexeme, nearestAstNode, entry.topLevel);
+ prevLookupResult = lookedUp;
+ }
+ if (lookedUp != null) {
+ for (AstNode found in lookedUp) {
+ if (verbosityLevel >= 20) log(" => found $found");
+ if (found.marked == Coloring.Untouched) {
+ found.marked = Coloring.Marked;
+ TopLevel foundTopLevel = entry.topLevel;
+ if (found.parent is TopLevel) {
+ foundTopLevel = found.parent as TopLevel;
+ }
+ worklist.add(new _TopLevelAndAstNode(foundTopLevel, found));
+ }
+ }
+ } else {
+ if (verbosityLevel >= 20) {
+ log("=> Should find this via an import probably?");
+ }
+
+ List<TopLevel> imported =
+ await _preprocessImportsAsNeeded(imports, entry.topLevel);
+
+ Set<Uri>? wantedImportUrls;
+ if (!lookupInThisScope && prevLookupResult != null) {
+ for (AstNode castMeAsImport in prevLookupResult) {
+ Import import = castMeAsImport as Import;
+ assert(import.asName != null);
+ (wantedImportUrls ??= {}).addAll(import.uris);
+ }
+ }
+
+ for (TopLevel other in imported) {
+ if (!lookupInThisScope && prevLookupResult != null) {
+ assert(wantedImportUrls != null);
+ if (!wantedImportUrls!.contains(other.uri)) continue;
+ }
+
+ Set<String> lookupStrings = lookupsAll[other] ??= {};
+ if (lookupStrings.add(identifier.token.lexeme)) {
+ List<String> lookupStringsWorklist =
+ lookupsWorklist[other] ??= [];
+ lookupStringsWorklist.add(identifier.token.lexeme);
+ }
+ }
+ }
+ } else {
+ if (verbosityLevel >= 30) {
+ log("Ignoring ${identifier.token} as it's a "
+ "${identifier.context}");
+ }
+ }
+ }
+ }
+ Map<TopLevel, List<String>> lookupsWorklistTmp = {};
+ for (MapEntry<TopLevel, List<String>> lookups
+ in lookupsWorklist.entries) {
+ TopLevel topLevel = lookups.key;
+ // We have to make the same lookups in parts and exports too.
+ for (AstNode child in topLevel.children) {
+ TopLevel? other;
+ if (child is Part) {
+ child.marked = Coloring.Marked;
+ // do stuff to part.
+ if (child.uri.scheme != "dart") {
+ other = parsed[child.uri] ??
+ await preprocessUri(child.uri, partOf: topLevel.uri);
+ }
+ } else if (child is Export) {
+ child.marked = Coloring.Marked;
+ // do stuff to export.
+ for (Uri importedUri in child.uris) {
+ if (importedUri.scheme != "dart") {
+ other = parsed[importedUri] ?? await preprocessUri(importedUri);
+ }
+ }
+ } else if (child is Extension) {
+ // TODO: Maybe put on a list to process later and only include if
+ // the on-class is included?
+ if (child.marked == Coloring.Untouched) {
+ child.marked = Coloring.Marked;
+ worklist.add(new _TopLevelAndAstNode(topLevel, child));
+ }
+ }
+ if (other != null) {
+ Set<String> lookupStrings = lookupsAll[other] ??= {};
+ for (String identifier in lookups.value) {
+ if (lookupStrings.add(identifier)) {
+ List<String> lookupStringsWorklist =
+ lookupsWorklistTmp[other] ??= [];
+ lookupStringsWorklist.add(identifier);
+ }
+ }
+ }
+ }
+
+ for (String identifier in lookups.value) {
+ List<AstNode>? foundInScope = topLevel.findInScope(identifier);
+ if (foundInScope != null) {
+ for (AstNode found in foundInScope) {
+ if (found.marked == Coloring.Untouched) {
+ found.marked = Coloring.Marked;
+ worklist.add(new _TopLevelAndAstNode(topLevel, found));
+ }
+ if (verbosityLevel >= 20) {
+ log(" => found $found via import (${found.marked})");
+ }
+ }
+ }
+ }
+ }
+ lookupsWorklist = lookupsWorklistTmp;
+ }
+
+ if (verbosityLevel >= 40) {
+ log("\n\n---------\n\n");
+ log(parsed.toString());
+ log("\n\n---------\n\n");
+ }
+
+ // Extract.
+ int count = 0;
+ Map<Uri, String> result = {};
+ // We only read imports if we need to lookup in them, but if a import
+ // statement is included in the output the file has to exist if it actually
+ // exists to not get a compilation error.
+ Set<Uri> imported = {};
+ for (MapEntry<Uri, TopLevel> entry in parsed.entries) {
+ if (verbosityLevel >= 40) log("${entry.key}:");
+ StringBuffer sb = new StringBuffer();
+ for (AstNode child in entry.value.children) {
+ if (child.marked == Coloring.Marked) {
+ String substring = entry.value.sourceText.substring(
+ child.startInclusive.charOffset, child.endInclusive.charEnd);
+ sb.writeln(substring);
+ if (verbosityLevel >= 40) {
+ log(substring);
+ }
+ if (child is Import) {
+ for (Uri importedUri in child.uris) {
+ if (importedUri.scheme != "dart") {
+ imported.add(importedUri);
+ }
+ }
+ }
+ }
+ }
+ if (sb.isNotEmpty) count++;
+ Uri uri = entry.key;
+ Uri fileUri = uri;
+ if (uri.scheme == "package") {
+ fileUri = uriTranslator.translate(uri)!;
+ }
+ result[fileUri] = sb.toString();
+ }
+ for (Uri uri in imported) {
+ TopLevel? topLevel = parsed[uri];
+ if (topLevel != null) continue;
+ // uri imports a file we haven't read. Check if it exists and include it
+ // as an empty file if it does.
+ Uri fileUri = uri;
+ if (uri.scheme == "package") {
+ fileUri = uriTranslator.translate(uri)!;
+ }
+ if (await fileSystem.entityForUri(fileUri).exists()) {
+ result[fileUri] = "";
+ }
+ }
+
+ print("=> Long story short got it to $count non-empty files...");
+
+ return result;
+ }
+
+ List<AstNode>? findInScope(
+ String name, AstNode nearestAstNode, TopLevel topLevel,
+ {Set<TopLevel>? visited}) {
+ List<AstNode>? result;
+ result = nearestAstNode.findInScope(name);
+ if (result != null) return result;
+ for (AstNode child in topLevel.children) {
+ if (child is Part) {
+ visited ??= {topLevel};
+ TopLevel partTopLevel = parsed[child.uri]!;
+ if (visited.add(partTopLevel)) {
+ result =
+ findInScope(name, partTopLevel, partTopLevel, visited: visited);
+ if (result != null) return result;
+ }
+ } else if (child is PartOf) {
+ visited ??= {topLevel};
+ TopLevel partOwnerTopLevel = parsed[child.partOfUri]!;
+ if (visited.add(partOwnerTopLevel)) {
+ result = findInScope(name, partOwnerTopLevel, partOwnerTopLevel,
+ visited: visited);
+ if (result != null) return result;
+ }
+ }
+ }
+ }
+}
+
+class _TopLevelAndAstNode {
+ final TopLevel topLevel;
+ final AstNode entry;
+
+ _TopLevelAndAstNode(this.topLevel, this.entry);
+}
+
+class _IdentifierExtractor {
+ List<DirectParserASTContentIdentifierHandle> identifiers = [];
+
+ void extract(DirectParserASTContent ast) {
+ if (ast is DirectParserASTContentIdentifierHandle) {
+ identifiers.add(ast);
+ }
+ List<DirectParserASTContent>? children = ast.children;
+ if (children != null) {
+ for (DirectParserASTContent child in children) {
+ extract(child);
+ }
+ }
+ }
+}
+
+class _ParserAstVisitor extends DirectParserASTContentVisitor {
+ final Uri uri;
+ final Uri? partOfUri;
+ late Container currentContainer;
+ final Map<DirectParserASTContent, AstNode> map = {};
+ final int verbosityLevel;
+ final List<Token> languageVersionsSeen;
+
+ _ParserAstVisitor(
+ this.verbosityLevel,
+ String sourceText,
+ this.uri,
+ this.partOfUri,
+ DirectParserASTContent rootAst,
+ this.languageVersionsSeen) {
+ currentContainer = new TopLevel(sourceText, uri, rootAst, map);
+ if (languageVersionsSeen.isNotEmpty) {
+ // Use first one.
+ Token languageVersion = languageVersionsSeen.first;
+ DirectParserASTContent dummyNode =
+ new DirectParserASTContentNoInitializersHandle(
+ DirectParserASTType.HANDLE);
+ LanguageVersion version =
+ new LanguageVersion(dummyNode, languageVersion, languageVersion);
+ version.marked = Coloring.Marked;
+ currentContainer.addChild(version, map);
+ }
+ }
+
+ void log(String s) {
+ if (verbosityLevel <= 0) return;
+ Container? x = currentContainer.parent;
+ int level = 0;
+ while (x != null) {
+ level++;
+ x = x.parent;
+ }
+ print(" " * level + s);
+ }
+
+ @override
+ void visitClass(DirectParserASTContentClassDeclarationEnd node,
+ Token startInclusive, Token endInclusive) {
+ DirectParserASTContentTopLevelDeclarationEnd parent =
+ node.parent! as DirectParserASTContentTopLevelDeclarationEnd;
+ DirectParserASTContentIdentifierHandle identifier = parent.getIdentifier();
+
+ log("Hello from class ${identifier.token}");
+
+ Class cls = new Class(
+ parent, identifier.token.lexeme, startInclusive, endInclusive);
+ currentContainer.addChild(cls, map);
+
+ Container previousContainer = currentContainer;
+ currentContainer = cls;
+ super.visitClass(node, startInclusive, endInclusive);
+ currentContainer = previousContainer;
+ }
+
+ @override
+ void visitClassConstructor(DirectParserASTContentClassConstructorEnd node,
+ Token startInclusive, Token endInclusive) {
+ assert(currentContainer is Class);
+ List<DirectParserASTContentIdentifierHandle> ids = node.getIdentifiers();
+ if (ids.length == 1) {
+ ClassConstructor classConstructor = new ClassConstructor(
+ node, ids.single.token.lexeme, startInclusive, endInclusive);
+ currentContainer.addChild(classConstructor, map);
+ log("Hello from constructor ${ids.single.token}");
+ } else if (ids.length == 2) {
+ ClassConstructor classConstructor = new ClassConstructor(node,
+ "${ids.first.token}.${ids.last.token}", startInclusive, endInclusive);
+ map[node] = classConstructor;
+ currentContainer.addChild(classConstructor, map);
+ log("Hello from constructor ${ids.first.token}.${ids.last.token}");
+ } else {
+ throw "Unexpected identifiers in class constructor";
+ }
+
+ super.visitClassConstructor(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitClassFactoryMethod(DirectParserASTContentClassFactoryMethodEnd node,
+ Token startInclusive, Token endInclusive) {
+ assert(currentContainer is Class);
+ List<DirectParserASTContentIdentifierHandle> ids = node.getIdentifiers();
+ if (ids.length == 1) {
+ ClassFactoryMethod classFactoryMethod = new ClassFactoryMethod(
+ node, ids.single.token.lexeme, startInclusive, endInclusive);
+ currentContainer.addChild(classFactoryMethod, map);
+ log("Hello from factory method ${ids.single.token}");
+ } else if (ids.length == 2) {
+ ClassFactoryMethod classFactoryMethod = new ClassFactoryMethod(node,
+ "${ids.first.token}.${ids.last.token}", startInclusive, endInclusive);
+ map[node] = classFactoryMethod;
+ currentContainer.addChild(classFactoryMethod, map);
+ log("Hello from factory method ${ids.first.token}.${ids.last.token}");
+ } else {
+ Container findTopLevel = currentContainer;
+ while (findTopLevel is! TopLevel) {
+ findTopLevel = findTopLevel.parent!;
+ }
+ String src = findTopLevel.sourceText
+ .substring(startInclusive.charOffset, endInclusive.charEnd);
+ throw "Unexpected identifiers in class factory method: $ids "
+ "(${ids.map((e) => e.token.lexeme).toList()}) --- "
+ "error on source ${src} --- "
+ "${node.children}";
+ }
+
+ super.visitClassFactoryMethod(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitClassFields(DirectParserASTContentClassFieldsEnd node,
+ Token startInclusive, Token endInclusive) {
+ assert(currentContainer is Class);
+ List<String> fields =
+ node.getFieldIdentifiers().map((e) => e.token.lexeme).toList();
+ ClassFields classFields =
+ new ClassFields(node, fields, startInclusive, endInclusive);
+ currentContainer.addChild(classFields, map);
+ log("Hello from class fields ${fields.join(", ")}");
+ super.visitClassFields(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitClassMethod(DirectParserASTContentClassMethodEnd node,
+ Token startInclusive, Token endInclusive) {
+ assert(currentContainer is Class);
+
+ String identifier;
+ try {
+ identifier = node.getNameIdentifier();
+ } catch (e) {
+ Container findTopLevel = currentContainer;
+ while (findTopLevel is! TopLevel) {
+ findTopLevel = findTopLevel.parent!;
+ }
+ String src = findTopLevel.sourceText
+ .substring(startInclusive.charOffset, endInclusive.charEnd);
+ throw "Unexpected identifiers in visitClassMethod --- "
+ "error on source ${src} --- "
+ "${node.children}";
+ }
+ ClassMethod classMethod =
+ new ClassMethod(node, identifier, startInclusive, endInclusive);
+ currentContainer.addChild(classMethod, map);
+ log("Hello from class method $identifier");
+ super.visitClassMethod(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitEnum(DirectParserASTContentEnumEnd node, Token startInclusive,
+ Token endInclusive) {
+ List<DirectParserASTContentIdentifierHandle> ids = node.getIdentifiers();
+
+ Enum e = new Enum(
+ node,
+ ids.first.token.lexeme,
+ ids.skip(1).map((e) => e.token.lexeme).toList(),
+ startInclusive,
+ endInclusive);
+ currentContainer.addChild(e, map);
+
+ log("Hello from enum ${ids.first.token} with content "
+ "${ids.skip(1).map((e) => e.token).join(", ")}");
+ super.visitEnum(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitExport(DirectParserASTContentExportEnd node, Token startInclusive,
+ Token endInclusive) {
+ String uriString = node.getExportUriString();
+ Uri exportUri = uri.resolve(uriString);
+ List<String>? conditionalUriStrings = node.getConditionalExportUriStrings();
+ List<Uri>? conditionalUris;
+ if (conditionalUriStrings != null) {
+ conditionalUris = [];
+ for (String conditionalUri in conditionalUriStrings) {
+ conditionalUris.add(uri.resolve(conditionalUri));
+ }
+ }
+ // TODO: Use 'show' and 'hide' stuff.
+ Export e = new Export(
+ node, exportUri, conditionalUris, startInclusive, endInclusive);
+ currentContainer.addChild(e, map);
+ log("Hello export");
+ }
+
+ @override
+ void visitExtension(DirectParserASTContentExtensionDeclarationEnd node,
+ Token startInclusive, Token endInclusive) {
+ DirectParserASTContentExtensionDeclarationBegin begin =
+ node.children!.first as DirectParserASTContentExtensionDeclarationBegin;
+ DirectParserASTContentTopLevelDeclarationEnd parent =
+ node.parent! as DirectParserASTContentTopLevelDeclarationEnd;
+ log("Hello from extension ${begin.name}");
+ Extension extension =
+ new Extension(parent, begin.name?.lexeme, startInclusive, endInclusive);
+ currentContainer.addChild(extension, map);
+
+ Container previousContainer = currentContainer;
+ currentContainer = extension;
+ super.visitExtension(node, startInclusive, endInclusive);
+ currentContainer = previousContainer;
+ }
+
+ @override
+ void visitExtensionConstructor(
+ DirectParserASTContentExtensionConstructorEnd node,
+ Token startInclusive,
+ Token endInclusive) {
+ // TODO: implement visitExtensionConstructor
+ throw node;
+ }
+
+ @override
+ void visitExtensionFactoryMethod(
+ DirectParserASTContentExtensionFactoryMethodEnd node,
+ Token startInclusive,
+ Token endInclusive) {
+ // TODO: implement visitExtensionFactoryMethod
+ throw node;
+ }
+
+ @override
+ void visitExtensionFields(DirectParserASTContentExtensionFieldsEnd node,
+ Token startInclusive, Token endInclusive) {
+ assert(currentContainer is Extension);
+ List<String> fields =
+ node.getFieldIdentifiers().map((e) => e.token.lexeme).toList();
+ ExtensionFields classFields =
+ new ExtensionFields(node, fields, startInclusive, endInclusive);
+ currentContainer.addChild(classFields, map);
+ log("Hello from extension fields ${fields.join(", ")}");
+ super.visitExtensionFields(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitExtensionMethod(DirectParserASTContentExtensionMethodEnd node,
+ Token startInclusive, Token endInclusive) {
+ assert(currentContainer is Extension);
+ ExtensionMethod extensionMethod = new ExtensionMethod(
+ node, node.getNameIdentifier(), startInclusive, endInclusive);
+ currentContainer.addChild(extensionMethod, map);
+ log("Hello from extension method ${node.getNameIdentifier()}");
+ super.visitExtensionMethod(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitImport(DirectParserASTContentImportEnd node, Token startInclusive,
+ Token? endInclusive) {
+ DirectParserASTContentIdentifierHandle? prefix = node.getImportPrefix();
+ String uriString = node.getImportUriString();
+ Uri importUri = uri.resolve(uriString);
+ List<String>? conditionalUriStrings = node.getConditionalImportUriStrings();
+ List<Uri>? conditionalUris;
+ if (conditionalUriStrings != null) {
+ conditionalUris = [];
+ for (String conditionalUri in conditionalUriStrings) {
+ conditionalUris.add(uri.resolve(conditionalUri));
+ }
+ }
+ // TODO: Use 'show' and 'hide' stuff.
+
+ // endInclusive can be null on syntax errors and there's recovery of the
+ // import. For now we'll ignore this.
+ Import i = new Import(node, importUri, conditionalUris,
+ prefix?.token.lexeme, startInclusive, endInclusive!);
+ currentContainer.addChild(i, map);
+ if (prefix == null) {
+ log("Hello import");
+ } else {
+ log("Hello import as '${prefix.token}'");
+ }
+ }
+
+ @override
+ void visitLibraryName(DirectParserASTContentLibraryNameEnd node,
+ Token startInclusive, Token endInclusive) {
+ LibraryName name = new LibraryName(node, startInclusive, endInclusive);
+ name.marked = Coloring.Marked;
+ currentContainer.addChild(name, map);
+ }
+
+ @override
+ void visitMetadata(DirectParserASTContentMetadataEnd node,
+ Token startInclusive, Token endInclusive) {
+ Metadata m = new Metadata(node, startInclusive, endInclusive);
+ currentContainer.addChild(m, map);
+ }
+
+ @override
+ void visitMixin(DirectParserASTContentMixinDeclarationEnd node,
+ Token startInclusive, Token endInclusive) {
+ DirectParserASTContentTopLevelDeclarationEnd parent =
+ node.parent! as DirectParserASTContentTopLevelDeclarationEnd;
+ DirectParserASTContentIdentifierHandle identifier = parent.getIdentifier();
+ log("Hello from mixin ${identifier.token}");
+
+ Mixin mixin = new Mixin(
+ parent, identifier.token.lexeme, startInclusive, endInclusive);
+ currentContainer.addChild(mixin, map);
+
+ Container previousContainer = currentContainer;
+ currentContainer = mixin;
+ super.visitMixin(node, startInclusive, endInclusive);
+ currentContainer = previousContainer;
+ }
+
+ @override
+ void visitMixinFields(DirectParserASTContentMixinFieldsEnd node,
+ Token startInclusive, Token endInclusive) {
+ assert(currentContainer is Mixin);
+ List<String> fields =
+ node.getFieldIdentifiers().map((e) => e.token.lexeme).toList();
+ MixinFields mixinFields =
+ new MixinFields(node, fields, startInclusive, endInclusive);
+ currentContainer.addChild(mixinFields, map);
+ log("Hello from mixin fields ${fields.join(", ")}");
+ super.visitMixinFields(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitMixinMethod(DirectParserASTContentMixinMethodEnd node,
+ Token startInclusive, Token endInclusive) {
+ assert(currentContainer is Mixin);
+ MixinMethod classMethod = new MixinMethod(
+ node, node.getNameIdentifier(), startInclusive, endInclusive);
+ currentContainer.addChild(classMethod, map);
+ log("Hello from mixin method ${node.getNameIdentifier()}");
+ super.visitMixinMethod(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitNamedMixin(DirectParserASTContentNamedMixinApplicationEnd node,
+ Token startInclusive, Token endInclusive) {
+ DirectParserASTContentTopLevelDeclarationEnd parent =
+ node.parent! as DirectParserASTContentTopLevelDeclarationEnd;
+ DirectParserASTContentIdentifierHandle identifier = parent.getIdentifier();
+ log("Hello from named mixin ${identifier.token}");
+
+ Mixin mixin = new Mixin(
+ parent, identifier.token.lexeme, startInclusive, endInclusive);
+ currentContainer.addChild(mixin, map);
+
+ Container previousContainer = currentContainer;
+ currentContainer = mixin;
+ super.visitNamedMixin(node, startInclusive, endInclusive);
+ currentContainer = previousContainer;
+ }
+
+ @override
+ void visitPart(DirectParserASTContentPartEnd node, Token startInclusive,
+ Token endInclusive) {
+ String uriString = node.getPartUriString();
+ Uri partUri = uri.resolve(uriString);
+
+ Part i = new Part(node, partUri, startInclusive, endInclusive);
+ currentContainer.addChild(i, map);
+ log("Hello part");
+ }
+
+ @override
+ void visitPartOf(DirectParserASTContentPartOfEnd node, Token startInclusive,
+ Token endInclusive) {
+ // We'll assume we've gotten here via a "part" so we'll ignore that for now.
+ // TODO: partOfUri could - in an error case - be null.
+ PartOf partof = new PartOf(node, partOfUri!, startInclusive, endInclusive);
+ partof.marked = Coloring.Marked;
+ currentContainer.addChild(partof, map);
+ }
+
+ @override
+ void visitTopLevelFields(DirectParserASTContentTopLevelFieldsEnd node,
+ Token startInclusive, Token endInclusive) {
+ List<String> fields =
+ node.getFieldIdentifiers().map((e) => e.token.lexeme).toList();
+ TopLevelFields f =
+ new TopLevelFields(node, fields, startInclusive, endInclusive);
+ currentContainer.addChild(f, map);
+ log("Hello from top level fields ${fields.join(", ")}");
+ super.visitTopLevelFields(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitTopLevelMethod(DirectParserASTContentTopLevelMethodEnd node,
+ Token startInclusive, Token endInclusive) {
+ TopLevelMethod m = new TopLevelMethod(node,
+ node.getNameIdentifier().token.lexeme, startInclusive, endInclusive);
+ currentContainer.addChild(m, map);
+ log("Hello from top level method ${node.getNameIdentifier().token}");
+ super.visitTopLevelMethod(node, startInclusive, endInclusive);
+ }
+
+ @override
+ void visitTypedef(DirectParserASTContentTypedefEnd node, Token startInclusive,
+ Token endInclusive) {
+ Typedef t = new Typedef(node, node.getNameIdentifier().token.lexeme,
+ startInclusive, endInclusive);
+ currentContainer.addChild(t, map);
+ log("Hello from typedef ${node.getNameIdentifier().token}");
+ super.visitTypedef(node, startInclusive, endInclusive);
+ }
+}
diff --git a/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/a.dart b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/a.dart
new file mode 100644
index 0000000..4e6a6de
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/a.dart
@@ -0,0 +1 @@
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/a2.dart b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/a2.dart
new file mode 100644
index 0000000..8c26988
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/a2.dart
@@ -0,0 +1 @@
+class Foo2 {}
diff --git a/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/b.dart b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/b.dart
new file mode 100644
index 0000000..4e6a6de
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/b.dart
@@ -0,0 +1 @@
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/b2.dart b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/b2.dart
new file mode 100644
index 0000000..8c26988
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/b2.dart
@@ -0,0 +1 @@
+class Foo2 {}
diff --git a/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/c.dart b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/c.dart
new file mode 100644
index 0000000..4e6a6de
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/c.dart
@@ -0,0 +1 @@
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/c2.dart b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/c2.dart
new file mode 100644
index 0000000..8c26988
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/c2.dart
@@ -0,0 +1 @@
+class Foo2 {}
diff --git a/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/main.dart b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/main.dart
new file mode 100644
index 0000000..bc74a47
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/main.dart
@@ -0,0 +1,8 @@
+import 'a.dart' if (dart.library.html) 'b.dart' if (dart.library.io) 'c.dart';
+export 'a2.dart'
+ if (dart.library.html) 'b2.dart'
+ if (dart.library.io) 'c2.dart';
+
+Foo x() {
+ return new Foo();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/main.dart.outline_extracted
new file mode 100644
index 0000000..a516b91
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/conditional_imports_exports/main.dart.outline_extracted
@@ -0,0 +1,40 @@
+org-dartlang-testcase:///main.dart:
+import 'a.dart' if (dart.library.html) 'b.dart' if (dart.library.io) 'c.dart';
+export 'a2.dart' if (dart.library.html) 'b2.dart' if (dart.library.io) 'c2.dart';
+Foo x() {}
+
+
+
+
+org-dartlang-testcase:///a2.dart:
+class Foo2 {}
+
+
+
+
+org-dartlang-testcase:///b2.dart:
+class Foo2 {}
+
+
+
+
+org-dartlang-testcase:///c2.dart:
+class Foo2 {}
+
+
+
+
+org-dartlang-testcase:///a.dart:
+class Foo {}
+
+
+
+
+org-dartlang-testcase:///b.dart:
+class Foo {}
+
+
+
+
+org-dartlang-testcase:///c.dart:
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/exports_export_01/main.dart b/pkg/front_end/outline_extraction_testcases/exports_export_01/main.dart
new file mode 100644
index 0000000..997da89
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/exports_export_01/main.dart
@@ -0,0 +1,3 @@
+import "test8.dart";
+
+ClassFromImportsExportsExport? zyx____xyz;
diff --git a/pkg/front_end/outline_extraction_testcases/exports_export_01/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/exports_export_01/main.dart.outline_extracted
new file mode 100644
index 0000000..1f5cc58
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/exports_export_01/main.dart.outline_extracted
@@ -0,0 +1,22 @@
+org-dartlang-testcase:///main.dart:
+import "test8.dart";
+ClassFromImportsExportsExport? zyx____xyz;
+
+
+
+
+org-dartlang-testcase:///test8.dart:
+export "test9.dart";
+
+
+
+
+org-dartlang-testcase:///test9.dart:
+export "test10.dart";
+
+
+
+
+org-dartlang-testcase:///test10.dart:
+export "dart:async";
+class ClassFromImportsExportsExport {}
diff --git a/pkg/front_end/outline_extraction_testcases/exports_export_01/test10.dart b/pkg/front_end/outline_extraction_testcases/exports_export_01/test10.dart
new file mode 100644
index 0000000..8513138
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/exports_export_01/test10.dart
@@ -0,0 +1,5 @@
+export "dart:async";
+
+void test10Method() {}
+
+class ClassFromImportsExportsExport {}
diff --git a/pkg/front_end/outline_extraction_testcases/exports_export_01/test8.dart b/pkg/front_end/outline_extraction_testcases/exports_export_01/test8.dart
new file mode 100644
index 0000000..c83a90a
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/exports_export_01/test8.dart
@@ -0,0 +1,3 @@
+export "test9.dart";
+
+void test8Method() {}
diff --git a/pkg/front_end/outline_extraction_testcases/exports_export_01/test9.dart b/pkg/front_end/outline_extraction_testcases/exports_export_01/test9.dart
new file mode 100644
index 0000000..d0fcb31
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/exports_export_01/test9.dart
@@ -0,0 +1,3 @@
+export "test10.dart";
+
+void test9Method() {}
diff --git a/pkg/front_end/outline_extraction_testcases/exports_included/main.dart b/pkg/front_end/outline_extraction_testcases/exports_included/main.dart
new file mode 100644
index 0000000..52091d7
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/exports_included/main.dart
@@ -0,0 +1 @@
+export "test6.dart";
diff --git a/pkg/front_end/outline_extraction_testcases/exports_included/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/exports_included/main.dart.outline_extracted
new file mode 100644
index 0000000..c2b824d
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/exports_included/main.dart.outline_extracted
@@ -0,0 +1,15 @@
+org-dartlang-testcase:///main.dart:
+export "test6.dart";
+
+
+
+
+org-dartlang-testcase:///test6.dart:
+export "test7.dart";
+void test6() {}
+
+
+
+
+org-dartlang-testcase:///test7.dart:
+void test7() {}
diff --git a/pkg/front_end/outline_extraction_testcases/exports_included/test6.dart b/pkg/front_end/outline_extraction_testcases/exports_included/test6.dart
new file mode 100644
index 0000000..c130634
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/exports_included/test6.dart
@@ -0,0 +1,3 @@
+export "test7.dart";
+
+void test6() {}
diff --git a/pkg/front_end/outline_extraction_testcases/exports_included/test7.dart b/pkg/front_end/outline_extraction_testcases/exports_included/test7.dart
new file mode 100644
index 0000000..5b3e78d
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/exports_included/test7.dart
@@ -0,0 +1 @@
+void test7() {}
diff --git a/pkg/front_end/outline_extraction_testcases/extends/foo.dart b/pkg/front_end/outline_extraction_testcases/extends/foo.dart
new file mode 100644
index 0000000..4e6a6de
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/extends/foo.dart
@@ -0,0 +1 @@
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/extends/main.dart b/pkg/front_end/outline_extraction_testcases/extends/main.dart
new file mode 100644
index 0000000..a126b2a
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/extends/main.dart
@@ -0,0 +1,3 @@
+import "foo.dart";
+
+class X extends Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/extends/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/extends/main.dart.outline_extracted
new file mode 100644
index 0000000..09b7413
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/extends/main.dart.outline_extracted
@@ -0,0 +1,9 @@
+org-dartlang-testcase:///main.dart:
+import "foo.dart";
+class X extends Foo {}
+
+
+
+
+org-dartlang-testcase:///foo.dart:
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/factories/main.dart b/pkg/front_end/outline_extraction_testcases/factories/main.dart
new file mode 100644
index 0000000..316b9c5
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/factories/main.dart
@@ -0,0 +1,12 @@
+import 'package:foo/test11.dart';
+
+class Abc {
+ Abc() {}
+ factory Abc.a() {
+ return Abc2();
+ }
+ // Abc3 currently gets in --- it doesn't have to.
+ factory Abc.b() => Abc3();
+ var v1 = Abc4();
+ var v2 = new Abc5();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/factories/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/factories/main.dart.outline_extracted
new file mode 100644
index 0000000..4127b46
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/factories/main.dart.outline_extracted
@@ -0,0 +1,24 @@
+org-dartlang-testcase:///main.dart:
+import 'package:foo/test11.dart';
+class Abc {
+ Abc() {}
+ factory Abc.a() {}
+ factory Abc.b() => Abc3();
+ var v1 = Abc4();
+ var v2 = new Abc5();
+}
+
+
+
+
+org-dartlang-testcase:///test11.dart:
+import 'package:foo/main.dart';
+class Abc3 extends Abc {
+ Abc3() {}
+}
+class Abc4 extends Abc {
+ Abc4() {}
+}
+class Abc5 extends Abc {
+ Abc5() {}
+}
diff --git a/pkg/front_end/outline_extraction_testcases/factories/test11.dart b/pkg/front_end/outline_extraction_testcases/factories/test11.dart
new file mode 100644
index 0000000..105387c
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/factories/test11.dart
@@ -0,0 +1,21 @@
+import 'package:foo/main.dart';
+
+class Abc2 extends Abc {
+ Abc2() {}
+}
+
+class Abc3 extends Abc {
+ Abc3() {}
+}
+
+class Abc4 extends Abc {
+ Abc4() {}
+}
+
+class Abc5 extends Abc {
+ Abc5() {}
+}
+
+class Abc6 extends Abc {
+ Abc6() {}
+}
diff --git a/pkg/front_end/outline_extraction_testcases/field_dotting_in/b.dart b/pkg/front_end/outline_extraction_testcases/field_dotting_in/b.dart
new file mode 100644
index 0000000..112fdbc
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/field_dotting_in/b.dart
@@ -0,0 +1,9 @@
+import "c.dart";
+
+class B {
+ C c() {
+ return new C();
+ }
+}
+
+class BPrime {}
diff --git a/pkg/front_end/outline_extraction_testcases/field_dotting_in/c.dart b/pkg/front_end/outline_extraction_testcases/field_dotting_in/c.dart
new file mode 100644
index 0000000..356bd56
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/field_dotting_in/c.dart
@@ -0,0 +1,9 @@
+import "d.dart";
+
+class C {
+ D d() {
+ return new D();
+ }
+}
+
+class CPrime {}
diff --git a/pkg/front_end/outline_extraction_testcases/field_dotting_in/d.dart b/pkg/front_end/outline_extraction_testcases/field_dotting_in/d.dart
new file mode 100644
index 0000000..8655cd4
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/field_dotting_in/d.dart
@@ -0,0 +1,7 @@
+class D {
+ String d() {
+ return "hello";
+ }
+}
+
+class DPrime {}
diff --git a/pkg/front_end/outline_extraction_testcases/field_dotting_in/main.dart b/pkg/front_end/outline_extraction_testcases/field_dotting_in/main.dart
new file mode 100644
index 0000000..3d53be6
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/field_dotting_in/main.dart
@@ -0,0 +1,9 @@
+import "b.dart";
+
+var x = A.b().c().d;
+
+class A {
+ static B b() {
+ return new B();
+ }
+}
diff --git a/pkg/front_end/outline_extraction_testcases/field_dotting_in/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/field_dotting_in/main.dart.outline_extracted
new file mode 100644
index 0000000..e9a8f51
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/field_dotting_in/main.dart.outline_extracted
@@ -0,0 +1,32 @@
+org-dartlang-testcase:///main.dart:
+import "b.dart";
+var x = A.b().c().d;
+class A {
+ static B b() {}
+}
+
+
+
+
+org-dartlang-testcase:///b.dart:
+import "c.dart";
+class B {
+ C c() {}
+}
+
+
+
+
+org-dartlang-testcase:///c.dart:
+import "d.dart";
+class C {
+ D d() {}
+}
+
+
+
+
+org-dartlang-testcase:///d.dart:
+class D {
+ String d() {}
+}
diff --git a/pkg/front_end/outline_extraction_testcases/fields/bar.dart b/pkg/front_end/outline_extraction_testcases/fields/bar.dart
new file mode 100644
index 0000000..79f980a
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/fields/bar.dart
@@ -0,0 +1,11 @@
+class Bar {}
+
+class Bar2 {}
+
+class Bar3 {}
+
+class Bar4 {}
+
+class Foo3 {}
+
+class Foo4 {}
diff --git a/pkg/front_end/outline_extraction_testcases/fields/foo.dart b/pkg/front_end/outline_extraction_testcases/fields/foo.dart
new file mode 100644
index 0000000..43434c3
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/fields/foo.dart
@@ -0,0 +1,13 @@
+export "bar.dart";
+
+class Baz {}
+
+class Baz2 {}
+
+class Baz3 {}
+
+class Baz4 {}
+
+class Foo {}
+
+class Foo2 {}
diff --git a/pkg/front_end/outline_extraction_testcases/fields/main.dart b/pkg/front_end/outline_extraction_testcases/fields/main.dart
new file mode 100644
index 0000000..bf52fd9
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/fields/main.dart
@@ -0,0 +1,19 @@
+import "foo.dart";
+
+class A {
+ Bar field = new Bar();
+ Bar2 field2 = new Bar2();
+ var field3 = new Bar3(), field4 = new Bar4();
+}
+
+mixin A2 {
+ Baz field = new Baz();
+ Baz2 field2 = new Baz2();
+ var field3 = new Baz3(), field4 = new Baz4();
+}
+
+extension A3 on Object {
+ static Foo field = new Foo();
+ static Foo2 field2 = new Foo2();
+ static var field3 = new Foo3(), field4 = new Foo4();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/fields/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/fields/main.dart.outline_extracted
new file mode 100644
index 0000000..13e239a
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/fields/main.dart.outline_extracted
@@ -0,0 +1,40 @@
+org-dartlang-testcase:///main.dart:
+import "foo.dart";
+class A {
+ Bar field = new Bar();
+ Bar2 field2 = new Bar2();
+ var field3 = new Bar3(), field4 = new Bar4();
+}
+mixin A2 {
+ Baz field = new Baz();
+ Baz2 field2 = new Baz2();
+ var field3 = new Baz3(), field4 = new Baz4();
+}
+extension A3 on Object {
+ static Foo field = new Foo();
+ static Foo2 field2 = new Foo2();
+ static var field3 = new Foo3(), field4 = new Foo4();
+}
+
+
+
+
+org-dartlang-testcase:///foo.dart:
+export "bar.dart";
+class Baz {}
+class Baz2 {}
+class Baz3 {}
+class Baz4 {}
+class Foo {}
+class Foo2 {}
+
+
+
+
+org-dartlang-testcase:///bar.dart:
+class Bar {}
+class Bar2 {}
+class Bar3 {}
+class Bar4 {}
+class Foo3 {}
+class Foo4 {}
diff --git a/pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/main.dart b/pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/main.dart
new file mode 100644
index 0000000..3cfbb90
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/main.dart
@@ -0,0 +1,6 @@
+import "test16.dart" as test16;
+
+class Test16ClassHelper {
+ // the naming matching is what makes it annoying!
+ final test16toplevel = test16.test16toplevel("hello");
+}
diff --git a/pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/main.dart.outline_extracted
new file mode 100644
index 0000000..e0a42eb
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/main.dart.outline_extracted
@@ -0,0 +1,11 @@
+org-dartlang-testcase:///main.dart:
+import "test16.dart" as test16;
+class Test16ClassHelper {
+ final test16toplevel = test16.test16toplevel("hello");
+}
+
+
+
+
+org-dartlang-testcase:///test16.dart:
+String test16toplevel(String s) {}
diff --git a/pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/test16.dart b/pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/test16.dart
new file mode 100644
index 0000000..41d882e
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/test16.dart
@@ -0,0 +1,3 @@
+String test16toplevel(String s) {
+ return s * 2;
+}
diff --git a/pkg/front_end/outline_extraction_testcases/import_with_prefix/bar.dart b/pkg/front_end/outline_extraction_testcases/import_with_prefix/bar.dart
new file mode 100644
index 0000000..91b2144
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_with_prefix/bar.dart
@@ -0,0 +1,5 @@
+int bar() {
+ return 42;
+}
+
+class Baz {}
diff --git a/pkg/front_end/outline_extraction_testcases/import_with_prefix/foo.dart b/pkg/front_end/outline_extraction_testcases/import_with_prefix/foo.dart
new file mode 100644
index 0000000..0ec48dd
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_with_prefix/foo.dart
@@ -0,0 +1,5 @@
+String bar() {
+ return "hello";
+}
+
+class Baz {}
diff --git a/pkg/front_end/outline_extraction_testcases/import_with_prefix/main.dart b/pkg/front_end/outline_extraction_testcases/import_with_prefix/main.dart
new file mode 100644
index 0000000..aef942d
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_with_prefix/main.dart
@@ -0,0 +1,7 @@
+import 'foo.dart' as foo;
+// This import isn't used --- foo.bar below explicitly wants bar from foo.
+import 'bar.dart';
+
+var x = foo.bar();
+
+foo.Baz? baz;
diff --git a/pkg/front_end/outline_extraction_testcases/import_with_prefix/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/import_with_prefix/main.dart.outline_extracted
new file mode 100644
index 0000000..1c804cb
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_with_prefix/main.dart.outline_extracted
@@ -0,0 +1,17 @@
+org-dartlang-testcase:///main.dart:
+import 'foo.dart' as foo;
+import 'bar.dart';
+var x = foo.bar();
+foo.Baz? baz;
+
+
+
+
+org-dartlang-testcase:///foo.dart:
+String bar() {}
+class Baz {}
+
+
+
+
+org-dartlang-testcase:///bar.dart:
diff --git a/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/a.dart b/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/a.dart
new file mode 100644
index 0000000..ff840ec
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/a.dart
@@ -0,0 +1,3 @@
+String foo() {
+ return "foo";
+}
diff --git a/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/b.dart b/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/b.dart
new file mode 100644
index 0000000..07f55de
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/b.dart
@@ -0,0 +1,3 @@
+String bar() {
+ return "bar";
+}
diff --git a/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/main.dart b/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/main.dart
new file mode 100644
index 0000000..73d125a
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/main.dart
@@ -0,0 +1,5 @@
+import 'a.dart' as x;
+import 'b.dart' as x;
+
+var foo = x.foo();
+var bar = x.bar();
diff --git a/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/main.dart.outline_extracted
new file mode 100644
index 0000000..166189c
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/import_with_prefix_02/main.dart.outline_extracted
@@ -0,0 +1,17 @@
+org-dartlang-testcase:///main.dart:
+import 'a.dart' as x;
+import 'b.dart' as x;
+var foo = x.foo();
+var bar = x.bar();
+
+
+
+
+org-dartlang-testcase:///a.dart:
+String foo() {}
+
+
+
+
+org-dartlang-testcase:///b.dart:
+String bar() {}
diff --git a/pkg/front_end/outline_extraction_testcases/initial_various/main.dart b/pkg/front_end/outline_extraction_testcases/initial_various/main.dart
new file mode 100644
index 0000000..2ff0638
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/initial_various/main.dart
@@ -0,0 +1,28 @@
+import "main.dart" as self;
+import "test3.dart";
+
+class Foo<E> extends Bar<int> with Qux1<int> implements Baz<Bar<int>> {
+ Foo<E>? parent;
+
+ Foo() {}
+ Foo.bar() {}
+ F? fooMethod1<F>() {
+ print(foo);
+ print(F);
+ print(x.A);
+ }
+
+ E? fooMethod2() {
+ print(E);
+ print(x.A);
+ }
+
+ self.Foo? fooMethod3() {
+ print(E);
+ print(x.A);
+ }
+
+ x fooMethod4() {
+ return x.A;
+ }
+}
diff --git a/pkg/front_end/outline_extraction_testcases/initial_various/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/initial_various/main.dart.outline_extracted
new file mode 100644
index 0000000..3426756
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/initial_various/main.dart.outline_extracted
@@ -0,0 +1,24 @@
+org-dartlang-testcase:///main.dart:
+import "main.dart" as self;
+import "test3.dart";
+class Foo<E> extends Bar<int> with Qux1<int> implements Baz<Bar<int>> {
+ Foo<E>? parent;
+ Foo() {}
+ Foo.bar() {}
+ F? fooMethod1<F>() {}
+ E? fooMethod2() {}
+ self.Foo? fooMethod3() {}
+ x fooMethod4() {}
+}
+
+
+
+
+org-dartlang-testcase:///test3.dart:
+class Bar<E> {}
+class Baz<E> {}
+class Qux1<E> {
+ Qux1AndAHalf? qux1AndAHalf() {}
+}
+class Qux1AndAHalf<E> {}
+enum x { A, B, C }
diff --git a/pkg/front_end/outline_extraction_testcases/initial_various/test3.dart b/pkg/front_end/outline_extraction_testcases/initial_various/test3.dart
new file mode 100644
index 0000000..530644b
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/initial_various/test3.dart
@@ -0,0 +1,25 @@
+import "test4.dart";
+
+class Bar<E> {}
+
+class Baz<E> {}
+
+class Qux1<E> {
+ Qux1AndAHalf? qux1AndAHalf() {
+ // nothing...
+ }
+}
+
+class Qux1AndAHalf<E> {}
+
+class Qux2<E> {
+ Qux3? foo() {}
+}
+
+enum x { A, B, C }
+
+int foo() {
+ return 42;
+}
+
+int foo2 = foo() * 2, foo3 = foo() * 3;
diff --git a/pkg/front_end/outline_extraction_testcases/initial_various/test4.dart b/pkg/front_end/outline_extraction_testcases/initial_various/test4.dart
new file mode 100644
index 0000000..3215ebf
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/initial_various/test4.dart
@@ -0,0 +1,8 @@
+import "test5.dart";
+export "test5.dart";
+
+class Qux3<E> {
+ Qux4? foo() {}
+}
+
+class Qux4<E> {}
diff --git a/pkg/front_end/outline_extraction_testcases/initial_various/test5.dart b/pkg/front_end/outline_extraction_testcases/initial_various/test5.dart
new file mode 100644
index 0000000..0315739
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/initial_various/test5.dart
@@ -0,0 +1,5 @@
+class Qux3x<E> {
+ Qux4x? foo() {}
+}
+
+class Qux4x<E> {}
diff --git a/pkg/front_end/outline_extraction_testcases/keeps_dart_version/bar.dart b/pkg/front_end/outline_extraction_testcases/keeps_dart_version/bar.dart
new file mode 100644
index 0000000..62e4db2
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/keeps_dart_version/bar.dart
@@ -0,0 +1,5 @@
+// @dart = 2.12
+
+class Bar {}
+
+class Baz {}
diff --git a/pkg/front_end/outline_extraction_testcases/keeps_dart_version/main.dart b/pkg/front_end/outline_extraction_testcases/keeps_dart_version/main.dart
new file mode 100644
index 0000000..f20e3af
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/keeps_dart_version/main.dart
@@ -0,0 +1,3 @@
+import "bar.dart";
+
+void foo(Bar bar) {}
diff --git a/pkg/front_end/outline_extraction_testcases/keeps_dart_version/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/keeps_dart_version/main.dart.outline_extracted
new file mode 100644
index 0000000..9a0da03
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/keeps_dart_version/main.dart.outline_extracted
@@ -0,0 +1,10 @@
+org-dartlang-testcase:///main.dart:
+import "bar.dart";
+void foo(Bar bar) {}
+
+
+
+
+org-dartlang-testcase:///bar.dart:
+// @dart = 2.12
+class Bar {}
diff --git a/pkg/front_end/outline_extraction_testcases/metadata_01/a.dart b/pkg/front_end/outline_extraction_testcases/metadata_01/a.dart
new file mode 100644
index 0000000..e7267c5
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/metadata_01/a.dart
@@ -0,0 +1 @@
+class AUnused {}
diff --git a/pkg/front_end/outline_extraction_testcases/metadata_01/b.dart b/pkg/front_end/outline_extraction_testcases/metadata_01/b.dart
new file mode 100644
index 0000000..8b42bda
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/metadata_01/b.dart
@@ -0,0 +1,9 @@
+const AbcX = const _AbcX();
+
+class _AbcX {
+ const _AbcX();
+}
+
+class AbcX2 {
+ const AbcX2();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/metadata_01/main.dart b/pkg/front_end/outline_extraction_testcases/metadata_01/main.dart
new file mode 100644
index 0000000..a55d3df
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/metadata_01/main.dart
@@ -0,0 +1,5 @@
+import "a.dart";
+import "b.dart";
+
+@AbcX
+void foo() {}
diff --git a/pkg/front_end/outline_extraction_testcases/metadata_01/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/metadata_01/main.dart.outline_extracted
new file mode 100644
index 0000000..6d8ae63
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/metadata_01/main.dart.outline_extracted
@@ -0,0 +1,19 @@
+org-dartlang-testcase:///main.dart:
+import "a.dart";
+import "b.dart";
+@AbcX
+void foo() {}
+
+
+
+
+org-dartlang-testcase:///a.dart:
+
+
+
+
+org-dartlang-testcase:///b.dart:
+const AbcX = const _AbcX();
+class _AbcX {
+ const _AbcX();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/metadata_02/main.dart b/pkg/front_end/outline_extraction_testcases/metadata_02/main.dart
new file mode 100644
index 0000000..851e231
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/metadata_02/main.dart
@@ -0,0 +1,5 @@
+import "test15.dart" as $test15;
+import "nottest15.dart";
+
+@$test15.Test15()
+void test15thing() {}
diff --git a/pkg/front_end/outline_extraction_testcases/metadata_02/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/metadata_02/main.dart.outline_extracted
new file mode 100644
index 0000000..123667f
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/metadata_02/main.dart.outline_extracted
@@ -0,0 +1,18 @@
+org-dartlang-testcase:///main.dart:
+import "test15.dart" as $test15;
+import "nottest15.dart";
+@$test15.Test15()
+void test15thing() {}
+
+
+
+
+org-dartlang-testcase:///test15.dart:
+class Test15 {
+ const Test15();
+}
+
+
+
+
+org-dartlang-testcase:///nottest15.dart:
diff --git a/pkg/front_end/outline_extraction_testcases/metadata_02/nottest15.dart b/pkg/front_end/outline_extraction_testcases/metadata_02/nottest15.dart
new file mode 100644
index 0000000..41594d9
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/metadata_02/nottest15.dart
@@ -0,0 +1,7 @@
+class Test15 {
+ const Test15();
+}
+
+class Test16 {
+ const Test16();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/metadata_02/test15.dart b/pkg/front_end/outline_extraction_testcases/metadata_02/test15.dart
new file mode 100644
index 0000000..41594d9
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/metadata_02/test15.dart
@@ -0,0 +1,7 @@
+class Test15 {
+ const Test15();
+}
+
+class Test16 {
+ const Test16();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/a.dart b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/a.dart
new file mode 100644
index 0000000..bde7250
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/a.dart
@@ -0,0 +1,5 @@
+library foo.a;
+
+export 'b.dart';
+export 'c.dart';
+export 'd.dart';
diff --git a/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/b.dart b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/b.dart
new file mode 100644
index 0000000..3a43d09
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/b.dart
@@ -0,0 +1 @@
+class B {}
diff --git a/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/c.dart b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/c.dart
new file mode 100644
index 0000000..ed37d13
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/c.dart
@@ -0,0 +1,7 @@
+class C<T> {
+ const C.b();
+}
+
+class C2<T> {
+ const C2.b();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/d.dart b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/d.dart
new file mode 100644
index 0000000..40416ad
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/d.dart
@@ -0,0 +1 @@
+class D {}
diff --git a/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/main.dart b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/main.dart
new file mode 100644
index 0000000..afbc02d
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/main.dart
@@ -0,0 +1,6 @@
+import 'package:foo/a.dart' as foo;
+
+class A {
+ var c1 = foo.C<A>.b();
+ var c2 = new foo.C2<A>.b();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/main.dart.outline_extracted
new file mode 100644
index 0000000..c1a663e
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/main.dart.outline_extracted
@@ -0,0 +1,36 @@
+org-dartlang-testcase:///main.dart:
+import 'package:foo/a.dart' as foo;
+class A {
+ var c1 = foo.C<A>.b();
+ var c2 = new foo.C2<A>.b();
+}
+
+
+
+
+org-dartlang-testcase:///a.dart:
+library foo.a;
+export 'b.dart';
+export 'c.dart';
+export 'd.dart';
+
+
+
+
+org-dartlang-testcase:///b.dart:
+
+
+
+
+org-dartlang-testcase:///c.dart:
+class C<T> {
+ const C.b();
+}
+class C2<T> {
+ const C2.b();
+}
+
+
+
+
+org-dartlang-testcase:///d.dart:
diff --git a/pkg/front_end/outline_extraction_testcases/named_mixin/bar.dart b/pkg/front_end/outline_extraction_testcases/named_mixin/bar.dart
new file mode 100644
index 0000000..512eabb
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_mixin/bar.dart
@@ -0,0 +1,3 @@
+export "baz.dart";
+
+class Bar {}
diff --git a/pkg/front_end/outline_extraction_testcases/named_mixin/baz.dart b/pkg/front_end/outline_extraction_testcases/named_mixin/baz.dart
new file mode 100644
index 0000000..f1e00d1
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_mixin/baz.dart
@@ -0,0 +1 @@
+class Baz {}
diff --git a/pkg/front_end/outline_extraction_testcases/named_mixin/main.dart b/pkg/front_end/outline_extraction_testcases/named_mixin/main.dart
new file mode 100644
index 0000000..4e0e374
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_mixin/main.dart
@@ -0,0 +1,3 @@
+import "bar.dart";
+
+class Foo = Object with Bar implements Baz;
diff --git a/pkg/front_end/outline_extraction_testcases/named_mixin/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/named_mixin/main.dart.outline_extracted
new file mode 100644
index 0000000..5ff64b3
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/named_mixin/main.dart.outline_extracted
@@ -0,0 +1,16 @@
+org-dartlang-testcase:///main.dart:
+import "bar.dart";
+class Foo = Object with Bar implements Baz;
+
+
+
+
+org-dartlang-testcase:///bar.dart:
+export "baz.dart";
+class Bar {}
+
+
+
+
+org-dartlang-testcase:///baz.dart:
+class Baz {}
diff --git a/pkg/front_end/outline_extraction_testcases/outline_extractor.status b/pkg/front_end/outline_extraction_testcases/outline_extractor.status
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/outline_extractor.status
diff --git a/pkg/front_end/outline_extraction_testcases/part_01/main.dart b/pkg/front_end/outline_extraction_testcases/part_01/main.dart
new file mode 100644
index 0000000..1d7ff4e
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_01/main.dart
@@ -0,0 +1,4 @@
+import "test12.dart";
+
+void test12part1usage(Test12Part1 x) {}
+void secondtest12part1usage(SecondTest12 x) {}
diff --git a/pkg/front_end/outline_extraction_testcases/part_01/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/part_01/main.dart.outline_extracted
new file mode 100644
index 0000000..ee384f7
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_01/main.dart.outline_extracted
@@ -0,0 +1,47 @@
+org-dartlang-testcase:///main.dart:
+import "test12.dart";
+void test12part1usage(Test12Part1 x) {}
+void secondtest12part1usage(SecondTest12 x) {}
+
+
+
+
+org-dartlang-testcase:///test12.dart:
+import "test13.dart";
+import "test14.dart";
+part 'test12_part1.dart';
+part 'test12_part2.dart';
+class Test12 {}
+class SecondTest12 {
+ void foo(SecondTest12Part1 x) {}
+}
+
+
+
+
+org-dartlang-testcase:///test12_part1.dart:
+part of "test12.dart";
+class Test12Part1 {
+ void foo(Test12 x) {}
+ void bar(Test12Part2 x) {}
+ void baz(Test13 x) {}
+}
+class SecondTest12Part1 {}
+
+
+
+
+org-dartlang-testcase:///test12_part2.dart:
+part of "test12.dart";
+class Test12Part2 {}
+
+
+
+
+org-dartlang-testcase:///test13.dart:
+class Test13 {}
+
+
+
+
+org-dartlang-testcase:///test14.dart:
diff --git a/pkg/front_end/outline_extraction_testcases/part_01/test12.dart b/pkg/front_end/outline_extraction_testcases/part_01/test12.dart
new file mode 100644
index 0000000..07c621e
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_01/test12.dart
@@ -0,0 +1,11 @@
+import "test13.dart";
+import "test14.dart";
+
+part 'test12_part1.dart';
+part 'test12_part2.dart';
+
+class Test12 {}
+
+class SecondTest12 {
+ void foo(SecondTest12Part1 x) {}
+}
diff --git a/pkg/front_end/outline_extraction_testcases/part_01/test12_part1.dart b/pkg/front_end/outline_extraction_testcases/part_01/test12_part1.dart
new file mode 100644
index 0000000..44473ff
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_01/test12_part1.dart
@@ -0,0 +1,9 @@
+part of "test12.dart";
+
+class Test12Part1 {
+ void foo(Test12 x) {}
+ void bar(Test12Part2 x) {}
+ void baz(Test13 x) {}
+}
+
+class SecondTest12Part1 {}
diff --git a/pkg/front_end/outline_extraction_testcases/part_01/test12_part2.dart b/pkg/front_end/outline_extraction_testcases/part_01/test12_part2.dart
new file mode 100644
index 0000000..413d50d
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_01/test12_part2.dart
@@ -0,0 +1,3 @@
+part of "test12.dart";
+
+class Test12Part2 {}
diff --git a/pkg/front_end/outline_extraction_testcases/part_01/test13.dart b/pkg/front_end/outline_extraction_testcases/part_01/test13.dart
new file mode 100644
index 0000000..caa3ed4
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_01/test13.dart
@@ -0,0 +1,3 @@
+import "test13andahalf.dart";
+
+class Test13 {}
diff --git a/pkg/front_end/outline_extraction_testcases/part_01/test13andahalf.dart b/pkg/front_end/outline_extraction_testcases/part_01/test13andahalf.dart
new file mode 100644
index 0000000..16bc96b
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_01/test13andahalf.dart
@@ -0,0 +1 @@
+class Test13AndAHalf {}
diff --git a/pkg/front_end/outline_extraction_testcases/part_01/test14.dart b/pkg/front_end/outline_extraction_testcases/part_01/test14.dart
new file mode 100644
index 0000000..023fefd
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_01/test14.dart
@@ -0,0 +1 @@
+class Test14 {}
diff --git a/pkg/front_end/outline_extraction_testcases/part_and_library_name/main.dart b/pkg/front_end/outline_extraction_testcases/part_and_library_name/main.dart
new file mode 100644
index 0000000..d9163f5
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_and_library_name/main.dart
@@ -0,0 +1,3 @@
+import 'test3.dart';
+
+var x = test3partfoo();
diff --git a/pkg/front_end/outline_extraction_testcases/part_and_library_name/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/part_and_library_name/main.dart.outline_extracted
new file mode 100644
index 0000000..2d108c7
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_and_library_name/main.dart.outline_extracted
@@ -0,0 +1,17 @@
+org-dartlang-testcase:///main.dart:
+import 'test3.dart';
+var x = test3partfoo();
+
+
+
+
+org-dartlang-testcase:///test3.dart:
+library test3;
+part "test3_part.dart";
+
+
+
+
+org-dartlang-testcase:///test3_part.dart:
+part of test3;
+void test3partfoo() {}
diff --git a/pkg/front_end/outline_extraction_testcases/part_and_library_name/test3.dart b/pkg/front_end/outline_extraction_testcases/part_and_library_name/test3.dart
new file mode 100644
index 0000000..8fdefdd
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_and_library_name/test3.dart
@@ -0,0 +1,5 @@
+library test3;
+
+part "test3_part.dart";
+
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/part_and_library_name/test3_part.dart b/pkg/front_end/outline_extraction_testcases/part_and_library_name/test3_part.dart
new file mode 100644
index 0000000..e6e3fc4
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/part_and_library_name/test3_part.dart
@@ -0,0 +1,5 @@
+part of test3;
+
+void test3partfoo() {}
+
+class Bar {}
diff --git a/pkg/front_end/outline_extraction_testcases/split_import_export_part/foo.dart b/pkg/front_end/outline_extraction_testcases/split_import_export_part/foo.dart
new file mode 100644
index 0000000..4e6a6de
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/split_import_export_part/foo.dart
@@ -0,0 +1 @@
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/split_import_export_part/main.dart b/pkg/front_end/outline_extraction_testcases/split_import_export_part/main.dart
new file mode 100644
index 0000000..85d6722
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/split_import_export_part/main.dart
@@ -0,0 +1,25 @@
+import "package:f"
+ "oobar"
+ "/"
+ "foo"
+ ".dart";
+
+export "package:f"
+ "oobar"
+ "/"
+ "foo"
+ ".dart";
+
+part "package:f"
+ "oobar"
+ "/"
+ "part"
+ ".dart";
+
+Foo giveFoo() {
+ return new Foo();
+}
+
+Bar giveBar() {
+ return new Bar();
+}
diff --git a/pkg/front_end/outline_extraction_testcases/split_import_export_part/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/split_import_export_part/main.dart.outline_extracted
new file mode 100644
index 0000000..ed23c5a
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/split_import_export_part/main.dart.outline_extracted
@@ -0,0 +1,19 @@
+org-dartlang-testcase:///main.dart:
+import "package:f" "oobar" "/" "foo" ".dart";
+export "package:f" "oobar" "/" "foo" ".dart";
+part "package:f" "oobar" "/" "part" ".dart";
+Foo giveFoo() {}
+Bar giveBar() {}
+
+
+
+
+org-dartlang-testcase:///foo.dart:
+class Foo {}
+
+
+
+
+org-dartlang-testcase:///part.dart:
+part of "package:f" "oobar" "/" "main" ".dart";
+class Bar {}
diff --git a/pkg/front_end/outline_extraction_testcases/split_import_export_part/part.dart b/pkg/front_end/outline_extraction_testcases/split_import_export_part/part.dart
new file mode 100644
index 0000000..009eb8e
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/split_import_export_part/part.dart
@@ -0,0 +1,7 @@
+part of "package:f"
+ "oobar"
+ "/"
+ "main"
+ ".dart";
+
+class Bar {}
diff --git a/pkg/front_end/outline_extraction_testcases/type_parameter_extends/bar.dart b/pkg/front_end/outline_extraction_testcases/type_parameter_extends/bar.dart
new file mode 100644
index 0000000..3507a7f
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/type_parameter_extends/bar.dart
@@ -0,0 +1,5 @@
+class Bar {}
+
+class Bar2 {}
+
+class Bar3 {}
diff --git a/pkg/front_end/outline_extraction_testcases/type_parameter_extends/foo.dart b/pkg/front_end/outline_extraction_testcases/type_parameter_extends/foo.dart
new file mode 100644
index 0000000..820b17e
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/type_parameter_extends/foo.dart
@@ -0,0 +1,7 @@
+export "bar.dart";
+
+class Baz {}
+
+class Baz2 {}
+
+class Baz3 {}
diff --git a/pkg/front_end/outline_extraction_testcases/type_parameter_extends/main.dart b/pkg/front_end/outline_extraction_testcases/type_parameter_extends/main.dart
new file mode 100644
index 0000000..73b5565
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/type_parameter_extends/main.dart
@@ -0,0 +1,16 @@
+import "foo.dart";
+
+class A<T extends Bar, U extends Baz> {
+ T? aMethod1() {}
+ U? aMethod2() {}
+}
+
+mixin A2<T extends Bar2, U extends Baz2> {
+ T? aMethod1() {}
+ U? aMethod2() {}
+}
+
+extension A3<T extends Bar3, U extends Baz3> on Object {
+ T? aMethod1() {}
+ U? aMethod2() {}
+}
diff --git a/pkg/front_end/outline_extraction_testcases/type_parameter_extends/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/type_parameter_extends/main.dart.outline_extracted
new file mode 100644
index 0000000..8b4f0c5
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/type_parameter_extends/main.dart.outline_extracted
@@ -0,0 +1,31 @@
+org-dartlang-testcase:///main.dart:
+import "foo.dart";
+class A<T extends Bar, U extends Baz> {
+ T? aMethod1() {}
+ U? aMethod2() {}
+}
+mixin A2<T extends Bar2, U extends Baz2> {
+ T? aMethod1() {}
+ U? aMethod2() {}
+}
+extension A3<T extends Bar3, U extends Baz3> on Object {
+ T? aMethod1() {}
+ U? aMethod2() {}
+}
+
+
+
+
+org-dartlang-testcase:///foo.dart:
+export "bar.dart";
+class Baz {}
+class Baz2 {}
+class Baz3 {}
+
+
+
+
+org-dartlang-testcase:///bar.dart:
+class Bar {}
+class Bar2 {}
+class Bar3 {}
diff --git a/pkg/front_end/outline_extraction_testcases/type_parameter_on_extension/main.dart b/pkg/front_end/outline_extraction_testcases/type_parameter_on_extension/main.dart
new file mode 100644
index 0000000..5093237
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/type_parameter_on_extension/main.dart
@@ -0,0 +1,5 @@
+class Foo<E> {}
+
+extension HiExtension<T extends Foo> on T {
+ void sayHi() {}
+}
diff --git a/pkg/front_end/outline_extraction_testcases/type_parameter_on_extension/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/type_parameter_on_extension/main.dart.outline_extracted
new file mode 100644
index 0000000..c561270
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/type_parameter_on_extension/main.dart.outline_extracted
@@ -0,0 +1,5 @@
+org-dartlang-testcase:///main.dart:
+class Foo<E> {}
+extension HiExtension<T extends Foo> on T {
+ void sayHi() {}
+}
diff --git a/pkg/front_end/outline_extraction_testcases/unused_import/foo.dart b/pkg/front_end/outline_extraction_testcases/unused_import/foo.dart
new file mode 100644
index 0000000..4e6a6de
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/unused_import/foo.dart
@@ -0,0 +1 @@
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/unused_import/main.dart b/pkg/front_end/outline_extraction_testcases/unused_import/main.dart
new file mode 100644
index 0000000..8713cd2
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/unused_import/main.dart
@@ -0,0 +1,6 @@
+import "foo.dart";
+
+void main() {
+ Foo foo = new Foo();
+ print(foo);
+}
diff --git a/pkg/front_end/outline_extraction_testcases/unused_import/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/unused_import/main.dart.outline_extracted
new file mode 100644
index 0000000..aea942e
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/unused_import/main.dart.outline_extracted
@@ -0,0 +1,8 @@
+org-dartlang-testcase:///main.dart:
+import "foo.dart";
+void main() {}
+
+
+
+
+org-dartlang-testcase:///foo.dart:
diff --git a/pkg/front_end/outline_extraction_testcases/unused_import_02/bar.dart b/pkg/front_end/outline_extraction_testcases/unused_import_02/bar.dart
new file mode 100644
index 0000000..5db1c42
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/unused_import_02/bar.dart
@@ -0,0 +1,8 @@
+import "baz.dart";
+
+class Bar {
+ void bar() {
+ Baz baz = new Baz();
+ print(baz);
+ }
+}
diff --git a/pkg/front_end/outline_extraction_testcases/unused_import_02/baz.dart b/pkg/front_end/outline_extraction_testcases/unused_import_02/baz.dart
new file mode 100644
index 0000000..f1e00d1
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/unused_import_02/baz.dart
@@ -0,0 +1 @@
+class Baz {}
diff --git a/pkg/front_end/outline_extraction_testcases/unused_import_02/foo.dart b/pkg/front_end/outline_extraction_testcases/unused_import_02/foo.dart
new file mode 100644
index 0000000..4e6a6de
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/unused_import_02/foo.dart
@@ -0,0 +1 @@
+class Foo {}
diff --git a/pkg/front_end/outline_extraction_testcases/unused_import_02/main.dart b/pkg/front_end/outline_extraction_testcases/unused_import_02/main.dart
new file mode 100644
index 0000000..732d2c4
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/unused_import_02/main.dart
@@ -0,0 +1,8 @@
+import "dart:core";
+import "foo.dart";
+export "bar.dart";
+
+void main() {
+ Foo foo = new Foo();
+ print(foo);
+}
diff --git a/pkg/front_end/outline_extraction_testcases/unused_import_02/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/unused_import_02/main.dart.outline_extracted
new file mode 100644
index 0000000..5e0cec5
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/unused_import_02/main.dart.outline_extracted
@@ -0,0 +1,24 @@
+org-dartlang-testcase:///main.dart:
+import "dart:core";
+import "foo.dart";
+export "bar.dart";
+void main() {}
+
+
+
+
+org-dartlang-testcase:///bar.dart:
+import "baz.dart";
+class Bar {
+ void bar() {}
+}
+
+
+
+
+org-dartlang-testcase:///foo.dart:
+
+
+
+
+org-dartlang-testcase:///baz.dart:
diff --git a/pkg/front_end/outline_extraction_testcases/use_of_imported_extension/foo.dart b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension/foo.dart
new file mode 100644
index 0000000..9a99089
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension/foo.dart
@@ -0,0 +1,11 @@
+extension Foo on String {
+ int get giveInt => 42;
+}
+
+// The below doesn't have to be included.
+
+extension BarExtension on Bar {
+ int get giveInt => 42;
+}
+
+class Bar {}
diff --git a/pkg/front_end/outline_extraction_testcases/use_of_imported_extension/main.dart b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension/main.dart
new file mode 100644
index 0000000..1b39ff1
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension/main.dart
@@ -0,0 +1,5 @@
+import "foo.dart";
+
+int get giveInt => 43;
+
+var x = "hello".giveInt;
diff --git a/pkg/front_end/outline_extraction_testcases/use_of_imported_extension/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension/main.dart.outline_extracted
new file mode 100644
index 0000000..cb49619
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension/main.dart.outline_extracted
@@ -0,0 +1,16 @@
+org-dartlang-testcase:///main.dart:
+import "foo.dart";
+int get giveInt => 43;
+var x = "hello".giveInt;
+
+
+
+
+org-dartlang-testcase:///foo.dart:
+extension Foo on String {
+ int get giveInt => 42;
+}
+extension BarExtension on Bar {
+ int get giveInt => 42;
+}
+class Bar {}
diff --git a/pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/foo.dart b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/foo.dart
new file mode 100644
index 0000000..bd036e7
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/foo.dart
@@ -0,0 +1,17 @@
+enum Foo {
+ a,
+ b,
+ c,
+ d,
+ e,
+}
+
+extension FooExtension on Foo {
+ int get giveInt => 42;
+}
+
+extension BarExtension on Bar {
+ int get giveInt => 42;
+}
+
+class Bar {}
diff --git a/pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/main.dart b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/main.dart
new file mode 100644
index 0000000..5deb533
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/main.dart
@@ -0,0 +1,5 @@
+import "foo.dart";
+
+final foo = [Foo.d, Foo.b];
+
+final foo2 = foo.map((f) => f.giveInt).toList();
diff --git a/pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/main.dart.outline_extracted b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/main.dart.outline_extracted
new file mode 100644
index 0000000..1b7506a
--- /dev/null
+++ b/pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/main.dart.outline_extracted
@@ -0,0 +1,17 @@
+org-dartlang-testcase:///main.dart:
+import "foo.dart";
+final foo = [Foo.d, Foo.b];
+final foo2 = foo.map((f) => f.giveInt).toList();
+
+
+
+
+org-dartlang-testcase:///foo.dart:
+enum Foo { a, b, c, d, e, }
+extension FooExtension on Foo {
+ int get giveInt => 42;
+}
+extension BarExtension on Bar {
+ int get giveInt => 42;
+}
+class Bar {}
diff --git a/pkg/front_end/test/outline_extractor_suite.dart b/pkg/front_end/test/outline_extractor_suite.dart
new file mode 100644
index 0000000..f022e0c
--- /dev/null
+++ b/pkg/front_end/test/outline_extractor_suite.dart
@@ -0,0 +1,315 @@
+// Copyright (c) 2021, 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 'dart:convert' show jsonDecode;
+
+import 'dart:io' show File;
+
+import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart';
+import 'package:front_end/src/fasta/util/outline_extractor.dart';
+import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
+import 'package:testing/testing.dart'
+ show
+ Chain,
+ ChainContext,
+ ExpectationSet,
+ Result,
+ Step,
+ TestDescription,
+ runMe;
+import 'package:kernel/src/equivalence.dart';
+import 'package:front_end/src/api_prototype/compiler_options.dart';
+import 'package:front_end/src/api_prototype/memory_file_system.dart';
+import 'package:kernel/ast.dart';
+
+import 'fasta/testing/suite.dart' show UPDATE_EXPECTATIONS;
+import 'utils/kernel_chain.dart' show MatchContext;
+
+import 'testing_utils.dart' show checkEnvironment;
+
+import 'incremental_suite.dart' as helper;
+
+const String EXPECTATIONS = '''
+[
+ {
+ "name": "ExpectationFileMismatch",
+ "group": "Fail"
+ },
+ {
+ "name": "ExpectationFileMissing",
+ "group": "Fail"
+ }
+]
+''';
+
+void main([List<String> arguments = const []]) =>
+ runMe(arguments, createContext, configurationPath: "../testing.json");
+
+Future<Context> createContext(
+ Chain suite, Map<String, String> environment) async {
+ const Set<String> knownEnvironmentKeys = {
+ "updateExpectations",
+ };
+ checkEnvironment(environment, knownEnvironmentKeys);
+
+ bool updateExpectations = environment["updateExpectations"] == "true";
+
+ return new Context(suite.name, updateExpectations);
+}
+
+class Context extends ChainContext with MatchContext {
+ @override
+ final bool updateExpectations;
+
+ @override
+ String get updateExpectationsOption => '${UPDATE_EXPECTATIONS}=true';
+
+ @override
+ bool get canBeFixWithUpdateExpectations => true;
+
+ final String suiteName;
+
+ Context(this.suiteName, this.updateExpectations);
+
+ @override
+ final List<Step> steps = const <Step>[
+ const OutlineExtractorStep(),
+ const CompileAndCompareStep(),
+ ];
+
+ @override
+ final ExpectationSet expectationSet =
+ new ExpectationSet.fromJsonList(jsonDecode(EXPECTATIONS));
+
+ // Override special handling of negative tests.
+ @override
+ Result processTestResult(
+ TestDescription description, Result result, bool last) {
+ return result;
+ }
+}
+
+class OutlineExtractorStep
+ extends Step<TestDescription, TestDescription, Context> {
+ const OutlineExtractorStep();
+
+ @override
+ String get name => "OutlineExtractorStep";
+
+ @override
+ Future<Result<TestDescription>> run(
+ TestDescription description, Context context) async {
+ Uri? packages = description.uri.resolve(".packages");
+ if (!new File.fromUri(packages).existsSync()) {
+ packages = null;
+ }
+ Map<Uri, String> result =
+ await extractOutline([description.uri], packages: packages);
+
+ StringBuffer sb = new StringBuffer();
+ Uri uri = description.uri;
+ Uri base = uri.resolve(".");
+ Uri dartBase = Uri.base;
+
+ for (MapEntry<Uri, String> entry in result.entries) {
+ sb.writeln("${entry.key}:");
+ sb.writeln(entry.value);
+ sb.writeln("\n\n");
+ }
+
+ String actual = sb.toString();
+ actual = actual.replaceAll("$base", "org-dartlang-testcase:///");
+ actual = actual.replaceAll("$dartBase", "org-dartlang-testcase-sdk:///");
+ actual = actual.replaceAll("\\n", "\n");
+
+ return context.match<TestDescription>(
+ ".outline_extracted",
+ actual,
+ description.uri,
+ description,
+ );
+ }
+}
+
+class CompileAndCompareStep
+ extends Step<TestDescription, TestDescription, Context> {
+ const CompileAndCompareStep();
+
+ @override
+ String get name => "CompileAndCompare";
+
+ @override
+ Future<Result<TestDescription>> run(
+ TestDescription description, Context context) async {
+ Uri? packages = description.uri.resolve(".packages");
+ if (!new File.fromUri(packages).existsSync()) {
+ packages = null;
+ }
+ Map<Uri, String> processedFiles =
+ await extractOutline([description.uri], packages: packages);
+
+ void onDiagnostic(DiagnosticMessage message) {
+ if (message.codeName == "InferredPackageUri") return;
+ if (message.severity == Severity.error ||
+ message.severity == Severity.warning) {
+ throw ("Unexpected error: ${message.plainTextFormatted.join('\n')}");
+ }
+ }
+
+ Library lib1;
+ {
+ CompilerOptions options = helper.getOptions();
+ options.onDiagnostic = onDiagnostic;
+ options.packagesFileUri = packages;
+ helper.TestIncrementalCompiler compiler =
+ new helper.TestIncrementalCompiler(options, description.uri,
+ /* initializeFrom = */ null, /* outlineOnly = */ true);
+ IncrementalCompilerResult c = await compiler.computeDelta();
+ lib1 = c.component.libraries
+ .firstWhere((element) => element.fileUri == description.uri);
+ }
+ Library lib2;
+ {
+ CompilerOptions options = helper.getOptions();
+ options.onDiagnostic = onDiagnostic;
+ options.packagesFileUri = packages;
+ MemoryFileSystem mfs = new MemoryFileSystem(Uri.base);
+ if (packages != null) {
+ mfs.entityForUri(packages).writeAsBytesSync(
+ await options.fileSystem.entityForUri(packages).readAsBytes());
+ }
+ if (options.sdkSummary != null) {
+ mfs.entityForUri(options.sdkSummary!).writeAsBytesSync(await options
+ .fileSystem
+ .entityForUri(options.sdkSummary!)
+ .readAsBytes());
+ }
+ if (options.librariesSpecificationUri != null) {
+ mfs.entityForUri(options.librariesSpecificationUri!).writeAsBytesSync(
+ await options.fileSystem
+ .entityForUri(options.librariesSpecificationUri!)
+ .readAsBytes());
+ }
+ for (MapEntry<Uri, String> entry in processedFiles.entries) {
+ mfs.entityForUri(entry.key).writeAsStringSync(entry.value);
+ }
+ options.fileSystem = mfs;
+ helper.TestIncrementalCompiler compiler =
+ new helper.TestIncrementalCompiler(options, description.uri,
+ /* initializeFrom = */ null, /* outlineOnly = */ true);
+ IncrementalCompilerResult c = await compiler.computeDelta();
+ lib2 = c.component.libraries
+ .firstWhere((element) => element.fileUri == description.uri);
+ }
+ EquivalenceResult result =
+ checkEquivalence(lib1, lib2, strategy: const Strategy());
+
+ if (result.isEquivalent) {
+ return new Result<TestDescription>.pass(description);
+ } else {
+ print("Bad:");
+ print(result);
+ return new Result<TestDescription>.fail(
+ description, /* error = */ result);
+ }
+ }
+}
+
+class Strategy extends EquivalenceStrategy {
+ const Strategy();
+
+ @override
+ bool checkTreeNode_fileOffset(
+ EquivalenceVisitor visitor, TreeNode node, TreeNode other) {
+ return true;
+ }
+
+ @override
+ bool checkAssertStatement_conditionStartOffset(
+ EquivalenceVisitor visitor, AssertStatement node, AssertStatement other) {
+ return true;
+ }
+
+ @override
+ bool checkAssertStatement_conditionEndOffset(
+ EquivalenceVisitor visitor, AssertStatement node, AssertStatement other) {
+ return true;
+ }
+
+ @override
+ bool checkClass_startFileOffset(
+ EquivalenceVisitor visitor, Class node, Class other) {
+ return true;
+ }
+
+ @override
+ bool checkClass_fileEndOffset(
+ EquivalenceVisitor visitor, Class node, Class other) {
+ return true;
+ }
+
+ @override
+ bool checkProcedure_startFileOffset(
+ EquivalenceVisitor visitor, Procedure node, Procedure other) {
+ return true;
+ }
+
+ @override
+ bool checkConstructor_startFileOffset(
+ EquivalenceVisitor visitor, Constructor node, Constructor other) {
+ return true;
+ }
+
+ @override
+ bool checkMember_fileEndOffset(
+ EquivalenceVisitor visitor, Member node, Member other) {
+ return true;
+ }
+
+ @override
+ bool checkFunctionNode_fileEndOffset(
+ EquivalenceVisitor visitor, FunctionNode node, FunctionNode other) {
+ return true;
+ }
+
+ @override
+ bool checkBlock_fileEndOffset(
+ EquivalenceVisitor visitor, Block node, Block other) {
+ return true;
+ }
+
+ @override
+ bool checkLibrary_additionalExports(
+ EquivalenceVisitor visitor, Library node, Library other) {
+ return visitor.checkSets(
+ node.additionalExports.toSet(),
+ other.additionalExports.toSet(),
+ visitor.matchReferences,
+ visitor.checkReferences,
+ 'additionalExports');
+ }
+
+ @override
+ bool checkClass_procedures(
+ EquivalenceVisitor visitor, Class node, Class other) {
+ // Check procedures as a set instead of a list to allow for reordering.
+ List<Procedure> a = node.procedures.toList();
+ int sorter(Procedure x, Procedure y) {
+ int result = x.name.text.compareTo(y.name.text);
+ if (result != 0) return result;
+ result = x.kind.index - y.kind.index;
+ if (result != 0) return result;
+ // other stuff?
+ return 0;
+ }
+
+ a.sort(sorter);
+ List<Procedure> b = other.procedures.toList();
+ b.sort(sorter);
+ // return visitor.checkSets(a.toSet(), b.toSet(),
+ // visitor.matchNamedNodes, visitor.checkNodes, 'procedures');
+
+ return visitor.checkLists(a, b, visitor.checkNodes, 'procedures');
+ }
+}
diff --git a/pkg/front_end/test/outline_extractor_tester.dart b/pkg/front_end/test/outline_extractor_tester.dart
new file mode 100644
index 0000000..941b5c0
--- /dev/null
+++ b/pkg/front_end/test/outline_extractor_tester.dart
@@ -0,0 +1,301 @@
+// Copyright (c) 2021, 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 'dart:io';
+
+import 'package:front_end/src/api_prototype/compiler_options.dart';
+import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart';
+import 'package:front_end/src/api_prototype/memory_file_system.dart';
+import 'package:kernel/ast.dart';
+import 'package:kernel/src/equivalence.dart';
+import 'package:compiler/src/kernel/dart2js_target.dart' show Dart2jsTarget;
+import 'package:kernel/target/targets.dart';
+import 'incremental_suite.dart' as helper;
+import 'package:front_end/src/fasta/util/outline_extractor.dart';
+import 'package:package_config/package_config.dart';
+
+Future<void> main(List<String> args) async {
+ if (args.length != 1) throw "Wants 1 argument.";
+ Uri input = Uri.base.resolve(args.single);
+ Uri packageUri = input.resolve(".packages");
+ Stopwatch stopwatch = new Stopwatch()..start();
+ PackageConfig packageFile = await loadPackageConfigUri(packageUri);
+ print("Read packages file in ${stopwatch.elapsedMilliseconds} ms");
+ List<Package> packages = packageFile.packages.toList();
+ int packageNum = 0;
+ for (Package package in packages) {
+ packageNum++;
+ print("\n\nProcessing package #$packageNum (${package.name}) "
+ "of ${packages.length}");
+ Directory dir = new Directory.fromUri(package.packageUriRoot);
+ List<Uri> uris = [];
+ for (FileSystemEntity entry in dir.listSync(recursive: true)) {
+ if (entry is File && entry.path.endsWith(".dart")) {
+ // Hack.
+ String content = entry.readAsStringSync();
+ if (content.contains("part of")) continue;
+ String asString = "${entry.uri}";
+ String packageName = package.name;
+ Uri packageUri = package.packageUriRoot;
+ String prefix = "${packageUri}";
+ if (asString.startsWith(prefix)) {
+ Uri reversed = Uri.parse(
+ "package:$packageName/${asString.substring(prefix.length)}");
+ uris.add(reversed);
+ } else {
+ throw "Unexpected!";
+ }
+ }
+ }
+ print("(found ${uris.length} files)");
+ if (uris.isEmpty) continue;
+ await processUri(uris, null, packageUri);
+ }
+ print(" => That's ${packages.length} packages!");
+
+ if (1 + 1 == 2) return;
+
+ Component fullComponent = await processUri([input], null, packageUri);
+ List<Uri> uris = fullComponent.libraries.map((l) => l.importUri).toList();
+ int i = 0;
+ for (Uri uri in uris) {
+ i++;
+ print("\n\nProcessing $uri (${i} of ${uris.length})");
+ try {
+ await processUri([uri], fullComponent, packageUri);
+ } catch (e, st) {
+ print("\n\n-------------\n\n");
+ print("Crashed on uri $uri");
+ print("Exception: '$e'");
+ print(st);
+ print("\n\n-------------\n\n");
+ }
+ }
+}
+
+Future<Component> processUri(final List<Uri> inputs, Component? fullComponent,
+ final Uri packageUri) async {
+ TargetFlags targetFlags =
+ new TargetFlags(enableNullSafety: true, trackWidgetCreation: false);
+ Target? target = new Dart2jsTarget("dart2js", targetFlags);
+ Uri sdkSummary = Uri.base.resolve("out/ReleaseX64/dart2js_outline.dill");
+ Stopwatch stopwatch = new Stopwatch()..start();
+ Stopwatch extractCompile = new Stopwatch()..start();
+ Map<Uri, String> processedFiles = await extractOutline(inputs,
+ packages: packageUri, target: target, platform: sdkSummary);
+ extractCompile.stop();
+ print("Got ${processedFiles.keys.length} files "
+ "in ${stopwatch.elapsedMilliseconds} ms");
+
+ Set<Uri> inputsSet = inputs.toSet();
+
+ Stopwatch plainCompile = new Stopwatch()..start();
+ List<Library> libs1;
+ {
+ stopwatch.reset();
+ CompilerOptions options = helper.getOptions();
+ options.target = target;
+ options.sdkSummary = sdkSummary;
+ options.packagesFileUri = packageUri;
+ helper.TestIncrementalCompiler compiler =
+ new helper.TestIncrementalCompiler(options, inputs.first,
+ /* initializeFrom = */ null, /* outlineOnly = */ true);
+ fullComponent = fullComponent ??
+ (await compiler.computeDelta(entryPoints: inputs)).component;
+ print("Compiled full in ${stopwatch.elapsedMilliseconds} ms "
+ "to ${fullComponent.libraries.length} libraries");
+ plainCompile.stop();
+
+ libs1 = fullComponent.libraries
+ .where((element) => inputsSet.contains(element.importUri))
+ .toList();
+ }
+ List<Library> libs2;
+ {
+ stopwatch.reset();
+ extractCompile.start();
+ CompilerOptions options = helper.getOptions();
+ options.target = target;
+ options.sdkSummary = sdkSummary;
+ options.packagesFileUri = packageUri;
+ MemoryFileSystem mfs = new MemoryFileSystem(Uri.base);
+ mfs.entityForUri(packageUri).writeAsBytesSync(
+ await options.fileSystem.entityForUri(packageUri).readAsBytes());
+ if (options.sdkSummary != null) {
+ mfs.entityForUri(options.sdkSummary!).writeAsBytesSync(await options
+ .fileSystem
+ .entityForUri(options.sdkSummary!)
+ .readAsBytes());
+ }
+ if (options.librariesSpecificationUri != null) {
+ mfs.entityForUri(options.librariesSpecificationUri!).writeAsBytesSync(
+ await options.fileSystem
+ .entityForUri(options.librariesSpecificationUri!)
+ .readAsBytes());
+ }
+ for (MapEntry<Uri, String> entry in processedFiles.entries) {
+ mfs.entityForUri(entry.key).writeAsStringSync(entry.value);
+ }
+ options.fileSystem = mfs;
+ helper.TestIncrementalCompiler compiler =
+ new helper.TestIncrementalCompiler(options, inputs.first,
+ /* initializeFrom = */ null, /* outlineOnly = */ true);
+ IncrementalCompilerResult c =
+ await compiler.computeDelta(entryPoints: inputs);
+ print("Compiled outlined in ${stopwatch.elapsedMilliseconds} ms "
+ "to ${c.component.libraries.length} libraries");
+ extractCompile.stop();
+
+ libs2 = c.component.libraries
+ .where((element) => inputsSet.contains(element.importUri))
+ .toList();
+ }
+
+ int libSorter(Library a, Library b) {
+ return a.importUri.toString().compareTo(b.importUri.toString());
+ }
+
+ libs1.sort(libSorter);
+ libs2.sort(libSorter);
+ if (libs1.length != libs2.length) {
+ print("Bad:");
+ print(
+ "Not the same amount of libraries: ${libs1.length} vs ${libs2.length}");
+ throw "bad result for $inputs";
+ }
+ List<EquivalenceResult> badResults = [];
+ for (int i = 0; i < libs1.length; i++) {
+ EquivalenceResult result =
+ checkEquivalence(libs1[i], libs2[i], strategy: const Strategy());
+ if (!result.isEquivalent) {
+ badResults.add(result);
+ }
+ }
+
+ if (badResults.isEmpty) {
+ print("OK");
+ } else {
+ print("Bad:");
+ for (EquivalenceResult badResult in badResults) {
+ print(badResult);
+ print("---");
+ }
+ // globalDebuggingNames = new NameSystem();
+ // print(lib1.leakingDebugToString());
+ // print("\n---\nvs\n----\n");
+ // globalDebuggingNames = new NameSystem();
+ // print(lib2.leakingDebugToString());
+ throw "bad result for $inputs";
+ }
+
+ if (plainCompile.elapsedMilliseconds > extractCompile.elapsedMilliseconds) {
+ print("=> Plain compile slower! "
+ "(${plainCompile.elapsedMilliseconds} vs "
+ "${extractCompile.elapsedMilliseconds})");
+ } else {
+ print("=> Plain compile faster! "
+ "(${plainCompile.elapsedMilliseconds} vs "
+ "${extractCompile.elapsedMilliseconds})");
+ }
+
+ return fullComponent;
+}
+
+class Strategy extends EquivalenceStrategy {
+ const Strategy();
+
+ @override
+ bool checkTreeNode_fileOffset(
+ EquivalenceVisitor visitor, TreeNode node, TreeNode other) {
+ return true;
+ }
+
+ @override
+ bool checkAssertStatement_conditionStartOffset(
+ EquivalenceVisitor visitor, AssertStatement node, AssertStatement other) {
+ return true;
+ }
+
+ @override
+ bool checkAssertStatement_conditionEndOffset(
+ EquivalenceVisitor visitor, AssertStatement node, AssertStatement other) {
+ return true;
+ }
+
+ @override
+ bool checkClass_startFileOffset(
+ EquivalenceVisitor visitor, Class node, Class other) {
+ return true;
+ }
+
+ @override
+ bool checkClass_fileEndOffset(
+ EquivalenceVisitor visitor, Class node, Class other) {
+ return true;
+ }
+
+ @override
+ bool checkProcedure_startFileOffset(
+ EquivalenceVisitor visitor, Procedure node, Procedure other) {
+ return true;
+ }
+
+ @override
+ bool checkConstructor_startFileOffset(
+ EquivalenceVisitor visitor, Constructor node, Constructor other) {
+ return true;
+ }
+
+ @override
+ bool checkMember_fileEndOffset(
+ EquivalenceVisitor visitor, Member node, Member other) {
+ return true;
+ }
+
+ @override
+ bool checkFunctionNode_fileEndOffset(
+ EquivalenceVisitor visitor, FunctionNode node, FunctionNode other) {
+ return true;
+ }
+
+ @override
+ bool checkBlock_fileEndOffset(
+ EquivalenceVisitor visitor, Block node, Block other) {
+ return true;
+ }
+
+ @override
+ bool checkLibrary_additionalExports(
+ EquivalenceVisitor visitor, Library node, Library other) {
+ return visitor.checkSets(
+ node.additionalExports.toSet(),
+ other.additionalExports.toSet(),
+ visitor.matchReferences,
+ visitor.checkReferences,
+ 'additionalExports');
+ }
+
+ @override
+ bool checkClass_procedures(
+ EquivalenceVisitor visitor, Class node, Class other) {
+ // Check procedures as a set instead of a list to allow for reordering.
+ List<Procedure> a = node.procedures.toList();
+ int sorter(Procedure x, Procedure y) {
+ int result = x.name.text.compareTo(y.name.text);
+ if (result != 0) return result;
+ result = x.kind.index - y.kind.index;
+ if (result != 0) return result;
+ // other stuff?
+ return 0;
+ }
+
+ a.sort(sorter);
+ List<Procedure> b = other.procedures.toList();
+ b.sort(sorter);
+ // return visitor.checkSets(a.toSet(), b.toSet(),
+ // visitor.matchNamedNodes, visitor.checkNodes, 'procedures');
+
+ return visitor.checkLists(a, b, visitor.checkNodes, 'procedures');
+ }
+}
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 0f0d0b8..9790926 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -11,6 +11,7 @@
a+b
abbreviate
+abc
abcdef
abs
accounting
@@ -216,6 +217,7 @@
codec
codes
collision
+coloring
colorized
com
combinations
@@ -399,6 +401,7 @@
engineered
enhanced
enters
+entrypointish
enumerates
env
eof
@@ -458,6 +461,7 @@
fieldformal
file's
filenames
+fileuri
finally's
finv
firsts
@@ -689,6 +693,7 @@
len
lets
letting
+levels
lex
lexemes
lf
@@ -863,6 +868,7 @@
orphans
ors
os
+outlined
outputs
outputting
overlap
@@ -894,6 +900,7 @@
parens
parenteses
particularly
+partof
patchup
path
patterns
@@ -934,7 +941,9 @@
prebuilt
preexisted
preexisting
+premark
preorder
+preprocess
presented
presubmit
presumably
@@ -945,6 +954,7 @@
println
prioritization
proc
+processor
producers
product
progresses
@@ -1087,6 +1097,7 @@
role
room
rooted
+rough
roughly
rounding
roundtrip
@@ -1201,6 +1212,7 @@
stopgap
stopped
storage
+story
str
strategies
streak
@@ -1388,6 +1400,7 @@
unsortable
unsound
unsoundness
+untouched
unwrapper
unwraps
unwritten
@@ -1398,6 +1411,7 @@
uri's
url
urls
+usages
usr
usual
usually
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index bb52fe8..5927e03 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -659,6 +659,7 @@
metric
metrics
mf
+mfs
micro
minimize
minimizer
@@ -699,6 +700,7 @@
nonexisting
noo
noted
+nottest
numerator
ob
obool
@@ -709,6 +711,7 @@
ol
onull
oo
+oobar
oocf
ooo
oovf
@@ -783,6 +786,9 @@
quux
quuz
qux
+qux1
+qux3x
+qux4x
r"
r"\s
r"k
@@ -843,6 +849,7 @@
sdkroot
sdks
secondary
+secondtest12part1usage
segment
selection
semifuzz
@@ -925,8 +932,19 @@
tails
talk
templates
+test10
+test12
+test12part1usage
+test13
+test13andahalf
+test15thing
+test16
+test16toplevel
test3a
test3b
+test3partfoo
+test8
+test9
theoretically
thereof
thread
@@ -996,6 +1014,7 @@
waiting
waits
walt
+wants
warmup
week
weekly
@@ -1014,8 +1033,10 @@
xxx
xxxxxxxx
xxxxxxxxxxxx
+xyz
y's
year
yxxx
yy
+zyx
zz
diff --git a/pkg/front_end/testing.json b/pkg/front_end/testing.json
index 3e12d26..11fca520 100644
--- a/pkg/front_end/testing.json
+++ b/pkg/front_end/testing.json
@@ -229,6 +229,17 @@
"exclude": []
},
{
+ "name": "outline_extractor",
+ "kind": "Chain",
+ "source": "test/outline_extractor_suite.dart",
+ "path": "outline_extraction_testcases/",
+ "status": "outline_extraction_testcases/outline_extractor.status",
+ "pattern": [
+ "main\\.dart$"
+ ],
+ "exclude": []
+ },
+ {
"name": "parser_equivalence",
"kind": "Chain",
"source": "test/parser_equivalence_suite.dart",
diff --git a/pkg/front_end/tool/_fasta/direct_parser_ast_helper_creator.dart b/pkg/front_end/tool/_fasta/direct_parser_ast_helper_creator.dart
index f368c90..2cfa64c 100644
--- a/pkg/front_end/tool/_fasta/direct_parser_ast_helper_creator.dart
+++ b/pkg/front_end/tool/_fasta/direct_parser_ast_helper_creator.dart
@@ -64,6 +64,7 @@
final DirectParserASTType type;
Map<String, Object?> get deprecatedArguments;
List<DirectParserASTContent>? children;
+ DirectParserASTContent? parent;
DirectParserASTContent(this.what, this.type);
diff --git a/pkg/front_end/tool/kernel_ast_file_rewriter.dart b/pkg/front_end/tool/kernel_ast_file_rewriter.dart
index 16f15e7..d80b5c2 100644
--- a/pkg/front_end/tool/kernel_ast_file_rewriter.dart
+++ b/pkg/front_end/tool/kernel_ast_file_rewriter.dart
@@ -80,7 +80,7 @@
} else if (member.isClassFields()) {
DirectParserASTContentClassFieldsEnd classFields =
member.getClassFields();
- Token identifierToken = classFields.getFieldIdentifiers().single!.token;
+ Token identifierToken = classFields.getFieldIdentifiers().single.token;
String identifier = identifierToken.toString();
namedFields.add(identifier);
}
@@ -152,7 +152,7 @@
throw "Notice ${classFields.count}";
}
- Token identifierToken = classFields.getFieldIdentifiers().single!.token;
+ Token identifierToken = classFields.getFieldIdentifiers().single.token;
String identifier = identifierToken.toString();
if (identifier == "frozen" && entry.key == "TreeNode") return;
diff --git a/pkg/front_end/tool/parser_direct_ast/viewer.dart b/pkg/front_end/tool/parser_direct_ast/viewer.dart
index aeee1b5..edf1159 100644
--- a/pkg/front_end/tool/parser_direct_ast/viewer.dart
+++ b/pkg/front_end/tool/parser_direct_ast/viewer.dart
@@ -17,7 +17,7 @@
uri = Uri.base.resolve(args.first);
}
Uint8List bytes = new File.fromUri(uri).readAsBytesSync();
- DirectParserASTContent ast = getAST(bytes);
+ DirectParserASTContent ast = getAST(bytes, enableExtensionMethods: true);
Widget widget = new QuitOnQWidget(
new WithSingleLineBottomWidget(
diff --git a/tools/VERSION b/tools/VERSION
index bab89d6..24fa5a6 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 53
+PRERELEASE 54
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/package_deps/bin/package_deps.dart b/tools/package_deps/bin/package_deps.dart
index d0a8abc..38cca34 100644
--- a/tools/package_deps/bin/package_deps.dart
+++ b/tools/package_deps/bin/package_deps.dart
@@ -321,6 +321,7 @@
// Skip 'pkg/analyzer_cli/test/data'.
// Skip 'pkg/front_end/test/id_testing/data/'.
// Skip 'pkg/front_end/test/language_versioning/data/'.
+ // Skip 'pkg/front_end/outline_extraction_testcases/'.
if (name == 'data' && path.split(entity.parent.path).contains('test')) {
continue;
}
@@ -335,6 +336,11 @@
continue;
}
+ // Skip 'pkg/front_end/outline_extraction_testcases'.
+ if (name == 'outline_extraction_testcases') {
+ continue;
+ }
+
if (!name.startsWith('.')) {
_collectDartFiles(entity, files);
}