Link augmented methods and augmentations, fill 'augmented.methods'

Change-Id: Ic7e979c46fc646e436bafa18b5c5b01622ab209b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/313544
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-07-14 18:32:10 +00:00 committed by Commit Queue
parent 7918525a6b
commit d1a75901c4
10 changed files with 534 additions and 45 deletions

View file

@ -87,7 +87,7 @@ import 'package:analyzer/src/utilities/uri_cache.dart';
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
static const int DATA_VERSION = 285;
static const int DATA_VERSION = 288;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.

View file

@ -1683,6 +1683,13 @@ class MethodElementLinkedData extends ElementLinkedData<MethodElementImpl> {
_readTypeParameters(reader, element.typeParameters);
_readFormalParameters(reader, element.parameters);
element.returnType = reader.readRequiredType();
element.augmentation = reader.readElement() as MethodElementImpl?;
if (reader.readBool()) {
final target = reader.readElement() as MethodElementImpl?;
element.maybeAugmentation = AugmentationMethodElementImpl(
augmentationTarget: target,
);
}
applyConstantOffsets?.perform();
}
}

View file

@ -432,6 +432,13 @@ class BundleWriter {
_writeList(element.parameters, _writeParameterElement);
_sink._writeTopLevelInferenceError(element.typeInferenceError);
_resolutionSink.writeType(element.returnType2);
_resolutionSink.writeElement(element.augmentation);
_resolutionSink.writeIfType<AugmentationMethodElementImpl>(
element.maybeAugmentation,
(maybe) {
_resolutionSink.writeElement(maybe.augmentationTarget);
},
);
});
}

View file

