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 |