constructor reference: don't ignore constructor parameters during method reference...
[idea/community.git] / java / java-analysis-impl / src / com / intellij / codeInspection / canBeFinal / CanBeFinalAnnotator.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.codeInspection.canBeFinal;
17
18 import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
19 import com.intellij.codeInspection.reference.*;
20 import com.intellij.psi.*;
21 import com.intellij.psi.controlFlow.*;
22 import com.intellij.psi.util.PsiTreeUtil;
23 import com.intellij.psi.util.PsiTypesUtil;
24 import com.intellij.util.ObjectUtils;
25 import com.intellij.util.containers.ContainerUtil;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.uast.UClass;
28 import org.jetbrains.uast.UTypeReferenceExpression;
29
30 import java.util.*;
31
32 class CanBeFinalAnnotator extends RefGraphAnnotatorEx {
33   private final RefManager myManager;
34   static long CAN_BE_FINAL_MASK;
35
36   CanBeFinalAnnotator(@NotNull RefManager manager) {
37     myManager = manager;
38   }
39
40   @Override
41   public void initialize(RefManager refManager) {
42     CAN_BE_FINAL_MASK = refManager.getLastUsedMask();
43   }
44
45   @Override
46   public void onInitialize(RefElement refElement) {
47     ((RefElementImpl)refElement).setFlag(true, CAN_BE_FINAL_MASK);
48     if (refElement instanceof RefClass) {
49       final RefClass refClass = (RefClass)refElement;
50       final UClass psiClass = refClass.getUastElement();
51       if (refClass.isEntry()) {
52         ((RefClassImpl)refClass).setFlag(false, CAN_BE_FINAL_MASK);
53         return;
54       }
55       if (psiClass != null && !refClass.isSelfInheritor(psiClass)) {
56         for (UTypeReferenceExpression superRef : psiClass.getUastSuperTypes()) {
57           PsiElement psi = PsiTypesUtil.getPsiClass(superRef.getType());
58           if (myManager.belongsToScope(psi)) {
59             RefClass refSuperClass = (RefClass)myManager.getReference(psi);
60             if (refSuperClass != null) {
61               ((RefClassImpl)refSuperClass).setFlag(false, CAN_BE_FINAL_MASK);
62             }
63           }
64         }
65       }
66       if (refClass.isAbstract() || refClass.isAnonymous() || refClass.isInterface()) {
67         ((RefClassImpl)refClass).setFlag(false, CAN_BE_FINAL_MASK);
68       }
69     }
70     else if (refElement instanceof RefMethod) {
71       final RefMethod refMethod = (RefMethod)refElement;
72       final PsiElement element = refMethod.getPsiElement();
73       if (element instanceof PsiMethod) {
74         PsiMethod psiMethod = (PsiMethod)element;
75         RefClass aClass = refMethod.getOwnerClass();
76         if (refMethod.isConstructor() || refMethod.isAbstract() || refMethod.isStatic() ||
77             PsiModifier.PRIVATE.equals(refMethod.getAccessModifier()) || (aClass != null && aClass.isAnonymous()) ||
78             (aClass != null && aClass.isInterface())) {
79           ((RefMethodImpl)refMethod).setFlag(false, CAN_BE_FINAL_MASK);
80         }
81         if (PsiModifier.PRIVATE.equals(refMethod.getAccessModifier()) && refMethod.getOwner() != null &&
82             !(aClass != null && aClass.getOwner() instanceof RefElement)) {
83           ((RefMethodImpl)refMethod).setFlag(false, CAN_BE_FINAL_MASK);
84         }
85         for (PsiMethod psiSuperMethod : psiMethod.findSuperMethods()) {
86           if (myManager.belongsToScope(psiSuperMethod)) {
87             RefMethod refSuperMethod = (RefMethod)myManager.getReference(psiSuperMethod);
88             if (refSuperMethod != null) {
89               ((RefMethodImpl)refSuperMethod).setFlag(false, CAN_BE_FINAL_MASK);
90             }
91           }
92         }
93       }
94     }
95     else if (refElement instanceof RefField) {
96       final PsiElement element = refElement.getPsiElement();
97       if (RefUtil.isImplicitWrite(element)) {
98         ((RefElementImpl)refElement).setFlag(false, CAN_BE_FINAL_MASK);
99       }
100     }
101   }
102
103
104   @Override
105   public void onMarkReferenced(RefElement refWhat,
106                                RefElement refFrom,
107                                boolean referencedFromClassInitializer,
108                                boolean forReading,
109                                boolean forWriting,
110                                PsiElement referenceElement) {
111     if (!(refWhat instanceof RefField)) return;
112     if (!(refFrom instanceof RefMethod) ||
113         !((RefMethod)refFrom).isConstructor() ||
114         ((RefField)refWhat).getUastElement().getUastInitializer() != null ||
115         ((RefMethod)refFrom).getOwnerClass() != ((RefField)refWhat).getOwnerClass() ||
116         ((RefField)refWhat).isStatic()) {
117       if (forWriting &&
118           !(referencedFromClassInitializer && PsiTreeUtil.getParentOfType(referenceElement, PsiLambdaExpression.class, true) == null)) {
119         ((RefFieldImpl)refWhat).setFlag(false, CAN_BE_FINAL_MASK);
120       }
121     }
122     else if (forWriting && PsiTreeUtil.getParentOfType(referenceElement, PsiLambdaExpression.class, true) != null) {
123       ((RefFieldImpl)refWhat).setFlag(false, CAN_BE_FINAL_MASK);
124     }
125   }
126
127   @Override
128   public void onReferencesBuild(RefElement refElement) {
129     if (refElement instanceof RefClass) {
130       final PsiClass psiClass = ObjectUtils.tryCast(refElement.getPsiElement(), PsiClass.class);
131       if (psiClass != null) {
132
133         if (refElement.isEntry()) {
134           ((RefClassImpl)refElement).setFlag(false, CAN_BE_FINAL_MASK);
135         }
136
137
138         PsiField[] psiFields = psiClass.getFields();
139
140         Set<PsiVariable> allFields = new HashSet<>();
141         ContainerUtil.addAll(allFields, psiFields);
142         List<PsiVariable> instanceInitializerInitializedFields = new ArrayList<>();
143         Set<PsiField> fieldsInitializedInInitializers = null;
144
145         for (PsiClassInitializer initializer : psiClass.getInitializers()) {
146           PsiCodeBlock body = initializer.getBody();
147           ControlFlow flow;
148           try {
149             flow = ControlFlowFactory.getInstance(body.getProject())
150               .getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
151           }
152           catch (AnalysisCanceledException e) {
153             flow = ControlFlow.EMPTY;
154           }
155           Collection<PsiVariable> writtenVariables = new ArrayList<>();
156           ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false, writtenVariables);
157           if (fieldsInitializedInInitializers == null) {
158             fieldsInitializedInInitializers = new HashSet<>();
159           }
160           for (PsiVariable psiVariable : writtenVariables) {
161             if (allFields.contains(psiVariable) && ControlFlowUtil.isVariableDefinitelyAssigned(psiVariable, flow)) {
162               if (instanceInitializerInitializedFields.contains(psiVariable)) {
163                 allFields.remove(psiVariable);
164                 instanceInitializerInitializedFields.remove(psiVariable);
165               }
166               else {
167                 instanceInitializerInitializedFields.add(psiVariable);
168               }
169               fieldsInitializedInInitializers.add((PsiField)psiVariable);
170             }
171           }
172           for (PsiVariable psiVariable : writtenVariables) {
173             if (!instanceInitializerInitializedFields.contains(psiVariable)) {
174               allFields.remove(psiVariable);
175             }
176           }
177         }
178
179         for (PsiMethod constructor : psiClass.getConstructors()) {
180           PsiCodeBlock body = constructor.getBody();
181           if (body != null) {
182             ControlFlow flow;
183             try {
184               flow = ControlFlowFactory.getInstance(body.getProject())
185                 .getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
186             }
187             catch (AnalysisCanceledException e) {
188               flow = ControlFlow.EMPTY;
189             }
190
191             Collection<PsiVariable> writtenVariables = ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false);
192             for (PsiVariable psiVariable : writtenVariables) {
193               if (instanceInitializerInitializedFields.contains(psiVariable)) {
194                 allFields.remove(psiVariable);
195                 instanceInitializerInitializedFields.remove(psiVariable);
196               }
197             }
198             List<PsiMethod> redirectedConstructors = JavaHighlightUtil.getChainedConstructors(constructor);
199             if (redirectedConstructors.isEmpty()) {
200               List<PsiVariable> ssaVariables = ControlFlowUtil.getSSAVariables(flow);
201               ArrayList<PsiVariable> good = new ArrayList<>(ssaVariables);
202               good.addAll(instanceInitializerInitializedFields);
203               allFields.retainAll(good);
204             }
205             else {
206               allFields.removeAll(writtenVariables);
207             }
208           }
209         }
210
211         for (PsiField psiField : psiFields) {
212           if ((fieldsInitializedInInitializers != null && !fieldsInitializedInInitializers.contains(psiField) ||
213                !allFields.contains(psiField)) && psiField.getInitializer() == null) {
214             final RefFieldImpl refField = (RefFieldImpl)myManager.getReference(psiField);
215             if (refField != null) {
216               refField.setFlag(false, CAN_BE_FINAL_MASK);
217             }
218           }
219         }
220
221       }
222     }
223     else if (refElement instanceof RefMethod) {
224       final RefMethod refMethod = (RefMethod)refElement;
225       if (refMethod.isEntry()) {
226         ((RefMethodImpl)refMethod).setFlag(false, CAN_BE_FINAL_MASK);
227       }
228     }
229   }
230 }