Refactor qualified send sets.

Closes #23795

R=floitsch@google.com

Review URL: https://codereview.chromium.org//1293953006.
This commit is contained in:
Johnni Winther 2015-08-21 09:34:45 +02:00
parent b5966db69e
commit 9ce4da5e1a
7 changed files with 229 additions and 410 deletions

View file

@ -521,7 +521,7 @@ class Elements {
if (element == null) return !isClosureSend(send, element);
return isInstanceMethod(element) ||
isInstanceField(element) ||
send.isConditional;
(send.isConditional && !element.isStatic);
}
static bool isClosureSend(Send send, Element element) {
@ -660,12 +660,6 @@ class Elements {
return null;
}
static String mapToUserOperator(String op) {
String userOperator = mapToUserOperatorOrNull(op);
if (userOperator == null) throw 'Unhandled operator: $op';
else return userOperator;
}
static bool isNumberOrStringSupertype(Element element, Compiler compiler) {
LibraryElement coreLibrary = compiler.coreLibrary;
return (element == coreLibrary.find('Comparable'));

View file

@ -639,148 +639,6 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
return const NoneResult();
}
ResolutionResult resolveSend(Send node) {
Selector selector = resolveSelector(node, null);
if (node.isSuperCall) registry.registerSuperUse(node);
if (node.receiver == null) {
// If this send is of the form "assert(expr);", then
// this is an assertion.
if (selector.isAssert) {
internalError(node, "Unexpected assert: $node");
}
return node.selector.accept(this);
}
var oldCategory = allowedCategory;
allowedCategory |= ElementCategory.PREFIX | ElementCategory.SUPER;
bool oldSendIsMemberAccess = sendIsMemberAccess;
int oldAllowedCategory = allowedCategory;
// Conditional sends like `e?.foo` treat the receiver as an expression. So
// `C?.foo` needs to be treated like `(C).foo`, not like C.foo. Prefixes and
// super are not allowed on their own in that context.
if (node.isConditional) {
sendIsMemberAccess = false;
allowedCategory =
ElementCategory.VARIABLE |
ElementCategory.FUNCTION |
ElementCategory.IMPLIES_TYPE;
}
ResolutionResult resolvedReceiver = visit(node.receiver);
if (node.isConditional) {
sendIsMemberAccess = oldSendIsMemberAccess;
allowedCategory = oldAllowedCategory;
}
allowedCategory = oldCategory;
Element target;
String name = node.selector.asIdentifier().source;
if (identical(name, 'this')) {
error(node.selector, MessageKind.THIS_PROPERTY);
return const NoneResult();
} else if (node.isSuperCall) {
if (node.isOperator) {
if (isUserDefinableOperator(name)) {
name = selector.name;
} else {
error(node.selector, MessageKind.ILLEGAL_SUPER_SEND, {'name': name});
return const NoneResult();
}
}
if (!inInstanceContext) {
error(node.receiver, MessageKind.NO_INSTANCE_AVAILABLE, {'name': name});
return const NoneResult();
}
if (currentClass.supertype == null) {
// This is just to guard against internal errors, so no need
// for a real error message.
error(node.receiver, MessageKind.GENERIC,
{'text': "Object has no superclass"});
return const NoneResult();
}
// TODO(johnniwinther): Ensure correct behavior if currentClass is a
// patch.
target = currentClass.lookupSuperByName(selector.memberName);
// [target] may be null which means invoking noSuchMethod on
// super.
if (target == null) {
target = reportAndCreateErroneousElement(
node, name, MessageKind.NO_SUCH_SUPER_MEMBER,
{'className': currentClass.name, 'memberName': name});
// We still need to register the invocation, because we might
// call [:super.noSuchMethod:] which calls
// [JSInvocationMirror._invokeOn].
registry.registerDynamicInvocation(
new UniverseSelector(selector, null));
registry.registerSuperNoSuchMethod();
}
} else if (Elements.isUnresolved(resolvedReceiver.element)) {
return const NoneResult();
} else if (resolvedReceiver.element.isClass) {
ClassElement receiverClass = resolvedReceiver.element;
receiverClass.ensureResolved(compiler);
if (node.isOperator) {
// When the resolved receiver is a class, we can have two cases:
// 1) a static send: C.foo, or
// 2) an operator send, where the receiver is a class literal: 'C + 1'.
// The following code that looks up the selector on the resolved
// receiver will treat the second as the invocation of a static operator
// if the resolved receiver is not null.
return const NoneResult();
}
MembersCreator.computeClassMembersByName(
compiler, receiverClass.declaration, name);
target = receiverClass.lookupLocalMember(name);
if (target == null || target.isInstanceMember) {
registry.registerThrowNoSuchMethod();
// TODO(johnniwinther): With the simplified [TreeElements] invariant,
// try to resolve injected elements if [currentClass] is in the patch
// library of [receiverClass].
// TODO(karlklose): this should be reported by the caller of
// [resolveSend] to select better warning messages for getters and
// setters.
MessageKind kind = (target == null)
? MessageKind.MEMBER_NOT_FOUND
: MessageKind.MEMBER_NOT_STATIC;
return new ElementResult(reportAndCreateErroneousElement(
node, name, kind,
{'className': receiverClass.name, 'memberName': name}));
} else if (isPrivateName(name) &&
target.library != enclosingElement.library) {
registry.registerThrowNoSuchMethod();
return new ElementResult(reportAndCreateErroneousElement(
node, name, MessageKind.PRIVATE_ACCESS,
{'libraryName': target.library.getLibraryOrScriptName(),
'name': name}));
}
} else if (resolvedReceiver.element.isPrefix) {
PrefixElement prefix = resolvedReceiver.element;
target = prefix.lookupLocalMember(name);
if (Elements.isUnresolved(target)) {
registry.registerThrowNoSuchMethod();
return new ElementResult(reportAndCreateErroneousElement(
node, name, MessageKind.NO_SUCH_LIBRARY_MEMBER,
{'libraryName': prefix.name, 'memberName': name}));
} else if (target.isAmbiguous) {
registry.registerThrowNoSuchMethod();
AmbiguousElement ambiguous = target;
target = reportAndCreateErroneousElement(
node, name, ambiguous.messageKind, ambiguous.messageArguments);
ambiguous.diagnose(enclosingElement, compiler);
return new ElementResult(target);
} else if (target.kind == ElementKind.CLASS) {
ClassElement classElement = target;
classElement.ensureResolved(compiler);
}
}
return new ResolutionResult.forElement(target);
}
static Selector computeSendSelector(Send node,
LibraryElement library,
Element element) {
@ -900,47 +758,6 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
isValidAsConstant: isValidAsConstant);
}
void registerTypeLiteralAccess(Send node, Element target) {
// Set the type of the node to [Type] to mark this send as a
// type literal.
DartType type;
// TODO(johnniwinther): Remove this hack when we can pass more complex
// information between methods than resolved elements.
if (target == compiler.typeClass && node.receiver == null) {
// Potentially a 'dynamic' type literal.
type = registry.getType(node.selector);
}
if (type == null) {
if (target.isTypedef || target.isClass) {
TypeDeclarationElement typeDeclaration = target;
typeDeclaration.computeType(compiler);
type = typeDeclaration.rawType;
} else {
TypeVariableElement typeVariable = target;
type = typeVariable.type;
}
}
registry.registerTypeLiteral(node, type);
if (!target.isTypeVariable) {
// Don't try to make constants of calls and assignments to type literals.
if (!node.isCall && node.asSendSet() == null) {
analyzeConstantDeferred(node, enforceConst: false);
} else {
// The node itself is not a constant but we register the selector (the
// identifier that refers to the class/typedef) as a constant.
if (node.receiver != null) {
// This is a hack for the case of prefix.Type, we need to store
// the element on the selector, so [analyzeConstant] can build
// the type literal from the selector.
registry.useElement(node.selector, target);
}
analyzeConstantDeferred(node.selector, enforceConst: false);
}
}
}
/// Check that access to `super` is currently allowed. Returns an
/// [AccessSemantics] in case of an error, `null` otherwise.
AccessSemantics checkSuperAccess(Send node) {
@ -1947,6 +1764,29 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
node, name, new StaticAccess.unresolved(error));
}
/// Handle qualified update to an unresolved static class member, like
/// `a.b = c` or `a.b++` where `a` is a class and `b` is unresolved.
ResolutionResult handleUnresolvedStaticMemberUpdate(
SendSet node, Name name, ClassElement receiverClass) {
// TODO(johnniwinther): Share code with [handleStaticInstanceMemberUpdate]
// and [handlePrivateStaticMemberUpdate].
registry.registerThrowNoSuchMethod();
// TODO(johnniwinther): Produce a different error if [name] is resolves to
// a constructor.
// TODO(johnniwinther): With the simplified [TreeElements] invariant,
// try to resolve injected elements if [currentClass] is in the patch
// library of [receiverClass].
// TODO(johnniwinther): Produce a different error for complex update.
ErroneousElement error = reportAndCreateErroneousElement(
node, name.text, MessageKind.MEMBER_NOT_FOUND,
{'className': receiverClass.name, 'memberName': name.text});
// TODO(johnniwinther): Add an [AccessSemantics] for unresolved static
// member access.
return handleUpdate(node, name, new StaticAccess.unresolved(error));
}
/// Handle qualified access of an instance member, like `a.b` or `a.b()` where
/// `a` is a class and `b` is a non-static member.
ResolutionResult handleStaticInstanceMemberAccess(
@ -1970,8 +1810,28 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
node, name, new StaticAccess.unresolved(error));
}
/// Handle qualified update of an instance member, like `a.b = c` or `a.b++`
/// where `a` is a class and `b` is a non-static member.
ResolutionResult handleStaticInstanceMemberUpdate(
SendSet node, Name name, ClassElement receiverClass, Element member) {
registry.registerThrowNoSuchMethod();
// TODO(johnniwinther): With the simplified [TreeElements] invariant,
// try to resolve injected elements if [currentClass] is in the patch
// library of [receiverClass].
// TODO(johnniwinther): Produce a different error for complex update.
ErroneousElement error = reportAndCreateErroneousElement(
node, name.text, MessageKind.MEMBER_NOT_STATIC,
{'className': receiverClass.name, 'memberName': name});
// TODO(johnniwinther): Add an [AccessSemantics] for statically accessed
// instance members.
return handleUpdate(node, name, new StaticAccess.unresolved(error));
}
/// Handle qualified access of an inaccessible private static class member,
/// like `a._b` or `a.b()` where `a` is class, `_b` is static member of `a`
/// like `a._b` or `a._b()` where `a` is class, `_b` is static member of `a`
/// but `a` is not defined in the current library.
ResolutionResult handlePrivateStaticMemberAccess(
Send node, Name name, ClassElement receiverClass, Element member) {
@ -1986,6 +1846,21 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
node, name, new StaticAccess.unresolved(error));
}
/// Handle qualified update of an inaccessible private static class member,
/// like `a._b = c` or `a._b++` where `a` is class, `_b` is static member of
/// `a` but `a` is not defined in the current library.
ResolutionResult handlePrivateStaticMemberUpdate(
SendSet node, Name name, ClassElement receiverClass, Element member) {
registry.registerThrowNoSuchMethod();
ErroneousElement error = reportAndCreateErroneousElement(
node, name.text, MessageKind.PRIVATE_ACCESS,
{'libraryName': member.library.getLibraryOrScriptName(),
'name': name});
// TODO(johnniwinther): Add an [AccessSemantics] for unresolved static
// member access.
return handleUpdate(node, name, new StaticAccess.unresolved(error));
}
/// Handle qualified access to a static member, like `a.b` or `a.b()` where
/// `a` is a class and `b` is a static member of `a`.
ResolutionResult handleStaticMemberAccess(
@ -2020,6 +1895,31 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
}
}
/// Handle qualified update to a static member, like `a.b = c` or `a.b++`
/// where `a` is a class and `b` is a static member of `a`.
ResolutionResult handleStaticMemberUpdate(
Send node, Name memberName, ClassElement receiverClass) {
String name = memberName.text;
receiverClass.ensureResolved(compiler);
MembersCreator.computeClassMembersByName(
compiler, receiverClass.declaration, name);
Element member = receiverClass.lookupLocalMember(name);
if (member == null) {
return handleUnresolvedStaticMemberUpdate(
node, memberName, receiverClass);
} else if (member.isAmbiguous) {
return handleAmbiguousUpdate(node, memberName, member);
} else if (member.isInstanceMember) {
return handleStaticInstanceMemberUpdate(
node, memberName, receiverClass, member);
} else if (memberName.isPrivate && memberName.library != member.library) {
return handlePrivateStaticMemberUpdate(
node, memberName, receiverClass, member);
} else {
return handleStaticOrTopLevelUpdate(node, memberName, member);
}
}
/// Handle access to a type literal of type variable [element]. Like `T` or
/// `T()` where 'T' is type variable.
// TODO(johnniwinther): Remove [name] when [Selector] is not required for the
@ -2331,6 +2231,34 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
return result;
}
/// Handle qualified [SendSet] where the receiver resolves to a [prefix],
/// like `prefix.toplevelField = b` or `prefix.Class.staticField++` where
/// `prefix` is a library prefix.
ResolutionResult handleLibraryPrefixSendSet(
SendSet node, Name name, PrefixElement prefix) {
ResolutionResult result;
Element member = prefix.lookupLocalMember(name.text);
if (member == null) {
registry.registerThrowNoSuchMethod();
Element error = reportAndCreateErroneousElement(
node, name.text, MessageKind.NO_SUCH_LIBRARY_MEMBER,
{'libraryName': prefix.name, 'memberName': name});
return handleUpdate(node, name, new StaticAccess.unresolved(error));
} else {
result = handleResolvedSendSet(node, name, member);
}
if (result.kind == ResultKind.PREFIX) {
// [member] is a class prefix of a static access like `prefix.Class` of
// `prefix.Class.foo`. No need to call [handleDeferredAccess]; it will
// called on the parent `prefix.Class.foo` node.
result = new PrefixResult(prefix, result.element);
} else if (prefix.isDeferred &&
(member == null || !member.isDeferredLoaderGetter)) {
result = handleDeferredAccess(node, prefix, result);
}
return result;
}
/// Handle a [Send] that resolves to a [prefix]. Like `prefix` in
/// `prefix.Class` or `prefix` in `prefix()`, the latter being a compile time
/// error.
@ -2378,6 +2306,27 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
}
}
/// Handle qualified [SendSet] where the receiver resolves to an [Element],
/// like `a.b = c` where `a` is a prefix or a class.
ResolutionResult handlePrefixSendSet(
SendSet node, Name name, PrefixResult prefixResult) {
Element element = prefixResult.element;
if (element.isPrefix) {
if (node.isConditional) {
return handleLibraryPrefix(node, name, element);
} else {
return handleLibraryPrefixSendSet(node, name, element);
}
} else {
assert(element.isClass);
ResolutionResult result = handleStaticMemberUpdate(node, name, element);
if (prefixResult.isDeferred) {
result = handleDeferredAccess(node, prefixResult.prefix, result);
}
return result;
}
}
/// Handle dynamic access of [semantics].
ResolutionResult handleDynamicAccessSemantics(
Send node, Name name, AccessSemantics semantics) {
@ -2482,6 +2431,37 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
}
}
/// Handle a qualified [SendSet], that is where the receiver is non-null, like
/// `a.b = c`, `a.b++`, and `a.b += c`.
ResolutionResult handleQualifiedSendSet(SendSet node) {
Identifier selector = node.selector.asIdentifier();
String text = selector.source;
Name name = new Name(text, enclosingElement.library);
if (text == 'this') {
return handleQualifiedThisAccess(node, name);
} else if (node.receiver.isThis()) {
if (checkThisAccess(node)) {
return handleThisPropertyUpdate(node, name, null);
}
// TODO(johnniwinther): Handle invalid this access as an
// [AccessSemantics].
return const NoneResult();
}
ResolutionResult result = visitExpressionPrefix(node.receiver);
if (result.kind == ResultKind.PREFIX) {
return handlePrefixSendSet(node, name, result);
} else if (node.isConditional) {
return handleDynamicUpdateSemantics(
node, name, null, const DynamicAccess.ifNotNullProperty());
} else {
// Handle dynamic property access, like `a.b = c`, `a.b++` or `a.b += c`
// where `a` is not a prefix or class.
// TODO(johnniwinther): Use the `element` of [result].
return handleDynamicUpdateSemantics(
node, name, null, const DynamicAccess.dynamicProperty());
}
}
/// Handle access unresolved access to [name] in a non-instance context.
ResolutionResult handleUnresolvedAccess(
Send node, Name name, Element element) {
@ -3030,7 +3010,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
} else if (element.isStatic || element.isTopLevel) {
return handleStaticOrTopLevelUpdate(node, name, element);
}
return oldVisitSendSet(node);
return internalError(node, "Unexpected resolved send: $element");
}
/// Handle an unqualified [Send], that is where the `node.receiver` is null,
@ -3499,150 +3479,23 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
ResolutionResult visitSendSet(SendSet node) {
if (node.isIndex) {
// `a[b] = c`
if (node.isSuperCall) {
// `super[b] = c`
return handleSuperIndexSendSet(node);
} else {
return handleIndexSendSet(node);
}
} else if (node.isSuperCall) {
// `super.a = c`
return handleSuperSendSet(node);
} else if (node.receiver == null) {
// `a = c`
return handleUnqualifiedSendSet(node);
} else {
// `a.b = c`
return handleQualifiedSendSet(node);
}
return oldVisitSendSet(node);
}
ResolutionResult oldVisitSendSet(SendSet node) {
bool oldSendIsMemberAccess = sendIsMemberAccess;
sendIsMemberAccess = node.isPropertyAccess || node.isCall;
ResolutionResult result = resolveSend(node);
sendIsMemberAccess = oldSendIsMemberAccess;
Element target = result.element;
Element setter = target;
Element getter = target;
String operatorName = node.assignmentOperator.source;
String source = operatorName;
bool isComplex = !identical(source, '=');
if (!(result is AssertResult || Elements.isUnresolved(target))) {
if (target.isAbstractField) {
AbstractFieldElement field = target;
setter = field.setter;
getter = field.getter;
if (setter == null) {
if (!inInstanceContext || getter.isTopLevel || getter.isStatic) {
setter = reportAndCreateErroneousElement(node.selector, field.name,
MessageKind.CANNOT_RESOLVE_SETTER, const {});
registry.registerThrowNoSuchMethod();
}
}
if (isComplex && getter == null && !inInstanceContext) {
getter = reportAndCreateErroneousElement(node.selector, field.name,
MessageKind.CANNOT_RESOLVE_GETTER, const {});
registry.registerThrowNoSuchMethod();
}
} else if (target.impliesType) {
if (node.isIfNullAssignment) {
setter = reportAndCreateErroneousElement(node.selector, target.name,
MessageKind.IF_NULL_ASSIGNING_TYPE, const {});
// In this case, no assignment happens, the rest of the compiler can
// treat the expression `C ??= e` as if it's just reading `C`.
} else {
setter = reportAndCreateErroneousElement(node.selector, target.name,
MessageKind.ASSIGNING_TYPE, const {});
registry.registerThrowNoSuchMethod();
}
registerTypeLiteralAccess(node, target);
} else if (target.isFinal || target.isConst) {
if (Elements.isStaticOrTopLevelField(target) || target.isLocal) {
setter = reportAndCreateErroneousElement(
node.selector, target.name, MessageKind.CANNOT_RESOLVE_SETTER,
const {});
} else if (node.isSuperCall) {
setter = reportAndCreateErroneousElement(
node.selector, target.name, MessageKind.SETTER_NOT_FOUND_IN_SUPER,
{'name': target.name, 'className': currentClass.name});
registry.registerSuperNoSuchMethod();
} else {
// For instance fields we don't report a warning here because the type
// checker will detect this as well and report a better error message
// with the context of the containing class.
}
registry.registerThrowNoSuchMethod();
} else if (target.isFunction && target.name != '[]=') {
assert(!target.isSetter);
if (Elements.isStaticOrTopLevelFunction(target) || target.isLocal) {
setter = reportAndCreateErroneousElement(
node.selector, target.name, MessageKind.ASSIGNING_METHOD,
const {});
} else if (node.isSuperCall) {
setter = reportAndCreateErroneousElement(
node.selector, target.name, MessageKind.ASSIGNING_METHOD_IN_SUPER,
{'name': target.name,
'superclassName': target.enclosingClass.name});
registry.registerSuperNoSuchMethod();
} else {
// For instance methods we don't report a warning here because the
// type checker will detect this as well and report a better error
// message with the context of the containing class.
}
registry.registerThrowNoSuchMethod();
}
if (isPotentiallyMutableTarget(target)) {
registry.registerPotentialMutation(target, node);
if (enclosingElement != target.enclosingElement) {
registry.registerPotentialMutationInClosure(target, node);
}
for (Node scope in promotionScope) {
registry.registerPotentialMutationIn(scope, target, node);
}
}
}
resolveArguments(node.argumentsNode);
Selector selector = registry.getSelector(node);
if (isComplex) {
Selector getterSelector;
if (selector.isSetter) {
getterSelector = new Selector.getterFrom(selector);
} else {
assert(selector.isIndexSet);
getterSelector = new Selector.index();
}
registerSend(getterSelector, getter);
registry.setGetterSelectorInComplexSendSet(node, getterSelector);
if (node.isSuperCall) {
getter = currentClass.lookupSuperByName(getterSelector.memberName);
if (getter == null) {
target = reportAndCreateErroneousElement(
node, selector.name, MessageKind.NO_SUCH_SUPER_MEMBER,
{'className': currentClass.name, 'memberName': selector.name});
registry.registerSuperNoSuchMethod();
}
}
registry.useElement(node.selector, getter);
// Make sure we include the + and - operators if we are using
// the ++ and -- ones. Also, if op= form is used, include op itself.
void registerBinaryOperator(String name) {
Selector binop = new Selector.binaryOperator(name);
registry.registerDynamicInvocation(
new UniverseSelector(binop, null));
registry.setOperatorSelectorInComplexSendSet(node, binop);
}
if (identical(source, '++')) {
registerBinaryOperator('+');
registry.registerInstantiatedClass(compiler.intClass);
} else if (identical(source, '--')) {
registerBinaryOperator('-');
registry.registerInstantiatedClass(compiler.intClass);
} else if (source.endsWith('=')) {
registerBinaryOperator(Elements.mapToUserOperator(operatorName));
}
}
registerSend(selector, setter);
return new ResolutionResult.forElement(registry.useElement(node, setter));
}
void registerSend(Selector selector, Element target) {

View file

@ -3628,11 +3628,13 @@ class SsaBuilder extends ast.Visitor
Element element,
HInstruction value,
{ast.Node location}) {
assert(send == null || !Elements.isInstanceSend(send, elements));
if (location == null) {
assert(send != null);
location = send;
}
assert(invariant(location,
send == null || !Elements.isInstanceSend(send, elements),
message: "Unexpected non instance setter: $element."));
if (Elements.isStaticOrTopLevelField(element)) {
if (element.isSetter) {
pushInvokeStatic(location, element, <HInstruction>[value]);

View file

@ -1041,6 +1041,10 @@ class TypeCheckerVisitor extends Visitor<DartType> {
Element receiverElement = elements[node.receiver];
if (receiverElement != null) {
if (receiverElement.isPrefix) {
if (node.isConditional) {
// Skip cases like `prefix?.topLevel`.
return const DynamicAccess();
}
assert(invariant(node, element != null,
message: 'Prefixed node has no element.'));
return computeResolvedAccess(node, name, element, memberKind);

View file

@ -766,11 +766,6 @@ class Selector {
name.getter,
CallStructure.NO_ARGS);
factory Selector.getterFrom(Selector selector)
=> new Selector(SelectorKind.GETTER,
selector.memberName.getter,
CallStructure.NO_ARGS);
factory Selector.setter(Name name)
=> new Selector(SelectorKind.SETTER,
name.setter,

View file

@ -135,6 +135,16 @@ const Map<String, List<Test>> SEND_TESTS = const {
''',
const Visit(VisitKind.VISIT_UNRESOLVED_GET,
name: 'o')),
const Test(
'''
class C {
var o;
}
m() { C.o = 42; }
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
rhs: '42')),
const Test(
'''
class C {
@ -300,14 +310,13 @@ const Map<String, List<Test>> SEND_TESTS = const {
const Visit(VisitKind.ERROR_INVALID_INVOKE,
error: MessageKind.THIS_PROPERTY,
arguments: '(null,42)')),
// TODO(johnniwinther): Expect [VISIT_FINAL_STATIC_FIELD_SET] instead.
const Test(
'''
class C { static final o = 0; }
m() { C.o = 42; }
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_FINAL_STATIC_FIELD_SET,
element: 'field(C#o)',
rhs: '42')),
const Test.clazz(
'''
@ -326,8 +335,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
m() { C.o = 42; }
}
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_FINAL_STATIC_FIELD_SET,
element: 'field(C#o)',
rhs: '42')),
const Test.prefix(
'''
@ -336,16 +345,16 @@ const Map<String, List<Test>> SEND_TESTS = const {
}
''',
'm() { p.C.o = 42; }',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_FINAL_STATIC_FIELD_SET,
element: 'field(C#o)',
rhs: '42')),
const Test(
'''
class C { static const o = 0; }
m() { C.o = 42; }
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_FINAL_STATIC_FIELD_SET,
element: 'field(C#o)',
rhs: '42')),
const Test.clazz(
'''
@ -364,8 +373,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
m() { C.o = 42; }
}
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_FINAL_STATIC_FIELD_SET,
element: 'field(C#o)',
rhs: '42')),
const Test.prefix(
'''
@ -374,8 +383,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
}
''',
'm() { p.C.o = 42; }',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_FINAL_STATIC_FIELD_SET,
element: 'field(C#o)',
rhs: '42')),
],
'Static properties': const [
@ -416,14 +425,13 @@ const Map<String, List<Test>> SEND_TESTS = const {
'm() => p.C.o;',
const Visit(VisitKind.VISIT_STATIC_GETTER_GET,
element: 'getter(C#o)')),
// TODO(johnniwinther): Expected [VISIT_STATIC_GETTER_SET] instead.
const Test(
'''
class C { static get o => 42; }
m() { C.o = 42; }
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_STATIC_GETTER_SET,
element: 'getter(C#o)',
rhs: '42')),
const Test.clazz(
'''
@ -442,8 +450,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
m() { C.o = 42; }
}
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_STATIC_GETTER_SET,
element: 'getter(C#o)',
rhs: '42')),
const Test.prefix(
'''
@ -452,8 +460,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
}
''',
'm() { p.C.o = 42; }',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_STATIC_GETTER_SET,
element: 'getter(C#o)',
rhs: '42')),
const Test(
'''
@ -643,14 +651,13 @@ const Map<String, List<Test>> SEND_TESTS = const {
''',
const Visit(VisitKind.VISIT_STATIC_FUNCTION_GET,
element: 'function(C#o)')),
// TODO(johnniwinther): Expect [VISIT_STATIC_FUNCTION_SET] instead.
const Test(
'''
class C { static o(a, b) {} }
m() { C.o = 42; }
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_STATIC_FUNCTION_SET,
element: 'function(C#o)',
rhs: '42')),
const Test.clazz(
'''
@ -669,8 +676,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
m() { C.o = 42; }
}
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_STATIC_FUNCTION_SET,
element: 'function(C#o)',
rhs: '42')),
const Test.prefix(
'''
@ -679,8 +686,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
'''
m() { p.C.o = 42; }
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_STATIC_FUNCTION_SET,
element: 'function(C#o)',
rhs: '42')),
const Test(
'''
@ -796,7 +803,6 @@ const Map<String, List<Test>> SEND_TESTS = const {
const Visit(VisitKind.VISIT_TOP_LEVEL_FIELD_SET,
element: 'field(o)',
rhs: '42')),
// TODO(johnniwinther): Expect [VISIT_FINAL_TOP_LEVEL_FIELD_SET] instead.
const Test(
'''
final o = 0;
@ -810,8 +816,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
final o = 0;
''',
'm() { p.o = 42; }',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_FINAL_TOP_LEVEL_FIELD_SET,
element: 'field(o)',
rhs: '42')),
const Test(
'''
@ -826,8 +832,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
const o = 0;
''',
'm() { p.o = 42; }',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_FINAL_TOP_LEVEL_FIELD_SET,
element: 'field(o)',
rhs: '42')),
const Test(
'''
@ -851,6 +857,13 @@ const Map<String, List<Test>> SEND_TESTS = const {
''',
const Visit(VisitKind.VISIT_UNRESOLVED_GET,
name: 'o')),
const Test(
'''
m() { o = 42; }
''',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
rhs: '42')),
],
'Top level properties': const [
// Top level properties
@ -886,7 +899,6 @@ const Map<String, List<Test>> SEND_TESTS = const {
''',
const Visit(VisitKind.VISIT_TOP_LEVEL_SETTER_GET,
element: 'setter(o)')),
// TODO(johnniwinther): Expect [VISIT_TOP_LEVEL_GETTER_SET] instead.
const Test(
'''
get o => null;
@ -900,8 +912,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
get o => null;
''',
'm() { p.o = 42; }',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_TOP_LEVEL_GETTER_SET,
element: 'getter(o)',
rhs: '42')),
const Test(
'''
@ -1031,7 +1043,6 @@ const Map<String, List<Test>> SEND_TESTS = const {
const Visit(VisitKind.ERROR_INVALID_INVOKE,
error: MessageKind.PREFIX_AS_EXPRESSION,
arguments: '(null,42)')),
// TODO(johnniwinther): Expect [VISIT_TOP_LEVEL_FUNCTION_SET] instead.
const Test(
'''
o(a, b) {}
@ -1045,8 +1056,8 @@ const Map<String, List<Test>> SEND_TESTS = const {
o(a, b) {}
''',
'm() { p.o = 42; }',
const Visit(VisitKind.VISIT_UNRESOLVED_SET,
name: 'o',
const Visit(VisitKind.VISIT_TOP_LEVEL_FUNCTION_SET,
element: 'function(o)',
rhs: '42')),
],
'Dynamic properties': const [

View file

@ -18,46 +18,6 @@ try_catch_on_syntax_test/11: Fail # Issue 19823
call_function_apply_test: RuntimeError # Issue 23873
conditional_property_assignment_test/23: Fail # Issue 23795
conditional_property_assignment_test/24: Fail # Issue 23795
conditional_property_assignment_test/25: Fail # Issue 23795
conditional_property_assignment_test/26: Fail # Issue 23795
conditional_property_assignment_test/27: Fail # Issue 23795
conditional_property_assignment_test/28: Fail # Issue 23795
conditional_property_assignment_test/29: Fail # Issue 23795
conditional_property_assignment_test/30: Fail # Issue 23795
conditional_property_assignment_test/31: Fail # Issue 23795
conditional_property_assignment_test/32: Fail # Issue 23795
conditional_property_assignment_test/33: Fail # Issue 23795
conditional_property_assignment_test/34: Fail # Issue 23795
conditional_property_assignment_test/35: Fail # Issue 23795
conditional_property_increment_decrement_test/17: Fail # Issue 23795
conditional_property_increment_decrement_test/18: Fail # Issue 23795
conditional_property_increment_decrement_test/19: Fail # Issue 23795
conditional_property_increment_decrement_test/20: Fail # Issue 23795
conditional_property_increment_decrement_test/21: Fail # Issue 23795
conditional_property_increment_decrement_test/22: Fail # Issue 23795
conditional_property_increment_decrement_test/23: Fail # Issue 23795
conditional_property_increment_decrement_test/24: Fail # Issue 23795
conditional_property_increment_decrement_test/25: Fail # Issue 23795
conditional_property_increment_decrement_test/26: Fail # Issue 23795
conditional_property_increment_decrement_test/27: Fail # Issue 23795
conditional_property_increment_decrement_test/28: Fail # Issue 23795
conditional_property_increment_decrement_test/29: Fail # Issue 23795
conditional_property_increment_decrement_test/30: Fail # Issue 23795
conditional_property_increment_decrement_test/31: Fail # Issue 23795
conditional_property_increment_decrement_test/32: Fail # Issue 23795
conditional_property_increment_decrement_test/33: Fail # Issue 23795
conditional_property_increment_decrement_test/34: Fail # Issue 23795
conditional_property_increment_decrement_test/35: Fail # Issue 23795
conditional_property_increment_decrement_test/36: Fail # Issue 23795
conditional_property_increment_decrement_test/37: Fail # Issue 23795
conditional_property_increment_decrement_test/38: Fail # Issue 23795
conditional_property_increment_decrement_test/39: Fail # Issue 23795
conditional_property_increment_decrement_test/40: Fail # Issue 23795
if_null_assignment_behavior_test/31: Fail # Issue 23795
if_null_assignment_behavior_test/32: Fail # Issue 23795
[ $compiler == dart2js && $runtime == jsshell ]
await_for_test: Skip # Jsshell does not provide periodic timers, Issue 7728
async_star_test: RuntimeError # Jsshell does not provide non-zero timers, Issue 7728