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

Side by Side Diff: lib/src/checker/checker.dart

Issue 1401273002: Move DDC to analyzer-based checker (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: rebase Created 5 years, 2 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
« no previous file with comments | « no previous file | lib/src/checker/resolver.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 library dev_compiler.src.checker.checker; 5 library dev_compiler.src.checker.checker;
6 6
7 import 'package:analyzer/analyzer.dart'; 7 export 'package:analyzer/src/task/strong/checker.dart';
8 import 'package:analyzer/src/generated/ast.dart';
9 import 'package:analyzer/src/generated/element.dart';
10 import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType;
11
12 import '../info.dart';
13 import '../utils.dart' show getMemberType;
14 import 'rules.dart';
15
16 /// Checks for overriding declarations of fields and methods. This is used to
17 /// check overrides between classes and superclasses, interfaces, and mixin
18 /// applications.
19 class _OverrideChecker {
20 bool _failure = false;
21 final TypeRules _rules;
22 final AnalysisErrorListener _reporter;
23
24 _OverrideChecker(this._rules, this._reporter);
25
26 void check(ClassDeclaration node) {
27 if (node.element.type.isObject) return;
28 _checkSuperOverrides(node);
29 _checkMixinApplicationOverrides(node);
30 _checkAllInterfaceOverrides(node);
31 }
32
33 /// Check overrides from mixin applications themselves. For example, in:
34 ///
35 /// A extends B with E, F
36 ///
37 /// we check:
38 ///
39 /// B & E against B (equivalently how E overrides B)
40 /// B & E & F against B & E (equivalently how F overrides both B and E)
41 void _checkMixinApplicationOverrides(ClassDeclaration node) {
42 var type = node.element.type;
43 var parent = type.superclass;
44 var mixins = type.mixins;
45
46 // Check overrides from applying mixins
47 for (int i = 0; i < mixins.length; i++) {
48 var seen = new Set<String>();
49 var current = mixins[i];
50 var errorLocation = node.withClause.mixinTypes[i];
51 for (int j = i - 1; j >= 0; j--) {
52 _checkIndividualOverridesFromType(
53 current, mixins[j], errorLocation, seen);
54 }
55 _checkIndividualOverridesFromType(current, parent, errorLocation, seen);
56 }
57 }
58
59 /// Check overrides between a class and its superclasses and mixins. For
60 /// example, in:
61 ///
62 /// A extends B with E, F
63 ///
64 /// we check A against B, B super classes, E, and F.
65 ///
66 /// Internally we avoid reporting errors twice and we visit classes bottom up
67 /// to ensure we report the most immediate invalid override first. For
68 /// example, in the following code we'll report that `Test` has an invalid
69 /// override with respect to `Parent` (as opposed to an invalid override with
70 /// respect to `Grandparent`):
71 ///
72 /// class Grandparent {
73 /// m(A a) {}
74 /// }
75 /// class Parent extends Grandparent {
76 /// m(A a) {}
77 /// }
78 /// class Test extends Parent {
79 /// m(B a) {} // invalid override
80 /// }
81 void _checkSuperOverrides(ClassDeclaration node) {
82 var seen = new Set<String>();
83 var current = node.element.type;
84 var visited = new Set<InterfaceType>();
85 do {
86 visited.add(current);
87 current.mixins.reversed
88 .forEach((m) => _checkIndividualOverridesFromClass(node, m, seen));
89 _checkIndividualOverridesFromClass(node, current.superclass, seen);
90 current = current.superclass;
91 } while (!current.isObject && !visited.contains(current));
92 }
93
94 /// Checks that implementations correctly override all reachable interfaces.
95 /// In particular, we need to check these overrides for the definitions in
96 /// the class itself and each its superclasses. If a superclass is not
97 /// abstract, then we can skip its transitive interfaces. For example, in:
98 ///
99 /// B extends C implements G
100 /// A extends B with E, F implements H, I
101 ///
102 /// we check:
103 ///
104 /// C against G, H, and I
105 /// B against G, H, and I
106 /// E against H and I // no check against G because B is a concrete class
107 /// F against H and I
108 /// A against H and I
109 void _checkAllInterfaceOverrides(ClassDeclaration node) {
110 var seen = new Set<String>();
111 // Helper function to collect all reachable interfaces.
112 find(InterfaceType interfaceType, Set result) {
113 if (interfaceType == null || interfaceType.isObject) return;
114 if (result.contains(interfaceType)) return;
115 result.add(interfaceType);
116 find(interfaceType.superclass, result);
117 interfaceType.mixins.forEach((i) => find(i, result));
118 interfaceType.interfaces.forEach((i) => find(i, result));
119 }
120
121 // Check all interfaces reachable from the `implements` clause in the
122 // current class against definitions here and in superclasses.
123 var localInterfaces = new Set<InterfaceType>();
124 var type = node.element.type;
125 type.interfaces.forEach((i) => find(i, localInterfaces));
126 _checkInterfacesOverrides(node, localInterfaces, seen,
127 includeParents: true);
128
129 // Check also how we override locally the interfaces from parent classes if
130 // the parent class is abstract. Otherwise, these will be checked as
131 // overrides on the concrete superclass.
132 var superInterfaces = new Set<InterfaceType>();
133 var parent = type.superclass;
134 // TODO(sigmund): we don't seem to be reporting the analyzer error that a
135 // non-abstract class is not implementing an interface. See
136 // https://github.com/dart-lang/dart-dev-compiler/issues/25
137 while (parent != null && parent.element.isAbstract) {
138 parent.interfaces.forEach((i) => find(i, superInterfaces));
139 parent = parent.superclass;
140 }
141 _checkInterfacesOverrides(node, superInterfaces, seen,
142 includeParents: false);
143 }
144
145 /// Checks that [cls] and its super classes (including mixins) correctly
146 /// overrides each interface in [interfaces]. If [includeParents] is false,
147 /// then mixins are still checked, but the base type and it's transitive
148 /// supertypes are not.
149 ///
150 /// [cls] can be either a [ClassDeclaration] or a [InterfaceType]. For
151 /// [ClassDeclaration]s errors are reported on the member that contains the
152 /// invalid override, for [InterfaceType]s we use [errorLocation] instead.
153 void _checkInterfacesOverrides(
154 cls, Iterable<InterfaceType> interfaces, Set<String> seen,
155 {Set<InterfaceType> visited,
156 bool includeParents: true,
157 AstNode errorLocation}) {
158 var node = cls is ClassDeclaration ? cls : null;
159 var type = cls is InterfaceType ? cls : node.element.type;
160
161 if (visited == null) {
162 visited = new Set<InterfaceType>();
163 } else if (visited.contains(type)) {
164 // Malformed type.
165 return;
166 } else {
167 visited.add(type);
168 }
169
170 // Check direct overrides on [type]
171 for (var interfaceType in interfaces) {
172 if (node != null) {
173 _checkIndividualOverridesFromClass(node, interfaceType, seen);
174 } else {
175 _checkIndividualOverridesFromType(
176 type, interfaceType, errorLocation, seen);
177 }
178 }
179
180 // Check overrides from its mixins
181 for (int i = 0; i < type.mixins.length; i++) {
182 var loc =
183 errorLocation != null ? errorLocation : node.withClause.mixinTypes[i];
184 for (var interfaceType in interfaces) {
185 // We copy [seen] so we can report separately if more than one mixin or
186 // the base class have an invalid override.
187 _checkIndividualOverridesFromType(
188 type.mixins[i], interfaceType, loc, new Set.from(seen));
189 }
190 }
191
192 // Check overrides from its superclasses
193 if (includeParents) {
194 var parent = type.superclass;
195 if (parent.isObject) return;
196 var loc = errorLocation != null ? errorLocation : node.extendsClause;
197 // No need to copy [seen] here because we made copies above when reporting
198 // errors on mixins.
199 _checkInterfacesOverrides(parent, interfaces, seen,
200 visited: visited, includeParents: true, errorLocation: loc);
201 }
202 }
203
204 /// Check that individual methods and fields in [subType] correctly override
205 /// the declarations in [baseType].
206 ///
207 /// The [errorLocation] node indicates where errors are reported, see
208 /// [_checkSingleOverride] for more details.
209 ///
210 /// The set [seen] is used to avoid reporting overrides more than once. It
211 /// is used when invoking this function multiple times when checking several
212 /// types in a class hierarchy. Errors are reported only the first time an
213 /// invalid override involving a specific member is encountered.
214 _checkIndividualOverridesFromType(InterfaceType subType,
215 InterfaceType baseType, AstNode errorLocation, Set<String> seen) {
216 void checkHelper(ExecutableElement e) {
217 if (e.isStatic) return;
218 if (seen.contains(e.name)) return;
219 if (_checkSingleOverride(e, baseType, null, errorLocation)) {
220 seen.add(e.name);
221 }
222 }
223 subType.methods.forEach(checkHelper);
224 subType.accessors.forEach(checkHelper);
225 }
226
227 /// Check that individual methods and fields in [subType] correctly override
228 /// the declarations in [baseType].
229 ///
230 /// The [errorLocation] node indicates where errors are reported, see
231 /// [_checkSingleOverride] for more details.
232 _checkIndividualOverridesFromClass(
233 ClassDeclaration node, InterfaceType baseType, Set<String> seen) {
234 for (var member in node.members) {
235 if (member is ConstructorDeclaration) continue;
236 if (member is FieldDeclaration) {
237 if (member.isStatic) continue;
238 for (var variable in member.fields.variables) {
239 var element = variable.element as PropertyInducingElement;
240 var name = element.name;
241 if (seen.contains(name)) continue;
242 var getter = element.getter;
243 var setter = element.setter;
244 bool found = _checkSingleOverride(getter, baseType, variable, member);
245 if (!variable.isFinal &&
246 !variable.isConst &&
247 _checkSingleOverride(setter, baseType, variable, member)) {
248 found = true;
249 }
250 if (found) seen.add(name);
251 }
252 } else {
253 if ((member as MethodDeclaration).isStatic) continue;
254 var method = (member as MethodDeclaration).element;
255 if (seen.contains(method.name)) continue;
256 if (_checkSingleOverride(method, baseType, member, member)) {
257 seen.add(method.name);
258 }
259 }
260 }
261 }
262
263 /// Checks that [element] correctly overrides its corresponding member in
264 /// [type]. Returns `true` if an override was found, that is, if [element] has
265 /// a corresponding member in [type] that it overrides.
266 ///
267 /// The [errorLocation] is a node where the error is reported. For example, a
268 /// bad override of a method in a class with respect to its superclass is
269 /// reported directly at the method declaration. However, invalid overrides
270 /// from base classes to interfaces, mixins to the base they are applied to,
271 /// or mixins to interfaces are reported at the class declaration, since the
272 /// base class or members on their own were not incorrect, only combining them
273 /// with the interface was problematic. For example, these are example error
274 /// locations in these cases:
275 ///
276 /// error: base class introduces an invalid override. The type of B.foo is
277 /// not a subtype of E.foo:
278 /// class A extends B implements E { ... }
279 /// ^^^^^^^^^
280 ///
281 /// error: mixin introduces an invalid override. The type of C.foo is not
282 /// a subtype of E.foo:
283 /// class A extends B with C implements E { ... }
284 /// ^
285 ///
286 /// When checking for overrides from a type and it's super types, [node] is
287 /// the AST node that defines [element]. This is used to determine whether the
288 /// type of the element could be inferred from the types in the super classes.
289 bool _checkSingleOverride(ExecutableElement element, InterfaceType type,
290 AstNode node, AstNode errorLocation) {
291 assert(!element.isStatic);
292
293 FunctionType subType = _rules.elementType(element);
294 // TODO(vsm): Test for generic
295 FunctionType baseType = getMemberType(type, element);
296
297 if (baseType == null) return false;
298 if (!_rules.isAssignable(subType, baseType)) {
299 // See whether non-assignable cases fit one of our common patterns:
300 //
301 // Common pattern 1: Inferable return type (on getters and methods)
302 // class A {
303 // int get foo => ...;
304 // String toString() { ... }
305 // }
306 // class B extends A {
307 // get foo => e; // no type specified.
308 // toString() { ... } // no return type specified.
309 // }
310 _recordMessage(new InvalidMethodOverride(
311 errorLocation, element, type, subType, baseType));
312 }
313 return true;
314 }
315
316 void _recordMessage(StaticInfo info) {
317 if (info == null) return;
318 var error = info.toAnalysisError();
319 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
320 _reporter.onError(error);
321 }
322 }
323
324 /// Checks the body of functions and properties.
325 class CodeChecker extends RecursiveAstVisitor {
326 final TypeRules rules;
327 final AnalysisErrorListener reporter;
328 final _OverrideChecker _overrideChecker;
329 bool _failure = false;
330 bool get failure => _failure || _overrideChecker._failure;
331
332 void reset() {
333 _failure = false;
334 _overrideChecker._failure = false;
335 }
336
337 CodeChecker(TypeRules rules, AnalysisErrorListener reporter)
338 : rules = rules,
339 reporter = reporter,
340 _overrideChecker = new _OverrideChecker(rules, reporter);
341
342 @override
343 void visitComment(Comment node) {
344 // skip, no need to do typechecking inside comments (they may contain
345 // comment references which would require resolution).
346 }
347
348 @override
349 void visitClassDeclaration(ClassDeclaration node) {
350 _overrideChecker.check(node);
351 super.visitClassDeclaration(node);
352 }
353
354 @override
355 void visitAssignmentExpression(AssignmentExpression node) {
356 var token = node.operator;
357 if (token.type != TokenType.EQ) {
358 _checkCompoundAssignment(node);
359 } else {
360 DartType staticType = _getStaticType(node.leftHandSide);
361 checkAssignment(node.rightHandSide, staticType);
362 }
363 node.visitChildren(this);
364 }
365
366 /// Check constructor declaration to ensure correct super call placement.
367 @override
368 void visitConstructorDeclaration(ConstructorDeclaration node) {
369 node.visitChildren(this);
370
371 final init = node.initializers;
372 for (int i = 0, last = init.length - 1; i < last; i++) {
373 final node = init[i];
374 if (node is SuperConstructorInvocation) {
375 _recordMessage(new InvalidSuperInvocation(node));
376 }
377 }
378 }
379
380 @override
381 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
382 var field = node.fieldName;
383 var element = field.staticElement;
384 DartType staticType = rules.elementType(element);
385 checkAssignment(node.expression, staticType);
386 node.visitChildren(this);
387 }
388
389 @override
390 void visitForEachStatement(ForEachStatement node) {
391 // Check that the expression is an Iterable.
392 var expr = node.iterable;
393 var iterableType = node.awaitKeyword != null
394 ? rules.provider.streamType
395 : rules.provider.iterableType;
396 var loopVariable = node.identifier != null
397 ? node.identifier
398 : node.loopVariable?.identifier;
399 if (loopVariable != null) {
400 var iteratorType = loopVariable.staticType;
401 var checkedType = iterableType.substitute4([iteratorType]);
402 checkAssignment(expr, checkedType);
403 }
404 node.visitChildren(this);
405 }
406
407 @override
408 void visitForStatement(ForStatement node) {
409 if (node.condition != null) {
410 checkBoolean(node.condition);
411 }
412 node.visitChildren(this);
413 }
414
415 @override
416 void visitIfStatement(IfStatement node) {
417 checkBoolean(node.condition);
418 node.visitChildren(this);
419 }
420
421 @override
422 void visitDoStatement(DoStatement node) {
423 checkBoolean(node.condition);
424 node.visitChildren(this);
425 }
426
427 @override
428 void visitWhileStatement(WhileStatement node) {
429 checkBoolean(node.condition);
430 node.visitChildren(this);
431 }
432
433 @override
434 void visitSwitchStatement(SwitchStatement node) {
435 // SwitchStatement defines a boolean conversion to check the result of the
436 // case value == the switch value, but in dev_compiler we require a boolean
437 // return type from an overridden == operator (because Object.==), so
438 // checking in SwitchStatement shouldn't be necessary.
439 node.visitChildren(this);
440 }
441
442 @override
443 void visitListLiteral(ListLiteral node) {
444 var type = rules.provider.dynamicType;
445 if (node.typeArguments != null) {
446 var targs = node.typeArguments.arguments;
447 if (targs.length > 0) type = targs[0].type;
448 }
449 var elements = node.elements;
450 for (int i = 0; i < elements.length; i++) {
451 checkArgument(elements[i], type);
452 }
453 super.visitListLiteral(node);
454 }
455
456 @override
457 void visitMapLiteral(MapLiteral node) {
458 var ktype = rules.provider.dynamicType;
459 var vtype = rules.provider.dynamicType;
460 if (node.typeArguments != null) {
461 var targs = node.typeArguments.arguments;
462 if (targs.length > 0) ktype = targs[0].type;
463 if (targs.length > 1) vtype = targs[1].type;
464 }
465 var entries = node.entries;
466 for (int i = 0; i < entries.length; i++) {
467 var entry = entries[i];
468 checkArgument(entry.key, ktype);
469 checkArgument(entry.value, vtype);
470 }
471 super.visitMapLiteral(node);
472 }
473
474 // Check invocations
475 void checkArgumentList(ArgumentList node, FunctionType type) {
476 NodeList<Expression> list = node.arguments;
477 int len = list.length;
478 for (int i = 0; i < len; ++i) {
479 Expression arg = list[i];
480 ParameterElement element = arg.staticParameterElement;
481 if (element == null) {
482 if (type.parameters.length < len) {
483 // We found an argument mismatch, the analyzer will report this too,
484 // so no need to insert an error for this here.
485 continue;
486 }
487 element = type.parameters[i];
488 // TODO(vsm): When can this happen?
489 assert(element != null);
490 }
491 DartType expectedType = rules.elementType(element);
492 if (expectedType == null) expectedType = rules.provider.dynamicType;
493 checkArgument(arg, expectedType);
494 }
495 }
496
497 void checkArgument(Expression arg, DartType expectedType) {
498 // Preserve named argument structure, so their immediate parent is the
499 // method invocation.
500 if (arg is NamedExpression) {
501 arg = (arg as NamedExpression).expression;
502 }
503 checkAssignment(arg, expectedType);
504 }
505
506 void checkFunctionApplication(
507 Expression node, Expression f, ArgumentList list) {
508 if (rules.isDynamicCall(f)) {
509 // If f is Function and this is a method invocation, we should have
510 // gotten an analyzer error, so no need to issue another error.
511 _recordDynamicInvoke(node, f);
512 } else {
513 checkArgumentList(list, rules.getTypeAsCaller(f));
514 }
515 }
516
517 @override
518 visitMethodInvocation(MethodInvocation node) {
519 var target = node.realTarget;
520 if (rules.isDynamicTarget(target) &&
521 !_isObjectMethod(node, node.methodName)) {
522 _recordDynamicInvoke(node, target);
523
524 // Mark the tear-off as being dynamic, too. This lets us distinguish
525 // cases like:
526 //
527 // dynamic d;
528 // d.someMethod(...); // the whole method call must be a dynamic send.
529 //
530 // ... from case like:
531 //
532 // SomeType s;
533 // s.someDynamicField(...); // static get, followed by dynamic call.
534 //
535 // The first case is handled here, the second case is handled below when
536 // we call [checkFunctionApplication].
537 DynamicInvoke.set(node.methodName, true);
538 } else {
539 checkFunctionApplication(node, node.methodName, node.argumentList);
540 }
541 node.visitChildren(this);
542 }
543
544 @override
545 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
546 checkFunctionApplication(node, node.function, node.argumentList);
547 node.visitChildren(this);
548 }
549
550 @override
551 void visitRedirectingConstructorInvocation(
552 RedirectingConstructorInvocation node) {
553 var type = node.staticElement.type;
554 checkArgumentList(node.argumentList, type);
555 node.visitChildren(this);
556 }
557
558 @override
559 void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
560 var element = node.staticElement;
561 if (element == null) {
562 _recordMessage(new MissingTypeError(node));
563 } else {
564 var type = node.staticElement.type;
565 checkArgumentList(node.argumentList, type);
566 }
567 node.visitChildren(this);
568 }
569
570 void _checkReturnOrYield(Expression expression, AstNode node,
571 {bool yieldStar: false}) {
572 var body = node.getAncestor((n) => n is FunctionBody);
573 var type = rules.getExpectedReturnType(body, yieldStar: yieldStar);
574 if (type == null) {
575 // We have a type mismatch: the async/async*/sync* modifier does
576 // not match the return or yield type. We should have already gotten an
577 // analyzer error in this case.
578 return;
579 }
580 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null.
581 if (expression != null) checkAssignment(expression, type);
582 }
583
584 @override
585 void visitExpressionFunctionBody(ExpressionFunctionBody node) {
586 _checkReturnOrYield(node.expression, node);
587 node.visitChildren(this);
588 }
589
590 @override
591 void visitReturnStatement(ReturnStatement node) {
592 _checkReturnOrYield(node.expression, node);
593 node.visitChildren(this);
594 }
595
596 @override
597 void visitYieldStatement(YieldStatement node) {
598 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null);
599 node.visitChildren(this);
600 }
601
602 @override
603 void visitPropertyAccess(PropertyAccess node) {
604 var target = node.realTarget;
605 if (rules.isDynamicTarget(target) &&
606 !_isObjectProperty(target, node.propertyName)) {
607 _recordDynamicInvoke(node, target);
608 }
609 node.visitChildren(this);
610 }
611
612 @override
613 void visitPrefixedIdentifier(PrefixedIdentifier node) {
614 final target = node.prefix;
615 if (rules.isDynamicTarget(target) &&
616 !_isObjectProperty(target, node.identifier)) {
617 _recordDynamicInvoke(node, target);
618 }
619 node.visitChildren(this);
620 }
621
622 @override
623 void visitDefaultFormalParameter(DefaultFormalParameter node) {
624 // Check that defaults have the proper subtype.
625 var parameter = node.parameter;
626 var parameterType = rules.elementType(parameter.element);
627 assert(parameterType != null);
628 var defaultValue = node.defaultValue;
629 if (defaultValue != null) {
630 checkAssignment(defaultValue, parameterType);
631 }
632
633 node.visitChildren(this);
634 }
635
636 @override
637 void visitFieldFormalParameter(FieldFormalParameter node) {
638 var element = node.element;
639 var typeName = node.type;
640 if (typeName != null) {
641 var type = rules.elementType(element);
642 var fieldElement =
643 node.identifier.staticElement as FieldFormalParameterElement;
644 var fieldType = rules.elementType(fieldElement.field);
645 if (!rules.isSubTypeOf(type, fieldType)) {
646 var staticInfo =
647 new InvalidParameterDeclaration(rules, node, fieldType);
648 _recordMessage(staticInfo);
649 }
650 }
651 node.visitChildren(this);
652 }
653
654 @override
655 void visitInstanceCreationExpression(InstanceCreationExpression node) {
656 var arguments = node.argumentList;
657 var element = node.staticElement;
658 if (element != null) {
659 var type = rules.elementType(node.staticElement);
660 checkArgumentList(arguments, type);
661 } else {
662 _recordMessage(new MissingTypeError(node));
663 }
664 node.visitChildren(this);
665 }
666
667 @override
668 void visitVariableDeclarationList(VariableDeclarationList node) {
669 TypeName type = node.type;
670 if (type == null) {
671 // No checks are needed when the type is var. Although internally the
672 // typing rules may have inferred a more precise type for the variable
673 // based on the initializer.
674 } else {
675 var dartType = getType(type);
676 for (VariableDeclaration variable in node.variables) {
677 var initializer = variable.initializer;
678 if (initializer != null) {
679 checkAssignment(initializer, dartType);
680 }
681 }
682 }
683 node.visitChildren(this);
684 }
685
686 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) {
687 var type = getType(typeName);
688 if (!rules.isGroundType(type)) {
689 _recordMessage(new NonGroundTypeCheckInfo(node, type));
690 }
691 }
692
693 @override
694 void visitAsExpression(AsExpression node) {
695 // We could do the same check as the IsExpression below, but that is
696 // potentially too conservative. Instead, at runtime, we must fail hard
697 // if the Dart as and the DDC as would return different values.
698 node.visitChildren(this);
699 }
700
701 @override
702 void visitIsExpression(IsExpression node) {
703 _checkRuntimeTypeCheck(node, node.type);
704 node.visitChildren(this);
705 }
706
707 @override
708 void visitPrefixExpression(PrefixExpression node) {
709 if (node.operator.type == TokenType.BANG) {
710 checkBoolean(node.operand);
711 } else {
712 _checkUnary(node);
713 }
714 node.visitChildren(this);
715 }
716
717 @override
718 void visitPostfixExpression(PostfixExpression node) {
719 _checkUnary(node);
720 node.visitChildren(this);
721 }
722
723 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) {
724 var op = node.operator;
725 if (op.isUserDefinableOperator ||
726 op.type == TokenType.PLUS_PLUS ||
727 op.type == TokenType.MINUS_MINUS) {
728 if (rules.isDynamicTarget(node.operand)) {
729 _recordDynamicInvoke(node, node.operand);
730 }
731 // For ++ and --, even if it is not dynamic, we still need to check
732 // that the user defined method accepts an `int` as the RHS.
733 // We assume Analyzer has done this already.
734 }
735 }
736
737 @override
738 void visitBinaryExpression(BinaryExpression node) {
739 var op = node.operator;
740 if (op.isUserDefinableOperator) {
741 if (rules.isDynamicTarget(node.leftOperand)) {
742 // Dynamic invocation
743 // TODO(vsm): Move this logic to the resolver?
744 if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) {
745 _recordDynamicInvoke(node, node.leftOperand);
746 }
747 } else {
748 var element = node.staticElement;
749 // Method invocation.
750 if (element is MethodElement) {
751 var type = element.type;
752 // Analyzer should enforce number of parameter types, but check in
753 // case we have erroneous input.
754 if (type.normalParameterTypes.isNotEmpty) {
755 checkArgument(node.rightOperand, type.normalParameterTypes[0]);
756 }
757 } else {
758 // TODO(vsm): Assert that the analyzer found an error here?
759 }
760 }
761 } else {
762 // Non-method operator.
763 switch (op.type) {
764 case TokenType.AMPERSAND_AMPERSAND:
765 case TokenType.BAR_BAR:
766 checkBoolean(node.leftOperand);
767 checkBoolean(node.rightOperand);
768 break;
769 case TokenType.BANG_EQ:
770 break;
771 case TokenType.QUESTION_QUESTION:
772 break;
773 default:
774 assert(false);
775 }
776 }
777 node.visitChildren(this);
778 }
779
780 @override
781 void visitConditionalExpression(ConditionalExpression node) {
782 checkBoolean(node.condition);
783 node.visitChildren(this);
784 }
785
786 @override
787 void visitIndexExpression(IndexExpression node) {
788 var target = node.realTarget;
789 if (rules.isDynamicTarget(target)) {
790 _recordDynamicInvoke(node, target);
791 } else {
792 var element = node.staticElement;
793 if (element is MethodElement) {
794 var type = element.type;
795 // Analyzer should enforce number of parameter types, but check in
796 // case we have erroneous input.
797 if (type.normalParameterTypes.isNotEmpty) {
798 checkArgument(node.index, type.normalParameterTypes[0]);
799 }
800 } else {
801 // TODO(vsm): Assert that the analyzer found an error here?
802 }
803 }
804 node.visitChildren(this);
805 }
806
807 DartType getType(TypeName name) {
808 return (name == null) ? rules.provider.dynamicType : name.type;
809 }
810
811 /// Analyzer checks boolean conversions, but we need to check too, because
812 /// it uses the default assignability rules that allow `dynamic` and `Object`
813 /// to be assigned to bool with no message.
814 void checkBoolean(Expression expr) =>
815 checkAssignment(expr, rules.provider.boolType);
816
817 void checkAssignment(Expression expr, DartType type) {
818 if (expr is ParenthesizedExpression) {
819 checkAssignment(expr.expression, type);
820 } else {
821 _recordMessage(rules.checkAssignment(expr, type));
822 }
823 }
824
825 DartType _specializedBinaryReturnType(
826 TokenType op, DartType t1, DartType t2, DartType normalReturnType) {
827 // This special cases binary return types as per 16.26 and 16.27 of the
828 // Dart language spec.
829 switch (op) {
830 case TokenType.PLUS:
831 case TokenType.MINUS:
832 case TokenType.STAR:
833 case TokenType.TILDE_SLASH:
834 case TokenType.PERCENT:
835 case TokenType.PLUS_EQ:
836 case TokenType.MINUS_EQ:
837 case TokenType.STAR_EQ:
838 case TokenType.TILDE_SLASH_EQ:
839 case TokenType.PERCENT_EQ:
840 if (t1 == rules.provider.intType &&
841 t2 == rules.provider.intType) return t1;
842 if (t1 == rules.provider.doubleType &&
843 t2 == rules.provider.doubleType) return t1;
844 // This particular combo is not spelled out in the spec, but all
845 // implementations and analyzer seem to follow this.
846 if (t1 == rules.provider.doubleType &&
847 t2 == rules.provider.intType) return t1;
848 }
849 return normalReturnType;
850 }
851
852 void _checkCompoundAssignment(AssignmentExpression expr) {
853 var op = expr.operator.type;
854 assert(op.isAssignmentOperator && op != TokenType.EQ);
855 var methodElement = expr.staticElement;
856 if (methodElement == null) {
857 // Dynamic invocation
858 _recordDynamicInvoke(expr, expr.leftHandSide);
859 } else {
860 // Sanity check the operator
861 assert(methodElement.isOperator);
862 var functionType = methodElement.type;
863 var paramTypes = functionType.normalParameterTypes;
864 assert(paramTypes.length == 1);
865 assert(functionType.namedParameterTypes.isEmpty);
866 assert(functionType.optionalParameterTypes.isEmpty);
867
868 // Check the lhs type
869 var staticInfo;
870 var rhsType = _getStaticType(expr.rightHandSide);
871 var lhsType = _getStaticType(expr.leftHandSide);
872 var returnType = _specializedBinaryReturnType(
873 op, lhsType, rhsType, functionType.returnType);
874
875 if (!rules.isSubTypeOf(returnType, lhsType)) {
876 final numType = rules.provider.numType;
877 // Try to fix up the numerical case if possible.
878 if (rules.isSubTypeOf(lhsType, numType) &&
879 rules.isSubTypeOf(lhsType, rhsType)) {
880 // This is also slightly different from spec, but allows us to keep
881 // compound operators in the int += num and num += dynamic cases.
882 staticInfo = DownCast.create(
883 rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType));
884 rhsType = lhsType;
885 } else {
886 // Static type error
887 staticInfo = new StaticTypeError(rules, expr, lhsType);
888 }
889 _recordMessage(staticInfo);
890 }
891
892 // Check the rhs type
893 if (staticInfo is! CoercionInfo) {
894 var paramType = paramTypes.first;
895 staticInfo = rules.checkAssignment(expr.rightHandSide, paramType);
896 _recordMessage(staticInfo);
897 }
898 }
899 }
900
901 bool _isObjectGetter(Expression target, SimpleIdentifier id) {
902 PropertyAccessorElement element =
903 rules.provider.objectType.element.getGetter(id.name);
904 return (element != null && !element.isStatic);
905 }
906
907 bool _isObjectMethod(Expression target, SimpleIdentifier id) {
908 MethodElement element =
909 rules.provider.objectType.element.getMethod(id.name);
910 return (element != null && !element.isStatic);
911 }
912
913 bool _isObjectProperty(Expression target, SimpleIdentifier id) {
914 return _isObjectGetter(target, id) || _isObjectMethod(target, id);
915 }
916
917 DartType _getStaticType(Expression expr) {
918 var type = expr.staticType;
919 if (type == null) {
920 reporter.onError(new MissingTypeError(expr).toAnalysisError());
921 }
922 return type ?? rules.provider.dynamicType;
923 }
924
925 void _recordDynamicInvoke(AstNode node, AstNode target) {
926 reporter.onError(new DynamicInvoke(rules, node).toAnalysisError());
927 // TODO(jmesserly): we may eventually want to record if the whole operation
928 // (node) was dynamic, rather than the target, but this is an easier fit
929 // with what we used to do.
930 DynamicInvoke.set(target, true);
931 }
932
933 void _recordMessage(StaticInfo info) {
934 if (info == null) return;
935 var error = info.toAnalysisError();
936 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
937 reporter.onError(error);
938
939 if (info is CoercionInfo) {
940 // TODO(jmesserly): if we're run again on the same AST, we'll produce the
941 // same annotations. This should be harmless. This might go away once
942 // CodeChecker is integrated better with analyzer, as it will know that
943 // checking has already been performed.
944 // assert(CoercionInfo.get(info.node) == null);
945 CoercionInfo.set(info.node, info);
946 }
947 }
948 }
OLDNEW
« no previous file with comments | « no previous file | lib/src/checker/resolver.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698