From ad6ffaab952ae0ad6816b122d4340c5fd43fd587 Mon Sep 17 00:00:00 2001 From: Jens Johansen Date: Fri, 26 Nov 2021 06:14:44 +0000 Subject: [PATCH] [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 Reviewed-by: Johnni Winther --- pkg/front_end/analysis_options_no_lints.yaml | 1 + .../src/fasta/util/abstracted_ast_nodes.dart | 864 ++++++++++++++++ .../lib/src/fasta/util/direct_parser_ast.dart | 338 +++++- .../fasta/util/direct_parser_ast_helper.dart | 1 + .../lib/src/fasta/util/outline_extractor.dart | 964 ++++++++++++++++++ .../conditional_imports_exports/a.dart | 1 + .../conditional_imports_exports/a2.dart | 1 + .../conditional_imports_exports/b.dart | 1 + .../conditional_imports_exports/b2.dart | 1 + .../conditional_imports_exports/c.dart | 1 + .../conditional_imports_exports/c2.dart | 1 + .../conditional_imports_exports/main.dart | 8 + .../main.dart.outline_extracted | 40 + .../exports_export_01/main.dart | 3 + .../main.dart.outline_extracted | 22 + .../exports_export_01/test10.dart | 5 + .../exports_export_01/test8.dart | 3 + .../exports_export_01/test9.dart | 3 + .../exports_included/main.dart | 1 + .../main.dart.outline_extracted | 15 + .../exports_included/test6.dart | 3 + .../exports_included/test7.dart | 1 + .../extends/foo.dart | 1 + .../extends/main.dart | 3 + .../extends/main.dart.outline_extracted | 9 + .../factories/main.dart | 12 + .../factories/main.dart.outline_extracted | 24 + .../factories/test11.dart | 21 + .../field_dotting_in/b.dart | 9 + .../field_dotting_in/c.dart | 9 + .../field_dotting_in/d.dart | 7 + .../field_dotting_in/main.dart | 9 + .../main.dart.outline_extracted | 32 + .../fields/bar.dart | 11 + .../fields/foo.dart | 13 + .../fields/main.dart | 19 + .../fields/main.dart.outline_extracted | 40 + .../main.dart | 6 + .../main.dart.outline_extracted | 11 + .../test16.dart | 3 + .../import_with_prefix/bar.dart | 5 + .../import_with_prefix/foo.dart | 5 + .../import_with_prefix/main.dart | 7 + .../main.dart.outline_extracted | 17 + .../import_with_prefix_02/a.dart | 3 + .../import_with_prefix_02/b.dart | 3 + .../import_with_prefix_02/main.dart | 5 + .../main.dart.outline_extracted | 17 + .../initial_various/main.dart | 28 + .../main.dart.outline_extracted | 24 + .../initial_various/test3.dart | 25 + .../initial_various/test4.dart | 8 + .../initial_various/test5.dart | 5 + .../keeps_dart_version/bar.dart | 5 + .../keeps_dart_version/main.dart | 3 + .../main.dart.outline_extracted | 10 + .../metadata_01/a.dart | 1 + .../metadata_01/b.dart | 9 + .../metadata_01/main.dart | 5 + .../metadata_01/main.dart.outline_extracted | 19 + .../metadata_02/main.dart | 5 + .../metadata_02/main.dart.outline_extracted | 18 + .../metadata_02/nottest15.dart | 7 + .../metadata_02/test15.dart | 7 + .../a.dart | 5 + .../b.dart | 1 + .../c.dart | 7 + .../d.dart | 1 + .../main.dart | 6 + .../main.dart.outline_extracted | 36 + .../named_mixin/bar.dart | 3 + .../named_mixin/baz.dart | 1 + .../named_mixin/main.dart | 3 + .../named_mixin/main.dart.outline_extracted | 16 + .../outline_extractor.status | 0 .../part_01/main.dart | 4 + .../part_01/main.dart.outline_extracted | 47 + .../part_01/test12.dart | 11 + .../part_01/test12_part1.dart | 9 + .../part_01/test12_part2.dart | 3 + .../part_01/test13.dart | 3 + .../part_01/test13andahalf.dart | 1 + .../part_01/test14.dart | 1 + .../part_and_library_name/main.dart | 3 + .../main.dart.outline_extracted | 17 + .../part_and_library_name/test3.dart | 5 + .../part_and_library_name/test3_part.dart | 5 + .../split_import_export_part/foo.dart | 1 + .../split_import_export_part/main.dart | 25 + .../main.dart.outline_extracted | 19 + .../split_import_export_part/part.dart | 7 + .../type_parameter_extends/bar.dart | 5 + .../type_parameter_extends/foo.dart | 7 + .../type_parameter_extends/main.dart | 16 + .../main.dart.outline_extracted | 31 + .../type_parameter_on_extension/main.dart | 5 + .../main.dart.outline_extracted | 5 + .../unused_import/foo.dart | 1 + .../unused_import/main.dart | 6 + .../unused_import/main.dart.outline_extracted | 8 + .../unused_import_02/bar.dart | 8 + .../unused_import_02/baz.dart | 1 + .../unused_import_02/foo.dart | 1 + .../unused_import_02/main.dart | 8 + .../main.dart.outline_extracted | 24 + .../use_of_imported_extension/foo.dart | 11 + .../use_of_imported_extension/main.dart | 5 + .../main.dart.outline_extracted | 16 + .../use_of_imported_extension_2/foo.dart | 17 + .../use_of_imported_extension_2/main.dart | 5 + .../main.dart.outline_extracted | 17 + .../test/outline_extractor_suite.dart | 315 ++++++ .../test/outline_extractor_tester.dart | 301 ++++++ .../test/spell_checking_list_code.txt | 14 + .../test/spell_checking_list_tests.txt | 21 + pkg/front_end/testing.json | 11 + .../direct_parser_ast_helper_creator.dart | 1 + .../tool/kernel_ast_file_rewriter.dart | 4 +- .../tool/parser_direct_ast/viewer.dart | 2 +- tools/package_deps/bin/package_deps.dart | 6 + 120 files changed, 3861 insertions(+), 9 deletions(-) create mode 100644 pkg/front_end/lib/src/fasta/util/abstracted_ast_nodes.dart create mode 100644 pkg/front_end/lib/src/fasta/util/outline_extractor.dart create mode 100644 pkg/front_end/outline_extraction_testcases/conditional_imports_exports/a.dart create mode 100644 pkg/front_end/outline_extraction_testcases/conditional_imports_exports/a2.dart create mode 100644 pkg/front_end/outline_extraction_testcases/conditional_imports_exports/b.dart create mode 100644 pkg/front_end/outline_extraction_testcases/conditional_imports_exports/b2.dart create mode 100644 pkg/front_end/outline_extraction_testcases/conditional_imports_exports/c.dart create mode 100644 pkg/front_end/outline_extraction_testcases/conditional_imports_exports/c2.dart create mode 100644 pkg/front_end/outline_extraction_testcases/conditional_imports_exports/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/conditional_imports_exports/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/exports_export_01/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/exports_export_01/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/exports_export_01/test10.dart create mode 100644 pkg/front_end/outline_extraction_testcases/exports_export_01/test8.dart create mode 100644 pkg/front_end/outline_extraction_testcases/exports_export_01/test9.dart create mode 100644 pkg/front_end/outline_extraction_testcases/exports_included/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/exports_included/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/exports_included/test6.dart create mode 100644 pkg/front_end/outline_extraction_testcases/exports_included/test7.dart create mode 100644 pkg/front_end/outline_extraction_testcases/extends/foo.dart create mode 100644 pkg/front_end/outline_extraction_testcases/extends/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/extends/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/factories/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/factories/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/factories/test11.dart create mode 100644 pkg/front_end/outline_extraction_testcases/field_dotting_in/b.dart create mode 100644 pkg/front_end/outline_extraction_testcases/field_dotting_in/c.dart create mode 100644 pkg/front_end/outline_extraction_testcases/field_dotting_in/d.dart create mode 100644 pkg/front_end/outline_extraction_testcases/field_dotting_in/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/field_dotting_in/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/fields/bar.dart create mode 100644 pkg/front_end/outline_extraction_testcases/fields/foo.dart create mode 100644 pkg/front_end/outline_extraction_testcases/fields/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/fields/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/import_prefix_overlap_with_field/test16.dart create mode 100644 pkg/front_end/outline_extraction_testcases/import_with_prefix/bar.dart create mode 100644 pkg/front_end/outline_extraction_testcases/import_with_prefix/foo.dart create mode 100644 pkg/front_end/outline_extraction_testcases/import_with_prefix/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/import_with_prefix/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/import_with_prefix_02/a.dart create mode 100644 pkg/front_end/outline_extraction_testcases/import_with_prefix_02/b.dart create mode 100644 pkg/front_end/outline_extraction_testcases/import_with_prefix_02/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/import_with_prefix_02/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/initial_various/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/initial_various/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/initial_various/test3.dart create mode 100644 pkg/front_end/outline_extraction_testcases/initial_various/test4.dart create mode 100644 pkg/front_end/outline_extraction_testcases/initial_various/test5.dart create mode 100644 pkg/front_end/outline_extraction_testcases/keeps_dart_version/bar.dart create mode 100644 pkg/front_end/outline_extraction_testcases/keeps_dart_version/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/keeps_dart_version/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/metadata_01/a.dart create mode 100644 pkg/front_end/outline_extraction_testcases/metadata_01/b.dart create mode 100644 pkg/front_end/outline_extraction_testcases/metadata_01/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/metadata_01/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/metadata_02/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/metadata_02/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/metadata_02/nottest15.dart create mode 100644 pkg/front_end/outline_extraction_testcases/metadata_02/test15.dart create mode 100644 pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/a.dart create mode 100644 pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/b.dart create mode 100644 pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/c.dart create mode 100644 pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/d.dart create mode 100644 pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/named_import_with_export_and_named_constructor_and_class_type_parameter/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/named_mixin/bar.dart create mode 100644 pkg/front_end/outline_extraction_testcases/named_mixin/baz.dart create mode 100644 pkg/front_end/outline_extraction_testcases/named_mixin/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/named_mixin/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/outline_extractor.status create mode 100644 pkg/front_end/outline_extraction_testcases/part_01/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/part_01/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/part_01/test12.dart create mode 100644 pkg/front_end/outline_extraction_testcases/part_01/test12_part1.dart create mode 100644 pkg/front_end/outline_extraction_testcases/part_01/test12_part2.dart create mode 100644 pkg/front_end/outline_extraction_testcases/part_01/test13.dart create mode 100644 pkg/front_end/outline_extraction_testcases/part_01/test13andahalf.dart create mode 100644 pkg/front_end/outline_extraction_testcases/part_01/test14.dart create mode 100644 pkg/front_end/outline_extraction_testcases/part_and_library_name/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/part_and_library_name/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/part_and_library_name/test3.dart create mode 100644 pkg/front_end/outline_extraction_testcases/part_and_library_name/test3_part.dart create mode 100644 pkg/front_end/outline_extraction_testcases/split_import_export_part/foo.dart create mode 100644 pkg/front_end/outline_extraction_testcases/split_import_export_part/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/split_import_export_part/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/split_import_export_part/part.dart create mode 100644 pkg/front_end/outline_extraction_testcases/type_parameter_extends/bar.dart create mode 100644 pkg/front_end/outline_extraction_testcases/type_parameter_extends/foo.dart create mode 100644 pkg/front_end/outline_extraction_testcases/type_parameter_extends/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/type_parameter_extends/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/type_parameter_on_extension/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/type_parameter_on_extension/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/unused_import/foo.dart create mode 100644 pkg/front_end/outline_extraction_testcases/unused_import/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/unused_import/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/unused_import_02/bar.dart create mode 100644 pkg/front_end/outline_extraction_testcases/unused_import_02/baz.dart create mode 100644 pkg/front_end/outline_extraction_testcases/unused_import_02/foo.dart create mode 100644 pkg/front_end/outline_extraction_testcases/unused_import_02/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/unused_import_02/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/use_of_imported_extension/foo.dart create mode 100644 pkg/front_end/outline_extraction_testcases/use_of_imported_extension/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/use_of_imported_extension/main.dart.outline_extracted create mode 100644 pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/foo.dart create mode 100644 pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/main.dart create mode 100644 pkg/front_end/outline_extraction_testcases/use_of_imported_extension_2/main.dart.outline_extracted create mode 100644 pkg/front_end/test/outline_extractor_suite.dart create mode 100644 pkg/front_end/test/outline_extractor_tester.dart diff --git a/pkg/front_end/analysis_options_no_lints.yaml b/pkg/front_end/analysis_options_no_lints.yaml index aca6b6124c0..cc832b879f0 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 00000000000..57ae59a0726 --- /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> 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 selfScope(); + + List? findInScope(String name) { + return scope[name] ?? parent?.findInScope(name); + } +} + +abstract class Container extends AstNode { + List _children = []; + Iterable get children => _children; + + void addChild(AstNode child, Map 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 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 entry in child.selfScope().entries) { + (scope[entry.key] ??= []).add(entry.value); + } + } + } + + @override + Map 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 entry in child.selfScope().entries) { + (scope[entry.key] ??= []).add(entry.value); + } + } + } + + @override + Map 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 entry in child.selfScope().entries) { + (scope[entry.key] ??= []).add(entry.value); + } + } + } + + @override + Map 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 entry in child.selfScope().entries) { + (scope[entry.key] ??= []).add(entry.value); + } + } + } + + @override + Map 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 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 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 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 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 selfScope() { + return {name: this}; + } +} + +class Enum extends AstNode { + @override + final DirectParserASTContentEnumEnd node; + final String name; + final List 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 selfScope() { + return {name: this}; + } +} + +class Import extends AstNode { + @override + final DirectParserASTContentImportEnd node; + final Uri firstUri; + final List? 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 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 selfScope() { + if (asName != null) { + return {asName!: this}; + } + return const {}; + } +} + +class Export extends AstNode { + @override + final DirectParserASTContentExportEnd node; + final Uri firstUri; + final List? conditionalUris; + @override + final Token startInclusive; + @override + final Token endInclusive; + + Export(this.node, this.firstUri, this.conditionalUris, this.startInclusive, + this.endInclusive); + + List 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 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 selfScope() { + return const {}; + } +} + +class TopLevelFields extends AstNode { + @override + final DirectParserASTContentTopLevelFieldsEnd node; + final List 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 selfScope() { + Map 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 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 selfScope() { + return {name: this}; + } +} + +class ClassFields extends AstNode { + @override + final DirectParserASTContentClassFieldsEnd node; + final List 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 selfScope() { + Map scope = {}; + for (String name in names) { + scope[name] = this; + } + return scope; + } +} + +class MixinFields extends AstNode { + @override + final DirectParserASTContentMixinFieldsEnd node; + final List 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 selfScope() { + Map scope = {}; + for (String name in names) { + scope[name] = this; + } + return scope; + } +} + +class ExtensionFields extends AstNode { + @override + final DirectParserASTContentExtensionFieldsEnd node; + final List 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 selfScope() { + Map 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 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 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 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 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 94a186563f8..a5bb20aecc3 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: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 rawBytes, bool includeComments: false, bool enableExtensionMethods: false, bool enableNonNullable: false, - bool enableTripleShift: false}) { + bool enableTripleShift: false, + List? languageVersionsSeen}) { Uint8List bytes = new Uint8List(rawBytes.length + 1); bytes.setRange(0, rawBytes.length, rawBytes); @@ -42,6 +51,7 @@ DirectParserASTContentCompilationUnitEnd getAST(List 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 getFieldIdentifiers() { - // For now blindly assume that the last count identifiers are the names - // of the fields. +extension MixinFieldsExtension on DirectParserASTContentMixinFieldsEnd { + List getFieldIdentifiers() { int countLeft = count; List? 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.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 getFieldIdentifiers() { + int countLeft = count; + List? 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.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 getFieldIdentifiers() { + int countLeft = count; + List? 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.filled( @@ -1056,6 +1111,190 @@ extension ClassFieldsExtension on DirectParserASTContentClassFieldsEnd { } } +extension EnumExtension on DirectParserASTContentEnumEnd { + List getIdentifiers() { + List ids = []; + for (DirectParserASTContent child in children!) { + if (child is DirectParserASTContentIdentifierHandle) ids.add(child); + } + return ids; + } +} + +extension ExtensionDeclarationExtension + on DirectParserASTContentExtensionDeclarationEnd { + List getIdentifiers() { + List 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? getConditionalImportUriStrings() { + List? 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? getConditionalExportUriStrings() { + List? 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 getFieldIdentifiers() { + int countLeft = count; + List? 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.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 getIdentifiers() { + List 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 getIdentifiers() { + List 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 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 2d73710105f..b0a26883891 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 @@ abstract class DirectParserASTContent { final DirectParserASTType type; Map get deprecatedArguments; List? 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 00000000000..e8ba2cc8d91 --- /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 main(List 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> extractOutline(List 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 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 parsed = {}; + + _Processor(this.verbosityLevel, this.fileSystem, this.uriTranslator); + + void log(String s) { + if (verbosityLevel <= 0) return; + print(s); + } + + Future 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 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 bytes2 = utf8.encode(outlined); + getAstStopwatch.start(); + List 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 _premarkTopLevel(List<_TopLevelAndAstNode> worklist, + Set 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> _preprocessImportsAsNeeded( + Map> imports, TopLevel topLevel) async { + List? 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 importsFromPart = + await _preprocessImportsAsNeeded(imports, part); + imported.addAll(importsFromPart); + } + } + } + } + return imported; + } + + Future> calculate(List entryPoints) async { + List<_TopLevelAndAstNode> worklist = []; + Map> imports = {}; + + // Mark all top-level in entry point. Also include parts and exports (and + // exports exports etc) of the entry point. + Set closed = {}; + for (TopLevel entryPoint in entryPoints) { + await _premarkTopLevel(worklist, closed, entryPoint); + } + + Map> lookupsAll = {}; + Map> 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? 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? 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 imported = + await _preprocessImportsAsNeeded(imports, entry.topLevel); + + Set? 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 lookupStrings = lookupsAll[other] ??= {}; + if (lookupStrings.add(identifier.token.lexeme)) { + List lookupStringsWorklist = + lookupsWorklist[other] ??= []; + lookupStringsWorklist.add(identifier.token.lexeme); + } + } + } + } else { + if (verbosityLevel >= 30) { + log("Ignoring ${identifier.token} as it's a " + "${identifier.context}"); + } + } + } + } + Map> lookupsWorklistTmp = {}; + for (MapEntry> 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 lookupStrings = lookupsAll[other] ??= {}; + for (String identifier in lookups.value) { + if (lookupStrings.add(identifier)) { + List lookupStringsWorklist = + lookupsWorklistTmp[other] ??= []; + lookupStringsWorklist.add(identifier); + } + } + } + } + + for (String identifier in lookups.value) { + List? 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 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 imported = {}; + for (MapEntry 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? findInScope( + String name, AstNode nearestAstNode, TopLevel topLevel, + {Set? visited}) { + List? 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 identifiers = []; + + void extract(DirectParserASTContent ast) { + if (ast is DirectParserASTContentIdentifierHandle) { + identifiers.add(ast); + } + List? 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 map = {}; + final int verbosityLevel; + final List 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 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 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 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 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? conditionalUriStrings = node.getConditionalExportUriStrings(); + List? 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 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? conditionalUriStrings = node.getConditionalImportUriStrings(); + List? 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 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 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 00000000000..4e6a6de6531 --- /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 00000000000..8c269883e62 --- /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 00000000000..4e6a6de6531 --- /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 00000000000..8c269883e62 --- /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 00000000000..4e6a6de6531 --- /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 00000000000..8c269883e62 --- /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 00000000000..bc74a472315 --- /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 00000000000..a516b91b8c5 --- /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 00000000000..997da89e139 --- /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 00000000000..1f5cc5849ff --- /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 00000000000..8513138af5e --- /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 00000000000..c83a90abff8 --- /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 00000000000..d0fcb3184e6 --- /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 00000000000..52091d76a68 --- /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 00000000000..c2b824dbeff --- /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 00000000000..c130634246f --- /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 00000000000..5b3e78d5573 --- /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 00000000000..4e6a6de6531 --- /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 00000000000..a126b2a15a8 --- /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 00000000000..09b74139c6e --- /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 00000000000..316b9c5ee25 --- /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 00000000000..4127b46729d --- /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 00000000000..105387cdf79 --- /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 00000000000..112fdbcde35 --- /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 00000000000..356bd56e178 --- /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 00000000000..8655cd4940d --- /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 00000000000..3d53be629d1 --- /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 00000000000..e9a8f51eaee --- /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 00000000000..79f980a4210 --- /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 00000000000..43434c33b17 --- /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 00000000000..bf52fd9b783 --- /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 00000000000..13e239ac122 --- /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 00000000000..3cfbb90e338 --- /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 00000000000..e0a42eb011d --- /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 00000000000..41d882e516b --- /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 00000000000..91b21444755 --- /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 00000000000..0ec48ddc019 --- /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 00000000000..aef942df8a7 --- /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 00000000000..1c804cb1532 --- /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 00000000000..ff840ec5c02 --- /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 00000000000..07f55dea258 --- /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 00000000000..73d125acbcd --- /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 00000000000..166189cf2e4 --- /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 00000000000..2ff0638580e --- /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 extends Bar with Qux1 implements Baz> { + Foo? parent; + + Foo() {} + Foo.bar() {} + F? fooMethod1() { + 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 00000000000..3426756fa96 --- /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 extends Bar with Qux1 implements Baz> { + Foo? parent; + Foo() {} + Foo.bar() {} + F? fooMethod1() {} + E? fooMethod2() {} + self.Foo? fooMethod3() {} + x fooMethod4() {} +} + + + + +org-dartlang-testcase:///test3.dart: +class Bar {} +class Baz {} +class Qux1 { + Qux1AndAHalf? qux1AndAHalf() {} +} +class Qux1AndAHalf {} +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 00000000000..530644ba0e1 --- /dev/null +++ b/pkg/front_end/outline_extraction_testcases/initial_various/test3.dart @@ -0,0 +1,25 @@ +import "test4.dart"; + +class Bar {} + +class Baz {} + +class Qux1 { + Qux1AndAHalf? qux1AndAHalf() { + // nothing... + } +} + +class Qux1AndAHalf {} + +class Qux2 { + 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 00000000000..3215ebf0bc0 --- /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 { + Qux4? foo() {} +} + +class Qux4 {} 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 00000000000..031573979e0 --- /dev/null +++ b/pkg/front_end/outline_extraction_testcases/initial_various/test5.dart @@ -0,0 +1,5 @@ +class Qux3x { + Qux4x? foo() {} +} + +class Qux4x {} 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 00000000000..62e4db2027a --- /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 00000000000..f20e3afbaaa --- /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 00000000000..9a0da03bc0f --- /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 00000000000..e7267c560a4 --- /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 00000000000..8b42bdafb46 --- /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 00000000000..a55d3df87b5 --- /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 00000000000..6d8ae632fdd --- /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 00000000000..851e2314693 --- /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 00000000000..123667f0a1e --- /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 00000000000..41594d9aa6c --- /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 00000000000..41594d9aa6c --- /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 00000000000..bde72507816 --- /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 00000000000..3a43d09b4ec --- /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 00000000000..ed37d133070 --- /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 { + const C.b(); +} + +class C2 { + 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 00000000000..40416ada2b6 --- /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 00000000000..afbc02d80b9 --- /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.b(); + var c2 = new foo.C2.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 00000000000..c1a663eb307 --- /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.b(); + var c2 = new foo.C2.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 { + const C.b(); +} +class C2 { + 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 00000000000..512eabb7825 --- /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 00000000000..f1e00d1589c --- /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 00000000000..4e0e374986f --- /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 00000000000..5ff64b368b8 --- /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 00000000000..e69de29bb2d 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 00000000000..1d7ff4efc84 --- /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 00000000000..ee384f734a7 --- /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 00000000000..07c621e54a8 --- /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 00000000000..44473fff933 --- /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 00000000000..413d50d2494 --- /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 00000000000..caa3ed4b197 --- /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 00000000000..16bc96bb444 --- /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 00000000000..023fefd6998 --- /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 00000000000..d9163f52f26 --- /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 00000000000..2d108c7018f --- /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 00000000000..8fdefdd1880 --- /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 00000000000..e6e3fc42ae2 --- /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 00000000000..4e6a6de6531 --- /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 00000000000..85d67221604 --- /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 00000000000..ed23c5a0a54 --- /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 00000000000..009eb8e4e16 --- /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 00000000000..3507a7f5d36 --- /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 00000000000..820b17e197d --- /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 00000000000..73b55650f1f --- /dev/null +++ b/pkg/front_end/outline_extraction_testcases/type_parameter_extends/main.dart @@ -0,0 +1,16 @@ +import "foo.dart"; + +class A { + T? aMethod1() {} + U? aMethod2() {} +} + +mixin A2 { + T? aMethod1() {} + U? aMethod2() {} +} + +extension A3 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 00000000000..8b4f0c5ca19 --- /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? aMethod1() {} + U? aMethod2() {} +} +mixin A2 { + T? aMethod1() {} + U? aMethod2() {} +} +extension A3 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 00000000000..5093237fd2a --- /dev/null +++ b/pkg/front_end/outline_extraction_testcases/type_parameter_on_extension/main.dart @@ -0,0 +1,5 @@ +class Foo {} + +extension HiExtension 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 00000000000..c561270c3f5 --- /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 {} +extension HiExtension 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 00000000000..4e6a6de6531 --- /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 00000000000..8713cd2366f --- /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 00000000000..aea942eb4d1 --- /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 00000000000..5db1c4264a1 --- /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 00000000000..f1e00d1589c --- /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 00000000000..4e6a6de6531 --- /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 00000000000..732d2c4709c --- /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 00000000000..5e0cec5a4cf --- /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 00000000000..9a990890b21 --- /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 00000000000..1b39ff1227d --- /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 00000000000..cb496190b4a --- /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 00000000000..bd036e73ab0 --- /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 00000000000..5deb533d33d --- /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 00000000000..1b7506a628b --- /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 00000000000..f022e0c93b0 --- /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 arguments = const []]) => + runMe(arguments, createContext, configurationPath: "../testing.json"); + +Future createContext( + Chain suite, Map environment) async { + const Set 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 steps = const [ + 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 { + const OutlineExtractorStep(); + + @override + String get name => "OutlineExtractorStep"; + + @override + Future> run( + TestDescription description, Context context) async { + Uri? packages = description.uri.resolve(".packages"); + if (!new File.fromUri(packages).existsSync()) { + packages = null; + } + Map 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 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( + ".outline_extracted", + actual, + description.uri, + description, + ); + } +} + +class CompileAndCompareStep + extends Step { + const CompileAndCompareStep(); + + @override + String get name => "CompileAndCompare"; + + @override + Future> run( + TestDescription description, Context context) async { + Uri? packages = description.uri.resolve(".packages"); + if (!new File.fromUri(packages).existsSync()) { + packages = null; + } + Map 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 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.pass(description); + } else { + print("Bad:"); + print(result); + return new Result.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 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 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 00000000000..941b5c08d42 --- /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 main(List 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 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 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 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 processUri(final List 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 processedFiles = await extractOutline(inputs, + packages: packageUri, target: target, platform: sdkSummary); + extractCompile.stop(); + print("Got ${processedFiles.keys.length} files " + "in ${stopwatch.elapsedMilliseconds} ms"); + + Set inputsSet = inputs.toSet(); + + Stopwatch plainCompile = new Stopwatch()..start(); + List 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 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 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 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 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 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 0f0d0b81fd8..9790926d90c 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 @@ codebase codec codes collision +coloring colorized com combinations @@ -399,6 +401,7 @@ enforcing engineered enhanced enters +entrypointish enumerates env eof @@ -458,6 +461,7 @@ field2 fieldformal file's filenames +fileuri finally's finv firsts @@ -689,6 +693,7 @@ leeway len lets letting +levels lex lexemes lf @@ -863,6 +868,7 @@ orphancy orphans ors os +outlined outputs outputting overlap @@ -894,6 +900,7 @@ paren parens parenteses particularly +partof patchup path patterns @@ -934,7 +941,9 @@ pre prebuilt preexisted preexisting +premark preorder +preprocess presented presubmit presumably @@ -945,6 +954,7 @@ printf println prioritization proc +processor producers product progresses @@ -1087,6 +1097,7 @@ robust role room rooted +rough roughly rounding roundtrip @@ -1201,6 +1212,7 @@ stmt stopgap stopped storage +story str strategies streak @@ -1388,6 +1400,7 @@ unshadowed unsortable unsound unsoundness +untouched unwrapper unwraps unwritten @@ -1398,6 +1411,7 @@ ur 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 bb52fe84af6..5927e037e9c 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 @@ method1d metric metrics mf +mfs micro minimize minimizer @@ -699,6 +700,7 @@ nondefault nonexisting noo noted +nottest numerator ob obool @@ -709,6 +711,7 @@ okay ol onull oo +oobar oocf ooo oovf @@ -783,6 +786,9 @@ quot quux quuz qux +qux1 +qux3x +qux4x r" r"\s r"k @@ -843,6 +849,7 @@ scroll sdkroot sdks secondary +secondtest12part1usage segment selection semifuzz @@ -925,8 +932,19 @@ t\u0008\f\u tails talk templates +test10 +test12 +test12part1usage +test13 +test13andahalf +test15thing +test16 +test16toplevel test3a test3b +test3partfoo +test8 +test9 theoretically thereof thread @@ -996,6 +1014,7 @@ waited waiting waits walt +wants warmup week weekly @@ -1014,8 +1033,10 @@ xrequired 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 3e12d2632e7..11fca520bd7 100644 --- a/pkg/front_end/testing.json +++ b/pkg/front_end/testing.json @@ -228,6 +228,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", 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 f368c901aaf..2cfa64c715e 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 @@ abstract class DirectParserASTContent { final DirectParserASTType type; Map get deprecatedArguments; List? 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 16f15e71ac1..d80b5c25b1c 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 @@ void main(List args) { } 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 @@ void processField( 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 aeee1b54481..edf1159df95 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 @@ void main(List args) { 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/package_deps/bin/package_deps.dart b/tools/package_deps/bin/package_deps.dart index d0a8abcdd02..38cca341c4f 100644 --- a/tools/package_deps/bin/package_deps.dart +++ b/tools/package_deps/bin/package_deps.dart @@ -321,6 +321,7 @@ class Package implements Comparable { // 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 @@ class Package implements Comparable { continue; } + // Skip 'pkg/front_end/outline_extraction_testcases'. + if (name == 'outline_extraction_testcases') { + continue; + } + if (!name.startsWith('.')) { _collectDartFiles(entity, files); }