blob: 7490fc9afe408dfe870130085b943d55d978ae7a [file] [log] [blame]
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/macros/api.dart' as macro;
import 'package:_fe_analyzer_shared/src/macros/executor.dart' as macro;
import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart';
import 'package:_fe_analyzer_shared/src/macros/executor/protocol.dart' as macro;
import 'package:analyzer/dart/ast/ast.dart' as ast;
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart' as ast;
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/summary2/macro.dart';
import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/summary2/macro_declarations.dart';
import 'package:analyzer/src/utilities/extensions/object.dart';
import 'package:collection/collection.dart';
/// The full list of [macro.ArgumentKind]s for this dart type (includes the type
/// itself as well as type arguments, in source order with
/// [macro.ArgumentKind.nullable] modifiers preceding the nullable types).
List<macro.ArgumentKind> _dartTypeArgumentKinds(DartType dartType) => [
if (dartType.nullabilitySuffix == NullabilitySuffix.question)
macro.ArgumentKind.nullable,
switch (dartType) {
DartType(isDartCoreBool: true) => macro.ArgumentKind.bool,
DartType(isDartCoreDouble: true) => macro.ArgumentKind.double,
DartType(isDartCoreInt: true) => macro.ArgumentKind.int,
DartType(isDartCoreNum: true) => macro.ArgumentKind.num,
DartType(isDartCoreNull: true) => macro.ArgumentKind.nil,
DartType(isDartCoreObject: true) => macro.ArgumentKind.object,
DartType(isDartCoreString: true) => macro.ArgumentKind.string,
// TODO: Support nested type arguments for collections.
DartType(isDartCoreList: true) => macro.ArgumentKind.list,
DartType(isDartCoreMap: true) => macro.ArgumentKind.map,
DartType(isDartCoreSet: true) => macro.ArgumentKind.set,
DynamicType() => macro.ArgumentKind.dynamic,
// TODO: Support type annotations and code objects
_ =>
throw UnsupportedError('Unsupported macro type argument $dartType'),
},
if (dartType is ParameterizedType) ...[
for (var type in dartType.typeArguments)
..._dartTypeArgumentKinds(type),
]
];
List<macro.ArgumentKind> _typeArgumentsForNode(ast.TypedLiteral node) {
if (node.typeArguments == null) {
return [
// TODO: Use downward inference to build these types and detect maps
// versus sets.
if (node is ast.ListLiteral || node is ast.SetOrMapLiteral)
macro.ArgumentKind.dynamic,
if (node is ast.SetOrMapLiteral &&
node.elements.first is ast.MapLiteralEntry)
macro.ArgumentKind.dynamic,
];
}
return [
for (var type in node.typeArguments!.arguments.map((arg) => arg.type!))
..._dartTypeArgumentKinds(type),
];
}
class LibraryMacroApplier {
final LinkedElementFactory elementFactory;
final MultiMacroExecutor macroExecutor;
final bool Function(Uri) isLibraryBeingLinked;
final DeclarationBuilder declarationBuilder;
/// The reversed queue of macro applications to apply.
///
/// We add classes before methods, and methods in the reverse order,
/// classes in the reverse order, annotations in the direct order.
///
/// We iterate from the end looking for the next application to apply.
/// This way we ensure two ordering rules:
/// 1. inner before outer
/// 2. right to left
/// 3. source order
final List<_MacroApplication> _applications = [];
/// The map from [InstanceElement] to the applications associated with it.
/// This includes applications on the class itself, and on the methods of
/// the class.
final Map<InstanceElement, List<_MacroApplication>> _interfaceApplications =
{};
late final macro.TypePhaseIntrospector _typesPhaseIntrospector =
_TypePhaseIntrospector(elementFactory, declarationBuilder);
LibraryMacroApplier({
required this.elementFactory,
required this.macroExecutor,
required this.isLibraryBeingLinked,
required this.declarationBuilder,
});
Future<void> add({
required LibraryElementImpl libraryElement,
required LibraryOrAugmentationElementImpl container,
required ast.CompilationUnit unit,
}) async {
for (final declaration in unit.declarations.reversed) {
switch (declaration) {
case ast.ClassDeclaration():
final element = declaration.declaredElement;
element as ClassElementImpl;
final declarationElement = element.augmented?.declaration ?? element;
declarationElement as ClassElementImpl;
await _addClassLike(
libraryElement: libraryElement,
container: container,
targetElement: declarationElement,
classNode: declaration,
classDeclarationKind: macro.DeclarationKind.classType,
classAnnotations: declaration.metadata,
declarationsPhaseInterface: declarationElement,
members: declaration.members,
);
case ast.MixinDeclaration():
final element = declaration.declaredElement;
element as MixinElementImpl;
final declarationElement = element.augmented?.declaration ?? element;
declarationElement as MixinElementImpl;
await _addClassLike(
libraryElement: libraryElement,
container: container,
targetElement: declarationElement,
classNode: declaration,
classDeclarationKind: macro.DeclarationKind.mixinType,
classAnnotations: declaration.metadata,
declarationsPhaseInterface: declarationElement,
members: declaration.members,
);
}
}
}
/// Builds the augmentation library code for [results].
String? buildAugmentationLibraryCode(
List<macro.MacroExecutionResult> results,
) {
if (results.isEmpty) {
return null;
}
return macroExecutor
.buildAugmentationLibrary(
results,
_resolveDeclaration,
_resolveIdentifier,
_inferOmittedType,
)
.trim();
}
Future<List<macro.MacroExecutionResult>?> executeDeclarationsPhase({
required LibraryElementImpl library,
}) async {
final application = _nextForDeclarationsPhase(
library: library,
);
if (application == null) {
return null;
}
final results = <macro.MacroExecutionResult>[];
await _runWithCatchingExceptions(
() async {
final declaration = _buildDeclaration(application.targetNode);
final introspector = _DeclarationPhaseIntrospector(
elementFactory,
declarationBuilder,
library.typeSystem,
);
final result = await macroExecutor.executeDeclarationsPhase(
application.instance,
declaration,
introspector,
);
if (result.isNotEmpty) {
results.add(result);
}
},
annotationIndex: 0, // TODO(scheglov)
onError: (error) {
application.targetElement.addMacroApplicationError(error);
},
);
return results;
}
Future<List<macro.MacroExecutionResult>?> executeTypesPhase() async {
final application = _nextForTypesPhase();
if (application == null) {
return null;
}
final results = <macro.MacroExecutionResult>[];
await _runWithCatchingExceptions(
() async {
final declaration = _buildDeclaration(application.targetNode);
final result = await macroExecutor.executeTypesPhase(
application.instance,
declaration,
_typesPhaseIntrospector,
);
if (result.isNotEmpty) {
results.add(result);
}
},
annotationIndex: 0, // TODO(scheglov)
onError: (error) {
application.targetElement.addMacroApplicationError(error);
},
);
return results;
}
Future<void> _addAnnotations({
required LibraryElementImpl libraryElement,
required LibraryOrAugmentationElementImpl container,
required InstanceElement? declarationsPhaseElement,
required ast.Declaration targetNode,
required macro.DeclarationKind targetDeclarationKind,
required List<ast.Annotation> annotations,
}) async {
final targetElement =
targetNode.declaredElement.ifTypeOrNull<MacroTargetElement>();
if (targetElement == null) {
return;
}
for (final annotation in annotations) {
final importedMacro = _importedMacro(
container: container,
annotation: annotation,
);
if (importedMacro == null) {
continue;
}
final arguments = await _runWithCatchingExceptions(
() async {
return _buildArguments(
annotationIndex: 0, // TODO(scheglov)
node: importedMacro.arguments,
);
},
annotationIndex: 0, // TODO(scheglov)
onError: (error) {
targetElement.addMacroApplicationError(error);
},
);
if (arguments == null) {
continue;
}
final instance = await importedMacro.bundleExecutor.instantiate(
libraryUri: importedMacro.macroLibrary.source.uri,
className: importedMacro.macroClass.name,
constructorName: importedMacro.constructorName ?? '',
arguments: arguments,
);
final phasesToExecute = macro.Phase.values.where((phase) {
return instance.shouldExecute(targetDeclarationKind, phase);
}).toSet();
final application = _MacroApplication(
libraryElement: libraryElement,
declarationsPhaseElement: declarationsPhaseElement,
targetNode: targetNode,
targetElement: targetElement,
targetDeclarationKind: targetDeclarationKind,
annotationNode: annotation,
instance: instance,
phasesToExecute: phasesToExecute,
);
_applications.add(application);
// Record mapping for declarations phase dependencies.
if (declarationsPhaseElement != null) {
(_interfaceApplications[declarationsPhaseElement] ??= [])
.add(application);
}
}
}
Future<void> _addClassLike({
required LibraryElementImpl libraryElement,
required LibraryOrAugmentationElementImpl container,
required MacroTargetElement targetElement,
required ast.Declaration classNode,
required macro.DeclarationKind classDeclarationKind,
required List<ast.Annotation> classAnnotations,
required InterfaceElement? declarationsPhaseInterface,
required List<ast.ClassMember> members,
}) async {
await _addAnnotations(
libraryElement: libraryElement,
container: container,
targetNode: classNode,
targetDeclarationKind: classDeclarationKind,
declarationsPhaseElement: declarationsPhaseInterface,
annotations: classAnnotations,
);
for (final member in members.reversed) {
await _addAnnotations(
libraryElement: libraryElement,
container: container,
targetNode: member,
// TODO(scheglov) incomplete
targetDeclarationKind: macro.DeclarationKind.method,
declarationsPhaseElement: declarationsPhaseInterface,
annotations: member.metadata,
);
}
}
macro.Declaration _buildDeclaration(ast.AstNode targetNode) {
final fromNode = declarationBuilder.fromNode;
switch (targetNode) {
case ast.ClassDeclaration():
return fromNode.classDeclaration(targetNode);
case ast.MethodDeclaration():
return fromNode.methodDeclaration(targetNode);
case ast.MixinDeclaration():
return fromNode.mixinDeclaration(targetNode);
default:
// TODO(scheglov) incomplete
throw UnimplementedError('${targetNode.runtimeType}');
}
}
bool _hasInterfaceDependenciesSatisfied(_MacroApplication application) {
final dependencyElements = _interfaceDependencies(
application.declarationsPhaseElement,
);
if (dependencyElements == null) {
return true;
}
for (final dependencyElement in dependencyElements) {
final applications = _interfaceApplications[dependencyElement];
if (applications != null) {
for (final dependencyApplication in applications) {
if (dependencyApplication.hasDeclarationsPhase) {
return false;
}
}
}
}
return true;
}
/// If [annotation] references a macro, invokes the right callback.
_AnnotationMacro? _importedMacro({
required LibraryOrAugmentationElementImpl container,
required ast.Annotation annotation,
}) {
final arguments = annotation.arguments;
if (arguments == null) {
return null;
}
final String? prefix;
final String name;
final String? constructorName;
final nameNode = annotation.name;
if (nameNode is ast.SimpleIdentifier) {
prefix = null;
name = nameNode.name;
constructorName = annotation.constructorName?.name;
} else if (nameNode is ast.PrefixedIdentifier) {
final importPrefixCandidate = nameNode.prefix.name;
final hasImportPrefix = container.libraryImports.any(
(import) => import.prefix?.element.name == importPrefixCandidate);
if (hasImportPrefix) {
prefix = importPrefixCandidate;
name = nameNode.identifier.name;
constructorName = annotation.constructorName?.name;
} else {
prefix = null;
name = nameNode.prefix.name;
constructorName = nameNode.identifier.name;
}
} else {
throw StateError('${nameNode.runtimeType} $nameNode');
}
for (final import in container.libraryImports) {
if (import.prefix?.element.name != prefix) {
continue;
}
final importedLibrary = import.importedLibrary;
if (importedLibrary == null) {
continue;
}
// Skip if a library that is being linked.
final importedUri = importedLibrary.source.uri;
if (isLibraryBeingLinked(importedUri)) {
continue;
}
final macroClass = importedLibrary.scope.lookup(name).getter;
if (macroClass is! ClassElementImpl) {
continue;
}
final macroLibrary = macroClass.library;
final bundleExecutor = macroLibrary.bundleMacroExecutor;
if (bundleExecutor == null) {
continue;
}
if (macroClass.isMacro) {
return _AnnotationMacro(
macroLibrary: macroLibrary,
bundleExecutor: bundleExecutor,
macroClass: macroClass,
constructorName: constructorName,
arguments: arguments,
);
}
}
return null;
}
macro.TypeAnnotation _inferOmittedType(
macro.OmittedTypeAnnotation omittedType,
) {
throw UnimplementedError();
}
Set<InstanceElement>? _interfaceDependencies(InstanceElement? element) {
// TODO(scheglov) other elements
switch (element) {
case ExtensionElement():
// TODO(scheglov) implement
throw UnimplementedError();
case MixinElement():
final augmented = element.augmented;
switch (augmented) {
case null:
return const {};
default:
return [
...augmented.superclassConstraints.map((e) => e.element),
...augmented.interfaces.map((e) => e.element),
].whereNotNull().toSet();
}
case InterfaceElement():
final augmented = element.augmented;
switch (augmented) {
case null:
return const {};
default:
return [
element.supertype?.element,
...augmented.mixins.map((e) => e.element),
...augmented.interfaces.map((e) => e.element),
].whereNotNull().toSet();
}
default:
return null;
}
}
_MacroApplication? _nextForDeclarationsPhase({
required LibraryElementImpl library,
}) {
for (final application in _applications.reversed) {
if (!application.hasDeclarationsPhase) {
continue;
}
if (application.libraryElement != library) {
continue;
}
if (!_hasInterfaceDependenciesSatisfied(application)) {
continue;
}
// The application has no dependencies to run.
application.removeDeclarationsPhase();
return application;
}
return null;
}
_MacroApplication? _nextForTypesPhase() {
for (final application in _applications.reversed) {
if (application.phasesToExecute.remove(macro.Phase.types)) {
return application;
}
}
return null;
}
macro.TypeDeclaration _resolveDeclaration(macro.Identifier identifier) {
final element = (identifier as IdentifierImpl).element;
if (element is ClassElementImpl) {
return declarationBuilder.fromElement.classElement(element);
} else if (element is MixinElementImpl) {
return declarationBuilder.fromElement.mixinElement(element);
} else {
throw ArgumentError('element: $element');
}
}
macro.ResolvedIdentifier _resolveIdentifier(macro.Identifier identifier) {
if (identifier is IdentifierImplFromElement) {
// TODO(scheglov) other elements
final element = identifier.element as InterfaceElementImpl;
return macro.ResolvedIdentifier(
// TODO(scheglov) other kinds
kind: macro.IdentifierKind.topLevelMember,
name: element.name,
uri: element.source.uri,
staticScope: null,
);
}
throw UnimplementedError();
}
static macro.Arguments _buildArguments({
required int annotationIndex,
required ast.ArgumentList node,
}) {
final positional = <macro.Argument>[];
final named = <String, macro.Argument>{};
for (var i = 0; i < node.arguments.length; ++i) {
final argument = node.arguments[i];
final evaluation = _ArgumentEvaluation(
annotationIndex: annotationIndex,
argumentIndex: i,
);
if (argument is ast.NamedExpression) {
final value = evaluation.evaluate(argument.expression);
named[argument.name.label.name] = value;
} else {
final value = evaluation.evaluate(argument);
positional.add(value);
}
}
return macro.Arguments(positional, named);
}
/// Run the [body], report exceptions as [MacroApplicationError]s to [onError].
static Future<T?> _runWithCatchingExceptions<T>(
Future<T> Function() body, {
required int annotationIndex,
required void Function(MacroApplicationError) onError,
}) async {
try {
return await body();
} on MacroApplicationError catch (e) {
onError(e);
} on macro.RemoteException catch (e) {
onError(
UnknownMacroApplicationError(
annotationIndex: annotationIndex,
message: e.error,
stackTrace: e.stackTrace ?? '<null>',
),
);
} catch (e, stackTrace) {
onError(
UnknownMacroApplicationError(
annotationIndex: annotationIndex,
message: e.toString(),
stackTrace: stackTrace.toString(),
),
);
}
return null;
}
}
class _AnnotationMacro {
final LibraryElementImpl macroLibrary;
final BundleMacroExecutor bundleExecutor;
final ClassElementImpl macroClass;
final String? constructorName;
final ast.ArgumentList arguments;
_AnnotationMacro({
required this.macroLibrary,
required this.bundleExecutor,
required this.macroClass,
required this.constructorName,
required this.arguments,
});
}
/// Helper class for evaluating arguments for a single constructor based
/// macro application.
class _ArgumentEvaluation {
final int annotationIndex;
final int argumentIndex;
_ArgumentEvaluation({
required this.annotationIndex,
required this.argumentIndex,
});
macro.Argument evaluate(ast.Expression node) {
if (node is ast.AdjacentStrings) {
return macro.StringArgument(
node.strings.map(evaluate).map((arg) => arg.value).join(''));
} else if (node is ast.BooleanLiteral) {
return macro.BoolArgument(node.value);
} else if (node is ast.DoubleLiteral) {
return macro.DoubleArgument(node.value);
} else if (node is ast.IntegerLiteral) {
return macro.IntArgument(node.value!);
} else if (node is ast.ListLiteral) {
final typeArguments = _typeArgumentsForNode(node);
return macro.ListArgument(
node.elements.cast<ast.Expression>().map(evaluate).toList(),
typeArguments);
} else if (node is ast.NullLiteral) {
return macro.NullArgument();
} else if (node is ast.PrefixExpression &&
node.operator.type == TokenType.MINUS) {
final operandValue = evaluate(node.operand);
if (operandValue is macro.DoubleArgument) {
return macro.DoubleArgument(-operandValue.value);
} else if (operandValue is macro.IntArgument) {
return macro.IntArgument(-operandValue.value);
}
} else if (node is ast.SetOrMapLiteral) {
return _setOrMapLiteral(node);
} else if (node is ast.SimpleStringLiteral) {
return macro.StringArgument(node.value);
}
_throwError(node, 'Not supported: ${node.runtimeType}');
}
macro.Argument _setOrMapLiteral(ast.SetOrMapLiteral node) {
final typeArguments = _typeArgumentsForNode(node);
if (node.elements.every((e) => e is ast.Expression)) {
final result = <macro.Argument>[];
for (final element in node.elements) {
if (element is! ast.Expression) {
_throwError(element, 'Expression expected');
}
final value = evaluate(element);
result.add(value);
}
return macro.SetArgument(result, typeArguments);
}
final result = <macro.Argument, macro.Argument>{};
for (final element in node.elements) {
if (element is! ast.MapLiteralEntry) {
_throwError(element, 'MapLiteralEntry expected');
}
final key = evaluate(element.key);
final value = evaluate(element.value);
result[key] = value;
}
return macro.MapArgument(result, typeArguments);
}
Never _throwError(ast.AstNode node, String message) {
throw ArgumentMacroApplicationError(
annotationIndex: annotationIndex,
argumentIndex: argumentIndex,
message: message,
);
}
}
class _DeclarationPhaseIntrospector extends _TypePhaseIntrospector
implements macro.DeclarationPhaseIntrospector {
final TypeSystemImpl typeSystem;
_DeclarationPhaseIntrospector(
super.elementFactory, super.declarationBuilder, this.typeSystem);
@override
Future<List<macro.ConstructorDeclaration>> constructorsOf(
covariant macro.IntrospectableType type) {
// TODO: implement constructorsOf
throw UnimplementedError();
}
@override
Future<List<macro.FieldDeclaration>> fieldsOf(
covariant macro.IntrospectableType type,
) async {
switch (type) {
case IntrospectableClassDeclarationImpl():
return type.element.fields
.where((e) => !e.isSynthetic)
.map(declarationBuilder.fromElement.fieldElement)
.toList();
case IntrospectableMixinDeclarationImpl():
return type.element.fields
.where((e) => !e.isSynthetic)
.map(declarationBuilder.fromElement.fieldElement)
.toList();
}
// TODO(scheglov) implement
throw UnsupportedError('Only introspection on classes is supported');
}
@override
Future<List<macro.MethodDeclaration>> methodsOf(
covariant macro.IntrospectableType type) async {
switch (type) {
case IntrospectableClassDeclarationImpl():
return type.element.augmented!.methods
.where((e) => !e.isSynthetic)
.map(declarationBuilder.fromElement.methodElement)
.toList();
}
// TODO(scheglov) implement
throw UnsupportedError('Only introspection on classes is supported');
}
@override
Future<macro.StaticType> resolve(macro.TypeAnnotationCode type) async {
var dartType = _resolve(type);
return _StaticTypeImpl(typeSystem, dartType);
}
@override
Future<macro.TypeDeclaration> typeDeclarationOf(
covariant IdentifierImpl identifier,
) async {
final element = identifier.element;
if (element is ClassElementImpl) {
return declarationBuilder.fromElement.classElement(element);
} else if (element is MixinElementImpl) {
return declarationBuilder.fromElement.mixinElement(element);
} else {
throw ArgumentError('element: $element');
}
}
@override
Future<List<macro.TypeDeclaration>> typesOf(covariant macro.Library library) {
// TODO: implement typesOf
throw UnimplementedError();
}
@override
Future<List<macro.EnumValueDeclaration>> valuesOf(
covariant macro.IntrospectableEnum type) {
// TODO: implement valuesOf
throw UnimplementedError();
}
DartType _resolve(macro.TypeAnnotationCode type) {
// TODO(scheglov) write tests
if (type is macro.NamedTypeAnnotationCode) {
final identifier = type.name as IdentifierImpl;
final element = identifier.element;
if (element is ClassElementImpl) {
return element.instantiate(
typeArguments: type.typeArguments.map(_resolve).toList(),
nullabilitySuffix: type.isNullable
? NullabilitySuffix.question
: NullabilitySuffix.none,
);
} else {
// TODO(scheglov) Implement other elements.
throw UnimplementedError('(${element.runtimeType}) $element');
}
} else {
// TODO(scheglov) Implement other types.
throw UnimplementedError('(${type.runtimeType}) $type');
}
}
}
class _MacroApplication {
final LibraryElementImpl libraryElement;
final InstanceElement? declarationsPhaseElement;
final ast.AstNode targetNode;
final MacroTargetElement targetElement;
final macro.DeclarationKind targetDeclarationKind;
final ast.Annotation annotationNode;
final macro.MacroInstanceIdentifier instance;
final Set<macro.Phase> phasesToExecute;
_MacroApplication({
required this.libraryElement,
required this.declarationsPhaseElement,
required this.targetNode,
required this.targetElement,
required this.targetDeclarationKind,
required this.annotationNode,
required this.instance,
required this.phasesToExecute,
});
bool get hasDeclarationsPhase {
return phasesToExecute.contains(macro.Phase.declarations);
}
void removeDeclarationsPhase() {
phasesToExecute.remove(macro.Phase.declarations);
}
}
class _StaticTypeImpl implements macro.StaticType {
final TypeSystemImpl typeSystem;
final DartType type;
_StaticTypeImpl(this.typeSystem, this.type);
@override
Future<bool> isExactly(_StaticTypeImpl other) {
// TODO: implement isExactly
throw UnimplementedError();
}
@override
Future<bool> isSubtypeOf(_StaticTypeImpl other) {
// TODO(scheglov) write tests
return Future.value(
typeSystem.isSubtypeOf(type, other.type),
);
}
}
class _TypePhaseIntrospector implements macro.TypePhaseIntrospector {
final LinkedElementFactory elementFactory;
final DeclarationBuilder declarationBuilder;
_TypePhaseIntrospector(
this.elementFactory,
this.declarationBuilder,
);
@override
Future<macro.Identifier> resolveIdentifier(Uri library, String name) async {
final libraryElement = elementFactory.libraryOfUri2(library);
final element = libraryElement.scope.lookup(name).getter!;
return declarationBuilder.fromElement.identifier(element);
}
}
extension on macro.MacroExecutionResult {
bool get isNotEmpty =>
enumValueAugmentations.isNotEmpty ||
interfaceAugmentations.isNotEmpty ||
libraryAugmentations.isNotEmpty ||
mixinAugmentations.isNotEmpty ||
typeAugmentations.isNotEmpty;
}