| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be | 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be |
| 6 // refactored to fit into analyzer. | 6 // refactored to fit into analyzer. |
| 7 library analyzer.src.task.strong.checker; | 7 library analyzer.src.task.strong.checker; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart'; | 9 import 'package:analyzer/analyzer.dart'; |
| 10 import 'package:analyzer/src/generated/ast.dart'; | 10 import 'package:analyzer/src/generated/ast.dart'; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 44 var parent = type.superclass; | 44 var parent = type.superclass; |
| 45 var mixins = type.mixins; | 45 var mixins = type.mixins; |
| 46 | 46 |
| 47 // Check overrides from applying mixins | 47 // Check overrides from applying mixins |
| 48 for (int i = 0; i < mixins.length; i++) { | 48 for (int i = 0; i < mixins.length; i++) { |
| 49 var seen = new Set<String>(); | 49 var seen = new Set<String>(); |
| 50 var current = mixins[i]; | 50 var current = mixins[i]; |
| 51 var errorLocation = node.withClause.mixinTypes[i]; | 51 var errorLocation = node.withClause.mixinTypes[i]; |
| 52 for (int j = i - 1; j >= 0; j--) { | 52 for (int j = i - 1; j >= 0; j--) { |
| 53 _checkIndividualOverridesFromType( | 53 _checkIndividualOverridesFromType( |
| 54 current, mixins[j], errorLocation, seen); | 54 current, mixins[j], errorLocation, seen, true); |
| 55 } | 55 } |
| 56 _checkIndividualOverridesFromType(current, parent, errorLocation, seen); | 56 _checkIndividualOverridesFromType( |
| 57 current, parent, errorLocation, seen, true); |
| 57 } | 58 } |
| 58 } | 59 } |
| 59 | 60 |
| 60 /// Check overrides between a class and its superclasses and mixins. For | 61 /// Check overrides between a class and its superclasses and mixins. For |
| 61 /// example, in: | 62 /// example, in: |
| 62 /// | 63 /// |
| 63 /// A extends B with E, F | 64 /// A extends B with E, F |
| 64 /// | 65 /// |
| 65 /// we check A against B, B super classes, E, and F. | 66 /// we check A against B, B super classes, E, and F. |
| 66 /// | 67 /// |
| (...skipping 11 matching lines...) Expand all Loading... |
| 78 /// } | 79 /// } |
| 79 /// class Test extends Parent { | 80 /// class Test extends Parent { |
| 80 /// m(B a) {} // invalid override | 81 /// m(B a) {} // invalid override |
| 81 /// } | 82 /// } |
| 82 void _checkSuperOverrides(ClassDeclaration node) { | 83 void _checkSuperOverrides(ClassDeclaration node) { |
| 83 var seen = new Set<String>(); | 84 var seen = new Set<String>(); |
| 84 var current = node.element.type; | 85 var current = node.element.type; |
| 85 var visited = new Set<InterfaceType>(); | 86 var visited = new Set<InterfaceType>(); |
| 86 do { | 87 do { |
| 87 visited.add(current); | 88 visited.add(current); |
| 88 current.mixins.reversed | 89 current.mixins.reversed.forEach( |
| 89 .forEach((m) => _checkIndividualOverridesFromClass(node, m, seen)); | 90 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); |
| 90 _checkIndividualOverridesFromClass(node, current.superclass, seen); | 91 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); |
| 91 current = current.superclass; | 92 current = current.superclass; |
| 92 } while (!current.isObject && !visited.contains(current)); | 93 } while (!current.isObject && !visited.contains(current)); |
| 93 } | 94 } |
| 94 | 95 |
| 95 /// Checks that implementations correctly override all reachable interfaces. | 96 /// Checks that implementations correctly override all reachable interfaces. |
| 96 /// In particular, we need to check these overrides for the definitions in | 97 /// In particular, we need to check these overrides for the definitions in |
| 97 /// the class itself and each its superclasses. If a superclass is not | 98 /// the class itself and each its superclasses. If a superclass is not |
| 98 /// abstract, then we can skip its transitive interfaces. For example, in: | 99 /// abstract, then we can skip its transitive interfaces. For example, in: |
| 99 /// | 100 /// |
| 100 /// B extends C implements G | 101 /// B extends C implements G |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 } else if (visited.contains(type)) { | 165 } else if (visited.contains(type)) { |
| 165 // Malformed type. | 166 // Malformed type. |
| 166 return; | 167 return; |
| 167 } else { | 168 } else { |
| 168 visited.add(type); | 169 visited.add(type); |
| 169 } | 170 } |
| 170 | 171 |
| 171 // Check direct overrides on [type] | 172 // Check direct overrides on [type] |
| 172 for (var interfaceType in interfaces) { | 173 for (var interfaceType in interfaces) { |
| 173 if (node != null) { | 174 if (node != null) { |
| 174 _checkIndividualOverridesFromClass(node, interfaceType, seen); | 175 _checkIndividualOverridesFromClass(node, interfaceType, seen, false); |
| 175 } else { | 176 } else { |
| 176 _checkIndividualOverridesFromType( | 177 _checkIndividualOverridesFromType( |
| 177 type, interfaceType, errorLocation, seen); | 178 type, interfaceType, errorLocation, seen, false); |
| 178 } | 179 } |
| 179 } | 180 } |
| 180 | 181 |
| 181 // Check overrides from its mixins | 182 // Check overrides from its mixins |
| 182 for (int i = 0; i < type.mixins.length; i++) { | 183 for (int i = 0; i < type.mixins.length; i++) { |
| 183 var loc = | 184 var loc = |
| 184 errorLocation != null ? errorLocation : node.withClause.mixinTypes[i]; | 185 errorLocation != null ? errorLocation : node.withClause.mixinTypes[i]; |
| 185 for (var interfaceType in interfaces) { | 186 for (var interfaceType in interfaces) { |
| 186 // We copy [seen] so we can report separately if more than one mixin or | 187 // We copy [seen] so we can report separately if more than one mixin or |
| 187 // the base class have an invalid override. | 188 // the base class have an invalid override. |
| 188 _checkIndividualOverridesFromType( | 189 _checkIndividualOverridesFromType( |
| 189 type.mixins[i], interfaceType, loc, new Set.from(seen)); | 190 type.mixins[i], interfaceType, loc, new Set.from(seen), false); |
| 190 } | 191 } |
| 191 } | 192 } |
| 192 | 193 |
| 193 // Check overrides from its superclasses | 194 // Check overrides from its superclasses |
| 194 if (includeParents) { | 195 if (includeParents) { |
| 195 var parent = type.superclass; | 196 var parent = type.superclass; |
| 196 if (parent.isObject) return; | 197 if (parent.isObject) return; |
| 197 var loc = errorLocation != null ? errorLocation : node.extendsClause; | 198 var loc = errorLocation != null ? errorLocation : node.extendsClause; |
| 198 // No need to copy [seen] here because we made copies above when reporting | 199 // No need to copy [seen] here because we made copies above when reporting |
| 199 // errors on mixins. | 200 // errors on mixins. |
| 200 _checkInterfacesOverrides(parent, interfaces, seen, | 201 _checkInterfacesOverrides(parent, interfaces, seen, |
| 201 visited: visited, includeParents: true, errorLocation: loc); | 202 visited: visited, includeParents: true, errorLocation: loc); |
| 202 } | 203 } |
| 203 } | 204 } |
| 204 | 205 |
| 205 /// Check that individual methods and fields in [subType] correctly override | 206 /// Check that individual methods and fields in [subType] correctly override |
| 206 /// the declarations in [baseType]. | 207 /// the declarations in [baseType]. |
| 207 /// | 208 /// |
| 208 /// The [errorLocation] node indicates where errors are reported, see | 209 /// The [errorLocation] node indicates where errors are reported, see |
| 209 /// [_checkSingleOverride] for more details. | 210 /// [_checkSingleOverride] for more details. |
| 210 /// | 211 /// |
| 211 /// The set [seen] is used to avoid reporting overrides more than once. It | 212 /// The set [seen] is used to avoid reporting overrides more than once. It |
| 212 /// is used when invoking this function multiple times when checking several | 213 /// is used when invoking this function multiple times when checking several |
| 213 /// types in a class hierarchy. Errors are reported only the first time an | 214 /// types in a class hierarchy. Errors are reported only the first time an |
| 214 /// invalid override involving a specific member is encountered. | 215 /// invalid override involving a specific member is encountered. |
| 215 _checkIndividualOverridesFromType(InterfaceType subType, | 216 _checkIndividualOverridesFromType( |
| 216 InterfaceType baseType, AstNode errorLocation, Set<String> seen) { | 217 InterfaceType subType, |
| 218 InterfaceType baseType, |
| 219 AstNode errorLocation, |
| 220 Set<String> seen, |
| 221 bool isSubclass) { |
| 217 void checkHelper(ExecutableElement e) { | 222 void checkHelper(ExecutableElement e) { |
| 218 if (e.isStatic) return; | 223 if (e.isStatic) return; |
| 219 if (seen.contains(e.name)) return; | 224 if (seen.contains(e.name)) return; |
| 220 if (_checkSingleOverride(e, baseType, null, errorLocation)) { | 225 if (_checkSingleOverride(e, baseType, null, errorLocation, isSubclass)) { |
| 221 seen.add(e.name); | 226 seen.add(e.name); |
| 222 } | 227 } |
| 223 } | 228 } |
| 224 subType.methods.forEach(checkHelper); | 229 subType.methods.forEach(checkHelper); |
| 225 subType.accessors.forEach(checkHelper); | 230 subType.accessors.forEach(checkHelper); |
| 226 } | 231 } |
| 227 | 232 |
| 228 /// Check that individual methods and fields in [subType] correctly override | 233 /// Check that individual methods and fields in [subType] correctly override |
| 229 /// the declarations in [baseType]. | 234 /// the declarations in [baseType]. |
| 230 /// | 235 /// |
| 231 /// The [errorLocation] node indicates where errors are reported, see | 236 /// The [errorLocation] node indicates where errors are reported, see |
| 232 /// [_checkSingleOverride] for more details. | 237 /// [_checkSingleOverride] for more details. |
| 233 _checkIndividualOverridesFromClass( | 238 _checkIndividualOverridesFromClass(ClassDeclaration node, |
| 234 ClassDeclaration node, InterfaceType baseType, Set<String> seen) { | 239 InterfaceType baseType, Set<String> seen, bool isSubclass) { |
| 235 for (var member in node.members) { | 240 for (var member in node.members) { |
| 236 if (member is ConstructorDeclaration) continue; | 241 if (member is ConstructorDeclaration) continue; |
| 237 if (member is FieldDeclaration) { | 242 if (member is FieldDeclaration) { |
| 238 if (member.isStatic) continue; | 243 if (member.isStatic) continue; |
| 239 for (var variable in member.fields.variables) { | 244 for (var variable in member.fields.variables) { |
| 240 var element = variable.element as PropertyInducingElement; | 245 var element = variable.element as PropertyInducingElement; |
| 241 var name = element.name; | 246 var name = element.name; |
| 242 if (seen.contains(name)) continue; | 247 if (seen.contains(name)) continue; |
| 243 var getter = element.getter; | 248 var getter = element.getter; |
| 244 var setter = element.setter; | 249 var setter = element.setter; |
| 245 bool found = _checkSingleOverride(getter, baseType, variable, member); | 250 bool found = _checkSingleOverride( |
| 251 getter, baseType, variable, member, isSubclass); |
| 246 if (!variable.isFinal && | 252 if (!variable.isFinal && |
| 247 !variable.isConst && | 253 !variable.isConst && |
| 248 _checkSingleOverride(setter, baseType, variable, member)) { | 254 _checkSingleOverride( |
| 255 setter, baseType, variable, member, isSubclass)) { |
| 249 found = true; | 256 found = true; |
| 250 } | 257 } |
| 251 if (found) seen.add(name); | 258 if (found) seen.add(name); |
| 252 } | 259 } |
| 253 } else { | 260 } else { |
| 254 if ((member as MethodDeclaration).isStatic) continue; | 261 if ((member as MethodDeclaration).isStatic) continue; |
| 255 var method = (member as MethodDeclaration).element; | 262 var method = (member as MethodDeclaration).element; |
| 256 if (seen.contains(method.name)) continue; | 263 if (seen.contains(method.name)) continue; |
| 257 if (_checkSingleOverride(method, baseType, member, member)) { | 264 if (_checkSingleOverride( |
| 265 method, baseType, member, member, isSubclass)) { |
| 258 seen.add(method.name); | 266 seen.add(method.name); |
| 259 } | 267 } |
| 260 } | 268 } |
| 261 } | 269 } |
| 262 } | 270 } |
| 263 | 271 |
| 264 /// Checks that [element] correctly overrides its corresponding member in | 272 /// Checks that [element] correctly overrides its corresponding member in |
| 265 /// [type]. Returns `true` if an override was found, that is, if [element] has | 273 /// [type]. Returns `true` if an override was found, that is, if [element] has |
| 266 /// a corresponding member in [type] that it overrides. | 274 /// a corresponding member in [type] that it overrides. |
| 267 /// | 275 /// |
| (...skipping 13 matching lines...) Expand all Loading... |
| 281 /// | 289 /// |
| 282 /// error: mixin introduces an invalid override. The type of C.foo is not | 290 /// error: mixin introduces an invalid override. The type of C.foo is not |
| 283 /// a subtype of E.foo: | 291 /// a subtype of E.foo: |
| 284 /// class A extends B with C implements E { ... } | 292 /// class A extends B with C implements E { ... } |
| 285 /// ^ | 293 /// ^ |
| 286 /// | 294 /// |
| 287 /// When checking for overrides from a type and it's super types, [node] is | 295 /// When checking for overrides from a type and it's super types, [node] is |
| 288 /// the AST node that defines [element]. This is used to determine whether the | 296 /// the AST node that defines [element]. This is used to determine whether the |
| 289 /// type of the element could be inferred from the types in the super classes. | 297 /// type of the element could be inferred from the types in the super classes. |
| 290 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, | 298 bool _checkSingleOverride(ExecutableElement element, InterfaceType type, |
| 291 AstNode node, AstNode errorLocation) { | 299 AstNode node, AstNode errorLocation, bool isSubclass) { |
| 292 assert(!element.isStatic); | 300 assert(!element.isStatic); |
| 293 | 301 |
| 294 FunctionType subType = _rules.elementType(element); | 302 FunctionType subType = _rules.elementType(element); |
| 295 // TODO(vsm): Test for generic | 303 // TODO(vsm): Test for generic |
| 296 FunctionType baseType = _getMemberType(type, element); | 304 FunctionType baseType = _getMemberType(type, element); |
| 305 if (baseType == null) return false; |
| 297 | 306 |
| 298 if (baseType == null) return false; | 307 if (isSubclass && element is PropertyAccessorElement) { |
| 308 // Disallow any overriding if the base class defines this member |
| 309 // as a field. We effectively treat fields as final / non-virtual. |
| 310 PropertyInducingElement field = _getMemberField(type, element); |
| 311 if (field != null) { |
| 312 _recordMessage(new InvalidFieldOverride( |
| 313 errorLocation, element, type, subType, baseType)); |
| 314 } |
| 315 } |
| 299 if (!_rules.isAssignable(subType, baseType)) { | 316 if (!_rules.isAssignable(subType, baseType)) { |
| 300 // See whether non-assignable cases fit one of our common patterns: | 317 // See whether non-assignable cases fit one of our common patterns: |
| 301 // | 318 // |
| 302 // Common pattern 1: Inferable return type (on getters and methods) | 319 // Common pattern 1: Inferable return type (on getters and methods) |
| 303 // class A { | 320 // class A { |
| 304 // int get foo => ...; | 321 // int get foo => ...; |
| 305 // String toString() { ... } | 322 // String toString() { ... } |
| 306 // } | 323 // } |
| 307 // class B extends A { | 324 // class B extends A { |
| 308 // get foo => e; // no type specified. | 325 // get foo => e; // no type specified. |
| (...skipping 632 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 941 // TODO(jmesserly): if we're run again on the same AST, we'll produce the | 958 // TODO(jmesserly): if we're run again on the same AST, we'll produce the |
| 942 // same annotations. This should be harmless. This might go away once | 959 // same annotations. This should be harmless. This might go away once |
| 943 // CodeChecker is integrated better with analyzer, as it will know that | 960 // CodeChecker is integrated better with analyzer, as it will know that |
| 944 // checking has already been performed. | 961 // checking has already been performed. |
| 945 // assert(CoercionInfo.get(info.node) == null); | 962 // assert(CoercionInfo.get(info.node) == null); |
| 946 CoercionInfo.set(info.node, info); | 963 CoercionInfo.set(info.node, info); |
| 947 } | 964 } |
| 948 } | 965 } |
| 949 } | 966 } |
| 950 | 967 |
| 968 // Return the field on type corresponding to member, or null if none |
| 969 // exists or the "field" is actually a getter/setter. |
| 970 PropertyInducingElement _getMemberField( |
| 971 InterfaceType type, PropertyAccessorElement member) { |
| 972 String memberName = member.name; |
| 973 PropertyInducingElement field; |
| 974 if (member.isGetter) { |
| 975 // The subclass member is an explicit getter or a field |
| 976 // - lookup the getter on the superclass. |
| 977 var getter = type.getGetter(memberName); |
| 978 if (getter == null || getter.isStatic) return null; |
| 979 field = getter.variable; |
| 980 } else if (!member.isSynthetic) { |
| 981 // The subclass member is an explicit setter |
| 982 // - lookup the setter on the superclass. |
| 983 // Note: an implicit (synthetic) setter would have already been flagged on |
| 984 // the getter above. |
| 985 var setter = type.getSetter(memberName); |
| 986 if (setter == null || setter.isStatic) return null; |
| 987 field = setter.variable; |
| 988 } else { |
| 989 return null; |
| 990 } |
| 991 if (field.isSynthetic) return null; |
| 992 return field; |
| 993 } |
| 994 |
| 951 /// Looks up the declaration that matches [member] in [type] and returns it's | 995 /// Looks up the declaration that matches [member] in [type] and returns it's |
| 952 /// declared type. | 996 /// declared type. |
| 953 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => | 997 FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => |
| 954 _memberTypeGetter(member)(type); | 998 _memberTypeGetter(member)(type); |
| 955 | 999 |
| 956 typedef FunctionType _MemberTypeGetter(InterfaceType type); | 1000 typedef FunctionType _MemberTypeGetter(InterfaceType type); |
| 957 | 1001 |
| 958 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) { | 1002 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) { |
| 959 String memberName = member.name; | 1003 String memberName = member.name; |
| 960 final isGetter = member is PropertyAccessorElement && member.isGetter; | 1004 final isGetter = member is PropertyAccessorElement && member.isGetter; |
| 961 final isSetter = member is PropertyAccessorElement && member.isSetter; | 1005 final isSetter = member is PropertyAccessorElement && member.isSetter; |
| 962 | 1006 |
| 963 FunctionType f(InterfaceType type) { | 1007 FunctionType f(InterfaceType type) { |
| 964 ExecutableElement baseMethod; | 1008 ExecutableElement baseMethod; |
| 1009 |
| 1010 if (member.isPrivate) { |
| 1011 var subtypeLibrary = member.library; |
| 1012 var baseLibrary = type.element.library; |
| 1013 if (baseLibrary != subtypeLibrary) { |
| 1014 return null; |
| 1015 } |
| 1016 } |
| 1017 |
| 965 try { | 1018 try { |
| 966 if (isGetter) { | 1019 if (isGetter) { |
| 967 assert(!isSetter); | 1020 assert(!isSetter); |
| 968 // Look for getter or field. | 1021 // Look for getter or field. |
| 969 baseMethod = type.getGetter(memberName); | 1022 baseMethod = type.getGetter(memberName); |
| 970 } else if (isSetter) { | 1023 } else if (isSetter) { |
| 971 baseMethod = type.getSetter(memberName); | 1024 baseMethod = type.getSetter(memberName); |
| 972 } else { | 1025 } else { |
| 973 baseMethod = type.getMethod(memberName); | 1026 baseMethod = type.getMethod(memberName); |
| 974 } | 1027 } |
| 975 } catch (e) { | 1028 } catch (e) { |
| 976 // TODO(sigmund): remove this try-catch block (see issue #48). | 1029 // TODO(sigmund): remove this try-catch block (see issue #48). |
| 977 } | 1030 } |
| 978 if (baseMethod == null || baseMethod.isStatic) return null; | 1031 if (baseMethod == null || baseMethod.isStatic) return null; |
| 979 return baseMethod.type; | 1032 return baseMethod.type; |
| 980 } | 1033 } |
| 981 ; | 1034 ; |
| 982 return f; | 1035 return f; |
| 983 } | 1036 } |
| OLD | NEW |