blob: 6bacd4e023dd6024c18c8923c64974ed7ed17db9 [file] [log] [blame]
/*
* Copyright 2000-2009 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.intellij.codeInspection.canBeFinal;
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
import com.intellij.codeInspection.reference.*;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* User: anna
* Date: 27-Dec-2005
*/
class CanBeFinalAnnotator extends RefGraphAnnotatorEx {
private final RefManager myManager;
public static int CAN_BE_FINAL_MASK;
public CanBeFinalAnnotator(@NotNull RefManager manager) {
myManager = manager;
}
@Override
public void initialize(RefManager refManager) {
CAN_BE_FINAL_MASK = refManager.getLastUsedMask();
}
@Override
public void onInitialize(RefElement refElement) {
((RefElementImpl)refElement).setFlag(true, CAN_BE_FINAL_MASK);
if (refElement instanceof RefClass) {
final RefClass refClass = (RefClass)refElement;
final PsiClass psiClass = refClass.getElement();
if (refClass.isEntry()) {
((RefClassImpl)refClass).setFlag(false, CAN_BE_FINAL_MASK);
return;
}
if (psiClass != null && !refClass.isSelfInheritor(psiClass)) {
for (PsiClass psiSuperClass : psiClass.getSupers()) {
if (myManager.belongsToScope(psiSuperClass)) {
RefClass refSuperClass = (RefClass)myManager.getReference(psiSuperClass);
if (refSuperClass != null) {
((RefClassImpl)refSuperClass).setFlag(false, CAN_BE_FINAL_MASK);
}
}
}
}
if (refClass.isAbstract() || refClass.isAnonymous() || refClass.isInterface()) {
((RefClassImpl)refClass).setFlag(false, CAN_BE_FINAL_MASK);
}
}
else if (refElement instanceof RefMethod) {
final RefMethod refMethod = (RefMethod)refElement;
final PsiElement element = refMethod.getElement();
if (element instanceof PsiMethod) {
PsiMethod psiMethod = (PsiMethod)element;
if (refMethod.isConstructor() || refMethod.isAbstract() || refMethod.isStatic() ||
PsiModifier.PRIVATE.equals(refMethod.getAccessModifier()) || refMethod.getOwnerClass().isAnonymous() ||
refMethod.getOwnerClass().isInterface()) {
((RefMethodImpl)refMethod).setFlag(false, CAN_BE_FINAL_MASK);
}
if (PsiModifier.PRIVATE.equals(refMethod.getAccessModifier()) && refMethod.getOwner() != null &&
!(refMethod.getOwnerClass().getOwner() instanceof RefElement)) {
((RefMethodImpl)refMethod).setFlag(false, CAN_BE_FINAL_MASK);
}
for (PsiMethod psiSuperMethod : psiMethod.findSuperMethods()) {
if (myManager.belongsToScope(psiSuperMethod)) {
RefMethod refSuperMethod = (RefMethod)myManager.getReference(psiSuperMethod);
if (refSuperMethod != null) {
((RefMethodImpl)refSuperMethod).setFlag(false, CAN_BE_FINAL_MASK);
}
}
}
}
}
}
@Override
public void onMarkReferenced(RefElement refWhat,
RefElement refFrom,
boolean referencedFromClassInitializer,
boolean forReading,
boolean forWriting) {
if (!(refWhat instanceof RefField)) return;
if (!(refFrom instanceof RefMethod) ||
!((RefMethod)refFrom).isConstructor() ||
((PsiField)refWhat.getElement()).hasInitializer() ||
((RefMethod)refFrom).getOwnerClass() != ((RefField)refWhat).getOwnerClass() ||
((RefField)refWhat).isStatic()) {
if (!referencedFromClassInitializer && forWriting) {
((RefFieldImpl)refWhat).setFlag(false, CAN_BE_FINAL_MASK);
}
}
}
@Override
public void onReferencesBuild(RefElement refElement) {
if (refElement instanceof RefClass) {
final PsiClass psiClass = (PsiClass)refElement.getElement();
if (psiClass != null) {
if (refElement.isEntry()) {
((RefClassImpl)refElement).setFlag(false, CAN_BE_FINAL_MASK);
}
PsiMethod[] psiMethods = psiClass.getMethods();
PsiField[] psiFields = psiClass.getFields();
Set<PsiVariable> allFields = new HashSet<PsiVariable>();
ContainerUtil.addAll(allFields, psiFields);
List<PsiVariable> instanceInitializerInitializedFields = new ArrayList<PsiVariable>();
boolean hasInitializers = false;
for (PsiClassInitializer initializer : psiClass.getInitializers()) {
PsiCodeBlock body = initializer.getBody();
hasInitializers = true;
ControlFlow flow;
try {
flow = ControlFlowFactory.getInstance(body.getProject())
.getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
}
catch (AnalysisCanceledException e) {
flow = ControlFlow.EMPTY;
}
Collection<PsiVariable> writtenVariables = new ArrayList<PsiVariable>();
ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false, writtenVariables);
for (PsiVariable psiVariable : writtenVariables) {
if (allFields.contains(psiVariable)) {
if (instanceInitializerInitializedFields.contains(psiVariable)) {
allFields.remove(psiVariable);
instanceInitializerInitializedFields.remove(psiVariable);
}
else {
instanceInitializerInitializedFields.add(psiVariable);
}
}
}
for (PsiVariable psiVariable : writtenVariables) {
if (!instanceInitializerInitializedFields.contains(psiVariable)) {
allFields.remove(psiVariable);
}
}
}
for (PsiMethod psiMethod : psiMethods) {
if (psiMethod.isConstructor()) {
PsiCodeBlock body = psiMethod.getBody();
if (body != null) {
hasInitializers = true;
ControlFlow flow;
try {
flow = ControlFlowFactory.getInstance(body.getProject())
.getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
}
catch (AnalysisCanceledException e) {
flow = ControlFlow.EMPTY;
}
Collection<PsiVariable> writtenVariables = ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false);
for (PsiVariable psiVariable : writtenVariables) {
if (instanceInitializerInitializedFields.contains(psiVariable)) {
allFields.remove(psiVariable);
instanceInitializerInitializedFields.remove(psiVariable);
}
}
List<PsiMethod> redirectedConstructors = JavaHighlightUtil.getChainedConstructors(psiMethod);
if (redirectedConstructors == null || redirectedConstructors.isEmpty()) {
List<PsiVariable> ssaVariables = ControlFlowUtil.getSSAVariables(flow);
ArrayList<PsiVariable> good = new ArrayList<PsiVariable>(ssaVariables);
good.addAll(instanceInitializerInitializedFields);
allFields.retainAll(good);
}
else {
allFields.removeAll(writtenVariables);
}
}
}
}
for (PsiField psiField : psiFields) {
if ((!hasInitializers || !allFields.contains(psiField)) && psiField.getInitializer() == null) {
final RefFieldImpl refField = (RefFieldImpl)myManager.getReference(psiField);
if (refField != null) {
refField.setFlag(false, CAN_BE_FINAL_MASK);
}
}
}
}
}
else if (refElement instanceof RefMethod) {
final RefMethod refMethod = (RefMethod)refElement;
if (refMethod.isEntry()) {
((RefMethodImpl)refMethod).setFlag(false, CAN_BE_FINAL_MASK);
}
}
}
}