IDEADEV-41150 (new "​ThreadLocal should be static final" inspection)

This commit is contained in:
Bas Leijdekkers
2009-11-06 23:45:31 +01:00
parent 801a96bde2
commit bd716cdde2
7 changed files with 197 additions and 119 deletions
@@ -1685,7 +1685,7 @@ logger.initialized.with.foreign.class.quickfix=Replace with ''{0}.class''
logger.initialized.with.foreign.class.problem.descriptor=Logger initializer with foreign class <code>#ref</code> #loc
logger.initialized.with.foreign.class.display.name=Logger initialized with foreign class
logger.factory.method.name=Logger factory &method:
non.constant.logger.quickfix=Make 'static final'
make.static.final.quickfix=Make ''{0}'' static final
logging.condition.disagrees.with.log.statement.display.name=Logging condition does not match log statement
logging.condition.disagrees.with.log.statement.problem.descriptor=Logging condition <code>#ref()</code> does not match ''{0}()'' log statement #loc
log.statement.guarded.by.log.condition.display.name=Log statement not guarded by log condition
@@ -1752,4 +1752,6 @@ multiple.exceptions.declared.on.test.method.problem.descriptor=<code>#ref</code>
multiple.exceptions.declared.on.test.method.quickfix=Replace with 'throws Exception'
unnecessary.javadoc.link.display.name=Unnecessary JavaDoc link
unnecessary.javadoc.link.problem.descriptor=<code>#ref</code> is unnecessary
unnecessary.javadoc.link.quickfix=Remove unnecessary {0}
unnecessary.javadoc.link.quickfix=Remove unnecessary {0}
thread.local.not.static.final.display.name=ThreadLocal field not declared static final
thread.local.not.static.final.problem.descriptor=ThreadLocal <code>#ref</code> is not declared 'static final'
@@ -849,6 +849,7 @@ public class InspectionGadgetsPlugin implements ApplicationComponent,
m_inspectionClasses.add(SynchronizeOnThisInspection.class);
m_inspectionClasses.add(SynchronizedMethodInspection.class);
m_inspectionClasses.add(SystemRunFinalizersOnExitInspection.class);
m_inspectionClasses.add(ThreadLocalNotStaticFinalInspection.class);
m_inspectionClasses.add(ThreadPriorityInspection.class);
m_inspectionClasses.add(ThreadRunInspection.class);
m_inspectionClasses.add(ThreadStartInConstructionInspection.class);
@@ -18,13 +18,10 @@ package com.siyeh.ig.fixes;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Query;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.psiutils.FinalUtils;
import com.siyeh.ig.psiutils.InitializationUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -39,11 +36,7 @@ public class MakeFieldFinalFix extends InspectionGadgetsFix {
@Nullable
public static InspectionGadgetsFix buildFix(PsiField field) {
if (field.hasModifierProperty(PsiModifier.STATIC)) {
if (!canStaticFieldBeFinal(field)) {
return null;
}
} else if (!canInstanceFieldBeFinal(field)) {
if (!FinalUtils.canFieldBeFinal(field)) {
return null;
}
final String name = field.getName();
@@ -55,77 +48,6 @@ public class MakeFieldFinalFix extends InspectionGadgetsFix {
return new MakeFieldFinalFix(field.getName());
}
private static boolean canStaticFieldBeFinal(PsiField field) {
final boolean hasInitializer = field.hasInitializer();
final boolean initializedInOneStaticInitializer =
isInitializedInOneStaticInitializer(field);
if (hasInitializer) {
if (initializedInOneStaticInitializer) {
return false;
}
} else {
if (!initializedInOneStaticInitializer) {
return false;
}
}
final Query<PsiReference> query = ReferencesSearch.search(field);
for (PsiReference reference : query) {
final PsiElement element = reference.getElement();
if (!(element instanceof PsiExpression)) {
continue;
}
final PsiExpression expression = (PsiExpression) element;
if (!PsiUtil.isOnAssignmentLeftHand(expression)) {
continue;
}
final PsiMethod method = PsiTreeUtil.getParentOfType(
expression, PsiMethod.class);
if (method != null) {
return false;
}
}
return true;
}
private static boolean canInstanceFieldBeFinal(PsiField field) {
final boolean hasInitializer = field.hasInitializer();
final boolean initializedInOneInitializer =
isInitializedInOneInitializer(field);
final boolean initializedInConstructors =
isInitializedInConstructors(field);
if (hasInitializer) {
if (initializedInOneInitializer) {
return false;
}
if (initializedInConstructors) {
return false;
}
} else if (initializedInOneInitializer) {
if (initializedInConstructors) {
return false;
}
} else if (!initializedInConstructors) {
return false;
}
final Query<PsiReference> query = ReferencesSearch.search(field);
for (PsiReference reference : query) {
final PsiElement element = reference.getElement();
if (!(element instanceof PsiExpression)) {
continue;
}
final PsiExpression expression = (PsiExpression) element;
if (!PsiUtil.isOnAssignmentLeftHand(expression)) {
continue;
}
final PsiMethod method = PsiTreeUtil.getParentOfType(
expression, PsiMethod.class);
if (method != null && !method.isConstructor()) {
return false;
}
}
return true;
}
@NotNull
public String getName() {
return InspectionGadgetsBundle.message("make.field.final.quickfix",
@@ -0,0 +1,76 @@
/*
* Copyright 2003-2009 Dave Griffith, Bas Leijdekkers
*
* 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.siyeh.ig.fixes;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.psiutils.FinalUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class MakeFieldStaticFinalFix extends InspectionGadgetsFix {
private final String fieldName;
private MakeFieldStaticFinalFix(String fieldName) {
this.fieldName = fieldName;
}
@NotNull
public static InspectionGadgetsFix buildFixUnconditional(
@NotNull PsiField field) {
return new MakeFieldStaticFinalFix(field.getName());
}
@Nullable
public static InspectionGadgetsFix buildFix(PsiField field) {
final PsiExpression initializer = field.getInitializer();
if (initializer == null) {
return null;
}
if (!FinalUtils.canFieldBeFinal(field)) {
return null;
}
return new MakeFieldStaticFinalFix(field.getName());
}
@NotNull
public String getName() {
return InspectionGadgetsBundle.message(
"make.static.final.quickfix", fieldName);
}
@Override
protected void doFix(Project project, ProblemDescriptor descriptor)
throws IncorrectOperationException {
final PsiElement element = descriptor.getPsiElement();
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiField)) {
return;
}
final PsiField field = (PsiField) parent;
final PsiModifierList modifierList = field.getModifierList();
if (modifierList == null) {
return;
}
modifierList.setModifierProperty(PsiModifier.FINAL, true);
modifierList.setModifierProperty(PsiModifier.STATIC, true);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2008 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2009 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,12 @@
package com.siyeh.ig.logging;
import com.intellij.psi.*;
import com.intellij.openapi.project.Project;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.RegExInputVerifier;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.fixes.MakeFieldStaticFinalFix;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -37,17 +35,20 @@ public class NonStaticFinalLoggerInspection extends BaseInspection {
/** @noinspection PublicField*/
public String loggerClassName = "java.util.logging.Logger";
@Override
@NotNull
public String getID(){
return "NonConstantLogger";
}
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message(
"non.constant.logger.display.name");
}
@Override
@NotNull
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message(
@@ -56,35 +57,12 @@ public class NonStaticFinalLoggerInspection extends BaseInspection {
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
return new NonStaticFinalLoggerFix();
}
private static class NonStaticFinalLoggerFix extends InspectionGadgetsFix {
@NotNull
public String getName() {
return InspectionGadgetsBundle.message(
"non.constant.logger.quickfix");
}
protected void doFix(Project project, ProblemDescriptor descriptor)
throws IncorrectOperationException {
final PsiElement element = descriptor.getPsiElement();
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiField)) {
return;
}
PsiField field = (PsiField) parent;
final PsiModifierList modifierList = field.getModifierList();
if (modifierList == null) {
return;
}
modifierList.setModifierProperty(PsiModifier.FINAL, true);
modifierList.setModifierProperty(PsiModifier.STATIC, true);
}
final PsiField field = (PsiField) infos[0];
return MakeFieldStaticFinalFix.buildFixUnconditional(field);
}
@Override
public JComponent createOptionsPanel() {
final GridBagLayout layout = new GridBagLayout();
final JPanel panel = new JPanel(layout);
@@ -137,6 +115,7 @@ public class NonStaticFinalLoggerInspection extends BaseInspection {
return panel;
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new NonStaticFinalLoggerVisitor();
}
@@ -157,17 +136,19 @@ public class NonStaticFinalLoggerInspection extends BaseInspection {
}
final PsiField[] fields = aClass.getFields();
for(final PsiField field : fields) {
if(isLogger(field)) {
if(!field.hasModifierProperty(PsiModifier.STATIC) ||
!field.hasModifierProperty(PsiModifier.FINAL)){
registerFieldError(field);
}
if (!isLogger(field)) {
continue;
}
if (field.hasModifierProperty(PsiModifier.STATIC) &&
field.hasModifierProperty(PsiModifier.FINAL)) {
continue;
}
registerFieldError(field, field);
}
}
private boolean isLogger(PsiField field) {
final PsiType type = field.getType();
private boolean isLogger(PsiVariable variable) {
final PsiType type = variable.getType();
final String text = type.getCanonicalText();
return text.equals(loggerClassName);
}
@@ -0,0 +1,83 @@
/*
* Copyright 2009 Bas Leijdekkers
*
* 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.siyeh.ig.threading;
import com.intellij.psi.*;
import com.siyeh.InspectionGadgetsBundle;import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.fixes.MakeFieldStaticFinalFix;
import com.siyeh.ig.psiutils.ClassUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
public class ThreadLocalNotStaticFinalInspection extends BaseInspection {
@Nls
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message(
"thread.local.not.static.final.display.name");
}
@NotNull
@Override
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message(
"thread.local.not.static.final.problem.descriptor");
}
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
return MakeFieldStaticFinalFix.buildFix((PsiField) infos[0]);
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new ThreadLocalNotStaticFinalVisitor();
}
private static class ThreadLocalNotStaticFinalVisitor
extends BaseInspectionVisitor {
@Override
public void visitField(PsiField field) {
super.visitField(field);
final PsiType type = field.getType();
if (!(type instanceof PsiClassType)) {
return;
}
final PsiClassType classType = (PsiClassType)type;
final PsiClass aClass = classType.resolve();
if (aClass == null) {
return;
}
if (!ClassUtils.isSubclass(aClass, "java.lang.ThreadLocal")) {
return;
}
final PsiModifierList modifierList = field.getModifierList();
if (modifierList == null) {
return;
}
if (modifierList.hasModifierProperty(PsiModifier.STATIC) &&
modifierList.hasModifierProperty(PsiModifier.FINAL)) {
return;
}
registerFieldError(field, field);
}
}
}
@@ -0,0 +1,13 @@
<html>
<body><table> <tr> <td valign="top" height="150">
<font face="verdana" size="-1">
In the most common case a <b><font color="#000080">java.lang.ThreadLocal</font></b> instance
associates state with a thread. A non-static non-final
<b><font color="#000080">java.lang.ThreadLocal</font></b> field associates state with
an instance-thread combination. This is seldom necessary and often a bug
which can cause memory leaks and possibly incorrect behavior.
<p>
This inspection has a quick fix to make the field <b><font color="#000080">static final</font></b>
</p>
</font></td> </tr> <tr> <td height="20"> <font face="verdana" size="-2">New in 9, Powered by InspectionGadgets </font> </td> </tr> </table> </body>
</html>