Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(532)

Unified Diff: pkg/analyzer/lib/src/task/strong/checker.dart

Issue 2782533002: Report errors for the new top-level inference rules. (Closed)
Patch Set: Update language_strong status for analyzer. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/lib/src/task/dart.dart ('k') | pkg/analyzer/lib/src/task/strong_mode.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « pkg/analyzer/lib/src/task/dart.dart ('k') | pkg/analyzer/lib/src/task/strong_mode.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698