/*
* Copyright 2016 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.threadsafety;
import static com.google.errorprone.BugPattern.Category.JDK;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Description.NO_MATCH;
import static com.google.errorprone.util.ASTHelpers.getSymbol;
import static com.google.errorprone.util.ASTHelpers.getType;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
import com.google.errorprone.bugpatterns.threadsafety.ImmutableAnalysis.Violation;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
/** @author cushon@google.com (Liam Miller-Cushon) */
@BugPattern(
name = "ImmutableAnnotationChecker",
altNames = "Immutable",
category = JDK,
summary = "Annotations should always be immutable",
severity = WARNING
)
public class ImmutableAnnotationChecker extends BugChecker implements ClassTreeMatcher {
public static final String ANNOTATED_ANNOTATION_MESSAGE =
"annotations are immutable by default; annotating them with @Immutable is unnecessary";
@Override
public Description matchClass(ClassTree tree, VisitorState state) {
ClassSymbol symbol = getSymbol(tree);
if (symbol == null
|| symbol.isAnnotationType()
|| !WellKnownMutability.isAnnotation(state, symbol.type)) {
return NO_MATCH;
}
if (ASTHelpers.hasAnnotation(symbol, Immutable.class, state)) {
AnnotationTree annotation =
ASTHelpers.getAnnotationWithSimpleName(tree.getModifiers().getAnnotations(), "Immutable");
if (annotation != null) {
state.reportMatch(
buildDescription(annotation)
.setMessage(ANNOTATED_ANNOTATION_MESSAGE)
.addFix(SuggestedFix.delete(annotation))
.build());
} else {
state.reportMatch(buildDescription(tree).setMessage(ANNOTATED_ANNOTATION_MESSAGE).build());
}
}
Violation info =
new ImmutableAnalysis(
this,
state,
"annotations should be immutable, and cannot have non-final fields",
"annotations should be immutable")
.checkForImmutability(Optional.of(tree), ImmutableSet.of(), getType(tree));
if (!info.isPresent()) {
return NO_MATCH;
}
String message = "annotations should be immutable: " + info.message();
return buildDescription(tree).setMessage(message).build();
}
}