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

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

Issue 2062793003: Revert "Revert "Refactor strong mode to use standard Analyzer errors"" (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 6 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/strong/ast_properties.dart ('k') | pkg/analyzer/lib/src/task/strong/info.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 01bea7f766a02154655cb08377b114bbc7a91753..783cdf0b0e8711178ac09413926a314e8ba54c80 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -14,10 +14,11 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
+import 'package:analyzer/src/generated/error.dart' show StrongModeCode;
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/generated/type_system.dart';
-import 'info.dart';
+import 'ast_properties.dart';
DartType _elementType(Element e) {
if (e == null) {
@@ -101,19 +102,21 @@ class CodeChecker extends RecursiveAstVisitor {
final StrongTypeSystemImpl rules;
final TypeProvider typeProvider;
final AnalysisErrorListener reporter;
- final _OverrideChecker _overrideChecker;
final AnalysisOptionsImpl _options;
+ _OverrideChecker _overrideChecker;
bool _failure = false;
+ bool _hasImplicitCasts;
CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules,
AnalysisErrorListener reporter, this._options)
: typeProvider = typeProvider,
rules = rules,
- reporter = reporter,
- _overrideChecker = new _OverrideChecker(typeProvider, rules, reporter);
+ reporter = reporter {
+ _overrideChecker = new _OverrideChecker(this);
+ }
- bool get failure => _failure || _overrideChecker._failure;
+ bool get failure => _failure;
void checkArgument(Expression arg, DartType expectedType) {
// Preserve named argument structure, so their immediate parent is the
@@ -170,7 +173,13 @@ class CodeChecker extends RecursiveAstVisitor {
void reset() {
_failure = false;
- _overrideChecker._failure = false;
+ }
+
+ @override
+ void visitCompilationUnit(CompilationUnit node) {
+ _hasImplicitCasts = false;
+ node.visitChildren(this);
+ setHasImplicitCasts(node, _hasImplicitCasts);
}
@override
@@ -264,7 +273,7 @@ class CodeChecker extends RecursiveAstVisitor {
for (int i = 0, last = init.length - 1; i < last; i++) {
final node = init[i];
if (node is SuperConstructorInvocation) {
- _recordMessage(new InvalidSuperInvocation(node));
+ _recordMessage(node, StrongModeCode.INVALID_SUPER_INVOCATION, [node]);
}
}
}
@@ -315,9 +324,8 @@ class CodeChecker extends RecursiveAstVisitor {
node.identifier.staticElement as FieldFormalParameterElement;
var fieldType = _elementType(fieldElement.field);
if (!rules.isSubtypeOf(type, fieldType)) {
- var staticInfo =
- new InvalidParameterDeclaration(rules, node, fieldType);
- _recordMessage(staticInfo);
+ _recordMessage(node, StrongModeCode.INVALID_PARAMETER_DECLARATION,
+ [node, fieldType]);
}
}
node.visitChildren(this);
@@ -345,8 +353,7 @@ class CodeChecker extends RecursiveAstVisitor {
sequenceInterface.instantiate([DynamicTypeImpl.instance]);
if (rules.isSubtypeOf(sequenceType, iterableType)) {
- _recordMessage(DownCast.create(
- rules, node.iterable, iterableType, sequenceType, _options));
+ _recordImplicitCast(node.iterable, iterableType, sequenceType);
elementType = DynamicTypeImpl.instance;
}
}
@@ -500,7 +507,7 @@ class CodeChecker extends RecursiveAstVisitor {
//
// The first case is handled here, the second case is handled below when
// we call [checkFunctionApplication].
- DynamicInvoke.set(node.methodName, true);
+ setIsDynamicInvoke(node.methodName, true);
} else {
checkFunctionApplication(node, node.methodName, node.argumentList);
}
@@ -619,7 +626,6 @@ class CodeChecker extends RecursiveAstVisitor {
assert(functionType.optionalParameterTypes.isEmpty);
// Check the LHS type.
- var staticInfo;
var rhsType = _getStaticType(expr.rightHandSide);
var lhsType = _getStaticType(expr.leftHandSide);
var returnType = rules.refineBinaryExpressionType(
@@ -632,19 +638,17 @@ class CodeChecker extends RecursiveAstVisitor {
rules.isSubtypeOf(lhsType, rhsType)) {
// This is also slightly different from spec, but allows us to keep
// compound operators in the int += num and num += dynamic cases.
- staticInfo = DownCast.create(
- rules, expr.rightHandSide, rhsType, lhsType, _options);
- rhsType = lhsType;
+ _recordImplicitCast(expr.rightHandSide, rhsType, lhsType);
} else {
- staticInfo = new StaticTypeError(expr, lhsType);
+ _recordMessage(expr, StrongModeCode.STATIC_TYPE_ERROR,
+ [expr, returnType, lhsType]);
}
- _recordMessage(staticInfo);
- }
-
- // Check the rhs type
- if (staticInfo is! CoercionInfo) {
- var paramType = paramTypes.first;
- _checkDowncast(expr.rightHandSide, paramType);
+ } else {
+ // Check the RHS type.
+ //
+ // This is only needed if we didn't already need a cast, and avoids
+ // emitting two messages for the same expression.
+ _checkDowncast(expr.rightHandSide, paramTypes.first);
}
}
}
@@ -679,7 +683,7 @@ class CodeChecker extends RecursiveAstVisitor {
// Downcast if toT <: fromT
if (rules.isSubtypeOf(to, from)) {
- _recordMessage(DownCast.create(rules, expr, from, to, _options));
+ _recordImplicitCast(expr, from, to);
return;
}
@@ -694,7 +698,7 @@ class CodeChecker extends RecursiveAstVisitor {
// Iterable<T> for some concrete T (e.g. Object). These are unrelated
// in the restricted system, but List<dynamic> <: Iterable<T> in dart.
if (from.isAssignableTo(to)) {
- _recordMessage(DownCast.create(rules, expr, from, to, _options));
+ _recordImplicitCast(expr, from, to);
}
}
@@ -731,7 +735,7 @@ class CodeChecker extends RecursiveAstVisitor {
void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) {
var type = getType(typeName);
if (!rules.isGroundType(type)) {
- _recordMessage(new NonGroundTypeCheckInfo(node, type));
+ _recordMessage(node, StrongModeCode.NON_GROUND_TYPE_CHECK_INFO, [type]);
}
}
@@ -749,6 +753,86 @@ class CodeChecker extends RecursiveAstVisitor {
}
}
+ /// Records an implicit cast for the [expression] from [fromType] to [toType].
+ ///
+ /// This will emit the appropriate error/warning/hint message as well as mark
+ /// the AST node.
+ void _recordImplicitCast(
+ Expression expression, DartType fromType, DartType toType) {
+ // toT <:_R fromT => to <: fromT
+ // NB: classes with call methods are subtypes of function
+ // types, but the function type is not assignable to the class
+ assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType));
+
+ // Inference "casts":
+ if (expression is Literal || expression is FunctionExpression) {
+ // fromT should be an exact type - this will almost certainly fail at
+ // runtime.
+ _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR,
+ [expression, fromType, toType]);
+ return;
+ }
+
+ if (expression is InstanceCreationExpression) {
+ ConstructorElement e = expression.staticElement;
+ if (e == null || !e.isFactory) {
+ // fromT should be an exact type - this will almost certainly fail at
+ // runtime.
+
+ _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR,
+ [expression, fromType, toType]);
+ return;
+ }
+ }
+
+ if (isKnownFunction(expression)) {
+ _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR,
+ [expression, fromType, toType]);
+ return;
+ }
+
+ // TODO(vsm): Change this to an assert when we have generic methods and
+ // fix TypeRules._coerceTo to disallow implicit sideways casts.
+ bool downCastComposite = false;
+ if (!rules.isSubtypeOf(toType, fromType)) {
+ assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType));
+ downCastComposite = true;
+ }
+
+ // Composite cast: these are more likely to fail.
+ if (!rules.isGroundType(toType)) {
+ // This cast is (probably) due to our different treatment of dynamic.
+ // It may be more likely to fail at runtime.
+ if (fromType is InterfaceType) {
+ // For class types, we'd like to allow non-generic down casts, e.g.,
+ // Iterable<T> to List<T>. The intuition here is that raw (generic)
+ // casts are problematic, and we should complain about those.
+ var typeArgs = fromType.typeArguments;
+ downCastComposite =
+ typeArgs.isEmpty || typeArgs.any((t) => t.isDynamic);
+ } else {
+ downCastComposite = true;
+ }
+ }
+
+ var parent = expression.parent;
+ ErrorCode errorCode;
+ if (downCastComposite) {
+ errorCode = StrongModeCode.DOWN_CAST_COMPOSITE;
+ } else if (fromType.isDynamic) {
+ errorCode = StrongModeCode.DYNAMIC_CAST;
+ } else if (parent is VariableDeclaration &&
+ parent.initializer == expression) {
+ errorCode = StrongModeCode.ASSIGNMENT_CAST;
+ } else {
+ errorCode = StrongModeCode.DOWN_CAST_IMPLICIT;
+ }
+
+ _recordMessage(expression, errorCode, [fromType, toType]);
+ setImplicitCast(expression, toType);
+ _hasImplicitCasts = true;
+ }
+
// Produce a coercion which coerces something of type fromT
// to something of type toT.
// Returns the error coercion if the types cannot be coerced
@@ -811,7 +895,7 @@ class CodeChecker extends RecursiveAstVisitor {
DartType t = expr.staticType ?? DynamicTypeImpl.instance;
// Remove fuzzy arrow if possible.
- if (t is FunctionType && StaticInfo.isKnownFunction(expr)) {
+ if (t is FunctionType && isKnownFunction(expr)) {
t = rules.functionTypeToConcreteType(typeProvider, t);
}
@@ -893,46 +977,57 @@ class CodeChecker extends RecursiveAstVisitor {
return _isObjectGetter(target, id) || _isObjectMethod(target, id);
}
- void _recordDynamicInvoke(AstNode node, AstNode target) {
- if (_options.strongModeHints) {
- reporter.onError(new DynamicInvoke(node).toAnalysisError());
- }
+ void _recordDynamicInvoke(AstNode node, Expression target) {
+ _recordMessage(node, StrongModeCode.DYNAMIC_INVOKE, [node]);
// TODO(jmesserly): we may eventually want to record if the whole operation
// (node) was dynamic, rather than the target, but this is an easier fit
// with what we used to do.
- DynamicInvoke.set(target, true);
+ setIsDynamicInvoke(target, true);
}
- void _recordMessage(StaticInfo info) {
- if (info == null) return;
- var error = info.toAnalysisError();
- var severity = error.errorCode.errorSeverity;
+ void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) {
+ var severity = errorCode.errorSeverity;
if (severity == ErrorSeverity.ERROR) _failure = true;
if (severity != ErrorSeverity.INFO || _options.strongModeHints) {
+ int begin = node is AnnotatedNode
+ ? node.firstTokenAfterCommentAndMetadata.offset
+ : node.offset;
+ int length = node.end - begin;
+ var source = (node.root as CompilationUnit).element.source;
+ var error =
+ new AnalysisError(source, begin, length, errorCode, arguments);
reporter.onError(error);
}
-
- if (info is CoercionInfo) {
- // TODO(jmesserly): if we're run again on the same AST, we'll produce the
- // same annotations. This should be harmless. This might go away once
- // CodeChecker is integrated better with analyzer, as it will know that
- // checking has already been performed.
- // assert(CoercionInfo.get(info.node) == null);
- CoercionInfo.set(info.node, info);
- }
}
}
+bool isKnownFunction(Expression expression) {
+ Element element = null;
+ if (expression is FunctionExpression) {
+ return true;
+ } else if (expression is PropertyAccess) {
+ element = expression.propertyName.staticElement;
+ } else if (expression is Identifier) {
+ element = expression.staticElement;
+ }
+// First class functions and static methods, where we know the original
+// declaration, will have an exact type, so we know a downcast will fail.
+ return element is FunctionElement ||
+ element is MethodElement && element.isStatic;
+}
+
/// Checks for overriding declarations of fields and methods. This is used to
/// check overrides between classes and superclasses, interfaces, and mixin
/// applications.
class _OverrideChecker {
- bool _failure = false;
final StrongTypeSystemImpl rules;
final TypeProvider _typeProvider;
- final AnalysisErrorListener _reporter;
+ final CodeChecker _checker;
- _OverrideChecker(this._typeProvider, this.rules, this._reporter);
+ _OverrideChecker(CodeChecker checker)
+ : _checker = checker,
+ rules = checker.rules,
+ _typeProvider = checker.typeProvider;
void check(ClassDeclaration node) {
if (node.element.type.isObject) return;
@@ -1196,8 +1291,14 @@ class _OverrideChecker {
// as a field. We effectively treat fields as final / non-virtual.
PropertyInducingElement field = _getMemberField(type, element);
if (field != null) {
- _recordMessage(new InvalidFieldOverride(
- errorLocation, element, type, subType, baseType));
+ _checker._recordMessage(
+ errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [
+ element.enclosingElement.name,
+ element.name,
+ subType,
+ type,
+ baseType
+ ]);
}
}
FunctionType concreteSubType = subType;
@@ -1225,8 +1326,23 @@ class _OverrideChecker {
// get foo => e; // no type specified.
// toString() { ... } // no return type specified.
// }
- _recordMessage(new InvalidMethodOverride(
- errorLocation, element, type, subType, baseType));
+
+ ErrorCode errorCode;
+ if (errorLocation is ExtendsClause) {
+ errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE;
+ } else if (errorLocation.parent is WithClause) {
+ errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN;
+ } else {
+ errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE;
+ }
+
+ _checker._recordMessage(errorLocation, errorCode, [
+ element.enclosingElement.name,
+ element.name,
+ subType,
+ type,
+ baseType
+ ]);
}
return true;
}
@@ -1265,11 +1381,4 @@ class _OverrideChecker {
current = current.superclass;
} while (!current.isObject && !visited.contains(current));
}
-
- void _recordMessage(StaticInfo info) {
- if (info == null) return;
- var error = info.toAnalysisError();
- if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
- _reporter.onError(error);
- }
}
« no previous file with comments | « pkg/analyzer/lib/src/task/strong/ast_properties.dart ('k') | pkg/analyzer/lib/src/task/strong/info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698