mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Inheritance inference for methods in augmentations, and using combined interfaces for classes and mixins.
Change-Id: I27af575219ab1e3bfba3f894ebc83e0806f7f968 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/314460 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
parent
f06a1d58d8
commit
6b4337ab69
|
@ -132,6 +132,22 @@ extension InterfaceElementExtension on InterfaceElement {
|
|||
}
|
||||
}
|
||||
|
||||
extension MixinElementExtension on MixinElement {
|
||||
/// The result of applying augmentations.
|
||||
///
|
||||
/// The target must be a declaration, not an augmentation.
|
||||
/// This getter will throw, if this is not the case.
|
||||
AugmentedMixinElement get augmentedOfDeclaration {
|
||||
if (isAugmentation) {
|
||||
throw StateError(
|
||||
'The target must be a declaration, not an augmentation.',
|
||||
);
|
||||
}
|
||||
// This is safe because declarations always have it.
|
||||
return augmented!;
|
||||
}
|
||||
}
|
||||
|
||||
extension ParameterElementExtensions on ParameterElement {
|
||||
/// Return [ParameterElement] with the specified properties replaced.
|
||||
ParameterElement copyWith({
|
||||
|
|
|
@ -448,6 +448,7 @@ class InheritanceManager3 {
|
|||
}
|
||||
|
||||
Interface _getInterfaceClass(InterfaceElement element) {
|
||||
final augmented = element.augmentedOfDeclaration;
|
||||
var classLibrary = element.library;
|
||||
var isNonNullableByDefault = classLibrary.isNonNullableByDefault;
|
||||
|
||||
|
@ -485,7 +486,7 @@ class InheritanceManager3 {
|
|||
// multiple candidates happen only when we merge super and multiple
|
||||
// interfaces. Consider using `Map<Name, ExecutableElement>` here.
|
||||
var mixinsConflicts = <List<Conflict>>[];
|
||||
for (var mixin in element.mixins) {
|
||||
for (var mixin in augmented.mixins) {
|
||||
var mixinElement = mixin.element;
|
||||
var substitution = Substitution.fromInterfaceType(mixin);
|
||||
var mixinInterface = getInterface(mixinElement);
|
||||
|
@ -573,7 +574,7 @@ class InheritanceManager3 {
|
|||
superImplemented.add(implemented);
|
||||
}
|
||||
|
||||
for (var interface in element.interfaces) {
|
||||
for (var interface in augmented.interfaces) {
|
||||
_addCandidates(
|
||||
namedCandidates: namedCandidates,
|
||||
substitution: Substitution.fromInterfaceType(interface),
|
||||
|
@ -648,11 +649,12 @@ class InheritanceManager3 {
|
|||
}
|
||||
|
||||
Interface _getInterfaceMixin(MixinElement element) {
|
||||
final augmented = element.augmentedOfDeclaration;
|
||||
var classLibrary = element.library;
|
||||
var isNonNullableByDefault = classLibrary.isNonNullableByDefault;
|
||||
|
||||
var superCandidates = <Name, List<ExecutableElement>>{};
|
||||
for (var constraint in element.superclassConstraints) {
|
||||
for (var constraint in augmented.superclassConstraints) {
|
||||
var substitution = Substitution.fromInterfaceType(constraint);
|
||||
var interfaceObj = getInterface(constraint.element);
|
||||
_addCandidates(
|
||||
|
@ -674,7 +676,7 @@ class InheritanceManager3 {
|
|||
);
|
||||
|
||||
var interfaceCandidates = Map.of(superCandidates);
|
||||
for (var interface in element.interfaces) {
|
||||
for (var interface in augmented.interfaces) {
|
||||
_addCandidates(
|
||||
namedCandidates: interfaceCandidates,
|
||||
substitution: Substitution.fromInterfaceType(interface),
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/dart/element/extensions.dart';
|
||||
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
|
||||
import 'package:analyzer/src/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/element/type_algebra.dart';
|
||||
|
@ -31,8 +32,8 @@ class InstanceMemberInferrer {
|
|||
|
||||
/// Infer type information for all of the instance members in the given
|
||||
/// compilation [unit].
|
||||
void inferCompilationUnit(CompilationUnitElement unit) {
|
||||
typeSystem = unit.library.typeSystem as TypeSystemImpl;
|
||||
void inferCompilationUnit(CompilationUnitElementImpl unit) {
|
||||
typeSystem = unit.library.typeSystem;
|
||||
isNonNullableByDefault = typeSystem.isNonNullableByDefault;
|
||||
_inferClasses(unit.classes);
|
||||
_inferClasses(unit.enums);
|
||||
|
@ -301,58 +302,69 @@ class InstanceMemberInferrer {
|
|||
|
||||
/// Infer type information for all of the instance members in the given
|
||||
/// [classElement].
|
||||
void _inferClass(InterfaceElement classElement) {
|
||||
void _inferClass(InterfaceElementImpl classElement) {
|
||||
if (classElement.isAugmentation) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (classElement.hasBeenInferred) {
|
||||
return;
|
||||
}
|
||||
|
||||
_setInducedModifier(classElement);
|
||||
if (classElement is InterfaceElementImpl) {
|
||||
if (classElement.hasBeenInferred) {
|
||||
return;
|
||||
}
|
||||
if (!elementsBeingInferred.add(classElement)) {
|
||||
// We have found a circularity in the class hierarchy. For now we just
|
||||
// stop trying to infer any type information for any classes that
|
||||
// inherit from any class in the cycle. We could potentially limit the
|
||||
// algorithm to only not inferring types in the classes in the cycle,
|
||||
// but it isn't clear that the results would be significantly better.
|
||||
throw _CycleException();
|
||||
}
|
||||
try {
|
||||
//
|
||||
// Ensure that all of instance members in the supertypes have had types
|
||||
// inferred for them.
|
||||
//
|
||||
_inferType(classElement.supertype);
|
||||
classElement.mixins.forEach(_inferType);
|
||||
classElement.interfaces.forEach(_inferType);
|
||||
//
|
||||
// Then infer the types for the members.
|
||||
//
|
||||
currentInterfaceElement = classElement;
|
||||
for (var field in classElement.fields) {
|
||||
|
||||
if (!elementsBeingInferred.add(classElement)) {
|
||||
// We have found a circularity in the class hierarchy. For now we just
|
||||
// stop trying to infer any type information for any classes that
|
||||
// inherit from any class in the cycle. We could potentially limit the
|
||||
// algorithm to only not inferring types in the classes in the cycle,
|
||||
// but it isn't clear that the results would be significantly better.
|
||||
throw _CycleException();
|
||||
}
|
||||
|
||||
try {
|
||||
//
|
||||
// Ensure that all of instance members in the supertypes have had types
|
||||
// inferred for them.
|
||||
//
|
||||
final augmented = classElement.augmentedOfDeclaration;
|
||||
_inferType(classElement.supertype);
|
||||
augmented.mixins.forEach(_inferType);
|
||||
augmented.interfaces.forEach(_inferType);
|
||||
//
|
||||
// Then infer the types for the members.
|
||||
//
|
||||
// TODO(scheglov) get other members from the container
|
||||
currentInterfaceElement = classElement;
|
||||
for (final container in classElement.withAugmentations) {
|
||||
for (final field in classElement.fields) {
|
||||
_inferAccessorOrField(
|
||||
field: field,
|
||||
);
|
||||
}
|
||||
for (var accessor in classElement.accessors) {
|
||||
for (final accessor in classElement.accessors) {
|
||||
_inferAccessorOrField(
|
||||
accessor: accessor,
|
||||
);
|
||||
}
|
||||
for (var method in classElement.methods) {
|
||||
for (final method in container.methods) {
|
||||
_inferExecutable(method);
|
||||
}
|
||||
//
|
||||
// Infer initializing formal parameter types. This must happen after
|
||||
// field types are inferred.
|
||||
//
|
||||
classElement.constructors.forEach(_inferConstructor);
|
||||
classElement.hasBeenInferred = true;
|
||||
} finally {
|
||||
elementsBeingInferred.remove(classElement);
|
||||
for (final constructor in classElement.constructors) {
|
||||
_inferConstructor(constructor);
|
||||
}
|
||||
}
|
||||
classElement.hasBeenInferred = true;
|
||||
} finally {
|
||||
elementsBeingInferred.remove(classElement);
|
||||
}
|
||||
}
|
||||
|
||||
void _inferClasses(List<InterfaceElement> elements) {
|
||||
void _inferClasses(List<InterfaceElementImpl> elements) {
|
||||
for (final element in elements) {
|
||||
try {
|
||||
_inferClass(element);
|
||||
|
@ -551,7 +563,8 @@ class InstanceMemberInferrer {
|
|||
/// interface [type].
|
||||
void _inferType(InterfaceType? type) {
|
||||
if (type != null) {
|
||||
_inferClass(type.element);
|
||||
final element = type.element as InterfaceElementImpl;
|
||||
_inferClass(element);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -784,6 +784,223 @@ library
|
|||
''');
|
||||
}
|
||||
|
||||
test_inferTypes_method_ofAugment() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
int foo(String a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
library augment 'test.dart';
|
||||
augment class B {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import 'a.dart';
|
||||
import augment 'b.dart';
|
||||
|
||||
class B extends A {}
|
||||
''');
|
||||
|
||||
checkElementText(library, r'''
|
||||
library
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
classes
|
||||
class B @49
|
||||
augmentation: self::@augmentation::package:test/b.dart::@class::B
|
||||
supertype: A
|
||||
constructors
|
||||
synthetic @-1
|
||||
superConstructor: package:test/a.dart::@class::A::@constructor::new
|
||||
augmented
|
||||
methods
|
||||
self::@augmentation::package:test/b.dart::@class::B::@method::foo
|
||||
augmentationImports
|
||||
package:test/b.dart
|
||||
definingUnit
|
||||
classes
|
||||
augment class B @43
|
||||
augmentationTarget: self::@class::B
|
||||
methods
|
||||
foo @49
|
||||
parameters
|
||||
requiredPositional a @53
|
||||
type: String
|
||||
returnType: int
|
||||
''');
|
||||
}
|
||||
|
||||
test_inferTypes_method_usingAugmentation_interface() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
int foo(String a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
library augment 'test.dart';
|
||||
import 'a.dart';
|
||||
augment class B implements A {}
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import augment 'b.dart';
|
||||
|
||||
class B {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
checkElementText(library, r'''
|
||||
library
|
||||
definingUnit
|
||||
classes
|
||||
class B @32
|
||||
augmentation: self::@augmentation::package:test/b.dart::@class::B
|
||||
constructors
|
||||
synthetic @-1
|
||||
methods
|
||||
foo @38
|
||||
parameters
|
||||
requiredPositional a @42
|
||||
type: String
|
||||
returnType: int
|
||||
augmented
|
||||
interfaces
|
||||
A
|
||||
methods
|
||||
self::@class::B::@method::foo
|
||||
augmentationImports
|
||||
package:test/b.dart
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
classes
|
||||
augment class B @60
|
||||
augmentationTarget: self::@class::B
|
||||
interfaces
|
||||
A
|
||||
''');
|
||||
}
|
||||
|
||||
test_inferTypes_method_usingAugmentation_mixin() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
mixin A {
|
||||
int foo(String a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
library augment 'test.dart';
|
||||
import 'a.dart';
|
||||
augment class B with A {}
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import augment 'b.dart';
|
||||
|
||||
class B {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
checkElementText(library, r'''
|
||||
library
|
||||
definingUnit
|
||||
classes
|
||||
class B @32
|
||||
augmentation: self::@augmentation::package:test/b.dart::@class::B
|
||||
constructors
|
||||
synthetic @-1
|
||||
methods
|
||||
foo @38
|
||||
parameters
|
||||
requiredPositional a @42
|
||||
type: String
|
||||
returnType: int
|
||||
augmented
|
||||
mixins
|
||||
A
|
||||
methods
|
||||
self::@class::B::@method::foo
|
||||
augmentationImports
|
||||
package:test/b.dart
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
classes
|
||||
augment class B @60
|
||||
augmentationTarget: self::@class::B
|
||||
mixins
|
||||
A
|
||||
''');
|
||||
}
|
||||
|
||||
test_inferTypes_method_withAugment() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
int foo(String a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
library augment 'test.dart';
|
||||
augment class B {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import 'a.dart';
|
||||
import augment 'b.dart';
|
||||
|
||||
class B extends A {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
checkElementText(library, r'''
|
||||
library
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
classes
|
||||
class B @49
|
||||
augmentation: self::@augmentation::package:test/b.dart::@class::B
|
||||
supertype: A
|
||||
constructors
|
||||
synthetic @-1
|
||||
superConstructor: package:test/a.dart::@class::A::@constructor::new
|
||||
methods
|
||||
foo @65
|
||||
parameters
|
||||
requiredPositional a @69
|
||||
type: String
|
||||
returnType: int
|
||||
augmentation: self::@augmentation::package:test/b.dart::@class::B::@method::foo
|
||||
augmented
|
||||
methods
|
||||
self::@augmentation::package:test/b.dart::@class::B::@method::foo
|
||||
augmentationImports
|
||||
package:test/b.dart
|
||||
definingUnit
|
||||
classes
|
||||
augment class B @43
|
||||
augmentationTarget: self::@class::B
|
||||
methods
|
||||
foo @49
|
||||
parameters
|
||||
requiredPositional a @53
|
||||
type: String
|
||||
returnType: int
|
||||
''');
|
||||
}
|
||||
|
||||
test_modifiers_abstract() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
library augment 'test.dart';
|
||||
|
@ -46314,6 +46531,226 @@ library
|
|||
''');
|
||||
}
|
||||
|
||||
test_inferTypes_method_ofAugment() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
int foo(String a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
library augment 'test.dart';
|
||||
augment mixin B {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import 'a.dart';
|
||||
import augment 'b.dart';
|
||||
|
||||
mixin B on A {}
|
||||
''');
|
||||
|
||||
checkElementText(library, r'''
|
||||
library
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
mixins
|
||||
mixin B @49
|
||||
augmentation: self::@augmentation::package:test/b.dart::@mixin::B
|
||||
superclassConstraints
|
||||
A
|
||||
augmented
|
||||
superclassConstraints
|
||||
A
|
||||
methods
|
||||
self::@augmentation::package:test/b.dart::@mixin::B::@method::foo
|
||||
augmentationImports
|
||||
package:test/b.dart
|
||||
definingUnit
|
||||
mixins
|
||||
augment mixin B @43
|
||||
augmentationTarget: self::@mixin::B
|
||||
methods
|
||||
foo @49
|
||||
parameters
|
||||
requiredPositional a @53
|
||||
type: String
|
||||
returnType: int
|
||||
''');
|
||||
}
|
||||
|
||||
test_inferTypes_method_usingAugmentation_interface() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
int foo(String a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
library augment 'test.dart';
|
||||
import 'a.dart';
|
||||
augment mixin B implements A {}
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import augment 'b.dart';
|
||||
|
||||
mixin B {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
checkElementText(library, r'''
|
||||
library
|
||||
definingUnit
|
||||
mixins
|
||||
mixin B @32
|
||||
augmentation: self::@augmentation::package:test/b.dart::@mixin::B
|
||||
superclassConstraints
|
||||
Object
|
||||
methods
|
||||
foo @38
|
||||
parameters
|
||||
requiredPositional a @42
|
||||
type: String
|
||||
returnType: int
|
||||
augmented
|
||||
superclassConstraints
|
||||
Object
|
||||
interfaces
|
||||
A
|
||||
methods
|
||||
self::@mixin::B::@method::foo
|
||||
augmentationImports
|
||||
package:test/b.dart
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
mixins
|
||||
augment mixin B @60
|
||||
augmentationTarget: self::@mixin::B
|
||||
interfaces
|
||||
A
|
||||
''');
|
||||
}
|
||||
|
||||
test_inferTypes_method_usingAugmentation_superclassConstraint() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
int foo(String a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
library augment 'test.dart';
|
||||
import 'a.dart';
|
||||
augment mixin B on A {}
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import augment 'b.dart';
|
||||
|
||||
mixin B {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
checkElementText(library, r'''
|
||||
library
|
||||
definingUnit
|
||||
mixins
|
||||
mixin B @32
|
||||
augmentation: self::@augmentation::package:test/b.dart::@mixin::B
|
||||
superclassConstraints
|
||||
Object
|
||||
methods
|
||||
foo @38
|
||||
parameters
|
||||
requiredPositional a @42
|
||||
type: String
|
||||
returnType: int
|
||||
augmented
|
||||
superclassConstraints
|
||||
Object
|
||||
A
|
||||
methods
|
||||
self::@mixin::B::@method::foo
|
||||
augmentationImports
|
||||
package:test/b.dart
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
mixins
|
||||
augment mixin B @60
|
||||
augmentationTarget: self::@mixin::B
|
||||
superclassConstraints
|
||||
A
|
||||
''');
|
||||
}
|
||||
|
||||
test_inferTypes_method_withAugment() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
int foo(String a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
library augment 'test.dart';
|
||||
augment mixin B {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import 'a.dart';
|
||||
import augment 'b.dart';
|
||||
|
||||
mixin B on A {
|
||||
foo(a) => 0;
|
||||
}
|
||||
''');
|
||||
|
||||
checkElementText(library, r'''
|
||||
library
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
mixins
|
||||
mixin B @49
|
||||
augmentation: self::@augmentation::package:test/b.dart::@mixin::B
|
||||
superclassConstraints
|
||||
A
|
||||
methods
|
||||
foo @60
|
||||
parameters
|
||||
requiredPositional a @64
|
||||
type: String
|
||||
returnType: int
|
||||
augmentation: self::@augmentation::package:test/b.dart::@mixin::B::@method::foo
|
||||
augmented
|
||||
superclassConstraints
|
||||
A
|
||||
methods
|
||||
self::@augmentation::package:test/b.dart::@mixin::B::@method::foo
|
||||
augmentationImports
|
||||
package:test/b.dart
|
||||
definingUnit
|
||||
mixins
|
||||
augment mixin B @43
|
||||
augmentationTarget: self::@mixin::B
|
||||
methods
|
||||
foo @49
|
||||
parameters
|
||||
requiredPositional a @53
|
||||
type: String
|
||||
returnType: int
|
||||
''');
|
||||
}
|
||||
|
||||
test_modifiers_base() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
library augment 'test.dart';
|
||||
|
|
Loading…
Reference in a new issue