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

Side by Side Diff: pkg/analyzer/lib/src/task/strong/info.dart

Issue 2060013002: Refactor strong mode to use standard Analyzer errors (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: fix hasImplicitCasts on the CompilationUnit Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698