Version 2.12.0-231.0.dev
Merge commit '989e42f18972918422c1b24ede86a6b2ae08ebf2' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
index 946989a..524cdaa 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
@@ -6,14 +6,11 @@
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
-import 'package:analysis_server/src/services/correction/fix/data_driven/element_kind.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_manager.dart';
-import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart' show LibraryElement;
-import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:meta/meta.dart';
@@ -36,16 +33,11 @@
importedUris.add(Uri.parse(uri));
}
}
- var components = _computeComponents();
- if (components == null) {
- // If we couldn't compute the components it's because the node doesn't
- // represent an element that can be transformed.
+ var matcher = ElementMatcher.forNode(node);
+ if (matcher == null) {
+ // The node doesn't represent an element that can be transformed.
return;
}
- var matcher = ElementMatcher(
- importedUris: importedUris,
- components: components,
- kinds: _kindsForNode(node));
for (var set in _availableTransformSetsForLibrary(library)) {
for (var transform
in set.transformsFor(matcher, applyingBulkFixes: applyingBulkFixes)) {
@@ -63,174 +55,6 @@
return TransformSetManager.instance.forLibrary(library);
}
- /// Return the components for the element associated with the given [node] by
- /// looking at the parent of the [node].
- List<String> _componentsFromParent(AstNode node) {
- var parent = node.parent;
- if (parent is ArgumentList) {
- parent = parent.parent;
- }
- if (parent is Annotation) {
- return [parent.constructorName?.name ?? '', parent.name.name];
- } else if (parent is ExtensionOverride) {
- return [parent.extensionName.name];
- } else if (parent is InstanceCreationExpression) {
- var constructorName = parent.constructorName;
- return [constructorName.name?.name ?? '', constructorName.type.name.name];
- } else if (parent is MethodInvocation) {
- var target = _nameOfTarget(parent.realTarget);
- if (target != null) {
- return [parent.methodName.name, target];
- }
- var ancestor = parent.parent;
- while (ancestor != null) {
- if (ancestor is ClassOrMixinDeclaration) {
- return [parent.methodName.name, ancestor.name.name];
- } else if (ancestor is ExtensionDeclaration) {
- return [parent.methodName.name, ancestor.name.name];
- }
- ancestor = ancestor.parent;
- }
- return [parent.methodName.name];
- } else if (parent is RedirectingConstructorInvocation) {
- var ancestor = parent.parent;
- if (ancestor is ConstructorDeclaration) {
- return [parent.constructorName?.name ?? '', ancestor.returnType.name];
- }
- } else if (parent is SuperConstructorInvocation) {
- var ancestor = parent.parent;
- if (ancestor is ConstructorDeclaration) {
- return [parent.constructorName?.name ?? '', ancestor.returnType.name];
- }
- }
- return null;
- }
-
- /// Return the components of the path of the element associated with the
- /// diagnostic. The components are ordered from the most local to the most
- /// global. For example, for a constructor this would be the name of the
- /// constructor followed by the name of the class in which the constructor is
- /// declared (with an empty string for the unnamed constructor).
- List<String> _computeComponents() {
- var node = this.node;
- if (node is SimpleIdentifier) {
- var parent = node.parent;
- if (parent is Label && parent.parent is NamedExpression) {
- // The parent of the named expression is an argument list. Because we
- // don't represent parameters as elements, the element we need to match
- // against is the invocation containing those arguments.
- return _componentsFromParent(parent.parent.parent);
- } else if (parent is TypeName && parent.parent is ConstructorName) {
- return ['', node.name];
- } else if (parent is MethodInvocation) {
- return _componentsFromParent(node);
- }
- return [node.name];
- } else if (node is PrefixedIdentifier) {
- var parent = node.parent;
- if (parent is TypeName && parent.parent is ConstructorName) {
- return ['', node.identifier.name];
- }
- return [node.identifier.name];
- } else if (node is ConstructorName) {
- return [node.name.name];
- } else if (node is NamedType) {
- return [node.name.name];
- } else if (node is TypeArgumentList) {
- return _componentsFromParent(node);
- } else if (node is ArgumentList) {
- return _componentsFromParent(node);
- } else if (node?.parent is ArgumentList) {
- return _componentsFromParent(node.parent);
- }
- return null;
- }
-
- List<ElementKind> _kindsForNode(AstNode node, {AstNode child}) {
- if (node is ConstructorName) {
- return const [ElementKind.constructorKind];
- } else if (node is ExtensionOverride) {
- return const [ElementKind.extensionKind];
- } else if (node is InstanceCreationExpression) {
- return const [ElementKind.constructorKind];
- } else if (node is Label) {
- var argumentList = node.parent.parent;
- return _kindsForNode(argumentList.parent, child: argumentList);
- } else if (node is MethodInvocation) {
- assert(child != null);
- if (node.target == child) {
- return const [
- ElementKind.classKind,
- ElementKind.enumKind,
- ElementKind.mixinKind
- ];
- } else if (node.realTarget != null) {
- return const [ElementKind.constructorKind, ElementKind.methodKind];
- }
- return const [
- ElementKind.classKind,
- ElementKind.extensionKind,
- ElementKind.functionKind,
- ElementKind.methodKind
- ];
- } else if (node is NamedType) {
- var parent = node.parent;
- if (parent is ConstructorName && parent.name == null) {
- return const [ElementKind.classKind, ElementKind.constructorKind];
- }
- return const [
- ElementKind.classKind,
- ElementKind.enumKind,
- ElementKind.mixinKind,
- ElementKind.typedefKind
- ];
- } else if (node is PrefixedIdentifier) {
- if (node.prefix == child) {
- return const [
- ElementKind.classKind,
- ElementKind.enumKind,
- ElementKind.extensionKind,
- ElementKind.mixinKind,
- ElementKind.typedefKind
- ];
- }
- return const [
- ElementKind.fieldKind,
- ElementKind.getterKind,
- ElementKind.setterKind
- ];
- } else if (node is PropertyAccess) {
- return const [ElementKind.getterKind, ElementKind.setterKind];
- } else if (node is SimpleIdentifier) {
- return _kindsForNode(node.parent, child: node);
- }
- return null;
- }
-
- /// Return the name of the class associated with the given [target].
- String _nameOfTarget(Expression target) {
- if (target is SimpleIdentifier) {
- var type = target.staticType;
- if (type != null) {
- if (type is InterfaceType) {
- return type.element.name;
- } else if (type.isDynamic) {
- // The name is likely to be undefined.
- return target.name;
- }
- return null;
- }
- return target.name;
- } else if (target != null) {
- var type = target.staticType;
- if (type is InterfaceType) {
- return type.element.name;
- }
- return null;
- }
- return null;
- }
-
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
static DataDriven newInstance() => DataDriven();
}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
index a938d2c..d07b075 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
@@ -4,6 +4,10 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/element_kind.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart'
+ show ClassElement, ExtensionElement;
+import 'package:analyzer/dart/element/type.dart';
import 'package:meta/meta.dart';
/// An object that can be used to determine whether an element is appropriate
@@ -89,4 +93,246 @@
}
return false;
}
+
+ /// Return an element matcher that will match the element that is, or should
+ /// be, associated with the given [node], or `null` if there is no appropriate
+ /// matcher for the node.
+ static ElementMatcher forNode(AstNode node) {
+ if (node == null) {
+ return null;
+ }
+ var importedUris = _importElementsForNode(node);
+ if (importedUris == null) {
+ return null;
+ }
+ var components = _componentsForNode(node);
+ if (components == null) {
+ return null;
+ }
+ return ElementMatcher(
+ importedUris: importedUris,
+ components: components,
+ kinds: _kindsForNode(node));
+ }
+
+ /// Return the components of the path of the element associated with the given
+ /// [node]. The components are ordered from the most local to the most global.
+ /// For example, for a constructor this would be the name of the constructor
+ /// followed by the name of the class in which the constructor is declared
+ /// (with an empty string for the unnamed constructor).
+ static List<String> _componentsForNode(AstNode node) {
+ if (node is SimpleIdentifier) {
+ var parent = node.parent;
+ if (parent is Label && parent.parent is NamedExpression) {
+ // The parent of the named expression is an argument list. Because we
+ // don't represent parameters as elements, the element we need to match
+ // against is the invocation containing those arguments.
+ return _componentsFromParent(parent.parent.parent);
+ } else if (parent is TypeName && parent.parent is ConstructorName) {
+ return ['', node.name];
+ } else if (parent is MethodDeclaration && node == parent.name) {
+ return [node.name];
+ } else if ((parent is MethodInvocation && node == parent.methodName) ||
+ (parent is PrefixedIdentifier && node == parent.identifier) ||
+ (parent is PropertyAccess && node == parent.propertyName)) {
+ return _componentsFromParent(node);
+ }
+ return _componentsFromIdentifier(node);
+ } else if (node is PrefixedIdentifier) {
+ var parent = node.parent;
+ if (parent is TypeName && parent.parent is ConstructorName) {
+ return ['', node.identifier.name];
+ }
+ return [node.identifier.name];
+ } else if (node is ConstructorName) {
+ return [node.name.name];
+ } else if (node is NamedType) {
+ return [node.name.name];
+ } else if (node is TypeArgumentList) {
+ return _componentsFromParent(node);
+ } else if (node is ArgumentList) {
+ return _componentsFromParent(node);
+ } else if (node?.parent is ArgumentList) {
+ return _componentsFromParent(node.parent);
+ }
+ return null;
+ }
+
+ /// Return the components associated with the [identifier] when there is no
+ /// contextual information.
+ static List<String> _componentsFromIdentifier(SimpleIdentifier identifier) {
+ var element = identifier.staticElement;
+ if (element == null) {
+ var parent = identifier.parent;
+ if (parent is AssignmentExpression && identifier == parent.leftHandSide) {
+ element = parent.writeElement;
+ }
+ }
+ if (element != null) {
+ var enclosingElement = element.enclosingElement;
+ if (enclosingElement is ClassElement ||
+ enclosingElement is ExtensionElement) {
+ return [identifier.name, enclosingElement.name];
+ }
+ }
+ return [identifier.name];
+ }
+
+ /// Return the components for the element associated with the given [node] by
+ /// looking at the parent of the [node].
+ static List<String> _componentsFromParent(AstNode node) {
+ var parent = node.parent;
+ if (parent is ArgumentList) {
+ parent = parent.parent;
+ }
+ if (parent is Annotation) {
+ return [parent.constructorName?.name ?? '', parent.name.name];
+ } else if (parent is ExtensionOverride) {
+ return [parent.extensionName.name];
+ } else if (parent is InstanceCreationExpression) {
+ var constructorName = parent.constructorName;
+ return [constructorName.name?.name ?? '', constructorName.type.name.name];
+ } else if (parent is MethodInvocation) {
+ var methodName = parent.methodName;
+ var targetName = _nameOfTarget(parent.realTarget);
+ if (targetName != null) {
+ return [methodName.name, targetName];
+ }
+ return _componentsFromIdentifier(methodName);
+ } else if (parent is PrefixedIdentifier) {
+ var identifier = parent.identifier;
+ var targetName = _nameOfTarget(parent.prefix);
+ if (targetName != null) {
+ return [identifier.name, targetName];
+ }
+ return _componentsFromIdentifier(identifier);
+ } else if (parent is PropertyAccess) {
+ var propertyName = parent.propertyName;
+ var targetName = _nameOfTarget(parent.realTarget);
+ if (targetName != null) {
+ return [propertyName.name, targetName];
+ }
+ return _componentsFromIdentifier(propertyName);
+ } else if (parent is RedirectingConstructorInvocation) {
+ var ancestor = parent.parent;
+ if (ancestor is ConstructorDeclaration) {
+ return [parent.constructorName?.name ?? '', ancestor.returnType.name];
+ }
+ } else if (parent is SuperConstructorInvocation) {
+ var ancestor = parent.parent;
+ if (ancestor is ConstructorDeclaration) {
+ return [parent.constructorName?.name ?? '', ancestor.returnType.name];
+ }
+ }
+ return null;
+ }
+
+ /// Return the URIs of the imports in the library containing the [node], or
+ /// `null` if the imports can't be determined.
+ static List<Uri> _importElementsForNode(AstNode node) {
+ var root = node.root;
+ if (root is! CompilationUnit) {
+ return null;
+ }
+ var importedUris = <Uri>[];
+ var library = (root as CompilationUnit).declaredElement.library;
+ for (var importElement in library.imports) {
+ // TODO(brianwilkerson) Filter based on combinators to help avoid making
+ // invalid suggestions.
+ var uri = importElement.importedLibrary?.source?.uri;
+ if (uri != null) {
+ // The [uri] is `null` if the literal string is not a valid URI.
+ importedUris.add(uri);
+ }
+ }
+ return importedUris;
+ }
+
+ /// Return the kinds of elements that could reasonably be referenced at the
+ /// location of the [node]. If [child] is no `null` then the [node] is a
+ /// parent of the original node.
+ static List<ElementKind> _kindsForNode(AstNode node, {AstNode child}) {
+ if (node is ConstructorName) {
+ return const [ElementKind.constructorKind];
+ } else if (node is ExtensionOverride) {
+ return const [ElementKind.extensionKind];
+ } else if (node is InstanceCreationExpression) {
+ return const [ElementKind.constructorKind];
+ } else if (node is Label) {
+ var argumentList = node.parent.parent;
+ return _kindsForNode(argumentList.parent, child: argumentList);
+ } else if (node is MethodInvocation) {
+ assert(child != null);
+ if (node.target == child) {
+ return const [
+ ElementKind.classKind,
+ ElementKind.enumKind,
+ ElementKind.mixinKind
+ ];
+ } else if (node.realTarget != null) {
+ return const [ElementKind.constructorKind, ElementKind.methodKind];
+ }
+ return const [
+ ElementKind.classKind,
+ ElementKind.extensionKind,
+ ElementKind.functionKind,
+ ElementKind.methodKind
+ ];
+ } else if (node is NamedType) {
+ var parent = node.parent;
+ if (parent is ConstructorName && parent.name == null) {
+ return const [ElementKind.classKind, ElementKind.constructorKind];
+ }
+ return const [
+ ElementKind.classKind,
+ ElementKind.enumKind,
+ ElementKind.mixinKind,
+ ElementKind.typedefKind
+ ];
+ } else if (node is PrefixedIdentifier) {
+ if (node.prefix == child) {
+ return const [
+ ElementKind.classKind,
+ ElementKind.enumKind,
+ ElementKind.extensionKind,
+ ElementKind.mixinKind,
+ ElementKind.typedefKind
+ ];
+ }
+ return const [
+ ElementKind.fieldKind,
+ ElementKind.getterKind,
+ ElementKind.setterKind
+ ];
+ } else if (node is PropertyAccess) {
+ return const [ElementKind.getterKind, ElementKind.setterKind];
+ } else if (node is SimpleIdentifier) {
+ return _kindsForNode(node.parent, child: node);
+ }
+ return null;
+ }
+
+ /// Return the name of the class associated with the given [target].
+ static String _nameOfTarget(Expression target) {
+ if (target is SimpleIdentifier) {
+ var type = target.staticType;
+ if (type != null) {
+ if (type is InterfaceType) {
+ return type.element.name;
+ } else if (type.isDynamic) {
+ // The name is likely to be undefined.
+ return target.name;
+ }
+ return null;
+ }
+ return target.name;
+ } else if (target != null) {
+ var type = target.staticType;
+ if (type is InterfaceType) {
+ return type.element.name;
+ }
+ return null;
+ }
+ return null;
+ }
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
new file mode 100644
index 0000000..89ec9da
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
@@ -0,0 +1,404 @@
+// 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:analysis_server/src/services/correction/fix/data_driven/element_kind.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
+import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'data_driven_test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ElementMatcherComponentAndKindTest);
+ defineReflectiveTests(ElementMatcherImportsTest);
+ });
+}
+
+abstract class AbstractElementMatcherTest extends DataDrivenFixProcessorTest {
+ void _assertMatcher(String search,
+ {List<String> expectedComponents,
+ List<ElementKind> expectedKinds,
+ List<String> expectedUris}) {
+ var node = findNodeAtString(search);
+ var matcher = ElementMatcher.forNode(node);
+ if (expectedUris != null) {
+ expect(matcher.importedUris,
+ unorderedEquals(expectedUris.map((uri) => Uri.parse(uri))));
+ }
+ if (expectedComponents != null) {
+ expect(matcher.components, expectedComponents);
+ }
+ if (expectedKinds != null) {
+ expect(matcher.validKinds, expectedKinds);
+ }
+ }
+}
+
+@reflectiveTest
+class ElementMatcherComponentAndKindTest extends AbstractElementMatcherTest {
+ /// The kinds that are expected where a getter or setter is allowed.
+ static List<ElementKind> accessorKinds = [
+ ElementKind.fieldKind,
+ ElementKind.getterKind,
+ ElementKind.setterKind,
+ ];
+
+ /// The kinds that are expected where an invocation is allowed.
+ static List<ElementKind> invocationKinds = [
+ ElementKind.classKind,
+ ElementKind.extensionKind,
+ ElementKind.functionKind,
+ ElementKind.methodKind,
+ ];
+
+ /// The kinds that are expected where a method or constructor is allowed.
+ static List<ElementKind> methodKinds = [
+ ElementKind.constructorKind,
+ ElementKind.methodKind
+ ];
+
+ /// The kinds that are expected where a type is allowed.
+ static List<ElementKind> typeKinds = [
+ ElementKind.classKind,
+ ElementKind.enumKind,
+ ElementKind.mixinKind,
+ ElementKind.typedefKind,
+ ];
+
+ @failingTest
+ Future<void> test_binaryExpression_resolved() async {
+ // This test fails because we don't yet support operators.
+ await resolveTestCode('''
+void f(int x, int y) {
+ x + y;
+}
+''');
+ _assertMatcher('+',
+ expectedComponents: ['+', 'int'],
+ expectedKinds: [ElementKind.methodKind]);
+ }
+
+ @failingTest
+ Future<void> test_binaryExpression_unresolved() async {
+ // This test fails because we don't yet support operators.
+ await resolveTestCode('''
+void f(C c1, C c2) {
+ c1 + c2;
+}
+class C {}
+''');
+ _assertMatcher('+',
+ expectedComponents: ['+', 'C'],
+ expectedKinds: [ElementKind.methodKind]);
+ }
+
+ Future<void> test_getter_withoutTarget_resolved() async {
+ await resolveTestCode('''
+class C {
+ String get g => '';
+ void m() {
+ g;
+ }
+}
+''');
+ _assertMatcher('g;', expectedComponents: ['g', 'C']);
+ }
+
+ Future<void> test_getter_withoutTarget_unresolved() async {
+ await resolveTestCode('''
+class C {
+ void m() {
+ foo;
+ }
+}
+''');
+ _assertMatcher('foo', expectedComponents: ['foo']);
+ }
+
+ Future<void> test_getter_withTarget_resolved() async {
+ await resolveTestCode('''
+void f(String s) {
+ s.length;
+}
+''');
+ _assertMatcher('length',
+ expectedComponents: ['length', 'String'], expectedKinds: accessorKinds);
+ }
+
+ Future<void> test_getter_withTarget_unresolved() async {
+ await resolveTestCode('''
+void f(String s) {
+ s.foo;
+}
+''');
+ _assertMatcher('foo',
+ expectedComponents: ['foo', 'String'], expectedKinds: accessorKinds);
+ }
+
+ Future<void> test_identifier_propertyAccess() async {
+ await resolveTestCode('''
+void f() {
+ s.length;
+}
+''');
+ // TODO(brianwilkerson) Several of these kinds don't seem to be appropriate,
+ // so we might want to narrow down the list.
+ _assertMatcher('s', expectedComponents: [
+ 's'
+ ], expectedKinds: [
+ ElementKind.classKind,
+ ElementKind.enumKind,
+ ElementKind.extensionKind,
+ ElementKind.mixinKind,
+ ElementKind.typedefKind,
+ ]);
+ }
+
+ Future<void> test_method_withoutTarget_resolved() async {
+ await resolveTestCode('''
+class C {
+ void m(int i) {}
+ void m2() {
+ m(0);
+ }
+}
+''');
+ _assertMatcher('m(0)',
+ expectedComponents: ['m', 'C'], expectedKinds: invocationKinds);
+ }
+
+ Future<void> test_method_withoutTarget_unresolved() async {
+ await resolveTestCode('''
+class C {
+ void m() {
+ foo();
+ }
+}
+''');
+ _assertMatcher('foo',
+ expectedComponents: ['foo'], expectedKinds: invocationKinds);
+ }
+
+ Future<void> test_method_withTarget_resolved() async {
+ await resolveTestCode('''
+void f(String s) {
+ s.substring(2);
+}
+''');
+ _assertMatcher('substring',
+ expectedComponents: ['substring', 'String'],
+ expectedKinds: methodKinds);
+ }
+
+ Future<void> test_method_withTarget_unresolved() async {
+ await resolveTestCode('''
+void f(String s) {
+ s.foo(2);
+}
+''');
+ _assertMatcher('foo',
+ expectedComponents: ['foo', 'String'], expectedKinds: methodKinds);
+ }
+
+ Future<void> test_setter_withoutTarget_resolved() async {
+ await resolveTestCode('''
+class C {
+ set s(String s) {}
+ void m() {
+ s = '';
+ }
+}
+''');
+ _assertMatcher('s =', expectedComponents: ['s', 'C']);
+ }
+
+ Future<void> test_setter_withoutTarget_unresolved() async {
+ await resolveTestCode('''
+class C {
+ void m() {
+ foo = '';
+ }
+}
+''');
+ _assertMatcher('foo', expectedComponents: ['foo']);
+ }
+
+ Future<void> test_setter_withTarget_resolved() async {
+ await resolveTestCode('''
+void f(C c) {
+ c.s = '';
+}
+class C {
+ set s(String s) {}
+}
+''');
+ _assertMatcher('s =',
+ expectedComponents: ['s', 'C'], expectedKinds: accessorKinds);
+ }
+
+ Future<void> test_setter_withTarget_unresolved() async {
+ await resolveTestCode('''
+void f(String s) {
+ s.foo = '';
+}
+''');
+ _assertMatcher('foo',
+ expectedComponents: ['foo', 'String'], expectedKinds: accessorKinds);
+ }
+
+ Future<void> test_type_field_resolved() async {
+ await resolveTestCode('''
+class C {
+ String s = '';
+}
+''');
+ _assertMatcher('String',
+ expectedComponents: ['String'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_field_unresolved() async {
+ await resolveTestCode('''
+class C {
+ Foo s = '';
+}
+''');
+ _assertMatcher('Foo',
+ expectedComponents: ['Foo'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_localVariable_resolved() async {
+ await resolveTestCode('''
+void f() {
+ String s = '';
+}
+''');
+ _assertMatcher('String',
+ expectedComponents: ['String'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_localVariable_unresolved() async {
+ await resolveTestCode('''
+void f() {
+ Foo s = '';
+}
+''');
+ _assertMatcher('Foo',
+ expectedComponents: ['Foo'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_method_resolved() async {
+ await resolveTestCode('''
+class C {
+ String m() => '';
+}
+''');
+ _assertMatcher('String',
+ expectedComponents: ['String'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_method_unresolved() async {
+ await resolveTestCode('''
+class C {
+ Foo m() => '';
+}
+''');
+ _assertMatcher('Foo',
+ expectedComponents: ['Foo'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_parameter_resolved() async {
+ await resolveTestCode('''
+void f(String s) {}
+''');
+ _assertMatcher('String',
+ expectedComponents: ['String'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_parameter_unresolved() async {
+ await resolveTestCode('''
+void f(Foo s) {}
+''');
+ _assertMatcher('Foo',
+ expectedComponents: ['Foo'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_topLevelFunction_resolved() async {
+ await resolveTestCode('''
+String f() => '';
+''');
+ _assertMatcher('String',
+ expectedComponents: ['String'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_topLevelFunction_unresolved() async {
+ await resolveTestCode('''
+Foo f() => '';
+''');
+ _assertMatcher('Foo',
+ expectedComponents: ['Foo'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_topLevelVariable_resolved() async {
+ await resolveTestCode('''
+String s = '';
+''');
+ _assertMatcher('String',
+ expectedComponents: ['String'], expectedKinds: typeKinds);
+ }
+
+ Future<void> test_type_topLevelVariable_unresolved() async {
+ await resolveTestCode('''
+Foo s = '';
+''');
+ _assertMatcher('Foo',
+ expectedComponents: ['Foo'], expectedKinds: typeKinds);
+ }
+}
+
+@reflectiveTest
+class ElementMatcherImportsTest extends AbstractElementMatcherTest {
+ Future<void> test_imports_noImports() async {
+ await resolveTestCode('''
+String s = '';
+''');
+ _assertMatcher('s', expectedUris: ['dart:core']);
+ }
+
+ Future<void> test_imports_package() async {
+ var packageRootPath = '$workspaceRootPath/other';
+ newFile('$packageRootPath/lib/other.dart', content: '');
+ writeTestPackageConfig(
+ config: PackageConfigFileBuilder()
+ ..add(name: 'other', rootPath: packageRootPath));
+
+ await resolveTestCode('''
+import 'package:other/other.dart';
+
+String s = '';
+''');
+ _assertMatcher('s',
+ expectedUris: ['dart:core', 'package:other/other.dart']);
+ }
+
+ Future<void> test_imports_relative() async {
+ addSource('$testPackageLibPath/a.dart', '');
+ await resolveTestCode('''
+import 'a.dart';
+
+String s = '';
+''');
+ _assertMatcher('s', expectedUris: ['dart:core', 'package:test/a.dart']);
+ }
+
+ Future<void> test_imports_sdkLibraries() async {
+ await resolveTestCode('''
+import 'dart:math';
+
+int f(int x, int y) => max(x, y);
+''');
+ _assertMatcher('f', expectedUris: ['dart:core', 'dart:math']);
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
index 4eb8242..22ee1a1 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
@@ -8,6 +8,7 @@
import 'code_fragment_parser_test.dart' as code_fragment_parser;
import 'code_template_test.dart' as code_template;
import 'diagnostics/test_all.dart' as diagnostics;
+import 'element_matcher_test.dart' as element_matcher;
import 'end_to_end_test.dart' as end_to_end;
import 'flutter_use_case_test.dart' as flutter_use_case;
import 'modify_parameters_test.dart' as modify_parameters;
@@ -22,6 +23,7 @@
code_fragment_parser.main();
code_template.main();
diagnostics.main();
+ element_matcher.main();
end_to_end.main();
flutter_use_case.main();
modify_parameters.main();
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index b3acb30..e629ce0 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -230,7 +230,19 @@
Set<Uri> invalidatedUris = this.invalidatedUris.toSet();
invalidateNotKeptUserBuilders(invalidatedUris);
ReusageResult reusedResult =
- computeReusedLibraries(invalidatedUris, uriTranslator);
+ computeReusedLibraries(invalidatedUris, uriTranslator, entryPoints);
+
+ // Use the reused libraries to re-write entry-points.
+ if (reusedResult.arePartsUsedAsEntryPoints()) {
+ for (int i = 0; i < entryPoints.length; i++) {
+ Uri entryPoint = entryPoints[i];
+ Uri redirect =
+ reusedResult.getLibraryUriForPartUsedAsEntryPoint(entryPoint);
+ if (redirect != null) {
+ entryPoints[i] = redirect;
+ }
+ }
+ }
// Experimental invalidation initialization (e.g. figure out if we can).
ExperimentalInvalidation experimentalInvalidation =
@@ -1440,18 +1452,26 @@
/// any saved component problems for such builders.
List<Library> computeTransitiveClosure(
List<Library> inputLibraries,
- List<Uri> entries,
+ List<Uri> entryPoints,
List<LibraryBuilder> reusedLibraries,
ClassHierarchy hierarchy,
UriTranslator uriTranslator,
Map<Uri, Source> uriToSource,
[List<Library> inputLibrariesFiltered]) {
List<Library> result = <Library>[];
+ Map<Uri, Uri> partUriToLibraryImportUri = <Uri, Uri>{};
Map<Uri, Library> libraryMap = <Uri, Library>{};
Map<Uri, Library> potentiallyReferencedLibraries = <Uri, Library>{};
Map<Uri, Library> potentiallyReferencedInputLibraries = <Uri, Library>{};
for (Library library in inputLibraries) {
libraryMap[library.importUri] = library;
+ if (library.parts.isNotEmpty) {
+ for (int partIndex = 0; partIndex < library.parts.length; partIndex++) {
+ LibraryPart part = library.parts[partIndex];
+ Uri partUri = getPartUri(library.importUri, part);
+ partUriToLibraryImportUri[partUri] = library.importUri;
+ }
+ }
if (library.importUri.scheme == "dart") {
result.add(library);
inputLibrariesFiltered?.add(library);
@@ -1460,9 +1480,6 @@
potentiallyReferencedInputLibraries[library.importUri] = library;
}
}
-
- List<Uri> worklist = <Uri>[];
- worklist.addAll(entries);
for (LibraryBuilder libraryBuilder in reusedLibraries) {
if (libraryBuilder.importUri.scheme == "dart" &&
!libraryBuilder.isSynthetic) {
@@ -1473,6 +1490,19 @@
libraryMap[libraryBuilder.importUri] = lib;
}
+ List<Uri> worklist = <Uri>[];
+ for (Uri entry in entryPoints) {
+ if (libraryMap.containsKey(entry)) {
+ worklist.add(entry);
+ } else {
+ // If the entry is a part redirect to the "main" entry.
+ Uri partTranslation = partUriToLibraryImportUri[entry];
+ if (partTranslation != null) {
+ worklist.add(partTranslation);
+ }
+ }
+ }
+
LibraryGraph graph = new LibraryGraph(libraryMap);
Set<Uri> partsUsed = new Set<Uri>();
while (worklist.isNotEmpty && potentiallyReferencedLibraries.isNotEmpty) {
@@ -1928,8 +1958,8 @@
}
/// Internal method.
- ReusageResult computeReusedLibraries(
- Set<Uri> invalidatedUris, UriTranslator uriTranslator) {
+ ReusageResult computeReusedLibraries(Set<Uri> invalidatedUris,
+ UriTranslator uriTranslator, List<Uri> entryPoints) {
Set<Uri> seenUris = new Set<Uri>();
List<LibraryBuilder> reusedLibraries = <LibraryBuilder>[];
for (int i = 0; i < platformBuilders.length; i++) {
@@ -1938,7 +1968,7 @@
reusedLibraries.add(builder);
}
if (userCode == null && userBuilders == null) {
- return new ReusageResult(const {}, const {}, false, reusedLibraries);
+ return new ReusageResult.reusedLibrariesOnly(reusedLibraries);
}
bool invalidatedBecauseOfPackageUpdate = false;
Set<LibraryBuilder> directlyInvalidated = new Set<LibraryBuilder>();
@@ -1946,6 +1976,7 @@
// Maps all non-platform LibraryBuilders from their import URI.
Map<Uri, LibraryBuilder> builders = <Uri, LibraryBuilder>{};
+ Map<Uri, LibraryBuilder> partUriToParent = <Uri, LibraryBuilder>{};
// Invalidated URIs translated back to their import URI (package:, dart:,
// etc.).
@@ -1989,7 +2020,10 @@
invalidatedImportUris.add(uri);
}
if (libraryBuilder is SourceLibraryBuilder) {
+ // TODO(jensj): This shouldn't be possible anymore.
for (LibraryBuilder part in libraryBuilder.parts) {
+ partUriToParent[part.importUri] = libraryBuilder;
+ partUriToParent[part.fileUri] = libraryBuilder;
if (isInvalidated(part.importUri, part.fileUri)) {
invalidatedImportUris.add(part.importUri);
builders[part.importUri] = part;
@@ -2000,6 +2034,8 @@
Uri partUri = getPartUri(libraryBuilder.importUri, part);
Uri fileUri = getPartFileUri(
libraryBuilder.library.fileUri, part, uriTranslator);
+ partUriToParent[partUri] = libraryBuilder;
+ partUriToParent[fileUri] = libraryBuilder;
if (isInvalidated(partUri, fileUri)) {
invalidatedImportUris.add(partUri);
@@ -2078,8 +2114,21 @@
reusedLibraries.add(builder);
}
- return new ReusageResult(notReusedLibraries, directlyInvalidated,
- invalidatedBecauseOfPackageUpdate, reusedLibraries);
+ ReusageResult result = new ReusageResult(
+ notReusedLibraries,
+ directlyInvalidated,
+ invalidatedBecauseOfPackageUpdate,
+ reusedLibraries);
+
+ for (Uri entryPoint in entryPoints) {
+ LibraryBuilder parent = partUriToParent[entryPoint];
+ if (reusedLibraries.contains(parent)) {
+ result.registerLibraryUriForPartUsedAsEntryPoint(
+ entryPoint, parent.importUri);
+ }
+ }
+
+ return result;
}
@override
@@ -2153,13 +2202,32 @@
final Set<LibraryBuilder> directlyInvalidated;
final bool invalidatedBecauseOfPackageUpdate;
final List<LibraryBuilder> reusedLibraries;
+ final Map<Uri, Uri> _reusedLibrariesPartsToParentForEntryPoints;
+
+ ReusageResult.reusedLibrariesOnly(this.reusedLibraries)
+ : notReusedLibraries = const {},
+ directlyInvalidated = const {},
+ invalidatedBecauseOfPackageUpdate = false,
+ _reusedLibrariesPartsToParentForEntryPoints = const {};
ReusageResult(this.notReusedLibraries, this.directlyInvalidated,
this.invalidatedBecauseOfPackageUpdate, this.reusedLibraries)
- : assert(notReusedLibraries != null),
+ : _reusedLibrariesPartsToParentForEntryPoints = {},
+ assert(notReusedLibraries != null),
assert(directlyInvalidated != null),
assert(invalidatedBecauseOfPackageUpdate != null),
assert(reusedLibraries != null);
+
+ void registerLibraryUriForPartUsedAsEntryPoint(
+ Uri entryPoint, Uri importUri) {
+ _reusedLibrariesPartsToParentForEntryPoints[entryPoint] = importUri;
+ }
+
+ bool arePartsUsedAsEntryPoints() =>
+ _reusedLibrariesPartsToParentForEntryPoints.isNotEmpty;
+
+ Uri getLibraryUriForPartUsedAsEntryPoint(Uri entryPoint) =>
+ _reusedLibrariesPartsToParentForEntryPoints[entryPoint];
}
class ExperimentalInvalidation {
diff --git a/pkg/front_end/test/incremental_load_from_dill_suite.dart b/pkg/front_end/test/incremental_load_from_dill_suite.dart
index 70a5360..84c74bc 100644
--- a/pkg/front_end/test/incremental_load_from_dill_suite.dart
+++ b/pkg/front_end/test/incremental_load_from_dill_suite.dart
@@ -831,18 +831,20 @@
}
if (!noFullComponent) {
- List<Library> entryLib = component.libraries
- .where((Library lib) =>
- entries.contains(lib.importUri) ||
- entries.contains(lib.fileUri))
- .toList();
- if (entryLib.length != entries.length) {
- return new Result<TestData>(
- data,
- UnexpectedEntryToLibraryCount,
- "Expected the entries to become libraries. "
- "Got ${entryLib.length} libraries for the expected "
- "${entries.length} entries.");
+ if (world["checkEntries"] != false) {
+ List<Library> entryLib = component.libraries
+ .where((Library lib) =>
+ entries.contains(lib.importUri) ||
+ entries.contains(lib.fileUri))
+ .toList();
+ if (entryLib.length != entries.length) {
+ return new Result<TestData>(
+ data,
+ UnexpectedEntryToLibraryCount,
+ "Expected the entries to become libraries. "
+ "Got ${entryLib.length} libraries for the expected "
+ "${entries.length} entries.");
+ }
}
}
if (compiler.initializedFromDill != expectInitializeFromDill) {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml
new file mode 100644
index 0000000..aba97dff
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml
@@ -0,0 +1,32 @@
+# 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.md file.
+
+type: newworld
+worlds:
+ - entry: main.dart
+ checkEntries: false
+ sources:
+ main.dart: |
+ part of 'lib.dart';
+ partMethod() {}
+ lib.dart: |
+ part 'main.dart';
+ main() {}
+ expectedLibraryCount: 1
+
+ - entry: main.dart
+ checkEntries: false
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - main.dart
+ expectedLibraryCount: 1
+
+ - entry: main.dart
+ checkEntries: false
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - lib.dart
+ expectedLibraryCount: 1
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml.world.1.expect
new file mode 100644
index 0000000..1d3c1ac
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml.world.1.expect
@@ -0,0 +1,7 @@
+main = lib::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+ part main.dart;
+ static method main() → dynamic {}
+ static method /* from org-dartlang-test:///main.dart */ partMethod() → dynamic {}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml.world.2.expect
new file mode 100644
index 0000000..1d3c1ac
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml.world.2.expect
@@ -0,0 +1,7 @@
+main = lib::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+ part main.dart;
+ static method main() → dynamic {}
+ static method /* from org-dartlang-test:///main.dart */ partMethod() → dynamic {}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml.world.3.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml.world.3.expect
new file mode 100644
index 0000000..1d3c1ac
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_entry.yaml.world.3.expect
@@ -0,0 +1,7 @@
+main = lib::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+ part main.dart;
+ static method main() → dynamic {}
+ static method /* from org-dartlang-test:///main.dart */ partMethod() → dynamic {}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml
new file mode 100644
index 0000000..e599398
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml
@@ -0,0 +1,34 @@
+# 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.md file.
+
+type: newworld
+worlds:
+ - entry: package:foo/main.dart
+ checkEntries: false
+ sources:
+ .packages: |
+ foo:.
+ main.dart: |
+ part of 'lib.dart';
+ partMethod() {}
+ lib.dart: |
+ part 'main.dart';
+ main() {}
+ expectedLibraryCount: 1
+
+ - entry: package:foo/main.dart
+ checkEntries: false
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - main.dart
+ expectedLibraryCount: 1
+
+ - entry: package:foo/main.dart
+ checkEntries: false
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - lib.dart
+ expectedLibraryCount: 1
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml.world.1.expect
new file mode 100644
index 0000000..c9c3c86
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml.world.1.expect
@@ -0,0 +1,7 @@
+main = lib::main;
+library from "package:foo/lib.dart" as lib {
+
+ part main.dart;
+ static method main() → dynamic {}
+ static method /* from org-dartlang-test:///main.dart */ partMethod() → dynamic {}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml.world.2.expect
new file mode 100644
index 0000000..c9c3c86
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml.world.2.expect
@@ -0,0 +1,7 @@
+main = lib::main;
+library from "package:foo/lib.dart" as lib {
+
+ part main.dart;
+ static method main() → dynamic {}
+ static method /* from org-dartlang-test:///main.dart */ partMethod() → dynamic {}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml.world.3.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml.world.3.expect
new file mode 100644
index 0000000..c9c3c86
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry.yaml.world.3.expect
@@ -0,0 +1,7 @@
+main = lib::main;
+library from "package:foo/lib.dart" as lib {
+
+ part main.dart;
+ static method main() → dynamic {}
+ static method /* from org-dartlang-test:///main.dart */ partMethod() → dynamic {}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml
new file mode 100644
index 0000000..3d91b09
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml
@@ -0,0 +1,37 @@
+# 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.md file.
+
+type: newworld
+worlds:
+ - entry: main.dart
+ warnings: true
+ checkEntries: false
+ sources:
+ .packages: |
+ foo:.
+ main.dart: |
+ part of 'lib.dart';
+ partMethod() {}
+ lib.dart: |
+ part 'main.dart';
+ main() {}
+ expectedLibraryCount: 1
+
+ - entry: main.dart
+ warnings: true
+ checkEntries: false
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - main.dart
+ expectedLibraryCount: 1
+
+ - entry: main.dart
+ warnings: true
+ checkEntries: false
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - lib.dart
+ expectedLibraryCount: 1
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml.world.1.expect
new file mode 100644
index 0000000..e826847
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml.world.1.expect
@@ -0,0 +1,12 @@
+main = lib::main;
+//
+// Problems in component:
+//
+// org-dartlang-test:///main.dart: Warning: Interpreting this as package URI, 'package:foo/main.dart'.
+//
+library from "package:foo/lib.dart" as lib {
+
+ part main.dart;
+ static method main() → dynamic {}
+ static method /* from org-dartlang-test:///main.dart */ partMethod() → dynamic {}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml.world.2.expect
new file mode 100644
index 0000000..e826847
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml.world.2.expect
@@ -0,0 +1,12 @@
+main = lib::main;
+//
+// Problems in component:
+//
+// org-dartlang-test:///main.dart: Warning: Interpreting this as package URI, 'package:foo/main.dart'.
+//
+library from "package:foo/lib.dart" as lib {
+
+ part main.dart;
+ static method main() → dynamic {}
+ static method /* from org-dartlang-test:///main.dart */ partMethod() → dynamic {}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml.world.3.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml.world.3.expect
new file mode 100644
index 0000000..e826847
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/part_as_package_entry_2.yaml.world.3.expect
@@ -0,0 +1,12 @@
+main = lib::main;
+//
+// Problems in component:
+//
+// org-dartlang-test:///main.dart: Warning: Interpreting this as package URI, 'package:foo/main.dart'.
+//
+library from "package:foo/lib.dart" as lib {
+
+ part main.dart;
+ static method main() → dynamic {}
+ static method /* from org-dartlang-test:///main.dart */ partMethod() → dynamic {}
+}
diff --git a/tools/VERSION b/tools/VERSION
index e955a98..61c91e3 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 230
+PRERELEASE 231
PRERELEASE_PATCH 0
\ No newline at end of file