IDEA-51944
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / analysis / HighlightUtil.java
blob9aaed86e9c53b3e850483aafd55b4b25eab1b001
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Created by IntelliJ IDEA.
19 * User: cdr
20 * Date: Jul 30, 2002
22 package com.intellij.codeInsight.daemon.impl.analysis;
24 import com.intellij.codeInsight.ExceptionUtil;
25 import com.intellij.codeInsight.daemon.JavaErrorMessages;
26 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
27 import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
28 import com.intellij.codeInsight.daemon.impl.quickfix.*;
29 import com.intellij.codeInsight.highlighting.HighlightUsagesDescriptionLocation;
30 import com.intellij.codeInsight.intention.IntentionAction;
31 import com.intellij.codeInsight.intention.QuickFixFactory;
32 import com.intellij.codeInsight.quickfix.ChangeVariableTypeQuickFixProvider;
33 import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
34 import com.intellij.lang.StdLanguages;
35 import com.intellij.lang.findUsages.LanguageFindUsages;
36 import com.intellij.openapi.diagnostic.Logger;
37 import com.intellij.openapi.extensions.Extensions;
38 import com.intellij.openapi.project.Project;
39 import com.intellij.openapi.util.Comparing;
40 import com.intellij.openapi.util.Key;
41 import com.intellij.openapi.util.Pair;
42 import com.intellij.openapi.util.TextRange;
43 import com.intellij.psi.*;
44 import com.intellij.psi.impl.source.jsp.jspJava.JspHolderMethod;
45 import com.intellij.psi.javadoc.PsiDocComment;
46 import com.intellij.psi.jsp.JspFile;
47 import com.intellij.psi.scope.processor.VariablesNotProcessor;
48 import com.intellij.psi.scope.util.PsiScopesUtil;
49 import com.intellij.psi.tree.IElementType;
50 import com.intellij.psi.util.*;
51 import com.intellij.util.ArrayUtil;
52 import com.intellij.util.IncorrectOperationException;
53 import com.intellij.xml.util.XmlStringUtil;
54 import gnu.trove.THashMap;
55 import gnu.trove.THashSet;
56 import org.jetbrains.annotations.NonNls;
57 import org.jetbrains.annotations.NotNull;
58 import org.jetbrains.annotations.Nullable;
60 import java.util.*;
62 public class HighlightUtil {
63 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil");
64 private static final Map<String, Set<String>> ourInterfaceIncompatibleModifiers;
65 private static final Map<String, Set<String>> ourMethodIncompatibleModifiers;
66 private static final Map<String, Set<String>> ourFieldIncompatibleModifiers;
67 private static final Map<String, Set<String>> ourClassIncompatibleModifiers;
68 private static final Map<String, Set<String>> ourClassInitializerIncompatibleModifiers;
69 private static final Set<String> ourConstructorNotAllowedModifiers;
71 @NonNls private static final String SERIAL_VERSION_UID_FIELD_NAME = "serialVersionUID";
72 @NonNls private static final String SERIAL_PERSISTENT_FIELDS_FIELD_NAME = "serialPersistentFields";
73 private static final QuickFixFactory QUICK_FIX_FACTORY = QuickFixFactory.getInstance();
75 private HighlightUtil() {
78 static {
79 ourClassIncompatibleModifiers = new THashMap<String, Set<String>>(8);
80 Set<String> modifiers = new THashSet<String>(1);
81 modifiers.add(PsiModifier.FINAL);
82 ourClassIncompatibleModifiers.put(PsiModifier.ABSTRACT, modifiers);
83 modifiers = new THashSet<String>(1);
84 modifiers.add(PsiModifier.ABSTRACT);
85 ourClassIncompatibleModifiers.put(PsiModifier.FINAL, modifiers);
86 modifiers = new THashSet<String>(3);
87 modifiers.add(PsiModifier.PRIVATE);
88 modifiers.add(PsiModifier.PUBLIC);
89 modifiers.add(PsiModifier.PROTECTED);
90 ourClassIncompatibleModifiers.put(PsiModifier.PACKAGE_LOCAL, modifiers);
91 modifiers = new THashSet<String>(3);
92 modifiers.add(PsiModifier.PACKAGE_LOCAL);
93 modifiers.add(PsiModifier.PUBLIC);
94 modifiers.add(PsiModifier.PROTECTED);
95 ourClassIncompatibleModifiers.put(PsiModifier.PRIVATE, modifiers);
96 modifiers = new THashSet<String>(3);
97 modifiers.add(PsiModifier.PACKAGE_LOCAL);
98 modifiers.add(PsiModifier.PRIVATE);
99 modifiers.add(PsiModifier.PROTECTED);
100 ourClassIncompatibleModifiers.put(PsiModifier.PUBLIC, modifiers);
101 modifiers = new THashSet<String>(3);
102 modifiers.add(PsiModifier.PACKAGE_LOCAL);
103 modifiers.add(PsiModifier.PUBLIC);
104 modifiers.add(PsiModifier.PRIVATE);
105 ourClassIncompatibleModifiers.put(PsiModifier.PROTECTED, modifiers);
106 ourClassIncompatibleModifiers.put(PsiModifier.STRICTFP, Collections.<String>emptySet());
107 ourClassIncompatibleModifiers.put(PsiModifier.STATIC, Collections.<String>emptySet());
108 ourInterfaceIncompatibleModifiers = new THashMap<String, Set<String>>(7);
109 ourInterfaceIncompatibleModifiers.put(PsiModifier.ABSTRACT, Collections.<String>emptySet());
110 modifiers = new THashSet<String>(3);
111 modifiers.add(PsiModifier.PRIVATE);
112 modifiers.add(PsiModifier.PUBLIC);
113 modifiers.add(PsiModifier.PROTECTED);
114 ourInterfaceIncompatibleModifiers.put(PsiModifier.PACKAGE_LOCAL, modifiers);
115 modifiers = new THashSet<String>(3);
116 modifiers.add(PsiModifier.PACKAGE_LOCAL);
117 modifiers.add(PsiModifier.PUBLIC);
118 modifiers.add(PsiModifier.PROTECTED);
119 ourInterfaceIncompatibleModifiers.put(PsiModifier.PRIVATE, modifiers);
120 modifiers = new THashSet<String>(3);
121 modifiers.add(PsiModifier.PRIVATE);
122 modifiers.add(PsiModifier.PACKAGE_LOCAL);
123 modifiers.add(PsiModifier.PROTECTED);
124 ourInterfaceIncompatibleModifiers.put(PsiModifier.PUBLIC, modifiers);
125 modifiers = new THashSet<String>(3);
126 modifiers.add(PsiModifier.PRIVATE);
127 modifiers.add(PsiModifier.PUBLIC);
128 modifiers.add(PsiModifier.PACKAGE_LOCAL);
129 ourInterfaceIncompatibleModifiers.put(PsiModifier.PROTECTED, modifiers);
130 ourInterfaceIncompatibleModifiers.put(PsiModifier.STRICTFP, Collections.<String>emptySet());
131 ourInterfaceIncompatibleModifiers.put(PsiModifier.STATIC, Collections.<String>emptySet());
132 ourMethodIncompatibleModifiers = new THashMap<String, Set<String>>(10);
133 modifiers = new THashSet<String>(6);
134 modifiers.addAll(Arrays.asList(PsiModifier.NATIVE, PsiModifier.STATIC, PsiModifier.FINAL, PsiModifier.PRIVATE, PsiModifier.STRICTFP,
135 PsiModifier.SYNCHRONIZED));
136 ourMethodIncompatibleModifiers.put(PsiModifier.ABSTRACT, modifiers);
137 modifiers = new THashSet<String>(2);
138 modifiers.add(PsiModifier.ABSTRACT);
139 modifiers.add(PsiModifier.STRICTFP);
140 ourMethodIncompatibleModifiers.put(PsiModifier.NATIVE, modifiers);
141 modifiers = new THashSet<String>(3);
142 modifiers.add(PsiModifier.PRIVATE);
143 modifiers.add(PsiModifier.PUBLIC);
144 modifiers.add(PsiModifier.PROTECTED);
145 ourMethodIncompatibleModifiers.put(PsiModifier.PACKAGE_LOCAL, modifiers);
146 modifiers = new THashSet<String>(4);
147 modifiers.add(PsiModifier.ABSTRACT);
148 modifiers.add(PsiModifier.PACKAGE_LOCAL);
149 modifiers.add(PsiModifier.PUBLIC);
150 modifiers.add(PsiModifier.PROTECTED);
151 ourMethodIncompatibleModifiers.put(PsiModifier.PRIVATE, modifiers);
152 modifiers = new THashSet<String>(3);
153 modifiers.add(PsiModifier.PACKAGE_LOCAL);
154 modifiers.add(PsiModifier.PRIVATE);
155 modifiers.add(PsiModifier.PROTECTED);
156 ourMethodIncompatibleModifiers.put(PsiModifier.PUBLIC, modifiers);
157 modifiers = new THashSet<String>(3);
158 modifiers.add(PsiModifier.PACKAGE_LOCAL);
159 modifiers.add(PsiModifier.PUBLIC);
160 modifiers.add(PsiModifier.PRIVATE);
161 ourMethodIncompatibleModifiers.put(PsiModifier.PROTECTED, modifiers);
162 modifiers = new THashSet<String>(1);
163 modifiers.add(PsiModifier.ABSTRACT);
164 ourMethodIncompatibleModifiers.put(PsiModifier.STATIC, modifiers);
165 ourMethodIncompatibleModifiers.put(PsiModifier.SYNCHRONIZED, modifiers);
166 ourMethodIncompatibleModifiers.put(PsiModifier.STRICTFP, modifiers);
167 ourMethodIncompatibleModifiers.put(PsiModifier.FINAL, modifiers);
168 ourFieldIncompatibleModifiers = new THashMap<String, Set<String>>(8);
169 modifiers = new THashSet<String>(1);
170 modifiers.add(PsiModifier.VOLATILE);
171 ourFieldIncompatibleModifiers.put(PsiModifier.FINAL, modifiers);
172 modifiers = new THashSet<String>(3);
173 modifiers.add(PsiModifier.PRIVATE);
174 modifiers.add(PsiModifier.PUBLIC);
175 modifiers.add(PsiModifier.PROTECTED);
176 ourFieldIncompatibleModifiers.put(PsiModifier.PACKAGE_LOCAL, modifiers);
177 modifiers = new THashSet<String>(3);
178 modifiers.add(PsiModifier.PACKAGE_LOCAL);
179 modifiers.add(PsiModifier.PUBLIC);
180 modifiers.add(PsiModifier.PROTECTED);
181 ourFieldIncompatibleModifiers.put(PsiModifier.PRIVATE, modifiers);
182 modifiers = new THashSet<String>(3);
183 modifiers.add(PsiModifier.PACKAGE_LOCAL);
184 modifiers.add(PsiModifier.PRIVATE);
185 modifiers.add(PsiModifier.PROTECTED);
186 ourFieldIncompatibleModifiers.put(PsiModifier.PUBLIC, modifiers);
187 modifiers = new THashSet<String>(3);
188 modifiers.add(PsiModifier.PACKAGE_LOCAL);
189 modifiers.add(PsiModifier.PRIVATE);
190 modifiers.add(PsiModifier.PUBLIC);
191 ourFieldIncompatibleModifiers.put(PsiModifier.PROTECTED, modifiers);
192 ourFieldIncompatibleModifiers.put(PsiModifier.STATIC, Collections.<String>emptySet());
193 ourFieldIncompatibleModifiers.put(PsiModifier.TRANSIENT, Collections.<String>emptySet());
194 modifiers = new THashSet<String>(1);
195 modifiers.add(PsiModifier.FINAL);
196 ourFieldIncompatibleModifiers.put(PsiModifier.VOLATILE, modifiers);
198 ourClassInitializerIncompatibleModifiers = new THashMap<String, Set<String>>(1);
199 ourClassInitializerIncompatibleModifiers.put(PsiModifier.STATIC, Collections.<String>emptySet());
201 ourConstructorNotAllowedModifiers = new THashSet<String>(6);
202 ourConstructorNotAllowedModifiers.add(PsiModifier.ABSTRACT);
203 ourConstructorNotAllowedModifiers.add(PsiModifier.STATIC);
204 ourConstructorNotAllowedModifiers.add(PsiModifier.NATIVE);
205 ourConstructorNotAllowedModifiers.add(PsiModifier.FINAL);
206 ourConstructorNotAllowedModifiers.add(PsiModifier.STRICTFP);
207 ourConstructorNotAllowedModifiers.add(PsiModifier.SYNCHRONIZED);
210 @Nullable
211 public static String getIncompatibleModifier(String modifier,
212 PsiModifierList modifierList,
213 Map<String, Set<String>> incompatibleModifiersHash) {
214 if (modifierList == null) return null;
215 // modifier is always incompatible with itself
216 PsiElement[] modifiers = modifierList.getChildren();
217 int modifierCount = 0;
218 for (PsiElement otherModifier : modifiers) {
219 if (Comparing.equal(modifier, otherModifier.getText(), true)) modifierCount++;
221 if (modifierCount > 1) {
222 return modifier;
225 Set<String> incompatibles = incompatibleModifiersHash.get(modifier);
226 if (incompatibles == null) return null;
227 for (@Modifier String incompatible : incompatibles) {
228 if (modifierList.hasModifierProperty(incompatible)) {
229 return incompatible;
232 return null;
236 * make element protected/package local/public suggestion
238 static void registerAccessQuickFixAction(PsiMember refElement,
239 PsiJavaCodeReferenceElement place,
240 HighlightInfo errorResult,
241 final PsiElement fileResolveScope) {
242 if (refElement instanceof PsiCompiledElement) return;
243 PsiModifierList modifierList = refElement.getModifierList();
244 if (modifierList == null) return;
246 PsiClass packageLocalClassInTheMiddle = getPackageLocalClassInTheMiddle(place);
247 if (packageLocalClassInTheMiddle != null) {
248 IntentionAction fix =
249 QUICK_FIX_FACTORY.createModifierListFix(packageLocalClassInTheMiddle, PsiModifier.PUBLIC, true, true);
250 QuickFixAction.registerQuickFixAction(errorResult, fix);
251 return;
254 try {
255 Project project = refElement.getProject();
256 JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
257 PsiModifierList modifierListCopy = facade.getElementFactory().createFieldFromText("int a;", null).getModifierList();
258 modifierListCopy.setModifierProperty(PsiModifier.STATIC, modifierList.hasModifierProperty(PsiModifier.STATIC));
259 @Modifier String minModifier = PsiModifier.PACKAGE_LOCAL;
260 if (refElement.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) {
261 minModifier = PsiModifier.PROTECTED;
263 if (refElement.hasModifierProperty(PsiModifier.PROTECTED)) {
264 minModifier = PsiModifier.PUBLIC;
266 String[] modifiers = {PsiModifier.PACKAGE_LOCAL, PsiModifier.PROTECTED, PsiModifier.PUBLIC,};
267 PsiClass accessObjectClass = PsiTreeUtil.getParentOfType(place, PsiClass.class, false);
269 for (int i = ArrayUtil.indexOf(modifiers, minModifier); i < modifiers.length; i++) {
270 @Modifier String modifier = modifiers[i];
271 modifierListCopy.setModifierProperty(modifier, true);
272 if (facade.getResolveHelper().isAccessible(refElement, modifierListCopy, place, accessObjectClass, fileResolveScope)) {
273 IntentionAction fix = QUICK_FIX_FACTORY.createModifierListFix(refElement, modifier, true, true);
274 TextRange fixRange = new TextRange(errorResult.startOffset, errorResult.endOffset);
275 PsiElement ref = place.getReferenceNameElement();
276 if (ref != null) {
277 fixRange = fixRange.union(ref.getTextRange());
279 QuickFixAction.registerQuickFixAction(errorResult, fixRange, fix, null);
283 catch (IncorrectOperationException e) {
284 LOG.error(e);
288 @Nullable
289 private static PsiClass getPackageLocalClassInTheMiddle(PsiJavaCodeReferenceElement place) {
290 if (place instanceof PsiReferenceExpression) {
291 // check for package local classes in the middle
292 PsiReferenceExpression expression = (PsiReferenceExpression)place;
293 while (true) {
294 PsiElement resolved = expression.resolve();
295 if (resolved instanceof PsiField) {
296 PsiField field = (PsiField)resolved;
297 PsiClass aClass = field.getContainingClass();
298 if (aClass != null && aClass.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) &&
299 !JavaPsiFacade.getInstance(aClass.getProject()).arePackagesTheSame(aClass, place)) {
301 return aClass;
304 PsiExpression qualifier = expression.getQualifierExpression();
305 if (!(qualifier instanceof PsiReferenceExpression)) break;
306 expression = (PsiReferenceExpression)qualifier;
309 return null;
313 @Nullable
314 static HighlightInfo checkInstanceOfApplicable(PsiInstanceOfExpression expression) {
315 PsiExpression operand = expression.getOperand();
316 PsiTypeElement typeElement = expression.getCheckType();
317 if (typeElement == null) return null;
318 PsiType checkType = typeElement.getType();
319 PsiType operandType = operand.getType();
320 if (operandType == null) return null;
321 if (TypeConversionUtil.isPrimitiveAndNotNull(operandType)
322 || TypeConversionUtil.isPrimitiveAndNotNull(checkType)
323 || !TypeConversionUtil.areTypesConvertible(operandType, checkType)) {
324 String message = JavaErrorMessages.message("inconvertible.type.cast", formatType(operandType), formatType(checkType));
325 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, message);
327 return null;
331 @Nullable
332 static HighlightInfo checkInconvertibleTypeCast(PsiTypeCastExpression expression) {
333 PsiExpression operand = expression.getOperand();
334 PsiType castType = expression.getCastType().getType();
335 PsiType operandType = operand == null ? null : operand.getType();
336 if (operandType != null && !TypeConversionUtil.areTypesConvertible(operandType, castType)) {
337 String message = JavaErrorMessages.message("inconvertible.type.cast", formatType(operandType), formatType(castType));
338 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, message);
340 return null;
344 static HighlightInfo checkVariableExpected(PsiExpression expression) {
345 PsiExpression lValue;
346 if (expression instanceof PsiAssignmentExpression) {
347 PsiAssignmentExpression assignment = (PsiAssignmentExpression)expression;
348 lValue = assignment.getLExpression();
350 else if (PsiUtil.isIncrementDecrementOperation(expression)) {
351 lValue = expression instanceof PsiPostfixExpression
352 ? ((PsiPostfixExpression)expression).getOperand()
353 : ((PsiPrefixExpression)expression).getOperand();
355 else {
356 lValue = null;
358 HighlightInfo errorResult = null;
359 if (lValue != null && !TypeConversionUtil.isLValue(lValue)) {
360 errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, lValue, JavaErrorMessages.message("variable.expected"));
363 return errorResult;
367 @Nullable
368 static HighlightInfo checkAssignmentOperatorApplicable(PsiAssignmentExpression assignment) {
369 PsiJavaToken operationSign = assignment.getOperationSign();
370 IElementType eqOpSign = operationSign.getTokenType();
371 IElementType opSign = TypeConversionUtil.convertEQtoOperation(eqOpSign);
372 if (opSign == null) return null;
373 HighlightInfo errorResult = null;
374 if (!TypeConversionUtil.isBinaryOperatorApplicable(opSign, assignment.getLExpression(), assignment.getRExpression(), true)) {
375 String operatorText = operationSign.getText().substring(0, operationSign.getText().length() - 1);
376 String message = JavaErrorMessages.message("binary.operator.not.applicable", operatorText,
377 formatType(assignment.getLExpression().getType()),
378 formatType(assignment.getRExpression().getType()));
380 errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, assignment, message);
382 return errorResult;
386 @Nullable
387 static HighlightInfo checkAssignmentCompatibleTypes(PsiAssignmentExpression assignment) {
388 if (!"=".equals(assignment.getOperationSign().getText())) return null;
389 PsiExpression lExpr = assignment.getLExpression();
390 PsiExpression rExpr = assignment.getRExpression();
391 if (rExpr == null) return null;
392 PsiType lType = lExpr.getType();
393 PsiType rType = rExpr.getType();
394 if (rType == null) return null;
396 HighlightInfo highlightInfo = checkAssignability(lType, rType, rExpr, assignment);
397 if (highlightInfo != null) {
398 PsiVariable leftVar = null;
399 if (lExpr instanceof PsiReferenceExpression) {
400 PsiElement element = ((PsiReferenceExpression)lExpr).resolve();
401 if (element instanceof PsiVariable) {
402 leftVar = (PsiVariable)element;
405 if (leftVar != null) {
406 registerChangeVariableTypeFixes(leftVar, rType, highlightInfo);
409 return highlightInfo;
412 private static boolean isCastIntentionApplicable(PsiExpression expression, PsiType toType) {
413 while (expression instanceof PsiTypeCastExpression || expression instanceof PsiParenthesizedExpression) {
414 if (expression instanceof PsiTypeCastExpression) {
415 expression = ((PsiTypeCastExpression)expression).getOperand();
417 if (expression instanceof PsiParenthesizedExpression) {
418 expression = ((PsiParenthesizedExpression)expression).getExpression();
421 if (expression == null) return false;
422 PsiType rType = expression.getType();
423 return rType != null && toType != null && TypeConversionUtil.areTypesConvertible(rType, toType);
427 @Nullable
428 static HighlightInfo checkVariableInitializerType(PsiVariable variable) {
429 PsiExpression initializer = variable.getInitializer();
430 // array initalizer checked in checkArrayInitializerApplicable
431 if (initializer == null || initializer instanceof PsiArrayInitializerExpression) return null;
432 PsiType lType = variable.getType();
433 PsiType rType = initializer.getType();
434 int start = variable.getTypeElement().getTextRange().getStartOffset();
435 int end = variable.getTextRange().getEndOffset();
436 HighlightInfo highlightInfo = checkAssignability(lType, rType, initializer, new TextRange(start, end));
437 if (highlightInfo != null) {
438 registerChangeVariableTypeFixes(variable, rType, highlightInfo);
440 return highlightInfo;
443 @Nullable
444 static HighlightInfo checkAssignability(PsiType lType, PsiType rType, PsiExpression expression, PsiElement elementToHighlight) {
445 TextRange textRange = elementToHighlight.getTextRange();
446 return checkAssignability(lType, rType, expression, textRange);
449 @Nullable
450 public static HighlightInfo checkAssignability(@Nullable PsiType lType, @Nullable PsiType rType, @Nullable PsiExpression expression, TextRange textRange) {
451 if (lType == rType) return null;
452 if (expression == null) {
453 if (rType == null || lType == null || TypeConversionUtil.isAssignable(lType, rType)) return null;
455 else if (TypeConversionUtil.areTypesAssignmentCompatible(lType, expression)) {
456 if (lType == null || rType == null) return null;
457 return GenericsHighlightUtil.checkRawToGenericAssignment(lType, rType, expression);
459 if (rType == null) {
460 rType = expression.getType();
462 HighlightInfo highlightInfo = createIncompatibleTypeHighlightInfo(lType, rType, textRange);
463 if (rType != null && expression != null && isCastIntentionApplicable(expression, lType)) {
464 QuickFixAction.registerQuickFixAction(highlightInfo, new AddTypeCastFix(lType, expression));
466 if (lType instanceof PsiClassType && expression != null) {
467 QuickFixAction.registerQuickFixAction(highlightInfo, new WrapExpressionFix((PsiClassType)lType, expression));
469 ChangeNewOperatorTypeFix.register(highlightInfo, expression, lType);
470 return highlightInfo;
474 @Nullable
475 static HighlightInfo checkReturnStatementType(PsiReturnStatement statement) {
476 PsiMethod method = null;
477 PsiElement parent = statement.getParent();
478 while (true) {
479 if (parent instanceof PsiFile) break;
480 if (parent instanceof PsiClassInitializer) break;
481 if (parent instanceof PsiMethod) {
482 method = (PsiMethod)parent;
483 break;
485 parent = parent.getParent();
487 String description;
488 int navigationShift = 0;
489 HighlightInfo errorResult = null;
490 if (method == null && !(parent instanceof JspFile)) {
491 description = JavaErrorMessages.message("return.outside.method");
492 errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, statement, description);
494 else {
495 PsiType returnType = method != null ? method.getReturnType() : null/*JSP page returns void*/;
496 boolean isMethodVoid = returnType == null || PsiType.VOID.equals(returnType);
497 final PsiExpression returnValue = statement.getReturnValue();
498 if (returnValue != null) {
499 PsiType valueType = returnValue.getType();
500 if (isMethodVoid) {
501 description = JavaErrorMessages.message("return.from.void.method");
502 errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, statement, description);
503 if (valueType != null) {
504 IntentionAction fix = QUICK_FIX_FACTORY.createMethodReturnFix(method, valueType, true);
505 QuickFixAction.registerQuickFixAction(errorResult, fix);
508 else {
509 errorResult = checkAssignability(returnType, valueType, returnValue, statement);
510 if (errorResult != null && valueType != null) {
511 IntentionAction fix = QUICK_FIX_FACTORY.createMethodReturnFix(method, valueType, true);
512 QuickFixAction.registerQuickFixAction(errorResult, fix);
513 if (returnType instanceof PsiArrayType && TypeConversionUtil.isAssignable(((PsiArrayType)returnType).getComponentType(), valueType)) {
514 QuickFixAction.registerQuickFixAction(errorResult, new SurroundWithArrayFix(null){
515 @Override
516 protected PsiExpression getExpression(final PsiElement element) {
517 return returnValue.isValid() ? returnValue : null;
523 navigationShift = returnValue.getStartOffsetInParent();
525 else {
526 if (!isMethodVoid) {
527 description = JavaErrorMessages.message("missing.return.value");
528 errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, statement, description);
529 IntentionAction fix = QUICK_FIX_FACTORY.createMethodReturnFix(method, PsiType.VOID, true);
530 QuickFixAction.registerQuickFixAction(errorResult, fix);
531 navigationShift = PsiKeyword.RETURN.length();
535 if (errorResult != null) {
536 errorResult.navigationShift = navigationShift;
538 return errorResult;
541 public static String getUnhandledExceptionsDescriptor(Collection<PsiClassType> unhandledExceptions) {
542 StringBuilder exceptionsText = new StringBuilder();
543 for (PsiClassType unhandledException : unhandledExceptions) {
544 if (exceptionsText.length() != 0) exceptionsText.append(", ");
545 exceptionsText.append(formatType(unhandledException));
548 return JavaErrorMessages.message("unhandled.exceptions", exceptionsText.toString(), unhandledExceptions.size());
552 @Nullable
553 static HighlightInfo checkVariableAlreadyDefined(PsiVariable variable) {
554 boolean isIncorrect = false;
555 PsiIdentifier identifier = variable.getNameIdentifier();
556 String name = variable.getName();
557 if (variable instanceof PsiLocalVariable ||
558 variable instanceof PsiParameter && ((PsiParameter)variable).getDeclarationScope() instanceof PsiCatchSection ||
559 variable instanceof PsiParameter && ((PsiParameter)variable).getDeclarationScope() instanceof PsiForeachStatement) {
560 PsiElement scope = PsiTreeUtil.getParentOfType(variable, PsiFile.class, PsiMethod.class, PsiClassInitializer.class);
561 VariablesNotProcessor proc = new VariablesNotProcessor(variable, false){
562 protected boolean check(final PsiVariable var, final ResolveState state) {
563 return (var instanceof PsiLocalVariable || var instanceof PsiParameter) && super.check(var, state);
566 PsiScopesUtil.treeWalkUp(proc, identifier, scope);
567 if (proc.size() > 0) {
568 isIncorrect = true;
571 else if (variable instanceof PsiField) {
572 PsiField field = (PsiField)variable;
573 PsiClass aClass = field.getContainingClass();
574 if (aClass == null) return null;
575 PsiField fieldByName = aClass.findFieldByName(name, false);
576 if (fieldByName != null && fieldByName != field) {
577 isIncorrect = true;
580 else {
581 PsiElement scope = variable.getParent();
582 PsiElement[] children = scope.getChildren();
583 for (PsiElement child : children) {
584 if (child instanceof PsiVariable) {
585 if (child.equals(variable)) continue;
586 if (name.equals(((PsiVariable)child).getName())) {
587 isIncorrect = true;
588 break;
594 if (isIncorrect) {
595 String description = JavaErrorMessages.message("variable.already.defined", name);
596 HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, identifier, description);
597 QuickFixAction.registerQuickFixAction(highlightInfo, new ReuseVariableDeclarationFix(variable, identifier));
598 return highlightInfo;
600 return null;
603 @NotNull
604 public static String formatClass(@NotNull PsiClass aClass) {
605 return formatClass(aClass, true);
608 @NotNull
609 public static String formatClass(@NotNull PsiClass aClass, boolean fqn) {
610 return PsiFormatUtil.formatClass(aClass, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_ANONYMOUS_CLASS_VERBOSE | (fqn ? PsiFormatUtil.SHOW_FQ_NAME : 0));
613 @NotNull
614 public static String formatMethod(@NotNull PsiMethod method) {
615 return PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS, PsiFormatUtil.SHOW_TYPE);
618 @NotNull
619 public static String formatType(@Nullable PsiType type) {
620 if (type == null) return PsiKeyword.NULL;
621 String text = type.getInternalCanonicalText();
622 return text == null ? PsiKeyword.NULL : text;
626 public static HighlightInfo checkUnhandledExceptions(PsiElement element, TextRange fixRange) {
627 List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(element);
628 HighlightInfo errorResult = null;
629 if (!unhandledExceptions.isEmpty()) {
630 if (fixRange == null) {
631 fixRange = element.getTextRange();
633 HighlightInfoType highlightType = getUnhandledExceptionHighlightType(element);
634 if (highlightType == null) return null;
635 errorResult = HighlightInfo.createHighlightInfo(highlightType, fixRange, getUnhandledExceptionsDescriptor(unhandledExceptions));
636 QuickFixAction.registerQuickFixAction(errorResult, new AddExceptionToCatchFix());
637 QuickFixAction.registerQuickFixAction(errorResult, new AddExceptionToThrowsFix(element));
638 QuickFixAction.registerQuickFixAction(errorResult, new SurroundWithTryCatchFix(element));
639 if (unhandledExceptions.size() == 1) {
640 QuickFixAction.registerQuickFixAction(errorResult, new GeneralizeCatchFix(element, unhandledExceptions.get(0)));
643 return errorResult;
646 private static HighlightInfoType getUnhandledExceptionHighlightType(final PsiElement element) {
647 if (!JspPsiUtil.isInJspFile(element)) {
648 return HighlightInfoType.UNHANDLED_EXCEPTION;
650 PsiMethod targetMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
651 if (!(targetMethod instanceof JspHolderMethod)) return HighlightInfoType.UNHANDLED_EXCEPTION;
652 // ignore JSP top level errors - it handled by UnhandledExceptionInJSP inspection
653 return null;
657 @Nullable
658 static HighlightInfo checkBreakOutsideLoop(PsiBreakStatement statement) {
659 if (statement.getLabelIdentifier() == null) {
660 if (new PsiMatcherImpl(statement).ancestor(EnclosingLoopOrSwitchMatcherExpression.INSTANCE).getElement() == null) {
661 return HighlightInfo
662 .createHighlightInfo(HighlightInfoType.ERROR, statement, JavaErrorMessages.message("break.outside.switch.or.loop"));
665 else {
666 // todo labeled
668 return null;
672 @Nullable
673 static HighlightInfo checkContinueOutsideLoop(PsiContinueStatement statement) {
674 if (statement.getLabelIdentifier() == null) {
675 if (new PsiMatcherImpl(statement).ancestor(EnclosingLoopMatcherExpression.INSTANCE).getElement() == null) {
676 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, statement, JavaErrorMessages.message("continue.outside.loop"));
679 else {
680 PsiStatement exitedStatement = statement.findContinuedStatement();
681 if (exitedStatement == null) return null;
682 if (!(exitedStatement instanceof PsiForStatement) && !(exitedStatement instanceof PsiWhileStatement) &&
683 !(exitedStatement instanceof PsiDoWhileStatement) && !(exitedStatement instanceof PsiForeachStatement)) {
684 String description = JavaErrorMessages.message("not.loop.label", statement.getLabelIdentifier().getText());
685 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, statement, description);
688 return null;
692 static HighlightInfo checkIllegalModifierCombination(PsiKeyword keyword, PsiModifierList modifierList) {
693 @Modifier String modifier = keyword.getText();
694 String incompatible = getIncompatibleModifier(modifier, modifierList);
696 HighlightInfo highlightInfo = null;
697 if (incompatible != null) {
698 String message = JavaErrorMessages.message("incompatible.modifiers", modifier, incompatible);
700 highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, keyword, message);
702 QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createModifierListFix(modifierList, modifier, false, false));
704 return highlightInfo;
707 @Nullable
708 private static Map<String, Set<String>> getIncompatibleModifierMap(PsiModifierList modifierList) {
709 PsiElement parent = modifierList.getParent();
710 if (parent == null || PsiUtilBase.hasErrorElementChild(parent)) return null;
711 return parent instanceof PsiClass
712 ? ((PsiClass)parent).isInterface() ? ourInterfaceIncompatibleModifiers : ourClassIncompatibleModifiers
713 : parent instanceof PsiMethod
714 ? ourMethodIncompatibleModifiers
715 : parent instanceof PsiVariable
716 ? ourFieldIncompatibleModifiers
717 : parent instanceof PsiClassInitializer ? ourClassInitializerIncompatibleModifiers : null;
720 @Nullable
721 public static String getIncompatibleModifier(String modifier, PsiModifierList modifierList) {
722 PsiElement parent = modifierList.getParent();
723 if (parent == null || PsiUtilBase.hasErrorElementChild(parent)) return null;
724 final Map<String, Set<String>> incompatibleModifierMap = getIncompatibleModifierMap(modifierList);
725 if (incompatibleModifierMap == null) return null;
726 return getIncompatibleModifier(modifier, modifierList, incompatibleModifierMap);
730 @Nullable
731 public static HighlightInfo checkNotAllowedModifier(PsiKeyword keyword, PsiModifierList modifierList) {
732 PsiElement modifierOwner = modifierList.getParent();
733 if (modifierOwner == null) return null;
734 if (PsiUtilBase.hasErrorElementChild(modifierOwner)) return null;
735 @Modifier String modifier = keyword.getText();
736 final Map<String, Set<String>> incompatibleModifierMap = getIncompatibleModifierMap(modifierList);
737 if (incompatibleModifierMap == null) return null;
738 Set<String> incompatibles = incompatibleModifierMap.get(modifier);
739 PsiElement modifierOwnerParent = modifierOwner instanceof PsiMember ? ((PsiMember)modifierOwner).getContainingClass() : modifierOwner.getParent();
740 if (modifierOwnerParent == null) modifierOwnerParent = modifierOwner.getParent();
741 boolean isAllowed = true;
742 if (modifierOwner instanceof PsiClass) {
743 PsiClass aClass = (PsiClass)modifierOwner;
744 if (aClass.isInterface()) {
745 if (PsiModifier.STATIC.equals(modifier) || PsiModifier.PRIVATE.equals(modifier) || PsiModifier.PROTECTED.equals(modifier) ||
746 PsiModifier.PACKAGE_LOCAL.equals(modifier)) {
747 isAllowed = modifierOwnerParent instanceof PsiClass;
750 else {
751 if (PsiModifier.PUBLIC.equals(modifier)) {
752 isAllowed = modifierOwnerParent instanceof PsiJavaFile || modifierOwnerParent instanceof PsiClass;
754 else if (PsiModifier.STATIC.equals(modifier) || PsiModifier.PRIVATE.equals(modifier) || PsiModifier.PROTECTED.equals(modifier) ||
755 PsiModifier.PACKAGE_LOCAL.equals(modifier)) {
756 isAllowed = modifierOwnerParent instanceof PsiClass;
759 if (aClass.isEnum()) {
760 isAllowed &= !(PsiModifier.FINAL.equals(modifier) || PsiModifier.ABSTRACT.equals(modifier));
764 else if (modifierOwner instanceof PsiMethod) {
765 PsiMethod method = (PsiMethod)modifierOwner;
766 isAllowed = !(method.isConstructor() && ourConstructorNotAllowedModifiers.contains(modifier));
767 if ((method.hasModifierProperty(PsiModifier.PUBLIC) || method.hasModifierProperty(PsiModifier.PROTECTED)) && method.isConstructor() &&
768 method.getContainingClass() != null && method.getContainingClass().isEnum()) {
769 isAllowed = false;
772 if (PsiModifier.PRIVATE.equals(modifier) || PsiModifier.PROTECTED.equals(modifier) || PsiModifier.TRANSIENT.equals(modifier) ||
773 PsiModifier.STRICTFP.equals(modifier) || PsiModifier.SYNCHRONIZED.equals(modifier)) {
774 isAllowed &= modifierOwnerParent instanceof PsiClass && !((PsiClass)modifierOwnerParent).isInterface();
777 else if (modifierOwner instanceof PsiField) {
778 if (PsiModifier.PRIVATE.equals(modifier) || PsiModifier.PROTECTED.equals(modifier) || PsiModifier.TRANSIENT.equals(modifier) ||
779 PsiModifier.STRICTFP.equals(modifier) || PsiModifier.SYNCHRONIZED.equals(modifier)) {
780 isAllowed = modifierOwnerParent instanceof PsiClass && !((PsiClass)modifierOwnerParent).isInterface();
783 else if (modifierOwner instanceof PsiClassInitializer) {
784 isAllowed = PsiModifier.STATIC.equals(modifier);
786 else if (modifierOwner instanceof PsiLocalVariable || modifierOwner instanceof PsiParameter) {
787 isAllowed = PsiModifier.FINAL.equals(modifier);
790 isAllowed &= incompatibles != null;
791 if (!isAllowed) {
792 String message = JavaErrorMessages.message("modifier.not.allowed", modifier);
794 HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, keyword, message);
795 QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createModifierListFix(modifierList, modifier, false, false));
796 return highlightInfo;
798 return null;
802 @Nullable
803 static HighlightInfo checkLiteralExpressionParsingError(PsiLiteralExpression expression) {
804 String error = expression.getParsingError();
805 if (error != null) {
806 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, error);
808 return null;
812 @Nullable
813 static HighlightInfo checkMustBeBoolean(PsiExpression expr) {
814 PsiElement parent = expr.getParent();
815 if (parent instanceof PsiIfStatement || parent instanceof PsiWhileStatement ||
816 parent instanceof PsiForStatement && expr.equals(((PsiForStatement)parent).getCondition()) ||
817 parent instanceof PsiDoWhileStatement && expr.equals(((PsiDoWhileStatement)parent).getCondition())) {
818 if (expr.getNextSibling() instanceof PsiErrorElement) return null;
820 PsiType type = expr.getType();
821 if (!TypeConversionUtil.isBooleanType(type)) {
822 final HighlightInfo info = createIncompatibleTypeHighlightInfo(PsiType.BOOLEAN, type, expr.getTextRange());
823 if (expr instanceof PsiMethodCallExpression) {
824 final PsiMethodCallExpression methodCall = (PsiMethodCallExpression) expr;
825 final PsiMethod method = methodCall.resolveMethod();
826 if (method != null && PsiType.VOID.equals(method.getReturnType())) {
827 QuickFixAction.registerQuickFixAction(info, new MethodReturnBooleanFix(method, PsiType.BOOLEAN));
830 return info;
833 return null;
837 @Nullable
838 static HighlightInfo checkExceptionThrownInTry(PsiParameter parameter) {
839 PsiElement declarationScope = parameter.getDeclarationScope();
840 if (!(declarationScope instanceof PsiCatchSection)) return null;
841 PsiTryStatement statement = ((PsiCatchSection)declarationScope).getTryStatement();
842 Collection<PsiClassType> classes = ExceptionUtil.collectUnhandledExceptions(statement.getTryBlock(), statement.getTryBlock());
844 PsiType caughtType = parameter.getType();
845 if (!(caughtType instanceof PsiClassType)) return null;
846 if (ExceptionUtil.isUncheckedExceptionOrSuperclass((PsiClassType)caughtType)) return null;
848 for (PsiClassType exceptionType : classes) {
849 if (exceptionType.isAssignableFrom(caughtType) || caughtType.isAssignableFrom(exceptionType)) return null;
852 String description = JavaErrorMessages.message("exception.never.thrown.try", formatType(caughtType));
853 HighlightInfo errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parameter, description);
855 QuickFixAction.registerQuickFixAction(errorResult, new DeleteCatchFix(parameter));
856 return errorResult;
860 @Nullable
861 static HighlightInfo checkNotAStatement(PsiStatement statement) {
862 if (!PsiUtil.isStatement(statement) && !PsiUtilBase.hasErrorElementChild(statement)) {
863 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, statement, JavaErrorMessages.message("not.a.statement"));
865 return null;
869 public static HighlightInfo checkSwitchSelectorType(PsiSwitchStatement statement) {
870 PsiExpression expression = statement.getExpression();
871 HighlightInfo errorResult = null;
872 if (expression != null && expression.getType() != null) {
873 PsiType type = expression.getType();
874 if (!isValidTypeForSwitchSelector(type)) {
875 String message =
876 JavaErrorMessages.message("incompatible.types", JavaErrorMessages.message("valid.switch.selector.types"), formatType(type));
877 errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, message);
878 if (PsiType.LONG.equals(type) || PsiType.FLOAT.equals(type) || PsiType.DOUBLE.equals(type)) {
879 QuickFixAction.registerQuickFixAction(errorResult, new AddTypeCastFix(PsiType.INT, expression));
883 return errorResult;
886 private static boolean isValidTypeForSwitchSelector(PsiType type) {
887 if (TypeConversionUtil.getTypeRank(type) <= TypeConversionUtil.INT_RANK) return true;
888 if (type instanceof PsiClassType) {
889 PsiClass psiClass = ((PsiClassType)type).resolve();
890 if (psiClass == null) return false;
891 if (psiClass.isEnum()) {
892 return true;
895 return false;
899 @Nullable
900 static HighlightInfo checkBinaryOperatorApplicable(PsiBinaryExpression expression) {
901 PsiExpression lOperand = expression.getLOperand();
902 PsiExpression rOperand = expression.getROperand();
903 PsiJavaToken operationSign = expression.getOperationSign();
904 if (!TypeConversionUtil.isBinaryOperatorApplicable(operationSign.getTokenType(), lOperand, rOperand, false)) {
905 String message = JavaErrorMessages
906 .message("binary.operator.not.applicable", operationSign.getText(), formatType(lOperand.getType()), formatType(rOperand.getType()));
907 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, message);
909 return null;
913 @Nullable
914 public static HighlightInfo checkUnaryOperatorApplicable(PsiJavaToken token, PsiExpression expression) {
915 if (token != null && expression != null && !TypeConversionUtil.isUnaryOperatorApplicable(token, expression)) {
916 PsiType type = expression.getType();
917 if (type == null) return null;
918 String message = JavaErrorMessages.message("unary.operator.not.applicable", token.getText(), formatType(type));
920 PsiElement parentExpr = token.getParent();
921 HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parentExpr, message);
922 if (parentExpr instanceof PsiPrefixExpression && token.getTokenType() == JavaTokenType.EXCL) {
923 QuickFixAction.registerQuickFixAction(highlightInfo, new NegationBroadScopeFix((PsiPrefixExpression)parentExpr));
925 return highlightInfo;
927 return null;
930 @Nullable
931 public static HighlightInfo checkThisOrSuperExpressionInIllegalContext(PsiExpression expr, @Nullable PsiJavaCodeReferenceElement qualifier) {
932 if (expr instanceof PsiSuperExpression && !(expr.getParent() instanceof PsiReferenceExpression)) {
933 // like in 'Object o = super;'
934 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expr.getTextRange().getEndOffset(),
935 expr.getTextRange().getEndOffset() + 1,
936 JavaErrorMessages.message("dot.expected.after.super.or.this"));
938 PsiClass aClass = qualifier == null ? PsiTreeUtil.getParentOfType(expr, PsiClass.class) : (PsiClass)qualifier.resolve();
939 if (aClass == null) return null;
940 if (qualifier != null && aClass.isInterface()) {
941 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, qualifier, HighlightClassUtil.CLASS_EXPECTED);
943 if (!HighlightClassUtil.hasEnclosingInstanceInScope(aClass, expr, false)) {
944 return HighlightClassUtil.reportIllegalEnclosingUsage(expr, null, aClass, expr);
947 return null;
951 static String buildProblemWithStaticDescription(PsiElement refElement) {
952 String type = LanguageFindUsages.INSTANCE.forLanguage(StdLanguages.JAVA).getType(refElement);
953 String name = HighlightMessageUtil.getSymbolName(refElement, PsiSubstitutor.EMPTY);
954 return JavaErrorMessages.message("non.static.symbol.referenced.from.static.context", type, name);
957 static void registerStaticProblemQuickFixAction(@NotNull PsiElement refElement, HighlightInfo errorResult, PsiJavaCodeReferenceElement place) {
958 if (refElement instanceof PsiModifierListOwner) {
959 QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createModifierListFix((PsiModifierListOwner)refElement, PsiModifier.STATIC, true, false));
961 // make context non static
962 PsiModifierListOwner staticParent = PsiUtil.getEnclosingStaticElement(place, null);
963 if (staticParent != null && isInstanceReference(place)) {
964 QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createModifierListFix(staticParent, PsiModifier.STATIC, false, false));
968 private static boolean isInstanceReference(PsiJavaCodeReferenceElement place) {
969 PsiElement qualifier = place.getQualifier();
970 if (qualifier == null) return true;
971 if (!(qualifier instanceof PsiJavaCodeReferenceElement)) return false;
972 PsiElement q = ((PsiReference)qualifier).resolve();
973 if (q instanceof PsiClass) return false;
974 if (q != null) return true;
975 String qname = ((PsiJavaCodeReferenceElement)qualifier).getQualifiedName();
976 return qname == null || !Character.isLowerCase(qname.charAt(0));
979 static String buildProblemWithAccessDescription(PsiJavaCodeReferenceElement reference, JavaResolveResult result) {
980 PsiModifierListOwner refElement = (PsiModifierListOwner)result.getElement();
981 String symbolName = HighlightMessageUtil.getSymbolName(refElement, result.getSubstitutor());
983 if (refElement.hasModifierProperty(PsiModifier.PRIVATE)) {
984 String containerName = HighlightMessageUtil.getSymbolName(refElement.getParent(), result.getSubstitutor());
985 return JavaErrorMessages.message("private.symbol", symbolName, containerName);
987 else {
988 if (refElement.hasModifierProperty(PsiModifier.PROTECTED)) {
989 String containerName = HighlightMessageUtil.getSymbolName(refElement.getParent(), result.getSubstitutor());
990 return JavaErrorMessages.message("protected.symbol", symbolName, containerName);
992 else {
993 PsiClass packageLocalClass = getPackageLocalClassInTheMiddle(reference);
994 if (packageLocalClass != null) {
995 refElement = packageLocalClass;
996 symbolName = HighlightMessageUtil.getSymbolName(refElement, result.getSubstitutor());
998 if (refElement.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || packageLocalClass != null) {
999 String containerName = HighlightMessageUtil.getSymbolName(refElement.getParent(), result.getSubstitutor());
1000 return JavaErrorMessages.message("package.local.symbol", symbolName, containerName);
1002 else {
1003 String containerName = HighlightMessageUtil.getSymbolName(
1004 refElement instanceof PsiTypeParameter ? refElement.getParent().getParent() : refElement.getParent(), result.getSubstitutor());
1005 return JavaErrorMessages.message("visibility.access.problem", symbolName, containerName);
1011 @Nullable
1012 static HighlightInfo checkValidArrayAccessExpression(PsiExpression arrayExpression, PsiExpression indexExpression) {
1013 PsiType arrayExpressionType = arrayExpression == null ? null : arrayExpression.getType();
1014 if (arrayExpressionType != null && !(arrayExpressionType instanceof PsiArrayType)) {
1015 String description = JavaErrorMessages.message("array.type.expected", formatType(arrayExpressionType));
1016 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, arrayExpression, description);
1018 return checkAssignability(PsiType.INT, indexExpression.getType(), indexExpression, indexExpression);
1022 @Nullable
1023 public static HighlightInfo checkCatchParameterIsThrowable(PsiParameter parameter) {
1024 if (parameter.getDeclarationScope() instanceof PsiCatchSection) {
1025 PsiType type = parameter.getType();
1026 return checkMustBeThrowable(type, parameter, true);
1028 return null;
1031 public static void checkArrayInitalizer(final PsiExpression initializer, final HighlightInfoHolder holder) {
1032 if (! (initializer instanceof PsiArrayInitializerExpression)) return;
1034 final PsiType arrayInitializerType = initializer.getType();
1035 if (! (arrayInitializerType instanceof PsiArrayType)) return;
1037 final PsiType componentType = ((PsiArrayType) arrayInitializerType).getComponentType();
1038 final PsiArrayInitializerExpression arrayInitializer = (PsiArrayInitializerExpression) initializer;
1040 boolean arrayTypeFixChecked = false;
1041 VariableArrayTypeFix fix = null;
1043 final PsiExpression[] initializers = arrayInitializer.getInitializers();
1044 for (PsiExpression expression : initializers) {
1045 final HighlightInfo info = checkArrayInitalizerCompatibleTypes(expression, componentType);
1046 if (info != null) {
1047 holder.add(info);
1049 if (!arrayTypeFixChecked) {
1050 final PsiType checkResult = sameType(initializers);
1051 fix = checkResult != null ? new VariableArrayTypeFix(arrayInitializer, checkResult) : null;
1052 arrayTypeFixChecked = true;
1054 if (fix != null) {
1055 QuickFixAction.registerQuickFixAction(info, fix);
1061 @Nullable
1062 private static PsiType getArrayInitializerType(final PsiArrayInitializerExpression element) {
1063 final PsiType typeCheckResult = sameType(element.getInitializers());
1064 if (typeCheckResult != null) {
1065 return typeCheckResult.createArrayType();
1067 return null;
1070 private static PsiType sameType(PsiExpression[] expressions) {
1071 PsiType type = null;
1072 for (PsiExpression expression : expressions) {
1073 final PsiType currentType;
1074 if (expression instanceof PsiArrayInitializerExpression) {
1075 currentType = getArrayInitializerType((PsiArrayInitializerExpression)expression);
1077 else {
1078 currentType = expression.getType();
1080 if (type == null) {
1081 type = currentType;
1083 else if (!type.equals(currentType)) {
1084 return null;
1087 return type;
1090 @Nullable
1091 private static HighlightInfo checkArrayInitalizerCompatibleTypes(PsiExpression initializer, final PsiType componentType) {
1092 PsiType initializerType = initializer.getType();
1093 if (initializerType == null) {
1094 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, initializer,
1095 JavaErrorMessages.message("illegal.initializer", formatType(componentType)));
1097 PsiExpression expression = initializer instanceof PsiArrayInitializerExpression ? null : initializer;
1098 return checkAssignability(componentType, initializerType, expression, initializer);
1101 @Nullable
1102 public static HighlightInfo checkExpressionRequired(PsiReferenceExpression expression) {
1103 if (expression.getNextSibling() instanceof PsiErrorElement) return null;
1104 PsiElement resolved = expression.advancedResolve(true).getElement();
1105 if (resolved == null) return null;
1106 PsiElement parent = expression.getParent();
1107 // String.class or String() are both correct
1108 if (parent instanceof PsiReferenceExpression || parent instanceof PsiMethodCallExpression) return null;
1109 if (resolved instanceof PsiVariable) return null;
1110 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, JavaErrorMessages.message("expression.expected"));
1114 @Nullable
1115 public static HighlightInfo checkArrayInitializerApplicable(PsiArrayInitializerExpression expression) {
1117 JLS 10.6 Array Initializers
1118 An array initializer may be specified in a declaration, or as part of an array creation expression
1120 PsiElement parent = expression.getParent();
1121 if (parent instanceof PsiVariable) {
1122 PsiVariable variable = (PsiVariable)parent;
1123 if (variable.getType() instanceof PsiArrayType) return null;
1125 else if (parent instanceof PsiNewExpression) {
1126 return null;
1128 else if (parent instanceof PsiArrayInitializerExpression) {
1129 return null;
1131 HighlightInfo info =
1132 HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, JavaErrorMessages.message("expression.expected"));
1133 QuickFixAction.registerQuickFixAction(info, new AddNewArrayExpressionFix(expression));
1135 return info;
1139 @Nullable
1140 public static HighlightInfo checkCaseStatement(PsiSwitchLabelStatement statement) {
1141 PsiSwitchStatement switchStatement = statement.getEnclosingSwitchStatement();
1142 if (switchStatement == null) {
1143 return HighlightInfo
1144 .createHighlightInfo(HighlightInfoType.ERROR, statement, JavaErrorMessages.message("case.statement.outside.switch"));
1146 if (switchStatement.getBody() == null) return null;
1147 PsiExpression switchExpression = switchStatement.getExpression();
1148 PsiType switchType = switchExpression == null ? PsiType.INT : switchExpression.getType();
1149 // check constant expression
1150 PsiExpression caseValue = statement.getCaseValue();
1152 // Every case constant expression associated with a switch statement must be assignable ($5.2) to the type of the switch Expression.
1153 if (caseValue != null && switchExpression != null) {
1154 HighlightInfo highlightInfo = checkAssignability(switchType, caseValue.getType(), caseValue, caseValue);
1155 if (highlightInfo != null) return highlightInfo;
1157 Object value = null;
1159 boolean isEnumSwitch = false;
1160 if (!statement.isDefaultCase() && caseValue != null) {
1161 if (caseValue instanceof PsiReferenceExpression) {
1162 PsiElement element = ((PsiReferenceExpression)caseValue).resolve();
1163 if (element instanceof PsiEnumConstant) {
1164 isEnumSwitch = true;
1165 value = ((PsiEnumConstant)element).getName();
1166 if (!(((PsiReferenceExpression)caseValue).getQualifier() == null)) {
1167 String message = JavaErrorMessages.message("qualified.enum.constant.in.switch");
1168 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, caseValue, message);
1172 if (!isEnumSwitch) {
1173 value = ConstantExpressionUtil.computeCastTo(caseValue, switchType);
1175 if (value == null) {
1176 return HighlightInfo
1177 .createHighlightInfo(HighlightInfoType.ERROR, caseValue, JavaErrorMessages.message("constant.expression.required"));
1181 // check duplicate
1182 PsiStatement[] statements = switchStatement.getBody().getStatements();
1183 for (PsiStatement st : statements) {
1184 if (st == statement) continue;
1185 if (!(st instanceof PsiSwitchLabelStatement)) continue;
1186 PsiSwitchLabelStatement labelStatement = (PsiSwitchLabelStatement)st;
1187 if (labelStatement.isDefaultCase() != statement.isDefaultCase()) continue;
1188 PsiExpression caseExpr = labelStatement.getCaseValue();
1189 if (isEnumSwitch && caseExpr instanceof PsiReferenceExpression) {
1190 PsiElement element = ((PsiReferenceExpression)caseExpr).resolve();
1191 if (!(element instanceof PsiEnumConstant && Comparing.equal(((PsiEnumConstant)element).getName(), value))) continue;
1193 else {
1194 // not assignable error already caught
1195 if (!TypeConversionUtil.areTypesAssignmentCompatible(switchType, caseExpr)) continue;
1196 if (!Comparing.equal(ConstantExpressionUtil.computeCastTo(caseExpr, switchType), value)) continue;
1198 String description = statement.isDefaultCase()
1199 ? JavaErrorMessages.message("duplicate.default.switch.label")
1200 : JavaErrorMessages.message("duplicate.switch.label", value);
1201 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, value == null ? statement : caseValue, description);
1204 // must be followed with colon
1205 PsiElement lastChild = statement.getLastChild();
1206 while (lastChild instanceof PsiComment || lastChild instanceof PsiWhiteSpace) {
1207 lastChild = lastChild.getPrevSibling();
1209 if (!(lastChild instanceof PsiJavaToken && ((PsiJavaToken)lastChild).getTokenType() == JavaTokenType.COLON)) {
1210 int start = statement.getTextRange().getEndOffset();
1211 int end = statement.getTextRange().getEndOffset() + 1;
1212 String description = JavaErrorMessages.message("switch.colon.expected.after.case.label");
1213 CharSequence chars = statement.getContainingFile().getViewProvider().getContents();
1214 boolean isAfterEndOfLine = end >= chars.length() || chars.charAt(start) == '\n' || chars.charAt(start) == '\r';
1215 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, null,start, end, description, description,isAfterEndOfLine, null);
1217 return null;
1222 * see JLS 8.3.2.3
1224 @Nullable
1225 public static HighlightInfo checkIllegalForwardReferenceToField(PsiReferenceExpression expression, PsiField referencedField) {
1226 PsiClass containingClass = referencedField.getContainingClass();
1227 if (containingClass == null) return null;
1228 if (expression.getContainingFile() != referencedField.getContainingFile()) return null;
1229 if (expression.getTextRange().getStartOffset() >= referencedField.getTextRange().getEndOffset()) return null;
1230 // only simple reference can be illegal
1231 if (expression.getQualifierExpression() != null) return null;
1232 PsiField initField = findEnclosingFieldInitializer(expression);
1233 PsiClassInitializer classInitializer = findParentClassInitializer(expression);
1234 if (initField == null && classInitializer == null) return null;
1235 // instance initializers may access static fields
1236 boolean isStaticClassInitializer = classInitializer != null && classInitializer.hasModifierProperty(PsiModifier.STATIC);
1237 boolean isStaticInitField = initField != null && initField.hasModifierProperty(PsiModifier.STATIC);
1238 boolean inStaticContext = isStaticInitField || isStaticClassInitializer;
1239 if (!inStaticContext && referencedField.hasModifierProperty(PsiModifier.STATIC)) return null;
1240 if (PsiUtil.isOnAssignmentLeftHand(expression) && !PsiUtil.isAccessedForReading(expression)) return null;
1241 if (!containingClass.getManager().areElementsEquivalent(containingClass, PsiTreeUtil.getParentOfType(expression, PsiClass.class))) {
1242 return null;
1244 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, JavaErrorMessages.message("illegal.forward.reference"));
1248 * @return field that has initializer with this element as subexpression or null if not found
1250 @Nullable
1251 static PsiField findEnclosingFieldInitializer(PsiElement element) {
1252 while (element != null) {
1253 PsiElement parent = element.getParent();
1254 if (parent instanceof PsiField) {
1255 PsiField field = (PsiField)parent;
1256 if (element == field.getInitializer()) return field;
1257 if (field instanceof PsiEnumConstant && element == ((PsiEnumConstant)field).getArgumentList()) return field;
1259 if (element instanceof PsiClass || element instanceof PsiMethod) return null;
1260 element = parent;
1262 return null;
1265 @Nullable
1266 private static PsiClassInitializer findParentClassInitializer(PsiElement element) {
1267 while (element != null) {
1268 if (element instanceof PsiClassInitializer) return (PsiClassInitializer)element;
1269 if (element instanceof PsiClass || element instanceof PsiMethod) return null;
1270 element = element.getParent();
1272 return null;
1276 @Nullable
1277 public static HighlightInfo checkIllegalType(PsiTypeElement typeElement) {
1278 if (typeElement == null || typeElement.getParent() instanceof PsiTypeElement) return null;
1280 if (PsiUtil.isInsideJavadocComment(typeElement)) return null;
1282 PsiType type = typeElement.getType();
1283 PsiType componentType = type.getDeepComponentType();
1284 if (componentType instanceof PsiClassType) {
1285 PsiClass aClass = PsiUtil.resolveClassInType(componentType);
1286 if (aClass == null) {
1287 String canonicalText = type.getCanonicalText();
1288 String description = JavaErrorMessages.message("unknown.class", canonicalText);
1289 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeElement, description);
1292 return null;
1296 @Nullable
1297 public static HighlightInfo checkIllegalVoidType(PsiKeyword type) {
1298 if (!PsiKeyword.VOID.equals(type.getText())) return null;
1300 PsiElement parent = type.getParent();
1301 if (parent instanceof PsiTypeElement) {
1302 PsiElement typeOwner = parent.getParent();
1303 if (typeOwner instanceof PsiMethod) {
1304 if (((PsiMethod)typeOwner).getReturnTypeElement() == parent) return null;
1306 else if (typeOwner instanceof PsiClassObjectAccessExpression &&
1307 TypeConversionUtil.isVoidType(((PsiClassObjectAccessExpression)typeOwner).getOperand().getType())) {
1308 // like in Class c = void.class;
1309 return null;
1311 else if (typeOwner != null && PsiUtilBase.hasErrorElementChild(typeOwner)) {
1312 // do not highlight incomplete declarations
1313 return null;
1315 else if (typeOwner instanceof JavaCodeFragment) {
1316 if (typeOwner.getUserData(PsiUtil.VALID_VOID_TYPE_IN_CODE_FRAGMENT) != null) return null;
1320 String description = JavaErrorMessages.message("illegal.type.void");
1321 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, type, description);
1324 @Nullable
1325 public static HighlightInfo checkMemberReferencedBeforeConstructorCalled(PsiElement expression) {
1326 PsiClass referencedClass;
1327 @NonNls String resolvedName;
1328 PsiType type;
1329 if (expression instanceof PsiJavaCodeReferenceElement) {
1330 PsiElement resolved = ((PsiJavaCodeReferenceElement)expression).advancedResolve(true).getElement();
1331 // redirected ctr
1332 if (PsiKeyword.THIS.equals(((PsiJavaCodeReferenceElement)expression).getReferenceName())
1333 && resolved instanceof PsiMethod
1334 && ((PsiMethod)resolved).isConstructor()) return null;
1335 PsiElement qualifier = ((PsiJavaCodeReferenceElement)expression).getQualifier();
1336 type = qualifier instanceof PsiExpression ? ((PsiExpression)qualifier).getType() : null;
1337 referencedClass = PsiUtil.resolveClassInType(type);
1339 boolean isSuperCall = isSuperMethodCall(expression.getParent());
1340 if (resolved == null && isSuperCall) {
1341 if (qualifier instanceof PsiReferenceExpression) {
1342 resolved = ((PsiReferenceExpression)qualifier).resolve();
1343 expression = qualifier;
1344 type = ((PsiReferenceExpression)qualifier).getType();
1345 referencedClass = PsiUtil.resolveClassInType(type);
1347 else if (qualifier instanceof PsiThisExpression || qualifier == null) {
1348 resolved = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, true, PsiMember.class);
1349 expression = qualifier == null ? expression : qualifier;
1350 if (resolved instanceof PsiMethod) {
1351 referencedClass = ((PsiMethod)resolved).getContainingClass();
1355 if (resolved instanceof PsiField) {
1356 PsiField referencedField = (PsiField)resolved;
1357 if (referencedField.hasModifierProperty(PsiModifier.STATIC)) return null;
1358 resolvedName = PsiFormatUtil.formatVariable(referencedField, PsiFormatUtil.SHOW_CONTAINING_CLASS | PsiFormatUtil.SHOW_NAME, PsiSubstitutor.EMPTY);
1359 referencedClass = referencedField.getContainingClass();
1361 else if (resolved instanceof PsiMethod) {
1362 PsiMethod method = (PsiMethod)resolved;
1363 if (method.hasModifierProperty(PsiModifier.STATIC)) return null;
1364 PsiElement nameElement = expression instanceof PsiThisExpression ? expression : ((PsiJavaCodeReferenceElement)expression).getReferenceNameElement();
1365 String name = nameElement == null ? null : nameElement.getText();
1366 if (isSuperCall) {
1367 if (referencedClass == null) return null;
1368 if (qualifier == null) {
1369 PsiClass superClass = referencedClass.getSuperClass();
1370 if (superClass != null
1371 && PsiUtil.isInnerClass(superClass)
1372 && InheritanceUtil.isInheritorOrSelf(referencedClass, superClass.getContainingClass(), true)) {
1373 // by default super() is considered this. - qualified
1374 resolvedName = PsiKeyword.THIS;
1376 else {
1377 return null;
1380 else {
1381 resolvedName = qualifier.getText();
1384 else if (PsiKeyword.THIS.equals(name)) {
1385 resolvedName = PsiKeyword.THIS;
1387 else {
1388 resolvedName = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_CONTAINING_CLASS | PsiFormatUtil.SHOW_NAME, 0);
1389 if (referencedClass == null) referencedClass = method.getContainingClass();
1392 else if (resolved instanceof PsiClass) {
1393 PsiClass aClass = (PsiClass)resolved;
1394 if (aClass.hasModifierProperty(PsiModifier.STATIC)) return null;
1395 referencedClass = aClass.getContainingClass();
1396 if (referencedClass == null) return null;
1397 resolvedName = PsiFormatUtil.formatClass(aClass, PsiFormatUtil.SHOW_NAME);
1399 else {
1400 return null;
1403 else if (expression instanceof PsiThisExpression) {
1404 PsiThisExpression thisExpression = (PsiThisExpression)expression;
1405 type = thisExpression.getType();
1406 referencedClass = PsiUtil.resolveClassInType(type);
1407 if (thisExpression.getQualifier() != null) {
1408 resolvedName = referencedClass == null
1409 ? null
1410 : PsiFormatUtil.formatClass(referencedClass, PsiFormatUtil.SHOW_CONTAINING_CLASS | PsiFormatUtil.SHOW_NAME) + ".this";
1412 else {
1413 resolvedName = "this";
1416 else {
1417 return null;
1419 if (referencedClass == null) return null;
1420 return checkReferenceToOurInstanceInsideThisOrSuper(expression, referencedClass, resolvedName);
1423 private static HighlightInfo checkReferenceToOurInstanceInsideThisOrSuper(final PsiElement expression,
1424 final PsiClass referencedClass,
1425 final String resolvedName) {
1426 if (PsiTreeUtil.getParentOfType(expression, PsiReferenceParameterList.class) != null) return null;
1427 PsiElement element = expression.getParent();
1428 while (element != null) {
1429 // check if expression inside super()/this() call
1430 if (isSuperOrThisMethodCall(element)) {
1431 PsiElement parentClass = new PsiMatcherImpl(element)
1432 .parent(PsiMatchers.hasClass(PsiExpressionStatement.class))
1433 .parent(PsiMatchers.hasClass(PsiCodeBlock.class))
1434 .parent(PsiMatchers.hasClass(PsiMethod.class))
1435 .dot(PsiMatchers.isConstructor(true))
1436 .parent(PsiMatchers.hasClass(PsiClass.class))
1437 .getElement();
1438 if (parentClass == null) {
1439 return null;
1442 // only this class/superclasses instance methods are not allowed to call
1443 PsiClass aClass = (PsiClass)parentClass;
1444 if (PsiUtil.isInnerClass(aClass) && referencedClass == aClass.getContainingClass()) return null;
1445 // field or method should be declared in this class or super
1446 if (!InheritanceUtil.isInheritorOrSelf(aClass, referencedClass, true)) return null;
1447 // and point to our instance
1448 if (expression instanceof PsiReferenceExpression &&
1449 !thisOrSuperReference(((PsiReferenceExpression)expression).getQualifierExpression(), aClass)) {
1450 return null;
1452 return createMemberReferencedError(resolvedName, expression.getTextRange());
1454 element = element.getParent();
1456 return null;
1459 private static HighlightInfo createMemberReferencedError(@NonNls final String resolvedName, TextRange textRange) {
1460 String description = JavaErrorMessages.message("member.referenced.before.constructor.called", resolvedName);
1461 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, textRange, description);
1464 public static HighlightInfo checkImplicitThisReferenceBeforeSuper(PsiClass aClass) {
1465 if (aClass instanceof PsiAnonymousClass) return null;
1466 PsiClass superClass = aClass.getSuperClass();
1467 if (superClass == null || !PsiUtil.isInnerClass(superClass)) return null;
1468 PsiClass outerClass = superClass.getContainingClass();
1469 if (!InheritanceUtil.isInheritorOrSelf(aClass, outerClass, true)) {
1470 return null;
1472 // 'this' can be used as an (implicit) super() qualifier
1473 PsiMethod[] constructors = aClass.getConstructors();
1474 if (constructors.length == 0) {
1475 TextRange range = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
1476 return createMemberReferencedError(aClass.getName()+".this", range);
1478 for (PsiMethod constructor : constructors) {
1479 if (!isSuperCalledInConstructor(constructor)) {
1480 return createMemberReferencedError(aClass.getName()+".this", HighlightNamesUtil.getMethodDeclarationTextRange(constructor));
1483 return null;
1486 private static boolean isSuperCalledInConstructor(final PsiMethod constructor) {
1487 final PsiCodeBlock body = constructor.getBody();
1488 if (body == null) return false;
1489 final PsiStatement[] statements = body.getStatements();
1490 if (statements.length == 0) return false;
1491 final PsiStatement statement = statements[0];
1492 final PsiElement element = new PsiMatcherImpl(statement)
1493 .dot(PsiMatchers.hasClass(PsiExpressionStatement.class))
1494 .firstChild(PsiMatchers.hasClass(PsiMethodCallExpression.class))
1495 .firstChild(PsiMatchers.hasClass(PsiReferenceExpression.class))
1496 .firstChild(PsiMatchers.hasClass(PsiKeyword.class))
1497 .dot(PsiMatchers.hasText(PsiKeyword.SUPER))
1498 .getElement();
1499 return element != null;
1502 private static String getMethodExpressionName(PsiElement element) {
1503 if (!(element instanceof PsiMethodCallExpression)) return null;
1504 PsiReferenceExpression methodExpression = ((PsiMethodCallExpression)element).getMethodExpression();
1505 return methodExpression.getReferenceName();
1507 public static boolean isSuperOrThisMethodCall(PsiElement element) {
1508 String name = getMethodExpressionName(element);
1509 return PsiKeyword.SUPER.equals(name) || PsiKeyword.THIS.equals(name);
1511 public static boolean isSuperMethodCall(PsiElement element) {
1512 String name = getMethodExpressionName(element);
1513 return PsiKeyword.SUPER.equals(name);
1516 private static boolean thisOrSuperReference(PsiExpression qualifierExpression, PsiClass aClass) {
1517 if (qualifierExpression == null) return true;
1518 PsiJavaCodeReferenceElement qualifier;
1519 if (qualifierExpression instanceof PsiThisExpression) {
1520 qualifier = ((PsiThisExpression)qualifierExpression).getQualifier();
1522 else if (qualifierExpression instanceof PsiSuperExpression) {
1523 qualifier = ((PsiSuperExpression)qualifierExpression).getQualifier();
1525 else {
1526 return false;
1528 if (qualifier == null) return true;
1529 PsiElement resolved = qualifier.resolve();
1530 return resolved instanceof PsiClass && InheritanceUtil.isInheritorOrSelf(aClass, (PsiClass)resolved, true);
1534 @Nullable
1535 public static HighlightInfo checkLabelWithoutStatement(PsiLabeledStatement statement) {
1536 if (statement.getStatement() == null) {
1537 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, statement, JavaErrorMessages.message("label.without.statement"));
1539 return null;
1543 @Nullable
1544 public static HighlightInfo checkLabelAlreadyInUse(PsiLabeledStatement statement) {
1545 PsiIdentifier identifier = statement.getLabelIdentifier();
1546 String text = identifier.getText();
1547 PsiElement element = statement;
1548 while (element != null) {
1549 if (element instanceof PsiMethod || element instanceof PsiClass) break;
1550 if (element instanceof PsiLabeledStatement && element != statement &&
1551 Comparing.equal(((PsiLabeledStatement)element).getLabelIdentifier().getText(), text)) {
1552 String description = JavaErrorMessages.message("duplicate.label", text);
1553 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, identifier, description);
1555 element = element.getParent();
1557 return null;
1561 @Nullable
1562 public static HighlightInfo checkUnclosedComment(PsiComment comment) {
1563 if (!(comment instanceof PsiDocComment) && !(comment.getTokenType() == JavaTokenType.C_STYLE_COMMENT)) return null;
1564 if (!comment.getText().endsWith("*/")) {
1565 int start = comment.getTextRange().getEndOffset() - 1;
1566 int end = start + 1;
1567 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, start, end, JavaErrorMessages.message("nonterminated.comment"));
1569 return null;
1573 @Nullable
1574 public static HighlightInfo checkExceptionAlreadyCaught(PsiJavaCodeReferenceElement element, PsiElement resolved) {
1575 if (!(resolved instanceof PsiClass)) return null;
1576 PsiClass catchClass = (PsiClass)resolved;
1577 if (!(element.getParent() instanceof PsiTypeElement)) return null;
1578 PsiElement catchParameter = element.getParent().getParent();
1579 if (!(catchParameter instanceof PsiParameter) || !(((PsiParameter)catchParameter).getDeclarationScope() instanceof PsiCatchSection)) {
1580 return null;
1582 PsiCatchSection catchSection = (PsiCatchSection)((PsiParameter)catchParameter).getDeclarationScope();
1583 PsiTryStatement statement = catchSection.getTryStatement();
1584 PsiCatchSection[] catchSections = statement.getCatchSections();
1585 int i = ArrayUtil.find(catchSections, catchSection);
1586 for (i--; i >= 0; i--) {
1587 PsiCatchSection section = catchSections[i];
1588 PsiType type = section.getCatchType();
1589 PsiClass upCatchClass = PsiUtil.resolveClassInType(type);
1590 if (upCatchClass == null) continue;
1591 if (InheritanceUtil.isInheritorOrSelf(catchClass, upCatchClass, true)) {
1592 String description = JavaErrorMessages
1593 .message("exception.already.caught", PsiFormatUtil.formatClass(catchClass, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_FQ_NAME));
1594 HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, element, description);
1595 QuickFixAction.registerQuickFixAction(highlightInfo, new MoveCatchUpFix(catchSection, section));
1596 QuickFixAction.registerQuickFixAction(highlightInfo, new DeleteCatchFix((PsiParameter)catchParameter));
1597 return highlightInfo;
1600 return null;
1604 @Nullable
1605 public static HighlightInfo checkTernaryOperatorConditionIsBoolean(PsiExpression expression) {
1606 if (expression.getParent() instanceof PsiConditionalExpression &&
1607 ((PsiConditionalExpression)expression.getParent()).getCondition() == expression && expression.getType() != null &&
1608 !TypeConversionUtil.isBooleanType(expression.getType())) {
1609 PsiType foundType = expression.getType();
1610 return createIncompatibleTypeHighlightInfo(PsiType.BOOLEAN, foundType, expression.getTextRange());
1612 return null;
1616 @Nullable
1617 public static HighlightInfo checkStatementPrependedWithCaseInsideSwitch(PsiStatement statement) {
1618 if (!(statement instanceof PsiSwitchLabelStatement) && statement.getParent() instanceof PsiCodeBlock &&
1619 statement.getParent().getParent() instanceof PsiSwitchStatement &&
1620 ((PsiCodeBlock)statement.getParent()).getStatements().length != 0 &&
1621 statement == ((PsiCodeBlock)statement.getParent()).getStatements()[0]) {
1622 return HighlightInfo
1623 .createHighlightInfo(HighlightInfoType.ERROR, statement, JavaErrorMessages.message("statement.must.be.prepended.with.case.label"));
1625 return null;
1629 @Nullable
1630 public static HighlightInfo checkAssertOperatorTypes(PsiExpression expression) {
1631 if (!(expression.getParent() instanceof PsiAssertStatement)) {
1632 return null;
1634 PsiAssertStatement assertStatement = (PsiAssertStatement)expression.getParent();
1635 PsiType type = expression.getType();
1636 if (type == null) return null;
1637 if (expression == assertStatement.getAssertCondition() && !TypeConversionUtil.isBooleanType(type)) {
1638 // addTypeCast quickfix is not applicable here since no type can be cast to boolean
1639 return createIncompatibleTypeHighlightInfo(PsiType.BOOLEAN, type, expression.getTextRange());
1641 else if (expression == assertStatement.getAssertDescription() && TypeConversionUtil.isVoidType(type)) {
1642 String description = JavaErrorMessages.message("void.type.is.not.allowed");
1643 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, description);
1645 return null;
1649 @Nullable
1650 public static HighlightInfo checkSynchronizedExpressionType(PsiExpression expression) {
1651 if (expression.getParent() instanceof PsiSynchronizedStatement) {
1652 PsiType type = expression.getType();
1653 if (type == null) return null;
1654 PsiSynchronizedStatement synchronizedStatement = (PsiSynchronizedStatement)expression.getParent();
1655 if (expression == synchronizedStatement.getLockExpression() &&
1656 (type instanceof PsiPrimitiveType || TypeConversionUtil.isNullType(type))) {
1657 PsiClassType objectType = PsiType.getJavaLangObject(expression.getManager(), expression.getResolveScope());
1658 return createIncompatibleTypeHighlightInfo(objectType, type, expression.getTextRange());
1661 return null;
1665 @Nullable
1666 public static HighlightInfo checkConditionalExpressionBranchTypesMatch(PsiExpression expression) {
1667 if (!(expression.getParent() instanceof PsiConditionalExpression)) {
1668 return null;
1670 PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)expression.getParent();
1671 // check else branches only
1672 if (conditionalExpression.getElseExpression() != expression) return null;
1673 final PsiExpression thenExpression = conditionalExpression.getThenExpression();
1674 assert thenExpression != null;
1675 PsiType thenType = thenExpression.getType();
1676 PsiType elseType = expression.getType();
1677 if (thenType == null || elseType == null) return null;
1678 if (conditionalExpression.getType() == null) {
1679 // cannot derive type of conditional expression
1680 // elsetype will never be castable to thentype, so no quick fix here
1681 return createIncompatibleTypeHighlightInfo(thenType, elseType, expression.getTextRange());
1683 return null;
1686 private static HighlightInfo createIncompatibleTypeHighlightInfo(final PsiType lType, final PsiType rType, final TextRange textRange) {
1687 PsiType lType1 = lType;
1688 PsiType rType1 = rType;
1689 PsiTypeParameter[] lTypeParams = PsiTypeParameter.EMPTY_ARRAY;
1690 PsiSubstitutor lTypeSubstitutor = PsiSubstitutor.EMPTY;
1691 if (lType1 instanceof PsiClassType) {
1692 PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)lType1).resolveGenerics();
1693 lTypeSubstitutor = resolveResult.getSubstitutor();
1694 PsiClass psiClass = resolveResult.getElement();
1695 if (psiClass instanceof PsiAnonymousClass) {
1696 lType1 = ((PsiAnonymousClass)psiClass).getBaseClassType();
1697 resolveResult = ((PsiClassType)lType1).resolveGenerics();
1698 lTypeSubstitutor = resolveResult.getSubstitutor();
1699 psiClass = resolveResult.getElement();
1701 lTypeParams = psiClass == null ? PsiTypeParameter.EMPTY_ARRAY : psiClass.getTypeParameters();
1703 PsiTypeParameter[] rTypeParams = PsiTypeParameter.EMPTY_ARRAY;
1704 PsiSubstitutor rTypeSubstitutor = PsiSubstitutor.EMPTY;
1705 if (rType1 instanceof PsiClassType) {
1706 PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)rType1).resolveGenerics();
1707 rTypeSubstitutor = resolveResult.getSubstitutor();
1708 PsiClass psiClass = resolveResult.getElement();
1709 if (psiClass instanceof PsiAnonymousClass) {
1710 rType1 = ((PsiAnonymousClass)psiClass).getBaseClassType();
1711 resolveResult = ((PsiClassType)rType1).resolveGenerics();
1712 rTypeSubstitutor = resolveResult.getSubstitutor();
1713 psiClass = resolveResult.getElement();
1715 rTypeParams = psiClass == null ? PsiTypeParameter.EMPTY_ARRAY : psiClass.getTypeParameters();
1719 int typeParamColumns = Math.max(lTypeParams.length, rTypeParams.length);
1720 @NonNls String requredRow = "";
1721 @NonNls String foundRow = "";
1722 for (int i = 0; i < typeParamColumns; i++) {
1723 PsiTypeParameter lTypeParameter = i >= lTypeParams.length ? null : lTypeParams[i];
1724 PsiTypeParameter rTypeParameter = i >= rTypeParams.length ? null : rTypeParams[i];
1725 PsiType lSubstedType = lTypeParameter == null ? null : lTypeSubstitutor.substitute(lTypeParameter);
1726 PsiType rSubstedType = rTypeParameter == null ? null : rTypeSubstitutor.substitute(rTypeParameter);
1727 boolean matches = Comparing.equal(lSubstedType, rSubstedType);
1728 @NonNls String openBrace = i == 0 ? "&lt;" : "";
1729 @NonNls String closeBrace = i == typeParamColumns - 1 ? "&gt;" : ",";
1730 requredRow += "<td>" + (lTypeParams.length == 0 ? "" : openBrace) + redIfNotMatch(lSubstedType, matches) +
1731 (i < lTypeParams.length ? closeBrace : "") + "</td>";
1732 foundRow += "<td>" + (rTypeParams.length == 0 ? "" : openBrace) + redIfNotMatch(rSubstedType, matches) +
1733 (i < rTypeParams.length ? closeBrace : "") + "</td>";
1735 PsiType lRawType = lType1 instanceof PsiClassType ? ((PsiClassType)lType1).rawType() : lType1;
1736 PsiType rRawType = rType1 instanceof PsiClassType ? ((PsiClassType)rType1).rawType() : rType1;
1737 boolean assignable = lRawType == null || rRawType == null || TypeConversionUtil.isAssignable(lRawType, rRawType);
1739 String toolTip = JavaErrorMessages.message("incompatible.types.html.tooltip",
1740 redIfNotMatch(lRawType, assignable), requredRow,
1741 redIfNotMatch(rRawType, assignable), foundRow);
1743 String description = JavaErrorMessages.message("incompatible.types", formatType(lType1), formatType(rType1));
1745 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, null, textRange.getStartOffset(), textRange.getEndOffset(),
1746 description, toolTip);
1749 @Nullable
1750 public static HighlightInfo checkSingleImportClassConflict(PsiImportStatement statement,
1751 Map<String, Pair<PsiImportStatementBase, PsiClass>> singleImportedClasses) {
1752 if (statement.isOnDemand()) return null;
1753 PsiElement element = statement.resolve();
1754 if (element instanceof PsiClass) {
1755 String name = ((PsiClass)element).getName();
1756 Pair<PsiImportStatementBase, PsiClass> imported = singleImportedClasses.get(name);
1757 PsiClass importedClass = imported == null ? null : imported.getSecond();
1758 if (importedClass != null && !element.getManager().areElementsEquivalent(importedClass, element)) {
1759 String description = JavaErrorMessages.message("single.import.class.conflict", formatClass(importedClass));
1760 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, statement, description);
1762 singleImportedClasses.put(name, Pair.<PsiImportStatementBase, PsiClass>create(statement, (PsiClass)element));
1764 return null;
1768 @NonNls
1769 private static String redIfNotMatch(PsiType type, boolean matches) {
1770 if (matches) return getFQName(type, false);
1771 return "<font color=red><b>" + getFQName(type, true) + "</b></font>";
1774 private static String getFQName(PsiType type, boolean longName) {
1775 if (type == null) return "";
1776 return XmlStringUtil.escapeString(longName ? type.getInternalCanonicalText() : type.getPresentableText());
1780 @Nullable
1781 public static HighlightInfo checkMustBeThrowable(PsiType type, PsiElement context, boolean addCastIntention) {
1782 if (type == null) return null;
1783 PsiElementFactory factory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory();
1784 PsiClassType throwable = factory.createTypeByFQClassName("java.lang.Throwable", context.getResolveScope());
1785 if (!TypeConversionUtil.isAssignable(throwable, type)) {
1786 HighlightInfo highlightInfo = createIncompatibleTypeHighlightInfo(throwable, type, context.getTextRange());
1787 if (addCastIntention && TypeConversionUtil.areTypesConvertible(type, throwable)) {
1788 if (context instanceof PsiExpression) {
1789 QuickFixAction.registerQuickFixAction(highlightInfo, new AddTypeCastFix(throwable, (PsiExpression)context));
1792 return highlightInfo;
1794 return null;
1798 @Nullable
1799 private static HighlightInfo checkMustBeThrowable(PsiClass aClass, PsiElement context) {
1800 if (aClass == null) return null;
1801 PsiClassType type = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(aClass);
1802 return checkMustBeThrowable(type, context, false);
1806 @Nullable
1807 public static HighlightInfo checkLabelDefined(PsiIdentifier labelIdentifier, PsiStatement exitedStatement) {
1808 if (labelIdentifier == null) return null;
1809 String label = labelIdentifier.getText();
1810 if (label == null) return null;
1811 if (exitedStatement == null) {
1812 String message = JavaErrorMessages.message("unresolved.label", label);
1813 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, labelIdentifier, message);
1815 return null;
1819 @Nullable
1820 public static HighlightInfo checkReference(PsiJavaCodeReferenceElement ref, JavaResolveResult result, PsiElement resolved) {
1821 PsiElement refName = ref.getReferenceNameElement();
1823 if (!(refName instanceof PsiIdentifier) && !(refName instanceof PsiKeyword)) return null;
1824 HighlightInfo highlightInfo = checkMemberReferencedBeforeConstructorCalled(ref);
1825 if (highlightInfo != null) return highlightInfo;
1827 PsiElement refParent = ref.getParent();
1828 if (!(refParent instanceof PsiMethodCallExpression)) {
1829 if (resolved == null) {
1830 // do not highlight unknown packages - javac does not care about illegal package names
1831 if (isInsidePackageStatement(refName)) return null;
1832 if (result.isPackagePrefixPackageReference()) return null;
1833 JavaResolveResult[] results = ref.multiResolve(true);
1834 String description;
1835 if (results.length > 1) {
1836 String t1 = format(results[0].getElement());
1837 String t2 = format(results[1].getElement());
1838 description = JavaErrorMessages.message("ambiguous.reference", refName.getText(), t1, t2);
1840 else {
1841 description = JavaErrorMessages.message("cannot.resolve.symbol", refName.getText());
1844 HighlightInfoType type = HighlightInfoType.WRONG_REF;
1845 if (PsiUtil.isInsideJavadocComment(ref)) return null;
1847 HighlightInfo info = HighlightInfo.createHighlightInfo(type, refName, description);
1849 UnresolvedReferenceQuickFixProvider.registerReferenceFixes(ref, new QuickFixActionRegistrarImpl(info));
1850 return info;
1853 if (!result.isValidResult() && !PsiUtil.isInsideJavadocComment(ref)) {
1854 if (!result.isAccessible()) {
1855 String description = buildProblemWithAccessDescription(ref, result);
1856 HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, ref.getReferenceNameElement(), description);
1857 if (result.isStaticsScopeCorrect()) {
1858 registerAccessQuickFixAction((PsiMember)resolved, ref, info, result.getCurrentFileResolveScope());
1859 if (ref instanceof PsiReferenceExpression) {
1860 QuickFixAction.registerQuickFixAction(info, new RenameWrongRefFix((PsiReferenceExpression)ref));
1863 return info;
1866 if (!result.isStaticsScopeCorrect()) {
1867 String description = buildProblemWithStaticDescription(resolved);
1868 HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, ref.getReferenceNameElement(), description);
1869 registerStaticProblemQuickFixAction(resolved, info, ref);
1870 if (ref instanceof PsiReferenceExpression) {
1871 QuickFixAction.registerQuickFixAction(info, new RenameWrongRefFix((PsiReferenceExpression)ref));
1873 return info;
1876 if ((resolved instanceof PsiLocalVariable || resolved instanceof PsiParameter) && !(resolved instanceof ImplicitVariable)) {
1877 highlightInfo = HighlightControlFlowUtil.checkVariableMustBeFinal((PsiVariable)resolved, ref);
1880 return highlightInfo;
1883 private static String format(PsiElement element) {
1884 if (element instanceof PsiClass) return formatClass((PsiClass)element);
1885 if (element instanceof PsiMethod) return formatMethod((PsiMethod)element);
1886 return ElementDescriptionUtil.getElementDescription(element, HighlightUsagesDescriptionLocation.INSTANCE);
1889 private static boolean isInsidePackageStatement(PsiElement element) {
1890 while (element != null) {
1891 if (element instanceof PsiPackageStatement) return true;
1892 if (!(element instanceof PsiIdentifier) && !(element instanceof PsiJavaCodeReferenceElement)) return false;
1893 element = element.getParent();
1895 return false;
1898 @Nullable
1899 public static HighlightInfo checkElementInReferenceList(PsiJavaCodeReferenceElement ref,
1900 PsiReferenceList referenceList,
1901 JavaResolveResult resolveResult) {
1902 PsiElement resolved = resolveResult.getElement();
1903 HighlightInfo highlightInfo = null;
1904 PsiElement refGrandParent = referenceList.getParent();
1905 if (resolved instanceof PsiClass) {
1906 PsiClass aClass = (PsiClass)resolved;
1907 if (refGrandParent instanceof PsiClass) {
1908 if (refGrandParent instanceof PsiTypeParameter) {
1909 highlightInfo = GenericsHighlightUtil.checkElementInTypeParameterExtendsList(referenceList, resolveResult, ref);
1911 else {
1912 highlightInfo = HighlightClassUtil.checkExtendsClassAndImplementsInterface(referenceList, resolveResult, ref);
1913 if (highlightInfo == null) {
1914 highlightInfo = HighlightClassUtil.checkCannotInheritFromFinal(aClass, ref);
1916 if (highlightInfo == null) {
1917 highlightInfo = GenericsHighlightUtil.checkCannotInheritFromEnum(aClass, ref);
1919 if (highlightInfo == null) {
1920 highlightInfo = GenericsHighlightUtil.checkCannotInheritFromTypeParameter(aClass, ref);
1924 else if (refGrandParent instanceof PsiMethod && ((PsiMethod)refGrandParent).getThrowsList() == referenceList) {
1925 highlightInfo = checkMustBeThrowable(aClass, ref);
1928 else if (refGrandParent instanceof PsiMethod && referenceList == ((PsiMethod)refGrandParent).getThrowsList()) {
1929 highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, ref, JavaErrorMessages.message("class.name.expected"));
1931 return highlightInfo;
1935 public static boolean isSerializable(PsiClass aClass) {
1936 PsiManager manager = aClass.getManager();
1937 PsiClass serializableClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.io.Serializable", aClass.getResolveScope());
1938 return serializableClass != null && aClass.isInheritor(serializableClass, true);
1941 public static boolean isSerializationImplicitlyUsedField(PsiField field) {
1942 final String name = field.getName();
1943 if (!SERIAL_VERSION_UID_FIELD_NAME.equals(name) && !SERIAL_PERSISTENT_FIELDS_FIELD_NAME.equals(name)) return false;
1944 if (!field.hasModifierProperty(PsiModifier.STATIC)) return false;
1945 PsiClass aClass = field.getContainingClass();
1946 return aClass == null || isSerializable(aClass);
1949 public static HighlightInfo checkClassReferenceAfterQualifier(final PsiReferenceExpression expression, final PsiElement resolved) {
1950 if (!(resolved instanceof PsiClass)) return null;
1951 final PsiExpression qualifier = expression.getQualifierExpression();
1952 if (qualifier == null) return null;
1953 if (qualifier instanceof PsiReferenceExpression) {
1954 PsiElement qualifierResolved = ((PsiReferenceExpression)qualifier).resolve();
1955 if (qualifierResolved instanceof PsiClass || qualifierResolved instanceof PsiPackage) return null;
1957 HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, qualifier, JavaErrorMessages.message("expected.class.or.package"));
1958 QuickFixAction.registerQuickFixAction(info, new RemoveQualifierFix(qualifier, expression, (PsiClass)resolved));
1959 return info;
1962 public static void registerChangeVariableTypeFixes(PsiVariable parameter, PsiType itemType, HighlightInfo highlightInfo) {
1963 for (ChangeVariableTypeQuickFixProvider fixProvider : Extensions.getExtensions(ChangeVariableTypeQuickFixProvider.EP_NAME)) {
1964 for (IntentionAction action : fixProvider.getFixes(parameter, itemType)) {
1965 QuickFixAction.registerQuickFixAction(highlightInfo, action, null);