Files
sdk/pkg/analyzer/test/utils.dart
T
Leaf Petersen a3b4366f11 Infer Null for return type of functions with empty returns.
This allows inference to infer Null as the return type of functions
which have no return statements, or which return with no value.

Also allows inference to propagate Null.

Fixes https://github.com/dart-lang/sdk/issues/28602
Fixes https://github.com/dart-lang/sdk/issues/28630

BUG=
R=brianwilkerson@google.com, jmesserly@google.com

Review-Url: https://codereview.chromium.org/2667343005 .
2017-02-06 12:24:39 -08:00

313 lines
10 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.
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:front_end/src/base/source.dart';
import 'package:test/test.dart';
/**
* 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 null;
}
/**
* 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 null;
}
/**
* 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 null;
}
/**
* 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 null;
}
/**
* 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 null;
}
/**
* 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 null;
}
/**
* 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);
};
}