0349520b98
No more flag, always parse into the new AST, always visit new AST nodes,
always return them as child entities, parent-child structure reflects
the new AST.
So, use `namePart` and `body` where possible. Deprecate previous
properties.
This is still de jure a breaking change, because `parent` of deprecated
properties changes. De facto this required very few changes in google3.
Once this CL lands, I will publish `analyzer 10.0.0`, migrate everything
to new properties, delete deprecated properties, and publish `analyzer
11.0.0`.
Maybe deprecate `NamedCompilationUnitMember.name` and migrate to
subclass specific `name` or `namePart` properties before publishing
`analyzer 10.0.0`. This part is not breaking per se.
* Deprecations in `ClassDeclaration`:
* Properties `leftBracket`, `members`, `rightBracket` are deprecated, use `body` instead.
* Properties `name`, `typeParameters` are deprecated, use `namePart` instead.
* Deprecations in `EnumDeclaration`:
* Properties `leftBracket`, `constants`, `members`, `rightBracket` are deprecated, use `body` instead.
* Properties `name`, `typeParameters` are deprecated, use `namePart` instead.
* Deprecations in `ExtensionDeclaration`:
* Properties `leftBracket`, `members`, `rightBracket` are deprecated, use `body` instead.
* Deprecations in `ExtensionTypeDeclaration`:
* Properties `leftBracket`, `constants`, `members`, `rightBracket` are deprecated, use `body` instead.
* Properties `constKeyword`, `name`, `representation`, `typeParameters` are deprecated,
use `primaryConstructor` instead.
* **Breaking Change:** While the deprecated members mentioned above still exist in the AST,
their parent nodes have changed. This means that code relying on specific parent-child
relationships for these nodes might break.
Bug: https://github.com/dart-lang/sdk/issues/61701
Change-Id: Ic48104da8b029c9b454bbd2336574b7823025565
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/461841
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
223 lines
8.4 KiB
Dart
223 lines
8.4 KiB
Dart
// 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<CompilationUnitMember> unitMembers = unit.declarations;
|
|
for (CompilationUnitMember 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<CompilationUnitMember> 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<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);
|
|
};
|
|
}
|