Chromium Code Reviews| 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 library related_types; | |
|
sigurdm
2015/09/11 11:43:28
Do we want to also provide these hints outside dar
Johnni Winther
2015/09/11 12:31:22
Potentially, as an opt-in.
| |
| 6 | |
| 7 import 'package:compiler/src/compiler.dart'; | |
| 8 import 'package:compiler/src/core_types.dart'; | |
| 9 import 'package:compiler/src/dart_types.dart'; | |
| 10 import 'package:compiler/src/diagnostics/messages.dart'; | |
| 11 import 'package:compiler/src/elements/elements.dart'; | |
| 12 import 'package:compiler/src/resolution/semantic_visitor.dart'; | |
| 13 import 'package:compiler/src/tree/tree.dart'; | |
| 14 import 'package:compiler/src/universe/universe.dart'; | |
| 15 import 'package:compiler/src/world.dart'; | |
| 16 | |
| 17 /// Check all loader libraries in [compiler] for unrelated types. | |
|
sigurdm
2015/09/11 11:43:28
loader => loaded
Johnni Winther
2015/09/11 12:31:22
Done.
| |
| 18 void checkRelatedTypes(Compiler compiler) { | |
| 19 for (LibraryElement library in compiler.libraryLoader.libraries) { | |
| 20 checkLibraryElement(compiler, library); | |
| 21 } | |
| 22 } | |
| 23 | |
| 24 /// Check [library] for unrelated types. | |
| 25 void checkLibraryElement(Compiler compiler, LibraryElement library) { | |
| 26 library.forEachLocalMember((Element element) { | |
| 27 if (element.isClass) { | |
| 28 ClassElement cls = element; | |
| 29 cls.forEachLocalMember((MemberElement member) { | |
| 30 checkMemberElement(compiler, member); | |
| 31 }); | |
| 32 } else if (!element.isTypedef) { | |
| 33 checkMemberElement(compiler, element); | |
| 34 } | |
| 35 }); | |
| 36 } | |
| 37 | |
| 38 /// Check [member] for unrelated types. | |
| 39 void checkMemberElement(Compiler compiler, MemberElement member) { | |
| 40 if (!compiler.enqueuer.resolution.hasBeenResolved(member)) return; | |
| 41 | |
| 42 ResolvedAst resolvedAst = member.resolvedAst; | |
| 43 RelatedTypesChecker relatedTypesChecker = | |
| 44 new RelatedTypesChecker(compiler, resolvedAst); | |
| 45 if (resolvedAst.node != null) { | |
| 46 compiler.withCurrentElement(member.implementation, () { | |
| 47 relatedTypesChecker.apply(resolvedAst.node); | |
| 48 }); | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 class RelatedTypesChecker extends TraversalVisitor<DartType, dynamic> { | |
| 53 final Compiler compiler; | |
| 54 final ResolvedAst resolvedAst; | |
| 55 | |
| 56 RelatedTypesChecker(this.compiler, ResolvedAst resolvedAst) | |
| 57 : this.resolvedAst = resolvedAst, | |
| 58 super(resolvedAst.elements); | |
| 59 | |
| 60 ClassWorld get world => compiler.world; | |
| 61 | |
| 62 CoreTypes get coreTypes => compiler.coreTypes; | |
| 63 | |
| 64 InterfaceType get thisType => resolvedAst.element.enclosingClass.thisType; | |
| 65 | |
| 66 /// Returns `true` if there exists no common subtype of [left] and [right]. | |
| 67 bool hasEmptyIntersection(DartType left, DartType right) { | |
| 68 if (left == right) return false; | |
| 69 if (left == null || right == null) return false; | |
| 70 ClassElement leftClass = const ClassFinder().findClass(left); | |
| 71 ClassElement rightClass = const ClassFinder().findClass(right); | |
| 72 if (leftClass != null && rightClass != null) { | |
| 73 return !world.haveAnyCommonSubtypes(leftClass, rightClass); | |
| 74 } | |
| 75 return false; | |
| 76 } | |
| 77 | |
| 78 /// Checks that there exists a common subtype of [left] and [right] or report | |
| 79 /// a hint otherwise. | |
| 80 void checkRelated(Node node, DartType left, DartType right) { | |
| 81 if (hasEmptyIntersection(left, right)) { | |
| 82 compiler.reportHint( | |
| 83 node, MessageKind.NO_COMMON_SUBTYPES, {'left': left, 'right': right}); | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 /// Check weakly typed collection methods, like `Map.containsKey`, | |
| 88 /// `Map.containsValue` and `Iterable.contains`. | |
| 89 void checkDynamicInvoke( | |
| 90 Node node, | |
| 91 DartType receiverType, | |
| 92 List<DartType> argumentTypes, | |
| 93 Selector selector) { | |
| 94 if (selector.name == 'containsKey' && | |
| 95 selector.callStructure == CallStructure.ONE_ARG) { | |
| 96 InterfaceType mapType = findMapType(receiverType); | |
| 97 if (mapType != null) { | |
| 98 DartType keyType = findMapKeyType(mapType); | |
| 99 checkRelated(node, keyType, argumentTypes.first); | |
| 100 } | |
| 101 } else if (selector.name == 'containsValue' && | |
| 102 selector.callStructure == CallStructure.ONE_ARG) { | |
| 103 InterfaceType mapType = findMapType(receiverType); | |
| 104 if (mapType != null) { | |
| 105 DartType valueType = findMapValueType(mapType); | |
| 106 checkRelated(node, valueType, argumentTypes.first); | |
| 107 } | |
| 108 } else if (selector.name == 'contains' && | |
| 109 selector.callStructure == CallStructure.ONE_ARG) { | |
| 110 InterfaceType iterableType = findIterableType(receiverType); | |
| 111 if (iterableType != null) { | |
| 112 DartType elementType = findIterableElementType(iterableType); | |
| 113 checkRelated(node, elementType, argumentTypes.first); | |
| 114 } | |
| 115 } else if (selector.name == 'remove' && | |
| 116 selector.callStructure == CallStructure.ONE_ARG) { | |
| 117 InterfaceType mapType = findMapType(receiverType); | |
| 118 if (mapType != null) { | |
| 119 DartType keyType = findMapKeyType(mapType); | |
| 120 checkRelated(node, keyType, argumentTypes.first); | |
| 121 } | |
| 122 InterfaceType listType = findListType(receiverType); | |
| 123 if (listType != null) { | |
| 124 DartType valueType = findListElementType(listType); | |
| 125 checkRelated(node, valueType, argumentTypes.first); | |
| 126 } | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 /// Return the interface type implemented by [type] or `null` if no interface | |
| 131 /// type is implied by [type]. | |
| 132 InterfaceType findInterfaceType(DartType type) { | |
| 133 if (type.treatAsDynamic) return null; | |
| 134 type = Types.computeUnaliasedBound(compiler, type); | |
| 135 if (type.treatAsDynamic) return null; | |
| 136 return Types.computeInterfaceType(compiler, type); | |
| 137 } | |
| 138 | |
| 139 /// Returns the supertype of [receiver] that implements [cls], if any. | |
| 140 InterfaceType findClassType(DartType receiver, ClassElement cls) { | |
| 141 InterfaceType interfaceType = findInterfaceType(receiver); | |
| 142 if (interfaceType == null) return null; | |
| 143 InterfaceType mapType = interfaceType.asInstanceOf(cls); | |
| 144 if (mapType == null) return null; | |
| 145 return mapType; | |
| 146 } | |
| 147 | |
| 148 /// Returns the supertype of [receiver] that implements `Iterable`, if any. | |
| 149 InterfaceType findIterableType(DartType receiver) { | |
| 150 return findClassType(receiver, compiler.iterableClass); | |
| 151 } | |
| 152 | |
| 153 /// Returns the element type of the supertype of [receiver] that implements | |
| 154 /// `Iterable`, if any. | |
| 155 DartType findIterableElementType(InterfaceType iterableType) { | |
| 156 if (iterableType == null) return null; | |
| 157 return iterableType.typeArguments[0]; | |
| 158 } | |
| 159 | |
| 160 /// Returns the supertype of [receiver] that implements `Map`, if any. | |
| 161 InterfaceType findMapType(DartType receiver) { | |
| 162 return findClassType(receiver, compiler.mapClass); | |
| 163 } | |
| 164 | |
| 165 /// Returns the key type of the supertype of [receiver] that implements | |
| 166 /// `Map`, if any. | |
| 167 DartType findMapKeyType(InterfaceType mapType) { | |
| 168 if (mapType == null) return null; | |
| 169 return mapType.typeArguments[0]; | |
| 170 } | |
| 171 | |
| 172 /// Returns the value type of the supertype of [receiver] that implements | |
| 173 /// `Map`, if any. | |
| 174 DartType findMapValueType(InterfaceType mapType) { | |
| 175 if (mapType == null) return null; | |
| 176 return mapType.typeArguments[1]; | |
| 177 } | |
| 178 | |
| 179 /// Returns the supertype of [receiver] that implements `List`, if any. | |
| 180 InterfaceType findListType(DartType receiver) { | |
| 181 return findClassType(receiver, compiler.listClass); | |
| 182 } | |
| 183 | |
| 184 /// Returns the element type of the supertype of [receiver] that implements | |
| 185 /// `List`, if any. | |
| 186 DartType findListElementType(InterfaceType listType) { | |
| 187 if (listType == null) return null; | |
| 188 return listType.typeArguments[0]; | |
| 189 } | |
| 190 | |
| 191 /// Returns the implied return type of [type] or `dynamic` if no return type | |
| 192 /// is implied. | |
| 193 DartType findReturnType(DartType type) { | |
| 194 if (type is FunctionType) { | |
| 195 return type.returnType; | |
| 196 } | |
| 197 return const DynamicType(); | |
| 198 } | |
| 199 | |
| 200 /// Visits [arguments] and returns the list of their corresponding types. | |
| 201 List<DartType> findArgumentTypes(NodeList arguments) { | |
| 202 List<DartType> argumentTypes = <DartType>[]; | |
| 203 for (Node argument in arguments) { | |
| 204 argumentTypes.add(apply(argument)); | |
| 205 } | |
| 206 return argumentTypes; | |
| 207 } | |
| 208 | |
| 209 /// Finds the [MemberSignature] of the [name] property on [type], if any. | |
| 210 MemberSignature lookupInterfaceMember(DartType type, Name name) { | |
| 211 InterfaceType interfaceType = findInterfaceType(type); | |
| 212 if (interfaceType == null) return null; | |
| 213 return interfaceType.lookupInterfaceMember(name); | |
| 214 } | |
| 215 | |
| 216 /// Returns the type of an access of the [name] property on [type], or | |
| 217 /// `dynamic` if no property was found. | |
| 218 DartType lookupInterfaceMemberAccessType(DartType type, Name name) { | |
| 219 MemberSignature member = lookupInterfaceMember(type, name); | |
| 220 if (member == null) return const DynamicType(); | |
| 221 return member.type; | |
| 222 } | |
| 223 | |
| 224 /// Returns the function type of the [name] property on [type], or | |
| 225 /// `dynamic` if no property was found. | |
| 226 FunctionType lookupInterfaceMemberInvocationType(DartType type, Name name) { | |
| 227 MemberSignature member = lookupInterfaceMember(type, name); | |
| 228 if (member == null) return null; | |
| 229 return member.functionType; | |
| 230 } | |
| 231 | |
| 232 DartType apply(Node node, [_]) { | |
| 233 DartType type = node.accept(this); | |
| 234 if (type == null) { | |
| 235 type = const DynamicType(); | |
| 236 } | |
| 237 return type; | |
| 238 } | |
| 239 | |
| 240 @override | |
| 241 DartType visitEquals(Send node, Node left, Node right, _) { | |
| 242 DartType leftType = apply(left); | |
| 243 DartType rightType = apply(right); | |
| 244 checkRelated(node, leftType, rightType); | |
| 245 return coreTypes.boolType; | |
| 246 } | |
| 247 | |
| 248 @override | |
| 249 DartType visitNotEquals(Send node, Node left, Node right, _) { | |
| 250 DartType leftType = apply(left); | |
| 251 DartType rightType = apply(right); | |
| 252 checkRelated(node, leftType, rightType); | |
| 253 return coreTypes.boolType; | |
| 254 } | |
| 255 | |
| 256 @override | |
| 257 DartType visitIndex(Send node, Node receiver, Node index, _) { | |
| 258 DartType receiverType = apply(receiver); | |
| 259 DartType indexType = apply(index); | |
| 260 InterfaceType mapType = findMapType(receiverType); | |
| 261 DartType keyType = findMapKeyType(mapType); | |
| 262 DartType valueType = findMapValueType(mapType); | |
| 263 checkRelated(index, keyType, indexType); | |
| 264 return valueType; | |
| 265 } | |
| 266 | |
| 267 @override | |
| 268 DartType visitLiteralInt(LiteralInt node) { | |
| 269 return coreTypes.intType; | |
| 270 } | |
| 271 | |
| 272 @override | |
| 273 DartType visitLiteralString(LiteralString node) { | |
| 274 return coreTypes.stringType; | |
| 275 } | |
| 276 | |
| 277 @override | |
| 278 DartType visitLiteralBool(LiteralBool node) { | |
| 279 return coreTypes.boolType; | |
| 280 } | |
| 281 | |
| 282 @override | |
| 283 DartType visitLiteralMap(LiteralMap node) { | |
| 284 return elements.getType(node); | |
| 285 } | |
| 286 | |
| 287 @override | |
| 288 DartType visitLiteralList(LiteralList node) { | |
| 289 return elements.getType(node); | |
| 290 } | |
| 291 | |
| 292 @override | |
| 293 DartType visitLiteralNull(LiteralNull node) { | |
| 294 return elements.getType(node); | |
| 295 } | |
| 296 | |
| 297 @override | |
| 298 DartType visitLocalVariableGet(Send node, LocalVariableElement variable, _) { | |
| 299 return variable.type; | |
| 300 } | |
| 301 | |
| 302 @override | |
| 303 DartType visitLocalFunctionGet(Send node, LocalFunctionElement function, _) { | |
| 304 return function.type; | |
| 305 } | |
| 306 | |
| 307 @override | |
| 308 DartType visitParameterGet(Send node, ParameterElement parameter, _) { | |
| 309 return parameter.type; | |
| 310 } | |
| 311 | |
| 312 @override | |
| 313 DartType visitThisPropertyGet(Send node, Name name, _) { | |
| 314 return lookupInterfaceMemberAccessType(thisType, name); | |
| 315 } | |
| 316 | |
| 317 @override | |
| 318 DartType visitDynamicPropertyGet(Send node, Node receiver, Name name, _) { | |
| 319 DartType receiverType = apply(receiver); | |
| 320 return lookupInterfaceMemberAccessType(receiverType, name); | |
| 321 } | |
| 322 | |
| 323 @override | |
| 324 DartType visitIfNotNullDynamicPropertyGet( | |
| 325 Send node, Node receiver, Name name, _) { | |
| 326 DartType receiverType = apply(receiver); | |
| 327 return lookupInterfaceMemberAccessType(receiverType, name); | |
| 328 } | |
| 329 | |
| 330 @override | |
| 331 DartType visitStaticFieldGet(Send node, FieldElement field, _) { | |
| 332 return field.type; | |
| 333 } | |
| 334 | |
| 335 @override | |
| 336 DartType visitTopLevelFieldGet(Send node, FieldElement field, _) { | |
| 337 return field.type; | |
| 338 } | |
| 339 | |
| 340 @override | |
| 341 DartType visitDynamicPropertyInvoke( | |
| 342 Send node, | |
| 343 Node receiver, | |
| 344 NodeList arguments, | |
| 345 Selector selector, _) { | |
| 346 DartType receiverType = apply(receiver); | |
| 347 List<DartType> argumentTypes = findArgumentTypes(arguments); | |
| 348 FunctionType methodType = lookupInterfaceMemberInvocationType( | |
| 349 receiverType, selector.memberName); | |
| 350 checkDynamicInvoke(node, receiverType, argumentTypes, selector); | |
| 351 return findReturnType(methodType); | |
| 352 } | |
| 353 | |
| 354 @override | |
| 355 DartType visitThisPropertyInvoke( | |
| 356 Send node, | |
| 357 NodeList arguments, | |
| 358 Selector selector, _) { | |
| 359 DartType receiverType = thisType; | |
| 360 List<DartType> argumentTypes = findArgumentTypes(arguments); | |
| 361 FunctionType methodType = lookupInterfaceMemberInvocationType( | |
| 362 receiverType, selector.memberName); | |
| 363 checkDynamicInvoke(node, receiverType, argumentTypes, selector); | |
| 364 return findReturnType(methodType); | |
| 365 } | |
| 366 | |
| 367 @override | |
| 368 DartType visitIfNotNullDynamicPropertyInvoke( | |
| 369 Send node, | |
| 370 Node receiver, | |
| 371 NodeList arguments, | |
| 372 Selector selector, _) { | |
| 373 DartType receiverType = apply(receiver); | |
| 374 List<DartType> argumentTypes = findArgumentTypes(arguments); | |
| 375 FunctionType methodType = lookupInterfaceMemberInvocationType( | |
| 376 receiverType, selector.memberName); | |
| 377 checkDynamicInvoke(node, receiverType, argumentTypes, selector); | |
| 378 return findReturnType(methodType); | |
| 379 } | |
| 380 | |
| 381 @override | |
| 382 DartType visitTopLevelFunctionInvoke( | |
| 383 Send node, | |
| 384 MethodElement function, | |
| 385 NodeList arguments, | |
| 386 CallStructure callStructure, _) { | |
| 387 apply(arguments); | |
| 388 return findReturnType(function.type); | |
| 389 } | |
| 390 | |
| 391 @override | |
| 392 DartType visitStaticFunctionInvoke( | |
| 393 Send node, | |
| 394 MethodElement function, | |
| 395 NodeList arguments, | |
| 396 CallStructure callStructure, _) { | |
| 397 apply(arguments); | |
| 398 return findReturnType(function.type); | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 /// Computes the [ClassElement] implied by a type. | |
| 403 // TODO(johnniwinther): Handle type variables, function types and typedefs. | |
| 404 class ClassFinder extends BaseDartTypeVisitor<ClassElement, dynamic> { | |
| 405 const ClassFinder(); | |
| 406 | |
| 407 ClassElement findClass(DartType type) => type.accept(this, null); | |
| 408 | |
| 409 @override | |
| 410 ClassElement visitType(DartType type, _) => null; | |
| 411 | |
| 412 @override | |
| 413 ClassElement visitInterfaceType(InterfaceType type, _) { | |
| 414 return type.element; | |
| 415 } | |
| 416 } | |
| OLD | NEW |