[CFE] First stab at prototype for textually extracting outlines

Change-Id: Ib84b47f0acb553cee6d891d7bbcf865f4a57a65a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221080
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Jens Johansen 2021-11-26 06:14:44 +00:00 committed by commit-bot@chromium.org
parent 4f6d813c3c
commit ad6ffaab95
120 changed files with 3861 additions and 9 deletions

View file

@ -4,6 +4,7 @@
analyzer:
exclude:
- outline_extraction_testcases/**
- parser_testcases/**
- test/analyser_ignored/**
- test/class_hierarchy/data/**

View file

@ -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 {};
}
}

View file

@ -6,6 +6,7 @@ import 'dart:typed_data' show Uint8List;
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/utf8_bytes_scanner.dart'
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 @@ DirectParserASTContentCompilationUnitEnd getAST(List<int> rawBytes,
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 @@ DirectParserASTContentCompilationUnitEnd getAST(List<int> rawBytes,
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 MemberExtension on DirectParserASTContentMemberEnd {
}
}
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 ClassFieldsExtension on DirectParserASTContentClassFieldsEnd {
}
}
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 @@ extension ClassMethodExtension on DirectParserASTContentClassMethodEnd {
}
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 @@ extension ClassConstructorExtension
}
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 @@ class DirectParserASTListener extends AbstractDirectParserASTListener {
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;

View file

@ -26,6 +26,7 @@ abstract class DirectParserASTContent {
final DirectParserASTType type;
Map<String, Object?> get deprecatedArguments;
List<DirectParserASTContent>? children;
DirectParserASTContent? parent;
DirectParserASTContent(this.what, this.type);

View file

@ -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);
}
}

View file

@ -0,0 +1 @@
class Foo {}

View file

@ -0,0 +1 @@
class Foo2 {}

View file

@ -0,0 +1 @@
class Foo {}

View file

@ -0,0 +1 @@
class Foo2 {}

View file

@ -0,0 +1 @@
class Foo {}

View file

@ -0,0 +1 @@
class Foo2 {}

View file

@ -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();
}

View file

@ -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 {}

View file

@ -0,0 +1,3 @@
import "test8.dart";
ClassFromImportsExportsExport? zyx____xyz;

View file

@ -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 {}

View file

@ -0,0 +1,5 @@
export "dart:async";
void test10Method() {}
class ClassFromImportsExportsExport {}

View file

@ -0,0 +1,3 @@
export "test9.dart";
void test8Method() {}

View file

@ -0,0 +1,3 @@
export "test10.dart";
void test9Method() {}

View file

@ -0,0 +1 @@
export "test6.dart";

View file

@ -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() {}

View file

@ -0,0 +1,3 @@
export "test7.dart";
void test6() {}

View file

@ -0,0 +1 @@
void test7() {}

View file

@ -0,0 +1 @@
class Foo {}

View file

@ -0,0 +1,3 @@
import "foo.dart";
class X extends Foo {}

View file

@ -0,0 +1,9 @@
org-dartlang-testcase:///main.dart:
import "foo.dart";
class X extends Foo {}
org-dartlang-testcase:///foo.dart:
class Foo {}

View file

@ -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();
}

View file

@ -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() {}
}

View file

@ -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() {}
}

View file

@ -0,0 +1,9 @@
import "c.dart";
class B {
C c() {
return new C();
}
}
class BPrime {}

View file

@ -0,0 +1,9 @@
import "d.dart";
class C {
D d() {
return new D();
}
}
class CPrime {}

View file

@ -0,0 +1,7 @@
class D {
String d() {
return "hello";
}
}
class DPrime {}

View file

@ -0,0 +1,9 @@
import "b.dart";
var x = A.b().c().d;
class A {
static B b() {
return new B();
}
}

View file

@ -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() {}
}

View file

@ -0,0 +1,11 @@
class Bar {}
class Bar2 {}
class Bar3 {}
class Bar4 {}
class Foo3 {}
class Foo4 {}

View file

@ -0,0 +1,13 @@
export "bar.dart";
class Baz {}
class Baz2 {}
class Baz3 {}
class Baz4 {}
class Foo {}
class Foo2 {}

View file

@ -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();
}

View file

@ -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 {}

View file

@ -0,0 +1,6 @@
import "test16.dart" as test16;
class Test16ClassHelper {
// the naming matching is what makes it annoying!
final test16toplevel = test16.test16toplevel("hello");
}

View file

@ -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) {}

View file

@ -0,0 +1,3 @@
String test16toplevel(String s) {
return s * 2;
}

View file

@ -0,0 +1,5 @@
int bar() {
return 42;
}
class Baz {}

View file

@ -0,0 +1,5 @@
String bar() {
return "hello";
}
class Baz {}

View file

@ -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;

View file

@ -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:

View file

@ -0,0 +1,3 @@
String foo() {
return "foo";
}

View file

@ -0,0 +1,3 @@
String bar() {
return "bar";
}

View file

@ -0,0 +1,5 @@
import 'a.dart' as x;
import 'b.dart' as x;
var foo = x.foo();
var bar = x.bar();

View file

@ -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() {}

View file

@ -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;
}
}

View file

@ -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 }

View file

@ -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;

View file

@ -0,0 +1,8 @@
import "test5.dart";
export "test5.dart";
class Qux3<E> {
Qux4? foo() {}
}
class Qux4<E> {}

View file

@ -0,0 +1,5 @@
class Qux3x<E> {
Qux4x? foo() {}
}
class Qux4x<E> {}

View file

@ -0,0 +1,5 @@
// @dart = 2.12
class Bar {}
class Baz {}

View file

@ -0,0 +1,3 @@
import "bar.dart";
void foo(Bar bar) {}

View file

@ -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 {}

View file

@ -0,0 +1 @@
class AUnused {}

View file

@ -0,0 +1,9 @@
const AbcX = const _AbcX();
class _AbcX {
const _AbcX();
}
class AbcX2 {
const AbcX2();
}

View file

@ -0,0 +1,5 @@
import "a.dart";
import "b.dart";
@AbcX
void foo() {}

View file

@ -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();
}

View file

@ -0,0 +1,5 @@
import "test15.dart" as $test15;
import "nottest15.dart";
@$test15.Test15()
void test15thing() {}

View file

@ -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:

View file

@ -0,0 +1,7 @@
class Test15 {
const Test15();
}
class Test16 {
const Test16();
}

View file

@ -0,0 +1,7 @@
class Test15 {
const Test15();
}
class Test16 {
const Test16();
}

View file

@ -0,0 +1,5 @@
library foo.a;
export 'b.dart';
export 'c.dart';
export 'd.dart';

View file

@ -0,0 +1,7 @@
class C<T> {
const C.b();
}
class C2<T> {
const C2.b();
}

View file

@ -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();
}

View file

@ -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:

View file

@ -0,0 +1,3 @@
export "baz.dart";
class Bar {}

View file

@ -0,0 +1 @@
class Baz {}

View file

@ -0,0 +1,3 @@
import "bar.dart";
class Foo = Object with Bar implements Baz;

View file

@ -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 {}

View file

@ -0,0 +1,4 @@
import "test12.dart";
void test12part1usage(Test12Part1 x) {}
void secondtest12part1usage(SecondTest12 x) {}

View file

@ -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:

View file

@ -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) {}
}

View file

@ -0,0 +1,9 @@
part of "test12.dart";
class Test12Part1 {
void foo(Test12 x) {}
void bar(Test12Part2 x) {}
void baz(Test13 x) {}
}
class SecondTest12Part1 {}

View file

@ -0,0 +1,3 @@
part of "test12.dart";
class Test12Part2 {}

View file

@ -0,0 +1,3 @@
import "test13andahalf.dart";
class Test13 {}

View file

@ -0,0 +1 @@
class Test13AndAHalf {}

View file

@ -0,0 +1 @@
class Test14 {}

View file

@ -0,0 +1,3 @@
import 'test3.dart';
var x = test3partfoo();

View file

@ -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() {}

View file

@ -0,0 +1,5 @@
library test3;
part "test3_part.dart";
class Foo {}

View file

@ -0,0 +1,5 @@
part of test3;
void test3partfoo() {}
class Bar {}

View file

@ -0,0 +1 @@
class Foo {}

View file

@ -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();
}

View file

@ -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 {}

View file

@ -0,0 +1,7 @@
part of "package:f"
"oobar"
"/"
"main"
".dart";
class Bar {}

View file

@ -0,0 +1,5 @@
class Bar {}
class Bar2 {}
class Bar3 {}

View file

@ -0,0 +1,7 @@
export "bar.dart";
class Baz {}
class Baz2 {}
class Baz3 {}

View file

@ -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() {}
}

View file

@ -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 {}

View file

@ -0,0 +1,5 @@
class Foo<E> {}
extension HiExtension<T extends Foo> on T {
void sayHi() {}
}

View file

@ -0,0 +1,5 @@
org-dartlang-testcase:///main.dart:
class Foo<E> {}
extension HiExtension<T extends Foo> on T {
void sayHi() {}
}

View file

@ -0,0 +1 @@
class Foo {}

View file

@ -0,0 +1,6 @@
import "foo.dart";
void main() {
Foo foo = new Foo();
print(foo);
}

View file

@ -0,0 +1,8 @@
org-dartlang-testcase:///main.dart:
import "foo.dart";
void main() {}
org-dartlang-testcase:///foo.dart:

Some files were not shown because too many files have changed in this diff Show more