Apply resolution to InstanceCreationExpression.

R=brianwilkerson@google.com

Bug:
Change-Id: I213f58fd8c71eb9933c13f696f6d1a407f4553bb
Reviewed-on: https://dart-review.googlesource.com/23727
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov 2017-11-27 03:00:51 +00:00 committed by commit-bot@chromium.org
parent ce477d7f93
commit cf4db2be5e
5 changed files with 296 additions and 38 deletions

View file

@ -458,11 +458,7 @@ class LibraryAnalyzer {
if (referencedNode is kernel.VariableDeclaration) {
element = declarationToElement[referencedNode];
assert(element != null);
} else if (referencedNode is kernel.Field) {
element = _kernelResynthesizer
.getElementFromCanonicalName(referencedNode.canonicalName);
assert(element != null);
} else if (referencedNode is kernel.Procedure) {
} else if (referencedNode is kernel.NamedNode) {
element = _kernelResynthesizer
.getElementFromCanonicalName(referencedNode.canonicalName);
assert(element != null);

View file

@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/standard_ast_factory.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
@ -94,8 +95,37 @@ class ResolutionApplier extends GeneralizingAstVisitor {
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
node.argumentList?.accept(this);
// TODO(paulberry): store resolution of node.constructorName.
node.staticType = _getTypeFor(node.constructorName);
ConstructorName constructorName = node.constructorName;
DartType type = _getTypeFor(constructorName);
ConstructorElement element = _getReferenceFor(constructorName);
ClassElement classElement = element?.enclosingElement;
node.staticElement = element;
node.staticType = type;
Identifier typeIdentifier = constructorName.type.name;
if (typeIdentifier is SimpleIdentifier) {
applyToTypeAnnotation(type, constructorName.type);
if (constructorName.name != null) {
constructorName.name.staticElement = element;
}
} else if (typeIdentifier is PrefixedIdentifier) {
// TODO(scheglov) Rewrite AST using knowledge about prefixes.
// TODO(scheglov) Add support for `new prefix.Type()`.
// TODO(scheglov) Add support for `new prefix.Type.name()`.
assert(constructorName.name == null);
constructorName.period = typeIdentifier.period;
constructorName.name = typeIdentifier.identifier;
SimpleIdentifier classNode = typeIdentifier.prefix;
constructorName.type = astFactory.typeName(classNode, null);
classNode.staticElement = classElement;
classNode.staticType = type;
constructorName.name.staticElement = element;
}
_associateArgumentsWithParameters(element, node.argumentList);
}
@override
@ -121,15 +151,18 @@ class ResolutionApplier extends GeneralizingAstVisitor {
@override
void visitMethodInvocation(MethodInvocation node) {
node.target?.accept(this);
ExecutableElement calleeElement = _getReferenceFor(node.methodName);
DartType invokeType = _getTypeFor(node.methodName);
node.staticInvokeType = invokeType;
node.methodName.staticType = invokeType;
// TODO(paulberry): store resolution of node.typeArguments.
// Apply resolution to arguments.
// Skip names of named arguments.
List<Expression> arguments = node.argumentList.arguments;
for (var argument in arguments) {
ArgumentList argumentList = node.argumentList;
for (var argument in argumentList.arguments) {
if (argument is NamedExpression) {
argument.expression.accept(this);
} else {
@ -137,32 +170,10 @@ class ResolutionApplier extends GeneralizingAstVisitor {
}
}
ExecutableElement calleeElement = _getReferenceFor(node.methodName);
node.methodName.staticElement = calleeElement;
node.staticType = _getTypeFor(node.argumentList);
node.staticType = _getTypeFor(argumentList);
// Associate arguments with parameters.
if (calleeElement != null) {
var correspondingParameters =
new List<ParameterElement>(arguments.length);
for (int i = 0; i < arguments.length; i++) {
var argument = arguments[i];
if (argument is NamedExpression) {
for (var parameter in calleeElement.parameters) {
SimpleIdentifier label = argument.name.label;
if (parameter.parameterKind == ParameterKind.NAMED &&
parameter.name == label.name) {
label.staticElement = parameter;
correspondingParameters[i] = parameter;
break;
}
}
} else {
correspondingParameters[i] = calleeElement.parameters[i];
}
}
node.argumentList.correspondingStaticParameters = correspondingParameters;
}
_associateArgumentsWithParameters(calleeElement, argumentList);
}
@override
@ -232,6 +243,34 @@ class ResolutionApplier extends GeneralizingAstVisitor {
}
}
/// Associate arguments of the [argumentList] with parameters of the
/// given [executable].
void _associateArgumentsWithParameters(
ExecutableElement executable, ArgumentList argumentList) {
if (executable != null) {
List<Expression> arguments = argumentList.arguments;
var correspondingParameters =
new List<ParameterElement>(arguments.length);
for (int i = 0; i < arguments.length; i++) {
var argument = arguments[i];
if (argument is NamedExpression) {
for (var parameter in executable.parameters) {
SimpleIdentifier label = argument.name.label;
if (parameter.parameterKind == ParameterKind.NAMED &&
parameter.name == label.name) {
label.staticElement = parameter;
correspondingParameters[i] = parameter;
break;
}
}
} else {
correspondingParameters[i] = executable.parameters[i];
}
}
argumentList.correspondingStaticParameters = correspondingParameters;
}
}
/// Return the element associated with the declaration represented by the
/// given [node].
Element _getDeclarationFor(AstNode node) {

View file

@ -30,6 +30,8 @@ class InstrumentedResolutionStorer extends ResolutionStorer {
final List<int> _declarationOffsets;
final List<int> _referenceOffsets;
final List<int> _typeOffsets;
final List<int> _deferredReferenceOffsets = [];
final List<int> _deferredTypeOffsets = [];
InstrumentedResolutionStorer(
@ -41,6 +43,14 @@ class InstrumentedResolutionStorer extends ResolutionStorer {
this._typeOffsets)
: super(declarations, references, types);
@override
void _deferReference(int offset) {
super._deferReference(offset);
if (_debug) {
_deferredReferenceOffsets.add(offset);
}
}
@override
void _deferType(int offset) {
super._deferType(offset);
@ -59,12 +69,12 @@ class InstrumentedResolutionStorer extends ResolutionStorer {
}
@override
void _recordReference(Node target, int offset) {
int _recordReference(Node target, int offset) {
if (_debug) {
print('Recording reference to $target for offset $offset');
}
_referenceOffsets.add(offset);
super._recordReference(target, offset);
return super._recordReference(target, offset);
}
@override
@ -77,6 +87,15 @@ class InstrumentedResolutionStorer extends ResolutionStorer {
return super._recordType(type, offset);
}
@override
void _replaceReference(Node reference) {
if (_debug) {
int offset = _deferredReferenceOffsets.removeLast();
print('Replacing reference $reference for offset $offset');
}
super._replaceReference(reference);
}
@override
void _replaceType(DartType type) {
if (_debug) {
@ -109,11 +128,33 @@ class ResolutionStorer extends TypeInferenceListener {
final List<Node> _references;
final List<DartType> _types;
/// Indices into [_references] which need to be filled in later.
final _deferredReferenceSlots = <int>[];
/// Indices into [_types] which need to be filled in later.
final _deferredTypeSlots = <int>[];
ResolutionStorer(this._declarations, this._references, this._types);
@override
bool constructorInvocationEnter(
InvocationExpression expression, DartType typeContext) {
return super.constructorInvocationEnter(expression, typeContext);
}
@override
void constructorInvocationExit(
InvocationExpression expression, DartType inferredType) {
if (expression is ConstructorInvocation) {
_recordReference(expression.target, expression.fileOffset);
} else if (expression is StaticInvocation) {
_recordReference(expression.target, expression.fileOffset);
} else {
throw new UnimplementedError('${expression.runtimeType}');
}
super.constructorInvocationExit(expression, inferredType);
}
/// Verifies that all deferred work has been completed.
void finished() {
assert(_deferredTypeSlots.isEmpty);
@ -143,6 +184,10 @@ class ResolutionStorer extends TypeInferenceListener {
@override
void methodInvocationBeforeArgs(Expression expression, bool isImplicitCall) {
if (!isImplicitCall) {
// When the invocation target is `VariableGet`, we record the target
// before arguments. To ensure this order for method invocations, we
// first record `null`, and then replace it on exit.
_deferReference(expression.fileOffset);
// We are visiting a method invocation like: a.f(args). We have visited a
// but we haven't visited the args yet.
//
@ -163,9 +208,9 @@ class ResolutionStorer extends TypeInferenceListener {
void methodInvocationExit(Expression expression, Arguments arguments,
bool isImplicitCall, Object interfaceMember, DartType inferredType) {
if (!isImplicitCall) {
_replaceReference(interfaceMember);
_replaceType(new MemberReferenceDartType(
interfaceMember as Member, arguments.types));
_recordReference(interfaceMember, expression.fileOffset);
}
_recordType(inferredType, arguments.fileOffset);
super.genericExpressionExit("methodInvocation", expression, inferredType);
@ -180,6 +225,10 @@ class ResolutionStorer extends TypeInferenceListener {
@override
bool staticInvocationEnter(
StaticInvocation expression, DartType typeContext) {
// When the invocation target is `VariableGet`, we record the target
// before arguments. To ensure this order for method invocations, we
// first record `null`, and then replace it on exit.
_deferReference(expression.fileOffset);
// We are visiting a static invocation like: f(args), and we haven't visited
// args yet.
//
@ -198,12 +247,12 @@ class ResolutionStorer extends TypeInferenceListener {
@override
void staticInvocationExit(
StaticInvocation expression, DartType inferredType) {
_replaceReference(expression.target);
// TODO(paulberry): get the actual callee function type from the inference
// engine
var calleeType = const DynamicType();
_replaceType(calleeType);
_recordType(inferredType, expression.arguments.fileOffset);
_recordReference(expression.target, expression.fileOffset);
super.genericExpressionExit("staticInvocation", expression, inferredType);
}
@ -240,6 +289,13 @@ class ResolutionStorer extends TypeInferenceListener {
}
}
/// Record `null` as the reference at the given [offset], and put the current
/// slot into the [_deferredReferenceSlots] stack.
void _deferReference(int offset) {
int slot = _recordReference(null, offset);
_deferredReferenceSlots.add(slot);
}
/// Record `null` as the type at the given [offset], and put the current
/// slot into the [_deferredTypeSlots] stack.
void _deferType(int offset) {
@ -251,8 +307,10 @@ class ResolutionStorer extends TypeInferenceListener {
_declarations.add(declaration);
}
void _recordReference(Node target, int offset) {
int _recordReference(Node target, int offset) {
int slot = _references.length;
_references.add(target);
return slot;
}
int _recordType(DartType type, int offset) {
@ -261,6 +319,11 @@ class ResolutionStorer extends TypeInferenceListener {
return slot;
}
void _replaceReference(Node reference) {
int slot = _deferredReferenceSlots.removeLast();
_references[slot] = reference;
}
void _replaceType(DartType type) {
int slot = _deferredTypeSlots.removeLast();
_types[slot] = type;

View file

@ -64,6 +64,157 @@ Future pumpEventQueue([int times = 5000]) {
*/
@reflectiveTest
class AnalysisDriverResolutionTest extends BaseAnalysisDriverTest {
test_apply_instanceCreation_noTypeArguments() async {
String content = r'''
class C {
C(int p);
C.named(int p);
}
var a = new C(1);
var b = new C.named(2);
''';
addTestFile(content);
AnalysisResult result = await driver.getResult(testFile);
CompilationUnit unit = result.unit;
ClassDeclaration cNode = unit.declarations[0];
ClassElement cElement = cNode.element;
ConstructorElement defaultConstructor = cElement.constructors[0];
ConstructorElement namedConstructor = cElement.constructors[1];
{
TopLevelVariableDeclaration aDeclaration = unit.declarations[1];
VariableDeclaration aNode = aDeclaration.variables.variables[0];
InstanceCreationExpression value = aNode.initializer;
expect(value.staticElement, defaultConstructor);
expect(value.staticType, cElement.type);
TypeName typeName = value.constructorName.type;
expect(typeName.typeArguments, isNull);
Identifier typeIdentifier = typeName.name;
expect(typeIdentifier.staticElement, cElement);
expect(typeIdentifier.staticType, cElement.type);
expect(value.constructorName.name, isNull);
Expression argument = value.argumentList.arguments[0];
expect(argument.staticParameterElement, defaultConstructor.parameters[0]);
}
{
TopLevelVariableDeclaration bDeclaration = unit.declarations[2];
VariableDeclaration bNode = bDeclaration.variables.variables[0];
InstanceCreationExpression value = bNode.initializer;
expect(value.staticElement, namedConstructor);
expect(value.staticType, cElement.type);
TypeName typeName = value.constructorName.type;
expect(typeName.typeArguments, isNull);
SimpleIdentifier typeIdentifier = typeName.name;
expect(typeIdentifier.staticElement, cElement);
expect(typeIdentifier.staticType, cElement.type);
SimpleIdentifier constructorName = value.constructorName.name;
expect(constructorName.staticElement, namedConstructor);
expect(constructorName.staticType, isNull);
Expression argument = value.argumentList.arguments[0];
expect(argument.staticParameterElement, namedConstructor.parameters[0]);
}
}
test_apply_instanceCreation_withTypeArguments() async {
String content = r'''
class C<K, V> {
C(K k, V v);
C.named(K k, V v);
}
var a = new C<int, double>(1, 2.3);
var b = new C<num, String>.named(4, 'five');
''';
addTestFile(content);
AnalysisResult result = await driver.getResult(testFile);
CompilationUnit unit = result.unit;
var typeProvider = unit.element.context.typeProvider;
ClassDeclaration cNode = unit.declarations[0];
ClassElement cElement = cNode.element;
ConstructorElement defaultConstructor = cElement.constructors[0];
ConstructorElement namedConstructor = cElement.constructors[1];
{
TopLevelVariableDeclaration aDeclaration = unit.declarations[1];
VariableDeclaration aNode = aDeclaration.variables.variables[0];
InstanceCreationExpression value = aNode.initializer;
InterfaceType instantiatedType = cElement.type
.instantiate([typeProvider.intType, typeProvider.doubleType]);
expect(value.staticElement, defaultConstructor);
expect(value.staticType, instantiatedType);
TypeName typeName = value.constructorName.type;
Identifier typeIdentifier = typeName.name;
expect(typeIdentifier.staticElement, cElement);
expect(typeIdentifier.staticType, instantiatedType);
TypeName typeArgument1 = typeName.typeArguments.arguments[0];
expect(typeArgument1.type, typeProvider.intType);
expect(typeArgument1.name.staticType, typeProvider.intType);
expect(typeArgument1.name.staticElement, typeProvider.intType.element);
TypeName typeArgument2 = typeName.typeArguments.arguments[1];
expect(typeArgument2.type, typeProvider.doubleType);
expect(typeArgument2.name.staticType, typeProvider.doubleType);
expect(typeArgument2.name.staticElement, typeProvider.doubleType.element);
expect(value.constructorName.name, isNull);
Expression argument = value.argumentList.arguments[0];
expect(argument.staticParameterElement, defaultConstructor.parameters[0]);
}
{
TopLevelVariableDeclaration bDeclaration = unit.declarations[2];
VariableDeclaration bNode = bDeclaration.variables.variables[0];
InstanceCreationExpression value = bNode.initializer;
InterfaceType instantiatedType = cElement.type
.instantiate([typeProvider.numType, typeProvider.stringType]);
expect(value.staticElement, namedConstructor);
expect(value.staticType, instantiatedType);
TypeName typeName = value.constructorName.type;
SimpleIdentifier typeIdentifier = typeName.name;
expect(typeIdentifier.staticElement, cElement);
expect(typeIdentifier.staticType, instantiatedType);
TypeName typeArgument1 = typeName.typeArguments.arguments[0];
expect(typeArgument1.type, typeProvider.numType);
expect(typeArgument1.name.staticType, typeProvider.numType);
expect(typeArgument1.name.staticElement, typeProvider.numType.element);
TypeName typeArgument2 = typeName.typeArguments.arguments[1];
expect(typeArgument2.type, typeProvider.stringType);
expect(typeArgument2.name.staticType, typeProvider.stringType);
expect(typeArgument2.name.staticElement, typeProvider.stringType.element);
SimpleIdentifier constructorName = value.constructorName.name;
expect(constructorName.staticElement, namedConstructor);
expect(constructorName.staticType, isNull);
Expression argument = value.argumentList.arguments[0];
expect(argument.staticParameterElement, namedConstructor.parameters[0]);
}
}
test_local_function() async {
addTestFile(r'''
void main() {

View file

@ -146,6 +146,7 @@ inference/future_union_downwards_generic_method_with_generic_return: Fail
inference/future_union_upwards_generic_methods: Fail
inference/generic_functions_return_typedef: Crash
inference/generic_methods_basic_downward_inference: Crash
inference/generic_methods_correctly_recognize_generic_upper_bound: Crash
inference/generic_methods_dart_math_min_max: Crash
inference/generic_methods_downwards_inference_fold: Crash
inference/generic_methods_handle_override_of_non_generic_with_generic: Crash
@ -246,6 +247,7 @@ inference/inferred_type_via_closure_type_depends_on_args: Crash
inference/inferred_type_via_closure_type_independent_of_args_field: Crash
inference/inferred_type_via_closure_type_independent_of_args_top_level: Crash
inference/inheritance_does_not_imply_circularity: Crash
inference/instantiate_to_bounds_invoke_constructor_type_args_exact: Crash
inference/lambda_does_not_have_propagated_type_hint: Crash
inference/lambda_void_context: Crash
inference/list_literal_typed: Crash
@ -308,6 +310,8 @@ inference/type_promotion_stopped_by_assignment_in_scope: Crash
inference/type_promotion_stopped_by_mutation_in_a_closure: Crash
inference/unresolved_super: Crash
inference/unsafe_block_closure_inference_closure_call: Crash
inference/unsafe_block_closure_inference_constructor_call_explicit_dynamic_param: Crash
inference/unsafe_block_closure_inference_constructor_call_explicit_type_param: Crash
inference/unsafe_block_closure_inference_function_call_explicit_dynamic_param: Crash
inference/unsafe_block_closure_inference_function_call_explicit_dynamic_param_via_expr1: Crash
inference/unsafe_block_closure_inference_function_call_explicit_dynamic_param_via_expr2: Crash
@ -370,11 +374,13 @@ local_generic_function: Crash
metadata_enum: Crash
metadata_named_mixin_application: Crash
micro: Crash
mixin: Crash
named_parameters: Crash
null_aware: Crash
operators: Crash
optional: Crash
override: Crash
qualified: Crash
rasta/abstract_constructor: Crash
rasta/bad_continue: Crash
rasta/bad_default_constructor: Crash
@ -384,6 +390,7 @@ rasta/breaking_bad: Crash
rasta/cascades: Crash
rasta/class_hierarchy: Crash
rasta/deferred_load: Crash
rasta/external_factory_redirection: Crash
rasta/for_loop: Crash
rasta/generic_factory: Crash
rasta/issue_000001: Crash
@ -459,6 +466,8 @@ runtime_checks/covariant_generic_parameter_in_interface: Crash
runtime_checks/covariant_generic_parameter_tear_off: Crash
runtime_checks/covariant_keyword: Crash
runtime_checks/covariant_setter: Crash
runtime_checks/dynamic_invocation: Crash
runtime_checks/dynamic_invocation_generic: Crash
runtime_checks/forwarding_stub_with_default_values: Crash
runtime_checks/implicit_downcast_assert_initializer: Crash
runtime_checks/implicit_downcast_assert_statement: Crash