diff options
Diffstat (limited to 'plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization')
3 files changed, 348 insertions, 246 deletions
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/DoubleBraceInitializationInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/DoubleBraceInitializationInspection.java new file mode 100644 index 000000000000..bdf58e720d8d --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/DoubleBraceInitializationInspection.java @@ -0,0 +1,216 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * 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.initialization; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class DoubleBraceInitializationInspection extends BaseInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("double.brace.initialization.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("double.brace.initialization.display.name"); + } + + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final PsiClass aClass = (PsiClass)infos[0]; + final PsiElement parent = PsiTreeUtil.skipParentsOfType(aClass, PsiNewExpression.class, ParenthesesUtils.class); + if (!(parent instanceof PsiVariable) && !(parent instanceof PsiAssignmentExpression)) { + return null; + } + return new DoubleBraceInitializationFix(); + } + + private static class DoubleBraceInitializationFix extends InspectionGadgetsFix { + + @NotNull + @Override + public String getName() { + return InspectionGadgetsBundle.message("double.brace.initialization.quickfix"); + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement().getParent(); + if (!(element instanceof PsiAnonymousClass)) { + return; + } + final PsiAnonymousClass aClass = (PsiAnonymousClass)element; + final PsiElement parent = aClass.getParent(); + if (!(parent instanceof PsiNewExpression)) { + return; + } + final PsiNewExpression newExpression = (PsiNewExpression)parent; + final PsiElement ancestor = PsiTreeUtil.skipParentsOfType(newExpression, ParenthesesUtils.class); + final String qualifierText; + if (ancestor instanceof PsiVariable) { + qualifierText = ((PsiVariable)ancestor).getName(); + } + else if (ancestor instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)ancestor; + final PsiExpression lhs = ParenthesesUtils.stripParentheses(assignmentExpression.getLExpression()); + if (!(lhs instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; + final PsiElement target = referenceExpression.resolve(); + if (!(target instanceof PsiVariable)) { + return; + } + qualifierText = referenceExpression.getText(); + } + else { + return; + } + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiJavaCodeReferenceElement baseClassReference = aClass.getBaseClassReference(); + final PsiElement baseClassTarget = baseClassReference.resolve(); + if (!(baseClassTarget instanceof PsiClass)) { + return; + } + final PsiExpressionList argumentList = aClass.getArgumentList(); + if (argumentList == null) { + return; + } + qualifyReferences(aClass, (PsiClass) baseClassTarget, qualifierText); + final PsiClassInitializer initializer = aClass.getInitializers()[0]; + final PsiCodeBlock body = initializer.getBody(); + PsiElement child = body.getLastBodyElement(); + final PsiElement stop = body.getFirstBodyElement(); + final PsiElement anchor = PsiTreeUtil.getParentOfType(aClass, PsiMember.class, PsiStatement.class); + if (anchor == null) { + return; + } + if (anchor instanceof PsiMember) { + final PsiMember member = (PsiMember)anchor; + final PsiClassInitializer newInitializer = factory.createClassInitializer(); + if (member.hasModifierProperty(PsiModifier.STATIC)) { + final PsiModifierList modifierList = newInitializer.getModifierList(); + if (modifierList != null) { + modifierList.setModifierProperty(PsiModifier.STATIC, true); + } + } + final PsiCodeBlock initializerBody = newInitializer.getBody(); + while (child != null && !child.equals(stop)) { + initializerBody.add(child); + child = child.getPrevSibling(); + } + member.getParent().addAfter(newInitializer, member); + } + else { + final PsiElement container = anchor.getParent(); + while (child != null && !child.equals(stop)) { + container.addAfter(child, anchor); + child = child.getPrevSibling(); + } + } + final PsiExpression newNewExpression = + factory.createExpressionFromText("new " + baseClassReference.getText() + argumentList.getText(), aClass); + newExpression.replace(newNewExpression); + } + + private static void qualifyReferences(PsiElement element, final PsiClass target, final String qualifierText) { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(element.getProject()); + element.accept(new JavaRecursiveElementVisitor() { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + super.visitReferenceExpression(expression); + if (expression.getQualifierExpression() != null) { + return; + } + final PsiElement expressionTarget = expression.resolve(); + if (!(expressionTarget instanceof PsiMember)) { + return; + } + final PsiMember member = (PsiMember)expressionTarget; + final PsiClass containingClass = member.getContainingClass(); + if (!target.equals(containingClass)) { + return; + } + final PsiExpression newExpression = factory.createExpressionFromText(qualifierText + '.' + expression.getText(), expression); + expression.replace(newExpression); + } + }); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new DoubleBraceInitializationVisitor(); + } + + private static class DoubleBraceInitializationVisitor extends BaseInspectionVisitor { + + @Override + public void visitAnonymousClass(PsiAnonymousClass aClass) { + super.visitAnonymousClass(aClass); + final PsiClassInitializer[] initializers = aClass.getInitializers(); + if (initializers.length != 1) { + return; + } + final PsiClassInitializer initializer = initializers[0]; + if (initializer.hasModifierProperty(PsiModifier.STATIC)) { + // don't warn on broken code + return; + } + final PsiField[] fields = aClass.getFields(); + if (fields.length != 0) { + return; + } + final PsiMethod[] methods = aClass.getMethods(); + if (methods.length != 0) { + return; + } + final PsiClass[] innerClasses = aClass.getInnerClasses(); + if (innerClasses.length != 0) { + return; + } + final PsiJavaCodeReferenceElement reference = aClass.getBaseClassReference(); + if (reference.resolve() == null) { + return; + } + registerClassError(aClass, aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java deleted file mode 100644 index 2e0bebac17b9..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2003-2007 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.initialization; - -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.tree.IElementType; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.Processor; -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.BaseInspection; -import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.InspectionGadgetsFix; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; - -public class NonThreadSafeLazyInitializationInspection - extends BaseInspection { - - @Override - @NotNull - public String getDisplayName() { - return InspectionGadgetsBundle.message( - "non.thread.safe.lazy.initialization.display.name"); - } - - @Override - @NotNull - public String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "non.thread.safe.lazy.initialization.problem.descriptor"); - } - - @Override - public BaseInspectionVisitor buildVisitor() { - return new UnsafeSafeLazyInitializationVisitor(); - } - - private static class UnsafeSafeLazyInitializationVisitor - extends BaseInspectionVisitor { - - @Override - public void visitAssignmentExpression( - @NotNull PsiAssignmentExpression expression) { - super.visitAssignmentExpression(expression); - final PsiExpression lhs = expression.getLExpression(); - if (!(lhs instanceof PsiReferenceExpression)) { - return; - } - final PsiReference reference = (PsiReference)lhs; - final PsiElement referent = reference.resolve(); - if (!(referent instanceof PsiField)) { - return; - } - final PsiField field = (PsiField)referent; - if (!field.hasModifierProperty(PsiModifier.STATIC)) { - return; - } - if (isInStaticInitializer(expression)) { - return; - } - if (isInSynchronizedContext(expression)) { - return; - } - if (!isLazy(expression, (PsiReferenceExpression)lhs)) { - return; - } - boolean assignedOnce = isAssignedOnce(referent); - boolean safeToDelete = isSafeToDeleteIfStatement(expression); - registerError(lhs, assignedOnce && safeToDelete); - } - - private static boolean isAssignedOnce(PsiElement referent) { - final int[] writeCount = new int[1]; - return ReferencesSearch.search(referent).forEach(new Processor<PsiReference>() { - @Override - public boolean process(PsiReference reference) { - PsiElement element = reference.getElement(); - if (!(element instanceof PsiExpression)) { - return true; - } - if (!PsiUtil.isAccessedForWriting((PsiExpression)element)) { - return true; - } - return ++writeCount[0] != 2; - } - }); - } - - private static boolean isSafeToDeleteIfStatement(PsiElement expression) { - PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(expression, PsiIfStatement.class); - if (ifStatement.getElseBranch() != null) { - return false; - } - PsiStatement thenBranch = ifStatement.getThenBranch(); - if (thenBranch == null) return false; - if (!(thenBranch instanceof PsiBlockStatement)) { - return true; - } - return ((PsiBlockStatement)thenBranch).getCodeBlock().getStatements().length == 1; - } - - private static boolean isLazy(PsiAssignmentExpression expression, - PsiReferenceExpression lhs) { - final PsiIfStatement ifStatement = - PsiTreeUtil.getParentOfType(expression, - PsiIfStatement.class); - if (ifStatement == null) { - return false; - } - final PsiExpression condition = ifStatement.getCondition(); - if (condition == null) { - return false; - } - return isNullComparison(condition, lhs); - } - - private static boolean isNullComparison( - PsiExpression condition, PsiReferenceExpression reference) { - if (!(condition instanceof PsiBinaryExpression)) { - return false; - } - final PsiBinaryExpression comparison = - (PsiBinaryExpression)condition; - final IElementType tokenType = comparison.getOperationTokenType(); - if (!tokenType.equals(JavaTokenType.EQEQ)) { - return false; - } - final PsiExpression lhs = comparison.getLOperand(); - final PsiExpression rhs = comparison.getROperand(); - if (rhs == null) { - return false; - } - final String lhsText = lhs.getText(); - final String rhsText = rhs.getText(); - if (!PsiKeyword.NULL.equals(lhsText) && - !PsiKeyword.NULL.equals(rhsText)) { - return false; - } - final String referenceText = reference.getText(); - return referenceText.equals(lhsText) || - referenceText.equals(rhsText); - } - - private static boolean isInSynchronizedContext(PsiElement element) { - final PsiSynchronizedStatement syncBlock = - PsiTreeUtil.getParentOfType(element, - PsiSynchronizedStatement.class); - if (syncBlock != null) { - return true; - } - final PsiMethod method = - PsiTreeUtil.getParentOfType(element, - PsiMethod.class); - return method != null && - method.hasModifierProperty(PsiModifier.SYNCHRONIZED) - && method.hasModifierProperty(PsiModifier.STATIC); - } - - private static boolean isInStaticInitializer(PsiElement element) { - final PsiClassInitializer initializer = - PsiTreeUtil.getParentOfType(element, - PsiClassInitializer.class); - return initializer != null && - initializer.hasModifierProperty(PsiModifier.STATIC); - } - } - - @Override - protected InspectionGadgetsFix buildFix(Object... infos) { - boolean isApplicable = ((Boolean)infos[0]).booleanValue(); - return isApplicable ? new IntroduceHolderFix() : null; - } - - private static class IntroduceHolderFix extends InspectionGadgetsFix { - @Override - protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { - PsiReferenceExpression expression = (PsiReferenceExpression)descriptor.getPsiElement(); - PsiElement resolved = expression.resolve(); - if (!(resolved instanceof PsiField)) return; - PsiField field = (PsiField)resolved; - String holderName = suggestHolderName(field); - @NonNls String text = "private static class " + holderName - + " {" + - "private static final " + field.getType().getCanonicalText() + " " + - field.getName() + " = " + ((PsiAssignmentExpression)expression.getParent()).getRExpression().getText() + ";" - + "}"; - PsiElementFactory elementFactory = JavaPsiFacade.getInstance(field.getProject()).getElementFactory(); - PsiClass holder = elementFactory.createClassFromText(text, field).getInnerClasses()[0]; - PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class); - method.getParent().addBefore(holder, method); - - PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(expression, PsiIfStatement.class); - ifStatement.delete(); - - final PsiExpression holderReference = elementFactory.createExpressionFromText(holderName + "." + field.getName(), field); - Collection<PsiReference> references = ReferencesSearch.search(field).findAll(); - for (PsiReference reference : references) { - PsiElement element = reference.getElement(); - element.replace(holderReference); - } - field.delete(); - } - - @NonNls - private static String suggestHolderName(PsiField field) { - String string = field.getType().getDeepComponentType().getPresentableText(); - final int index = string.indexOf('<'); - if (index != -1) { - string = string.substring(0, index); - } - return string + "Holder"; - } - - @Override - @NotNull - public String getName() { - return "Introduce holder class"; - } - - @NotNull - @Override - public String getFamilyName() { - return getName(); - } - } -}
\ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionBase.java new file mode 100644 index 000000000000..f29c461bcf64 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionBase.java @@ -0,0 +1,132 @@ +/* + * Copyright 2003-2014 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.initialization; + +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.psiutils.VariableAccessUtils; +import org.jetbrains.annotations.NotNull; + +public class NonThreadSafeLazyInitializationInspectionBase extends BaseInspection { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "non.thread.safe.lazy.initialization.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "non.thread.safe.lazy.initialization.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UnsafeSafeLazyInitializationVisitor(); + } + + private static class UnsafeSafeLazyInitializationVisitor + extends BaseInspectionVisitor { + + @Override + public void visitAssignmentExpression( + @NotNull PsiAssignmentExpression expression) { + super.visitAssignmentExpression(expression); + final PsiExpression lhs = expression.getLExpression(); + if (!(lhs instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression reference = (PsiReferenceExpression)lhs; + final PsiElement referent = reference.resolve(); + if (!(referent instanceof PsiField)) { + return; + } + final PsiField field = (PsiField)referent; + if (!field.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + if (isInStaticInitializer(expression)) { + return; + } + if (isInSynchronizedContext(expression)) { + return; + } + final PsiStatement statement = PsiTreeUtil.getParentOfType(expression, PsiStatement.class); + final PsiElement parent = + PsiTreeUtil.skipParentsOfType(statement, PsiCodeBlock.class, PsiBlockStatement.class); + if (!(parent instanceof PsiIfStatement)) { + return; + } + final PsiIfStatement ifStatement = (PsiIfStatement)parent; + final PsiExpression condition = ifStatement.getCondition(); + if (condition == null|| !isNullComparison(condition, field)) { + return; + } + registerError(lhs, ifStatement, field); + } + + private static boolean isNullComparison(PsiExpression condition, PsiVariable variable) { + if (!(condition instanceof PsiBinaryExpression)) { + return false; + } + final PsiBinaryExpression comparison = (PsiBinaryExpression)condition; + final IElementType tokenType = comparison.getOperationTokenType(); + if (!tokenType.equals(JavaTokenType.EQEQ)) { + return false; + } + final PsiExpression lhs = comparison.getLOperand(); + final PsiExpression rhs = comparison.getROperand(); + if (ExpressionUtils.isNullLiteral(rhs)) { + return VariableAccessUtils.evaluatesToVariable(lhs, variable); + } + else if (ExpressionUtils.isNullLiteral(lhs)) { + return VariableAccessUtils.evaluatesToVariable(rhs, variable); + } + return false; + } + + private static boolean isInSynchronizedContext(PsiElement element) { + final PsiSynchronizedStatement syncBlock = + PsiTreeUtil.getParentOfType(element, + PsiSynchronizedStatement.class); + if (syncBlock != null) { + return true; + } + final PsiMethod method = + PsiTreeUtil.getParentOfType(element, + PsiMethod.class); + return method != null && + method.hasModifierProperty(PsiModifier.SYNCHRONIZED) + && method.hasModifierProperty(PsiModifier.STATIC); + } + + private static boolean isInStaticInitializer(PsiElement element) { + final PsiClassInitializer initializer = + PsiTreeUtil.getParentOfType(element, + PsiClassInitializer.class); + return initializer != null && + initializer.hasModifierProperty(PsiModifier.STATIC); + } + } +}
\ No newline at end of file |