| 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 | 
|---|