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 |