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

Side by Side Diff: packages/analyzer/lib/src/task/strong/checker.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be
6 // refactored to fit into analyzer. 6 // refactored to fit into analyzer.
7 library analyzer.src.task.strong.checker; 7 library analyzer.src.task.strong.checker;
8 8
9 import 'package:analyzer/analyzer.dart'; 9 import 'package:analyzer/analyzer.dart';
10 import 'package:analyzer/src/generated/ast.dart'; 10 import 'package:analyzer/dart/ast/ast.dart';
11 import 'package:analyzer/src/generated/element.dart'; 11 import 'package:analyzer/dart/ast/token.dart' show TokenType;
12 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType; 12 import 'package:analyzer/dart/ast/token.dart';
13 13 import 'package:analyzer/dart/ast/visitor.dart';
14 import 'info.dart'; 14 import 'package:analyzer/dart/element/element.dart';
15 import 'rules.dart'; 15 import 'package:analyzer/dart/element/type.dart';
16 16 import 'package:analyzer/src/dart/element/type.dart';
17 /// Checks for overriding declarations of fields and methods. This is used to 17 import 'package:analyzer/src/error/codes.dart' show StrongModeCode;
18 /// check overrides between classes and superclasses, interfaces, and mixin 18 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
19 /// applications. 19 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
20 class _OverrideChecker { 20 import 'package:analyzer/src/generated/type_system.dart';
21
22 import 'ast_properties.dart';
23
24 /// Given an [expression] and a corresponding [typeSystem] and [typeProvider],
25 /// gets the known static type of the expression.
26 ///
27 /// Normally when we ask for an expression's type, we get the type of the
28 /// storage slot that would contain it. For function types, this is necessarily
29 /// a "fuzzy arrow" that treats `dynamic` as bottom. However, if we're
30 /// interested in the expression's own type, it can often be a "strict arrow"
31 /// because we know it evaluates to a specific, concrete function, and we can
32 /// treat "dynamic" as top for that case, which is more permissive.
33 DartType getDefiniteType(
34 Expression expression, TypeSystem typeSystem, TypeProvider typeProvider) {
35 DartType type = expression.staticType ?? DynamicTypeImpl.instance;
36 if (typeSystem is StrongTypeSystemImpl &&
37 type is FunctionType &&
38 _hasStrictArrow(expression)) {
39 // Remove fuzzy arrow if possible.
40 return typeSystem.functionTypeToConcreteType(typeProvider, type);
41 }
42 return type;
43 }
44
45 bool isKnownFunction(Expression expression) {
46 var element = _getKnownElement(expression);
47 // First class functions and static methods, where we know the original
48 // declaration, will have an exact type, so we know a downcast will fail.
49 return element is FunctionElement ||
50 element is MethodElement && element.isStatic;
51 }
52
53 DartType _elementType(Element e) {
54 if (e == null) {
55 // Malformed code - just return dynamic.
56 return DynamicTypeImpl.instance;
57 }
58 return (e as dynamic).type;
59 }
60
61 Element _getKnownElement(Expression expression) {
62 if (expression is ParenthesizedExpression) {
63 expression = (expression as ParenthesizedExpression).expression;
64 }
65 if (expression is FunctionExpression) {
66 return expression.element;
67 } else if (expression is PropertyAccess) {
68 return expression.propertyName.staticElement;
69 } else if (expression is Identifier) {
70 return expression.staticElement;
71 }
72 return null;
73 }
74
75 /// Return the field on type corresponding to member, or null if none
76 /// exists or the "field" is actually a getter/setter.
77 FieldElement _getMemberField(
78 InterfaceType type, PropertyAccessorElement member) {
79 String memberName = member.name;
80 FieldElement field;
81 if (member.isGetter) {
82 // The subclass member is an explicit getter or a field
83 // - lookup the getter on the superclass.
84 var getter = type.getGetter(memberName);
85 if (getter == null || getter.isStatic) return null;
86 field = getter.variable;
87 } else if (!member.isSynthetic) {
88 // The subclass member is an explicit setter
89 // - lookup the setter on the superclass.
90 // Note: an implicit (synthetic) setter would have already been flagged on
91 // the getter above.
92 var setter = type.getSetter(memberName);
93 if (setter == null || setter.isStatic) return null;
94 field = setter.variable;
95 } else {
96 return null;
97 }
98 if (field.isSynthetic) return null;
99 return field;
100 }
101
102 /// Looks up the declaration that matches [member] in [type] and returns it's
103 /// declared type.
104 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) =>
105 _memberTypeGetter(member)(type);
106
107 bool _hasStrictArrow(Expression expression) {
108 var element = _getKnownElement(expression);
109 return element is FunctionElement || element is MethodElement;
110 }
111
112 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) {
113 String memberName = member.name;
114 final isGetter = member is PropertyAccessorElement && member.isGetter;
115 final isSetter = member is PropertyAccessorElement && member.isSetter;
116
117 FunctionType f(InterfaceType type) {
118 ExecutableElement baseMethod;
119
120 if (member.isPrivate) {
121 var subtypeLibrary = member.library;
122 var baseLibrary = type.element.library;
123 if (baseLibrary != subtypeLibrary) {
124 return null;
125 }
126 }
127
128 try {
129 if (isGetter) {
130 assert(!isSetter);
131 // Look for getter or field.
132 baseMethod = type.getGetter(memberName);
133 } else if (isSetter) {
134 baseMethod = type.getSetter(memberName);
135 } else {
136 baseMethod = type.getMethod(memberName);
137 }
138 } catch (e) {
139 // TODO(sigmund): remove this try-catch block (see issue #48).
140 }
141 if (baseMethod == null || baseMethod.isStatic) return null;
142 return baseMethod.type;
143 }
144
145 return f;
146 }
147
148 typedef FunctionType _MemberTypeGetter(InterfaceType type);
149
150 /// Checks the body of functions and properties.
151 class CodeChecker extends RecursiveAstVisitor {
152 final StrongTypeSystemImpl rules;
153 final TypeProvider typeProvider;
154 final AnalysisErrorListener reporter;
155 final AnalysisOptionsImpl _options;
156 _OverrideChecker _overrideChecker;
157
21 bool _failure = false; 158 bool _failure = false;
22 final TypeRules _rules; 159 bool _hasImplicitCasts;
23 final AnalysisErrorListener _reporter; 160
24 161 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules,
25 _OverrideChecker(this._rules, this._reporter); 162 AnalysisErrorListener reporter, this._options)
26 163 : typeProvider = typeProvider,
27 void check(ClassDeclaration node) { 164 rules = rules,
28 if (node.element.type.isObject) return; 165 reporter = reporter {
29 _checkSuperOverrides(node); 166 _overrideChecker = new _OverrideChecker(this);
30 _checkMixinApplicationOverrides(node); 167 }
31 _checkAllInterfaceOverrides(node); 168
32 } 169 bool get failure => _failure;
33 170
34 /// Check overrides from mixin applications themselves. For example, in: 171 void checkArgument(Expression arg, DartType expectedType) {
35 /// 172 // Preserve named argument structure, so their immediate parent is the
36 /// A extends B with E, F 173 // method invocation.
37 /// 174 Expression baseExpression = arg is NamedExpression ? arg.expression : arg;
38 /// we check: 175 checkAssignment(baseExpression, expectedType);
39 /// 176 }
40 /// B & E against B (equivalently how E overrides B) 177
41 /// B & E & F against B & E (equivalently how F overrides both B and E) 178 void checkArgumentList(ArgumentList node, FunctionType type) {
42 void _checkMixinApplicationOverrides(ClassDeclaration node) { 179 NodeList<Expression> list = node.arguments;
43 var type = node.element.type; 180 int len = list.length;
44 var parent = type.superclass; 181 for (int i = 0; i < len; ++i) {
45 var mixins = type.mixins; 182 Expression arg = list[i];
46 183 ParameterElement element = arg.staticParameterElement;
47 // Check overrides from applying mixins 184 if (element == null) {
48 for (int i = 0; i < mixins.length; i++) { 185 // We found an argument mismatch, the analyzer will report this too,
49 var seen = new Set<String>(); 186 // so no need to insert an error for this here.
50 var current = mixins[i]; 187 continue;
51 var errorLocation = node.withClause.mixinTypes[i]; 188 }
52 for (int j = i - 1; j >= 0; j--) { 189 checkArgument(arg, _elementType(element));
53 _checkIndividualOverridesFromType( 190 }
54 current, mixins[j], errorLocation, seen); 191 }
55 } 192
56 _checkIndividualOverridesFromType(current, parent, errorLocation, seen); 193 void checkAssignment(Expression expr, DartType type) {
57 } 194 if (expr is ParenthesizedExpression) {
58 } 195 checkAssignment(expr.expression, type);
59 196 } else {
60 /// Check overrides between a class and its superclasses and mixins. For 197 _checkImplicitCast(expr, type);
61 /// example, in: 198 }
62 /// 199 }
63 /// A extends B with E, F 200
64 /// 201 /// Analyzer checks boolean conversions, but we need to check too, because
65 /// we check A against B, B super classes, E, and F. 202 /// it uses the default assignability rules that allow `dynamic` and `Object`
66 /// 203 /// to be assigned to bool with no message.
67 /// Internally we avoid reporting errors twice and we visit classes bottom up 204 void checkBoolean(Expression expr) =>
68 /// to ensure we report the most immediate invalid override first. For 205 checkAssignment(expr, typeProvider.boolType);
69 /// example, in the following code we'll report that `Test` has an invalid 206
70 /// override with respect to `Parent` (as opposed to an invalid override with 207 void checkFunctionApplication(InvocationExpression node) {
71 /// respect to `Grandparent`): 208 var ft = _getTypeAsCaller(node);
72 /// 209
73 /// class Grandparent { 210 if (_isDynamicCall(node, ft)) {
74 /// m(A a) {} 211 // If f is Function and this is a method invocation, we should have
75 /// } 212 // gotten an analyzer error, so no need to issue another error.
76 /// class Parent extends Grandparent { 213 _recordDynamicInvoke(node, node.function);
77 /// m(A a) {} 214 } else {
78 /// } 215 checkArgumentList(node.argumentList, ft);
79 /// class Test extends Parent { 216 }
80 /// m(B a) {} // invalid override 217 }
81 /// } 218
82 void _checkSuperOverrides(ClassDeclaration node) { 219 DartType getType(TypeName name) {
83 var seen = new Set<String>(); 220 return (name == null) ? DynamicTypeImpl.instance : name.type;
84 var current = node.element.type; 221 }
85 var visited = new Set<InterfaceType>(); 222
86 do { 223 void reset() {
87 visited.add(current); 224 _failure = false;
88 current.mixins.reversed 225 }
89 .forEach((m) => _checkIndividualOverridesFromClass(node, m, seen)); 226
90 _checkIndividualOverridesFromClass(node, current.superclass, seen); 227 @override
91 current = current.superclass; 228 void visitAsExpression(AsExpression node) {
92 } while (!current.isObject && !visited.contains(current)); 229 // We could do the same check as the IsExpression below, but that is
93 } 230 // potentially too conservative. Instead, at runtime, we must fail hard
94 231 // if the Dart as and the DDC as would return different values.
95 /// Checks that implementations correctly override all reachable interfaces. 232 node.visitChildren(this);
96 /// In particular, we need to check these overrides for the definitions in 233 }
97 /// the class itself and each its superclasses. If a superclass is not 234
98 /// abstract, then we can skip its transitive interfaces. For example, in: 235 @override
99 /// 236 void visitAssignmentExpression(AssignmentExpression node) {
100 /// B extends C implements G 237 Token operator = node.operator;
101 /// A extends B with E, F implements H, I 238 TokenType operatorType = operator.type;
102 /// 239 if (operatorType == TokenType.EQ ||
103 /// we check: 240 operatorType == TokenType.QUESTION_QUESTION_EQ) {
104 /// 241 DartType staticType = _getDefiniteType(node.leftHandSide);
105 /// C against G, H, and I 242 checkAssignment(node.rightHandSide, staticType);
106 /// B against G, H, and I 243 } else if (operatorType == TokenType.AMPERSAND_AMPERSAND_EQ ||
107 /// E against H and I // no check against G because B is a concrete class 244 operatorType == TokenType.BAR_BAR_EQ) {
108 /// F against H and I 245 checkAssignment(node.leftHandSide, typeProvider.boolType);
109 /// A against H and I 246 checkAssignment(node.rightHandSide, typeProvider.boolType);
110 void _checkAllInterfaceOverrides(ClassDeclaration node) { 247 } else {
111 var seen = new Set<String>(); 248 _checkCompoundAssignment(node);
112 // Helper function to collect all reachable interfaces. 249 }
113 find(InterfaceType interfaceType, Set result) { 250 node.visitChildren(this);
114 if (interfaceType == null || interfaceType.isObject) return; 251 }
115 if (result.contains(interfaceType)) return; 252
116 result.add(interfaceType); 253 @override
117 find(interfaceType.superclass, result); 254 void visitBinaryExpression(BinaryExpression node) {
118 interfaceType.mixins.forEach((i) => find(i, result)); 255 var op = node.operator;
119 interfaceType.interfaces.forEach((i) => find(i, result)); 256 if (op.isUserDefinableOperator) {
120 } 257 var element = node.staticElement;
121 258 if (element == null) {
122 // Check all interfaces reachable from the `implements` clause in the 259 // Dynamic invocation
123 // current class against definitions here and in superclasses. 260 // TODO(vsm): Move this logic to the resolver?
124 var localInterfaces = new Set<InterfaceType>(); 261 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) {
125 var type = node.element.type; 262 _recordDynamicInvoke(node, node.leftOperand);
126 type.interfaces.forEach((i) => find(i, localInterfaces));
127 _checkInterfacesOverrides(node, localInterfaces, seen,
128 includeParents: true);
129
130 // Check also how we override locally the interfaces from parent classes if
131 // the parent class is abstract. Otherwise, these will be checked as
132 // overrides on the concrete superclass.
133 var superInterfaces = new Set<InterfaceType>();
134 var parent = type.superclass;
135 // TODO(sigmund): we don't seem to be reporting the analyzer error that a
136 // non-abstract class is not implementing an interface. See
137 // https://github.com/dart-lang/dart-dev-compiler/issues/25
138 while (parent != null && parent.element.isAbstract) {
139 parent.interfaces.forEach((i) => find(i, superInterfaces));
140 parent = parent.superclass;
141 }
142 _checkInterfacesOverrides(node, superInterfaces, seen,
143 includeParents: false);
144 }
145
146 /// Checks that [cls] and its super classes (including mixins) correctly
147 /// overrides each interface in [interfaces]. If [includeParents] is false,
148 /// then mixins are still checked, but the base type and it's transitive
149 /// supertypes are not.
150 ///
151 /// [cls] can be either a [ClassDeclaration] or a [InterfaceType]. For
152 /// [ClassDeclaration]s errors are reported on the member that contains the
153 /// invalid override, for [InterfaceType]s we use [errorLocation] instead.
154 void _checkInterfacesOverrides(
155 cls, Iterable<InterfaceType> interfaces, Set<String> seen,
156 {Set<InterfaceType> visited,
157 bool includeParents: true,
158 AstNode errorLocation}) {
159 var node = cls is ClassDeclaration ? cls : null;
160 var type = cls is InterfaceType ? cls : node.element.type;
161
162 if (visited == null) {
163 visited = new Set<InterfaceType>();
164 } else if (visited.contains(type)) {
165 // Malformed type.
166 return;
167 } else {
168 visited.add(type);
169 }
170
171 // Check direct overrides on [type]
172 for (var interfaceType in interfaces) {
173 if (node != null) {
174 _checkIndividualOverridesFromClass(node, interfaceType, seen);
175 } else {
176 _checkIndividualOverridesFromType(
177 type, interfaceType, errorLocation, seen);
178 }
179 }
180
181 // Check overrides from its mixins
182 for (int i = 0; i < type.mixins.length; i++) {
183 var loc =
184 errorLocation != null ? errorLocation : node.withClause.mixinTypes[i];
185 for (var interfaceType in interfaces) {
186 // We copy [seen] so we can report separately if more than one mixin or
187 // the base class have an invalid override.
188 _checkIndividualOverridesFromType(
189 type.mixins[i], interfaceType, loc, new Set.from(seen));
190 }
191 }
192
193 // Check overrides from its superclasses
194 if (includeParents) {
195 var parent = type.superclass;
196 if (parent.isObject) return;
197 var loc = errorLocation != null ? errorLocation : node.extendsClause;
198 // No need to copy [seen] here because we made copies above when reporting
199 // errors on mixins.
200 _checkInterfacesOverrides(parent, interfaces, seen,
201 visited: visited, includeParents: true, errorLocation: loc);
202 }
203 }
204
205 /// Check that individual methods and fields in [subType] correctly override
206 /// the declarations in [baseType].
207 ///
208 /// The [errorLocation] node indicates where errors are reported, see
209 /// [_checkSingleOverride] for more details.
210 ///
211 /// The set [seen] is used to avoid reporting overrides more than once. It
212 /// is used when invoking this function multiple times when checking several
213 /// types in a class hierarchy. Errors are reported only the first time an
214 /// invalid override involving a specific member is encountered.
215 _checkIndividualOverridesFromType(InterfaceType subType,
216 InterfaceType baseType, AstNode errorLocation, Set<String> seen) {
217 void checkHelper(ExecutableElement e) {
218 if (e.isStatic) return;
219 if (seen.contains(e.name)) return;
220 if (_checkSingleOverride(e, baseType, null, errorLocation)) {
221 seen.add(e.name);
222 }
223 }
224 subType.methods.forEach(checkHelper);
225 subType.accessors.forEach(checkHelper);
226 }
227
228 /// Check that individual methods and fields in [subType] correctly override
229 /// the declarations in [baseType].
230 ///
231 /// The [errorLocation] node indicates where errors are reported, see
232 /// [_checkSingleOverride] for more details.
233 _checkIndividualOverridesFromClass(
234 ClassDeclaration node, InterfaceType baseType, Set<String> seen) {
235 for (var member in node.members) {
236 if (member is ConstructorDeclaration) continue;
237 if (member is FieldDeclaration) {
238 if (member.isStatic) continue;
239 for (var variable in member.fields.variables) {
240 var element = variable.element as PropertyInducingElement;
241 var name = element.name;
242 if (seen.contains(name)) continue;
243 var getter = element.getter;
244 var setter = element.setter;
245 bool found = _checkSingleOverride(getter, baseType, variable, member);
246 if (!variable.isFinal &&
247 !variable.isConst &&
248 _checkSingleOverride(setter, baseType, variable, member)) {
249 found = true;
250 }
251 if (found) seen.add(name);
252 } 263 }
253 } else { 264 } else {
254 if ((member as MethodDeclaration).isStatic) continue; 265 // Method invocation.
255 var method = (member as MethodDeclaration).element; 266 if (element is MethodElement) {
256 if (seen.contains(method.name)) continue; 267 var type = element.type;
257 if (_checkSingleOverride(method, baseType, member, member)) { 268 // Analyzer should enforce number of parameter types, but check in
258 seen.add(method.name); 269 // case we have erroneous input.
270 if (type.normalParameterTypes.isNotEmpty) {
271 checkArgument(node.rightOperand, type.normalParameterTypes[0]);
272 }
273 } else {
274 // TODO(vsm): Assert that the analyzer found an error here?
259 } 275 }
260 } 276 }
261 } 277 } else {
262 } 278 // Non-method operator.
263 279 switch (op.type) {
264 /// Checks that [element] correctly overrides its corresponding member in 280 case TokenType.AMPERSAND_AMPERSAND:
265 /// [type]. Returns `true` if an override was found, that is, if [element] has 281 case TokenType.BAR_BAR:
266 /// a corresponding member in [type] that it overrides. 282 checkBoolean(node.leftOperand);
267 /// 283 checkBoolean(node.rightOperand);
268 /// The [errorLocation] is a node where the error is reported. For example, a 284 break;
269 /// bad override of a method in a class with respect to its superclass is 285 case TokenType.BANG_EQ:
270 /// reported directly at the method declaration. However, invalid overrides 286 break;
271 /// from base classes to interfaces, mixins to the base they are applied to, 287 case TokenType.QUESTION_QUESTION:
272 /// or mixins to interfaces are reported at the class declaration, since the 288 break;
273 /// base class or members on their own were not incorrect, only combining them 289 default:
274 /// with the interface was problematic. For example, these are example error 290 assert(false);
275 /// locations in these cases: 291 }
276 /// 292 }
277 /// error: base class introduces an invalid override. The type of B.foo is 293 node.visitChildren(this);
278 /// not a subtype of E.foo: 294 }
279 /// class A extends B implements E { ... } 295
280 /// ^^^^^^^^^ 296 @override
281 /// 297 void visitClassDeclaration(ClassDeclaration node) {
282 /// error: mixin introduces an invalid override. The type of C.foo is not 298 _overrideChecker.check(node);
283 /// a subtype of E.foo: 299 super.visitClassDeclaration(node);
284 /// class A extends B with C implements E { ... } 300 }
285 /// ^
286 ///
287 /// When checking for overrides from a type and it's super types, [node] is
288 /// the AST node that defines [element]. This is used to determine whether the
289 /// type of the element could be inferred from the types in the super classes.
290 bool _checkSingleOverride(ExecutableElement element, InterfaceType type,
291 AstNode node, AstNode errorLocation) {
292 assert(!element.isStatic);
293
294 FunctionType subType = _rules.elementType(element);
295 // TODO(vsm): Test for generic
296 FunctionType baseType = _getMemberType(type, element);
297
298 if (baseType == null) return false;
299 if (!_rules.isAssignable(subType, baseType)) {
300 // See whether non-assignable cases fit one of our common patterns:
301 //
302 // Common pattern 1: Inferable return type (on getters and methods)
303 // class A {
304 // int get foo => ...;
305 // String toString() { ... }
306 // }
307 // class B extends A {
308 // get foo => e; // no type specified.
309 // toString() { ... } // no return type specified.
310 // }
311 _recordMessage(new InvalidMethodOverride(
312 errorLocation, element, type, subType, baseType));
313 }
314 return true;
315 }
316
317 void _recordMessage(StaticInfo info) {
318 if (info == null) return;
319 var error = info.toAnalysisError();
320 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
321 _reporter.onError(error);
322 }
323 }
324
325 /// Checks the body of functions and properties.
326 class CodeChecker extends RecursiveAstVisitor {
327 final TypeRules rules;
328 final AnalysisErrorListener reporter;
329 final _OverrideChecker _overrideChecker;
330 final bool _hints;
331
332 bool _failure = false;
333 bool get failure => _failure || _overrideChecker._failure;
334
335 void reset() {
336 _failure = false;
337 _overrideChecker._failure = false;
338 }
339
340 CodeChecker(TypeRules rules, AnalysisErrorListener reporter,
341 {bool hints: false})
342 : rules = rules,
343 reporter = reporter,
344 _hints = hints,
345 _overrideChecker = new _OverrideChecker(rules, reporter);
346 301
347 @override 302 @override
348 void visitComment(Comment node) { 303 void visitComment(Comment node) {
349 // skip, no need to do typechecking inside comments (they may contain 304 // skip, no need to do typechecking inside comments (they may contain
350 // comment references which would require resolution). 305 // comment references which would require resolution).
351 } 306 }
352 307
353 @override 308 @override
354 void visitClassDeclaration(ClassDeclaration node) { 309 void visitCompilationUnit(CompilationUnit node) {
355 _overrideChecker.check(node); 310 _hasImplicitCasts = false;
356 super.visitClassDeclaration(node); 311 node.visitChildren(this);
312 setHasImplicitCasts(node, _hasImplicitCasts);
357 } 313 }
358 314
359 @override 315 @override
360 void visitAssignmentExpression(AssignmentExpression node) { 316 void visitConditionalExpression(ConditionalExpression node) {
361 var token = node.operator; 317 checkBoolean(node.condition);
362 if (token.type != TokenType.EQ) {
363 _checkCompoundAssignment(node);
364 } else {
365 DartType staticType = _getStaticType(node.leftHandSide);
366 checkAssignment(node.rightHandSide, staticType);
367 }
368 node.visitChildren(this); 318 node.visitChildren(this);
369 } 319 }
370 320
371 /// Check constructor declaration to ensure correct super call placement. 321 /// Check constructor declaration to ensure correct super call placement.
372 @override 322 @override
373 void visitConstructorDeclaration(ConstructorDeclaration node) { 323 void visitConstructorDeclaration(ConstructorDeclaration node) {
374 node.visitChildren(this); 324 node.visitChildren(this);
375 325
376 final init = node.initializers; 326 final init = node.initializers;
377 for (int i = 0, last = init.length - 1; i < last; i++) { 327 for (int i = 0, last = init.length - 1; i < last; i++) {
378 final node = init[i]; 328 final node = init[i];
379 if (node is SuperConstructorInvocation) { 329 if (node is SuperConstructorInvocation) {
380 _recordMessage(new InvalidSuperInvocation(node)); 330 _recordMessage(node, StrongModeCode.INVALID_SUPER_INVOCATION, [node]);
381 } 331 }
382 } 332 }
383 } 333 }
384 334
335 // Check invocations
385 @override 336 @override
386 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { 337 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
387 var field = node.fieldName; 338 var field = node.fieldName;
388 var element = field.staticElement; 339 var element = field.staticElement;
389 DartType staticType = rules.elementType(element); 340 DartType staticType = _elementType(element);
390 checkAssignment(node.expression, staticType); 341 checkAssignment(node.expression, staticType);
391 node.visitChildren(this); 342 node.visitChildren(this);
392 } 343 }
393 344
394 @override 345 @override
395 void visitForEachStatement(ForEachStatement node) { 346 void visitDefaultFormalParameter(DefaultFormalParameter node) {
396 // Check that the expression is an Iterable. 347 // Check that defaults have the proper subtype.
397 var expr = node.iterable; 348 var parameter = node.parameter;
398 var iterableType = node.awaitKeyword != null 349 var parameterType = _elementType(parameter.element);
399 ? rules.provider.streamType 350 assert(parameterType != null);
400 : rules.provider.iterableType; 351 var defaultValue = node.defaultValue;
401 var loopVariable = node.identifier != null 352 if (defaultValue != null) {
402 ? node.identifier 353 checkAssignment(defaultValue, parameterType);
403 : node.loopVariable?.identifier; 354 }
404 if (loopVariable != null) { 355
405 var iteratorType = loopVariable.staticType; 356 node.visitChildren(this);
406 var checkedType = iterableType.substitute4([iteratorType]); 357 }
407 checkAssignment(expr, checkedType); 358
359 @override
360 void visitDoStatement(DoStatement node) {
361 checkBoolean(node.condition);
362 node.visitChildren(this);
363 }
364
365 @override
366 void visitExpressionFunctionBody(ExpressionFunctionBody node) {
367 _checkReturnOrYield(node.expression, node);
368 node.visitChildren(this);
369 }
370
371 @override
372 void visitFieldFormalParameter(FieldFormalParameter node) {
373 var element = node.element;
374 var typeName = node.type;
375 if (typeName != null) {
376 var type = _elementType(element);
377 var fieldElement =
378 node.identifier.staticElement as FieldFormalParameterElement;
379 var fieldType = _elementType(fieldElement.field);
380 if (!rules.isSubtypeOf(type, fieldType)) {
381 _recordMessage(node, StrongModeCode.INVALID_PARAMETER_DECLARATION,
382 [node, fieldType]);
383 }
408 } 384 }
409 node.visitChildren(this); 385 node.visitChildren(this);
410 } 386 }
411 387
388 @override
389 void visitForEachStatement(ForEachStatement node) {
390 var loopVariable = node.identifier ?? node.loopVariable?.identifier;
391
392 // Safely handle malformed statements.
393 if (loopVariable != null) {
394 // Find the element type of the sequence.
395 var sequenceInterface = node.awaitKeyword != null
396 ? typeProvider.streamType
397 : typeProvider.iterableType;
398 var iterableType = _getDefiniteType(node.iterable);
399 var elementType =
400 rules.mostSpecificTypeArgument(iterableType, sequenceInterface);
401
402 // If the sequence is not an Iterable (or Stream for await for) but is a
403 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then
404 // we'll do a separate cast of the dynamic element to the variable's type.
405 if (elementType == null) {
406 var sequenceType =
407 sequenceInterface.instantiate([DynamicTypeImpl.instance]);
408
409 if (rules.isSubtypeOf(sequenceType, iterableType)) {
410 _recordImplicitCast(node.iterable, sequenceType, from: iterableType);
411 elementType = DynamicTypeImpl.instance;
412 }
413 }
414
415 // If the sequence doesn't implement the interface at all, [ErrorVerifier]
416 // will report the error, so ignore it here.
417 if (elementType != null) {
418 // Insert a cast from the sequence's element type to the loop variable's
419 // if needed.
420 _checkImplicitCast(loopVariable, _getDefiniteType(loopVariable),
421 from: elementType);
422 }
423 }
424
425 node.visitChildren(this);
426 }
427
412 @override 428 @override
413 void visitForStatement(ForStatement node) { 429 void visitForStatement(ForStatement node) {
414 if (node.condition != null) { 430 if (node.condition != null) {
415 checkBoolean(node.condition); 431 checkBoolean(node.condition);
416 } 432 }
417 node.visitChildren(this); 433 node.visitChildren(this);
418 } 434 }
419 435
420 @override 436 @override
437 void visitFunctionExpression(FunctionExpression node) {
438 _checkForUnsafeBlockClosureInference(node);
439 super.visitFunctionExpression(node);
440 }
441
442 @override
443 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
444 checkFunctionApplication(node);
445 node.visitChildren(this);
446 }
447
448 @override
421 void visitIfStatement(IfStatement node) { 449 void visitIfStatement(IfStatement node) {
422 checkBoolean(node.condition); 450 checkBoolean(node.condition);
423 node.visitChildren(this); 451 node.visitChildren(this);
424 } 452 }
425 453
426 @override 454 @override
427 void visitDoStatement(DoStatement node) { 455 void visitIndexExpression(IndexExpression node) {
428 checkBoolean(node.condition); 456 var target = node.realTarget;
457 var element = node.staticElement;
458 if (element == null) {
459 _recordDynamicInvoke(node, target);
460 } else if (element is MethodElement) {
461 var type = element.type;
462 // Analyzer should enforce number of parameter types, but check in
463 // case we have erroneous input.
464 if (type.normalParameterTypes.isNotEmpty) {
465 checkArgument(node.index, type.normalParameterTypes[0]);
466 }
467 } else {
468 // TODO(vsm): Assert that the analyzer found an error here?
469 }
429 node.visitChildren(this); 470 node.visitChildren(this);
430 } 471 }
431 472
432 @override 473 @override
433 void visitWhileStatement(WhileStatement node) { 474 void visitInstanceCreationExpression(InstanceCreationExpression node) {
434 checkBoolean(node.condition); 475 var arguments = node.argumentList;
476 var element = node.staticElement;
477 if (element != null) {
478 var type = _elementType(node.staticElement);
479 checkArgumentList(arguments, type);
480 }
435 node.visitChildren(this); 481 node.visitChildren(this);
436 } 482 }
437 483
438 @override 484 @override
439 void visitSwitchStatement(SwitchStatement node) { 485 void visitIsExpression(IsExpression node) {
440 // SwitchStatement defines a boolean conversion to check the result of the 486 _checkRuntimeTypeCheck(node, node.type);
441 // case value == the switch value, but in dev_compiler we require a boolean
442 // return type from an overridden == operator (because Object.==), so
443 // checking in SwitchStatement shouldn't be necessary.
444 node.visitChildren(this); 487 node.visitChildren(this);
445 } 488 }
446 489
447 @override 490 @override
448 void visitListLiteral(ListLiteral node) { 491 void visitListLiteral(ListLiteral node) {
449 var type = rules.provider.dynamicType; 492 DartType type = DynamicTypeImpl.instance;
450 if (node.typeArguments != null) { 493 if (node.typeArguments != null) {
451 var targs = node.typeArguments.arguments; 494 NodeList<TypeName> targs = node.typeArguments.arguments;
452 if (targs.length > 0) type = targs[0].type; 495 if (targs.length > 0) {
496 type = targs[0].type;
497 }
498 } else {
499 DartType staticType = node.staticType;
500 if (staticType is InterfaceType) {
501 List<DartType> targs = staticType.typeArguments;
502 if (targs != null && targs.length > 0) {
503 type = targs[0];
504 }
505 }
453 } 506 }
454 var elements = node.elements; 507 NodeList<Expression> elements = node.elements;
455 for (int i = 0; i < elements.length; i++) { 508 for (int i = 0; i < elements.length; i++) {
456 checkArgument(elements[i], type); 509 checkArgument(elements[i], type);
457 } 510 }
458 super.visitListLiteral(node); 511 super.visitListLiteral(node);
459 } 512 }
460 513
461 @override 514 @override
462 void visitMapLiteral(MapLiteral node) { 515 void visitMapLiteral(MapLiteral node) {
463 var ktype = rules.provider.dynamicType; 516 DartType ktype = DynamicTypeImpl.instance;
464 var vtype = rules.provider.dynamicType; 517 DartType vtype = DynamicTypeImpl.instance;
465 if (node.typeArguments != null) { 518 if (node.typeArguments != null) {
466 var targs = node.typeArguments.arguments; 519 NodeList<TypeName> targs = node.typeArguments.arguments;
467 if (targs.length > 0) ktype = targs[0].type; 520 if (targs.length > 0) {
468 if (targs.length > 1) vtype = targs[1].type; 521 ktype = targs[0].type;
522 }
523 if (targs.length > 1) {
524 vtype = targs[1].type;
525 }
526 } else {
527 DartType staticType = node.staticType;
528 if (staticType is InterfaceType) {
529 List<DartType> targs = staticType.typeArguments;
530 if (targs != null) {
531 if (targs.length > 0) {
532 ktype = targs[0];
533 }
534 if (targs.length > 1) {
535 vtype = targs[1];
536 }
537 }
538 }
469 } 539 }
470 var entries = node.entries; 540 NodeList<MapLiteralEntry> entries = node.entries;
471 for (int i = 0; i < entries.length; i++) { 541 for (int i = 0; i < entries.length; i++) {
472 var entry = entries[i]; 542 MapLiteralEntry entry = entries[i];
473 checkArgument(entry.key, ktype); 543 checkArgument(entry.key, ktype);
474 checkArgument(entry.value, vtype); 544 checkArgument(entry.value, vtype);
475 } 545 }
476 super.visitMapLiteral(node); 546 super.visitMapLiteral(node);
477 } 547 }
478 548
479 // Check invocations
480 void checkArgumentList(ArgumentList node, FunctionType type) {
481 NodeList<Expression> list = node.arguments;
482 int len = list.length;
483 for (int i = 0; i < len; ++i) {
484 Expression arg = list[i];
485 ParameterElement element = arg.staticParameterElement;
486 if (element == null) {
487 if (type.parameters.length < len) {
488 // We found an argument mismatch, the analyzer will report this too,
489 // so no need to insert an error for this here.
490 continue;
491 }
492 element = type.parameters[i];
493 // TODO(vsm): When can this happen?
494 assert(element != null);
495 }
496 DartType expectedType = rules.elementType(element);
497 if (expectedType == null) expectedType = rules.provider.dynamicType;
498 checkArgument(arg, expectedType);
499 }
500 }
501
502 void checkArgument(Expression arg, DartType expectedType) {
503 // Preserve named argument structure, so their immediate parent is the
504 // method invocation.
505 if (arg is NamedExpression) {
506 arg = (arg as NamedExpression).expression;
507 }
508 checkAssignment(arg, expectedType);
509 }
510
511 void checkFunctionApplication(
512 Expression node, Expression f, ArgumentList list) {
513 if (rules.isDynamicCall(f)) {
514 // If f is Function and this is a method invocation, we should have
515 // gotten an analyzer error, so no need to issue another error.
516 _recordDynamicInvoke(node, f);
517 } else {
518 checkArgumentList(list, rules.getTypeAsCaller(f));
519 }
520 }
521
522 @override 549 @override
523 visitMethodInvocation(MethodInvocation node) { 550 visitMethodInvocation(MethodInvocation node) {
524 var target = node.realTarget; 551 var target = node.realTarget;
525 if (rules.isDynamicTarget(target) && 552 var element = node.methodName.staticElement;
526 !_isObjectMethod(node, node.methodName)) { 553 if (element == null && !typeProvider.isObjectMethod(node.methodName.name)) {
527 _recordDynamicInvoke(node, target); 554 _recordDynamicInvoke(node, target);
528 555
529 // Mark the tear-off as being dynamic, too. This lets us distinguish 556 // Mark the tear-off as being dynamic, too. This lets us distinguish
530 // cases like: 557 // cases like:
531 // 558 //
532 // dynamic d; 559 // dynamic d;
533 // d.someMethod(...); // the whole method call must be a dynamic send. 560 // d.someMethod(...); // the whole method call must be a dynamic send.
534 // 561 //
535 // ... from case like: 562 // ... from case like:
536 // 563 //
537 // SomeType s; 564 // SomeType s;
538 // s.someDynamicField(...); // static get, followed by dynamic call. 565 // s.someDynamicField(...); // static get, followed by dynamic call.
539 // 566 //
540 // The first case is handled here, the second case is handled below when 567 // The first case is handled here, the second case is handled below when
541 // we call [checkFunctionApplication]. 568 // we call [checkFunctionApplication].
542 DynamicInvoke.set(node.methodName, true); 569 setIsDynamicInvoke(node.methodName, true);
543 } else { 570 } else {
544 checkFunctionApplication(node, node.methodName, node.argumentList); 571 checkFunctionApplication(node);
545 } 572 }
546 node.visitChildren(this); 573 node.visitChildren(this);
547 } 574 }
548 575
549 @override 576 @override
550 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { 577 void visitPostfixExpression(PostfixExpression node) {
551 checkFunctionApplication(node, node.function, node.argumentList); 578 _checkUnary(node.operand, node.operator, node.staticElement);
552 node.visitChildren(this); 579 node.visitChildren(this);
580 }
581
582 @override
583 void visitPrefixedIdentifier(PrefixedIdentifier node) {
584 _checkFieldAccess(node, node.prefix, node.identifier);
585 }
586
587 @override
588 void visitPrefixExpression(PrefixExpression node) {
589 if (node.operator.type == TokenType.BANG) {
590 checkBoolean(node.operand);
591 } else {
592 _checkUnary(node.operand, node.operator, node.staticElement);
593 }
594 node.visitChildren(this);
595 }
596
597 @override
598 void visitPropertyAccess(PropertyAccess node) {
599 _checkFieldAccess(node, node.realTarget, node.propertyName);
553 } 600 }
554 601
555 @override 602 @override
556 void visitRedirectingConstructorInvocation( 603 void visitRedirectingConstructorInvocation(
557 RedirectingConstructorInvocation node) { 604 RedirectingConstructorInvocation node) {
558 var type = node.staticElement.type; 605 var type = node.staticElement?.type;
559 checkArgumentList(node.argumentList, type); 606 // TODO(leafp): There's a TODO in visitRedirectingConstructorInvocation
607 // in the element_resolver to handle the case that the element is null
608 // and emit an error. In the meantime, just be defensive here.
609 if (type != null) {
610 checkArgumentList(node.argumentList, type);
611 }
612 node.visitChildren(this);
613 }
614
615 @override
616 void visitReturnStatement(ReturnStatement node) {
617 _checkReturnOrYield(node.expression, node);
560 node.visitChildren(this); 618 node.visitChildren(this);
561 } 619 }
562 620
563 @override 621 @override
564 void visitSuperConstructorInvocation(SuperConstructorInvocation node) { 622 void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
565 var element = node.staticElement; 623 var element = node.staticElement;
566 if (element != null) { 624 if (element != null) {
567 var type = node.staticElement.type; 625 var type = node.staticElement.type;
568 checkArgumentList(node.argumentList, type); 626 checkArgumentList(node.argumentList, type);
569 } 627 }
570 node.visitChildren(this); 628 node.visitChildren(this);
571 } 629 }
572 630
631 @override
632 void visitSwitchStatement(SwitchStatement node) {
633 // SwitchStatement defines a boolean conversion to check the result of the
634 // case value == the switch value, but in dev_compiler we require a boolean
635 // return type from an overridden == operator (because Object.==), so
636 // checking in SwitchStatement shouldn't be necessary.
637 node.visitChildren(this);
638 }
639
640 @override
641 Object visitVariableDeclaration(VariableDeclaration node) {
642 if (!node.isConst &&
643 !node.isFinal &&
644 node.initializer == null &&
645 rules.isNonNullableType(node?.element?.type)) {
646 _recordMessage(
647 node,
648 StaticTypeWarningCode.NON_NULLABLE_FIELD_NOT_INITIALIZED,
649 [node.name, node?.element?.type]);
650 }
651 return super.visitVariableDeclaration(node);
652 }
653
654 @override
655 void visitVariableDeclarationList(VariableDeclarationList node) {
656 TypeName type = node.type;
657 if (type == null) {
658 // No checks are needed when the type is var. Although internally the
659 // typing rules may have inferred a more precise type for the variable
660 // based on the initializer.
661 } else {
662 for (VariableDeclaration variable in node.variables) {
663 var initializer = variable.initializer;
664 if (initializer != null) {
665 checkAssignment(initializer, type.type);
666 }
667 }
668 }
669 node.visitChildren(this);
670 }
671
672 @override
673 void visitWhileStatement(WhileStatement node) {
674 checkBoolean(node.condition);
675 node.visitChildren(this);
676 }
677
678 @override
679 void visitYieldStatement(YieldStatement node) {
680 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null);
681 node.visitChildren(this);
682 }
683
684 void _checkCompoundAssignment(AssignmentExpression expr) {
685 var op = expr.operator.type;
686 assert(op.isAssignmentOperator && op != TokenType.EQ);
687 var methodElement = expr.staticElement;
688 if (methodElement == null) {
689 // Dynamic invocation.
690 _recordDynamicInvoke(expr, expr.leftHandSide);
691 } else {
692 // Sanity check the operator.
693 assert(methodElement.isOperator);
694 var functionType = methodElement.type;
695 var paramTypes = functionType.normalParameterTypes;
696 assert(paramTypes.length == 1);
697 assert(functionType.namedParameterTypes.isEmpty);
698 assert(functionType.optionalParameterTypes.isEmpty);
699
700 // Refine the return type.
701 var rhsType = _getDefiniteType(expr.rightHandSide);
702 var lhsType = _getDefiniteType(expr.leftHandSide);
703 var returnType = rules.refineBinaryExpressionType(
704 typeProvider, lhsType, op, rhsType, functionType.returnType);
705
706 // Check the argument for an implicit cast.
707 _checkImplicitCast(expr.rightHandSide, paramTypes[0], from: rhsType);
708
709 // Check the return type for an implicit cast.
710 //
711 // If needed, mark the assignment to indicate a down cast when we assign
712 // back to it. So these two implicit casts are equivalent:
713 //
714 // y = /*implicit cast*/(y + 42);
715 // /*implicit assignment cast*/y += 42;
716 //
717 _checkImplicitCast(expr.leftHandSide, lhsType,
718 from: returnType, opAssign: true);
719 }
720 }
721
722 /// Returns true if we need an implicit cast of [expr] from [from] type to
723 /// [to] type, otherwise returns false.
724 ///
725 /// If [from] is omitted, uses the static type of [expr].
726 bool _needsImplicitCast(Expression expr, DartType to, {DartType from}) {
727 from ??= _getDefiniteType(expr);
728
729 if (!_checkNonNullAssignment(expr, to, from)) return false;
730
731 // We can use anything as void.
732 if (to.isVoid) return false;
733
734 // fromT <: toT, no coercion needed.
735 if (rules.isSubtypeOf(from, to)) return false;
736
737 // Note: a function type is never assignable to a class per the Dart
738 // spec - even if it has a compatible call method. We disallow as
739 // well for consistency.
740 if (from is FunctionType && rules.getCallMethodType(to) != null) {
741 return false;
742 }
743
744 // Downcast if toT <: fromT
745 if (rules.isSubtypeOf(to, from)) {
746 return true;
747 }
748
749 // Anything else is an illegal sideways cast.
750 // However, these will have been reported already in error_verifier, so we
751 // don't need to report them again.
752 return false;
753 }
754
755 /// Checks if an implicit cast of [expr] from [from] type to [to] type is
756 /// needed, and if so records it.
757 ///
758 /// If [from] is omitted, uses the static type of [expr].
759 ///
760 /// If [expr] does not require an implicit cast because it is not related to
761 /// [to] or is already a subtype of it, does nothing.
762 void _checkImplicitCast(Expression expr, DartType to,
763 {DartType from, bool opAssign: false}) {
764 from ??= _getDefiniteType(expr);
765
766 if (_needsImplicitCast(expr, to, from: from)) {
767 _recordImplicitCast(expr, to, from: from, opAssign: opAssign);
768 }
769 }
770
771 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) {
772 if (field.staticElement == null &&
773 !typeProvider.isObjectMember(field.name)) {
774 _recordDynamicInvoke(node, target);
775 }
776 node.visitChildren(this);
777 }
778
779 /**
780 * Check if the closure [node] is unsafe due to dartbug.com/26947. If so,
781 * issue a warning.
782 *
783 * TODO(paulberry): eliminate this once dartbug.com/26947 is fixed.
784 */
785 void _checkForUnsafeBlockClosureInference(FunctionExpression node) {
786 if (node.body is! BlockFunctionBody) {
787 return;
788 }
789 if (node.element.returnType.isDynamic) {
790 return;
791 }
792 // Find the enclosing variable declaration whose inferred type might depend
793 // on the inferred return type of the block closure (if any).
794 AstNode prevAncestor = node;
795 AstNode ancestor = node.parent;
796 while (ancestor != null && ancestor is! VariableDeclaration) {
797 if (ancestor is BlockFunctionBody) {
798 // node is inside another block function body; if that block
799 // function body is unsafe, we've already warned about it.
800 return;
801 }
802 if (ancestor is InstanceCreationExpression) {
803 // node appears inside an instance creation expression; we may be safe
804 // if the type of the instance creation expression requires no
805 // inference.
806 TypeName typeName = ancestor.constructorName.type;
807 if (typeName.typeArguments != null) {
808 // Type arguments were explicitly specified. We are safe.
809 return;
810 }
811 DartType type = typeName.type;
812 if (!(type is ParameterizedType && type.typeParameters.isNotEmpty)) {
813 // Type is not generic. We are safe.
814 return;
815 }
816 }
817 if (ancestor is MethodInvocation) {
818 // node appears inside a method or function invocation; we may be safe
819 // if the type of the method or function requires no inference.
820 if (ancestor.typeArguments != null) {
821 // Type arguments were explicitly specified. We are safe.
822 return;
823 }
824 Element methodElement = ancestor.methodName.staticElement;
825 if (!(methodElement is ExecutableElement &&
826 methodElement.typeParameters.isNotEmpty)) {
827 // Method is not generic. We are safe.
828 return;
829 }
830 }
831 if (ancestor is FunctionExpressionInvocation &&
832 !identical(prevAncestor, ancestor.function)) {
833 // node appears inside an argument to a function expression invocation;
834 // we may be safe if the type of the function expression requires no
835 // inference.
836 if (ancestor.typeArguments != null) {
837 // Type arguments were explicitly specified. We are safe.
838 return;
839 }
840 DartType type = ancestor.function.staticType;
841 if (!(type is FunctionTypeImpl && type.typeFormals.isNotEmpty)) {
842 // Type is not generic or has had its type parameters instantiated.
843 // We are safe.
844 return;
845 }
846 }
847 if ((ancestor is ListLiteral && ancestor.typeArguments != null) ||
848 (ancestor is MapLiteral && ancestor.typeArguments != null)) {
849 // node appears inside a list or map literal with an explicit type. We
850 // are safe because no type inference is required.
851 return;
852 }
853 prevAncestor = ancestor;
854 ancestor = ancestor.parent;
855 }
856 if (ancestor == null) {
857 // node is not inside a variable declaration, so it is safe.
858 return;
859 }
860 VariableDeclaration decl = ancestor;
861 VariableElement declElement = decl.element;
862 if (!declElement.hasImplicitType) {
863 // Variable declaration has an explicit type, so it's safe.
864 return;
865 }
866 if (declElement.type.isDynamic) {
867 // No type was successfully inferred for this variable, so it's safe.
868 return;
869 }
870 if (declElement.enclosingElement is ExecutableElement) {
871 // Variable declaration is inside a function or method, so it's safe.
872 return;
873 }
874 _recordMessage(node, StrongModeCode.UNSAFE_BLOCK_CLOSURE_INFERENCE,
875 [declElement.name]);
876 }
877
878 /// Checks if the assignment is valid with respect to non-nullable types.
879 /// Returns `false` if a nullable expression is assigned to a variable of
880 /// non-nullable type and `true` otherwise.
881 bool _checkNonNullAssignment(
882 Expression expression, DartType to, DartType from) {
883 if (rules.isNonNullableType(to) && rules.isNullableType(from)) {
884 _recordMessage(
885 expression, StaticTypeWarningCode.INVALID_ASSIGNMENT, [from, to]);
886 return false;
887 }
888 return true;
889 }
890
573 void _checkReturnOrYield(Expression expression, AstNode node, 891 void _checkReturnOrYield(Expression expression, AstNode node,
574 {bool yieldStar: false}) { 892 {bool yieldStar: false}) {
575 var body = node.getAncestor((n) => n is FunctionBody); 893 FunctionBody body = node.getAncestor((n) => n is FunctionBody);
576 var type = rules.getExpectedReturnType(body, yieldStar: yieldStar); 894 var type = _getExpectedReturnType(body, yieldStar: yieldStar);
577 if (type == null) { 895 if (type == null) {
578 // We have a type mismatch: the async/async*/sync* modifier does 896 // We have a type mismatch: the async/async*/sync* modifier does
579 // not match the return or yield type. We should have already gotten an 897 // not match the return or yield type. We should have already gotten an
580 // analyzer error in this case. 898 // analyzer error in this case.
581 return; 899 return;
582 } 900 }
583 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. 901 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null.
584 if (expression != null) checkAssignment(expression, type); 902 if (expression != null) checkAssignment(expression, type);
585 } 903 }
586 904
587 @override
588 void visitExpressionFunctionBody(ExpressionFunctionBody node) {
589 _checkReturnOrYield(node.expression, node);
590 node.visitChildren(this);
591 }
592
593 @override
594 void visitReturnStatement(ReturnStatement node) {
595 _checkReturnOrYield(node.expression, node);
596 node.visitChildren(this);
597 }
598
599 @override
600 void visitYieldStatement(YieldStatement node) {
601 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null);
602 node.visitChildren(this);
603 }
604
605 @override
606 void visitPropertyAccess(PropertyAccess node) {
607 var target = node.realTarget;
608 if (rules.isDynamicTarget(target) &&
609 !_isObjectProperty(target, node.propertyName)) {
610 _recordDynamicInvoke(node, target);
611 }
612 node.visitChildren(this);
613 }
614
615 @override
616 void visitPrefixedIdentifier(PrefixedIdentifier node) {
617 final target = node.prefix;
618 if (rules.isDynamicTarget(target) &&
619 !_isObjectProperty(target, node.identifier)) {
620 _recordDynamicInvoke(node, target);
621 }
622 node.visitChildren(this);
623 }
624
625 @override
626 void visitDefaultFormalParameter(DefaultFormalParameter node) {
627 // Check that defaults have the proper subtype.
628 var parameter = node.parameter;
629 var parameterType = rules.elementType(parameter.element);
630 assert(parameterType != null);
631 var defaultValue = node.defaultValue;
632 if (defaultValue != null) {
633 checkAssignment(defaultValue, parameterType);
634 }
635
636 node.visitChildren(this);
637 }
638
639 @override
640 void visitFieldFormalParameter(FieldFormalParameter node) {
641 var element = node.element;
642 var typeName = node.type;
643 if (typeName != null) {
644 var type = rules.elementType(element);
645 var fieldElement =
646 node.identifier.staticElement as FieldFormalParameterElement;
647 var fieldType = rules.elementType(fieldElement.field);
648 if (!rules.isSubTypeOf(type, fieldType)) {
649 var staticInfo =
650 new InvalidParameterDeclaration(rules, node, fieldType);
651 _recordMessage(staticInfo);
652 }
653 }
654 node.visitChildren(this);
655 }
656
657 @override
658 void visitInstanceCreationExpression(InstanceCreationExpression node) {
659 var arguments = node.argumentList;
660 var element = node.staticElement;
661 if (element != null) {
662 var type = rules.elementType(node.staticElement);
663 checkArgumentList(arguments, type);
664 }
665 node.visitChildren(this);
666 }
667
668 @override
669 void visitVariableDeclarationList(VariableDeclarationList node) {
670 TypeName type = node.type;
671 if (type == null) {
672 // No checks are needed when the type is var. Although internally the
673 // typing rules may have inferred a more precise type for the variable
674 // based on the initializer.
675 } else {
676 var dartType = getType(type);
677 for (VariableDeclaration variable in node.variables) {
678 var initializer = variable.initializer;
679 if (initializer != null) {
680 checkAssignment(initializer, dartType);
681 }
682 }
683 }
684 node.visitChildren(this);
685 }
686
687 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { 905 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) {
688 var type = getType(typeName); 906 var type = getType(typeName);
689 if (!rules.isGroundType(type)) { 907 if (!rules.isGroundType(type)) {
690 _recordMessage(new NonGroundTypeCheckInfo(node, type)); 908 _recordMessage(node, StrongModeCode.NON_GROUND_TYPE_CHECK_INFO, [type]);
691 } 909 }
692 } 910 }
693 911
694 @override 912 void _checkUnary(Expression operand, Token op, MethodElement element) {
695 void visitAsExpression(AsExpression node) { 913 bool isIncrementAssign =
696 // We could do the same check as the IsExpression below, but that is 914 op.type == TokenType.PLUS_PLUS || op.type == TokenType.MINUS_MINUS;
697 // potentially too conservative. Instead, at runtime, we must fail hard 915 if (op.isUserDefinableOperator || isIncrementAssign) {
698 // if the Dart as and the DDC as would return different values. 916 if (element == null) {
699 node.visitChildren(this); 917 _recordDynamicInvoke(operand.parent, operand);
700 } 918 } else if (isIncrementAssign) {
701 919 // For ++ and --, even if it is not dynamic, we still need to check
702 @override 920 // that the user defined method accepts an `int` as the RHS.
703 void visitIsExpression(IsExpression node) { 921 //
704 _checkRuntimeTypeCheck(node, node.type); 922 // We assume Analyzer has done this already (in ErrorVerifier).
705 node.visitChildren(this); 923 //
706 } 924 // However, we also need to check the return type.
707 925
708 @override 926 // Refine the return type.
709 void visitPrefixExpression(PrefixExpression node) { 927 var functionType = element.type;
710 if (node.operator.type == TokenType.BANG) { 928 var rhsType = typeProvider.intType;
711 checkBoolean(node.operand); 929 var lhsType = _getDefiniteType(operand);
930 var returnType = rules.refineBinaryExpressionType(typeProvider, lhsType,
931 TokenType.PLUS, rhsType, functionType.returnType);
932
933 // Skip the argument check - `int` cannot be downcast.
934 //
935 // Check the return type for an implicit cast.
936 //
937 // If needed, mark the assignment to indicate a down cast when we assign
938 // back to it. So these two implicit casts are equivalent:
939 //
940 // y = /*implicit cast*/(y + 1);
941 // /*implicit assignment cast*/y++;
942 //
943 _checkImplicitCast(operand, lhsType, from: returnType, opAssign: true);
944 }
945 }
946 }
947
948 DartType _getDefiniteType(Expression expr) =>
949 getDefiniteType(expr, rules, typeProvider);
950
951 /// Gets the expected return type of the given function [body], either from
952 /// a normal return/yield, or from a yield*.
953 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) {
954 FunctionType functionType;
955 var parent = body.parent;
956 if (parent is Declaration) {
957 functionType = _elementType(parent.element);
712 } else { 958 } else {
713 _checkUnary(node); 959 assert(parent is FunctionExpression);
714 } 960 functionType =
715 node.visitChildren(this); 961 (parent as FunctionExpression).staticType ?? DynamicTypeImpl.instance;
716 } 962 }
717 963
718 @override 964 var type = functionType.returnType;
719 void visitPostfixExpression(PostfixExpression node) { 965
720 _checkUnary(node); 966 InterfaceType expectedType = null;
721 node.visitChildren(this); 967 if (body.isAsynchronous) {
722 } 968 if (body.isGenerator) {
723 969 // Stream<T> -> T
724 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { 970 expectedType = typeProvider.streamType;
725 var op = node.operator; 971 } else {
726 if (op.isUserDefinableOperator || 972 // Don't validate return type of async methods.
727 op.type == TokenType.PLUS_PLUS || 973 // They're handled by the runtime implementation.
728 op.type == TokenType.MINUS_MINUS) { 974 return null;
729 if (rules.isDynamicTarget(node.operand)) {
730 _recordDynamicInvoke(node, node.operand);
731 }
732 // For ++ and --, even if it is not dynamic, we still need to check
733 // that the user defined method accepts an `int` as the RHS.
734 // We assume Analyzer has done this already.
735 }
736 }
737
738 @override
739 void visitBinaryExpression(BinaryExpression node) {
740 var op = node.operator;
741 if (op.isUserDefinableOperator) {
742 if (rules.isDynamicTarget(node.leftOperand)) {
743 // Dynamic invocation
744 // TODO(vsm): Move this logic to the resolver?
745 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) {
746 _recordDynamicInvoke(node, node.leftOperand);
747 }
748 } else {
749 var element = node.staticElement;
750 // Method invocation.
751 if (element is MethodElement) {
752 var type = element.type;
753 // Analyzer should enforce number of parameter types, but check in
754 // case we have erroneous input.
755 if (type.normalParameterTypes.isNotEmpty) {
756 checkArgument(node.rightOperand, type.normalParameterTypes[0]);
757 }
758 } else {
759 // TODO(vsm): Assert that the analyzer found an error here?
760 }
761 } 975 }
762 } else { 976 } else {
763 // Non-method operator. 977 if (body.isGenerator) {
764 switch (op.type) { 978 // Iterable<T> -> T
765 case TokenType.AMPERSAND_AMPERSAND: 979 expectedType = typeProvider.iterableType;
766 case TokenType.BAR_BAR: 980 } else {
767 checkBoolean(node.leftOperand); 981 // T -> T
768 checkBoolean(node.rightOperand); 982 return type;
769 break; 983 }
770 case TokenType.BANG_EQ: 984 }
771 break; 985 if (yieldStar) {
772 case TokenType.QUESTION_QUESTION: 986 if (type.isDynamic) {
773 break; 987 // Ensure it's at least a Stream / Iterable.
774 default: 988 return expectedType.instantiate([typeProvider.dynamicType]);
775 assert(false); 989 } else {
776 } 990 // Analyzer will provide a separate error if expected type
777 } 991 // is not compatible with type.
778 node.visitChildren(this); 992 return type;
779 } 993 }
780 994 }
781 @override 995 if (type.isDynamic) {
782 void visitConditionalExpression(ConditionalExpression node) { 996 return type;
783 checkBoolean(node.condition); 997 } else if (type is InterfaceType && type.element == expectedType.element) {
784 node.visitChildren(this); 998 return type.typeArguments[0];
785 }
786
787 @override
788 void visitIndexExpression(IndexExpression node) {
789 var target = node.realTarget;
790 if (rules.isDynamicTarget(target)) {
791 _recordDynamicInvoke(node, target);
792 } else { 999 } else {
793 var element = node.staticElement; 1000 // Malformed type - fallback on analyzer error.
794 if (element is MethodElement) { 1001 return null;
795 var type = element.type; 1002 }
796 // Analyzer should enforce number of parameter types, but check in 1003 }
797 // case we have erroneous input. 1004
798 if (type.normalParameterTypes.isNotEmpty) { 1005 /// Given an expression, return its type assuming it is
799 checkArgument(node.index, type.normalParameterTypes[0]); 1006 /// in the caller position of a call (that is, accounting
800 } 1007 /// for the possibility of a call method). Returns null
801 } else { 1008 /// if expression is not statically callable.
802 // TODO(vsm): Assert that the analyzer found an error here? 1009 FunctionType _getTypeAsCaller(InvocationExpression node) {
803 } 1010 DartType type = node.staticInvokeType;
804 } 1011 if (type is FunctionType) {
805 node.visitChildren(this); 1012 return type;
806 } 1013 } else if (type is InterfaceType) {
807 1014 return rules.getCallMethodType(type);
808 DartType getType(TypeName name) { 1015 }
809 return (name == null) ? rules.provider.dynamicType : name.type; 1016 return null;
810 } 1017 }
811 1018
812 /// Analyzer checks boolean conversions, but we need to check too, because 1019 /// Returns `true` if the expression is a dynamic function call or method
813 /// it uses the default assignability rules that allow `dynamic` and `Object` 1020 /// invocation.
814 /// to be assigned to bool with no message. 1021 bool _isDynamicCall(InvocationExpression call, FunctionType ft) {
815 void checkBoolean(Expression expr) => 1022 // TODO(leafp): This will currently return true if t is Function
816 checkAssignment(expr, rules.provider.boolType); 1023 // This is probably the most correct thing to do for now, since
817 1024 // this code is also used by the back end. Maybe revisit at some
818 void checkAssignment(Expression expr, DartType type) { 1025 // point?
819 if (expr is ParenthesizedExpression) { 1026 if (ft == null) return true;
820 checkAssignment(expr.expression, type); 1027 // Dynamic as the parameter type is treated as bottom. A function with
821 } else { 1028 // a dynamic parameter type requires a dynamic call in general.
822 _recordMessage(rules.checkAssignment(expr, type)); 1029 // However, as an optimization, if we have an original definition, we know
823 } 1030 // dynamic is reified as Object - in this case a regular call is fine.
824 } 1031 if (_hasStrictArrow(call.function)) {
825 1032 return false;
826 DartType _specializedBinaryReturnType( 1033 }
827 TokenType op, DartType t1, DartType t2, DartType normalReturnType) { 1034 return rules.anyParameterType(ft, (pt) => pt.isDynamic);
828 // This special cases binary return types as per 16.26 and 16.27 of the 1035 }
829 // Dart language spec. 1036
830 switch (op) { 1037 void _recordDynamicInvoke(AstNode node, Expression target) {
831 case TokenType.PLUS: 1038 _recordMessage(node, StrongModeCode.DYNAMIC_INVOKE, [node]);
832 case TokenType.MINUS:
833 case TokenType.STAR:
834 case TokenType.TILDE_SLASH:
835 case TokenType.PERCENT:
836 case TokenType.PLUS_EQ:
837 case TokenType.MINUS_EQ:
838 case TokenType.STAR_EQ:
839 case TokenType.TILDE_SLASH_EQ:
840 case TokenType.PERCENT_EQ:
841 if (t1 == rules.provider.intType &&
842 t2 == rules.provider.intType) return t1;
843 if (t1 == rules.provider.doubleType &&
844 t2 == rules.provider.doubleType) return t1;
845 // This particular combo is not spelled out in the spec, but all
846 // implementations and analyzer seem to follow this.
847 if (t1 == rules.provider.doubleType &&
848 t2 == rules.provider.intType) return t1;
849 }
850 return normalReturnType;
851 }
852
853 void _checkCompoundAssignment(AssignmentExpression expr) {
854 var op = expr.operator.type;
855 assert(op.isAssignmentOperator && op != TokenType.EQ);
856 var methodElement = expr.staticElement;
857 if (methodElement == null) {
858 // Dynamic invocation
859 _recordDynamicInvoke(expr, expr.leftHandSide);
860 } else {
861 // Sanity check the operator
862 assert(methodElement.isOperator);
863 var functionType = methodElement.type;
864 var paramTypes = functionType.normalParameterTypes;
865 assert(paramTypes.length == 1);
866 assert(functionType.namedParameterTypes.isEmpty);
867 assert(functionType.optionalParameterTypes.isEmpty);
868
869 // Check the lhs type
870 var staticInfo;
871 var rhsType = _getStaticType(expr.rightHandSide);
872 var lhsType = _getStaticType(expr.leftHandSide);
873 var returnType = _specializedBinaryReturnType(
874 op, lhsType, rhsType, functionType.returnType);
875
876 if (!rules.isSubTypeOf(returnType, lhsType)) {
877 final numType = rules.provider.numType;
878 // Try to fix up the numerical case if possible.
879 if (rules.isSubTypeOf(lhsType, numType) &&
880 rules.isSubTypeOf(lhsType, rhsType)) {
881 // This is also slightly different from spec, but allows us to keep
882 // compound operators in the int += num and num += dynamic cases.
883 staticInfo = DownCast.create(
884 rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType));
885 rhsType = lhsType;
886 } else {
887 // Static type error
888 staticInfo = new StaticTypeError(rules, expr, lhsType);
889 }
890 _recordMessage(staticInfo);
891 }
892
893 // Check the rhs type
894 if (staticInfo is! CoercionInfo) {
895 var paramType = paramTypes.first;
896 staticInfo = rules.checkAssignment(expr.rightHandSide, paramType);
897 _recordMessage(staticInfo);
898 }
899 }
900 }
901
902 bool _isObjectGetter(Expression target, SimpleIdentifier id) {
903 PropertyAccessorElement element =
904 rules.provider.objectType.element.getGetter(id.name);
905 return (element != null && !element.isStatic);
906 }
907
908 bool _isObjectMethod(Expression target, SimpleIdentifier id) {
909 MethodElement element =
910 rules.provider.objectType.element.getMethod(id.name);
911 return (element != null && !element.isStatic);
912 }
913
914 bool _isObjectProperty(Expression target, SimpleIdentifier id) {
915 return _isObjectGetter(target, id) || _isObjectMethod(target, id);
916 }
917
918 DartType _getStaticType(Expression expr) {
919 return expr.staticType ?? rules.provider.dynamicType;
920 }
921
922 void _recordDynamicInvoke(AstNode node, AstNode target) {
923 if (_hints) {
924 reporter.onError(new DynamicInvoke(rules, node).toAnalysisError());
925 }
926 // TODO(jmesserly): we may eventually want to record if the whole operation 1039 // TODO(jmesserly): we may eventually want to record if the whole operation
927 // (node) was dynamic, rather than the target, but this is an easier fit 1040 // (node) was dynamic, rather than the target, but this is an easier fit
928 // with what we used to do. 1041 // with what we used to do.
929 DynamicInvoke.set(target, true); 1042 if (target != null) setIsDynamicInvoke(target, true);
930 } 1043 }
931 1044
932 void _recordMessage(StaticInfo info) { 1045 /// Records an implicit cast for the [expr] from [from] to [to].
933 if (info == null) return; 1046 ///
934 var error = info.toAnalysisError(); 1047 /// This will emit the appropriate error/warning/hint message as well as mark
935 1048 /// the AST node.
936 var severity = error.errorCode.errorSeverity; 1049 void _recordImplicitCast(Expression expr, DartType to,
1050 {DartType from, bool opAssign: false}) {
1051 assert(rules.isSubtypeOf(to, from));
1052
1053 // Inference "casts":
1054 if (expr is Literal || expr is FunctionExpression) {
1055 // fromT should be an exact type - this will almost certainly fail at
1056 // runtime.
1057 _recordMessage(expr, StrongModeCode.STATIC_TYPE_ERROR, [expr, from, to]);
1058 return;
1059 }
1060
1061 if (expr is InstanceCreationExpression) {
1062 ConstructorElement e = expr.staticElement;
1063 if (e == null || !e.isFactory) {
1064 // fromT should be an exact type - this will almost certainly fail at
1065 // runtime.
1066
1067 _recordMessage(
1068 expr, StrongModeCode.STATIC_TYPE_ERROR, [expr, from, to]);
1069 return;
1070 }
1071 }
1072
1073 if (isKnownFunction(expr)) {
1074 _recordMessage(expr, StrongModeCode.STATIC_TYPE_ERROR, [expr, from, to]);
1075 return;
1076 }
1077
1078 // Composite cast: these are more likely to fail.
1079 bool downCastComposite = false;
1080 if (!rules.isGroundType(to)) {
1081 // This cast is (probably) due to our different treatment of dynamic.
1082 // It may be more likely to fail at runtime.
1083 if (from is InterfaceType) {
1084 // For class types, we'd like to allow non-generic down casts, e.g.,
1085 // Iterable<T> to List<T>. The intuition here is that raw (generic)
1086 // casts are problematic, and we should complain about those.
1087 var typeArgs = from.typeArguments;
1088 downCastComposite =
1089 typeArgs.isEmpty || typeArgs.any((t) => t.isDynamic);
1090 } else {
1091 downCastComposite = true;
1092 }
1093 }
1094
1095 var parent = expr.parent;
1096 ErrorCode errorCode;
1097 if (downCastComposite) {
1098 errorCode = StrongModeCode.DOWN_CAST_COMPOSITE;
1099 } else if (from.isDynamic) {
1100 errorCode = StrongModeCode.DYNAMIC_CAST;
1101 } else if (parent is VariableDeclaration && parent.initializer == expr) {
1102 errorCode = StrongModeCode.ASSIGNMENT_CAST;
1103 } else {
1104 errorCode = opAssign
1105 ? StrongModeCode.DOWN_CAST_IMPLICIT_ASSIGN
1106 : StrongModeCode.DOWN_CAST_IMPLICIT;
1107 }
1108 _recordMessage(expr, errorCode, [from, to]);
1109 if (opAssign) {
1110 setImplicitAssignmentCast(expr, to);
1111 } else {
1112 setImplicitCast(expr, to);
1113 }
1114 _hasImplicitCasts = true;
1115 }
1116
1117 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) {
1118 var severity = errorCode.errorSeverity;
937 if (severity == ErrorSeverity.ERROR) _failure = true; 1119 if (severity == ErrorSeverity.ERROR) _failure = true;
938 if (severity != ErrorSeverity.INFO || _hints) { 1120 if (severity != ErrorSeverity.INFO || _options.strongModeHints) {
1121 int begin = node is AnnotatedNode
1122 ? node.firstTokenAfterCommentAndMetadata.offset
1123 : node.offset;
1124 int length = node.end - begin;
1125 var source = (node.root as CompilationUnit).element.source;
1126 var error =
1127 new AnalysisError(source, begin, length, errorCode, arguments);
939 reporter.onError(error); 1128 reporter.onError(error);
940 } 1129 }
941
942 if (info is CoercionInfo) {
943 // TODO(jmesserly): if we're run again on the same AST, we'll produce the
944 // same annotations. This should be harmless. This might go away once
945 // CodeChecker is integrated better with analyzer, as it will know that
946 // checking has already been performed.
947 // assert(CoercionInfo.get(info.node) == null);
948 CoercionInfo.set(info.node, info);
949 }
950 } 1130 }
951 } 1131 }
952 1132
953 /// Looks up the declaration that matches [member] in [type] and returns it's 1133 /// Checks for overriding declarations of fields and methods. This is used to
954 /// declared type. 1134 /// check overrides between classes and superclasses, interfaces, and mixin
955 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => 1135 /// applications.
956 _memberTypeGetter(member)(type); 1136 class _OverrideChecker {
957 1137 final StrongTypeSystemImpl rules;
958 typedef FunctionType _MemberTypeGetter(InterfaceType type); 1138 final CodeChecker _checker;
959 1139
960 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) { 1140 _OverrideChecker(CodeChecker checker)
961 String memberName = member.name; 1141 : _checker = checker,
962 final isGetter = member is PropertyAccessorElement && member.isGetter; 1142 rules = checker.rules;
963 final isSetter = member is PropertyAccessorElement && member.isSetter; 1143
964 1144 void check(ClassDeclaration node) {
965 FunctionType f(InterfaceType type) { 1145 if (node.element.type.isObject) return;
966 ExecutableElement baseMethod; 1146 _checkSuperOverrides(node);
967 try { 1147 _checkMixinApplicationOverrides(node);
968 if (isGetter) { 1148 _checkAllInterfaceOverrides(node);
969 assert(!isSetter); 1149 }
970 // Look for getter or field. 1150
971 baseMethod = type.getGetter(memberName); 1151 /// Checks that implementations correctly override all reachable interfaces.
972 } else if (isSetter) { 1152 /// In particular, we need to check these overrides for the definitions in
973 baseMethod = type.getSetter(memberName); 1153 /// the class itself and each its superclasses. If a superclass is not
974 } else { 1154 /// abstract, then we can skip its transitive interfaces. For example, in:
975 baseMethod = type.getMethod(memberName); 1155 ///
976 } 1156 /// B extends C implements G
977 } catch (e) { 1157 /// A extends B with E, F implements H, I
978 // TODO(sigmund): remove this try-catch block (see issue #48). 1158 ///
979 } 1159 /// we check:
980 if (baseMethod == null || baseMethod.isStatic) return null; 1160 ///
981 return baseMethod.type; 1161 /// C against G, H, and I
982 } 1162 /// B against G, H, and I
983 ; 1163 /// E against H and I // no check against G because B is a concrete class
984 return f; 1164 /// F against H and I
1165 /// A against H and I
1166 void _checkAllInterfaceOverrides(ClassDeclaration node) {
1167 var seen = new Set<String>();
1168 // Helper function to collect all reachable interfaces.
1169 find(InterfaceType interfaceType, Set result) {
1170 if (interfaceType == null || interfaceType.isObject) return;
1171 if (result.contains(interfaceType)) return;
1172 result.add(interfaceType);
1173 find(interfaceType.superclass, result);
1174 interfaceType.mixins.forEach((i) => find(i, result));
1175 interfaceType.interfaces.forEach((i) => find(i, result));
1176 }
1177
1178 // Check all interfaces reachable from the `implements` clause in the
1179 // current class against definitions here and in superclasses.
1180 var localInterfaces = new Set<InterfaceType>();
1181 var type = node.element.type;
1182 type.interfaces.forEach((i) => find(i, localInterfaces));
1183 _checkInterfacesOverrides(node, localInterfaces, seen,
1184 includeParents: true);
1185
1186 // Check also how we override locally the interfaces from parent classes if
1187 // the parent class is abstract. Otherwise, these will be checked as
1188 // overrides on the concrete superclass.
1189 var superInterfaces = new Set<InterfaceType>();
1190 var parent = type.superclass;
1191 // TODO(sigmund): we don't seem to be reporting the analyzer error that a
1192 // non-abstract class is not implementing an interface. See
1193 // https://github.com/dart-lang/dart-dev-compiler/issues/25
1194 while (parent != null && parent.element.isAbstract) {
1195 parent.interfaces.forEach((i) => find(i, superInterfaces));
1196 parent = parent.superclass;
1197 }
1198 _checkInterfacesOverrides(node, superInterfaces, seen,
1199 includeParents: false);
1200 }
1201
1202 /// Check that individual methods and fields in [node] correctly override
1203 /// the declarations in [baseType].
1204 ///
1205 /// The [errorLocation] node indicates where errors are reported, see
1206 /// [_checkSingleOverride] for more details.
1207 _checkIndividualOverridesFromClass(ClassDeclaration node,
1208 InterfaceType baseType, Set<String> seen, bool isSubclass) {
1209 for (var member in node.members) {
1210 if (member is FieldDeclaration) {
1211 if (member.isStatic) {
1212 continue;
1213 }
1214 for (var variable in member.fields.variables) {
1215 var element = variable.element as PropertyInducingElement;
1216 var name = element.name;
1217 if (seen.contains(name)) {
1218 continue;
1219 }
1220 var getter = element.getter;
1221 var setter = element.setter;
1222 bool found = _checkSingleOverride(
1223 getter, baseType, variable.name, member, isSubclass);
1224 if (!variable.isFinal &&
1225 !variable.isConst &&
1226 _checkSingleOverride(
1227 setter, baseType, variable.name, member, isSubclass)) {
1228 found = true;
1229 }
1230 if (found) {
1231 seen.add(name);
1232 }
1233 }
1234 } else if (member is MethodDeclaration) {
1235 if (member.isStatic) {
1236 continue;
1237 }
1238 var method = member.element;
1239 if (seen.contains(method.name)) {
1240 continue;
1241 }
1242 if (_checkSingleOverride(
1243 method, baseType, member.name, member, isSubclass)) {
1244 seen.add(method.name);
1245 }
1246 } else {
1247 assert(member is ConstructorDeclaration);
1248 }
1249 }
1250 }
1251
1252 /// Check that individual methods and fields in [subType] correctly override
1253 /// the declarations in [baseType].
1254 ///
1255 /// The [errorLocation] node indicates where errors are reported, see
1256 /// [_checkSingleOverride] for more details.
1257 ///
1258 /// The set [seen] is used to avoid reporting overrides more than once. It
1259 /// is used when invoking this function multiple times when checking several
1260 /// types in a class hierarchy. Errors are reported only the first time an
1261 /// invalid override involving a specific member is encountered.
1262 _checkIndividualOverridesFromType(
1263 InterfaceType subType,
1264 InterfaceType baseType,
1265 AstNode errorLocation,
1266 Set<String> seen,
1267 bool isSubclass) {
1268 void checkHelper(ExecutableElement e) {
1269 if (e.isStatic) return;
1270 if (seen.contains(e.name)) return;
1271 if (_checkSingleOverride(e, baseType, null, errorLocation, isSubclass)) {
1272 seen.add(e.name);
1273 }
1274 }
1275
1276 subType.methods.forEach(checkHelper);
1277 subType.accessors.forEach(checkHelper);
1278 }
1279
1280 /// Checks that [cls] and its super classes (including mixins) correctly
1281 /// overrides each interface in [interfaces]. If [includeParents] is false,
1282 /// then mixins are still checked, but the base type and it's transitive
1283 /// supertypes are not.
1284 ///
1285 /// [cls] can be either a [ClassDeclaration] or a [InterfaceType]. For
1286 /// [ClassDeclaration]s errors are reported on the member that contains the
1287 /// invalid override, for [InterfaceType]s we use [errorLocation] instead.
1288 void _checkInterfacesOverrides(
1289 cls, Iterable<InterfaceType> interfaces, Set<String> seen,
1290 {Set<InterfaceType> visited,
1291 bool includeParents: true,
1292 AstNode errorLocation}) {
1293 var node = cls is ClassDeclaration ? cls : null;
1294 var type = cls is InterfaceType ? cls : node.element.type;
1295
1296 if (visited == null) {
1297 visited = new Set<InterfaceType>();
1298 } else if (visited.contains(type)) {
1299 // Malformed type.
1300 return;
1301 } else {
1302 visited.add(type);
1303 }
1304
1305 // Check direct overrides on [type]
1306 for (var interfaceType in interfaces) {
1307 if (node != null) {
1308 _checkIndividualOverridesFromClass(node, interfaceType, seen, false);
1309 } else {
1310 _checkIndividualOverridesFromType(
1311 type, interfaceType, errorLocation, seen, false);
1312 }
1313 }
1314
1315 // Check overrides from its mixins
1316 for (int i = 0; i < type.mixins.length; i++) {
1317 var loc = errorLocation ?? node.withClause.mixinTypes[i];
1318 for (var interfaceType in interfaces) {
1319 // We copy [seen] so we can report separately if more than one mixin or
1320 // the base class have an invalid override.
1321 _checkIndividualOverridesFromType(
1322 type.mixins[i], interfaceType, loc, new Set.from(seen), false);
1323 }
1324 }
1325
1326 // Check overrides from its superclasses
1327 if (includeParents) {
1328 var parent = type.superclass;
1329 if (parent.isObject) {
1330 return;
1331 }
1332 var loc = errorLocation ?? node.extendsClause;
1333 // No need to copy [seen] here because we made copies above when reporting
1334 // errors on mixins.
1335 _checkInterfacesOverrides(parent, interfaces, seen,
1336 visited: visited, includeParents: true, errorLocation: loc);
1337 }
1338 }
1339
1340 /// Check overrides from mixin applications themselves. For example, in:
1341 ///
1342 /// A extends B with E, F
1343 ///
1344 /// we check:
1345 ///
1346 /// B & E against B (equivalently how E overrides B)
1347 /// B & E & F against B & E (equivalently how F overrides both B and E)
1348 void _checkMixinApplicationOverrides(ClassDeclaration node) {
1349 var type = node.element.type;
1350 var parent = type.superclass;
1351 var mixins = type.mixins;
1352
1353 // Check overrides from applying mixins
1354 for (int i = 0; i < mixins.length; i++) {
1355 var seen = new Set<String>();
1356 var current = mixins[i];
1357 var errorLocation = node.withClause.mixinTypes[i];
1358 for (int j = i - 1; j >= 0; j--) {
1359 _checkIndividualOverridesFromType(
1360 current, mixins[j], errorLocation, seen, true);
1361 }
1362 _checkIndividualOverridesFromType(
1363 current, parent, errorLocation, seen, true);
1364 }
1365 }
1366
1367 /// Checks that [element] correctly overrides its corresponding member in
1368 /// [type]. Returns `true` if an override was found, that is, if [element] has
1369 /// a corresponding member in [type] that it overrides.
1370 ///
1371 /// The [errorLocation] is a node where the error is reported. For example, a
1372 /// bad override of a method in a class with respect to its superclass is
1373 /// reported directly at the method declaration. However, invalid overrides
1374 /// from base classes to interfaces, mixins to the base they are applied to,
1375 /// or mixins to interfaces are reported at the class declaration, since the
1376 /// base class or members on their own were not incorrect, only combining them
1377 /// with the interface was problematic. For example, these are example error
1378 /// locations in these cases:
1379 ///
1380 /// error: base class introduces an invalid override. The type of B.foo is
1381 /// not a subtype of E.foo:
1382 /// class A extends B implements E { ... }
1383 /// ^^^^^^^^^
1384 ///
1385 /// error: mixin introduces an invalid override. The type of C.foo is not
1386 /// a subtype of E.foo:
1387 /// class A extends B with C implements E { ... }
1388 /// ^
1389 ///
1390 /// When checking for overrides from a type and it's super types, [node] is
1391 /// the AST node that defines [element]. This is used to determine whether the
1392 /// type of the element could be inferred from the types in the super classes.
1393 bool _checkSingleOverride(ExecutableElement element, InterfaceType type,
1394 AstNode node, AstNode errorLocation, bool isSubclass) {
1395 assert(!element.isStatic);
1396
1397 FunctionType subType = _elementType(element);
1398 // TODO(vsm): Test for generic
1399 FunctionType baseType = _getMemberType(type, element);
1400 if (baseType == null) return false;
1401
1402 if (isSubclass && element is PropertyAccessorElement) {
1403 // Disallow any overriding if the base class defines this member
1404 // as a field. We effectively treat fields as final / non-virtual,
1405 // unless they are explicitly marked as @virtual
1406 var field = _getMemberField(type, element);
1407 if (field != null && !field.isVirtual) {
1408 _checker._recordMessage(
1409 errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [
1410 element.enclosingElement.name,
1411 element.name,
1412 subType,
1413 type,
1414 baseType
1415 ]);
1416 }
1417 }
1418 FunctionType concreteSubType = subType;
1419 FunctionType concreteBaseType = baseType;
1420 if (concreteSubType.typeFormals.isNotEmpty) {
1421 if (concreteBaseType.typeFormals.isEmpty) {
1422 concreteSubType = rules.instantiateToBounds(concreteSubType);
1423 }
1424 }
1425
1426 if (!rules.isOverrideSubtypeOf(concreteSubType, concreteBaseType)) {
1427 ErrorCode errorCode;
1428 if (errorLocation is ExtendsClause) {
1429 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE;
1430 } else if (errorLocation.parent is WithClause) {
1431 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN;
1432 } else {
1433 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE;
1434 }
1435
1436 _checker._recordMessage(errorLocation, errorCode, [
1437 element.enclosingElement.name,
1438 element.name,
1439 subType,
1440 type,
1441 baseType
1442 ]);
1443 }
1444
1445 // If we have any covariant parameters and we're comparing against a
1446 // superclass, we check all superclasses instead of stopping the search.
1447 bool hasCovariant = element.parameters.any((p) => p.isCovariant);
1448 bool keepSearching = hasCovariant && isSubclass;
1449 return !keepSearching;
1450 }
1451
1452 /// Check overrides between a class and its superclasses and mixins. For
1453 /// example, in:
1454 ///
1455 /// A extends B with E, F
1456 ///
1457 /// we check A against B, B super classes, E, and F.
1458 ///
1459 /// Internally we avoid reporting errors twice and we visit classes bottom up
1460 /// to ensure we report the most immediate invalid override first. For
1461 /// example, in the following code we'll report that `Test` has an invalid
1462 /// override with respect to `Parent` (as opposed to an invalid override with
1463 /// respect to `Grandparent`):
1464 ///
1465 /// class Grandparent {
1466 /// m(A a) {}
1467 /// }
1468 /// class Parent extends Grandparent {
1469 /// m(A a) {}
1470 /// }
1471 /// class Test extends Parent {
1472 /// m(B a) {} // invalid override
1473 /// }
1474 void _checkSuperOverrides(ClassDeclaration node) {
1475 var seen = new Set<String>();
1476 var current = node.element.type;
1477 var visited = new Set<InterfaceType>();
1478 do {
1479 visited.add(current);
1480 current.mixins.reversed.forEach(
1481 (m) => _checkIndividualOverridesFromClass(node, m, seen, true));
1482 _checkIndividualOverridesFromClass(node, current.superclass, seen, true);
1483 current = current.superclass;
1484 } while (!current.isObject && !visited.contains(current));
1485 }
985 } 1486 }
OLDNEW
« no previous file with comments | « packages/analyzer/lib/src/task/strong/ast_properties.dart ('k') | packages/analyzer/lib/src/task/strong/info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698