diff --git a/pkg/analyzer/lib/dart/ast/visitor.dart b/pkg/analyzer/lib/dart/ast/visitor.dart index 0e34030aa95b..b84095d3caa1 100644 --- a/pkg/analyzer/lib/dart/ast/visitor.dart +++ b/pkg/analyzer/lib/dart/ast/visitor.dart @@ -508,6 +508,10 @@ class GeneralizingAstVisitor implements AstVisitor { R? visitMethodInvocation(MethodInvocation node) => visitInvocationExpression(node); + @override + R? visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) => + visitNamedCompilationUnitMember(node); + @override R? visitMixinDeclaration(MixinDeclaration node) => visitNamedCompilationUnitMember(node); @@ -1385,6 +1389,12 @@ class RecursiveAstVisitor implements AstVisitor { return null; } + @override + R? visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) { + node.visitChildren(this); + return null; + } + @override R? visitMixinDeclaration(MixinDeclaration node) { node.visitChildren(this); @@ -2114,6 +2124,10 @@ class SimpleAstVisitor implements AstVisitor { @override R? visitMethodInvocation(MethodInvocation node) => null; + @override + R? visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) => + null; + @override R? visitMixinDeclaration(MixinDeclaration node) => null; @@ -2652,6 +2666,10 @@ class ThrowingAstVisitor implements AstVisitor { @override R? visitMethodInvocation(MethodInvocation node) => _throw(node); + @override + R? visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) => + _throw(node); + @override R? visitMixinDeclaration(MixinDeclaration node) => _throw(node); @@ -3693,6 +3711,14 @@ class TimedAstVisitor implements AstVisitor { return result; } + @override + T? visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) { + stopwatch.start(); + T? result = _baseVisitor.visitMixinAugmentationDeclaration(node); + stopwatch.stop(); + return result; + } + @override T? visitMixinDeclaration(MixinDeclaration node) { stopwatch.start(); @@ -4578,6 +4604,10 @@ class UnifyingAstVisitor implements AstVisitor { @override R? visitMethodInvocation(MethodInvocation node) => visitNode(node); + @override + R? visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) => + visitNode(node); + @override R? visitMixinDeclaration(MixinDeclaration node) => visitNode(node); diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart index 1039887e2d24..4a8b02772afd 100644 --- a/pkg/analyzer/lib/dart/element/element.dart +++ b/pkg/analyzer/lib/dart/element/element.dart @@ -358,6 +358,7 @@ abstract class CompilationUnitElement implements UriReferencedElement { List get accessors; /// The class augmentations declared in this compilation unit. + @experimental List get classAugmentations; /// The classes declared in this compilation unit. @@ -384,6 +385,10 @@ abstract class CompilationUnitElement implements UriReferencedElement { /// The [LineInfo] for the [source]. LineInfo get lineInfo; + /// The mixin augmentations declared in this compilation unit. + @experimental + List get mixinAugmentations; + /// The mixins declared in this compilation unit. List get mixins; @@ -1169,6 +1174,8 @@ abstract class ElementVisitor { R? visitMethodElement(MethodElement element); + R? visitMixinAugmentationElement(MixinAugmentationElement element); + R? visitMixinElement(MixinElement element); R? visitMultiplyDefinedElement(MultiplyDefinedElement element); @@ -2177,25 +2184,6 @@ abstract class MixinElement @override AugmentedMixinElement get augmented; - /// Whether the mixin is a base mixin. - /// - /// A mixin is a base mixin if it has an explicit `base` modifier, or the - /// mixin has a `base` induced modifier and [isSealed] is `true` as well. - /// The base modifier allows a mixin to be mixed in but not implemented. - bool get isBase; - - /// The superclass constraints defined for this mixin. - /// - /// If the declaration does not have an `on` clause, then the list will - /// contain the type for the class `Object`. - /// - /// Note: Because the element model represents the state of the code, - /// it is possible for it to be semantically invalid. In particular, it is not - /// safe to assume that the inheritance structure of a class does not contain - /// a cycle. Clients that traverse the inheritance structure must explicitly - /// guard against infinite loops. - List get superclassConstraints; - /// Whether the element, assuming that it is within scope, is /// implementable to classes, mixins, and enums in the given [library]. bool isImplementableIn(LibraryElement library); @@ -2216,6 +2204,24 @@ abstract class MixinOrAugmentationElement @override MixinElement? get augmentedDeclaration; + + /// Whether the mixin is a base mixin. + /// + /// A mixin is a base mixin if it has an explicit `base` modifier. + /// The base modifier allows a mixin to be mixed in, but not implemented. + bool get isBase; + + /// The superclass constraints defined for this mixin. + /// + /// If the declaration does not have an `on` clause, then the list will + /// contain the type for the class `Object`. + /// + /// Note: Because the element model represents the state of the code, + /// it is possible for it to be semantically invalid. In particular, it is not + /// safe to assume that the inheritance structure of a class does not contain + /// a cycle. Clients that traverse the inheritance structure must explicitly + /// guard against infinite loops. + List get superclassConstraints; } /// A pseudo-element that represents multiple elements defined within a single diff --git a/pkg/analyzer/lib/dart/element/visitor.dart b/pkg/analyzer/lib/dart/element/visitor.dart index d17f0d25cba8..90d9bb89858b 100644 --- a/pkg/analyzer/lib/dart/element/visitor.dart +++ b/pkg/analyzer/lib/dart/element/visitor.dart @@ -169,6 +169,10 @@ class GeneralizingElementVisitor implements ElementVisitor { R? visitMethodElement(MethodElement element) => visitExecutableElement(element); + @override + R? visitMixinAugmentationElement(MixinAugmentationElement element) => + visitElement(element); + @override R? visitMixinElement(MixinElement element) => visitElement(element); @@ -334,6 +338,12 @@ class RecursiveElementVisitor implements ElementVisitor { return null; } + @override + R? visitMixinAugmentationElement(MixinAugmentationElement element) { + element.visitChildren(this); + return null; + } + @override R? visitMixinElement(MixinElement element) { element.visitChildren(this); @@ -462,6 +472,9 @@ class SimpleElementVisitor implements ElementVisitor { @override R? visitMethodElement(MethodElement element) => null; + @override + R? visitMixinAugmentationElement(MixinAugmentationElement element) => null; + @override R? visitMixinElement(MixinElement element) => null; @@ -565,6 +578,10 @@ class ThrowingElementVisitor implements ElementVisitor { @override R? visitMethodElement(MethodElement element) => _throw(element); + @override + R? visitMixinAugmentationElement(MixinAugmentationElement element) => + _throw(element); + @override R? visitMixinElement(MixinElement element) => _throw(element); diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart index 3396b1eb4fd0..c58264f22e2a 100644 --- a/pkg/analyzer/lib/src/dart/analysis/driver.dart +++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart @@ -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 = 282; + static const int DATA_VERSION = 283; /// The number of exception contexts allowed to write. Once this field is /// zero, we stop writing any new exception contexts in this process. diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart index 236a106cc3c7..548e16532ac4 100644 --- a/pkg/analyzer/lib/src/dart/ast/ast.dart +++ b/pkg/analyzer/lib/src/dart/ast/ast.dart @@ -1447,6 +1447,8 @@ abstract class AstVisitor { R? visitMethodInvocation(MethodInvocation node); + R? visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node); + R? visitMixinDeclaration(MixinDeclaration node); R? visitNamedExpression(NamedExpression node); @@ -12389,6 +12391,35 @@ abstract final class MixinAugmentationDeclaration MixinAugmentationElement? get declaredElement; } +final class MixinAugmentationDeclarationImpl + extends MixinOrAugmentationDeclarationImpl + implements MixinAugmentationDeclaration { + @override + MixinAugmentationElementImpl? declaredElement; + + @override + final Token augmentKeyword; + + MixinAugmentationDeclarationImpl({ + required super.comment, + required super.metadata, + required this.augmentKeyword, + required super.baseKeyword, + required super.mixinKeyword, + required super.name, + required super.typeParameters, + required super.onClause, + required super.implementsClause, + required super.leftBracket, + required super.members, + required super.rightBracket, + }); + + @override + E? accept(AstVisitor visitor) => + visitor.visitMixinAugmentationDeclaration(this); +} + /// The declaration of a mixin. /// /// mixinDeclaration ::= @@ -12422,113 +12453,124 @@ abstract final class MixinDeclaration TypeParameterList? get typeParameters; } -/// The declaration of a mixin. -/// -/// mixinDeclaration ::= -/// metadata? 'base'? 'mixin' [SimpleIdentifier] -/// [TypeParameterList]? [RequiresClause]? [ImplementsClause]? -/// '{' [ClassMember]* '}' -final class MixinDeclarationImpl extends NamedCompilationUnitMemberImpl +final class MixinDeclarationImpl extends MixinOrAugmentationDeclarationImpl implements MixinDeclaration { - /// Return the 'augment' keyword, or `null` if the keyword was absent. - final Token? augmentKeyword; + @override + MixinElementImpl? declaredElement; + + MixinDeclarationImpl({ + required super.comment, + required super.metadata, + required super.baseKeyword, + required super.mixinKeyword, + required super.name, + required super.typeParameters, + required super.onClause, + required super.implementsClause, + required super.leftBracket, + required super.members, + required super.rightBracket, + }); + @override + E? accept(AstVisitor visitor) => visitor.visitMixinDeclaration(this); +} + +/// Shared interface between [MixinDeclaration] and +/// [MixinAugmentationDeclaration]. +@experimental +abstract final class MixinOrAugmentationDeclaration + implements NamedCompilationUnitMember { /// Return the 'base' keyword, or `null` if the keyword was absent. + Token? get baseKeyword; + + @override + MixinOrAugmentationElement? get declaredElement; + + /// Returns the `implements` clause for the mixin, or `null` if the mixin + /// does not implement any interfaces. + ImplementsClause? get implementsClause; + + /// Returns the left curly bracket. + Token get leftBracket; + + /// Returns the members defined by the mixin. + NodeList get members; + + /// Return the token representing the 'mixin' keyword. + Token get mixinKeyword; + + /// Return the on clause for the mixin, or `null` if the mixin does not have + /// any superclass constraints. + OnClause? get onClause; + + /// Returns the right curly bracket. + Token get rightBracket; + + /// Returns the type parameters for the mixin, or `null` if the mixin does + /// not have any type parameters. + TypeParameterList? get typeParameters; +} + +sealed class MixinOrAugmentationDeclarationImpl + extends NamedCompilationUnitMemberImpl + implements MixinOrAugmentationDeclaration { @override final Token? baseKeyword; @override final Token mixinKeyword; - /// The type parameters for the class or mixin, - /// or `null` if the declaration does not have any type parameters. - TypeParameterListImpl? _typeParameters; - - /// The on clause for the mixin, or `null` if the mixin does not have any - /// super-class constraints. - OnClauseImpl? _onClause; + @override + final TypeParameterListImpl? typeParameters; - /// The implements clause for the class or mixin, - /// or `null` if the declaration does not implement any interfaces. - ImplementsClauseImpl? _implementsClause; + @override + final OnClauseImpl? onClause; @override - MixinElementImpl? declaredElement; + final ImplementsClauseImpl? implementsClause; - /// The left curly bracket. @override final Token leftBracket; - /// The members defined by the class or mixin. - final NodeListImpl _members = NodeListImpl._(); + @override + final NodeListImpl members = NodeListImpl._(); - /// The right curly bracket. @override final Token rightBracket; - /// Initialize a newly created mixin declaration. Either or both of the - /// [comment] and [metadata] can be `null` if the mixin does not have the - /// corresponding attribute. The [typeParameters] can be `null` if the mixin - /// does not have any type parameters. Either or both of the [onClause], - /// and [implementsClause] can be `null` if the mixin does not have the - /// corresponding clause. The list of [members] can be `null` if the mixin - /// does not have any members. - MixinDeclarationImpl({ + MixinOrAugmentationDeclarationImpl({ required super.comment, required super.metadata, - required this.augmentKeyword, required this.baseKeyword, required this.mixinKeyword, required super.name, - required TypeParameterListImpl? typeParameters, - required OnClauseImpl? onClause, - required ImplementsClauseImpl? implementsClause, + required this.typeParameters, + required this.onClause, + required this.implementsClause, required this.leftBracket, required List members, required this.rightBracket, - }) : _typeParameters = typeParameters, - _onClause = onClause, - _implementsClause = implementsClause { - _becomeParentOf(_typeParameters); - _becomeParentOf(_onClause); - _becomeParentOf(_implementsClause); - _members._initialize(this, members); + }) { + _becomeParentOf(typeParameters); + _becomeParentOf(onClause); + _becomeParentOf(implementsClause); + this.members._initialize(this, members); } + Token? get augmentKeyword => null; + @override Token get endToken => rightBracket; @override Token get firstTokenAfterCommentAndMetadata { - return baseKeyword ?? mixinKeyword; - } - - @override - ImplementsClauseImpl? get implementsClause => _implementsClause; - - set implementsClause(ImplementsClauseImpl? implementsClause) { - _implementsClause = _becomeParentOf(implementsClause); - } - - @override - NodeListImpl get members => _members; - - @override - OnClauseImpl? get onClause => _onClause; - - set onClause(OnClauseImpl? onClause) { - _onClause = _becomeParentOf(onClause); - } - - @override - TypeParameterListImpl? get typeParameters => _typeParameters; - - set typeParameters(TypeParameterListImpl? typeParameters) { - _typeParameters = _becomeParentOf(typeParameters); + return augmentKeyword ?? baseKeyword ?? mixinKeyword; } @override ChildEntities get _childEntities => super._childEntities + ..addToken('augmentKeyword', augmentKeyword) ..addToken('baseKeyword', baseKeyword) ..addToken('mixinKeyword', mixinKeyword) ..addToken('name', name) @@ -12539,55 +12581,16 @@ final class MixinDeclarationImpl extends NamedCompilationUnitMemberImpl ..addNodeList('members', members) ..addToken('rightBracket', rightBracket); - @override - E? accept(AstVisitor visitor) => visitor.visitMixinDeclaration(this); - @override void visitChildren(AstVisitor visitor) { super.visitChildren(visitor); - _typeParameters?.accept(visitor); - _onClause?.accept(visitor); - _implementsClause?.accept(visitor); + typeParameters?.accept(visitor); + onClause?.accept(visitor); + implementsClause?.accept(visitor); members.accept(visitor); } } -/// Shared interface between [MixinDeclaration] and -/// [MixinAugmentationDeclaration]. -@experimental -abstract final class MixinOrAugmentationDeclaration - implements NamedCompilationUnitMember { - /// Return the 'base' keyword, or `null` if the keyword was absent. - Token? get baseKeyword; - - @override - MixinOrAugmentationElement? get declaredElement; - - /// Returns the `implements` clause for the mixin, or `null` if the mixin - /// does not implement any interfaces. - ImplementsClause? get implementsClause; - - /// Returns the left curly bracket. - Token get leftBracket; - - /// Returns the members defined by the mixin. - NodeList get members; - - /// Return the token representing the 'mixin' keyword. - Token get mixinKeyword; - - /// Return the on clause for the mixin, or `null` if the mixin does not have - /// any superclass constraints. - OnClause? get onClause; - - /// Returns the right curly bracket. - Token get rightBracket; - - /// Returns the type parameters for the mixin, or `null` if the mixin does - /// not have any type parameters. - TypeParameterList? get typeParameters; -} - /// A node that declares a single name within the scope of a compilation unit. abstract final class NamedCompilationUnitMember implements CompilationUnitMember { diff --git a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart index 784bbdebb774..5e00886a4342 100644 --- a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart +++ b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart @@ -890,18 +890,17 @@ class ToSourceVisitor implements AstVisitor { _visitNode(node.argumentList); } + @override + void visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) { + _mixinOrAugmentationDeclaration( + node, + augmentKeyword: node.augmentKeyword, + ); + } + @override void visitMixinDeclaration(MixinDeclaration node) { - _visitNodeList(node.metadata, separator: ' ', suffix: ' '); - _visitToken(node.baseKeyword, suffix: ' '); - sink.write('mixin '); - _visitToken(node.name); - _visitNode(node.typeParameters); - _visitNode(node.onClause, prefix: ' '); - _visitNode(node.implementsClause, prefix: ' '); - sink.write(' {'); - _visitNodeList(node.members, separator: ' '); - sink.write('}'); + _mixinOrAugmentationDeclaration(node); } @override @@ -1446,6 +1445,23 @@ class ToSourceVisitor implements AstVisitor { sink.write('}'); } + void _mixinOrAugmentationDeclaration( + MixinOrAugmentationDeclaration node, { + Token? augmentKeyword, + }) { + _visitNodeList(node.metadata, separator: ' ', suffix: ' '); + _visitToken(augmentKeyword, suffix: ' '); + _visitToken(node.baseKeyword, suffix: ' '); + sink.write('mixin '); + _visitToken(node.name); + _visitNode(node.typeParameters); + _visitNode(node.onClause, prefix: ' '); + _visitNode(node.implementsClause, prefix: ' '); + sink.write(' {'); + _visitNodeList(node.members, separator: ' '); + sink.write('}'); + } + /// Visit the given function [body], printing a prefix before if the body /// is not empty. void _visitFunctionBody(FunctionBody body) { diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart index 320dc807f3d3..8ef3467f0dec 100644 --- a/pkg/analyzer/lib/src/dart/ast/utilities.dart +++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart @@ -1031,12 +1031,31 @@ class AstComparator implements AstVisitor { isEqualNodes(node.argumentList, other.argumentList); } + @override + bool visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) { + final other = _other as MixinAugmentationDeclaration; + return isEqualNodes( + node.documentationComment, other.documentationComment) && + _isEqualNodeLists(node.metadata, other.metadata) && + isEqualTokens(node.augmentKeyword, other.augmentKeyword) && + isEqualTokens(node.baseKeyword, other.baseKeyword) && + isEqualTokens(node.mixinKeyword, other.mixinKeyword) && + isEqualTokens(node.name, other.name) && + isEqualNodes(node.typeParameters, other.typeParameters) && + isEqualNodes(node.onClause, other.onClause) && + isEqualNodes(node.implementsClause, other.implementsClause) && + isEqualTokens(node.leftBracket, other.leftBracket) && + _isEqualNodeLists(node.members, other.members) && + isEqualTokens(node.rightBracket, other.rightBracket); + } + @override bool visitMixinDeclaration(MixinDeclaration node) { MixinDeclaration other = _other as MixinDeclaration; return isEqualNodes( node.documentationComment, other.documentationComment) && _isEqualNodeLists(node.metadata, other.metadata) && + isEqualTokens(node.baseKeyword, other.baseKeyword) && isEqualTokens(node.mixinKeyword, other.mixinKeyword) && isEqualTokens(node.name, other.name) && isEqualNodes(node.typeParameters, other.typeParameters) && @@ -2944,28 +2963,6 @@ class NodeReplacer extends ThrowingAstVisitor { return visitNode(node); } - @override - bool visitMixinDeclaration(covariant MixinDeclarationImpl node) { - if (identical(node.documentationComment, _oldNode)) { - node.documentationComment = _newNode as CommentImpl; - return true; - } else if (_replaceInList(node.metadata)) { - return true; - } else if (identical(node.typeParameters, _oldNode)) { - node.typeParameters = _newNode as TypeParameterListImpl; - return true; - } else if (identical(node.onClause, _oldNode)) { - node.onClause = _newNode as OnClauseImpl; - return true; - } else if (identical(node.implementsClause, _oldNode)) { - node.implementsClause = _newNode as ImplementsClauseImpl; - return true; - } else if (_replaceInList(node.members)) { - return true; - } - return visitNode(node); - } - @override bool visitNamedExpression(covariant NamedExpressionImpl node) { if (identical(node.name, _oldNode)) { diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart index 3cb234871061..7039530d94a4 100644 --- a/pkg/analyzer/lib/src/dart/element/element.dart +++ b/pkg/analyzer/lib/src/dart/element/element.dart @@ -165,8 +165,7 @@ abstract class AugmentedInterfaceElementImpl class AugmentedMixinElementImpl extends AugmentedInterfaceElementImpl implements AugmentedMixinElement { @override - // TODO: implement superclassConstraints - List get superclassConstraints => throw UnimplementedError(); + List superclassConstraints = []; } abstract class AugmentedNamedInstanceElementImpl @@ -735,8 +734,8 @@ class CompilationUnitElementImpl extends UriReferencedElementImpl /// compilation unit. List _functions = const []; - /// A list containing all of the mixins contained in this compilation unit. List _mixins = const []; + List _mixinAugmentations = const []; /// A list containing all of the type aliases contained in this compilation /// unit. @@ -877,6 +876,18 @@ class CompilationUnitElementImpl extends UriReferencedElementImpl return super.metadata; } + @override + List get mixinAugmentations { + return _mixinAugmentations; + } + + set mixinAugmentations(List elements) { + for (final element in elements) { + element.enclosingElement = this; + } + _mixinAugmentations = elements; + } + @override List get mixins { return _mixins; @@ -5044,31 +5055,57 @@ class MethodElementImpl extends ExecutableElementImpl implements MethodElement { T? accept(ElementVisitor visitor) => visitor.visitMethodElement(this); } -/// A [ClassElementImpl] representing a mixin declaration. -class MixinElementImpl extends ClassOrMixinElementImpl implements MixinElement { - /// A list containing all of the superclass constraints that are defined for - /// the mixin. - List _superclassConstraints = const []; +class MixinAugmentationElementImpl extends InterfaceAugmentationElementImpl + with MixinOrAugmentationElementMixin + implements MixinAugmentationElement { + MixinOrAugmentationElementMixin? _augmentationTarget; + + MixinAugmentationElementImpl(super.name, super.offset); + @override + MixinOrAugmentationElementMixin? get augmentationTarget { + linkedData?.read(this); + return _augmentationTarget; + } + + set augmentationTarget(MixinOrAugmentationElementMixin? value) { + _augmentationTarget = value; + } + + @override + MixinElementImpl? get augmentedDeclaration { + return augmentationTarget?.augmentedDeclaration; + } + + @override + ElementKind get kind => ElementKind.CLASS_AUGMENTATION; + + @override + T? accept(ElementVisitor visitor) { + return visitor.visitMixinAugmentationElement(this); + } +} + +/// A [ClassElementImpl] representing a mixin declaration. +class MixinElementImpl extends ClassOrMixinElementImpl + with MixinOrAugmentationElementMixin + implements MixinElement { /// Names of methods, getters, setters, and operators that this mixin /// declaration super-invokes. For setters this includes the trailing "=". /// The list will be empty if this class is not a mixin declaration. late List superInvokedNames; + final AugmentedMixinElementImpl augmentedInternal = + AugmentedMixinElementImpl(); + /// Initialize a newly created class element to have the given [name] at the /// given [offset] in the file that contains the declaration of this element. MixinElementImpl(super.name, super.offset); @override - Never get augmentation { - // TODO(scheglov) implement - throw UnimplementedError(); - } - - @override - Never get augmented { - // TODO(scheglov) implement - throw UnimplementedError(); + AugmentedMixinElementImpl get augmented { + linkedData?.read(this); + return augmentedInternal; } @override @@ -5082,16 +5119,6 @@ class MixinElementImpl extends ClassOrMixinElementImpl implements MixinElement { throw StateError('Attempt to set mixins for a mixin declaration.'); } - @override - List get superclassConstraints { - linkedData?.read(this); - return _superclassConstraints; - } - - set superclassConstraints(List superclassConstraints) { - _superclassConstraints = superclassConstraints; - } - @override InterfaceType? get supertype => null; @@ -5119,6 +5146,44 @@ class MixinElementImpl extends ClassOrMixinElementImpl implements MixinElement { } } +mixin MixinOrAugmentationElementMixin on InterfaceOrAugmentationElementMixin + implements MixinOrAugmentationElement { + MixinAugmentationElementImpl? _augmentation; + List _superclassConstraints = const []; + + @override + MixinAugmentationElementImpl? get augmentation { + linkedData?.read(this); + return _augmentation; + } + + set augmentation(MixinAugmentationElementImpl? value) { + _augmentation = value; + } + + @override + MixinElementImpl? get augmentedDeclaration; + + @override + bool get isBase { + return hasModifier(Modifier.BASE); + } + + set isBase(bool isBase) { + setModifier(Modifier.BASE, isBase); + } + + @override + List get superclassConstraints { + linkedData?.read(this); + return _superclassConstraints; + } + + set superclassConstraints(List superclassConstraints) { + _superclassConstraints = superclassConstraints; + } +} + /// The constants for all of the modifiers defined by the Dart language and for /// a few additional flags that are useful. /// diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart index 794bae593fcd..54d43d6a6a76 100644 --- a/pkg/analyzer/lib/src/fasta/ast_builder.dart +++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart @@ -2503,9 +2503,19 @@ class AstBuilder extends StackListener { debugEvent("MixinDeclaration"); final builder = _classLikeBuilder as _MixinDeclarationBuilder; - declarations.add( - builder.build(), - ); + final augmentKeyword = builder.augmentKeyword; + + if (augmentKeyword != null) { + declarations.add( + builder.buildAugmentation( + augmentKeyword: augmentKeyword, + ), + ); + } else { + declarations.add( + builder.build(), + ); + } _classLikeBuilder = null; } @@ -6031,6 +6041,24 @@ class _MixinDeclarationBuilder extends _ClassLikeDeclarationBuilder { MixinDeclarationImpl build() { return MixinDeclarationImpl( + comment: comment, + metadata: metadata, + baseKeyword: baseKeyword, + mixinKeyword: mixinKeyword, + name: name, + typeParameters: typeParameters, + onClause: onClause, + implementsClause: implementsClause, + leftBracket: leftBracket, + members: members, + rightBracket: rightBracket, + ); + } + + MixinAugmentationDeclarationImpl buildAugmentation({ + required Token augmentKeyword, + }) { + return MixinAugmentationDeclarationImpl( comment: comment, metadata: metadata, augmentKeyword: augmentKeyword, diff --git a/pkg/analyzer/lib/src/lint/linter_visitor.dart b/pkg/analyzer/lib/src/lint/linter_visitor.dart index c1643af36cd5..f1a20d7885dc 100644 --- a/pkg/analyzer/lib/src/lint/linter_visitor.dart +++ b/pkg/analyzer/lib/src/lint/linter_visitor.dart @@ -623,6 +623,12 @@ class LinterVisitor implements AstVisitor { node.visitChildren(this); } + @override + void visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) { + _runSubscriptions(node, registry._forMixinAugmentationDeclaration); + node.visitChildren(this); + } + @override void visitMixinDeclaration(MixinDeclaration node) { _runSubscriptions(node, registry._forMixinDeclaration); @@ -1184,6 +1190,8 @@ class NodeLintRegistry { final List<_Subscription> _forMethodDeclaration = []; final List<_Subscription> _forMethodInvocation = []; final List<_Subscription> _forMixinDeclaration = []; + final List<_Subscription> + _forMixinAugmentationDeclaration = []; final List<_Subscription> _forNamedExpression = []; final List<_Subscription> _forNamedType = []; final List<_Subscription> _forNativeClause = []; @@ -1715,6 +1723,11 @@ class NodeLintRegistry { _forMethodInvocation.add(_Subscription(linter, visitor, _getTimer(linter))); } + void addMixinAugmentationDeclaration(LintRule linter, AstVisitor visitor) { + _forMixinAugmentationDeclaration + .add(_Subscription(linter, visitor, _getTimer(linter))); + } + void addMixinDeclaration(LintRule linter, AstVisitor visitor) { _forMixinDeclaration.add(_Subscription(linter, visitor, _getTimer(linter))); } diff --git a/pkg/analyzer/lib/src/summary2/bundle_reader.dart b/pkg/analyzer/lib/src/summary2/bundle_reader.dart index c20400c6b9a9..beeef947672f 100644 --- a/pkg/analyzer/lib/src/summary2/bundle_reader.dart +++ b/pkg/analyzer/lib/src/summary2/bundle_reader.dart @@ -1198,6 +1198,49 @@ class LibraryReader { }); } + MixinAugmentationElementImpl _readMixinAugmentationElement( + CompilationUnitElementImpl unitElement, + Reference unitReference, + ) { + var resolutionOffset = _baseResolutionOffset + _reader.readUInt30(); + var name = _reader.readStringReference(); + var reference = unitReference.getChild('@mixinAugmentation').getChild(name); + + var element = MixinAugmentationElementImpl(name, -1); + + var linkedData = MixinAugmentationElementLinkedData( + reference: reference, + libraryReader: this, + unitElement: unitElement, + offset: resolutionOffset, + ); + element.setLinkedData(reference, linkedData); + MixinAugmentationElementFlags.read(_reader, element); + + element.typeParameters = _readTypeParameters(); + + var fields = []; + var accessors = []; + _readFields(unitElement, element, reference, accessors, fields); + _readPropertyAccessors( + unitElement, element, reference, accessors, fields, '@field'); + element.fields = fields.toFixedList(); + element.accessors = accessors.toFixedList(); + + element.methods = _readMethods(unitElement, element, reference); + + return element; + } + + void _readMixinAugmentations( + CompilationUnitElementImpl unitElement, + Reference unitReference, + ) { + unitElement.mixinAugmentations = _reader.readTypedList(() { + return _readMixinAugmentationElement(unitElement, unitReference); + }); + } + MixinElementImpl _readMixinElement( CompilationUnitElementImpl unitElement, Reference unitReference, @@ -1584,6 +1627,7 @@ class LibraryReader { _readExtensions(unitElement, unitReference); _readFunctions(unitElement, unitReference); _readMixins(unitElement, unitReference); + _readMixinAugmentations(unitElement, unitReference); _readTypeAliases(unitElement, unitReference); var accessors = []; @@ -1636,6 +1680,33 @@ class MethodElementLinkedData extends ElementLinkedData { } } +class MixinAugmentationElementLinkedData + extends ElementLinkedData { + ApplyConstantOffsets? applyConstantOffsets; + + MixinAugmentationElementLinkedData({ + required Reference reference, + required LibraryReader libraryReader, + required CompilationUnitElementImpl unitElement, + required int offset, + }) : super(reference, libraryReader, unitElement, offset); + + @override + void _read(element, reader) { + element.metadata = reader._readAnnotationList( + unitElement: element.enclosingElement2, + ); + _readTypeParameters(reader, element.typeParameters); + element.superclassConstraints = reader._readInterfaceTypeList(); + element.interfaces = reader._readInterfaceTypeList(); + element.augmentationTarget = + reader.readElement() as MixinOrAugmentationElementMixin?; + element.augmentation = + reader.readElement() as MixinAugmentationElementImpl?; + applyConstantOffsets?.perform(); + } +} + class MixinElementLinkedData extends ElementLinkedData { ApplyConstantOffsets? applyConstantOffsets; @@ -1654,6 +1725,10 @@ class MixinElementLinkedData extends ElementLinkedData { _readTypeParameters(reader, element.typeParameters); element.superclassConstraints = reader._readInterfaceTypeList(); element.interfaces = reader._readInterfaceTypeList(); + element.augmentation = + reader.readElement() as MixinAugmentationElementImpl?; + element.augmented.superclassConstraints = reader._readInterfaceTypeList(); + element.augmented.interfaces = reader._readInterfaceTypeList(); applyConstantOffsets?.perform(); } } diff --git a/pkg/analyzer/lib/src/summary2/bundle_writer.dart b/pkg/analyzer/lib/src/summary2/bundle_writer.dart index 7599297ba4fc..643d432dbc24 100644 --- a/pkg/analyzer/lib/src/summary2/bundle_writer.dart +++ b/pkg/analyzer/lib/src/summary2/bundle_writer.dart @@ -429,6 +429,32 @@ class BundleWriter { }); } + void _writeMixinAugmentationElement(MixinAugmentationElementImpl element) { + _sink.writeUInt30(_resolutionSink.offset); + + _sink._writeStringReference(element.name); + MixinAugmentationElementFlags.write(_sink, element); + + _resolutionSink._writeAnnotationList(element.metadata); + + _writeTypeParameters(element.typeParameters, () { + _resolutionSink._writeTypeList(element.superclassConstraints); + _resolutionSink._writeTypeList(element.interfaces); + _resolutionSink.writeElement(element.augmentationTarget); + _resolutionSink.writeElement(element.augmentation); + + _writeList( + element.fields.where((e) => !e.isSynthetic).toList(), + _writeFieldElement, + ); + _writeList( + element.accessors.where((e) => !e.isSynthetic).toList(), + _writePropertyAccessorElement, + ); + _writeList(element.methods, _writeMethodElement); + }); + } + void _writeMixinElement(MixinElementImpl element) { _sink.writeUInt30(_resolutionSink.offset); @@ -439,6 +465,9 @@ class BundleWriter { _writeTypeParameters(element.typeParameters, () { _resolutionSink._writeTypeList(element.superclassConstraints); _resolutionSink._writeTypeList(element.interfaces); + _resolutionSink.writeElement(element.augmentation); + _resolutionSink._writeTypeList(element.augmented.superclassConstraints); + _resolutionSink._writeTypeList(element.augmented.interfaces); _writeList( element.fields.where((e) => !e.isSynthetic).toList(), @@ -568,6 +597,7 @@ class BundleWriter { _writeList(unitElement.extensions, _writeExtensionElement); _writeList(unitElement.functions, _writeFunctionElement); _writeList(unitElement.mixins, _writeMixinElement); + _writeList(unitElement.mixinAugmentations, _writeMixinAugmentationElement); _writeList(unitElement.typeAliases, _writeTypeAliasElement); _writeList( diff --git a/pkg/analyzer/lib/src/summary2/default_types_builder.dart b/pkg/analyzer/lib/src/summary2/default_types_builder.dart index 06e5dccbce14..51adfd55dfb8 100644 --- a/pkg/analyzer/lib/src/summary2/default_types_builder.dart +++ b/pkg/analyzer/lib/src/summary2/default_types_builder.dart @@ -28,6 +28,11 @@ class DefaultTypesBuilder { _breakSelfCycles(node.typeParameters); _breakRawTypeCycles(element, node.typeParameters); _computeBounds(element, node.typeParameters); + } else if (node is ClassAugmentationDeclaration) { + var element = node.declaredElement!; + _breakSelfCycles(node.typeParameters); + _breakRawTypeCycles(element, node.typeParameters); + _computeBounds(element, node.typeParameters); } else if (node is ClassTypeAlias) { var element = node.declaredElement!; _breakSelfCycles(node.typeParameters); @@ -73,6 +78,8 @@ class DefaultTypesBuilder { for (var node in nodes) { if (node is ClassDeclaration) { _build(node.typeParameters); + } else if (node is ClassAugmentationDeclaration) { + _build(node.typeParameters); } else if (node is ClassTypeAlias) { _build(node.typeParameters); } else if (node is EnumDeclaration) { diff --git a/pkg/analyzer/lib/src/summary2/element_builder.dart b/pkg/analyzer/lib/src/summary2/element_builder.dart index 124a7ca4a179..91ab56956eaa 100644 --- a/pkg/analyzer/lib/src/summary2/element_builder.dart +++ b/pkg/analyzer/lib/src/summary2/element_builder.dart @@ -53,6 +53,7 @@ class ElementBuilder extends ThrowingAstVisitor { _unitElement.extensions = _enclosingContext.extensions; _unitElement.functions = _enclosingContext.functions; _unitElement.mixins = _enclosingContext.mixins; + _unitElement.mixinAugmentations = _enclosingContext.mixinAugmentations; _unitElement.topLevelVariables = _enclosingContext.topLevelVariables; _unitElement.typeAliases = _enclosingContext.typeAliases; } @@ -909,6 +910,53 @@ class ElementBuilder extends ThrowingAstVisitor { _buildType(node.returnType); } + @override + void visitMixinAugmentationDeclaration( + covariant MixinAugmentationDeclarationImpl node, + ) { + final nameToken = node.name; + final name = nameToken.lexeme; + + final element = MixinAugmentationElementImpl(name, nameToken.offset); + element.isBase = node.baseKeyword != null; + element.metadata = _buildAnnotations(node.metadata); + _setCodeRange(element, node); + _setDocumentation(element, node); + + 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, () { + final typeParameters = node.typeParameters; + if (typeParameters != null) { + typeParameters.accept(this); + element.typeParameters = holder.typeParameters; + } + }); + + node.onClause?.accept(this); + node.implementsClause?.accept(this); + + _withEnclosing(holder, () { + _visitPropertyFirst(node.members); + }); + + element.accessors = holder.propertyAccessors; + element.fields = holder.fields; + element.methods = holder.methods; + } + @override void visitMixinDeclaration(covariant MixinDeclarationImpl node) { var nameToken = node.name; @@ -925,6 +973,7 @@ class ElementBuilder extends ThrowingAstVisitor { var reference = _enclosingContext.addMixin(name, element); _libraryBuilder.declare(name, reference); + _libraryBuilder.putAugmentationTarget(name, element); var holder = _EnclosingContext(reference, element); _withEnclosing(holder, () { @@ -1397,6 +1446,7 @@ class _EnclosingContext { final List _functions = []; final List _methods = []; final List _mixins = []; + final List _mixinAugmentations = []; final List _parameters = []; final List _propertyAccessors = []; final List _topLevelVariables = []; @@ -1456,6 +1506,10 @@ class _EnclosingContext { return _methods.toFixedList(); } + List get mixinAugmentations { + return _mixinAugmentations.toFixedList(); + } + List get mixins { return _mixins.toFixedList(); } @@ -1535,6 +1589,14 @@ class _EnclosingContext { return _bindReference('@mixin', name, element); } + Reference addMixinAugmentation( + String name, + MixinAugmentationElementImpl element, + ) { + _mixinAugmentations.add(element); + return _bindReference('@mixinAugmentation', name, element); + } + void addNonSyntheticField(FieldElementImpl element) { var name = element.name; element.createImplicitAccessors(reference, name); diff --git a/pkg/analyzer/lib/src/summary2/element_flags.dart b/pkg/analyzer/lib/src/summary2/element_flags.dart index 38a19ed233ee..4f46a58b282f 100644 --- a/pkg/analyzer/lib/src/summary2/element_flags.dart +++ b/pkg/analyzer/lib/src/summary2/element_flags.dart @@ -260,6 +260,24 @@ class MethodElementFlags { } } +class MixinAugmentationElementFlags { + static const int _isBase = 1 << 0; + + static void read( + SummaryDataReader reader, + MixinAugmentationElementImpl element, + ) { + final byte = reader.readByte(); + element.isBase = (byte & _isBase) != 0; + } + + static void write(BufferedSink sink, MixinAugmentationElementImpl element) { + var result = 0; + result |= element.isBase ? _isBase : 0; + sink.writeUInt30(result); + } +} + class MixinElementFlags { static const int _isBase = 1 << 0; static const int _isSimplyBounded = 1 << 1; diff --git a/pkg/analyzer/lib/src/summary2/informative_data.dart b/pkg/analyzer/lib/src/summary2/informative_data.dart index e896399830b9..13cb76cb367b 100644 --- a/pkg/analyzer/lib/src/summary2/informative_data.dart +++ b/pkg/analyzer/lib/src/summary2/informative_data.dart @@ -127,6 +127,12 @@ class InformativeDataApplier { forCorrespondingPairs(unitElement.mixins, unitInfo.mixinDeclarations, _applyToMixinDeclaration); + forCorrespondingPairs( + unitElement.mixinAugmentations, + unitInfo.mixinAugmentationDeclarations, + _applyToMixinAugmentationDeclaration, + ); + forCorrespondingPairs(unitElement.topLevelVariables, unitInfo.topLevelVariable, _applyToTopLevelVariable); @@ -606,6 +612,33 @@ class InformativeDataApplier { ); } + void _applyToMixinAugmentationDeclaration( + MixinAugmentationElementImpl element, + _InfoClassDeclaration info, + ) { + element.setCodeRange(info.codeOffset, info.codeLength); + element.nameOffset = info.nameOffset; + element.documentationComment = info.documentationComment; + _applyToTypeParameters( + element.typeParameters_unresolved, + info.typeParameters, + ); + + _applyToConstructors(element.constructors, info.constructors); + _applyToFields(element.fields, info.fields); + _applyToAccessors(element.accessors, info.accessors); + _applyToMethods(element.methods, info.methods); + + var linkedData = element.linkedData as MixinAugmentationElementLinkedData; + linkedData.applyConstantOffsets = ApplyConstantOffsets( + info.constantOffsets, + (applier) { + applier.applyToMetadata(element); + applier.applyToTypeParameters(element.typeParameters); + }, + ); + } + void _applyToMixinDeclaration( MixinElement element, _InfoClassDeclaration info, @@ -1313,6 +1346,22 @@ class _InformativeDataWriter { ); }); + sink.writeList2(unit.declarations, (node) { + sink.writeUInt30(node.offset); + sink.writeUInt30(node.length); + sink.writeUInt30(node.name.offset); + _writeDocumentationComment(node); + _writeTypeParameters(node.typeParameters); + _writeConstructors(node.members); + _writeFields(node.members); + _writeGettersSetters(node.members); + _writeMethods(node.members); + _writeOffsets( + metadata: node.metadata, + typeParameters: node.typeParameters, + ); + }); + sink.writeList( unit.declarations .whereType() @@ -1678,6 +1727,7 @@ class _InfoUnit { final List<_InfoFunctionTypeAlias> functionTypeAliases; final List<_InfoGenericTypeAlias> genericTypeAliases; final List<_InfoClassDeclaration> mixinDeclarations; + final List<_InfoClassDeclaration> mixinAugmentationDeclarations; final List<_InfoTopLevelVariable> topLevelVariable; factory _InfoUnit(SummaryDataReader reader) { @@ -1727,6 +1777,9 @@ class _InfoUnit { mixinDeclarations: reader.readTypedList( () => _InfoClassDeclaration(reader), ), + mixinAugmentationDeclarations: reader.readTypedList( + () => _InfoClassDeclaration(reader), + ), topLevelVariable: reader.readTypedList( () => _InfoTopLevelVariable(reader), ), @@ -1753,6 +1806,7 @@ class _InfoUnit { required this.functionTypeAliases, required this.genericTypeAliases, required this.mixinDeclarations, + required this.mixinAugmentationDeclarations, required this.topLevelVariable, }); } diff --git a/pkg/analyzer/lib/src/summary2/metadata_resolver.dart b/pkg/analyzer/lib/src/summary2/metadata_resolver.dart index b32e505eb52e..c1cb7cd93eaf 100644 --- a/pkg/analyzer/lib/src/summary2/metadata_resolver.dart +++ b/pkg/analyzer/lib/src/summary2/metadata_resolver.dart @@ -203,6 +203,12 @@ class MetadataResolver extends ThrowingAstVisitor { node.parameters?.accept(this); } + @override + void visitMixinAugmentationDeclaration(MixinAugmentationDeclaration node) { + // TODO: implement visitMixinAugmentationDeclaration + // super.visitMixinAugmentationDeclaration(node); + } + @override void visitMixinDeclaration(MixinDeclaration node) { node.metadata.accept(this); diff --git a/pkg/analyzer/lib/src/summary2/reference_resolver.dart b/pkg/analyzer/lib/src/summary2/reference_resolver.dart index b84c875ed900..008d5e8e9be1 100644 --- a/pkg/analyzer/lib/src/summary2/reference_resolver.dart +++ b/pkg/analyzer/lib/src/summary2/reference_resolver.dart @@ -330,6 +330,29 @@ class ReferenceResolver extends ThrowingAstVisitor { scope = outerScope; } + @override + void visitMixinAugmentationDeclaration( + covariant MixinAugmentationDeclarationImpl node, + ) { + var outerScope = scope; + + var element = node.declaredElement!; + + scope = TypeParameterScope(scope, element.typeParameters); + + node.typeParameters?.accept(this); + node.onClause?.accept(this); + node.implementsClause?.accept(this); + + // TODO(scheglov) implements + // scope = InterfaceScope(scope, element); + // LinkingNodeContext(node, scope); + // node.members.accept(this); + + nodesToBuildType.addDeclaration(node); + scope = outerScope; + } + @override void visitMixinDeclaration(MixinDeclaration node) { var outerScope = scope; diff --git a/pkg/analyzer/lib/src/summary2/types_builder.dart b/pkg/analyzer/lib/src/summary2/types_builder.dart index 9990379533a5..e40019bdf630 100644 --- a/pkg/analyzer/lib/src/summary2/types_builder.dart +++ b/pkg/analyzer/lib/src/summary2/types_builder.dart @@ -237,6 +237,8 @@ class TypesBuilder { element.returnType = returnType; } else if (node is MixinDeclaration) { _mixinDeclaration(node); + } else if (node is MixinAugmentationDeclarationImpl) { + _mixinAugmentationDeclaration(node); } else if (node is SimpleFormalParameter) { var element = node.declaredElement as ParameterElementImpl; element.type = node.type?.type ?? _dynamicType; @@ -330,6 +332,32 @@ class TypesBuilder { return unit!.featureSet.isEnabled(Feature.non_nullable); } + void _mixinAugmentationDeclaration(MixinAugmentationDeclarationImpl node) { + final element = node.declaredElement!; + + element.superclassConstraints = _toInterfaceTypeList( + node.onClause?.superclassConstraints, + ); + + element.interfaces = _toInterfaceTypeList( + node.implementsClause?.interfaces, + ); + + _updatedAugmented( + element, + element.augmentedDeclaration, + (declaration, substitution) { + final augmented = declaration.augmented; + augmented.superclassConstraints.addAll( + substitution.mapInterfaceTypes(element.superclassConstraints), + ); + augmented.interfaces.addAll( + substitution.mapInterfaceTypes(element.interfaces), + ); + }, + ); + } + void _mixinDeclaration(MixinDeclaration node) { var element = node.declaredElement as MixinElementImpl; @@ -344,6 +372,10 @@ class TypesBuilder { element.interfaces = _toInterfaceTypeList( node.implementsClause?.interfaces, ); + + final augmented = element.augmented; + augmented.superclassConstraints.addAll(element.superclassConstraints); + augmented.interfaces.addAll(element.interfaces); } NullabilitySuffix _nullability(AstNode node, bool hasQuestion) { diff --git a/pkg/analyzer/lib/src/test_utilities/find_node.dart b/pkg/analyzer/lib/src/test_utilities/find_node.dart index fda9fc6dc814..3a9863db9cc6 100644 --- a/pkg/analyzer/lib/src/test_utilities/find_node.dart +++ b/pkg/analyzer/lib/src/test_utilities/find_node.dart @@ -109,6 +109,9 @@ class FindNode { MethodInvocation get singleMethodInvocation => _single(); + MixinAugmentationDeclaration get singleMixinAugmentationDeclaration => + _single(); + MixinDeclaration get singleMixinDeclaration => _single(); OnClause get singleOnClause => _single(); @@ -574,6 +577,10 @@ class FindNode { return _node(search, (n) => n is MixinDeclaration); } + MixinAugmentationDeclaration mixinAugmentationDeclaration(String search) { + return _node(search, (n) => n is MixinAugmentationDeclaration); + } + MixinDeclaration mixinDeclaration(String search) { return _node(search, (n) => n is MixinDeclaration); } diff --git a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart index 58f73a34238c..98ebd2c28b6d 100644 --- a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart +++ b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart @@ -2654,6 +2654,24 @@ void f() { _assertSource(code, findNode.methodInvocation(code)); } + void test_visitMixinAugmentationDeclaration() { + final code = 'augment mixin M {}'; + var findNode = _parseStringToFindNode(code); + _assertSource( + code, + findNode.singleMixinAugmentationDeclaration, + ); + } + + void test_visitMixinAugmentationDeclaration_base() { + final code = 'augment base mixin M {}'; + var findNode = _parseStringToFindNode(code); + _assertSource( + code, + findNode.singleMixinAugmentationDeclaration, + ); + } + void test_visitMixinDeclaration_base() { var findNode = _parseStringToFindNode(r''' base mixin M {} diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart index bdadd0d9a911..a74ce8b94c05 100644 --- a/pkg/analyzer/test/src/summary/element_text.dart +++ b/pkg/analyzer/test/src/summary/element_text.dart @@ -151,11 +151,17 @@ class _ElementWriter { } void _writeAugmentation(InterfaceOrAugmentationElementMixin e) { - if (e case ClassOrAugmentationElementMixin e) { - final augmentation = e.augmentation; - if (augmentation != null) { - _elementPrinter.writeNamedElement('augmentation', augmentation); - } + switch (e) { + case ClassOrAugmentationElementMixin e: + final augmentation = e.augmentation; + if (augmentation != null) { + _elementPrinter.writeNamedElement('augmentation', augmentation); + } + case MixinOrAugmentationElementMixin e: + final augmentation = e.augmentation; + if (augmentation != null) { + _elementPrinter.writeNamedElement('augmentation', augmentation); + } } } @@ -188,7 +194,7 @@ class _ElementWriter { void _writeAugmented(InstanceElementImpl e) { // TODO(scheglov) enable for other types - if (e is! ClassElementImpl) { + if (!(e is ClassElementImpl || e is MixinElementImpl)) { return; } @@ -201,9 +207,15 @@ class _ElementWriter { _sink.withIndent(() { final augmented = e.augmented; switch (augmented) { - case AugmentedInterfaceElementImpl(): + case AugmentedClassElementImpl(): _elementPrinter.writeTypeList('mixins', augmented.mixins); _elementPrinter.writeTypeList('interfaces', augmented.interfaces); + case AugmentedMixinElementImpl(): + _elementPrinter.writeTypeList( + 'superclassConstraints', + augmented.superclassConstraints, + ); + _elementPrinter.writeTypeList('interfaces', augmented.interfaces); } // TODO(scheglov) Add other types and properties }); @@ -534,7 +546,7 @@ class _ElementWriter { if (e is EnumElementImpl) { _sink.write('enum '); - } else if (e is MixinElementImpl) { + } else if (e is MixinOrAugmentationElementMixin) { _sink.writeIf(e.isBase, 'base '); _sink.write('mixin '); } else { @@ -565,10 +577,12 @@ class _ElementWriter { } } - if (e is MixinElementImpl) { - var superclassConstraints = e.superclassConstraints; - if (superclassConstraints.isEmpty) { - throw StateError('At least Object is expected.'); + if (e is MixinOrAugmentationElementMixin) { + final superclassConstraints = e.superclassConstraints; + if (e is MixinElementImpl) { + if (superclassConstraints.isEmpty) { + throw StateError('At least Object is expected.'); + } } _elementPrinter.writeTypeList( 'superclassConstraints', @@ -1079,6 +1093,11 @@ class _ElementWriter { _writeElements('enums', e.enums, _writeInterfaceOrAugmentationElement); _writeElements('extensions', e.extensions, _writeExtensionElement); _writeElements('mixins', e.mixins, _writeInterfaceOrAugmentationElement); + _writeElements( + 'mixinAugmentations', + e.mixinAugmentations, + _writeInterfaceOrAugmentationElement, + ); _writeElements('typeAliases', e.typeAliases, _writeTypeAliasElement); _writeElements( 'topLevelVariables', diff --git a/pkg/analyzer/test/src/summary/elements_test.dart b/pkg/analyzer/test/src/summary/elements_test.dart index 0b3b4fd0606f..caa55843953f 100644 --- a/pkg/analyzer/test/src/summary/elements_test.dart +++ b/pkg/analyzer/test/src/summary/elements_test.dart @@ -230,6 +230,63 @@ library augment class A @43 typeParameters covariant T2 @45 + defaultType: dynamic + augmentationTarget: self::@class::A + augmentedDeclaration: self::@class::A + interfaces + I2 +'''); + } + + test_augmented_interfaces_generic_mismatch() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +augment class A implements I2 {} +class I2 {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +class A implements I1 {} +class I1 {} +'''); + + checkElementText(library, r''' +library + definingUnit + classes + class A @31 + typeParameters + covariant T @33 + defaultType: dynamic + augmentation: self::@augmentation::package:test/a.dart::@classAugmentation::A + interfaces + I1 + constructors + synthetic @-1 + augmented + interfaces + I1 + class I1 @59 + constructors + synthetic @-1 + augmentationImports + package:test/a.dart + definingUnit + classes + class I2 @80 + typeParameters + covariant E @83 + defaultType: dynamic + constructors + synthetic @-1 + classAugmentations + augment class A @43 + typeParameters + covariant T2 @45 + defaultType: dynamic + covariant T3 @49 + defaultType: dynamic augmentationTarget: self::@class::A augmentedDeclaration: self::@class::A interfaces @@ -282,20 +339,100 @@ library mixin M2 @62 superclassConstraints Object +'''); + } + + test_notSimplyBounded_self() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +augment class A {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +class A {} +'''); + + checkElementText(library, r''' +library + definingUnit + classes + notSimplyBounded class A @31 + typeParameters + covariant T @33 + bound: A + defaultType: dynamic + augmentation: self::@augmentation::package:test/a.dart::@classAugmentation::A + constructors + synthetic @-1 + augmented + augmentationImports + package:test/a.dart + definingUnit + classAugmentations + augment class A @43 + typeParameters + covariant T @45 + bound: A + defaultType: A + augmentationTarget: self::@class::A + augmentedDeclaration: self::@class::A +'''); + } + + test_typeParameters_defaultType() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +augment class A {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +class A {} +class B {} +'''); + + checkElementText(library, r''' +library + definingUnit + classes + class A @31 + typeParameters + covariant T @33 + bound: B + defaultType: B + augmentation: self::@augmentation::package:test/a.dart::@classAugmentation::A + constructors + synthetic @-1 + augmented + class B @55 + constructors + synthetic @-1 + augmentationImports + package:test/a.dart + definingUnit + classAugmentations + augment class A @43 + typeParameters + covariant T @45 + bound: B + defaultType: B + augmentationTarget: self::@class::A + augmentedDeclaration: self::@class::A '''); } } @reflectiveTest class ElementsFromBytesTest extends ElementsTest - with ClassAugmentationElementsMixin { + with ClassAugmentationElementsMixin, MixinAugmentationElementsMixin { @override bool get keepLinkingLibraries => false; } @reflectiveTest class ElementsKeepLinkingTest extends ElementsTest - with ClassAugmentationElementsMixin { + with ClassAugmentationElementsMixin, MixinAugmentationElementsMixin { @override bool get keepLinkingLibraries => true; } @@ -44840,6 +44977,410 @@ library } } +mixin MixinAugmentationElementsMixin on ElementsBaseTest { + test_augmentationTarget() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +import augment 'b.dart'; +augment mixin A {} +'''); + + newFile('$testPackageLibPath/b.dart', r''' +library augment 'a.dart'; +augment mixin A {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +mixin A {} +'''); + + checkElementText(library, r''' +library + definingUnit + mixins + mixin A @31 + augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + superclassConstraints + Object + augmented + superclassConstraints + Object + 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 + augmentationImports + package:test/b.dart + definingUnit + mixinAugmentations + augment mixin A @40 + augmentationTarget: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + augmentedDeclaration: self::@mixin::A +'''); + } + + test_augmented_interfaces() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +augment mixin A implements I2 {} +class I2 {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +mixin A implements I1 {} +class I1 {} +'''); + + checkElementText(library, r''' +library + definingUnit + classes + class I1 @56 + constructors + synthetic @-1 + mixins + mixin A @31 + augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + superclassConstraints + Object + interfaces + I1 + augmented + superclassConstraints + Object + interfaces + I1 + I2 + augmentationImports + package:test/a.dart + definingUnit + classes + class I2 @68 + constructors + synthetic @-1 + mixinAugmentations + augment mixin A @43 + augmentationTarget: self::@mixin::A + augmentedDeclaration: self::@mixin::A + interfaces + I2 +'''); + } + + test_augmented_interfaces_chain() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +import augment 'b.dart'; +augment mixin A implements I2 {} +class I2 {} +'''); + + newFile('$testPackageLibPath/b.dart', r''' +library augment 'a.dart'; +augment mixin A implements I3 {} +class I3 {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +mixin A implements I1 {} +class I1 {} +'''); + + checkElementText(library, r''' +library + definingUnit + classes + class I1 @56 + constructors + synthetic @-1 + mixins + mixin A @31 + augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + superclassConstraints + Object + interfaces + I1 + augmented + superclassConstraints + Object + interfaces + I1 + I2 + I3 + augmentationImports + package:test/a.dart + definingUnit + classes + class I2 @93 + constructors + synthetic @-1 + mixinAugmentations + augment mixin A @68 + augmentationTarget: self::@mixin::A + augmentedDeclaration: self::@mixin::A + augmentation: self::@augmentation::package:test/b.dart::@mixinAugmentation::A + interfaces + I2 + augmentationImports + package:test/b.dart + definingUnit + classes + class I3 @65 + constructors + synthetic @-1 + mixinAugmentations + augment mixin A @40 + augmentationTarget: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + augmentedDeclaration: self::@mixin::A + interfaces + I3 +'''); + } + + test_augmented_superclassConstraints() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +augment mixin A on B2 {} +class B2 {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +mixin A on B1 {} +class B1 {} +'''); + + checkElementText(library, r''' +library + definingUnit + classes + class B1 @48 + constructors + synthetic @-1 + mixins + mixin A @31 + augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + superclassConstraints + B1 + augmented + superclassConstraints + B1 + B2 + augmentationImports + package:test/a.dart + definingUnit + classes + class B2 @60 + constructors + synthetic @-1 + mixinAugmentations + augment mixin A @43 + augmentationTarget: self::@mixin::A + augmentedDeclaration: self::@mixin::A + superclassConstraints + B2 +'''); + } + + test_augmented_superclassConstraints_chain() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +import augment 'b.dart'; +augment mixin A on I2 {} +class I2 {} +'''); + + newFile('$testPackageLibPath/b.dart', r''' +library augment 'a.dart'; +augment mixin A on I3 {} +class I3 {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +mixin A on I1 {} +class I1 {} +'''); + + checkElementText(library, r''' +library + definingUnit + classes + class I1 @48 + constructors + synthetic @-1 + mixins + mixin A @31 + augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + superclassConstraints + I1 + augmented + superclassConstraints + I1 + I2 + I3 + augmentationImports + package:test/a.dart + definingUnit + classes + class I2 @85 + constructors + synthetic @-1 + mixinAugmentations + augment mixin A @68 + augmentationTarget: self::@mixin::A + augmentedDeclaration: self::@mixin::A + augmentation: self::@augmentation::package:test/b.dart::@mixinAugmentation::A + superclassConstraints + I2 + augmentationImports + package:test/b.dart + definingUnit + classes + class I3 @57 + constructors + synthetic @-1 + mixinAugmentations + augment mixin A @40 + augmentationTarget: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + augmentedDeclaration: self::@mixin::A + superclassConstraints + I3 +'''); + } + + test_augmented_superclassConstraints_generic() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +augment mixin A on I2 {} +class I2 {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +mixin A on I1 {} +class I1 {} +'''); + + checkElementText(library, r''' +library + definingUnit + classes + class I1 @51 + constructors + synthetic @-1 + mixins + mixin A @31 + typeParameters + covariant T @33 + defaultType: dynamic + augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + superclassConstraints + I1 + augmented + superclassConstraints + I1 + I2 + augmentationImports + package:test/a.dart + definingUnit + classes + class I2 @68 + typeParameters + covariant E @71 + defaultType: dynamic + constructors + synthetic @-1 + mixinAugmentations + augment mixin A @43 + typeParameters + covariant T2 @45 + augmentationTarget: self::@mixin::A + augmentedDeclaration: self::@mixin::A + superclassConstraints + I2 +'''); + } + + test_modifiers_base() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +augment base mixin A {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +base mixin A {} +'''); + + checkElementText(library, r''' +library + definingUnit + mixins + base mixin A @36 + augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + superclassConstraints + Object + augmented + superclassConstraints + Object + augmentationImports + package:test/a.dart + definingUnit + mixinAugmentations + augment base mixin A @48 + augmentationTarget: self::@mixin::A + augmentedDeclaration: self::@mixin::A +'''); + } + + test_notSimplyBounded_self() async { + newFile('$testPackageLibPath/a.dart', r''' +library augment 'test.dart'; +augment mixin A {} +'''); + + var library = await buildLibrary(r''' +import augment 'a.dart'; +mixin A {} +'''); + + checkElementText(library, r''' +library + definingUnit + mixins + notSimplyBounded mixin A @31 + typeParameters + covariant T @33 + bound: A + defaultType: dynamic + augmentation: self::@augmentation::package:test/a.dart::@mixinAugmentation::A + superclassConstraints + Object + augmented + superclassConstraints + Object + augmentationImports + package:test/a.dart + definingUnit + mixinAugmentations + augment mixin A @43 + typeParameters + covariant T @45 + bound: A + augmentationTarget: self::@mixin::A + augmentedDeclaration: self::@mixin::A +'''); + } +} + extension on ElementTextConfiguration { void forPromotableFields({ Set classNames = const {},