blob: 764d8abceb0f0a8a56aa605fabdfc8e754840b1a [file] [log] [blame]
// Copyright (c) 2020, 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:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/generic_inferrer.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
extension ClassElementExtensions on ClassElement {
/// Return `true` if this element represents the class `Iterable` from
/// `dart:core`.
bool get isDartCoreIterable => name == 'Iterable' && library.isDartCore;
/// Return `true` if this element represents the class `List` from
/// `dart:core`.
bool get isDartCoreList => name == 'List' && library.isDartCore;
/// Return `true` if this element represents the class `Map` from
/// `dart:core`.
bool get isDartCoreMap => name == 'Map' && library.isDartCore;
/// Return `true` if this element represents the class `Set` from
/// `dart:core`.
bool get isDartCoreSet => name == 'Set' && library.isDartCore;
}
extension ElementExtension on Element {
/// Return `true` if this element, the enclosing class (if there is one), or
/// the enclosing library, has been annotated with the `@deprecated`
/// annotation.
bool get hasOrInheritsDeprecated {
if (hasDeprecated) {
return true;
}
var ancestor = enclosingElement;
if (ancestor is ClassElement) {
if (ancestor.hasDeprecated) {
return true;
}
ancestor = ancestor.enclosingElement;
}
return ancestor is CompilationUnitElement &&
ancestor.enclosingElement.hasDeprecated;
}
/// Return this element and all its enclosing elements.
Iterable<Element> get withAncestors sync* {
var current = this;
while (true) {
yield current;
var enclosing = current.enclosingElement;
if (enclosing == null) {
break;
}
current = enclosing;
}
}
}
extension ExtensionElementExtensions on ExtensionElement {
/// Use the [type] of the object being extended in the [library] to compute
/// the actual type extended by this [extension]. Return the computed type,
/// or `null` if the type can't be computed.
DartType? resolvedExtendedType(LibraryElement library, DartType type) {
final typeParameters = this.typeParameters;
var inferrer =
GenericInferrer(library.typeSystem as TypeSystemImpl, typeParameters);
inferrer.constrainArgument(
type,
extendedType,
'extendedType',
);
var typeArguments = inferrer.infer(typeParameters,
failAtError: true, genericMetadataIsEnabled: true);
if (typeArguments == null) {
return null;
}
var substitution = Substitution.fromPairs(
typeParameters,
typeArguments,
);
return substitution.substituteType(
extendedType,
);
}
}
extension LibraryElementExtensions on LibraryElement {
/// Return the extensions in this library that can be applied, within the
/// [containingLibrary], to the [targetType] and that define a member with the
/// given [memberName].
Iterable<ExtensionElement> matchingExtensionsWithMember(
LibraryElement containingLibrary,
DartType targetType,
String memberName) sync* {
for (var unit in units) {
for (var extension in unit.extensions) {
var extensionName = extension.name;
if (extensionName != null && !Identifier.isPrivateName(extensionName)) {
var extendedType =
extension.resolvedExtendedType(containingLibrary, targetType);
if (extendedType != null &&
typeSystem.isSubtypeOf(targetType, extendedType)) {
yield extension;
}
}
}
}
}
}
extension MethodElementExtensions on MethodElement {
/// Return `true` if this element represents the method `cast` from either
/// `Iterable`, `List`, `Map`, or `Set`.
bool get isCastMethod {
if (name != 'cast') {
return false;
}
var definingClass = enclosingElement;
if (definingClass is! ClassElement) {
return false;
}
return definingClass.isDartCoreIterable ||
definingClass.isDartCoreList ||
definingClass.isDartCoreMap ||
definingClass.isDartCoreSet;
}
/// Return `true` if this element represents the method `toList` from either
/// `Iterable` or `List`.
bool get isToListMethod {
if (name != 'toList') {
return false;
}
var definingClass = enclosingElement;
if (definingClass is! ClassElement) {
return false;
}
return definingClass.isDartCoreIterable || definingClass.isDartCoreList;
}
}