OLD | NEW |
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 /// Defines static information collected by the type checker and used later by | 5 /// Defines static information collected by the type checker and used later by |
6 /// emitters to generate code. | 6 /// emitters to generate code. |
7 library dev_compiler.src.info; | 7 library dev_compiler.src.info; |
8 | 8 |
9 import 'package:analyzer/src/generated/ast.dart'; | 9 import 'package:analyzer/src/generated/ast.dart'; |
10 import 'package:analyzer/src/generated/element.dart'; | 10 import 'package:analyzer/src/generated/element.dart'; |
11 import 'package:analyzer/src/generated/error.dart'; | |
12 import 'package:analyzer/src/generated/parser.dart'; | 11 import 'package:analyzer/src/generated/parser.dart'; |
13 | 12 |
14 import 'checker/rules.dart'; | |
15 import 'utils.dart' as utils; | 13 import 'utils.dart' as utils; |
| 14 import 'package:analyzer/src/task/strong/info.dart'; |
| 15 export 'package:analyzer/src/task/strong/info.dart'; |
16 | 16 |
17 /// Represents a summary of the results collected by running the program | 17 /// Represents a summary of the results collected by running the program |
18 /// checker. | 18 /// checker. |
19 class CheckerResults { | 19 class CheckerResults { |
20 final List<LibraryInfo> libraries; | 20 final List<LibraryInfo> libraries; |
21 final TypeRules rules; | |
22 final bool failure; | 21 final bool failure; |
23 | 22 |
24 CheckerResults(this.libraries, this.rules, this.failure); | 23 CheckerResults(this.libraries, this.failure); |
25 } | 24 } |
26 | 25 |
27 /// Computed information about each library. | 26 /// Computed information about each library. |
28 class LibraryInfo { | 27 class LibraryInfo { |
29 /// Canonical name of the library. This is unfortunately not derived from the | 28 /// Canonical name of the library. This is unfortunately not derived from the |
30 /// library directive as it doesn't have any meaningful rules enforced. | 29 /// library directive as it doesn't have any meaningful rules enforced. |
31 /// Instead, this is inferred from the path to the file defining the library. | 30 /// Instead, this is inferred from the path to the file defining the library. |
32 final String name; | 31 final String name; |
33 | 32 |
34 /// Corresponding analyzer element. | 33 /// Corresponding analyzer element. |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
95 // TODO(jmesserly): as a workaround for analyzer <0.26.0-alpha.1. | 94 // TODO(jmesserly): as a workaround for analyzer <0.26.0-alpha.1. |
96 // ResolutionCopier won't copy the type, so we do it here. | 95 // ResolutionCopier won't copy the type, so we do it here. |
97 @override | 96 @override |
98 AwaitExpression visitAwaitExpression(AwaitExpression node) { | 97 AwaitExpression visitAwaitExpression(AwaitExpression node) { |
99 var clone = super.visitAwaitExpression(node); | 98 var clone = super.visitAwaitExpression(node); |
100 clone.staticType = node.staticType; | 99 clone.staticType = node.staticType; |
101 clone.propagatedType = node.propagatedType; | 100 clone.propagatedType = node.propagatedType; |
102 return clone; | 101 return clone; |
103 } | 102 } |
104 } | 103 } |
105 | |
106 // The abstract type of coercions mapping one type to another. | |
107 // This class also exposes static builder functions which | |
108 // check for errors and reduce redundant coercions to the identity. | |
109 abstract class Coercion { | |
110 final DartType fromType; | |
111 final DartType toType; | |
112 Coercion(this.fromType, this.toType); | |
113 static Coercion cast(DartType fromT, DartType toT) => new Cast(fromT, toT); | |
114 static Coercion identity(DartType type) => new Identity(type); | |
115 static Coercion error() => new CoercionError(); | |
116 } | |
117 | |
118 // Coercion which casts one type to another | |
119 class Cast extends Coercion { | |
120 Cast(DartType fromType, DartType toType) : super(fromType, toType); | |
121 } | |
122 | |
123 // The identity coercion | |
124 class Identity extends Coercion { | |
125 Identity(DartType fromType) : super(fromType, fromType); | |
126 } | |
127 | |
128 // The error coercion. This coercion signals that a coercion | |
129 // could not be generated. The code generator should not see | |
130 // these. | |
131 class CoercionError extends Coercion { | |
132 CoercionError() : super(null, null); | |
133 } | |
134 | |
135 // TODO(jmesserly): this could use some refactoring. These are essentially | |
136 // like ErrorCodes in analyzer, but we're including some details in our message. | |
137 // Analyzer instead has template strings, and replaces '{0}' with the first | |
138 // argument. | |
139 abstract class StaticInfo { | |
140 /// AST Node this info is attached to. | |
141 AstNode get node; | |
142 | |
143 // TODO(jmesserly): review the usage of error codes. We probably want our own, | |
144 // as well as some DDC specific [ErrorType]s. | |
145 ErrorCode toErrorCode(); | |
146 | |
147 // TODO(jmesserly): what convention to use here? | |
148 String get name => 'dev_compiler.$runtimeType'; | |
149 | |
150 List<Object> get arguments => [node]; | |
151 | |
152 AnalysisError toAnalysisError() { | |
153 int begin = node is AnnotatedNode | |
154 ? (node as AnnotatedNode).firstTokenAfterCommentAndMetadata.offset | |
155 : node.offset; | |
156 int length = node.end - begin; | |
157 var source = (node.root as CompilationUnit).element.source; | |
158 return new AnalysisError(source, begin, length, toErrorCode(), arguments); | |
159 } | |
160 } | |
161 | |
162 /// Implicitly injected expression conversion. | |
163 abstract class CoercionInfo extends StaticInfo { | |
164 final TypeRules rules; | |
165 | |
166 final Expression node; | |
167 | |
168 DartType get convertedType; | |
169 | |
170 CoercionInfo(this.rules, this.node); | |
171 | |
172 DartType get baseType => rules.getStaticType(node); | |
173 DartType get staticType => convertedType; | |
174 | |
175 String get message; | |
176 toErrorCode() => new HintCode(name, message); | |
177 | |
178 static const String _propertyName = 'dev_compiler.src.info.CoercionInfo'; | |
179 | |
180 /// Gets the coercion info associated with this node. | |
181 static CoercionInfo get(AstNode node) => node.getProperty(_propertyName); | |
182 | |
183 /// Sets the coercion info associated with this node. | |
184 static CoercionInfo set(AstNode node, CoercionInfo info) { | |
185 node.setProperty(_propertyName, info); | |
186 return info; | |
187 } | |
188 } | |
189 | |
190 // Base class for all casts from base type to sub type. | |
191 abstract class DownCast extends CoercionInfo { | |
192 Cast _cast; | |
193 | |
194 DownCast._internal(TypeRules rules, Expression expression, this._cast) | |
195 : super(rules, expression) { | |
196 assert(_cast.toType != baseType && | |
197 _cast.fromType == baseType && | |
198 (baseType.isDynamic || | |
199 // Call methods make the following non-redundant | |
200 _cast.toType.isSubtypeOf(baseType) || | |
201 baseType.isAssignableTo(_cast.toType))); | |
202 } | |
203 | |
204 Cast get cast => _cast; | |
205 | |
206 DartType get convertedType => _cast.toType; | |
207 | |
208 @override List<Object> get arguments => [node, baseType, convertedType]; | |
209 @override String get message => '{0} ({1}) will need runtime check ' | |
210 'to cast to type {2}'; | |
211 | |
212 // Factory to create correct DownCast variant. | |
213 static StaticInfo create(TypeRules rules, Expression expression, Cast cast, | |
214 {String reason}) { | |
215 final fromT = cast.fromType; | |
216 final toT = cast.toType; | |
217 | |
218 // toT <:_R fromT => to <: fromT | |
219 // NB: classes with call methods are subtypes of function | |
220 // types, but the function type is not assignable to the class | |
221 assert(toT.isSubtypeOf(fromT) || fromT.isAssignableTo(toT)); | |
222 | |
223 // Handle null call specially. | |
224 if (expression is NullLiteral) { | |
225 // TODO(vsm): Create a NullCast for this once we revisit nonnullability. | |
226 return new DownCastImplicit(rules, expression, cast); | |
227 } | |
228 | |
229 // Inference "casts": | |
230 if (expression is Literal) { | |
231 // fromT should be an exact type - this will almost certainly fail at | |
232 // runtime. | |
233 return new StaticTypeError(rules, expression, toT, reason: reason); | |
234 } | |
235 if (expression is FunctionExpression) { | |
236 // fromT should be an exact type - this will almost certainly fail at | |
237 // runtime. | |
238 return new UninferredClosure(rules, expression, cast); | |
239 } | |
240 if (expression is InstanceCreationExpression) { | |
241 // fromT should be an exact type - this will almost certainly fail at | |
242 // runtime. | |
243 return new StaticTypeError(rules, expression, toT, reason: reason); | |
244 } | |
245 | |
246 // Composite cast: these are more likely to fail. | |
247 if (!rules.isGroundType(toT)) { | |
248 // This cast is (probably) due to our different treatment of dynamic. | |
249 // It may be more likely to fail at runtime. | |
250 return new DownCastComposite(rules, expression, cast); | |
251 } | |
252 | |
253 // Dynamic cast | |
254 if (fromT.isDynamic) { | |
255 return new DynamicCast(rules, expression, cast); | |
256 } | |
257 | |
258 // Assignment cast | |
259 var parent = expression.parent; | |
260 if (parent is VariableDeclaration && (parent.initializer == expression)) { | |
261 return new AssignmentCast(rules, expression, cast); | |
262 } | |
263 | |
264 // Other casts | |
265 return new DownCastImplicit(rules, expression, cast); | |
266 } | |
267 } | |
268 | |
269 // | |
270 // Standard down casts. These casts are implicitly injected by the compiler. | |
271 // | |
272 | |
273 // A down cast from dynamic to T. | |
274 class DynamicCast extends DownCast { | |
275 DynamicCast(TypeRules rules, Expression expression, Cast cast) | |
276 : super._internal(rules, expression, cast); | |
277 | |
278 toErrorCode() => new HintCode(name, message); | |
279 } | |
280 | |
281 // A down cast due to a variable declaration to a ground type. E.g., | |
282 // T x = expr; | |
283 // where T is ground. We exclude non-ground types as these behave differently | |
284 // compared to standard Dart. | |
285 class AssignmentCast extends DownCast { | |
286 AssignmentCast(TypeRules rules, Expression expression, Cast cast) | |
287 : super._internal(rules, expression, cast); | |
288 | |
289 toErrorCode() => new HintCode(name, message); | |
290 } | |
291 | |
292 // | |
293 // Temporary "casts" of allocation sites - literals, constructor invocations, | |
294 // and closures. These should be handled by contextual inference. In most | |
295 // cases, inference will be sufficient, though in some it may unmask an actual | |
296 // error: e.g., | |
297 // List<int> l = [1, 2, 3]; // Inference succeeds | |
298 // List<String> l = [1, 2, 3]; // Inference reveals static type error | |
299 // We're marking all as warnings for now. | |
300 // | |
301 // TODO(vsm,leafp): Remove this. | |
302 class UninferredClosure extends DownCast { | |
303 UninferredClosure(TypeRules rules, FunctionExpression expression, Cast cast) | |
304 : super._internal(rules, expression, cast); | |
305 | |
306 toErrorCode() => new StaticTypeWarningCode(name, message); | |
307 } | |
308 | |
309 // | |
310 // Implicit down casts. These are only injected by the compiler by flag. | |
311 // | |
312 | |
313 // A down cast to a non-ground type. These behave differently from standard | |
314 // Dart and may be more likely to fail at runtime. | |
315 class DownCastComposite extends DownCast { | |
316 DownCastComposite(TypeRules rules, Expression expression, Cast cast) | |
317 : super._internal(rules, expression, cast); | |
318 | |
319 toErrorCode() => new StaticTypeWarningCode(name, message); | |
320 } | |
321 | |
322 // A down cast to a non-ground type. These behave differently from standard | |
323 // Dart and may be more likely to fail at runtime. | |
324 class DownCastImplicit extends DownCast { | |
325 DownCastImplicit(TypeRules rules, Expression expression, Cast cast) | |
326 : super._internal(rules, expression, cast); | |
327 | |
328 toErrorCode() => new HintCode(name, message); | |
329 } | |
330 | |
331 // An inferred type for the wrapped expression, which may need to be | |
332 // reified into the term | |
333 abstract class InferredTypeBase extends CoercionInfo { | |
334 final DartType _type; | |
335 | |
336 InferredTypeBase._internal(TypeRules rules, Expression expression, this._type) | |
337 : super(rules, expression); | |
338 | |
339 DartType get type => _type; | |
340 DartType get convertedType => type; | |
341 @override String get message => '{0} has inferred type {1}'; | |
342 @override List get arguments => [node, type]; | |
343 | |
344 toErrorCode() => new HintCode(name, message); | |
345 } | |
346 | |
347 // Standard / unspecialized inferred type | |
348 class InferredType extends InferredTypeBase { | |
349 InferredType(TypeRules rules, Expression expression, DartType type) | |
350 : super._internal(rules, expression, type); | |
351 | |
352 // Factory to create correct InferredType variant. | |
353 static InferredTypeBase create( | |
354 TypeRules rules, Expression expression, DartType type) { | |
355 // Specialized inference: | |
356 if (expression is Literal) { | |
357 return new InferredTypeLiteral(rules, expression, type); | |
358 } | |
359 if (expression is InstanceCreationExpression) { | |
360 return new InferredTypeAllocation(rules, expression, type); | |
361 } | |
362 if (expression is FunctionExpression) { | |
363 return new InferredTypeClosure(rules, expression, type); | |
364 } | |
365 return new InferredType(rules, expression, type); | |
366 } | |
367 } | |
368 | |
369 // An infered type for a literal expression. | |
370 class InferredTypeLiteral extends InferredTypeBase { | |
371 InferredTypeLiteral(TypeRules rules, Expression expression, DartType type) | |
372 : super._internal(rules, expression, type); | |
373 } | |
374 | |
375 // An inferred type for a non-literal allocation site. | |
376 class InferredTypeAllocation extends InferredTypeBase { | |
377 InferredTypeAllocation(TypeRules rules, Expression expression, DartType type) | |
378 : super._internal(rules, expression, type); | |
379 } | |
380 | |
381 // An inferred type for a closure expression | |
382 class InferredTypeClosure extends InferredTypeBase { | |
383 InferredTypeClosure(TypeRules rules, Expression expression, DartType type) | |
384 : super._internal(rules, expression, type); | |
385 } | |
386 | |
387 class DynamicInvoke extends CoercionInfo { | |
388 DynamicInvoke(TypeRules rules, Expression expression) | |
389 : super(rules, expression); | |
390 | |
391 DartType get convertedType => rules.provider.dynamicType; | |
392 String get message => '{0} requires dynamic invoke'; | |
393 toErrorCode() => new HintCode(name, message); | |
394 | |
395 static const String _propertyName = 'dev_compiler.src.info.DynamicInvoke'; | |
396 | |
397 /// Whether this [node] is the target of a dynamic operation. | |
398 static bool get(AstNode node) { | |
399 var value = node.getProperty(_propertyName); | |
400 return value != null ? value : false; | |
401 } | |
402 | |
403 /// Sets whether this node is the target of a dynamic operation. | |
404 static bool set(AstNode node, bool value) { | |
405 // Free the storage for things that aren't dynamic. | |
406 if (value == false) value = null; | |
407 node.setProperty(_propertyName, value); | |
408 return value; | |
409 } | |
410 } | |
411 | |
412 abstract class StaticError extends StaticInfo { | |
413 final AstNode node; | |
414 | |
415 StaticError(this.node); | |
416 | |
417 String get message; | |
418 | |
419 toErrorCode() => new CompileTimeErrorCode(name, message); | |
420 } | |
421 | |
422 class StaticTypeError extends StaticError { | |
423 final DartType baseType; | |
424 final DartType expectedType; | |
425 String reason = null; | |
426 | |
427 StaticTypeError(TypeRules rules, Expression expression, this.expectedType, | |
428 {this.reason}) | |
429 : baseType = rules.getStaticType(expression), | |
430 super(expression); | |
431 | |
432 @override List<Object> get arguments => [node, baseType, expectedType]; | |
433 @override String get message => | |
434 'Type check failed: {0} ({1}) is not of type {2}' + | |
435 ((reason == null) ? '' : ' because $reason'); | |
436 } | |
437 | |
438 class InvalidVariableDeclaration extends StaticError { | |
439 final DartType expectedType; | |
440 | |
441 InvalidVariableDeclaration( | |
442 TypeRules rules, AstNode declaration, this.expectedType) | |
443 : super(declaration); | |
444 | |
445 @override List<Object> get arguments => [expectedType]; | |
446 @override String get message => 'Type check failed: null is not of type {0}'; | |
447 } | |
448 | |
449 class InvalidParameterDeclaration extends StaticError { | |
450 final DartType expectedType; | |
451 | |
452 InvalidParameterDeclaration( | |
453 TypeRules rules, FormalParameter declaration, this.expectedType) | |
454 : super(declaration); | |
455 | |
456 @override List<Object> get arguments => [node, expectedType]; | |
457 @override String get message => 'Type check failed: {0} is not of type {1}'; | |
458 } | |
459 | |
460 class NonGroundTypeCheckInfo extends StaticInfo { | |
461 final DartType type; | |
462 final AstNode node; | |
463 | |
464 NonGroundTypeCheckInfo(this.node, this.type) { | |
465 assert(node is IsExpression || node is AsExpression); | |
466 } | |
467 | |
468 @override List<Object> get arguments => [type]; | |
469 String get message => | |
470 "Runtime check on non-ground type {0} may throw StrongModeError"; | |
471 | |
472 toErrorCode() => new HintCode(name, message); | |
473 } | |
474 | |
475 // Invalid override of an instance member of a class. | |
476 abstract class InvalidOverride extends StaticError { | |
477 /// Member declaration with the invalid override. | |
478 final ExecutableElement element; | |
479 | |
480 /// Type (class or interface) that provides the base declaration. | |
481 final InterfaceType base; | |
482 | |
483 /// Actual type of the overridden member. | |
484 final DartType subType; | |
485 | |
486 /// Actual type of the base member. | |
487 final DartType baseType; | |
488 | |
489 /// Whether the error comes from combining a base class and an interface | |
490 final bool fromBaseClass; | |
491 | |
492 /// Whether the error comes from a mixin (either overriding a base class or an | |
493 /// interface declaration). | |
494 final bool fromMixin; | |
495 | |
496 InvalidOverride( | |
497 AstNode node, this.element, this.base, this.subType, this.baseType) | |
498 : fromBaseClass = node is ExtendsClause, | |
499 fromMixin = node.parent is WithClause, | |
500 super(node); | |
501 | |
502 ClassElement get parent => element.enclosingElement; | |
503 | |
504 @override List<Object> get arguments => | |
505 [parent.name, element.name, subType, base, baseType]; | |
506 | |
507 String _messageHelper(String errorName) { | |
508 var lcErrorName = errorName.toLowerCase(); | |
509 var intro = fromBaseClass | |
510 ? 'Base class introduces an $lcErrorName' | |
511 : (fromMixin ? 'Mixin introduces an $lcErrorName' : errorName); | |
512 return '$intro. The type of {0}.{1} ({2}) is not a ' | |
513 'subtype of {3}.{1} ({4}).'; | |
514 } | |
515 } | |
516 | |
517 // Invalid override due to incompatible type. I.e., the overridden signature | |
518 // is not compatible with the original. | |
519 class InvalidMethodOverride extends InvalidOverride { | |
520 InvalidMethodOverride(AstNode node, ExecutableElement element, | |
521 InterfaceType base, FunctionType subType, FunctionType baseType) | |
522 : super(node, element, base, subType, baseType); | |
523 | |
524 String get message => _messageHelper('Invalid override'); | |
525 } | |
526 | |
527 /// Used to mark unexpected situations in our compiler were we couldn't compute | |
528 /// the type of an expression. | |
529 // TODO(sigmund): This is normally a result of another error that is caught by | |
530 // the analyzer, so this should likely be removed in the future. | |
531 class MissingTypeError extends StaticInfo { | |
532 final AstNode node; | |
533 toErrorCode() => new StaticTypeWarningCode(name, message); | |
534 | |
535 MissingTypeError(this.node); | |
536 | |
537 @override List<Object> get arguments => [node, node.runtimeType]; | |
538 String get message => "type analysis didn't compute the type of: {0} {1}"; | |
539 } | |
540 | |
541 /// Dart constructors have one weird quirk, illustrated with this example: | |
542 /// | |
543 /// class Base { | |
544 /// var x; | |
545 /// Base() : x = print('Base.1') { | |
546 /// print('Base.2'); | |
547 /// } | |
548 /// } | |
549 /// | |
550 /// class Derived extends Base { | |
551 /// var y, z; | |
552 /// Derived() | |
553 /// : y = print('Derived.1'), | |
554 /// super(), | |
555 /// z = print('Derived.2') { | |
556 /// print('Derived.3'); | |
557 /// } | |
558 /// } | |
559 /// | |
560 /// The order will be Derived.1, Base.1, Derived.2, Base.2, Derived.3; this | |
561 /// ordering preserves the invariant that code can't observe uninitialized | |
562 /// state, however it results in super constructor body not being run | |
563 /// immediately after super initializers. Normally this isn't observable, but it | |
564 /// could be if initializers have side effects. | |
565 /// | |
566 /// Better to have `super` at the end, as required by the Dart style guide: | |
567 /// <http://goo.gl/q1T4BB> | |
568 /// | |
569 /// For now this is the only pattern we support. | |
570 class InvalidSuperInvocation extends StaticError { | |
571 InvalidSuperInvocation(SuperConstructorInvocation node) : super(node); | |
572 | |
573 @override String get message => "super call must be last in an initializer " | |
574 "list (see http://goo.gl/q1T4BB): {0}"; | |
575 } | |
OLD | NEW |