/*
* Copyright 2012 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.Category.JDK;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.util.Name;
import javax.lang.model.element.ElementKind;
/** @author scottjohnson@google.com (Scott Johnson) */
@BugPattern(
name = "ParameterPackage",
summary = "Method parameter has wrong package",
explanation =
"Method does not override method in superclass due to wrong package for "
+ "parameter. For example, defining a method void foo(alpha.Foo x) when the superclass "
+ "contains a method void foo(beta.Foo x). The defined method was probably meant to "
+ "override the superclass method.",
category = JDK,
severity = ERROR
)
public class WrongParameterPackage extends BugChecker implements MethodTreeMatcher {
private MethodSymbol supermethod;
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
MethodSymbol method = ASTHelpers.getSymbol(tree);
if (method == null) {
return Description.NO_MATCH;
}
ClassSymbol classSym = method.enclClass();
if (classSym == null) {
return Description.NO_MATCH;
}
TypeSymbol superClass = classSym.getSuperclass().tsym;
if (superClass == null) {
return Description.NO_MATCH;
}
for (Symbol s : superClass.members().getSymbols()) {
if (s.name.contentEquals(method.name) && s.getKind() == ElementKind.METHOD) {
MethodSymbol supermethod = (MethodSymbol) s;
// if this method actually overrides the supermethod, then it's correct and not a match.
if (method.overrides(supermethod, superClass, state.getTypes(), true)) {
return Description.NO_MATCH;
}
// if this doesn't have the right number of parameters, look at other ones.
if (supermethod.params().size() != method.params().size()) {
continue;
}
for (int x = 0; x < method.params().size(); x++) {
Type methodParamType = method.params().get(x).type;
Type supermethodParamType = supermethod.params().get(x).type;
if (methodParamType.tsym.name.contentEquals(supermethodParamType.tsym.name)
&& !state.getTypes().isSameType(methodParamType, supermethodParamType)) {
this.supermethod = supermethod;
return describe(tree, state);
}
}
}
}
return Description.NO_MATCH;
}
public Description describe(MethodTree tree, VisitorState state) {
SuggestedFix.Builder builder = null;
MethodSymbol method = ASTHelpers.getSymbol(tree);
if (supermethod == null){
throw new IllegalStateException("Matching supermethod was not found");
}
for (int x = 0; x < method.params().size(); x++) {
Type methodParamType = method.params().get(x).type;
Type supermethodParamType = supermethod.params().get(x).type;
if (methodParamType.tsym.name.contentEquals(supermethodParamType.tsym.name)
&& !state.getTypes().isSameType(methodParamType, supermethodParamType)) {
VariableTree param = tree.getParameters().get(x);
// TODO(user): Name is most likely more qualified than necessary.
Name replacement = supermethodParamType.tsym.getQualifiedName();
if (builder == null) {
builder = SuggestedFix.builder();
}
builder.replace(param, replacement + " " + param.getName());
}
}
return (builder != null) ? describeMatch(tree, builder.build()) : describeMatch(tree);
}
}