| Index: pkg/analyzer/lib/src/task/strong/checker.dart
|
| diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
|
| index 491b50a6703913089c0f861a7cca12038e01a1b4..695e6ddfd8607896520a19dc8bfc75e388dc1597 100644
|
| --- a/pkg/analyzer/lib/src/task/strong/checker.dart
|
| +++ b/pkg/analyzer/lib/src/task/strong/checker.dart
|
| @@ -14,11 +14,13 @@ import 'package:analyzer/dart/ast/token.dart';
|
| import 'package:analyzer/dart/ast/visitor.dart';
|
| import 'package:analyzer/dart/element/element.dart';
|
| import 'package:analyzer/dart/element/type.dart';
|
| +import 'package:analyzer/src/dart/element/element.dart';
|
| import 'package:analyzer/src/dart/element/type.dart';
|
| import 'package:analyzer/src/error/codes.dart' show StrongModeCode;
|
| import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
|
| import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
|
| import 'package:analyzer/src/generated/type_system.dart';
|
| +import 'package:analyzer/src/summary/idl.dart';
|
|
|
| import 'ast_properties.dart';
|
|
|
| @@ -43,6 +45,11 @@ DartType getDefiniteType(
|
| return type;
|
| }
|
|
|
| +bool hasStrictArrow(Expression expression) {
|
| + var element = _getKnownElement(expression);
|
| + return element is FunctionElement || element is MethodElement;
|
| +}
|
| +
|
| DartType _elementType(Element e) {
|
| if (e == null) {
|
| // Malformed code - just return dynamic.
|
| @@ -98,11 +105,6 @@ FieldElement _getMemberField(
|
| FunctionType _getMemberType(InterfaceType type, ExecutableElement member) =>
|
| _memberTypeGetter(member)(type);
|
|
|
| -bool hasStrictArrow(Expression expression) {
|
| - var element = _getKnownElement(expression);
|
| - return element is FunctionElement || element is MethodElement;
|
| -}
|
| -
|
| _MemberTypeGetter _memberTypeGetter(ExecutableElement member) {
|
| String memberName = member.name;
|
| final isGetter = member is PropertyAccessorElement && member.isGetter;
|
| @@ -428,12 +430,6 @@ class CodeChecker extends RecursiveAstVisitor {
|
| }
|
|
|
| @override
|
| - void visitFunctionExpression(FunctionExpression node) {
|
| - _checkForUnsafeBlockClosureInference(node);
|
| - super.visitFunctionExpression(node);
|
| - }
|
| -
|
| - @override
|
| void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
|
| checkFunctionApplication(node);
|
| node.visitChildren(this);
|
| @@ -645,6 +641,16 @@ class CodeChecker extends RecursiveAstVisitor {
|
| StaticTypeWarningCode.NON_NULLABLE_FIELD_NOT_INITIALIZED,
|
| [node.name, variableElement?.type]);
|
| }
|
| + AstNode parent = node.parent;
|
| + if (variableElement != null &&
|
| + parent is VariableDeclarationList &&
|
| + parent.type == null &&
|
| + node.initializer != null) {
|
| + if (variableElement.kind == ElementKind.TOP_LEVEL_VARIABLE ||
|
| + variableElement.kind == ElementKind.FIELD) {
|
| + _validateTopLevelInitializer(variableElement.name, node.initializer);
|
| + }
|
| + }
|
| return super.visitVariableDeclaration(node);
|
| }
|
|
|
| @@ -724,108 +730,6 @@ class CodeChecker extends RecursiveAstVisitor {
|
| node.visitChildren(this);
|
| }
|
|
|
| - /**
|
| - * Check if the closure [node] is unsafe due to dartbug.com/26947. If so,
|
| - * issue a warning.
|
| - *
|
| - * TODO(paulberry): eliminate this once dartbug.com/26947 is fixed.
|
| - */
|
| - void _checkForUnsafeBlockClosureInference(FunctionExpression node) {
|
| - if (node.body is! BlockFunctionBody) {
|
| - return;
|
| - }
|
| - if (resolutionMap
|
| - .elementDeclaredByFunctionExpression(node)
|
| - .returnType
|
| - .isDynamic) {
|
| - return;
|
| - }
|
| - // Find the enclosing variable declaration whose inferred type might depend
|
| - // on the inferred return type of the block closure (if any).
|
| - AstNode prevAncestor = node;
|
| - AstNode ancestor = node.parent;
|
| - while (ancestor != null && ancestor is! VariableDeclaration) {
|
| - if (ancestor is BlockFunctionBody) {
|
| - // node is inside another block function body; if that block
|
| - // function body is unsafe, we've already warned about it.
|
| - return;
|
| - }
|
| - if (ancestor is InstanceCreationExpression) {
|
| - // node appears inside an instance creation expression; we may be safe
|
| - // if the type of the instance creation expression requires no
|
| - // inference.
|
| - TypeName typeName = ancestor.constructorName.type;
|
| - if (typeName.typeArguments != null) {
|
| - // Type arguments were explicitly specified. We are safe.
|
| - return;
|
| - }
|
| - DartType type = typeName.type;
|
| - if (!(type is ParameterizedType && type.typeParameters.isNotEmpty)) {
|
| - // Type is not generic. We are safe.
|
| - return;
|
| - }
|
| - }
|
| - if (ancestor is MethodInvocation) {
|
| - // node appears inside a method or function invocation; we may be safe
|
| - // if the type of the method or function requires no inference.
|
| - if (ancestor.typeArguments != null) {
|
| - // Type arguments were explicitly specified. We are safe.
|
| - return;
|
| - }
|
| - Element methodElement = ancestor.methodName.staticElement;
|
| - if (!(methodElement is ExecutableElement &&
|
| - methodElement.typeParameters.isNotEmpty)) {
|
| - // Method is not generic. We are safe.
|
| - return;
|
| - }
|
| - }
|
| - if (ancestor is FunctionExpressionInvocation &&
|
| - !identical(prevAncestor, ancestor.function)) {
|
| - // node appears inside an argument to a function expression invocation;
|
| - // we may be safe if the type of the function expression requires no
|
| - // inference.
|
| - if (ancestor.typeArguments != null) {
|
| - // Type arguments were explicitly specified. We are safe.
|
| - return;
|
| - }
|
| - DartType type = ancestor.function.staticType;
|
| - if (!(type is FunctionTypeImpl && type.typeFormals.isNotEmpty)) {
|
| - // Type is not generic or has had its type parameters instantiated.
|
| - // We are safe.
|
| - return;
|
| - }
|
| - }
|
| - if ((ancestor is ListLiteral && ancestor.typeArguments != null) ||
|
| - (ancestor is MapLiteral && ancestor.typeArguments != null)) {
|
| - // node appears inside a list or map literal with an explicit type. We
|
| - // are safe because no type inference is required.
|
| - return;
|
| - }
|
| - prevAncestor = ancestor;
|
| - ancestor = ancestor.parent;
|
| - }
|
| - if (ancestor == null) {
|
| - // node is not inside a variable declaration, so it is safe.
|
| - return;
|
| - }
|
| - VariableDeclaration decl = ancestor;
|
| - VariableElement declElement = decl.element;
|
| - if (!declElement.hasImplicitType) {
|
| - // Variable declaration has an explicit type, so it's safe.
|
| - return;
|
| - }
|
| - if (declElement.type.isDynamic) {
|
| - // No type was successfully inferred for this variable, so it's safe.
|
| - return;
|
| - }
|
| - if (declElement.enclosingElement is ExecutableElement) {
|
| - // Variable declaration is inside a function or method, so it's safe.
|
| - return;
|
| - }
|
| - _recordMessage(node, StrongModeCode.UNSAFE_BLOCK_CLOSURE_INFERENCE,
|
| - [declElement.name]);
|
| - }
|
| -
|
| /// Checks if an implicit cast of [expr] from [from] type to [to] type is
|
| /// needed, and if so records it.
|
| ///
|
| @@ -1149,6 +1053,158 @@ class CodeChecker extends RecursiveAstVisitor {
|
| reporter.onError(error);
|
| }
|
| }
|
| +
|
| + void _validateTopLevelInitializer(String name, Expression n) {
|
| + void validateHasType(PropertyAccessorElement e) {
|
| + if (e.hasImplicitReturnType) {
|
| + var variable = e.variable as VariableElementImpl;
|
| + TopLevelInferenceError error = variable.typeInferenceError;
|
| + if (error != null) {
|
| + if (error.kind == TopLevelInferenceErrorKind.dependencyCycle) {
|
| + _recordMessage(
|
| + n, StrongModeCode.TOP_LEVEL_CYCLE, [name, error.arguments]);
|
| + } else {
|
| + _recordMessage(
|
| + n, StrongModeCode.TOP_LEVEL_IDENTIFIER_NO_TYPE, [name, e.name]);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + void validateIdentifierElement(AstNode n, Element e) {
|
| + if (e == null) {
|
| + return;
|
| + }
|
| +
|
| + Element enclosing = e.enclosingElement;
|
| + if (enclosing is CompilationUnitElement) {
|
| + if (e is PropertyAccessorElement) {
|
| + validateHasType(e);
|
| + }
|
| + } else if (enclosing is ClassElement) {
|
| + if (e is PropertyAccessorElement) {
|
| + if (e.isStatic) {
|
| + validateHasType(e);
|
| + } else {
|
| + _recordMessage(
|
| + n, StrongModeCode.TOP_LEVEL_INSTANCE_GETTER, [name, e.name]);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (n == null ||
|
| + n is NullLiteral ||
|
| + n is BooleanLiteral ||
|
| + n is DoubleLiteral ||
|
| + n is IntegerLiteral ||
|
| + n is StringLiteral ||
|
| + n is SymbolLiteral) {
|
| + // Nothing to validate.
|
| + } else if (n is AwaitExpression) {
|
| + _validateTopLevelInitializer(name, n.expression);
|
| + } else if (n is ThrowExpression) {
|
| + // Nothing to validate.
|
| + } else if (n is ParenthesizedExpression) {
|
| + _validateTopLevelInitializer(name, n.expression);
|
| + } else if (n is ConditionalExpression) {
|
| + _validateTopLevelInitializer(name, n.thenExpression);
|
| + _validateTopLevelInitializer(name, n.elseExpression);
|
| + } else if (n is BinaryExpression) {
|
| + TokenType operator = n.operator.type;
|
| + if (operator == TokenType.AMPERSAND_AMPERSAND ||
|
| + operator == TokenType.BAR_BAR ||
|
| + operator == TokenType.EQ_EQ ||
|
| + operator == TokenType.BANG_EQ) {
|
| + // These operators give 'bool', no need to validate operands.
|
| + } else if (operator == TokenType.QUESTION_QUESTION) {
|
| + _recordMessage(n, StrongModeCode.TOP_LEVEL_UNSUPPORTED,
|
| + [name, n.runtimeType.toString()]);
|
| + } else {
|
| + _validateTopLevelInitializer(name, n.leftOperand);
|
| + }
|
| + } else if (n is PrefixExpression) {
|
| + TokenType operator = n.operator.type;
|
| + if (operator == TokenType.BANG) {
|
| + // This operator gives 'bool', no need to validate operands.
|
| + } else {
|
| + _validateTopLevelInitializer(name, n.operand);
|
| + }
|
| + } else if (n is PostfixExpression) {
|
| + _validateTopLevelInitializer(name, n.operand);
|
| + } else if (n is ListLiteral) {
|
| + if (n.typeArguments == null) {
|
| + for (Expression element in n.elements) {
|
| + _validateTopLevelInitializer(name, element);
|
| + }
|
| + }
|
| + } else if (n is MapLiteral) {
|
| + if (n.typeArguments == null) {
|
| + for (MapLiteralEntry entry in n.entries) {
|
| + _validateTopLevelInitializer(name, entry.key);
|
| + _validateTopLevelInitializer(name, entry.value);
|
| + }
|
| + }
|
| + } else if (n is FunctionExpression) {
|
| + for (FormalParameter p in n.parameters.parameters) {
|
| + if (p is DefaultFormalParameter) {
|
| + p = (p as DefaultFormalParameter).parameter;
|
| + }
|
| + if (p is SimpleFormalParameter) {
|
| + if (p.type == null) {
|
| + _recordMessage(
|
| + p,
|
| + StrongModeCode.TOP_LEVEL_FUNCTION_LITERAL_PARAMETER,
|
| + [name, p.element?.name]);
|
| + }
|
| + }
|
| + }
|
| +
|
| + FunctionBody body = n.body;
|
| + if (body is ExpressionFunctionBody) {
|
| + _validateTopLevelInitializer(name, body.expression);
|
| + } else {
|
| + _recordMessage(n, StrongModeCode.TOP_LEVEL_FUNCTION_LITERAL_BLOCK, []);
|
| + }
|
| + } else if (n is InstanceCreationExpression) {
|
| + ConstructorElement constructor = n.staticElement;
|
| + ClassElement clazz = constructor?.enclosingElement;
|
| + if (clazz != null && clazz.typeParameters.isNotEmpty) {
|
| + TypeName type = n.constructorName.type;
|
| + if (type.typeArguments == null) {
|
| + _recordMessage(type, StrongModeCode.TOP_LEVEL_TYPE_ARGUMENTS,
|
| + [name, clazz.name]);
|
| + }
|
| + }
|
| + } else if (n is AsExpression) {
|
| + // Nothing to validate.
|
| + } else if (n is IsExpression) {
|
| + // Nothing to validate.
|
| + } else if (n is Identifier) {
|
| + validateIdentifierElement(n, n.staticElement);
|
| + } else if (n is PropertyAccess) {
|
| + Element element = n.propertyName.staticElement;
|
| + validateIdentifierElement(n.propertyName, element);
|
| + } else if (n is FunctionExpressionInvocation) {
|
| + _validateTopLevelInitializer(name, n.function);
|
| + // TODO(scheglov) type arguments
|
| + } else if (n is MethodInvocation) {
|
| + _validateTopLevelInitializer(name, n.target);
|
| + SimpleIdentifier methodName = n.methodName;
|
| + Element element = methodName.staticElement;
|
| + if (element is ExecutableElement && element.typeParameters.isNotEmpty) {
|
| + if (n.typeArguments == null) {
|
| + _recordMessage(methodName, StrongModeCode.TOP_LEVEL_TYPE_ARGUMENTS,
|
| + [name, methodName.name]);
|
| + }
|
| + }
|
| + } else if (n is CascadeExpression) {
|
| + _validateTopLevelInitializer(name, n.target);
|
| + } else {
|
| + _recordMessage(n, StrongModeCode.TOP_LEVEL_UNSUPPORTED,
|
| + [name, n.runtimeType.toString()]);
|
| + }
|
| + }
|
| }
|
|
|
| /// Checks for overriding declarations of fields and methods. This is used to
|
|
|