com.siyeh.ig.threading.VariableAccessVisitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-analysis-impl Show documentation
Show all versions of java-analysis-impl Show documentation
A packaging of the IntelliJ Community Edition java-analysis-impl library.
This is release number 1 of trunk branch 142.
The newest version!
/*
* Copyright 2003-2015 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.threading;
import com.intellij.psi.*;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.HashMap;
import com.siyeh.ig.psiutils.SynchronizationUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
class VariableAccessVisitor extends JavaRecursiveElementVisitor {
private final PsiClass aClass;
private final Set m_synchronizedAccesses =
new HashSet(2);
private final Set m_unsynchronizedAccesses =
new HashSet(2);
private final Set methodsAlwaysSynchronized =
new HashSet();
private final Set methodsNotAlwaysSynchronized =
new HashSet();
private final Set unusedMethods = new HashSet();
private final Set usedMethods = new HashSet();
private boolean m_inInitializer = false;
private boolean m_inSynchronizedContext = false;
private boolean privateMethodUsagesCalculated = false;
private final boolean countGettersAndSetters;
VariableAccessVisitor(PsiClass aClass, boolean countGettersAndSetters) {
this.aClass = aClass;
this.countGettersAndSetters = countGettersAndSetters;
}
@Override
public void visitClass(PsiClass classToVisit) {
calculatePrivateMethodUsagesIfNecessary();
final boolean wasInSync = m_inSynchronizedContext;
if (!classToVisit.equals(aClass)) {
m_inSynchronizedContext = false;
}
super.visitClass(classToVisit);
m_inSynchronizedContext = wasInSync;
}
@Override
public void visitLambdaExpression(PsiLambdaExpression expression) {
final boolean wasInSync = m_inSynchronizedContext;
m_inSynchronizedContext = false;
super.visitLambdaExpression(expression);
m_inSynchronizedContext = wasInSync;
}
@Override
public void visitReferenceExpression(@NotNull PsiReferenceExpression ref) {
super.visitReferenceExpression(ref);
final PsiExpression qualifier = ref.getQualifierExpression();
if (qualifier != null && !(qualifier instanceof PsiThisExpression)) {
return;
}
final PsiElement element = ref.resolve();
if (!(element instanceof PsiField)) {
return;
}
if (m_inInitializer) {
}
else if (m_inSynchronizedContext) {
m_synchronizedAccesses.add((PsiField)element);
}
else if (ref.getParent() instanceof PsiSynchronizedStatement) {
//covers the very specific case of a field reference being directly
// used as a lock
m_synchronizedAccesses.add((PsiField)element);
}
else {
m_unsynchronizedAccesses.add((PsiField)element);
}
}
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
if (!countGettersAndSetters) {
return;
}
final PsiReferenceExpression methodExpression =
expression.getMethodExpression();
final PsiExpression qualifier =
methodExpression.getQualifierExpression();
if (qualifier != null && !(qualifier instanceof PsiThisExpression)) {
return;
}
final PsiMethod method = (PsiMethod)methodExpression.resolve();
PsiField field = PropertyUtil.getFieldOfGetter(method);
if (field == null) {
field = PropertyUtil.getFieldOfSetter(method);
}
if (field == null) {
return;
}
if (m_inInitializer) {
}
else if (m_inSynchronizedContext) {
m_synchronizedAccesses.add(field);
}
else {
m_unsynchronizedAccesses.add(field);
}
}
@Override
public void visitCodeBlock(PsiCodeBlock block) {
final boolean wasInSync = m_inSynchronizedContext;
if (block.getParent() instanceof PsiSynchronizedStatement) {
m_inSynchronizedContext = true;
}
super.visitCodeBlock(block);
m_inSynchronizedContext = wasInSync;
}
@Override
public void visitAssertStatement(PsiAssertStatement statement) {
final PsiExpression condition = statement.getAssertCondition();
if (SynchronizationUtil.isCallToHoldsLock(condition)) {
m_inSynchronizedContext = true;
}
super.visitAssertStatement(statement);
}
@Override
public void visitMethod(@NotNull PsiMethod method) {
if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
if (unusedMethods.contains(method)) {
return;
}
}
final boolean methodIsSynchronized =
method.hasModifierProperty(PsiModifier.SYNCHRONIZED)
|| methodIsAlwaysUsedSynchronized(method);
boolean wasInSync = false;
if (methodIsSynchronized) {
wasInSync = m_inSynchronizedContext;
m_inSynchronizedContext = true;
}
final boolean isConstructor = method.isConstructor();
if (isConstructor) {
m_inInitializer = true;
}
super.visitMethod(method);
if (methodIsSynchronized) {
m_inSynchronizedContext = wasInSync;
}
if (isConstructor) {
m_inInitializer = false;
}
}
private boolean methodIsAlwaysUsedSynchronized(PsiMethod method) {
if (!method.hasModifierProperty(PsiModifier.PRIVATE)) {
return false;
}
return methodsAlwaysSynchronized.contains(method);
}
private void calculatePrivateMethodUsagesIfNecessary() {
if (privateMethodUsagesCalculated) {
return;
}
final Set privateMethods = findPrivateMethods();
final HashMap> referenceMap =
buildReferenceMap(privateMethods);
determineUsedMethods(privateMethods, referenceMap);
determineUsageMap(referenceMap);
privateMethodUsagesCalculated = true;
}
private void determineUsageMap(HashMap> referenceMap) {
final Set remainingMethods =
new HashSet(usedMethods);
boolean stabilized = false;
while (!stabilized) {
stabilized = true;
final Set methodsDeterminedThisPass =
new HashSet();
for (PsiMethod method : remainingMethods) {
final Collection references =
referenceMap.get(method);
boolean areAllReferencesSynchronized = true;
for (PsiReference reference : references) {
if (isKnownToBeUsed(reference)) {
if (isInKnownUnsynchronizedContext(reference)) {
methodsNotAlwaysSynchronized.add(method);
methodsDeterminedThisPass.add(method);
areAllReferencesSynchronized = false;
stabilized = false;
break;
}
if (!isInKnownSynchronizedContext(reference)) {
areAllReferencesSynchronized = false;
}
}
}
if (areAllReferencesSynchronized &&
unusedMethods.contains(method)) {
methodsAlwaysSynchronized.add(method);
methodsDeterminedThisPass.add(method);
stabilized = false;
}
}
remainingMethods.removeAll(methodsDeterminedThisPass);
}
methodsAlwaysSynchronized.addAll(remainingMethods);
}
private void determineUsedMethods(
Set privateMethods,
HashMap> referenceMap) {
final Set remainingMethods =
new HashSet(privateMethods);
boolean stabilized = false;
while (!stabilized) {
stabilized = true;
final Set methodsDeterminedThisPass =
new HashSet();
for (PsiMethod method : remainingMethods) {
final Collection references =
referenceMap.get(method);
for (PsiReference reference : references) {
if (isKnownToBeUsed(reference)) {
usedMethods.add(method);
methodsDeterminedThisPass.add(method);
stabilized = false;
}
}
}
remainingMethods.removeAll(methodsDeterminedThisPass);
}
unusedMethods.addAll(remainingMethods);
}
private static HashMap>
buildReferenceMap(Set privateMethods) {
final HashMap> referenceMap =
new HashMap();
for (PsiMethod method : privateMethods) {
final SearchScope scope = method.getUseScope();
final Collection references =
ReferencesSearch.search(method, scope).findAll();
referenceMap.put(method, references);
}
return referenceMap;
}
private Set findPrivateMethods() {
final Set privateMethods = new HashSet();
final PsiMethod[] methods = aClass.getMethods();
for (PsiMethod method : methods) {
if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
privateMethods.add(method);
}
}
return privateMethods;
}
private boolean isKnownToBeUsed(PsiReference reference) {
final PsiElement element = reference.getElement();
final PsiMethod method =
PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (method == null) {
return true;
}
if (!method.hasModifierProperty(PsiModifier.PRIVATE)) {
return true;
}
return usedMethods.contains(method);
}
private boolean isInKnownSynchronizedContext(PsiReference reference) {
final PsiElement element = reference.getElement();
if (PsiTreeUtil.getParentOfType(element,
PsiSynchronizedStatement.class) != null) {
return true;
}
final PsiMethod method =
PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (method == null) {
return false;
}
if (method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
return true;
}
if (methodsAlwaysSynchronized.contains(method)) {
return true;
}
return !methodsNotAlwaysSynchronized.contains(method);
}
private boolean isInKnownUnsynchronizedContext(PsiReference reference) {
final PsiElement element = reference.getElement();
if (PsiTreeUtil.getParentOfType(element,
PsiSynchronizedStatement.class) != null) {
return false;
}
final PsiMethod method =
PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (method == null) {
return true;
}
if (method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
return false;
}
if (!method.hasModifierProperty(PsiModifier.PRIVATE)) {
return true;
}
if (methodsAlwaysSynchronized.contains(method)) {
return false;
}
return methodsNotAlwaysSynchronized.contains(method);
}
@Override
public void visitClassInitializer(@NotNull PsiClassInitializer initializer) {
m_inInitializer = true;
super.visitClassInitializer(initializer);
m_inInitializer = false;
}
@Override
public void visitField(@NotNull PsiField field) {
m_inInitializer = true;
super.visitField(field);
m_inInitializer = false;
}
public Set getInappropriatelyAccessedFields() {
final Set out =
new HashSet(m_synchronizedAccesses);
out.retainAll(m_unsynchronizedAccesses);
return out;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy