summaryrefslogtreecommitdiff
path: root/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java')
-rw-r--r--plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java693
1 files changed, 86 insertions, 607 deletions
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java
index bf8c94d309df..1155a1083906 100644
--- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java
+++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 Bas Leijdekkers
+ * Copyright 2009-2013 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,650 +16,129 @@
package com.siyeh.ig.psiutils;
import com.intellij.psi.*;
-import com.intellij.psi.tree.IElementType;
-import com.intellij.psi.util.PsiTreeUtil;
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.Nullable;
+import com.intellij.psi.util.PsiUtil;
+import org.jetbrains.annotations.NotNull;
-import java.util.HashSet;
-import java.util.Set;
-
-// todo handle variable initialization in loops
public class FinalUtils {
- private FinalUtils() {
+ private FinalUtils() {}
+
+ public static boolean canBeFinal(@NotNull PsiVariable variable) {
+ final FinalDefiniteAssignment definiteAssignment = new FinalDefiniteAssignment(variable);
+ DefiniteAssignmentUtil.checkVariable(variable, definiteAssignment);
+ return definiteAssignment.isDefinitelyAssigned() &&
+ !definiteAssignment.isDefinitelyUnassigned() && // spec?
+ definiteAssignment.canBeFinal() &&
+ !isWrittenToOutsideOfConstruction(variable);
}
- public static boolean canBeFinal(PsiField field) {
- PsiClass containingClass = field.getContainingClass();
- if (containingClass == null) {
- return false;
- }
- final boolean fieldIsStatic =
- field.hasModifierProperty(PsiModifier.STATIC);
- final PsiField[] fields = containingClass.getFields();
- final DefiniteAssignmentVisitor visitor =
- new DefiniteAssignmentVisitor(field);
- if (field.hasInitializer()) {
- visitor.setDefiniteAssignment(true, false);
- }
- for (PsiField aField : fields) {
- final PsiExpression initializer = aField.getInitializer();
- if (initializer != null) {
- initializer.accept(visitor);
- if (!visitor.isFinalCandidate()) {
- return false;
- }
- }
- }
- final PsiClassInitializer[] initializers =
- containingClass.getInitializers();
- for (PsiClassInitializer initializer : initializers) {
- initializer.accept(visitor);
- if (!visitor.isFinalCandidate()) {
- return false;
- }
- }
- if (!fieldIsStatic) {
- final boolean da = visitor.isDefinitelyAssigned();
- final boolean du = visitor.isDefinitelyUnassigned();
- final PsiMethod[] constructors = containingClass.getConstructors();
- for (PsiMethod constructor : constructors) {
- final PsiCodeBlock body = constructor.getBody();
- if (body != null) {
- visitor.setDefiniteAssignment(da, du);
- body.accept(visitor);
- }
- if (!visitor.isDefinitelyAssigned()) {
- return false;
- }
- if (!visitor.isFinalCandidate()) {
- return false;
- }
- }
- }
- if (!visitor.isDefinitelyAssigned()) {
+ private static boolean isWrittenToOutsideOfConstruction(PsiVariable variable) {
+ if (!(variable instanceof PsiField)) {
return false;
}
- if (!visitor.isFinalCandidate()) {
- return false;
- }
- checkMembers(fieldIsStatic, containingClass, null, visitor);
- if (!visitor.isFinalCandidate()) {
+ final PsiField field = (PsiField)variable;
+ final PsiClass containingClass = field.getContainingClass();
+ if (containingClass == null) {
return false;
}
- PsiClass aClass = containingClass.getContainingClass();
- while (aClass != null) {
- visitor.setSkipClass(containingClass);
- aClass.accept(visitor);
- if (!visitor.isFinalCandidate()) {
- return false;
- }
- containingClass = aClass;
- aClass = containingClass.getContainingClass();
- }
- return true;
- }
-
- private static void checkMembers(boolean checkConstructors,
- PsiClass containingClass,
- @Nullable PsiClass skipClass,
- DefiniteAssignmentVisitor visitor) {
- final PsiMethod[] methods = containingClass.getMethods();
- for (PsiMethod method : methods) {
- if (!checkConstructors && method.isConstructor()) {
- continue;
- }
- method.accept(visitor);
- if (!visitor.isFinalCandidate()) {
- return;
- }
- }
- final PsiClass[] innerClasses = containingClass.getInnerClasses();
- for (PsiClass innerClass : innerClasses) {
- if (innerClass == skipClass) {
- continue;
- }
- innerClass.accept(visitor);
- if (!visitor.isFinalCandidate()) {
- return;
- }
- }
- }
-
- private static boolean isReadAccess(PsiReferenceExpression expression) {
- final PsiElement parent = PsiTreeUtil.skipParentsOfType(expression,
- PsiParenthesizedExpression.class);
- if (!(parent instanceof PsiAssignmentExpression)) {
- return true;
- }
- final PsiAssignmentExpression assignmentExpression =
- (PsiAssignmentExpression)parent;
- final PsiExpression lhs = assignmentExpression.getLExpression();
- if (!PsiTreeUtil.isAncestor(lhs, expression, false)) {
- return true;
- }
- final IElementType tokenType =
- assignmentExpression.getOperationTokenType();
- return tokenType != JavaTokenType.EQ;
- }
-
- private static boolean isWriteAccess(PsiReferenceExpression expression) {
- final PsiElement parent =
- PsiTreeUtil.skipParentsOfType(expression,
- PsiParenthesizedExpression.class);
- if (parent instanceof PsiAssignmentExpression) {
- final PsiAssignmentExpression assignmentExpression =
- (PsiAssignmentExpression)parent;
- final PsiExpression lhs = assignmentExpression.getLExpression();
- return PsiTreeUtil.isAncestor(lhs, expression, false);
- }
- else if (parent instanceof PsiPrefixExpression) {
- final PsiPrefixExpression prefixExpression =
- (PsiPrefixExpression)parent;
- final IElementType tokenType =
- prefixExpression.getOperationTokenType();
- return tokenType == JavaTokenType.PLUSPLUS ||
- tokenType == JavaTokenType.MINUSMINUS;
- }
- else if (parent instanceof PsiPostfixExpression) {
- final PsiPostfixExpression postfixExpression =
- (PsiPostfixExpression)parent;
- final IElementType tokenType =
- postfixExpression.getOperationTokenType();
- return tokenType == JavaTokenType.PLUSPLUS ||
- tokenType == JavaTokenType.MINUSMINUS;
- }
- return false;
- }
-
- private static class DefiniteAssignmentVisitor
- extends JavaRecursiveElementVisitor {
-
- private static final byte NOT_CONSTANT = 0;
- private static final byte CONSTANT_TRUE = 1;
- private static final byte CONSTANT_FALSE = 2;
-
- private final PsiField field;
- private final Set<PsiStatement> exits = new HashSet();
-
- private byte constant = NOT_CONSTANT;
- private boolean definitelyAssigned = false;
- private boolean definitelyUnassigned = true;
- private boolean finalCandidate = true;
- private PsiClass skipClass = null;
-
- private DefiniteAssignmentVisitor(PsiField field) {
- this.field = field;
- }
-
- public boolean isDefinitelyAssigned() {
- return definitelyAssigned;
- }
-
- public boolean isDefinitelyUnassigned() {
- return definitelyUnassigned;
- }
-
- public void setSkipClass(PsiClass skipClass) {
- this.skipClass = skipClass;
- }
-
- public void setDefiniteAssignment(boolean da, boolean du) {
- definitelyAssigned = da;
- definitelyUnassigned = du;
- }
-
- public boolean isFinalCandidate() {
- return finalCandidate;
- }
-
- @Override
- public void visitElement(PsiElement element) {
- if (!isFinalCandidate()) {
- return;
- }
- super.visitElement(element);
- }
-
- @Override
- public void visitMethod(PsiMethod method) {
- definitelyAssigned = true;
- definitelyUnassigned = false;
- super.visitMethod(method);
- }
-
- @Override
- public void visitClass(PsiClass aClass) {
- if (aClass == skipClass) {
- return;
- }
- final boolean da = definitelyAssigned;
- final boolean du = definitelyUnassigned;
- definitelyAssigned = true;
- definitelyUnassigned = false;
- super.visitClass(aClass);
- definitelyAssigned = da;
- definitelyUnassigned = du;
- }
-
- @Override
- public void visitReferenceExpression(PsiReferenceExpression expression) {
- super.visitReferenceExpression(expression);
- if (PsiType.BOOLEAN.equals(expression.getType())) {
- final Object constant =
- ExpressionUtils.computeConstantExpression(expression);
- if (Boolean.TRUE == constant) {
- this.constant = CONSTANT_TRUE;
- }
- else if (Boolean.FALSE == constant) {
- this.constant = CONSTANT_FALSE;
- }
- }
- final PsiExpression qualifierExpression =
- expression.getQualifierExpression();
- if (qualifierExpression != null &&
- !(qualifierExpression instanceof PsiThisExpression)) {
- final PsiElement target = expression.resolve();
- if (!field.equals(target)) {
- return;
- }
- if (isWriteAccess(expression)) {
- finalCandidate = false;
- }
- return;
- }
- if (isPrePostFixExpression(expression)) {
- final PsiElement target = expression.resolve();
- if (!field.equals(target)) {
- return;
- }
- if (!definitelyAssigned || !definitelyUnassigned) {
- finalCandidate = false;
- }
- else {
- definitelyUnassigned = false;
- }
- }
- else if (isReadAccess(expression)) {
- final PsiElement target = expression.resolve();
- if (!field.equals(target)) {
- return;
- }
- if (!definitelyAssigned) {
- finalCandidate = false;
+ final PsiClass topLevelClass = PsiUtil.getTopLevelClass(variable);
+ final VariableAssignedVisitor visitor = new VariableAssignedVisitor(field);
+ if (topLevelClass != null && !containingClass.equals(topLevelClass)) {
+ visitor.setExcludedElement(containingClass);
+ topLevelClass.accept(visitor);
+ if (visitor.isAssigned()) {
+ return true;
+ }
+ }
+ if (field.hasModifierProperty(PsiModifier.STATIC)) {
+ for (PsiElement child : containingClass.getChildren()) {
+ if (child instanceof PsiClassInitializer) {
+ final PsiClassInitializer classInitializer = (PsiClassInitializer)child;
+ if (classInitializer.hasModifierProperty(PsiModifier.STATIC)) {
+ continue;
+ }
+ classInitializer.accept(visitor);
}
- }
- }
-
- @Override
- public void visitAssignmentExpression(
- PsiAssignmentExpression expression) {
- if (!finalCandidate) {
- return;
- }
- final PsiExpression rhs = expression.getRExpression();
- if (rhs != null) {
- rhs.accept(this);
- }
- final PsiExpression lhs = ParenthesesUtils.stripParentheses(
- expression.getLExpression());
- if (!(lhs instanceof PsiReferenceExpression)) {
- return;
- }
- final PsiReferenceExpression referenceExpression =
- (PsiReferenceExpression)lhs;
- final PsiExpression qualifierExpression =
- referenceExpression.getQualifierExpression();
- if (qualifierExpression != null &&
- !(qualifierExpression instanceof PsiThisExpression)) {
- visitReferenceExpression(referenceExpression);
- return;
- }
- final PsiElement target = referenceExpression.resolve();
- if (!field.equals(target)) {
- return;
- }
- final IElementType tokenType = expression.getOperationTokenType();
- if (!JavaTokenType.EQ.equals(tokenType)) {
- finalCandidate = false;
- }
- if (definitelyUnassigned) {
- definitelyAssigned = true;
- definitelyUnassigned = false;
- }
- else {
- finalCandidate = false;
- }
- }
-
- @Override
- public void visitAssertStatement(PsiAssertStatement statement) {
- final PsiExpression condition = statement.getAssertCondition();
- final boolean da = definitelyAssigned;
- final boolean du = definitelyUnassigned;
- if (condition != null) {
- condition.accept(this);
- }
- final PsiExpression description = statement.getAssertDescription();
- if (description != null) {
- description.accept(this);
- }
- definitelyAssigned &= da;
- definitelyUnassigned &= du;
- }
-
- @Override
- public void visitLiteralExpression(PsiLiteralExpression expression) {
- final Object value = expression.getValue();
- if (value instanceof Boolean) {
- final Boolean aBoolean = (Boolean)value;
- if (Boolean.TRUE == aBoolean) {
- constant = CONSTANT_TRUE;
+ else if (child instanceof PsiField) {
+ final PsiField otherField = (PsiField)child;
+ if (otherField.hasModifierProperty(PsiModifier.STATIC)) {
+ continue;
+ }
+ otherField.accept(visitor);
}
- else if (Boolean.FALSE == aBoolean) {
- constant = CONSTANT_FALSE;
+ else if (child instanceof PsiMethod || child instanceof PsiClass) {
+ child.accept(visitor);
}
- else {
- throw new AssertionError();
+ if (visitor.isAssigned()) {
+ return true;
}
}
- else {
- constant = NOT_CONSTANT;
- }
- }
-
- @Override
- public void visitPrefixExpression(PsiPrefixExpression expression) {
- final IElementType tokenType = expression.getOperationTokenType();
- if (JavaTokenType.EXCL != tokenType) {
-
- }
- final PsiExpression operand = expression.getOperand();
- if (operand != null) {
- operand.accept(this);
- }
- if (constant == CONSTANT_FALSE) {
- constant = CONSTANT_TRUE;
- }
- else if (constant == CONSTANT_TRUE) {
- constant = CONSTANT_FALSE;
- }
}
-
- @Override
- public void visitWhileStatement(PsiWhileStatement statement) {
- final PsiExpression condition = statement.getCondition();
- final PsiStatement body = statement.getBody();
- final boolean da = definitelyAssigned;
- final boolean du = definitelyUnassigned;
- for (int i = 0; i < 2; i++) {
- if (condition != null) {
- condition.accept(this);
+ else {
+ for (PsiElement child : containingClass.getChildren()) {
+ if (child instanceof PsiField) {
+ final PsiField otherField = (PsiField)child;
+ if (!otherField.hasModifierProperty(PsiModifier.STATIC)) {
+ continue;
+ }
+ otherField.accept(visitor);
}
- final byte constant = this.constant;
- if (constant == CONSTANT_FALSE) {
- satisfyVacuously();
+ else if (child instanceof PsiClassInitializer) {
+ final PsiClassInitializer classInitializer = (PsiClassInitializer)child;
+ if (!classInitializer.hasModifierProperty(PsiModifier.STATIC)) {
+ continue;
+ }
+ classInitializer.accept(visitor);
}
- if (body != null) {
- body.accept(this);
+ else if (child instanceof PsiMethod) {
+ final PsiMethod method = (PsiMethod)child;
+ if (method.isConstructor()) {
+ continue;
+ }
+ method.accept(visitor);
}
- }
- if (constant != CONSTANT_TRUE /*|| exits.remove(statement)*/) {
- definitelyAssigned &= da;
- definitelyUnassigned &= du;
- }
- }
-
- @Override
- public void visitIfStatement(PsiIfStatement statement) {
- final PsiExpression condition = statement.getCondition();
- constant = NOT_CONSTANT;
- if (condition != null) {
- condition.accept(this);
- }
- final byte constant = this.constant;
- final PsiStatement thenBranch = statement.getThenBranch();
- final PsiStatement elseBranch = statement.getElseBranch();
- if (thenBranch == null) {
- return;
- }
- final boolean da = definitelyAssigned;
- final boolean du = definitelyUnassigned;
- if (constant == CONSTANT_FALSE) {
- satisfyVacuously();
- }
- thenBranch.accept(this);
- if (elseBranch == null) {
- if (constant != CONSTANT_TRUE) {
- definitelyAssigned &= da;
- definitelyUnassigned &= du;
+ else if (child instanceof PsiClass) {
+ child.accept(visitor);
}
- return;
- }
- final boolean thenDa = definitelyAssigned;
- final boolean thenDu = definitelyUnassigned;
- definitelyAssigned = da;
- definitelyUnassigned = du;
- if (constant == CONSTANT_TRUE) {
- satisfyVacuously();
- }
- elseBranch.accept(this);
- definitelyAssigned &= thenDa;
- definitelyUnassigned &= thenDu;
- }
-
- @Override
- public void visitConditionalExpression(
- PsiConditionalExpression expression) {
- final PsiType type = expression.getType();
- final boolean booleanConditional = PsiType.BOOLEAN.equals(type);
- final PsiExpression condition = expression.getCondition();
- constant = NOT_CONSTANT;
- condition.accept(this);
- final byte constant = this.constant;
- final PsiExpression thenExpression = expression.getThenExpression();
- final PsiExpression elseExpression = expression.getElseExpression();
- final boolean da = definitelyAssigned;
- final boolean du = definitelyUnassigned;
- if (constant == CONSTANT_FALSE) {
- satisfyVacuously();
- }
- if (thenExpression != null) {
- thenExpression.accept(this);
- }
- final boolean thenDa = definitelyAssigned;
- final boolean thenDu = definitelyUnassigned;
- definitelyAssigned = da;
- definitelyUnassigned = du;
- byte constantOut = NOT_CONSTANT;
- if (constant == CONSTANT_TRUE) {
- if (booleanConditional) {
- constantOut = this.constant;
+ if (visitor.isAssigned()) {
+ return true;
}
- satisfyVacuously();
- }
- if (elseExpression != null) {
- elseExpression.accept(this);
- }
- if (constant == CONSTANT_TRUE) {
- definitelyAssigned = thenDa;
- definitelyUnassigned = thenDu;
- this.constant = constantOut;
- }
- else if (constant != CONSTANT_FALSE) {
- definitelyAssigned &= thenDa;
- definitelyUnassigned &= thenDu;
- this.constant = NOT_CONSTANT;
}
}
+ return false;
+ }
- @Override
- public void visitBinaryExpression(PsiBinaryExpression expression) {
- final IElementType tokenType = expression.getOperationTokenType();
- if (JavaTokenType.ANDAND.equals(tokenType)) {
- final PsiExpression lhs = expression.getLOperand();
- constant = NOT_CONSTANT;
- lhs.accept(this);
- final byte constant = this.constant;
- final boolean da = definitelyAssigned;
- final boolean du = definitelyUnassigned;
- if (constant == CONSTANT_FALSE) {
- satisfyVacuously();
- }
- final PsiExpression rhs = expression.getROperand();
- if (rhs != null) {
- rhs.accept(this);
- }
- if (this.constant == CONSTANT_TRUE) {
- this.constant = constant;
- }
- else if (constant == CONSTANT_FALSE) {
- this.constant = CONSTANT_FALSE;
- definitelyAssigned = da;
- definitelyUnassigned = du;
- }
- else if (constant == NOT_CONSTANT) {
- this.constant = NOT_CONSTANT;
- }
- }
- else if (JavaTokenType.OROR.equals(tokenType)) {
- final PsiExpression lhs = expression.getLOperand();
- constant = NOT_CONSTANT;
- lhs.accept(this);
- final int constant = this.constant;
- final boolean da = definitelyAssigned;
- final boolean du = definitelyUnassigned;
- if (constant == CONSTANT_TRUE) {
- satisfyVacuously();
- }
- final PsiExpression rhs = expression.getROperand();
- if (rhs != null) {
- rhs.accept(this);
- }
- if (constant == CONSTANT_TRUE) {
- this.constant = CONSTANT_TRUE;
- }
- else if (constant == NOT_CONSTANT) {
- this.constant = NOT_CONSTANT;
- }
- if (constant == CONSTANT_TRUE) {
- definitelyAssigned = da;
- definitelyUnassigned = du;
- }
- }
- else {
- final PsiType type = expression.getType();
- if (PsiType.BOOLEAN.equals(type)) {
- final Object constant =
- ExpressionUtils.computeConstantExpression(
- expression);
- if (constant instanceof Boolean) {
- if (Boolean.TRUE == constant) {
- this.constant = CONSTANT_TRUE;
- }
- else if (Boolean.FALSE == constant) {
- this.constant = CONSTANT_FALSE;
- }
- else {
- this.constant = NOT_CONSTANT;
- }
- }
- else {
- this.constant = NOT_CONSTANT;
- }
- }
- if (constant == NOT_CONSTANT) {
- super.visitBinaryExpression(expression);
- }
- }
- }
+ private static class FinalDefiniteAssignment extends DefiniteAssignment {
- @Override
- public void visitMethodCallExpression(
- PsiMethodCallExpression expression) {
- super.visitMethodCallExpression(expression);
- final PsiReferenceExpression methodExpression =
- expression.getMethodExpression();
- final PsiExpression qualifierExpression =
- methodExpression.getQualifierExpression();
- if (qualifierExpression != null) {
- return;
- }
- @NonNls final String referenceName =
- methodExpression.getReferenceName();
- if (PsiKeyword.THIS.equals(referenceName)) {
- definitelyUnassigned = false;
- definitelyAssigned = true;
- }
- }
+ private boolean canBeFinal = true;
- @Override
- public void visitReturnStatement(PsiReturnStatement statement) {
- final PsiExpression returnValue = statement.getReturnValue();
- if (returnValue != null) {
- returnValue.accept(this);
- }
- if (!definitelyAssigned || definitelyUnassigned) {
- finalCandidate = false;
- }
- satisfyVacuously();
+ public FinalDefiniteAssignment(PsiVariable variable) {
+ super(variable);
}
@Override
- public void visitThrowStatement(PsiThrowStatement statement) {
- final PsiExpression exception = statement.getException();
- if (exception != null) {
- exception.accept(this);
+ public void assign(@NotNull PsiReferenceExpression expression, boolean definiteAssignment) {
+ if (!isDefinitelyUnassigned()) {
+ canBeFinal = false;
}
- satisfyVacuously();
+ super.assign(expression, definiteAssignment);
}
@Override
- public void visitBreakStatement(PsiBreakStatement statement) {
- final PsiStatement exit = statement.findExitedStatement();
- if (exit != null) {
- exits.add(exit);
+ public void valueAccess(PsiReferenceExpression expression) {
+ if (!isDefinitelyAssigned()) {
+ canBeFinal = false;
}
- satisfyVacuously();
+ super.valueAccess(expression);
}
@Override
- public void visitContinueStatement(PsiContinueStatement statement) {
- satisfyVacuously();
+ public boolean stop() {
+ return !canBeFinal;
}
- private void satisfyVacuously() {
- if (definitelyAssigned) {
- definitelyUnassigned = true;
- }
- }
-
- private static boolean isPrePostFixExpression(
- PsiReferenceExpression expression) {
- final PsiElement parent = PsiTreeUtil.skipParentsOfType(expression,
- PsiParenthesizedExpression.class);
- if (parent instanceof PsiPrefixExpression) {
- final PsiPrefixExpression prefixExpression =
- (PsiPrefixExpression)parent;
- final IElementType tokenType =
- prefixExpression.getOperationTokenType();
- if (tokenType == JavaTokenType.PLUSPLUS ||
- tokenType == JavaTokenType.MINUSMINUS) {
- return true;
- }
- }
- else if (parent instanceof PsiPostfixExpression) {
- final PsiPostfixExpression postfixExpression =
- (PsiPostfixExpression)parent;
- final IElementType tokenType =
- postfixExpression.getOperationTokenType();
- if (tokenType == JavaTokenType.PLUSPLUS ||
- tokenType == JavaTokenType.MINUSMINUS) {
- return true;
- }
- }
- return false;
+ public boolean canBeFinal() {
+ return canBeFinal;
}
}
} \ No newline at end of file