Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(489)

Side by Side Diff: pkg/analyzer/lib/src/task/strong/checker.dart

Issue 1430953004: Check field overrides (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: fix typo Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/task/strong/info.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/task/strong/info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698