| // 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/element/element.dart'; | |
| import 'package:analyzer/dart/element/type.dart'; | |
| import 'package:analyzer/dart/element/type_provider.dart'; | |
| import 'package:analyzer/source/source.dart'; | |
| import 'package:analyzer/src/dart/element/type.dart'; | |
| import 'package:test/test.dart'; | |
| /// The type of an assertion which asserts properties of [T]s. | |
| typedef Asserter<T> = void Function(T type); | |
| /// The type of a function which given an [S], builds an assertion over [T]s. | |
| typedef AsserterBuilder<S, T> = Asserter<T> Function(S arg); | |
| /// The type of a function which given an [S0] and an S1, builds an assertion | |
| /// over [T]s. | |
| typedef AsserterBuilder2<S0, S1, T> = Asserter<T> Function(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 AsserterBuilderBuilder<R, S, T> = AsserterBuilder<S, T> Function(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<AnnotatedNode> unitMembers = unit.declarations2; | |
| for (AnnotatedNode unitMember in unitMembers) { | |
| if (unitMember is ClassDeclaration && | |
| unitMember.namePart.typeName.lexeme == className) { | |
| return unitMember; | |
| } | |
| } | |
| Source source = unit.declaredFragment!.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); | |
| var classBody = unitMember.body as BlockClassBody; | |
| NodeList<ClassMember> classMembers = classBody.members; | |
| for (ClassMember classMember in classMembers) { | |
| if (classMember is ConstructorDeclaration) { | |
| if (classMember.name?.lexeme == 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); | |
| var classBody = unitMember.body as BlockClassBody; | |
| NodeList<ClassMember> classMembers = classBody.members; | |
| for (ClassMember classMember in classMembers) { | |
| if (classMember is FieldDeclaration) { | |
| NodeList<VariableDeclaration> fields = classMember.fields.variables; | |
| for (VariableDeclaration field in fields) { | |
| if (field.name.lexeme == fieldName) { | |
| return field; | |
| } | |
| } | |
| } | |
| } | |
| fail('No field named $fieldName in $className'); | |
| } | |
| /// 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); | |
| var classBody = unitMember.body as BlockClassBody; | |
| NodeList<ClassMember> classMembers = classBody.members; | |
| for (ClassMember classMember in classMembers) { | |
| if (classMember is MethodDeclaration) { | |
| if (classMember.name.lexeme == methodName) { | |
| return classMember; | |
| } | |
| } | |
| } | |
| fail('No method named $methodName in $className'); | |
| } | |
| /// 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); | |
| var body = function.functionExpression.body as BlockFunctionBody; | |
| 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<AnnotatedNode> unitMembers = unit.declarations2; | |
| for (AnnotatedNode unitMember in unitMembers) { | |
| if (unitMember is FunctionDeclaration) { | |
| if (unitMember.name.lexeme == functionName) { | |
| return unitMember; | |
| } | |
| } | |
| } | |
| fail('No toplevel function named $functionName found'); | |
| } | |
| } | |
| /// Class for compositionally building up assertions on types | |
| class TypeAssertions { | |
| // TODO(leafp): Make these matchers. | |
| // https://pub.dev/documentation/matcher/latest/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 dynamic type. | |
| Asserter<DartType> get isInvalidType => isType(InvalidTypeImpl.instance); | |
| /// Primitive assertion for the list type. | |
| Asserter<DartType> get isList => hasElement(_typeProvider.listElement); | |
| /// Primitive assertion for the map type. | |
| Asserter<DartType> get isMap => hasElement(_typeProvider.mapElement); | |
| /// Primitive assertion for the Never type. | |
| Asserter<DartType> get isNever => isType(_typeProvider.neverType); | |
| /// 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 as InterfaceType).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); | |
| }; | |
| } |