blob: 07190a4135d264e4a57c510e5495499f1316d911 [file] [log] [blame]
// Copyright (c) 2013, 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/ast/standard_resolution_map.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/generated/testing/element_search.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:test/test.dart';
/**
* Search the [unit] for the [LocalVariableElement] with the given [name].
* Fail if there is not exactly one such variable.
*/
FunctionElement findLocalFunction(CompilationUnit unit, String name) {
List<Element> elements = findElementsByName(unit, name);
List<Element> functions =
elements.where((e) => e is FunctionElement).toList();
expect(functions, hasLength(1));
return functions[0];
}
/**
* Search the [unit] for the [LocalVariableElement] with the given [name].
* Fail if there is not exactly one such variable.
*/
LocalVariableElement findLocalVariable(CompilationUnit unit, String name) {
List<Element> elements = findElementsByName(unit, name);
List<Element> localVariables =
elements.where((e) => e is LocalVariableElement).toList();
expect(localVariables, hasLength(1));
return localVariables[0];
}
/**
* The type of an assertion which asserts properties of [T]s.
*/
typedef void Asserter<T>(T type);
/**
* The type of a function which given an [S], builds an assertion over [T]s.
*/
typedef Asserter<T> AsserterBuilder<S, T>(S arg);
/**
* The type of a function which given an [S0] and an S1, builds an assertion
* over [T]s.
*/
typedef Asserter<T> AsserterBuilder2<S0, S1, T>(S0 arg0, S1 arg1);
/**
* The type of a function which given an [R] returns an [AsserterBuilder] over
* [S]s and [T]s. That is, it returns a function which given an [S], returns
* a function over [T]s.
*/
typedef AsserterBuilder<S, T> AsserterBuilderBuilder<R, S, T>(R arg);
class AstFinder {
/**
* Return the declaration of the class with the given [className] in the given
* compilation [unit].
*/
static ClassDeclaration getClass(CompilationUnit unit, String className) {
NodeList<CompilationUnitMember> unitMembers = unit.declarations;
for (CompilationUnitMember unitMember in unitMembers) {
if (unitMember is ClassDeclaration && unitMember.name.name == className) {
return unitMember;
}
}
Source source = resolutionMap.elementDeclaredByCompilationUnit(unit).source;
fail('No class named $className in $source');
}
/**
* Return the declaration of the constructor with the given [constructorName] in
* the class with the given [className] in the given compilation [unit]. If
* constructorName is null, return the default constructor;
*/
static ConstructorDeclaration getConstructorInClass(
CompilationUnit unit, String className, String constructorName) {
ClassDeclaration unitMember = getClass(unit, className);
NodeList<ClassMember> classMembers = unitMember.members;
for (ClassMember classMember in classMembers) {
if (classMember is ConstructorDeclaration) {
if (classMember.name?.name == constructorName) {
return classMember;
}
}
}
fail('No constructor named $constructorName in $className');
}
/**
* Return the declaration of the field with the given [fieldName] in the class
* with the given [className] in the given compilation [unit].
*/
static VariableDeclaration getFieldInClass(
CompilationUnit unit, String className, String fieldName) {
ClassDeclaration unitMember = getClass(unit, className);
NodeList<ClassMember> classMembers = unitMember.members;
for (ClassMember classMember in classMembers) {
if (classMember is FieldDeclaration) {
NodeList<VariableDeclaration> fields = classMember.fields.variables;
for (VariableDeclaration field in fields) {
if (field.name.name == fieldName) {
return field;
}
}
}
}
fail('No field named $fieldName in $className');
}
/**
* Return the element of the field with the given [fieldName] in the class
* with the given [className] in the given compilation [unit].
*/
static FieldElement getFieldInClassElement(
CompilationUnit unit, String className, String fieldName) {
return getFieldInClass(unit, className, fieldName)?.name?.staticElement;
}
/**
* Return the declaration of the method with the given [methodName] in the
* class with the given [className] in the given compilation [unit].
*/
static MethodDeclaration getMethodInClass(
CompilationUnit unit, String className, String methodName) {
ClassDeclaration unitMember = getClass(unit, className);
NodeList<ClassMember> classMembers = unitMember.members;
for (ClassMember classMember in classMembers) {
if (classMember is MethodDeclaration) {
if (classMember.name.name == methodName) {
return classMember;
}
}
}
fail('No method named $methodName in $className');
}
/**
* Return the statements in the body of a the method with the given
* [methodName] in the class with the given [className] in the given
* compilation [unit].
*/
static List<Statement> getStatementsInMethod(
CompilationUnit unit, String className, String methodName) {
MethodDeclaration method = getMethodInClass(unit, className, methodName);
BlockFunctionBody body = method.body;
return body.block.statements;
}
/**
* Return the statements in the body of the top-level function with the given
* [functionName] in the given compilation [unit].
*/
static List<Statement> getStatementsInTopLevelFunction(
CompilationUnit unit, String functionName) {
FunctionDeclaration function = getTopLevelFunction(unit, functionName);
BlockFunctionBody body = function.functionExpression.body;
return body.block.statements;
}
/**
* Return the declaration of the top-level function with the given
* [functionName] in the given compilation [unit].
*/
static FunctionDeclaration getTopLevelFunction(
CompilationUnit unit, String functionName) {
NodeList<CompilationUnitMember> unitMembers = unit.declarations;
for (CompilationUnitMember unitMember in unitMembers) {
if (unitMember is FunctionDeclaration) {
if (unitMember.name.name == functionName) {
return unitMember;
}
}
}
fail('No toplevel function named $functionName found');
}
/**
* Return the declaration of the top-level variable with the given
* [variableName] in the given compilation [unit].
*/
static VariableDeclaration getTopLevelVariable(
CompilationUnit unit, String variableName) {
NodeList<CompilationUnitMember> unitMembers = unit.declarations;
for (CompilationUnitMember unitMember in unitMembers) {
if (unitMember is TopLevelVariableDeclaration) {
NodeList<VariableDeclaration> variables =
unitMember.variables.variables;
for (VariableDeclaration variable in variables) {
if (variable.name.name == variableName) {
return variable;
}
}
}
}
fail('No toplevel variable named $variableName found');
}
/**
* Return the top-level variable element with the given [name].
*/
static TopLevelVariableElement getTopLevelVariableElement(
CompilationUnit unit, String name) {
return getTopLevelVariable(unit, name)?.name?.staticElement;
}
}
/**
* Class for compositionally building up assertions on types
*/
class TypeAssertions {
// TODO(leafp): Make these matchers.
// https://www.dartdocs.org/documentation/matcher/0.12.0%2B1/matcher/Matcher-class.html
/**
* Provides primitive types for basic type assertions.
*/
final TypeProvider _typeProvider;
TypeAssertions(this._typeProvider);
/**
* Primitive assertion for the dynamic type
*/
Asserter<DartType> get isDynamic => isType(_typeProvider.dynamicType);
/**
* Primitive assertion for the int type
*/
Asserter<DartType> get isInt => isType(_typeProvider.intType);
/**
* Primitive assertion for the list type
*/
Asserter<DartType> get isList => hasElementOf(_typeProvider.listType);
/**
* Primitive assertion for the map type
*/
Asserter<DartType> get isMap => hasElementOf(_typeProvider.mapType);
/**
* Primitive assertion for the Null type
*/
Asserter<DartType> get isNull => isType(_typeProvider.nullType);
/**
* Primitive assertion for the num type
*/
Asserter<DartType> get isNum => isType(_typeProvider.numType);
/**
* Primitive assertion for the Object type
*/
Asserter<DartType> get isObject => isType(_typeProvider.objectType);
/**
* Primitive assertion for the string type
*/
Asserter<DartType> get isString => isType(_typeProvider.stringType);
/**
* Assert that a type has the element that is equal to the [expected].
*/
Asserter<DartType> hasElement(Element expected) =>
(DartType type) => expect(expected, type.element);
/**
* Assert that a type has the element that is equal to the element of the
* given [type].
*/
Asserter<DartType> hasElementOf(DartType type) => hasElement(type.element);
/**
* Given assertions for the argument and return types, produce an
* assertion over unary function types.
*/
Asserter<DartType> isFunction2Of(
Asserter<DartType> argType, Asserter<DartType> returnType) =>
(DartType type) {
FunctionType fType = (type as FunctionType);
argType(fType.normalParameterTypes[0]);
returnType(fType.returnType);
};
/**
* Given an assertion for the base type and assertions over the type
* parameters, produce an assertion over instantiations.
*/
AsserterBuilder<List<Asserter<DartType>>, DartType> isInstantiationOf(
Asserter<DartType> baseAssert) =>
(List<Asserter<DartType>> argAsserts) => (DartType type) {
InterfaceType t = (type as InterfaceType);
baseAssert(t);
List<DartType> typeArguments = t.typeArguments;
expect(typeArguments, hasLength(argAsserts.length));
for (int i = 0; i < typeArguments.length; i++) {
argAsserts[i](typeArguments[i]);
}
};
/**
* Assert that a type is the List type, and that the given assertion holds
* over the type parameter.
*/
Asserter<InterfaceType> isListOf(Asserter<DartType> argAssert) =>
isInstantiationOf(isList)([argAssert]);
/**
* Assert that a type is the Map type, and that the given assertions hold
* over the type parameters.
*/
Asserter<InterfaceType> isMapOf(
Asserter<DartType> argAssert0, Asserter<DartType> argAssert1) =>
isInstantiationOf(isMap)([argAssert0, argAssert1]);
/**
* Assert that a type is equal to the [expected].
*/
Asserter<DartType> isType(DartType expected) => (DartType t) {
expect(t, expected);
};
}