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