// 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. library analyzer.test.utils; 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:front_end/src/base/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 elements = findElementsByName(unit, name); List 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 elements = findElementsByName(unit, name); List 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 type); /** * The type of a function which given an [S], builds an assertion over [T]s. */ typedef Asserter AsserterBuilder(S arg); /** * The type of a function which given an [S0] and an S1, builds an assertion * over [T]s. */ typedef Asserter AsserterBuilder2(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 AsserterBuilderBuilder(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 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 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 classMembers = unitMember.members; for (ClassMember classMember in classMembers) { if (classMember is FieldDeclaration) { NodeList 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 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 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 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 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 unitMembers = unit.declarations; for (CompilationUnitMember unitMember in unitMembers) { if (unitMember is TopLevelVariableDeclaration) { NodeList 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 get isDynamic => isType(_typeProvider.dynamicType); /** * Primitive assertion for the int type */ Asserter get isInt => isType(_typeProvider.intType); /** * Primitive assertion for the list type */ Asserter get isList => hasElementOf(_typeProvider.listType); /** * Primitive assertion for the map type */ Asserter get isMap => hasElementOf(_typeProvider.mapType); /** * Primitive assertion for the Null type */ Asserter get isNull => isType(_typeProvider.nullType); /** * Primitive assertion for the num type */ Asserter get isNum => isType(_typeProvider.numType); /** * Primitive assertion for the Object type */ Asserter get isObject => isType(_typeProvider.objectType); /** * Primitive assertion for the string type */ Asserter get isString => isType(_typeProvider.stringType); /** * Assert that a type has the element that is equal to the [expected]. */ Asserter 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 hasElementOf(DartType type) => hasElement(type.element); /** * Given assertions for the argument and return types, produce an * assertion over unary function types. */ Asserter isFunction2Of( Asserter argType, Asserter 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>, DartType> isInstantiationOf( Asserter baseAssert) => (List> argAsserts) => (DartType type) { InterfaceType t = (type as InterfaceType); baseAssert(t); List 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 isListOf(Asserter argAssert) => isInstantiationOf(isList)([argAssert]); /** * Assert that a type is the Map type, and that the given assertions hold * over the type parameters. */ Asserter isMapOf( Asserter argAssert0, Asserter argAssert1) => isInstantiationOf(isMap)([argAssert0, argAssert1]); /** * Assert that a type is equal to the [expected]. */ Asserter isType(DartType expected) => (DartType t) { expect(t, expected); }; }