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 * Checks and Highlights problems with classes
22 package com
.intellij
.codeInsight
.daemon
.impl
.analysis
;
24 import com
.intellij
.codeInsight
.ClassUtil
;
25 import com
.intellij
.codeInsight
.ExceptionUtil
;
26 import com
.intellij
.codeInsight
.daemon
.JavaErrorMessages
;
27 import com
.intellij
.codeInsight
.daemon
.impl
.HighlightInfo
;
28 import com
.intellij
.codeInsight
.daemon
.impl
.HighlightInfoType
;
29 import com
.intellij
.codeInsight
.daemon
.impl
.RefCountHolder
;
30 import com
.intellij
.codeInsight
.daemon
.impl
.quickfix
.*;
31 import com
.intellij
.codeInsight
.intention
.IntentionAction
;
32 import com
.intellij
.codeInsight
.intention
.QuickFixFactory
;
33 import com
.intellij
.openapi
.application
.ApplicationManager
;
34 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
35 import com
.intellij
.openapi
.module
.Module
;
36 import com
.intellij
.openapi
.module
.ModuleUtil
;
37 import com
.intellij
.openapi
.util
.Comparing
;
38 import com
.intellij
.openapi
.util
.TextRange
;
39 import com
.intellij
.openapi
.util
.io
.FileUtil
;
40 import com
.intellij
.openapi
.vfs
.VirtualFile
;
41 import com
.intellij
.psi
.*;
42 import com
.intellij
.psi
.impl
.source
.jsp
.jspJava
.JspClass
;
43 import com
.intellij
.psi
.search
.GlobalSearchScope
;
44 import com
.intellij
.psi
.util
.*;
45 import org
.jetbrains
.annotations
.NotNull
;
47 import java
.util
.ArrayList
;
48 import java
.util
.Collection
;
49 import java
.util
.HashSet
;
50 import java
.util
.List
;
52 public class HighlightClassUtil
{
53 public static final String INTERFACE_EXPECTED
= JavaErrorMessages
.message("interface.expected");
54 public static final String CLASS_EXPECTED
= JavaErrorMessages
.message("class.expected");
55 private static final String STATIC_DECLARATION_IN_INNER_CLASS
= JavaErrorMessages
.message("static.declaration.in.inner.class");
56 private static final QuickFixFactory QUICK_FIX_FACTORY
= QuickFixFactory
.getInstance();
59 * new ref(...) or new ref(..) { ... } where ref is abstract class
61 static HighlightInfo
checkAbstractInstantiation(PsiJavaCodeReferenceElement ref
) {
62 PsiElement parent
= ref
.getParent();
63 HighlightInfo highlightInfo
= null;
64 if (parent
instanceof PsiNewExpression
&& !PsiUtilBase
.hasErrorElementChild(parent
)) {
65 if (((PsiNewExpression
)parent
).getType() instanceof PsiArrayType
) return null;
66 PsiElement refElement
= ref
.resolve();
67 if (refElement
instanceof PsiClass
) {
68 highlightInfo
= checkInstantiationOfAbstractClass((PsiClass
)refElement
, ref
);
71 else if (parent
instanceof PsiAnonymousClass
72 && parent
.getParent() instanceof PsiNewExpression
73 && !PsiUtilBase
.hasErrorElementChild(parent
.getParent())) {
74 PsiAnonymousClass aClass
= (PsiAnonymousClass
)parent
;
75 highlightInfo
= checkClassWithAbstractMethods(aClass
, ref
.getTextRange());
80 static HighlightInfo
checkClassWithAbstractMethods(PsiClass aClass
, TextRange textRange
) {
81 PsiMethod abstractMethod
= ClassUtil
.getAnyAbstractMethod(aClass
);
83 if (abstractMethod
== null || abstractMethod
.getContainingClass() == null) {
86 String baseClassName
= HighlightUtil
.formatClass(aClass
, false);
87 String methodName
= HighlightUtil
.formatMethod(abstractMethod
);
88 String message
= JavaErrorMessages
.message("class.must.be.abstract",
91 HighlightUtil
.formatClass(abstractMethod
.getContainingClass(), false));
93 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
94 if (ClassUtil
.getAnyMethodToImplement(aClass
) != null) {
95 QuickFixAction
.registerQuickFixAction(errorResult
, QUICK_FIX_FACTORY
.createImplementMethodsFix(aClass
));
97 if (!(aClass
instanceof PsiAnonymousClass
)
98 && HighlightUtil
.getIncompatibleModifier(PsiModifier
.ABSTRACT
, aClass
.getModifierList()) == null) {
99 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.ABSTRACT
, true, false);
100 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
105 static HighlightInfo
checkClassMustBeAbstract(final PsiClass aClass
, final TextRange textRange
) {
106 if (aClass
.hasModifierProperty(PsiModifier
.ABSTRACT
) || aClass
.getRBrace() == null
107 || aClass
.isEnum() && hasEnumConstants(aClass
)
111 return checkClassWithAbstractMethods(aClass
, textRange
);
115 public static HighlightInfo
checkInstantiationOfAbstractClass(PsiClass aClass
, PsiElement highlighElement
) {
116 HighlightInfo errorResult
= null;
117 if (aClass
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
118 String baseClassName
= aClass
.getName();
119 String message
= JavaErrorMessages
.message("abstract.cannot.be.instantiated", baseClassName
);
120 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, highlighElement
, message
);
121 if (!aClass
.isInterface() && ClassUtil
.getAnyAbstractMethod(aClass
) == null) {
122 // suggest to make not abstract only if possible
123 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.ABSTRACT
, false, false);
124 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
130 private static boolean hasEnumConstants(PsiClass aClass
) {
131 PsiField
[] fields
= aClass
.getFields();
132 for (PsiField field
: fields
) {
133 if (field
instanceof PsiEnumConstant
) return true;
139 static HighlightInfo
checkDuplicateTopLevelClass(PsiClass aClass
) {
140 if (!(aClass
.getParent() instanceof PsiFile
)) return null;
141 String qualifiedName
= aClass
.getQualifiedName();
142 if (qualifiedName
== null) return null;
143 int numOfClassesToFind
= 2;
144 if (qualifiedName
.contains("$")) {
145 qualifiedName
= qualifiedName
.replaceAll("\\$", ".");
146 numOfClassesToFind
= 1;
148 PsiManager manager
= aClass
.getManager();
149 Module module
= ModuleUtil
.findModuleForPsiElement(aClass
);
150 if (module
== null) return null;
152 PsiClass
[] classes
= JavaPsiFacade
.getInstance(aClass
.getProject()).findClasses(qualifiedName
, GlobalSearchScope
.moduleScope(module
));
153 if (classes
.length
< numOfClassesToFind
) return null;
154 String dupFileName
= null;
155 for (PsiClass dupClass
: classes
) {
157 if (dupClass
!= aClass
) {
158 VirtualFile file
= dupClass
.getContainingFile().getVirtualFile();
159 if (file
!= null && manager
.isInProject(dupClass
)) {
160 dupFileName
= FileUtil
.toSystemDependentName(file
.getPath());
165 if (dupFileName
== null) return null;
166 String message
= JavaErrorMessages
.message("duplicate.class.in.other.file", dupFileName
);
167 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
169 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
173 static HighlightInfo
checkDuplicateNestedClass(PsiClass aClass
) {
174 if (aClass
== null) return null;
175 PsiElement parent
= aClass
;
176 if (aClass
.getParent() instanceof PsiDeclarationStatement
) {
177 parent
= aClass
.getParent();
179 String name
= aClass
.getName();
180 if (name
== null) return null;
181 boolean duplicateFound
= false;
182 boolean checkSiblings
= true;
183 while (parent
!= null) {
184 if (parent
instanceof PsiFile
) break;
185 PsiElement element
= checkSiblings ? parent
.getPrevSibling() : null;
186 if (element
== null) {
187 element
= parent
.getParent();
189 // The name of a local class C may not be redeclared
190 // as a local class of the directly enclosing method, constructor, or initializer block within the scope of C
191 // , or a compile-time error occurs.
192 // However, a local class declaration may be shadowed (?6.3.1)
193 // anywhere inside a class declaration nested within the local class declaration's scope.
194 if (element
instanceof PsiMethod
|| element
instanceof PsiClass
) {
195 checkSiblings
= false;
200 if (element
instanceof PsiDeclarationStatement
) element
= PsiTreeUtil
.getChildOfType(element
, PsiClass
.class);
201 if (element
instanceof PsiClass
&& name
.equals(((PsiClass
)element
).getName())) {
202 duplicateFound
= true;
207 if (duplicateFound
) {
208 String message
= JavaErrorMessages
.message("duplicate.class", name
);
209 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
210 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
217 static HighlightInfo
checkPublicClassInRightFile(PsiKeyword keyword
, PsiModifierList psiModifierList
) {
218 // todo most testcase classes located in wrong files
219 if (ApplicationManager
.getApplication().isUnitTestMode()) return null;
220 if (new PsiMatcherImpl(keyword
)
221 .dot(PsiMatchers
.hasText(PsiModifier
.PUBLIC
))
222 .parent(PsiMatchers
.hasClass(PsiModifierList
.class))
223 .parent(PsiMatchers
.hasClass(PsiClass
.class))
224 .parent(PsiMatchers
.hasClass(PsiJavaFile
.class))
225 .getElement() == null) {
228 PsiClass aClass
= (PsiClass
)keyword
.getParent().getParent();
229 PsiJavaFile file
= (PsiJavaFile
)aClass
.getContainingFile();
230 VirtualFile virtualFile
= file
.getVirtualFile();
231 HighlightInfo errorResult
= null;
232 if (virtualFile
!= null && !aClass
.getName().equals(virtualFile
.getNameWithoutExtension()) && aClass
.getNameIdentifier() != null) {
233 String message
= JavaErrorMessages
.message("public.class.should.be.named.after.file", aClass
.getName());
235 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, aClass
.getNameIdentifier(), message
);
236 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(psiModifierList
, PsiModifier
.PUBLIC
, false, false);
237 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
238 PsiClass
[] classes
= file
.getClasses();
239 if (classes
.length
> 1) {
240 QuickFixAction
.registerQuickFixAction(errorResult
, new MoveClassToSeparateFileFix(aClass
));
242 for (PsiClass otherClass
: classes
) {
243 if (!otherClass
.getManager().areElementsEquivalent(otherClass
, aClass
) && otherClass
.hasModifierProperty(PsiModifier
.PUBLIC
)
244 && otherClass
.getName().equals(virtualFile
.getNameWithoutExtension())) {
248 QuickFixAction
.registerQuickFixAction(errorResult
, new RenameFileFix(aClass
.getName() + "." + StdFileTypes
.JAVA
.getDefaultExtension()));
249 QuickFixAction
.registerQuickFixAction(errorResult
, new RenameElementFix(aClass
));
255 private static HighlightInfo
checkStaticFieldDeclarationInInnerClass(PsiKeyword keyword
) {
256 if (getEnclosingStaticClass(keyword
, PsiField
.class) == null) {
259 PsiField field
= (PsiField
)keyword
.getParent().getParent();
260 if (PsiUtilBase
.hasErrorElementChild(field
)) return null;
261 // except compile time constants
262 if (PsiUtil
.isCompileTimeConstant(field
)) {
265 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
267 STATIC_DECLARATION_IN_INNER_CLASS
);
268 IntentionAction fix1
= QUICK_FIX_FACTORY
.createModifierListFix(field
, PsiModifier
.STATIC
, false, false);
269 QuickFixAction
.registerQuickFixAction(errorResult
, fix1
);
270 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(field
.getContainingClass(), PsiModifier
.STATIC
, true, false);
271 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
276 private static HighlightInfo
checkStaticMethodDeclarationInInnerClass(PsiKeyword keyword
) {
277 if (getEnclosingStaticClass(keyword
, PsiMethod
.class) == null) {
280 PsiMethod method
= (PsiMethod
)keyword
.getParent().getParent();
281 if (PsiUtilBase
.hasErrorElementChild(method
)) return null;
282 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
284 STATIC_DECLARATION_IN_INNER_CLASS
);
285 IntentionAction fix1
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.STATIC
, false, false);
286 QuickFixAction
.registerQuickFixAction(errorResult
, fix1
);
287 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix((PsiClass
)keyword
.getParent().getParent().getParent(), PsiModifier
.STATIC
, true, false);
288 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
293 private static HighlightInfo
checkStaticInitializerDeclarationInInnerClass(PsiKeyword keyword
) {
294 if (getEnclosingStaticClass(keyword
, PsiClassInitializer
.class) == null) {
297 PsiClassInitializer initializer
= (PsiClassInitializer
)keyword
.getParent().getParent();
298 if (PsiUtilBase
.hasErrorElementChild(initializer
)) return null;
299 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
301 STATIC_DECLARATION_IN_INNER_CLASS
);
302 IntentionAction fix1
= QUICK_FIX_FACTORY
.createModifierListFix(initializer
, PsiModifier
.STATIC
, false, false);
303 QuickFixAction
.registerQuickFixAction(errorResult
, fix1
);
304 PsiClass owner
= (PsiClass
)keyword
.getParent().getParent().getParent();
305 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(owner
, PsiModifier
.STATIC
, true, false);
306 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
310 private static PsiElement
getEnclosingStaticClass(PsiKeyword keyword
, Class
<?
> parentClass
) {
311 return new PsiMatcherImpl(keyword
)
312 .dot(PsiMatchers
.hasText(PsiModifier
.STATIC
))
313 .parent(PsiMatchers
.hasClass(PsiModifierList
.class))
314 .parent(PsiMatchers
.hasClass(parentClass
))
315 .parent(PsiMatchers
.hasClass(PsiClass
.class))
316 .dot(PsiMatchers
.hasModifier(PsiModifier
.STATIC
, false))
317 .parent(PsiMatchers
.hasClass(new Class
[]{PsiClass
.class, PsiDeclarationStatement
.class, PsiNewExpression
.class, PsiEnumConstant
.class}))
322 private static HighlightInfo
checkStaticClassDeclarationInInnerClass(PsiKeyword keyword
) {
323 // keyword points to 'class' or 'interface' or 'enum'
324 if (new PsiMatcherImpl(keyword
)
325 .parent(PsiMatchers
.hasClass(PsiClass
.class))
326 .dot(PsiMatchers
.hasModifier(PsiModifier
.STATIC
, true))
327 .parent(PsiMatchers
.hasClass(PsiClass
.class))
328 .dot(PsiMatchers
.hasModifier(PsiModifier
.STATIC
, false))
329 .parent(PsiMatchers
.hasClass(new Class
[]{PsiClass
.class, PsiDeclarationStatement
.class, PsiNewExpression
.class, PsiEnumConstant
.class}))
330 .getElement() == null) {
333 PsiClass aClass
= (PsiClass
)keyword
.getParent();
334 if (PsiUtilBase
.hasErrorElementChild(aClass
)) return null;
335 // highlight 'static' keyword if any, or class or interface if not
336 PsiElement context
= null;
337 PsiModifierList modifierList
= aClass
.getModifierList();
338 PsiElement
[] children
= modifierList
.getChildren();
339 for (PsiElement element
: children
) {
340 if (Comparing
.equal(element
.getText(), PsiModifier
.STATIC
)) {
345 TextRange textRange
= context
== null ?
null : context
.getTextRange();
346 if (textRange
== null) {
347 textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
349 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, STATIC_DECLARATION_IN_INNER_CLASS
);
350 if (context
!= keyword
) {
351 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.STATIC
, false, false);
352 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
354 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
.getContainingClass(), PsiModifier
.STATIC
, true, false);
355 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
360 static HighlightInfo
checkStaticDeclarationInInnerClass(PsiKeyword keyword
) {
361 HighlightInfo errorResult
= checkStaticFieldDeclarationInInnerClass(keyword
);
362 if (errorResult
!= null) return errorResult
;
363 errorResult
= checkStaticMethodDeclarationInInnerClass(keyword
);
364 if (errorResult
!= null) return errorResult
;
365 errorResult
= checkStaticClassDeclarationInInnerClass(keyword
);
366 if (errorResult
!= null) return errorResult
;
367 errorResult
= checkStaticInitializerDeclarationInInnerClass(keyword
);
368 if (errorResult
!= null) return errorResult
;
373 static HighlightInfo
checkImplementsAllowed(PsiReferenceList list
) {
374 if (list
.getParent() instanceof PsiClass
) {
375 PsiClass aClass
= (PsiClass
)list
.getParent();
376 if (aClass
.isInterface()) {
377 boolean isImplements
= list
.equals(aClass
.getImplementsList());
379 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, list
, JavaErrorMessages
.message("implements.after.interface"));
387 static HighlightInfo
checkExtendsClassAndImplementsInterface(PsiReferenceList referenceList
,
388 JavaResolveResult resolveResult
,
389 PsiJavaCodeReferenceElement context
) {
390 PsiClass aClass
= (PsiClass
)referenceList
.getParent();
391 boolean isImplements
= referenceList
.equals(aClass
.getImplementsList());
392 boolean isInterface
= aClass
.isInterface();
393 if (isInterface
&& isImplements
) return null;
394 boolean mustBeInterface
= isImplements
|| isInterface
;
395 HighlightInfo errorResult
= null;
396 PsiClass extendFrom
= (PsiClass
)resolveResult
.getElement();
397 if (extendFrom
.isInterface() != mustBeInterface
) {
398 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
400 mustBeInterface ? INTERFACE_EXPECTED
: CLASS_EXPECTED
);
402 JavaPsiFacade
.getInstance(aClass
.getProject()).getElementFactory().createType(extendFrom
, resolveResult
.getSubstitutor());
403 QuickFixAction
.registerQuickFixAction(errorResult
, new ChangeExtendsToImplementsFix(aClass
, type
));
409 static HighlightInfo
checkCannotInheritFromFinal(PsiClass superClass
, PsiElement elementToHighlight
) {
410 HighlightInfo errorResult
= null;
411 if (superClass
.hasModifierProperty(PsiModifier
.FINAL
) || superClass
.isEnum()) {
412 String message
= JavaErrorMessages
.message("inheritance.from.final.class", superClass
.getQualifiedName());
413 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, elementToHighlight
, message
);
414 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(superClass
, PsiModifier
.FINAL
, false, false);
415 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
421 static HighlightInfo
checkAnonymousInheritFinal(PsiNewExpression expression
) {
422 PsiAnonymousClass aClass
= PsiTreeUtil
.getChildOfType(expression
, PsiAnonymousClass
.class);
423 if (aClass
== null) return null;
424 PsiClassType baseClassReference
= aClass
.getBaseClassType();
425 PsiClass baseClass
= baseClassReference
.resolve();
426 if (baseClass
== null) return null;
427 return checkCannotInheritFromFinal(baseClass
, aClass
.getBaseClassReference());
433 private static String
checkDefaultConstructorThrowsException(PsiMethod constructor
, @NotNull PsiClassType
[] handledExceptions
) {
434 PsiClassType
[] referencedTypes
= constructor
.getThrowsList().getReferencedTypes();
435 List
<PsiClassType
> exceptions
= new ArrayList
<PsiClassType
>();
436 for (PsiClassType referencedType
: referencedTypes
) {
437 if (!ExceptionUtil
.isUncheckedException(referencedType
) && !ExceptionUtil
.isHandledBy(referencedType
, handledExceptions
)) {
438 exceptions
.add(referencedType
);
441 if (!exceptions
.isEmpty()) {
442 return HighlightUtil
.getUnhandledExceptionsDescriptor(exceptions
);
448 public static HighlightInfo
checkClassDoesNotCallSuperConstructorOrHandleExceptions(PsiClass aClass
,
449 RefCountHolder refCountHolder
,
450 final PsiResolveHelper resolveHelper
) {
451 if (aClass
.isEnum()) return null;
452 // check only no-ctr classes. Problem with specific constructor will be highlighted inside it
453 if (aClass
.getConstructors().length
!= 0) return null;
454 // find no-args base class ctr
455 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
456 return checkBaseClassDefaultConstructorProblem(aClass
, refCountHolder
, resolveHelper
, textRange
, PsiClassType
.EMPTY_ARRAY
);
459 public static HighlightInfo
checkBaseClassDefaultConstructorProblem(PsiClass aClass
,
460 RefCountHolder refCountHolder
,
461 PsiResolveHelper resolveHelper
,
463 @NotNull PsiClassType
[] handledExceptions
) {
464 PsiClass baseClass
= aClass
.getSuperClass();
465 if (baseClass
== null) return null;
466 PsiMethod
[] constructors
= baseClass
.getConstructors();
467 if (constructors
.length
== 0) return null;
469 for (PsiMethod constructor
: constructors
) {
470 if (resolveHelper
.isAccessible(constructor
, aClass
, null)) {
471 if (constructor
.getParameterList().getParametersCount() == 0 ||
472 constructor
.getParameterList().getParametersCount() == 1 && constructor
.isVarArgs()
474 // it is an error if base ctr throws exceptions
475 String description
= checkDefaultConstructorThrowsException(constructor
, handledExceptions
);
476 if (description
!= null) {
477 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, description
);
479 if (refCountHolder
!= null) {
480 refCountHolder
.registerLocallyReferenced(constructor
);
487 String description
= JavaErrorMessages
.message("no.default.constructor.available", HighlightUtil
.formatClass(baseClass
));
489 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, description
);
490 QuickFixAction
.registerQuickFixAction(info
, new CreateConstructorMatchingSuperFix(aClass
));
496 static HighlightInfo
checkInterfaceCannotBeLocal(PsiClass aClass
) {
497 if (PsiUtil
.isLocalClass(aClass
)) {
498 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
499 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
501 JavaErrorMessages
.message("interface.cannot.be.local"));
507 public static HighlightInfo
checkCyclicInheritance(PsiClass aClass
) {
508 PsiClass circularClass
= getCircularClass(aClass
, new HashSet
<PsiClass
>());
509 if (circularClass
!= null) {
510 String description
= JavaErrorMessages
.message("cyclic.inheritance", HighlightUtil
.formatClass(circularClass
));
511 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
512 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, description
);
517 private static PsiClass
getCircularClass(PsiClass aClass
, Collection
<PsiClass
> usedClasses
) {
518 if (usedClasses
.contains(aClass
)) {
522 usedClasses
.add(aClass
);
523 PsiClass
[] superTypes
= aClass
.getSupers();
524 for (PsiElement superType
: superTypes
) {
525 while (superType
instanceof PsiClass
) {
526 if (!"java.lang.Object".equals(((PsiClass
)superType
).getQualifiedName())) {
527 PsiClass circularClass
= getCircularClass((PsiClass
)superType
, usedClasses
);
528 if (circularClass
!= null) return circularClass
;
530 // check class qualifier
531 superType
= superType
.getParent();
536 usedClasses
.remove(aClass
);
542 public static HighlightInfo
checkExtendsDuplicate(PsiJavaCodeReferenceElement element
, PsiElement resolved
) {
543 if (!(element
.getParent() instanceof PsiReferenceList
)) return null;
544 PsiReferenceList list
= (PsiReferenceList
)element
.getParent();
545 if (!(list
.getParent() instanceof PsiClass
)) return null;
546 if (!(resolved
instanceof PsiClass
)) return null;
547 PsiClass aClass
= (PsiClass
)resolved
;
548 PsiClassType
[] referencedTypes
= list
.getReferencedTypes();
550 for (PsiClassType referencedType
: referencedTypes
) {
551 PsiClass resolvedElement
= referencedType
.resolve();
552 if (resolvedElement
!= null && list
.getManager().areElementsEquivalent(resolvedElement
, aClass
)) {
557 String description
= JavaErrorMessages
.message("duplicate.class", HighlightUtil
.formatClass(aClass
));
558 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, element
, description
);
564 public static HighlightInfo
checkClassAlreadyImported(PsiClass aClass
, PsiElement elementToHighlight
) {
565 PsiFile file
= aClass
.getContainingFile();
566 if (!(file
instanceof PsiJavaFile
)) return null;
567 PsiJavaFile javaFile
= (PsiJavaFile
)file
;
568 // check only top-level classes conflicts
569 if (aClass
.getParent() != javaFile
) return null;
570 PsiImportList importList
= javaFile
.getImportList();
571 if (importList
== null) return null;
572 PsiImportStatementBase
[] importStatements
= importList
.getAllImportStatements();
573 for (PsiImportStatementBase importStatement
: importStatements
) {
574 if (importStatement
.isOnDemand()) continue;
575 PsiElement resolved
= importStatement
.resolve();
576 if (resolved
instanceof PsiClass
&& !resolved
.equals(aClass
) && Comparing
.equal(aClass
.getName(), ((PsiClass
)resolved
).getName(), true)) {
577 String description
= JavaErrorMessages
.message("class.already.imported", HighlightUtil
.formatClass(aClass
, false));
578 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
587 public static HighlightInfo
checkClassExtendsOnlyOneClass(PsiReferenceList list
) {
588 PsiClassType
[] referencedTypes
= list
.getReferencedTypes();
589 PsiElement parent
= list
.getParent();
590 if (!(parent
instanceof PsiClass
)) return null;
592 PsiClass aClass
= (PsiClass
)parent
;
593 if (!aClass
.isInterface()
594 && referencedTypes
.length
> 1
595 && aClass
.getExtendsList() == list
) {
596 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
598 JavaErrorMessages
.message("class.cannot.extend.multiple.classes"));
605 public static HighlightInfo
checkThingNotAllowedInInterface(PsiElement element
, PsiClass aClass
) {
606 if (aClass
== null || !aClass
.isInterface()) return null;
607 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
609 JavaErrorMessages
.message("not.allowed.in.interface"));
613 public static HighlightInfo
checkQualifiedNewOfStaticClass(PsiNewExpression expression
) {
614 PsiExpression qualifier
= expression
.getQualifier();
615 if (qualifier
== null) return null;
616 PsiType type
= expression
.getType();
617 if (type
instanceof PsiArrayType
) {
618 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
620 JavaErrorMessages
.message("invalid.qualified.new"));
621 QuickFixAction
.registerQuickFixAction(info
, new RemoveNewQualifierFix(expression
, null));
624 PsiClass aClass
= PsiUtil
.resolveClassInType(type
);
625 if (aClass
!= null && aClass
.hasModifierProperty(PsiModifier
.STATIC
)) {
626 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
628 JavaErrorMessages
.message("qualified.new.of.static.class"));
629 if (!aClass
.isEnum()) {
630 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.STATIC
, false, false);
631 QuickFixAction
.registerQuickFixAction(info
, fix
);
633 QuickFixAction
.registerQuickFixAction(info
, new RemoveNewQualifierFix(expression
, aClass
));
641 * class c extends foreign.inner {}
643 * @param extendRef points to the class in the extends list
644 * @param resolved extendRef resolved
646 public static HighlightInfo
checkClassExtendsForeignInnerClass(PsiJavaCodeReferenceElement extendRef
, PsiElement resolved
) {
647 PsiElement parent
= extendRef
.getParent();
648 if (!(parent
instanceof PsiReferenceList
)) {
651 PsiElement grand
= parent
.getParent();
652 if (!(grand
instanceof PsiClass
)) {
655 PsiClass aClass
= (PsiClass
)grand
;
656 if (aClass
.getExtendsList() != parent
|| aClass
instanceof PsiTypeParameter
) {
659 if (!(resolved
instanceof PsiClass
)) {
660 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, extendRef
, JavaErrorMessages
.message("class.name.expected"));
662 PsiClass base
= (PsiClass
)resolved
;
663 // must be inner class
664 if (!PsiUtil
.isInnerClass(base
)) return null;
665 PsiClass baseClass
= base
.getContainingClass();
667 if (!hasEnclosingInstanceInScope(baseClass
, extendRef
, true) && !qualifiedNewCalledInConstructors(aClass
, baseClass
)) {
668 String description
= JavaErrorMessages
.message("no.enclosing.instance.in.scope", HighlightUtil
.formatClass(baseClass
));
669 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, extendRef
, description
);
675 private static boolean qualifiedNewCalledInConstructors(final PsiClass aClass
, final PsiClass baseClass
) {
676 PsiMethod
[] constructors
= aClass
.getConstructors();
677 if (constructors
.length
== 0) return false;
678 for (PsiMethod constructor
: constructors
) {
679 PsiCodeBlock body
= constructor
.getBody();
680 if (body
== null) return false;
681 PsiStatement
[] statements
= body
.getStatements();
682 if (statements
.length
== 0) return false;
683 PsiStatement firstStatement
= statements
[0];
684 if (!(firstStatement
instanceof PsiExpressionStatement
)) return false;
685 PsiExpression expression
= ((PsiExpressionStatement
)firstStatement
).getExpression();
686 if (!HighlightUtil
.isSuperOrThisMethodCall(expression
)) return false;
687 PsiMethodCallExpression methodCallExpression
= (PsiMethodCallExpression
)expression
;
688 if (PsiKeyword
.THIS
.equals(methodCallExpression
.getMethodExpression().getReferenceName())) continue;
689 PsiReferenceExpression referenceExpression
= methodCallExpression
.getMethodExpression();
690 PsiExpression qualifierExpression
= referenceExpression
.getQualifierExpression();
691 if (!(qualifierExpression
instanceof PsiReferenceExpression
)) return false;
692 PsiType type
= qualifierExpression
.getType();
693 if (!(type
instanceof PsiClassType
)) return false;
694 PsiClass resolved
= ((PsiClassType
)type
).resolve();
695 if (resolved
!= baseClass
) return false;
701 static HighlightInfo
checkExternalizableHasPublicNoArgsConstructor(PsiClass aClass
, PsiElement context
) {
702 if (!isExternalizable(aClass
) || aClass
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
705 PsiMethod
[] constructors
= aClass
.getConstructors();
706 boolean hasPublicNoArgsConstructor
= constructors
.length
== 0;
707 for (PsiMethod constructor
: constructors
) {
708 if (constructor
.getParameterList().getParametersCount() == 0 && constructor
.hasModifierProperty(PsiModifier
.PUBLIC
)) {
709 hasPublicNoArgsConstructor
= true;
713 if (!hasPublicNoArgsConstructor
) {
714 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.WARNING
,
716 JavaErrorMessages
.message("externalizable.class.should.have.public.constructor"));
717 QuickFixAction
.registerQuickFixAction(highlightInfo
, QUICK_FIX_FACTORY
.createAddDefaultConstructorFix(aClass
));
718 return highlightInfo
;
723 private static boolean isExternalizable(PsiClass aClass
) {
724 PsiManager manager
= aClass
.getManager();
725 PsiClass externalizableClass
=
726 JavaPsiFacade
.getInstance(manager
.getProject()).findClass("java.io.Externalizable", aClass
.getResolveScope());
727 return externalizableClass
!= null && aClass
.isInheritor(externalizableClass
, true);
730 public static boolean hasEnclosingInstanceInScope(PsiClass aClass
, PsiElement scope
, final boolean isSuperClassAccepted
) {
731 PsiManager manager
= aClass
.getManager();
732 PsiElement place
= scope
;
733 while (place
!= null && place
!= aClass
&& !(place
instanceof PsiFile
)) {
734 if (place
instanceof PsiClass
) {
735 if (isSuperClassAccepted
) {
736 if (InheritanceUtil
.isInheritorOrSelf((PsiClass
)place
, aClass
, true)) return true;
739 if (manager
.areElementsEquivalent(place
, aClass
)) return true;
742 if (place
instanceof PsiModifierListOwner
&& ((PsiModifierListOwner
)place
).hasModifierProperty(PsiModifier
.STATIC
)) {
745 place
= place
.getParent();
747 return place
== aClass
;
750 public static HighlightInfo
checkCreateInnerClassFromStaticContext(PsiNewExpression expression
) {
751 PsiType type
= expression
.getType();
752 if (type
== null || type
instanceof PsiArrayType
|| type
instanceof PsiPrimitiveType
) return null;
753 PsiClass aClass
= PsiUtil
.resolveClassInType(type
);
754 if (aClass
== null) return null;
755 if (aClass
instanceof PsiAnonymousClass
) {
756 aClass
= ((PsiAnonymousClass
)aClass
).getBaseClassType().resolve();
757 if (aClass
== null) return null;
760 if (!PsiUtil
.isInnerClass(aClass
)) return null;
761 PsiClass outerClass
= aClass
.getContainingClass();
762 if (outerClass
== null) return null;
764 PsiElement placeToSearchEnclosingFrom
;
765 PsiExpression qualifier
= expression
.getQualifier();
766 if (qualifier
!= null) {
767 PsiType qtype
= qualifier
.getType();
768 placeToSearchEnclosingFrom
= PsiUtil
.resolveClassInType(qtype
);
771 placeToSearchEnclosingFrom
= expression
;
774 if (outerClass
instanceof JspClass
775 || hasEnclosingInstanceInScope(outerClass
, placeToSearchEnclosingFrom
, true)) return null;
776 return reportIllegalEnclosingUsage(placeToSearchEnclosingFrom
, aClass
, outerClass
, expression
);
779 public static HighlightInfo
checkSuperQualifierType(PsiMethodCallExpression superCall
) {
780 if (!HighlightUtil
.isSuperMethodCall(superCall
)) return null;
781 PsiMethod ctr
= PsiTreeUtil
.getParentOfType(superCall
, PsiMethod
.class, true, PsiMember
.class);
782 if (ctr
== null) return null;
783 PsiClass targetClass
= ctr
.getContainingClass().getSuperClass();
784 if (targetClass
== null) return null;
785 PsiExpression qualifier
= superCall
.getMethodExpression().getQualifierExpression();
786 if (qualifier
!= null && PsiUtil
.isInnerClass(targetClass
)) {
787 PsiClass outerClass
= targetClass
.getContainingClass();
788 PsiClassType outerType
= JavaPsiFacade
.getInstance(superCall
.getProject()).getElementFactory().createType(outerClass
);
789 return HighlightUtil
.checkAssignability(outerType
, null, qualifier
, qualifier
);
794 public static HighlightInfo
reportIllegalEnclosingUsage(PsiElement place
,
795 PsiClass aClass
, PsiClass outerClass
,
796 PsiElement elementToHighlight
) {
797 if (outerClass
!= null && !PsiTreeUtil
.isAncestor(outerClass
, place
, false)) {
798 String description
= JavaErrorMessages
.message("is.not.an.enclosing.class", HighlightUtil
.formatClass(outerClass
));
799 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, elementToHighlight
, description
);
801 PsiModifierListOwner staticParent
= PsiUtil
.getEnclosingStaticElement(place
, outerClass
);
802 if (staticParent
!= null) {
803 String description
= JavaErrorMessages
.message("cannot.be.referenced.from.static.context",
806 : HighlightUtil
.formatClass(outerClass
) + "." + PsiKeyword
.THIS
);
807 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, elementToHighlight
, description
);
808 // make context not static or referenced class static
809 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(staticParent
, PsiModifier
.STATIC
, false, false);
810 QuickFixAction
.registerQuickFixAction(highlightInfo
, fix
);
811 if (aClass
!= null && HighlightUtil
.getIncompatibleModifier(PsiModifier
.STATIC
, aClass
.getModifierList()) == null) {
812 IntentionAction fix2
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.STATIC
, true, false);
813 QuickFixAction
.registerQuickFixAction(highlightInfo
, fix2
);
815 return highlightInfo
;