// 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/element2.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 = void Function(T type); /// The type of a function which given an [S], builds an assertion over [T]s. typedef AsserterBuilder = Asserter Function(S arg); /// The type of a function which given an [S0] and an S1, builds an assertion /// over [T]s. typedef AsserterBuilder2 = Asserter 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 = AsserterBuilder 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 unitMembers = unit.declarations; for (CompilationUnitMember unitMember in unitMembers) { if (unitMember is ClassDeclaration && unitMember.name.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); NodeList classMembers = unitMember.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); 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.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); NodeList classMembers = unitMember.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 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 unitMembers = unit.declarations; for (CompilationUnitMember 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 get isDynamic => isType(_typeProvider.dynamicType); /// Primitive assertion for the int type. Asserter get isInt => isType(_typeProvider.intType); /// Primitive assertion for the dynamic type. Asserter get isInvalidType => isType(InvalidTypeImpl.instance); /// Primitive assertion for the list type. Asserter get isList => hasElement(_typeProvider.listElement2); /// Primitive assertion for the map type. Asserter get isMap => hasElement(_typeProvider.mapElement2); /// Primitive assertion for the Never type. Asserter get isNever => isType(_typeProvider.neverType); /// 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(Element2 expected) => (DartType type) => expect(expected, (type as InterfaceType).element3); /// 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); }; }