[cfe] Correctly resolve extension properties in face of getter/setter conflict

Change-Id: I0b818edb27de1b2cd1dcd2e046f42616cd7cb257
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/117729
Reviewed-by: Jens Johansen <jensj@google.com>
This commit is contained in:
Johnni Winther 2019-09-24 07:55:45 +00:00
parent 71896d5586
commit 2be1d090ca
7 changed files with 359 additions and 14 deletions

View file

@ -103,6 +103,9 @@ abstract class LibraryBuilder extends ModifierBuilder {
/// Returns the [Library] built by this builder.
Library get library;
/// Returns the import uri for the library.
///
/// This is the canonical uri for the library, for instance 'dart:core'.
Uri get uri;
Iterator<Builder> get iterator {

View file

@ -717,13 +717,23 @@ abstract class TypeInferrerImpl extends TypeInferrer {
}
if (target.isUnresolved && includeExtensionMethods) {
Member otherMember =
_getInterfaceMember(classNode, name, !setter, fileOffset);
if (otherMember != null) {
// If we're looking for `foo` and `foo=` can be found or vice-versa then
// extension methods should not be found.
return target;
}
ExtensionAccessCandidate bestSoFar;
List<ExtensionAccessCandidate> noneMoreSpecific = [];
library.scope.forEachExtension((ExtensionBuilder extensionBuilder) {
MemberBuilder memberBuilder =
extensionBuilder.lookupLocalMember(name.name, setter: setter);
if (memberBuilder != null && !memberBuilder.isStatic) {
Extension extension = extensionBuilder.extension;
MemberBuilder getterBuilder =
extensionBuilder.lookupLocalMember(name.name, setter: false);
MemberBuilder setterBuilder =
extensionBuilder.lookupLocalMember(name.name, setter: true);
if ((getterBuilder != null && !getterBuilder.isStatic) ||
(setterBuilder != null && !setterBuilder.isStatic)) {
DartType onType;
DartType onTypeInstantiateToBounds;
List<DartType> inferredTypeArguments;
@ -762,15 +772,20 @@ abstract class TypeInferrerImpl extends TypeInferrer {
}
if (typeSchemaEnvironment.isSubtypeOf(receiverType, onType)) {
MemberBuilder memberBuilder =
setter ? setterBuilder : getterBuilder;
ExtensionAccessCandidate candidate = new ExtensionAccessCandidate(
extension,
onType,
onTypeInstantiateToBounds,
new ObjectAccessTarget.extensionMember(
memberBuilder.procedure,
memberBuilder.extensionTearOff,
memberBuilder.kind,
inferredTypeArguments));
memberBuilder != null
? new ObjectAccessTarget.extensionMember(
memberBuilder.procedure,
memberBuilder.extensionTearOff,
memberBuilder.kind,
inferredTypeArguments)
: const ObjectAccessTarget.missing(),
isPlatform: extensionBuilder.library.uri.scheme == 'dart');
if (noneMoreSpecific.isNotEmpty) {
bool isMostSpecific = true;
for (ExtensionAccessCandidate other in noneMoreSpecific) {
@ -2839,11 +2854,11 @@ class ExtensionAccessCandidate {
final bool isPlatform;
final DartType onType;
final DartType onTypeInstantiateToBounds;
final ExtensionAccessTarget target;
final ObjectAccessTarget target;
ExtensionAccessCandidate(Extension extension, this.onType,
this.onTypeInstantiateToBounds, this.target)
: isPlatform = extension.enclosingLibrary.importUri.scheme == 'dart';
ExtensionAccessCandidate(
this.onType, this.onTypeInstantiateToBounds, this.target,
{this.isPlatform});
bool isMoreSpecificThan(TypeSchemaEnvironment typeSchemaEnvironment,
ExtensionAccessCandidate other) {

View file

@ -0,0 +1,42 @@
// Copyright (c) 2019, 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.
class Class {
int get m1 => 0;
void set m2(int x) {}
}
extension Extension0 on Class {
void set m1(int x) {}
int get m2 => 0;
void set m3(int x) {}
int get m4 => 0;
}
extension Extension1 on Class {
int get m3 => 0;
void set m4(int x) {}
}
main() {
var c = new Class();
expect(0, c.m1);
c.m2 = 2;
}
errors() {
var c = new Class();
expect(0, c.m2);
c.m1 = 2;
c.m3;
c.m3 = 2;
c.m4;
c.m4 = 2;
}
expect(expected, actual) {
if (expected != actual) {
throw 'Mismatch: expected=$expected, actual=$actual';
}
}

View file

@ -0,0 +1,40 @@
library;
import self as self;
import "dart:core" as core;
class Class extends core::Object {
synthetic constructor •() → self::Class*
;
get m1() → core::int*
;
set m2(core::int* x) → void
;
}
extension Extension0 on self::Class* {
get m2 = self::Extension0|get#m2;
get m4 = self::Extension0|get#m4;
set m1 = self::Extension0|set#m1;
set m3 = self::Extension0|set#m3;
}
extension Extension1 on self::Class* {
get m3 = self::Extension1|get#m3;
set m4 = self::Extension1|set#m4;
}
static method Extension0|set#m1(final self::Class* #this, core::int* x) → void
;
static method Extension0|get#m2(final self::Class* #this) → core::int*
;
static method Extension0|set#m3(final self::Class* #this, core::int* x) → void
;
static method Extension0|get#m4(final self::Class* #this) → core::int*
;
static method Extension1|get#m3(final self::Class* #this) → core::int*
;
static method Extension1|set#m4(final self::Class* #this, core::int* x) → void
;
static method main() → dynamic
;
static method errors() → dynamic
;
static method expect(dynamic expected, dynamic actual) → dynamic
;

View file

@ -0,0 +1,122 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:30:15: Error: The getter 'm2' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'm2'.
// expect(0, c.m2);
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:31:5: Error: The setter 'm1' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'm1'.
// c.m1 = 2;
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:32:5: Error: The getter 'm3' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'm3'.
// c.m3;
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:33:5: Error: The setter 'm3' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'm3'.
// c.m3 = 2;
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:34:5: Error: The getter 'm4' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'm4'.
// c.m4;
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:35:5: Error: The setter 'm4' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'm4'.
// c.m4 = 2;
// ^^
//
import self as self;
import "dart:core" as core;
class Class extends core::Object {
synthetic constructor •() → self::Class*
: super core::Object::•()
;
get m1() → core::int*
return 0;
set m2(core::int* x) → void {}
}
extension Extension0 on self::Class* {
get m2 = self::Extension0|get#m2;
get m4 = self::Extension0|get#m4;
set m1 = self::Extension0|set#m1;
set m3 = self::Extension0|set#m3;
}
extension Extension1 on self::Class* {
get m3 = self::Extension1|get#m3;
set m4 = self::Extension1|set#m4;
}
static method Extension0|set#m1(final self::Class* #this, core::int* x) → core::int* {
final core::int* #t1 = x;
return #t1;
}
static method Extension0|get#m2(final self::Class* #this) → core::int*
return 0;
static method Extension0|set#m3(final self::Class* #this, core::int* x) → core::int* {
final core::int* #t2 = x;
return #t2;
}
static method Extension0|get#m4(final self::Class* #this) → core::int*
return 0;
static method Extension1|get#m3(final self::Class* #this) → core::int*
return 0;
static method Extension1|set#m4(final self::Class* #this, core::int* x) → core::int* {
final core::int* #t3 = x;
return #t3;
}
static method main() → dynamic {
self::Class* c = new self::Class::•();
self::expect(0, c.{self::Class::m1});
c.{self::Class::m2} = 2;
}
static method errors() → dynamic {
self::Class* c = new self::Class::•();
self::expect(0, invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:30:15: Error: The getter 'm2' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'm2'.
expect(0, c.m2);
^^");
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:31:5: Error: The setter 'm1' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'm1'.
c.m1 = 2;
^^";
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:32:5: Error: The getter 'm3' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'm3'.
c.m3;
^^";
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:33:5: Error: The setter 'm3' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'm3'.
c.m3 = 2;
^^";
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:34:5: Error: The getter 'm4' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'm4'.
c.m4;
^^";
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:35:5: Error: The setter 'm4' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'm4'.
c.m4 = 2;
^^";
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!expected.{core::Object::==}(actual)) {
throw "Mismatch: expected=${expected}, actual=${actual}";
}
}

View file

@ -0,0 +1,122 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:30:15: Error: The getter 'm2' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'm2'.
// expect(0, c.m2);
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:31:5: Error: The setter 'm1' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'm1'.
// c.m1 = 2;
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:32:5: Error: The getter 'm3' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'm3'.
// c.m3;
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:33:5: Error: The setter 'm3' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'm3'.
// c.m3 = 2;
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:34:5: Error: The getter 'm4' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'm4'.
// c.m4;
// ^^
//
// pkg/front_end/testcases/extensions/getter_setter_conflict.dart:35:5: Error: The setter 'm4' isn't defined for the class 'Class'.
// - 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'm4'.
// c.m4 = 2;
// ^^
//
import self as self;
import "dart:core" as core;
class Class extends core::Object {
synthetic constructor •() → self::Class*
: super core::Object::•()
;
get m1() → core::int*
return 0;
set m2(core::int* x) → void {}
}
extension Extension0 on self::Class* {
get m2 = self::Extension0|get#m2;
get m4 = self::Extension0|get#m4;
set m1 = self::Extension0|set#m1;
set m3 = self::Extension0|set#m3;
}
extension Extension1 on self::Class* {
get m3 = self::Extension1|get#m3;
set m4 = self::Extension1|set#m4;
}
static method Extension0|set#m1(final self::Class* #this, core::int* x) → core::int* {
final core::int* #t1 = x;
return #t1;
}
static method Extension0|get#m2(final self::Class* #this) → core::int*
return 0;
static method Extension0|set#m3(final self::Class* #this, core::int* x) → core::int* {
final core::int* #t2 = x;
return #t2;
}
static method Extension0|get#m4(final self::Class* #this) → core::int*
return 0;
static method Extension1|get#m3(final self::Class* #this) → core::int*
return 0;
static method Extension1|set#m4(final self::Class* #this, core::int* x) → core::int* {
final core::int* #t3 = x;
return #t3;
}
static method main() → dynamic {
self::Class* c = new self::Class::•();
self::expect(0, c.{self::Class::m1});
c.{self::Class::m2} = 2;
}
static method errors() → dynamic {
self::Class* c = new self::Class::•();
self::expect(0, invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:30:15: Error: The getter 'm2' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'm2'.
expect(0, c.m2);
^^");
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:31:5: Error: The setter 'm1' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'm1'.
c.m1 = 2;
^^";
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:32:5: Error: The getter 'm3' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'm3'.
c.m3;
^^";
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:33:5: Error: The setter 'm3' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'm3'.
c.m3 = 2;
^^";
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:34:5: Error: The getter 'm4' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'm4'.
c.m4;
^^";
invalid-expression "pkg/front_end/testcases/extensions/getter_setter_conflict.dart:35:5: Error: The setter 'm4' isn't defined for the class 'Class'.
- 'Class' is from 'pkg/front_end/testcases/extensions/getter_setter_conflict.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'm4'.
c.m4 = 2;
^^";
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!expected.{core::Object::==}(actual)) {
throw "Mismatch: expected=${expected}, actual=${actual}";
}
}

View file

@ -17,6 +17,7 @@ extensions/explicit_extension_inference: TextSerializationFailure
extensions/explicit_generic_extension_access: TextSerializationFailure
extensions/explicit_this: TextSerializationFailure
extensions/extension_methods: TextSerializationFailure
extensions/getter_setter_conflict: TextSerializationFailure
extensions/implicit_extension_inference: TextSerializationFailure
extensions/implicit_this: TextSerializationFailure
extensions/index: TextSerializationFailure