OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /// Defines static information collected by the type checker and used later by | |
6 /// emitters to generate code. | |
7 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be | |
8 // refactored to fit into analyzer. | |
9 library analyzer.src.task.strong.info; | |
10 | |
11 import 'package:analyzer/dart/ast/ast.dart'; | |
12 import 'package:analyzer/dart/element/element.dart'; | |
13 import 'package:analyzer/dart/element/type.dart'; | |
14 import 'package:analyzer/src/dart/element/type.dart'; | |
15 import 'package:analyzer/src/generated/error.dart'; | |
16 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; | |
17 import 'package:analyzer/src/generated/type_system.dart'; | |
18 | |
19 /// Implicitly injected expression conversion. | |
20 abstract class CoercionInfo extends StaticInfo { | |
21 static const String _propertyName = 'dev_compiler.src.info.CoercionInfo'; | |
22 | |
23 final Expression node; | |
24 | |
25 CoercionInfo(this.node); | |
26 | |
27 DartType get baseType => node.staticType ?? DynamicTypeImpl.instance; | |
28 DartType get convertedType; | |
29 | |
30 String get message; | |
31 DartType get staticType => convertedType; | |
32 | |
33 toErrorCode() => new HintCode(name, message); | |
34 | |
35 /// Gets the coercion info associated with this node. | |
36 static CoercionInfo get(AstNode node) => node.getProperty(_propertyName); | |
37 | |
38 /// Sets the coercion info associated with this node. | |
39 static CoercionInfo set(AstNode node, CoercionInfo info) { | |
40 node.setProperty(_propertyName, info); | |
41 return info; | |
42 } | |
43 } | |
44 | |
45 /// Implicit casts from base type to sub type. | |
46 class DownCast extends CoercionInfo { | |
47 final DartType _fromType; | |
48 final DartType _toType; | |
49 ErrorCode _errorCode; | |
50 | |
51 DownCast._( | |
52 Expression expression, this._fromType, this._toType, this._errorCode) | |
53 : super(expression); | |
54 | |
55 @override | |
56 List<Object> get arguments => [baseType, convertedType]; | |
57 | |
58 /// The type being cast from. | |
59 /// | |
60 /// This is usually the static type of the associated expression, but may not | |
61 /// be if the cast is attached to a variable in a for-in loop. | |
62 @override | |
63 DartType get baseType => _fromType; | |
64 | |
65 @override | |
66 DartType get convertedType => _toType; | |
67 | |
68 @override | |
69 String get message => _message; | |
70 | |
71 @override | |
72 String get name => _errorCode.name; | |
73 | |
74 @override | |
75 toErrorCode() => _errorCode; | |
76 | |
77 static const String _message = 'Unsound implicit cast from {0} to {1}'; | |
78 | |
79 /// Factory to create correct DownCast variant. | |
80 static StaticInfo create(StrongTypeSystemImpl rules, Expression expression, | |
81 DartType fromType, DartType toType, AnalysisOptionsImpl options) { | |
82 // toT <:_R fromT => to <: fromT | |
83 // NB: classes with call methods are subtypes of function | |
84 // types, but the function type is not assignable to the class | |
85 assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType)); | |
86 | |
87 // Inference "casts": | |
88 if (expression is Literal || expression is FunctionExpression) { | |
89 // fromT should be an exact type - this will almost certainly fail at | |
90 // runtime. | |
91 return new StaticTypeError(expression, toType); | |
92 } | |
93 | |
94 if (expression is InstanceCreationExpression) { | |
95 ConstructorElement e = expression.staticElement; | |
96 if (e == null || !e.isFactory) { | |
97 // fromT should be an exact type - this will almost certainly fail at | |
98 // runtime. | |
99 return new StaticTypeError(expression, toType); | |
100 } | |
101 } | |
102 | |
103 if (StaticInfo.isKnownFunction(expression)) { | |
104 return new StaticTypeError(expression, toType); | |
105 } | |
106 | |
107 // TODO(vsm): Change this to an assert when we have generic methods and | |
108 // fix TypeRules._coerceTo to disallow implicit sideways casts. | |
109 bool downCastComposite = false; | |
110 if (!rules.isSubtypeOf(toType, fromType)) { | |
111 assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType)); | |
112 downCastComposite = true; | |
113 } | |
114 | |
115 // Composite cast: these are more likely to fail. | |
116 if (!rules.isGroundType(toType)) { | |
117 // This cast is (probably) due to our different treatment of dynamic. | |
118 // It may be more likely to fail at runtime. | |
119 if (fromType is InterfaceType) { | |
120 // For class types, we'd like to allow non-generic down casts, e.g., | |
121 // Iterable<T> to List<T>. The intuition here is that raw (generic) | |
122 // casts are problematic, and we should complain about those. | |
123 var typeArgs = fromType.typeArguments; | |
124 downCastComposite = | |
125 typeArgs.isEmpty || typeArgs.any((t) => t.isDynamic); | |
126 } else { | |
127 downCastComposite = true; | |
128 } | |
129 } | |
130 | |
131 var parent = expression.parent; | |
132 String name; | |
133 if (downCastComposite) { | |
134 name = 'STRONG_MODE_DOWN_CAST_COMPOSITE'; | |
135 } else if (fromType.isDynamic) { | |
136 name = 'STRONG_MODE_DYNAMIC_CAST'; | |
137 } else if (parent is VariableDeclaration && | |
138 parent.initializer == expression) { | |
139 name = 'STRONG_MODE_ASSIGNMENT_CAST'; | |
140 } else { | |
141 name = 'STRONG_MODE_DOWN_CAST_IMPLICIT'; | |
142 } | |
143 | |
144 // For the remaining cases, we allow implicit casts by default. | |
145 // However this can be disabled with an option. | |
146 ErrorCode errorCode; | |
147 if (!options.implicitCasts) { | |
148 errorCode = new CompileTimeErrorCode(name, _message); | |
149 } else if (downCastComposite) { | |
150 errorCode = new StaticWarningCode(name, _message); | |
151 } else { | |
152 errorCode = new HintCode(name, _message); | |
153 } | |
154 return new DownCast._(expression, fromType, toType, errorCode); | |
155 } | |
156 } | |
157 | |
158 class DynamicInvoke extends CoercionInfo { | |
159 static const String _propertyName = 'dev_compiler.src.info.DynamicInvoke'; | |
160 | |
161 DynamicInvoke(Expression expression) : super(expression); | |
162 DartType get convertedType => DynamicTypeImpl.instance; | |
163 String get message => '{0} requires dynamic invoke'; | |
164 | |
165 @override | |
166 String get name => 'STRONG_MODE_DYNAMIC_INVOKE'; | |
167 | |
168 toErrorCode() => new HintCode(name, message); | |
169 | |
170 /// Whether this [node] is the target of a dynamic operation. | |
171 static bool get(AstNode node) => node.getProperty(_propertyName) ?? false; | |
172 | |
173 /// Sets whether this node is the target of a dynamic operation. | |
174 static bool set(AstNode node, bool value) { | |
175 // Free the storage for things that aren't dynamic. | |
176 if (value == false) value = null; | |
177 node.setProperty(_propertyName, value); | |
178 return value; | |
179 } | |
180 } | |
181 | |
182 /// A marker for an inferred type. | |
183 class InferredType extends CoercionInfo { | |
184 @override | |
185 final String name; | |
186 | |
187 final DartType type; | |
188 | |
189 InferredType(Expression expression, this.type, this.name) : super(expression); | |
190 | |
191 /// Factory to create correct InferredType variant. | |
192 static InferredType create( | |
193 TypeSystem rules, Expression expression, DartType type) { | |
194 // Specialized inference: | |
195 String name; | |
196 if (expression is Literal) { | |
197 name = 'STRONG_MODE_INFERRED_TYPE_LITERAL'; | |
198 } else if (expression is InstanceCreationExpression) { | |
199 name = 'STRONG_MODE_INFERRED_TYPE_ALLOCATION'; | |
200 } else if (expression is FunctionExpression) { | |
201 name = 'STRONG_MODE_INFERRED_TYPE_CLOSURE'; | |
202 } else { | |
203 name = 'STRONG_MODE_INFERRED_TYPE'; | |
204 } | |
205 return new InferredType(expression, type, name); | |
206 } | |
207 | |
208 @override | |
209 List get arguments => [node, type]; | |
210 | |
211 DartType get convertedType => type; | |
212 | |
213 @override | |
214 String get message => '{0} has inferred type {1}'; | |
215 | |
216 toErrorCode() => new HintCode(name, message); | |
217 } | |
218 | |
219 class InvalidFieldOverride extends InvalidOverride { | |
220 InvalidFieldOverride(AstNode node, ExecutableElement element, | |
221 InterfaceType base, DartType subType, DartType baseType) | |
222 : super(node, element, base, subType, baseType); | |
223 | |
224 String get message => 'Field declaration {3}.{1} cannot be ' | |
225 'overridden in {0}.'; | |
226 | |
227 @override | |
228 String get name => 'STRONG_MODE_INVALID_FIELD_OVERRIDE'; | |
229 } | |
230 | |
231 /// Invalid override due to incompatible type. I.e., the overridden signature | |
232 /// is not compatible with the original. | |
233 class InvalidMethodOverride extends InvalidOverride { | |
234 InvalidMethodOverride(AstNode node, ExecutableElement element, | |
235 InterfaceType base, FunctionType subType, FunctionType baseType) | |
236 : super(node, element, base, subType, baseType); | |
237 | |
238 String get message => _messageHelper('Invalid override'); | |
239 | |
240 @override | |
241 String get name => 'STRONG_MODE_INVALID_METHOD_OVERRIDE'; | |
242 } | |
243 | |
244 /// Invalid override of an instance member of a class. | |
245 abstract class InvalidOverride extends StaticError { | |
246 /// Member declaration with the invalid override. | |
247 final ExecutableElement element; | |
248 | |
249 /// Type (class or interface) that provides the base declaration. | |
250 final InterfaceType base; | |
251 | |
252 /// Actual type of the overridden member. | |
253 final DartType subType; | |
254 | |
255 /// Actual type of the base member. | |
256 final DartType baseType; | |
257 | |
258 /// Whether the error comes from combining a base class and an interface | |
259 final bool fromBaseClass; | |
260 | |
261 /// Whether the error comes from a mixin (either overriding a base class or an | |
262 /// interface declaration). | |
263 final bool fromMixin; | |
264 | |
265 InvalidOverride( | |
266 AstNode node, this.element, this.base, this.subType, this.baseType) | |
267 : fromBaseClass = node is ExtendsClause, | |
268 fromMixin = node.parent is WithClause, | |
269 super(node); | |
270 | |
271 @override | |
272 List<Object> get arguments => | |
273 [parent.name, element.name, subType, base, baseType]; | |
274 | |
275 ClassElement get parent => element.enclosingElement; | |
276 | |
277 String _messageHelper(String errorName) { | |
278 var lcErrorName = errorName.toLowerCase(); | |
279 var intro = fromBaseClass | |
280 ? 'Base class introduces an $lcErrorName' | |
281 : (fromMixin ? 'Mixin introduces an $lcErrorName' : errorName); | |
282 return '$intro. The type of {0}.{1} ({2}) is not a ' | |
283 'subtype of {3}.{1} ({4}).'; | |
284 } | |
285 } | |
286 | |
287 class InvalidParameterDeclaration extends StaticError { | |
288 final DartType expectedType; | |
289 | |
290 InvalidParameterDeclaration( | |
291 TypeSystem rules, FormalParameter declaration, this.expectedType) | |
292 : super(declaration); | |
293 | |
294 @override | |
295 List<Object> get arguments => [node, expectedType]; | |
296 @override | |
297 String get message => 'Type check failed: {0} is not of type {1}'; | |
298 @override | |
299 String get name => 'STRONG_MODE_INVALID_PARAMETER_DECLARATION'; | |
300 } | |
301 | |
302 /// Dart constructors have one weird quirk, illustrated with this example: | |
303 /// | |
304 /// class Base { | |
305 /// var x; | |
306 /// Base() : x = print('Base.1') { | |
307 /// print('Base.2'); | |
308 /// } | |
309 /// } | |
310 /// | |
311 /// class Derived extends Base { | |
312 /// var y, z; | |
313 /// Derived() | |
314 /// : y = print('Derived.1'), | |
315 /// super(), | |
316 /// z = print('Derived.2') { | |
317 /// print('Derived.3'); | |
318 /// } | |
319 /// } | |
320 /// | |
321 /// The order will be Derived.1, Base.1, Derived.2, Base.2, Derived.3; this | |
322 /// ordering preserves the invariant that code can't observe uninitialized | |
323 /// state, however it results in super constructor body not being run | |
324 /// immediately after super initializers. Normally this isn't observable, but it | |
325 /// could be if initializers have side effects. | |
326 /// | |
327 /// Better to have `super` at the end, as required by the Dart style guide: | |
328 /// <https://goo.gl/EY6hDP> | |
329 /// | |
330 /// For now this is the only pattern we support. | |
331 class InvalidSuperInvocation extends StaticError { | |
332 InvalidSuperInvocation(SuperConstructorInvocation node) : super(node); | |
333 | |
334 @override | |
335 String get message => "super call must be last in an initializer " | |
336 "list (see https://goo.gl/EY6hDP): {0}"; | |
337 | |
338 @override | |
339 String get name => 'STRONG_MODE_INVALID_SUPER_INVOCATION'; | |
340 } | |
341 | |
342 class InvalidVariableDeclaration extends StaticError { | |
343 final DartType expectedType; | |
344 | |
345 InvalidVariableDeclaration( | |
346 TypeSystem rules, AstNode declaration, this.expectedType) | |
347 : super(declaration); | |
348 | |
349 @override | |
350 List<Object> get arguments => [expectedType]; | |
351 @override | |
352 String get message => 'Type check failed: null is not of type {0}'; | |
353 | |
354 @override | |
355 String get name => 'STRONG_MODE_INVALID_VARIABLE_DECLARATION'; | |
356 } | |
357 | |
358 class NonGroundTypeCheckInfo extends StaticInfo { | |
359 final DartType type; | |
360 final AstNode node; | |
361 | |
362 NonGroundTypeCheckInfo(this.node, this.type) { | |
363 assert(node is IsExpression || node is AsExpression); | |
364 } | |
365 | |
366 @override | |
367 List<Object> get arguments => [type]; | |
368 String get message => | |
369 "Runtime check on non-ground type {0} may throw StrongModeError"; | |
370 | |
371 @override | |
372 String get name => 'STRONG_MODE_NON_GROUND_TYPE_CHECK_INFO'; | |
373 | |
374 toErrorCode() => new HintCode(name, message); | |
375 } | |
376 | |
377 abstract class StaticError extends StaticInfo { | |
378 final AstNode node; | |
379 | |
380 StaticError(this.node); | |
381 | |
382 String get message; | |
383 | |
384 toErrorCode() => new CompileTimeErrorCode(name, message); | |
385 } | |
386 | |
387 // TODO(jmesserly): this could use some refactoring. These are essentially | |
388 // like ErrorCodes in analyzer, but we're including some details in our message. | |
389 // Analyzer instead has template strings, and replaces '{0}' with the first | |
390 // argument. | |
391 abstract class StaticInfo { | |
392 /// Strong-mode error code names. | |
393 /// | |
394 /// Used for error code configuration validation in an analysis options file. | |
395 static const List<String> names = const [ | |
396 // | |
397 // Manually populated. | |
398 // | |
399 'STRONG_MODE_ASSIGNMENT_CAST', | |
400 'STRONG_MODE_DOWN_CAST_COMPOSITE', | |
401 'STRONG_MODE_DOWN_CAST_IMPLICIT', | |
402 'STRONG_MODE_DYNAMIC_CAST', | |
403 'STRONG_MODE_DYNAMIC_INVOKE', | |
404 'STRONG_MODE_INFERRED_TYPE', | |
405 'STRONG_MODE_INFERRED_TYPE_ALLOCATION', | |
406 'STRONG_MODE_INFERRED_TYPE_CLOSURE', | |
407 'STRONG_MODE_INFERRED_TYPE_LITERAL', | |
408 'STRONG_MODE_INVALID_FIELD_OVERRIDE', | |
409 'STRONG_MODE_INVALID_METHOD_OVERRIDE', | |
410 'STRONG_MODE_INVALID_PARAMETER_DECLARATION', | |
411 'STRONG_MODE_INVALID_SUPER_INVOCATION', | |
412 'STRONG_MODE_INVALID_VARIABLE_DECLARATION', | |
413 'STRONG_MODE_NON_GROUND_TYPE_CHECK_INFO', | |
414 'STRONG_MODE_STATIC_TYPE_ERROR', | |
415 ]; | |
416 | |
417 List<Object> get arguments => [node]; | |
418 | |
419 String get name; | |
420 | |
421 /// AST Node this info is attached to. | |
422 AstNode get node; | |
423 | |
424 AnalysisError toAnalysisError() { | |
425 int begin = node is AnnotatedNode | |
426 ? (node as AnnotatedNode).firstTokenAfterCommentAndMetadata.offset | |
427 : node.offset; | |
428 int length = node.end - begin; | |
429 var source = (node.root as CompilationUnit).element.source; | |
430 return new AnalysisError(source, begin, length, toErrorCode(), arguments); | |
431 } | |
432 | |
433 // TODO(jmesserly): review the usage of error codes. We probably want our own, | |
434 // as well as some DDC specific [ErrorType]s. | |
435 ErrorCode toErrorCode(); | |
436 | |
437 static bool isKnownFunction(Expression expression) { | |
438 Element element = null; | |
439 if (expression is FunctionExpression) { | |
440 return true; | |
441 } else if (expression is PropertyAccess) { | |
442 element = expression.propertyName.staticElement; | |
443 } else if (expression is Identifier) { | |
444 element = expression.staticElement; | |
445 } | |
446 // First class functions and static methods, where we know the original | |
447 // declaration, will have an exact type, so we know a downcast will fail. | |
448 return element is FunctionElement || | |
449 element is MethodElement && element.isStatic; | |
450 } | |
451 } | |
452 | |
453 class StaticTypeError extends StaticError { | |
454 final DartType baseType; | |
455 final DartType expectedType; | |
456 | |
457 StaticTypeError(Expression expression, this.expectedType) | |
458 : baseType = expression.staticType ?? DynamicTypeImpl.instance, | |
459 super(expression); | |
460 | |
461 @override | |
462 List<Object> get arguments => [node, baseType, expectedType]; | |
463 @override | |
464 String get message => 'Type check failed: {0} ({1}) is not of type {2}'; | |
465 | |
466 @override | |
467 String get name => 'STRONG_MODE_STATIC_TYPE_ERROR'; | |
468 } | |
OLD | NEW |