blob: 7f4576d9252639a15a2f5da7710b222de5b6a41c [file] [log] [blame]
// Copyright (c) 2016, 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.
/// Helpers for Analyzer's Element model and corelib model.
import 'package:analyzer/dart/ast/ast.dart'
show
ConstructorDeclaration,
Expression,
FunctionBody,
FunctionExpression,
MethodDeclaration,
MethodInvocation,
SimpleIdentifier;
import 'package:analyzer/dart/element/element.dart'
show Element, ExecutableElement, FunctionElement;
import 'package:analyzer/dart/element/type.dart'
show DartType, InterfaceType, ParameterizedType;
import 'package:analyzer/src/dart/element/type.dart' show DynamicTypeImpl;
import 'package:analyzer/src/generated/constant.dart'
show DartObject, DartObjectImpl;
class Tuple2<T0, T1> {
final T0 e0;
final T1 e1;
Tuple2(this.e0, this.e1);
}
/*=T*/ fillDynamicTypeArgs/*<T extends DartType>*/(/*=T*/ t) {
if (t is ParameterizedType) {
var dyn = new List<DartType>.filled(
t.typeArguments.length, DynamicTypeImpl.instance);
return t.substitute2(dyn, t.typeArguments) as dynamic/*=T*/;
}
return t;
}
/// Given an annotated [node] and a [test] function, returns the first matching
/// constant valued annotation.
///
/// For example if we had the ClassDeclaration node for `FontElement`:
///
/// @js.JS('HTMLFontElement')
/// @deprecated
/// class FontElement { ... }
///
/// We could match `@deprecated` with a test function like:
///
/// (v) => v.type.name == 'Deprecated' && v.type.element.library.isDartCore
///
DartObject findAnnotation(Element element, bool test(DartObjectImpl value)) {
for (var metadata in element.metadata) {
var value = metadata.computeConstantValue();
if (value != null && test(value)) return value;
}
return null;
}
/// Searches all supertype, in order of most derived members, to see if any
/// [match] a condition. If so, returns the first match, otherwise returns null.
InterfaceType findSupertype(InterfaceType type, bool match(InterfaceType t)) {
for (var m in type.mixins.reversed) {
if (match(m)) return m;
}
var s = type.superclass;
if (s == null) return null;
if (match(s)) return type;
return findSupertype(s, match);
}
//TODO(leafp): Is this really necessary? In theory I think
// the static type should always be filled in for resolved
// ASTs. This may be a vestigial workaround.
DartType getStaticType(Expression e) =>
e.staticType ?? DynamicTypeImpl.instance;
// TODO(leafp) Factor this out or use an existing library
/// Similar to [SimpleIdentifier] inGetterContext, inSetterContext, and
/// inDeclarationContext, this method returns true if [node] is used in an
/// invocation context such as a MethodInvocation.
bool inInvocationContext(SimpleIdentifier node) {
var parent = node.parent;
return parent is MethodInvocation && parent.methodName == node;
}
bool isInlineJS(Element e) =>
e is FunctionElement &&
e.name == 'JS' &&
e.library.isInSdk &&
e.library.source.uri.toString() == 'dart:_foreign_helper';
ExecutableElement getFunctionBodyElement(FunctionBody body) {
var f = body.parent;
if (f is FunctionExpression) {
return f.element;
} else if (f is MethodDeclaration) {
return f.element;
} else {
return (f as ConstructorDeclaration).element;
}
}
/// Returns the value of the `name` field from the [match]ing annotation on
/// [element], or `null` if we didn't find a valid match or it was not a string.
///
/// For example, consider this code:
///
/// class MyAnnotation {
/// final String name;
/// // ...
/// const MyAnnotation(this.name/*, ... other params ... */);
/// }
///
/// @MyAnnotation('FooBar')
/// main() { ... }
///
/// If we match the annotation for the `@MyAnnotation('FooBar')` this will
/// return the string 'FooBar'.
String getAnnotationName(Element element, bool match(DartObjectImpl value)) =>
findAnnotation(element, match)?.getField('name')?.toStringValue();