| 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/src/generated/ast.dart'; | |
| 12 import 'package:analyzer/src/generated/element.dart'; | |
| 13 import 'package:analyzer/src/generated/error.dart'; | |
| 14 | |
| 15 import 'rules.dart'; | |
| 16 | |
| 17 // The abstract type of coercions mapping one type to another. | |
| 18 // This class also exposes static builder functions which | |
| 19 // check for errors and reduce redundant coercions to the identity. | |
| 20 abstract class Coercion { | |
| 21 final DartType fromType; | |
| 22 final DartType toType; | |
| 23 Coercion(this.fromType, this.toType); | |
| 24 static Coercion cast(DartType fromT, DartType toT) => new Cast(fromT, toT); | |
| 25 static Coercion identity(DartType type) => new Identity(type); | |
| 26 static Coercion error() => new CoercionError(); | |
| 27 } | |
| 28 | |
| 29 // Coercion which casts one type to another | |
| 30 class Cast extends Coercion { | |
| 31 Cast(DartType fromType, DartType toType) : super(fromType, toType); | |
| 32 } | |
| 33 | |
| 34 // The identity coercion | |
| 35 class Identity extends Coercion { | |
| 36 Identity(DartType fromType) : super(fromType, fromType); | |
| 37 } | |
| 38 | |
| 39 // The error coercion. This coercion signals that a coercion | |
| 40 // could not be generated. The code generator should not see | |
| 41 // these. | |
| 42 class CoercionError extends Coercion { | |
| 43 CoercionError() : super(null, null); | |
| 44 } | |
| 45 | |
| 46 // TODO(jmesserly): this could use some refactoring. These are essentially | |
| 47 // like ErrorCodes in analyzer, but we're including some details in our message. | |
| 48 // Analyzer instead has template strings, and replaces '{0}' with the first | |
| 49 // argument. | |
| 50 abstract class StaticInfo { | |
| 51 /// AST Node this info is attached to. | |
| 52 AstNode get node; | |
| 53 | |
| 54 // TODO(jmesserly): review the usage of error codes. We probably want our own, | |
| 55 // as well as some DDC specific [ErrorType]s. | |
| 56 ErrorCode toErrorCode(); | |
| 57 | |
| 58 // TODO(jmesserly): what convention to use here? | |
| 59 String get name => 'dev_compiler.$runtimeType'; | |
| 60 | |
| 61 List<Object> get arguments => [node]; | |
| 62 | |
| 63 AnalysisError toAnalysisError() { | |
| 64 int begin = node is AnnotatedNode | |
| 65 ? (node as AnnotatedNode).firstTokenAfterCommentAndMetadata.offset | |
| 66 : node.offset; | |
| 67 int length = node.end - begin; | |
| 68 var source = (node.root as CompilationUnit).element.source; | |
| 69 return new AnalysisError(source, begin, length, toErrorCode(), arguments); | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 /// Implicitly injected expression conversion. | |
| 74 abstract class CoercionInfo extends StaticInfo { | |
| 75 final TypeRules rules; | |
| 76 | |
| 77 final Expression node; | |
| 78 | |
| 79 DartType get convertedType; | |
| 80 | |
| 81 CoercionInfo(this.rules, this.node); | |
| 82 | |
| 83 DartType get baseType => rules.getStaticType(node); | |
| 84 DartType get staticType => convertedType; | |
| 85 | |
| 86 String get message; | |
| 87 toErrorCode() => new HintCode(name, message); | |
| 88 | |
| 89 static const String _propertyName = 'dev_compiler.src.info.CoercionInfo'; | |
| 90 | |
| 91 /// Gets the coercion info associated with this node. | |
| 92 static CoercionInfo get(AstNode node) => node.getProperty(_propertyName); | |
| 93 | |
| 94 /// Sets the coercion info associated with this node. | |
| 95 static CoercionInfo set(AstNode node, CoercionInfo info) { | |
| 96 node.setProperty(_propertyName, info); | |
| 97 return info; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 // Base class for all casts from base type to sub type. | |
| 102 abstract class DownCast extends CoercionInfo { | |
| 103 Cast _cast; | |
| 104 | |
| 105 DownCast._internal(TypeRules rules, Expression expression, this._cast) | |
| 106 : super(rules, expression) { | |
| 107 assert(_cast.toType != baseType && | |
| 108 _cast.fromType == baseType && | |
| 109 (baseType.isDynamic || | |
| 110 // Call methods make the following non-redundant | |
| 111 _cast.toType.isSubtypeOf(baseType) || | |
| 112 baseType.isAssignableTo(_cast.toType))); | |
| 113 } | |
| 114 | |
| 115 Cast get cast => _cast; | |
| 116 | |
| 117 DartType get convertedType => _cast.toType; | |
| 118 | |
| 119 @override List<Object> get arguments => [node, baseType, convertedType]; | |
| 120 @override String get message => '{0} ({1}) will need runtime check ' | |
| 121 'to cast to type {2}'; | |
| 122 | |
| 123 // Factory to create correct DownCast variant. | |
| 124 static StaticInfo create(TypeRules rules, Expression expression, Cast cast, | |
| 125 {String reason}) { | |
| 126 final fromT = cast.fromType; | |
| 127 final toT = cast.toType; | |
| 128 | |
| 129 // toT <:_R fromT => to <: fromT | |
| 130 // NB: classes with call methods are subtypes of function | |
| 131 // types, but the function type is not assignable to the class | |
| 132 assert(toT.isSubtypeOf(fromT) || fromT.isAssignableTo(toT)); | |
| 133 | |
| 134 // Handle null call specially. | |
| 135 if (expression is NullLiteral) { | |
| 136 // TODO(vsm): Create a NullCast for this once we revisit nonnullability. | |
| 137 return new DownCastImplicit(rules, expression, cast); | |
| 138 } | |
| 139 | |
| 140 // Inference "casts": | |
| 141 if (expression is Literal) { | |
| 142 // fromT should be an exact type - this will almost certainly fail at | |
| 143 // runtime. | |
| 144 return new StaticTypeError(rules, expression, toT, reason: reason); | |
| 145 } | |
| 146 if (expression is FunctionExpression) { | |
| 147 // fromT should be an exact type - this will almost certainly fail at | |
| 148 // runtime. | |
| 149 return new UninferredClosure(rules, expression, cast); | |
| 150 } | |
| 151 if (expression is InstanceCreationExpression) { | |
| 152 // fromT should be an exact type - this will almost certainly fail at | |
| 153 // runtime. | |
| 154 return new StaticTypeError(rules, expression, toT, reason: reason); | |
| 155 } | |
| 156 | |
| 157 // Composite cast: these are more likely to fail. | |
| 158 if (!rules.isGroundType(toT)) { | |
| 159 // This cast is (probably) due to our different treatment of dynamic. | |
| 160 // It may be more likely to fail at runtime. | |
| 161 return new DownCastComposite(rules, expression, cast); | |
| 162 } | |
| 163 | |
| 164 // Dynamic cast | |
| 165 if (fromT.isDynamic) { | |
| 166 return new DynamicCast(rules, expression, cast); | |
| 167 } | |
| 168 | |
| 169 // Assignment cast | |
| 170 var parent = expression.parent; | |
| 171 if (parent is VariableDeclaration && (parent.initializer == expression)) { | |
| 172 return new AssignmentCast(rules, expression, cast); | |
| 173 } | |
| 174 | |
| 175 // Other casts | |
| 176 return new DownCastImplicit(rules, expression, cast); | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 // | |
| 181 // Standard down casts. These casts are implicitly injected by the compiler. | |
| 182 // | |
| 183 | |
| 184 // A down cast from dynamic to T. | |
| 185 class DynamicCast extends DownCast { | |
| 186 DynamicCast(TypeRules rules, Expression expression, Cast cast) | |
| 187 : super._internal(rules, expression, cast); | |
| 188 | |
| 189 toErrorCode() => new HintCode(name, message); | |
| 190 } | |
| 191 | |
| 192 // A down cast due to a variable declaration to a ground type. E.g., | |
| 193 // T x = expr; | |
| 194 // where T is ground. We exclude non-ground types as these behave differently | |
| 195 // compared to standard Dart. | |
| 196 class AssignmentCast extends DownCast { | |
| 197 AssignmentCast(TypeRules rules, Expression expression, Cast cast) | |
| 198 : super._internal(rules, expression, cast); | |
| 199 | |
| 200 toErrorCode() => new HintCode(name, message); | |
| 201 } | |
| 202 | |
| 203 // | |
| 204 // Temporary "casts" of allocation sites - literals, constructor invocations, | |
| 205 // and closures. These should be handled by contextual inference. In most | |
| 206 // cases, inference will be sufficient, though in some it may unmask an actual | |
| 207 // error: e.g., | |
| 208 // List<int> l = [1, 2, 3]; // Inference succeeds | |
| 209 // List<String> l = [1, 2, 3]; // Inference reveals static type error | |
| 210 // We're marking all as warnings for now. | |
| 211 // | |
| 212 // TODO(vsm,leafp): Remove this. | |
| 213 class UninferredClosure extends DownCast { | |
| 214 UninferredClosure(TypeRules rules, FunctionExpression expression, Cast cast) | |
| 215 : super._internal(rules, expression, cast); | |
| 216 | |
| 217 toErrorCode() => new StaticTypeWarningCode(name, message); | |
| 218 } | |
| 219 | |
| 220 // | |
| 221 // Implicit down casts. These are only injected by the compiler by flag. | |
| 222 // | |
| 223 | |
| 224 // A down cast to a non-ground type. These behave differently from standard | |
| 225 // Dart and may be more likely to fail at runtime. | |
| 226 class DownCastComposite extends DownCast { | |
| 227 DownCastComposite(TypeRules rules, Expression expression, Cast cast) | |
| 228 : super._internal(rules, expression, cast); | |
| 229 | |
| 230 toErrorCode() => new StaticTypeWarningCode(name, message); | |
| 231 } | |
| 232 | |
| 233 // A down cast to a non-ground type. These behave differently from standard | |
| 234 // Dart and may be more likely to fail at runtime. | |
| 235 class DownCastImplicit extends DownCast { | |
| 236 DownCastImplicit(TypeRules rules, Expression expression, Cast cast) | |
| 237 : super._internal(rules, expression, cast); | |
| 238 | |
| 239 toErrorCode() => new HintCode(name, message); | |
| 240 } | |
| 241 | |
| 242 // An inferred type for the wrapped expression, which may need to be | |
| 243 // reified into the term | |
| 244 abstract class InferredTypeBase extends CoercionInfo { | |
| 245 final DartType _type; | |
| 246 | |
| 247 InferredTypeBase._internal(TypeRules rules, Expression expression, this._type) | |
| 248 : super(rules, expression); | |
| 249 | |
| 250 DartType get type => _type; | |
| 251 DartType get convertedType => type; | |
| 252 @override String get message => '{0} has inferred type {1}'; | |
| 253 @override List get arguments => [node, type]; | |
| 254 | |
| 255 toErrorCode() => new HintCode(name, message); | |
| 256 } | |
| 257 | |
| 258 // Standard / unspecialized inferred type | |
| 259 class InferredType extends InferredTypeBase { | |
| 260 InferredType(TypeRules rules, Expression expression, DartType type) | |
| 261 : super._internal(rules, expression, type); | |
| 262 | |
| 263 // Factory to create correct InferredType variant. | |
| 264 static InferredTypeBase create( | |
| 265 TypeRules rules, Expression expression, DartType type) { | |
| 266 // Specialized inference: | |
| 267 if (expression is Literal) { | |
| 268 return new InferredTypeLiteral(rules, expression, type); | |
| 269 } | |
| 270 if (expression is InstanceCreationExpression) { | |
| 271 return new InferredTypeAllocation(rules, expression, type); | |
| 272 } | |
| 273 if (expression is FunctionExpression) { | |
| 274 return new InferredTypeClosure(rules, expression, type); | |
| 275 } | |
| 276 return new InferredType(rules, expression, type); | |
| 277 } | |
| 278 } | |
| 279 | |
| 280 // An infered type for a literal expression. | |
| 281 class InferredTypeLiteral extends InferredTypeBase { | |
| 282 InferredTypeLiteral(TypeRules rules, Expression expression, DartType type) | |
| 283 : super._internal(rules, expression, type); | |
| 284 } | |
| 285 | |
| 286 // An inferred type for a non-literal allocation site. | |
| 287 class InferredTypeAllocation extends InferredTypeBase { | |
| 288 InferredTypeAllocation(TypeRules rules, Expression expression, DartType type) | |
| 289 : super._internal(rules, expression, type); | |
| 290 } | |
| 291 | |
| 292 // An inferred type for a closure expression | |
| 293 class InferredTypeClosure extends InferredTypeBase { | |
| 294 InferredTypeClosure(TypeRules rules, Expression expression, DartType type) | |
| 295 : super._internal(rules, expression, type); | |
| 296 } | |
| 297 | |
| 298 class DynamicInvoke extends CoercionInfo { | |
| 299 DynamicInvoke(TypeRules rules, Expression expression) | |
| 300 : super(rules, expression); | |
| 301 | |
| 302 DartType get convertedType => rules.provider.dynamicType; | |
| 303 String get message => '{0} requires dynamic invoke'; | |
| 304 toErrorCode() => new HintCode(name, message); | |
| 305 | |
| 306 static const String _propertyName = 'dev_compiler.src.info.DynamicInvoke'; | |
| 307 | |
| 308 /// Whether this [node] is the target of a dynamic operation. | |
| 309 static bool get(AstNode node) { | |
| 310 var value = node.getProperty(_propertyName); | |
| 311 return value != null ? value : false; | |
| 312 } | |
| 313 | |
| 314 /// Sets whether this node is the target of a dynamic operation. | |
| 315 static bool set(AstNode node, bool value) { | |
| 316 // Free the storage for things that aren't dynamic. | |
| 317 if (value == false) value = null; | |
| 318 node.setProperty(_propertyName, value); | |
| 319 return value; | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 abstract class StaticError extends StaticInfo { | |
| 324 final AstNode node; | |
| 325 | |
| 326 StaticError(this.node); | |
| 327 | |
| 328 String get message; | |
| 329 | |
| 330 toErrorCode() => new CompileTimeErrorCode(name, message); | |
| 331 } | |
| 332 | |
| 333 class StaticTypeError extends StaticError { | |
| 334 final DartType baseType; | |
| 335 final DartType expectedType; | |
| 336 String reason = null; | |
| 337 | |
| 338 StaticTypeError(TypeRules rules, Expression expression, this.expectedType, | |
| 339 {this.reason}) | |
| 340 : baseType = rules.getStaticType(expression), | |
| 341 super(expression); | |
| 342 | |
| 343 @override List<Object> get arguments => [node, baseType, expectedType]; | |
| 344 @override String get message => | |
| 345 'Type check failed: {0} ({1}) is not of type {2}' + | |
| 346 ((reason == null) ? '' : ' because $reason'); | |
| 347 } | |
| 348 | |
| 349 class InvalidVariableDeclaration extends StaticError { | |
| 350 final DartType expectedType; | |
| 351 | |
| 352 InvalidVariableDeclaration( | |
| 353 TypeRules rules, AstNode declaration, this.expectedType) | |
| 354 : super(declaration); | |
| 355 | |
| 356 @override List<Object> get arguments => [expectedType]; | |
| 357 @override String get message => 'Type check failed: null is not of type {0}'; | |
| 358 } | |
| 359 | |
| 360 class InvalidParameterDeclaration extends StaticError { | |
| 361 final DartType expectedType; | |
| 362 | |
| 363 InvalidParameterDeclaration( | |
| 364 TypeRules rules, FormalParameter declaration, this.expectedType) | |
| 365 : super(declaration); | |
| 366 | |
| 367 @override List<Object> get arguments => [node, expectedType]; | |
| 368 @override String get message => 'Type check failed: {0} is not of type {1}'; | |
| 369 } | |
| 370 | |
| 371 class NonGroundTypeCheckInfo extends StaticInfo { | |
| 372 final DartType type; | |
| 373 final AstNode node; | |
| 374 | |
| 375 NonGroundTypeCheckInfo(this.node, this.type) { | |
| 376 assert(node is IsExpression || node is AsExpression); | |
| 377 } | |
| 378 | |
| 379 @override List<Object> get arguments => [type]; | |
| 380 String get message => | |
| 381 "Runtime check on non-ground type {0} may throw StrongModeError"; | |
| 382 | |
| 383 toErrorCode() => new HintCode(name, message); | |
| 384 } | |
| 385 | |
| 386 // Invalid override of an instance member of a class. | |
| 387 abstract class InvalidOverride extends StaticError { | |
| 388 /// Member declaration with the invalid override. | |
| 389 final ExecutableElement element; | |
| 390 | |
| 391 /// Type (class or interface) that provides the base declaration. | |
| 392 final InterfaceType base; | |
| 393 | |
| 394 /// Actual type of the overridden member. | |
| 395 final DartType subType; | |
| 396 | |
| 397 /// Actual type of the base member. | |
| 398 final DartType baseType; | |
| 399 | |
| 400 /// Whether the error comes from combining a base class and an interface | |
| 401 final bool fromBaseClass; | |
| 402 | |
| 403 /// Whether the error comes from a mixin (either overriding a base class or an | |
| 404 /// interface declaration). | |
| 405 final bool fromMixin; | |
| 406 | |
| 407 InvalidOverride( | |
| 408 AstNode node, this.element, this.base, this.subType, this.baseType) | |
| 409 : fromBaseClass = node is ExtendsClause, | |
| 410 fromMixin = node.parent is WithClause, | |
| 411 super(node); | |
| 412 | |
| 413 ClassElement get parent => element.enclosingElement; | |
| 414 | |
| 415 @override List<Object> get arguments => | |
| 416 [parent.name, element.name, subType, base, baseType]; | |
| 417 | |
| 418 String _messageHelper(String errorName) { | |
| 419 var lcErrorName = errorName.toLowerCase(); | |
| 420 var intro = fromBaseClass | |
| 421 ? 'Base class introduces an $lcErrorName' | |
| 422 : (fromMixin ? 'Mixin introduces an $lcErrorName' : errorName); | |
| 423 return '$intro. The type of {0}.{1} ({2}) is not a ' | |
| 424 'subtype of {3}.{1} ({4}).'; | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 // Invalid override due to incompatible type. I.e., the overridden signature | |
| 429 // is not compatible with the original. | |
| 430 class InvalidMethodOverride extends InvalidOverride { | |
| 431 InvalidMethodOverride(AstNode node, ExecutableElement element, | |
| 432 InterfaceType base, FunctionType subType, FunctionType baseType) | |
| 433 : super(node, element, base, subType, baseType); | |
| 434 | |
| 435 String get message => _messageHelper('Invalid override'); | |
| 436 } | |
| 437 | |
| 438 /// Dart constructors have one weird quirk, illustrated with this example: | |
| 439 /// | |
| 440 /// class Base { | |
| 441 /// var x; | |
| 442 /// Base() : x = print('Base.1') { | |
| 443 /// print('Base.2'); | |
| 444 /// } | |
| 445 /// } | |
| 446 /// | |
| 447 /// class Derived extends Base { | |
| 448 /// var y, z; | |
| 449 /// Derived() | |
| 450 /// : y = print('Derived.1'), | |
| 451 /// super(), | |
| 452 /// z = print('Derived.2') { | |
| 453 /// print('Derived.3'); | |
| 454 /// } | |
| 455 /// } | |
| 456 /// | |
| 457 /// The order will be Derived.1, Base.1, Derived.2, Base.2, Derived.3; this | |
| 458 /// ordering preserves the invariant that code can't observe uninitialized | |
| 459 /// state, however it results in super constructor body not being run | |
| 460 /// immediately after super initializers. Normally this isn't observable, but it | |
| 461 /// could be if initializers have side effects. | |
| 462 /// | |
| 463 /// Better to have `super` at the end, as required by the Dart style guide: | |
| 464 /// <http://goo.gl/q1T4BB> | |
| 465 /// | |
| 466 /// For now this is the only pattern we support. | |
| 467 class InvalidSuperInvocation extends StaticError { | |
| 468 InvalidSuperInvocation(SuperConstructorInvocation node) : super(node); | |
| 469 | |
| 470 @override String get message => "super call must be last in an initializer " | |
| 471 "list (see http://goo.gl/q1T4BB): {0}"; | |
| 472 } | |
| OLD | NEW |