@ -109,15 +109,8 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
node.declaredElement = element;
_linker.elementNodes[element] = node;
switch (_libraryBuilder.getAugmentationTarget(name)) {
case ClassOrAugmentationElementMixin target:
element.augmentationTarget = target;
target.augmentation = element;
}
final reference = _enclosingContext.addClassAugmentation(name, element);
_libraryBuilder.declare(name, reference);
_libraryBuilder.putAugmentationTarget(name, element);
final holder = _EnclosingContext(reference, element);
_withEnclosing(holder, () {
@ -140,6 +133,11 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
element.fields = holder.fields;
element.methods = holder.methods;
switch (_libraryBuilder.getAugmentedBuilder(name)) {
case AugmentedClassDeclarationBuilder builder:
builder.augment(element);
}
// TODO(scheglov) We cannot do this anymore.
// Not for class augmentations, not for classes.
// At the time when we build, we don't know all fields yet.
@ -172,7 +170,6 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
var reference = _enclosingContext.addClass(name, element);
_libraryBuilder.declare(name, reference);
_libraryBuilder.putAugmentationTarget(name, element);
var holder = _EnclosingContext(reference, element);
_withEnclosing(holder, () {
@ -187,6 +184,13 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
node.withClause?.accept(this);
node.implementsClause?.accept(this);
_buildClass(node);
_libraryBuilder.putAugmentedBuilder(
name,
AugmentedClassDeclarationBuilder(
declaration: element,
),
);
}
@override
@ -936,15 +940,8 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
node.declaredElement = element;
_linker.elementNodes[element] = node;
switch (_libraryBuilder.getAugmentationTarget(name)) {
case MixinOrAugmentationElementMixin target:
element.augmentationTarget = target;
target.augmentation = element;
}
final reference = _enclosingContext.addMixinAugmentation(name, element);
_libraryBuilder.declare(name, reference);
_libraryBuilder.putAugmentationTarget(name, element);
final holder = _EnclosingContext(reference, element);
_withEnclosing(holder, () {
@ -965,6 +962,11 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
element.accessors = holder.propertyAccessors;
element.fields = holder.fields;
element.methods = holder.methods;
switch (_libraryBuilder.getAugmentedBuilder(name)) {
case AugmentedMixinDeclarationBuilder builder:
builder.augment(element);
}
}
@override
@ -983,7 +985,6 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
var reference = _enclosingContext.addMixin(name, element);
_libraryBuilder.declare(name, reference);
_libraryBuilder.putAugmentationTarget(name, element);
var holder = _EnclosingContext(reference, element);
_withEnclosing(holder, () {
@ -997,6 +998,13 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
node.onClause?.accept(this);
node.implementsClause?.accept(this);
_buildMixin(node);
_libraryBuilder.putAugmentedBuilder(
name,
AugmentedMixinDeclarationBuilder(
declaration: element,
),
);
}
@override

View file

@ -29,6 +29,62 @@ import 'package:analyzer/src/summary2/types_builder.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
class AugmentedClassDeclarationBuilder
extends AugmentedInstanceDeclarationBuilder {
final ClassElementImpl declaration;
ClassOrAugmentationElementMixin target;
AugmentedClassDeclarationBuilder({
required this.declaration,
}) : target = declaration {
addMethods(declaration.methods);
}
void augment(ClassAugmentationElementImpl element) {
element.augmentationTarget = target;
target.augmentation = element;
target = element;
addMethods(element.methods);
}
}
abstract class AugmentedInstanceDeclarationBuilder {
final Map<String, MethodElementImpl> methods = {};
void addMethods(List<MethodElementImpl> elements) {
for (final element in elements) {
final name = element.name;
final existing = methods[name];
if (existing != null) {
existing.augmentation = element;
element.maybeAugmentation = AugmentationMethodElementImpl(
augmentationTarget: existing,
);
}
methods[name] = element;
}
}
}
class AugmentedMixinDeclarationBuilder
extends AugmentedInstanceDeclarationBuilder {
final MixinElementImpl declaration;
MixinOrAugmentationElementMixin target;
AugmentedMixinDeclarationBuilder({
required this.declaration,
}) : target = declaration {
addMethods(declaration.methods);
}
void augment(MixinAugmentationElementImpl element) {
element.augmentationTarget = target;
target.augmentation = element;
target = element;
addMethods(element.methods);
}
}
class DefiningLinkingUnit extends LinkingUnit {
DefiningLinkingUnit({
required super.reference,
@ -61,7 +117,8 @@ class LibraryBuilder {
final List<ImplicitEnumNodes> implicitEnumNodes = [];
/// The top-level elements that can be augmented.
final Map<String, ElementImpl> _augmentationTargets = {};
final Map<String, AugmentedInstanceDeclarationBuilder> _augmentedBuilders =
{};
/// Local declarations.
final Map<String, Reference> _declaredReferences = {};
@ -406,12 +463,15 @@ class LibraryBuilder {
);
}
ElementImpl? getAugmentationTarget(String name) {
return _augmentationTargets[name];
AugmentedInstanceDeclarationBuilder? getAugmentedBuilder(String name) {
return _augmentedBuilders[name];
}
void putAugmentationTarget(String name, ElementImpl element) {
_augmentationTargets[name] = element;
void putAugmentedBuilder(
String name,
AugmentedInstanceDeclarationBuilder element,
) {
_augmentedBuilders[name] = element;
}
void resolveConstructors() {

View file

@ -162,7 +162,7 @@ class TypesBuilder {
element.augmentedInternal = augmented;
augmented.mixins.addAll(element.mixins);
augmented.interfaces.addAll(element.interfaces);
augmented.methods.addAll(element.methods);
augmented.methods.addAll(element.methods.notAugmented);
}
}
@ -359,7 +359,7 @@ class TypesBuilder {
element.augmentedInternal = augmented;
augmented.superclassConstraints.addAll(element.superclassConstraints);
augmented.interfaces.addAll(element.interfaces);
augmented.methods.addAll(element.methods);
augmented.methods.addAll(element.methods.notAugmented);
}
}
@ -453,7 +453,7 @@ class TypesBuilder {
}
augmented.methods.addAll(
element.methods.map(mapMethodElement),
element.methods.notAugmented.map(mapMethodElement),
);
}
}
@ -705,3 +705,9 @@ class _MixinsInference {
}
}
}
extension on List<MethodElement> {
Iterable<MethodElement> get notAugmented {
return where((e) => e.augmentation == null);
}
}

View file

@ -210,14 +210,8 @@ class _ElementWriter {
final augmented = e.augmented;
void writeMethods() {
_writeElements('methods', augmented.methods, (element) {
if (element.enclosingElement2 == e) {
_writeMethodElement(element);
} else {
_sink.writeIndent();
_elementPrinter.writeElement(element);
}
});
final sorted = augmented.methods.sortedBy((e) => e.name);
_elementPrinter.writeElementList('methods', sorted);
}
_sink.writelnWithIndent('augmented');
@ -715,6 +709,22 @@ class _ElementWriter {
_writeParameterElements(e.parameters);
_writeType('returnType', e.returnType2);
_writeNonSyntheticElement(e);
final maybeAugmentation = e.maybeAugmentation;
if (maybeAugmentation != null) {
_sink.writelnWithIndent('maybeAugmentation');
_sink.withIndent(() {
_elementPrinter.writeNamedElement(
'augmentationTarget',
maybeAugmentation.augmentationTarget,
);
});
}
final augmentation = e.augmentation;
if (augmentation != null) {
_elementPrinter.writeNamedElement('augmentation', augmentation);
}
});
if (e.isSynthetic && e.enclosingElement2 is EnumElementImpl) {

View file

@ -19,11 +19,29 @@ import 'elements_base.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ElementsKeepLinkingTest);
defineReflectiveTests(ClassAugmentationElementsKeepLinkingTest);
defineReflectiveTests(MixinAugmentationElementsKeepLinkingTest);
defineReflectiveTests(ElementsFromBytesTest);
defineReflectiveTests(ClassAugmentationElementsFromBytesTest);
defineReflectiveTests(MixinAugmentationElementsFromBytesTest3);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
@reflectiveTest
class ClassAugmentationElementsFromBytesTest extends ElementsBaseTest
with ClassAugmentationElementsMixin {
@override
bool get keepLinkingLibraries => false;
}
@reflectiveTest
class ClassAugmentationElementsKeepLinkingTest extends ElementsBaseTest
with ClassAugmentationElementsMixin {
@override
bool get keepLinkingLibraries => true;
}
mixin ClassAugmentationElementsMixin on ElementsBaseTest {
test_augmentationTarget() async {
newFile('$testPackageLibPath/a1.dart', r'''
@ -375,9 +393,8 @@ library
returnType: void
augmented
methods
foo @42
returnType: void
self::@augmentation::package:test/a.dart::@classAugmentation::A::@method::bar
self::@class::A::@method::foo
augmentationImports
package:test/a.dart
definingUnit
@ -391,6 +408,122 @@ library
''');
}
test_augmented_methods_augment() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';
augment class A {
augment void foo1() {}
}
''');
var library = await buildLibrary(r'''
import augment 'a.dart';
class A {
void foo1() {}
void foo2() {}
}
''');
checkElementText(library, r'''
library
definingUnit
classes
class A @31
augmentation: self::@augmentation::package:test/a.dart::@classAugmentation::A
constructors
synthetic @-1
methods
foo1 @42
returnType: void
augmentation: self::@augmentation::package:test/a.dart::@classAugmentation::A::@method::foo1
foo2 @59
returnType: void
augmented
methods
self::@augmentation::package:test/a.dart::@classAugmentation::A::@method::foo1
self::@class::A::@method::foo2
augmentationImports
package:test/a.dart
definingUnit
classAugmentations
augment class A @43
augmentationTarget: self::@class::A
augmentedDeclaration: self::@class::A
methods
foo1 @62
returnType: void
maybeAugmentation
augmentationTarget: self::@class::A::@method::foo1
''');
}
test_augmented_methods_augment2() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';
import augment 'b.dart';
augment class A {
augment void foo() {}
}
''');
newFile('$testPackageLibPath/b.dart', r'''
library augment 'a.dart';
augment class A {
augment void foo() {}
}
''');
var library = await buildLibrary(r'''
import augment 'a.dart';
class A {
void foo() {}
}
''');
checkElementText(library, r'''
library
definingUnit
classes
class A @31
augmentation: self::@augmentation::package:test/a.dart::@classAugmentation::A
constructors
synthetic @-1
methods
foo @42
returnType: void
augmentation: self::@augmentation::package:test/a.dart::@classAugmentation::A::@method::foo
augmented
methods
self::@augmentation::package:test/b.dart::@classAugmentation::A::@method::foo
augmentationImports
package:test/a.dart
definingUnit
classAugmentations
augment class A @68
augmentationTarget: self::@class::A
augmentedDeclaration: self::@class::A
augmentation: self::@augmentation::package:test/b.dart::@classAugmentation::A
methods
foo @87
returnType: void
maybeAugmentation
augmentationTarget: self::@class::A::@method::foo
augmentation: self::@augmentation::package:test/b.dart::@classAugmentation::A::@method::foo
augmentationImports
package:test/b.dart
definingUnit
classAugmentations
augment class A @40
augmentationTarget: self::@augmentation::package:test/a.dart::@classAugmentation::A
augmentedDeclaration: self::@class::A
methods
foo @59
returnType: void
maybeAugmentation
augmentationTarget: self::@augmentation::package:test/a.dart::@classAugmentation::A::@method::foo
''');
}
test_augmented_methods_generic() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';
@ -422,11 +555,10 @@ library
returnType: T
augmented
methods
foo @42
returnType: T
MethodMember
base: self::@augmentation::package:test/a.dart::@classAugmentation::A::@method::bar
substitution: {T2: T}
self::@class::A::@method::foo
augmentationImports
package:test/a.dart
definingUnit
@ -443,6 +575,59 @@ library
''');
}
test_augmented_methods_generic_augment() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';
augment class A<T2> {
augment T2 foo() => throw 0;
}
''');
var library = await buildLibrary(r'''
import augment 'a.dart';
class A<T> {
T foo() => throw 0;
}
''');
checkElementText(library, r'''
library
definingUnit
classes
class A @31
typeParameters
covariant T @33
defaultType: dynamic
augmentation: self::@augmentation::package:test/a.dart::@classAugmentation::A
constructors
synthetic @-1
methods
foo @42
returnType: T
augmentation: self::@augmentation::package:test/a.dart::@classAugmentation::A::@method::foo
augmented
methods
MethodMember
base: self::@augmentation::package:test/a.dart::@classAugmentation::A::@method::foo
substitution: {T2: T}
augmentationImports
package:test/a.dart
definingUnit
classAugmentations
augment class A @43
typeParameters
covariant T2 @45
defaultType: dynamic
augmentationTarget: self::@class::A
augmentedDeclaration: self::@class::A
methods
foo @64
returnType: T2
maybeAugmentation
augmentationTarget: self::@class::A::@method::foo
''');
}
test_augmented_mixins() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';
@ -838,15 +1023,13 @@ library
}
@reflectiveTest
class ElementsFromBytesTest extends ElementsTest
with ClassAugmentationElementsMixin, MixinAugmentationElementsMixin {
class ElementsFromBytesTest extends ElementsTest {
@override
bool get keepLinkingLibraries => false;
}
@reflectiveTest
class ElementsKeepLinkingTest extends ElementsTest
with ClassAugmentationElementsMixin, MixinAugmentationElementsMixin {
class ElementsKeepLinkingTest extends ElementsTest {
@override
bool get keepLinkingLibraries => true;
}
@ -45391,6 +45574,20 @@ library
}
}
@reflectiveTest
class MixinAugmentationElementsFromBytesTest3 extends ElementsBaseTest
with MixinAugmentationElementsMixin {
@override
bool get keepLinkingLibraries => false;
}
@reflectiveTest
class MixinAugmentationElementsKeepLinkingTest extends ElementsBaseTest
with MixinAugmentationElementsMixin {
@override
bool get keepLinkingLibraries => true;
}
mixin MixinAugmentationElementsMixin on ElementsBaseTest {
test_augmentationTarget() async {
newFile('$testPackageLibPath/a.dart', r'''
@ -45588,9 +45785,8 @@ library
superclassConstraints
Object
methods
foo @42
returnType: void
self::@augmentation::package:test/a.dart::@mixinAugmentation::A::@method::bar
self::@mixin::A::@method::foo
augmentationImports
package:test/a.dart
definingUnit
@ -45604,6 +45800,126 @@ library
''');
}
test_augmented_methods_augment() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';
augment mixin A {
augment void foo1() {}
}
''');
var library = await buildLibrary(r'''
import augment 'a.dart';
mixin A {
void foo1() {}
void foo2() {}
}
''');
checkElementText(library, r'''
library
definingUnit
mixins
mixin A @31
augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A
superclassConstraints
Object
methods
foo1 @42
returnType: void
augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A::@method::foo1
foo2 @59
returnType: void
augmented
superclassConstraints
Object
methods
self::@augmentation::package:test/a.dart::@mixinAugmentation::A::@method::foo1
self::@mixin::A::@method::foo2
augmentationImports
package:test/a.dart
definingUnit
mixinAugmentations
augment mixin A @43
augmentationTarget: self::@mixin::A
augmentedDeclaration: self::@mixin::A
methods
foo1 @62
returnType: void
maybeAugmentation
augmentationTarget: self::@mixin::A::@method::foo1
''');
}
test_augmented_methods_augment2() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';
import augment 'b.dart';
augment mixin A {
augment void foo() {}
}
''');
newFile('$testPackageLibPath/b.dart', r'''
library augment 'a.dart';
augment mixin A {
augment void foo() {}
}
''');
var library = await buildLibrary(r'''
import augment 'a.dart';
mixin A {
void foo() {}
}
''');
checkElementText(library, r'''
library
definingUnit
mixins
mixin A @31
augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A
superclassConstraints
Object
methods
foo @42
returnType: void
augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A::@method::foo
augmented
superclassConstraints
Object
methods
self::@augmentation::package:test/b.dart::@mixinAugmentation::A::@method::foo
augmentationImports
package:test/a.dart
definingUnit
mixinAugmentations
augment mixin A @68
augmentationTarget: self::@mixin::A
augmentedDeclaration: self::@mixin::A
augmentation: self::@augmentation::package:test/b.dart::@mixinAugmentation::A
methods
foo @87
returnType: void
maybeAugmentation
augmentationTarget: self::@mixin::A::@method::foo
augmentation: self::@augmentation::package:test/b.dart::@mixinAugmentation::A::@method::foo
augmentationImports
package:test/b.dart
definingUnit
mixinAugmentations
augment mixin A @40
augmentationTarget: self::@augmentation::package:test/a.dart::@mixinAugmentation::A
augmentedDeclaration: self::@mixin::A
methods
foo @59
returnType: void
maybeAugmentation
augmentationTarget: self::@augmentation::package:test/a.dart::@mixinAugmentation::A::@method::foo
''');
}
test_augmented_methods_generic() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';
@ -45637,11 +45953,10 @@ library
superclassConstraints
Object
methods
foo @42
returnType: T
MethodMember
base: self::@augmentation::package:test/a.dart::@mixinAugmentation::A::@method::bar
substitution: {T2: T}
self::@mixin::A::@method::foo
augmentationImports
package:test/a.dart
definingUnit
@ -45657,6 +45972,60 @@ library
''');
}
test_augmented_methods_generic_augment() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';
augment mixin A<T2> {
augment T2 foo() => throw 0;
}
''');
var library = await buildLibrary(r'''
import augment 'a.dart';
mixin A<T> {
T foo() => throw 0;
}
''');
checkElementText(library, r'''
library
definingUnit
mixins
mixin A @31
typeParameters
covariant T @33
defaultType: dynamic
augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A
superclassConstraints
Object
methods
foo @42
returnType: T
augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A::@method::foo
augmented
superclassConstraints
Object
methods
MethodMember
base: self::@augmentation::package:test/a.dart::@mixinAugmentation::A::@method::foo
substitution: {T2: T}
augmentationImports
package:test/a.dart
definingUnit
mixinAugmentations
augment mixin A @43
typeParameters
covariant T2 @45
augmentationTarget: self::@mixin::A
augmentedDeclaration: self::@mixin::A
methods
foo @64
returnType: T2
maybeAugmentation
augmentationTarget: self::@mixin::A::@method::foo
''');
}
test_augmented_superclassConstraints() async {
newFile('$testPackageLibPath/a.dart', r'''
library augment 'test.dart';

View file

@ -89,6 +89,13 @@ class ElementPrinter {
}
}
void writeElementList(String name, List<Element> elements) {
_sink.writeElements(name, elements, (element) {
_sink.writeIndent();
writeElement(element);
});
}
void writeNamedElement(String name, Element? element) {
_sink.writeWithIndent('$name: ');
writeElement(element);

View file

@ -24,6 +24,21 @@ class TreeStringSink {
_sink.write(object);
}
void writeElements<T extends Object>(
String name,
List<T> elements,
void Function(T) f,
) {
if (elements.isNotEmpty) {
writelnWithIndent(name);
withIndent(() {
for (var element in elements) {
f(element);
}
});
}
}
void writeIf(bool flag, Object object) {
if (flag) {
write(object);