/* * Copyright (c) 2014, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.engine.internal.hint; import com.google.dart.engine.ast.MethodDeclaration; import com.google.dart.engine.ast.visitor.RecursiveAstVisitor; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.ExecutableElement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.element.MethodElement; import com.google.dart.engine.element.PropertyAccessorElement; import com.google.dart.engine.error.HintCode; import com.google.dart.engine.internal.error.ErrorReporter; import com.google.dart.engine.internal.resolver.InheritanceManager; /** * Instances of the class {@code OverrideVerifier} visit all of the declarations in a compilation * unit to verify that if they have an override annotation it is being used correctly. */ public class OverrideVerifier extends RecursiveAstVisitor<Void> { /** * The inheritance manager used to find overridden methods. */ private InheritanceManager manager; /** * The error reporter used to report errors. */ private ErrorReporter errorReporter; /** * Initialize a newly created verifier to look for inappropriate uses of the override annotation. * * @param manager the inheritance manager used to find overridden methods * @param errorReporter the error reporter used to report errors */ public OverrideVerifier(InheritanceManager manager, ErrorReporter errorReporter) { this.manager = manager; this.errorReporter = errorReporter; } // // As future enhancements, consider adding a hint when // - an override annotation is found on anything other than a method or field, or // - a method or field that overrides another does not have an annotation. // // @Override // public Void visitFieldDeclaration(FieldDeclaration node) { // // TODO(brianwilkerson) Override can also be applied to fields, in which case we need to check // // the getter and setter (not clear whether both should override, or if it's enough that one // // overrides something; probably the latter). // return super.visitFieldDeclaration(node); // } @Override public Void visitMethodDeclaration(MethodDeclaration node) { ExecutableElement element = node.getElement(); if (isOverride(element)) { if (getOverriddenMember(element) == null) { if (element instanceof MethodElement) { errorReporter.reportErrorForNode(HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD, node.getName()); } else if (element instanceof PropertyAccessorElement) { if (((PropertyAccessorElement) element).isGetter()) { errorReporter.reportErrorForNode(HintCode.OVERRIDE_ON_NON_OVERRIDING_GETTER, node.getName()); } else { errorReporter.reportErrorForNode(HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER, node.getName()); } } } } return super.visitMethodDeclaration(node); } /** * Return the member that overrides the given member. * * @param member the member that overrides the returned member * @return the member that overrides the given member */ private ExecutableElement getOverriddenMember(ExecutableElement member) { LibraryElement library = member.getLibrary(); if (library == null) { return null; } ClassElement classElement = member.getAncestor(ClassElement.class); if (classElement == null) { return null; } return manager.lookupInheritance(classElement, member.getName()); } /** * Return {@code true} if the given element has an override annotation associated with it. * * @param element the element being tested * @return {@code true} if the element has an override annotation associated with it */ private boolean isOverride(Element element) { return element != null && element.isOverride(); } }