
com.palantir.baseline.errorprone.RedundantModifier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of baseline-error-prone Show documentation
Show all versions of baseline-error-prone Show documentation
A Gradle plugin for applying Baseline-recommended build and IDE settings
/*
* (c) Copyright 2019 Palantir Technologies 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.palantir.baseline.errorprone;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import java.util.Locale;
import javax.lang.model.element.Modifier;
/**
* In a future change we may want to validate against unnecessary modifiers based on encapsulating component visibility,
* for example there's no reason to allow a public constructor for a private class.
*/
@AutoService(BugChecker.class)
@BugPattern(
link = "https://github.com/palantir/gradle-baseline#baseline-error-prone-checks",
linkType = BugPattern.LinkType.CUSTOM,
severity = SeverityLevel.WARNING,
summary = "Avoid using redundant modifiers")
public final class RedundantModifier extends BugChecker
implements BugChecker.ClassTreeMatcher, BugChecker.MethodTreeMatcher, BugChecker.VariableTreeMatcher {
private static final Matcher STATIC_ENUM_OR_INTERFACE = Matchers.allOf(
Matchers.anyOf(Matchers.kindIs(Tree.Kind.ENUM), Matchers.kindIs(Tree.Kind.INTERFACE)),
MoreMatchers.hasExplicitModifier(Modifier.STATIC));
private static final Matcher PRIVATE_ENUM_CONSTRUCTOR = Matchers.allOf(
Matchers.methodIsConstructor(),
Matchers.enclosingClass(Matchers.kindIs(Tree.Kind.ENUM)),
MoreMatchers.hasExplicitModifier(Modifier.PRIVATE));
private static final Matcher STATIC_FINAL_METHOD = Matchers.allOf(
MoreMatchers.hasExplicitModifier(Modifier.STATIC), MoreMatchers.hasExplicitModifier(Modifier.FINAL));
private static final Matcher UNNECESSARY_INTERFACE_METHOD_MODIFIERS = Matchers.allOf(
Matchers.enclosingClass(Matchers.kindIs(Tree.Kind.INTERFACE)),
Matchers.not(Matchers.isStatic()),
Matchers.not(Matchers.hasModifier(Modifier.DEFAULT)),
Matchers.anyOf(
MoreMatchers.hasExplicitModifier(Modifier.PUBLIC),
MoreMatchers.hasExplicitModifier(Modifier.ABSTRACT)));
private static final Matcher INTERFACE_STATIC_METHOD_MODIFIERS = Matchers.allOf(
Matchers.enclosingClass(Matchers.kindIs(Tree.Kind.INTERFACE)),
Matchers.isStatic(),
MoreMatchers.hasExplicitModifier(Modifier.PUBLIC));
private static final Matcher INTERFACE_FIELD_MODIFIERS = Matchers.allOf(
Matchers.enclosingClass(Matchers.kindIs(Tree.Kind.INTERFACE)),
Matchers.isStatic(),
Matchers.anyOf(
MoreMatchers.hasExplicitModifier(Modifier.PUBLIC),
MoreMatchers.hasExplicitModifier(Modifier.STATIC),
MoreMatchers.hasExplicitModifier(Modifier.FINAL)));
// Applies to both abstract class abstract methods and interface methods.
private static final Matcher ABSTRACT_METHOD_MODIFIERS = Matchers.allOf(
Matchers.enclosingNode(
Matchers.allOf(Matchers.kindIs(Tree.Kind.METHOD), Matchers.hasModifier(Modifier.ABSTRACT))),
MoreMatchers.hasExplicitModifier(Modifier.FINAL));
private static final Matcher INTERFACE_NESTED_CLASS_MODIFIERS = Matchers.allOf(
MoreMatchers.classEnclosingClass(Matchers.kindIs(Tree.Kind.INTERFACE)),
Matchers.anyOf(
MoreMatchers.hasExplicitModifier(Modifier.PUBLIC),
MoreMatchers.hasExplicitModifier(Modifier.STATIC)));
private static final Matcher UNNECESSARY_FINAL_METHOD_ON_FINAL_CLASS = Matchers.allOf(
Matchers.not(Matchers.isStatic()),
Matchers.enclosingClass(
Matchers.allOf(Matchers.kindIs(Tree.Kind.CLASS), MoreMatchers.hasExplicitModifier(Modifier.FINAL))),
Matchers.allOf(
MoreMatchers.hasExplicitModifier(Modifier.FINAL),
Matchers.not(Matchers.hasAnnotation(SafeVarargs.class))));
@Override
public Description matchClass(ClassTree tree, VisitorState state) {
if (INTERFACE_NESTED_CLASS_MODIFIERS.matches(tree, state)) {
return buildDescription(tree)
.setMessage("Types nested in interfaces are public and static by default.")
.addFix(SuggestedFixes.removeModifiers(tree, state, Modifier.PUBLIC, Modifier.STATIC)
.orElseGet(SuggestedFix::emptyFix))
.build();
}
if (STATIC_ENUM_OR_INTERFACE.matches(tree, state)) {
return buildDescription(tree)
.setMessage(tree.getKind().name().toLowerCase(Locale.ENGLISH)
+ "s are static by default. The 'static' modifier is unnecessary.")
.addFix(SuggestedFixes.removeModifiers(tree, state, Modifier.STATIC)
.orElseGet(SuggestedFix::emptyFix))
.build();
}
return Description.NO_MATCH;
}
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
if (PRIVATE_ENUM_CONSTRUCTOR.matches(tree, state)) {
return buildDescription(tree)
.setMessage("Enum constructors are private by default. The 'private' modifier is unnecessary.")
.addFix(SuggestedFixes.removeModifiers(tree, state, Modifier.PRIVATE)
.orElseGet(SuggestedFix::emptyFix))
.build();
}
if (STATIC_FINAL_METHOD.matches(tree, state)) {
return buildDescription(tree)
.setMessage("Static methods cannot be overridden. The 'final' modifier is unnecessary.")
.addFix(SuggestedFixes.removeModifiers(tree, state, Modifier.FINAL)
.orElseGet(SuggestedFix::emptyFix))
.build();
}
if (UNNECESSARY_INTERFACE_METHOD_MODIFIERS.matches(tree, state)) {
return buildDescription(tree)
.setMessage("Interface methods are public and abstract by default. "
+ "The 'public' and 'abstract' modifiers are unnecessary.")
.addFix(SuggestedFixes.removeModifiers(tree, state, Modifier.PUBLIC, Modifier.ABSTRACT)
.orElseGet(SuggestedFix::emptyFix))
.build();
}
if (INTERFACE_STATIC_METHOD_MODIFIERS.matches(tree, state)) {
return buildDescription(tree)
.setMessage("Interface components are public by default. The 'public' modifier is unnecessary.")
.addFix(SuggestedFixes.removeModifiers(tree, state, Modifier.PUBLIC)
.orElseGet(SuggestedFix::emptyFix))
.build();
}
if (UNNECESSARY_FINAL_METHOD_ON_FINAL_CLASS.matches(tree, state)) {
return buildDescription(tree)
.setMessage("Redundant 'final' modifier on an instance method of a final class.")
.addFix(SuggestedFixes.removeModifiers(tree, state, Modifier.FINAL)
.orElseGet(SuggestedFix::emptyFix))
.build();
}
return Description.NO_MATCH;
}
@Override
public Description matchVariable(VariableTree tree, VisitorState state) {
if (INTERFACE_FIELD_MODIFIERS.matches(tree, state)) {
return buildDescription(tree)
.setMessage("Interface fields are public, static, and final by default. "
+ "These modifiers are unnecessary to specify.")
.addFix(SuggestedFixes.removeModifiers(
tree, state, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.orElseGet(SuggestedFix::emptyFix))
.build();
}
if (ABSTRACT_METHOD_MODIFIERS.matches(tree, state)) {
return buildDescription(tree)
.setMessage("The final modifier has no impact on abstract methods.")
.addFix(SuggestedFixes.removeModifiers(tree, state, Modifier.FINAL)
.orElseGet(SuggestedFix::emptyFix))
.build();
}
return Description.NO_MATCH;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy