Chromium Code Reviews| Index: pkg/analyzer/lib/src/task/strong/checker.dart |
| diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart |
| index d43a594a660f443a5420865bb3ec5a571c15f276..c608371878ee9e018f14114784fab4d23ae23a7c 100644 |
| --- a/pkg/analyzer/lib/src/task/strong/checker.dart |
| +++ b/pkg/analyzer/lib/src/task/strong/checker.dart |
| @@ -51,9 +51,10 @@ class _OverrideChecker { |
| var errorLocation = node.withClause.mixinTypes[i]; |
| for (int j = i - 1; j >= 0; j--) { |
| _checkIndividualOverridesFromType( |
| - current, mixins[j], errorLocation, seen); |
| + current, mixins[j], errorLocation, seen, true); |
| } |
| - _checkIndividualOverridesFromType(current, parent, errorLocation, seen); |
| + _checkIndividualOverridesFromType( |
| + current, parent, errorLocation, seen, true); |
| } |
| } |
| @@ -85,9 +86,9 @@ class _OverrideChecker { |
| var visited = new Set<InterfaceType>(); |
| do { |
| visited.add(current); |
| - current.mixins.reversed |
| - .forEach((m) => _checkIndividualOverridesFromClass(node, m, seen)); |
| - _checkIndividualOverridesFromClass(node, current.superclass, seen); |
| + current.mixins.reversed.forEach( |
| + (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); |
| + _checkIndividualOverridesFromClass(node, current.superclass, seen, true); |
| current = current.superclass; |
| } while (!current.isObject && !visited.contains(current)); |
| } |
| @@ -171,10 +172,10 @@ class _OverrideChecker { |
| // Check direct overrides on [type] |
| for (var interfaceType in interfaces) { |
| if (node != null) { |
| - _checkIndividualOverridesFromClass(node, interfaceType, seen); |
| + _checkIndividualOverridesFromClass(node, interfaceType, seen, false); |
| } else { |
| _checkIndividualOverridesFromType( |
| - type, interfaceType, errorLocation, seen); |
| + type, interfaceType, errorLocation, seen, false); |
| } |
| } |
| @@ -186,7 +187,7 @@ class _OverrideChecker { |
| // We copy [seen] so we can report separately if more than one mixin or |
| // the base class have an invalid override. |
| _checkIndividualOverridesFromType( |
| - type.mixins[i], interfaceType, loc, new Set.from(seen)); |
| + type.mixins[i], interfaceType, loc, new Set.from(seen), false); |
| } |
| } |
| @@ -212,12 +213,16 @@ class _OverrideChecker { |
| /// is used when invoking this function multiple times when checking several |
| /// types in a class hierarchy. Errors are reported only the first time an |
| /// invalid override involving a specific member is encountered. |
| - _checkIndividualOverridesFromType(InterfaceType subType, |
| - InterfaceType baseType, AstNode errorLocation, Set<String> seen) { |
| + _checkIndividualOverridesFromType( |
| + InterfaceType subType, |
| + InterfaceType baseType, |
| + AstNode errorLocation, |
| + Set<String> seen, |
| + bool isSubclass) { |
| void checkHelper(ExecutableElement e) { |
| if (e.isStatic) return; |
| if (seen.contains(e.name)) return; |
| - if (_checkSingleOverride(e, baseType, null, errorLocation)) { |
| + if (_checkSingleOverride(e, baseType, null, errorLocation, isSubclass)) { |
| seen.add(e.name); |
| } |
| } |
| @@ -230,8 +235,8 @@ class _OverrideChecker { |
| /// |
| /// The [errorLocation] node indicates where errors are reported, see |
| /// [_checkSingleOverride] for more details. |
| - _checkIndividualOverridesFromClass( |
| - ClassDeclaration node, InterfaceType baseType, Set<String> seen) { |
| + _checkIndividualOverridesFromClass(ClassDeclaration node, |
| + InterfaceType baseType, Set<String> seen, bool isSubclass) { |
| for (var member in node.members) { |
| if (member is ConstructorDeclaration) continue; |
| if (member is FieldDeclaration) { |
| @@ -242,10 +247,12 @@ class _OverrideChecker { |
| if (seen.contains(name)) continue; |
| var getter = element.getter; |
| var setter = element.setter; |
| - bool found = _checkSingleOverride(getter, baseType, variable, member); |
| + bool found = _checkSingleOverride( |
| + getter, baseType, variable, member, isSubclass); |
| if (!variable.isFinal && |
| !variable.isConst && |
| - _checkSingleOverride(setter, baseType, variable, member)) { |
| + _checkSingleOverride( |
| + setter, baseType, variable, member, isSubclass)) { |
| found = true; |
| } |
| if (found) seen.add(name); |
| @@ -254,7 +261,8 @@ class _OverrideChecker { |
| if ((member as MethodDeclaration).isStatic) continue; |
| var method = (member as MethodDeclaration).element; |
| if (seen.contains(method.name)) continue; |
| - if (_checkSingleOverride(method, baseType, member, member)) { |
| + if (_checkSingleOverride( |
| + method, baseType, member, member, isSubclass)) { |
| seen.add(method.name); |
| } |
| } |
| @@ -288,14 +296,23 @@ class _OverrideChecker { |
| /// the AST node that defines [element]. This is used to determine whether the |
| /// type of the element could be inferred from the types in the super classes. |
| bool _checkSingleOverride(ExecutableElement element, InterfaceType type, |
| - AstNode node, AstNode errorLocation) { |
| + AstNode node, AstNode errorLocation, bool isSubclass) { |
| assert(!element.isStatic); |
| FunctionType subType = _rules.elementType(element); |
| // TODO(vsm): Test for generic |
| FunctionType baseType = _getMemberType(type, element); |
| - |
| if (baseType == null) return false; |
| + |
| + if (isSubclass && element is PropertyAccessorElement) { |
| + // Disallow any overridding if the base class defines this member |
|
Leaf
2015/11/06 21:08:36
overriding
|
| + // as a field. We effectively treat fields as final / non-virtual. |
| + PropertyInducingElement field = _getMemberField(type, element); |
| + if (field != null) { |
| + _recordMessage(new InvalidFieldOverride( |
| + errorLocation, element, type, subType, baseType)); |
| + } |
| + } |
| if (!_rules.isAssignable(subType, baseType)) { |
| // See whether non-assignable cases fit one of our common patterns: |
| // |
| @@ -948,6 +965,33 @@ class CodeChecker extends RecursiveAstVisitor { |
| } |
| } |
| +// Return the field on type corresponding to member, or null if none |
| +// exists or the "field" is actually a getter/setter. |
| +PropertyInducingElement _getMemberField( |
| + InterfaceType type, PropertyAccessorElement member) { |
| + String memberName = member.name; |
| + PropertyInducingElement field; |
| + if (member.isGetter) { |
| + // The subclass member is an explicit getter or a field |
| + // - lookup the getter on the superclass. |
| + var getter = type.getGetter(memberName); |
| + if (getter == null || getter.isStatic) return null; |
| + field = getter.variable; |
| + } else if (!member.isSynthetic) { |
| + // The subclass member is an explicit setter |
| + // - lookup the setter on the superclass. |
| + // Note: an implicit (synthetic) setter would have already been flagged on |
| + // the getter above. |
| + var setter = type.getSetter(memberName); |
| + if (setter == null || setter.isStatic) return null; |
| + field = setter.variable; |
| + } else { |
| + return null; |
| + } |
| + if (field.isSynthetic) return null; |
| + return field; |
| +} |
| + |
| /// Looks up the declaration that matches [member] in [type] and returns it's |
| /// declared type. |
| FunctionType _getMemberType(InterfaceType type, ExecutableElement member) => |