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
.module
.Module
;
35 import com
.intellij
.openapi
.module
.ModuleUtil
;
36 import com
.intellij
.openapi
.util
.Comparing
;
37 import com
.intellij
.openapi
.util
.TextRange
;
38 import com
.intellij
.openapi
.util
.io
.FileUtil
;
39 import com
.intellij
.openapi
.vfs
.VirtualFile
;
40 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
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
);
80 public static HighlightInfo
checkClassWithAbstractMethods(PsiClass aClass
, PsiElement highlightElement
) {
81 Collection
<HierarchicalMethodSignature
> allMethods
= aClass
.getVisibleSignatures();
82 PsiMethod abstractMethod
= ClassUtil
.getAnyAbstractMethod(aClass
, allMethods
);
84 if (abstractMethod
!= null && abstractMethod
.getContainingClass() != null) {
85 String baseClassName
= HighlightUtil
.formatClass(aClass
, false);
86 String methodName
= HighlightUtil
.formatMethod(abstractMethod
);
87 String message
= JavaErrorMessages
.message("class.must.be.abstract",
90 HighlightUtil
.formatClass(abstractMethod
.getContainingClass(), false));
92 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, highlightElement
, message
);
93 if (ClassUtil
.getAnyMethodToImplement(aClass
, allMethods
) != null) {
94 QuickFixAction
.registerQuickFixAction(highlightInfo
, QUICK_FIX_FACTORY
.createImplementMethodsFix(aClass
));
102 public static HighlightInfo
checkInstantiationOfAbstractClass(PsiClass aClass
, PsiElement highlighElement
) {
103 HighlightInfo errorResult
= null;
104 if (aClass
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
105 String baseClassName
= aClass
.getName();
106 String message
= JavaErrorMessages
.message("abstract.cannot.be.instantiated", baseClassName
);
107 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, highlighElement
, message
);
108 if (!aClass
.isInterface() && ClassUtil
.getAnyAbstractMethod(aClass
, aClass
.getVisibleSignatures()) == null) {
109 // suggest to make not abstract only if possible
110 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.ABSTRACT
, false, false);
111 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
118 static HighlightInfo
checkClassMustBeAbstract(PsiClass aClass
) {
119 if (aClass
.hasModifierProperty(PsiModifier
.ABSTRACT
) || aClass
.getRBrace() == null ||
120 aClass
.isEnum() && hasEnumConstants(aClass
)
124 HighlightInfo errorResult
= null;
125 Collection
<HierarchicalMethodSignature
> allMethods
= aClass
.getVisibleSignatures();
126 PsiMethod abstractMethod
= ClassUtil
.getAnyAbstractMethod(aClass
, allMethods
);
127 if (abstractMethod
!= null) {
128 String message
= JavaErrorMessages
.message("class.must.be.abstract",
129 HighlightUtil
.formatClass(aClass
, false),
130 HighlightUtil
.formatMethod(abstractMethod
),
131 HighlightUtil
.formatClass(abstractMethod
.getContainingClass()));
133 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
134 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
135 if (ClassUtil
.getAnyMethodToImplement(aClass
, allMethods
) != null) {
136 QuickFixAction
.registerQuickFixAction(errorResult
, QUICK_FIX_FACTORY
.createImplementMethodsFix(aClass
));
138 if (!(aClass
instanceof PsiAnonymousClass
)
139 && HighlightUtil
.getIncompatibleModifier(PsiModifier
.ABSTRACT
, aClass
.getModifierList()) == null) {
140 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.ABSTRACT
, true, false);
141 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
147 private static boolean hasEnumConstants(PsiClass aClass
) {
148 PsiField
[] fields
= aClass
.getFields();
149 for (PsiField field
: fields
) {
150 if (field
instanceof PsiEnumConstant
) return true;
156 static HighlightInfo
checkDuplicateTopLevelClass(PsiClass aClass
) {
157 if (!(aClass
.getParent() instanceof PsiFile
)) return null;
158 String qualifiedName
= aClass
.getQualifiedName();
159 if (qualifiedName
== null) return null;
160 int numOfClassesToFind
= 2;
161 if (qualifiedName
.contains("$")) {
162 qualifiedName
= qualifiedName
.replaceAll("\\$", ".");
163 numOfClassesToFind
= 1;
165 PsiManager manager
= aClass
.getManager();
166 Module module
= ModuleUtil
.findModuleForPsiElement(aClass
);
167 if (module
== null) return null;
169 PsiClass
[] classes
= JavaPsiFacade
.getInstance(aClass
.getProject()).findClasses(qualifiedName
, GlobalSearchScope
.moduleScope(module
));
170 if (classes
.length
< numOfClassesToFind
) return null;
171 String dupFileName
= null;
172 for (PsiClass dupClass
: classes
) {
174 if (dupClass
!= aClass
) {
175 VirtualFile file
= dupClass
.getContainingFile().getVirtualFile();
176 if (file
!= null && manager
.isInProject(dupClass
)) {
177 dupFileName
= FileUtil
.toSystemDependentName(file
.getPath());
182 if (dupFileName
== null) return null;
183 String message
= JavaErrorMessages
.message("duplicate.class.in.other.file", dupFileName
);
184 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
186 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
190 static HighlightInfo
checkDuplicateNestedClass(PsiClass aClass
) {
191 if (aClass
== null) return null;
192 PsiElement parent
= aClass
;
193 if (aClass
.getParent() instanceof PsiDeclarationStatement
) {
194 parent
= aClass
.getParent();
196 String name
= aClass
.getName();
197 if (name
== null) return null;
198 boolean duplicateFound
= false;
199 boolean checkSiblings
= true;
200 while (parent
!= null) {
201 if (parent
instanceof PsiFile
) break;
202 PsiElement element
= checkSiblings ? parent
.getPrevSibling() : null;
203 if (element
== null) {
204 element
= parent
.getParent();
206 // The name of a local class C may not be redeclared
207 // as a local class of the directly enclosing method, constructor, or initializer block within the scope of C
208 // , or a compile-time error occurs.
209 // However, a local class declaration may be shadowed (?6.3.1)
210 // anywhere inside a class declaration nested within the local class declaration's scope.
211 if (element
instanceof PsiMethod
|| element
instanceof PsiClass
) {
212 checkSiblings
= false;
217 if (element
instanceof PsiDeclarationStatement
) element
= PsiTreeUtil
.getChildOfType(element
, PsiClass
.class);
218 if (element
instanceof PsiClass
&& name
.equals(((PsiClass
)element
).getName())) {
219 duplicateFound
= true;
224 if (duplicateFound
) {
225 String message
= JavaErrorMessages
.message("duplicate.class", name
);
226 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
227 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
234 static HighlightInfo
checkPublicClassInRightFile(PsiKeyword keyword
, PsiModifierList psiModifierList
) {
235 // todo most testcase classes located in wrong files
236 if (ApplicationManager
.getApplication().isUnitTestMode()) return null;
237 if (new PsiMatcherImpl(keyword
)
238 .dot(PsiMatchers
.hasText(PsiModifier
.PUBLIC
))
239 .parent(PsiMatchers
.hasClass(PsiModifierList
.class))
240 .parent(PsiMatchers
.hasClass(PsiClass
.class))
241 .parent(PsiMatchers
.hasClass(PsiJavaFile
.class))
242 .getElement() == null) {
245 PsiClass aClass
= (PsiClass
)keyword
.getParent().getParent();
246 PsiJavaFile file
= (PsiJavaFile
)aClass
.getContainingFile();
247 VirtualFile virtualFile
= file
.getVirtualFile();
248 HighlightInfo errorResult
= null;
249 if (virtualFile
!= null && !aClass
.getName().equals(virtualFile
.getNameWithoutExtension()) && aClass
.getNameIdentifier() != null) {
250 String message
= JavaErrorMessages
.message("public.class.should.be.named.after.file", aClass
.getName());
252 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, aClass
.getNameIdentifier(), message
);
253 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(psiModifierList
, PsiModifier
.PUBLIC
, false, false);
254 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
255 PsiClass
[] classes
= file
.getClasses();
256 if (classes
.length
> 1) {
257 QuickFixAction
.registerQuickFixAction(errorResult
, new MoveClassToSeparateFileFix(aClass
));
259 for (PsiClass otherClass
: classes
) {
260 if (!otherClass
.getManager().areElementsEquivalent(otherClass
, aClass
) && otherClass
.hasModifierProperty(PsiModifier
.PUBLIC
)
261 && otherClass
.getName().equals(virtualFile
.getNameWithoutExtension())) {
265 QuickFixAction
.registerQuickFixAction(errorResult
, new RenameFileFix(aClass
.getName() + "." + StdFileTypes
.JAVA
.getDefaultExtension()));
266 QuickFixAction
.registerQuickFixAction(errorResult
, new RenameElementFix(aClass
));
272 private static HighlightInfo
checkStaticFieldDeclarationInInnerClass(PsiKeyword keyword
) {
273 if (getEnclosingStaticClass(keyword
, PsiField
.class) == null) {
276 PsiField field
= (PsiField
)keyword
.getParent().getParent();
277 if (PsiUtilBase
.hasErrorElementChild(field
)) return null;
278 // except compile time constants
279 if (PsiUtil
.isCompileTimeConstant(field
)) {
282 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
284 STATIC_DECLARATION_IN_INNER_CLASS
);
285 IntentionAction fix1
= QUICK_FIX_FACTORY
.createModifierListFix(field
, PsiModifier
.STATIC
, false, false);
286 QuickFixAction
.registerQuickFixAction(errorResult
, fix1
);
287 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(field
.getContainingClass(), PsiModifier
.STATIC
, true, false);
288 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
293 private static HighlightInfo
checkStaticMethodDeclarationInInnerClass(PsiKeyword keyword
) {
294 if (getEnclosingStaticClass(keyword
, PsiMethod
.class) == null) {
297 PsiMethod method
= (PsiMethod
)keyword
.getParent().getParent();
298 if (PsiUtilBase
.hasErrorElementChild(method
)) return null;
299 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
301 STATIC_DECLARATION_IN_INNER_CLASS
);
302 IntentionAction fix1
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.STATIC
, false, false);
303 QuickFixAction
.registerQuickFixAction(errorResult
, fix1
);
304 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix((PsiClass
)keyword
.getParent().getParent().getParent(), PsiModifier
.STATIC
, true, false);
305 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
310 private static HighlightInfo
checkStaticInitializerDeclarationInInnerClass(PsiKeyword keyword
) {
311 if (getEnclosingStaticClass(keyword
, PsiClassInitializer
.class) == null) {
314 PsiClassInitializer initializer
= (PsiClassInitializer
)keyword
.getParent().getParent();
315 if (PsiUtilBase
.hasErrorElementChild(initializer
)) return null;
316 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
318 STATIC_DECLARATION_IN_INNER_CLASS
);
319 IntentionAction fix1
= QUICK_FIX_FACTORY
.createModifierListFix(initializer
, PsiModifier
.STATIC
, false, false);
320 QuickFixAction
.registerQuickFixAction(errorResult
, fix1
);
321 PsiClass owner
= (PsiClass
)keyword
.getParent().getParent().getParent();
322 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(owner
, PsiModifier
.STATIC
, true, false);
323 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
327 private static PsiElement
getEnclosingStaticClass(PsiKeyword keyword
, Class
<?
> parentClass
) {
328 return new PsiMatcherImpl(keyword
)
329 .dot(PsiMatchers
.hasText(PsiModifier
.STATIC
))
330 .parent(PsiMatchers
.hasClass(PsiModifierList
.class))
331 .parent(PsiMatchers
.hasClass(parentClass
))
332 .parent(PsiMatchers
.hasClass(PsiClass
.class))
333 .dot(PsiMatchers
.hasModifier(PsiModifier
.STATIC
, false))
334 .parent(PsiMatchers
.hasClass(new Class
[]{PsiClass
.class, PsiDeclarationStatement
.class, PsiNewExpression
.class, PsiEnumConstant
.class}))
339 private static HighlightInfo
checkStaticClassDeclarationInInnerClass(PsiKeyword keyword
) {
340 // keyword points to 'class' or 'interface' or 'enum'
341 if (new PsiMatcherImpl(keyword
)
342 .parent(PsiMatchers
.hasClass(PsiClass
.class))
343 .dot(PsiMatchers
.hasModifier(PsiModifier
.STATIC
, true))
344 .parent(PsiMatchers
.hasClass(PsiClass
.class))
345 .dot(PsiMatchers
.hasModifier(PsiModifier
.STATIC
, false))
346 .parent(PsiMatchers
.hasClass(new Class
[]{PsiClass
.class, PsiDeclarationStatement
.class, PsiNewExpression
.class, PsiEnumConstant
.class}))
347 .getElement() == null) {
350 PsiClass aClass
= (PsiClass
)keyword
.getParent();
351 if (PsiUtilBase
.hasErrorElementChild(aClass
)) return null;
352 // highlight 'static' keyword if any, or class or interface if not
353 PsiElement context
= null;
354 PsiModifierList modifierList
= aClass
.getModifierList();
355 PsiElement
[] children
= modifierList
.getChildren();
356 for (PsiElement element
: children
) {
357 if (Comparing
.equal(element
.getText(), PsiModifier
.STATIC
)) {
362 TextRange textRange
= context
== null ?
null : context
.getTextRange();
363 if (textRange
== null) {
364 textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
366 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, STATIC_DECLARATION_IN_INNER_CLASS
);
367 if (context
!= keyword
) {
368 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.STATIC
, false, false);
369 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
371 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
.getContainingClass(), PsiModifier
.STATIC
, true, false);
372 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
377 static HighlightInfo
checkStaticDeclarationInInnerClass(PsiKeyword keyword
) {
378 HighlightInfo errorResult
= checkStaticFieldDeclarationInInnerClass(keyword
);
379 if (errorResult
!= null) return errorResult
;
380 errorResult
= checkStaticMethodDeclarationInInnerClass(keyword
);
381 if (errorResult
!= null) return errorResult
;
382 errorResult
= checkStaticClassDeclarationInInnerClass(keyword
);
383 if (errorResult
!= null) return errorResult
;
384 errorResult
= checkStaticInitializerDeclarationInInnerClass(keyword
);
385 if (errorResult
!= null) return errorResult
;
390 static HighlightInfo
checkImplementsAllowed(PsiReferenceList list
) {
391 if (list
.getParent() instanceof PsiClass
) {
392 PsiClass aClass
= (PsiClass
)list
.getParent();
393 if (aClass
.isInterface()) {
394 boolean isImplements
= list
.equals(aClass
.getImplementsList());
396 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, list
, JavaErrorMessages
.message("implements.after.interface"));
404 static HighlightInfo
checkExtendsClassAndImplementsInterface(PsiReferenceList referenceList
,
405 JavaResolveResult resolveResult
,
406 PsiJavaCodeReferenceElement context
) {
407 PsiClass aClass
= (PsiClass
)referenceList
.getParent();
408 boolean isImplements
= referenceList
.equals(aClass
.getImplementsList());
409 boolean isInterface
= aClass
.isInterface();
410 if (isInterface
&& isImplements
) return null;
411 boolean mustBeInterface
= isImplements
|| isInterface
;
412 HighlightInfo errorResult
= null;
413 PsiClass extendFrom
= (PsiClass
)resolveResult
.getElement();
414 if (extendFrom
.isInterface() != mustBeInterface
) {
415 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
417 mustBeInterface ? INTERFACE_EXPECTED
: CLASS_EXPECTED
);
419 JavaPsiFacade
.getInstance(aClass
.getProject()).getElementFactory().createType(extendFrom
, resolveResult
.getSubstitutor());
420 QuickFixAction
.registerQuickFixAction(errorResult
, new ChangeExtendsToImplementsFix(aClass
, type
));
426 static HighlightInfo
checkCannotInheritFromFinal(PsiClass superClass
, PsiElement elementToHighlight
) {
427 HighlightInfo errorResult
= null;
428 if (superClass
.hasModifierProperty(PsiModifier
.FINAL
) || superClass
.isEnum()) {
429 String message
= JavaErrorMessages
.message("inheritance.from.final.class", superClass
.getQualifiedName());
430 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, elementToHighlight
, message
);
431 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(superClass
, PsiModifier
.FINAL
, false, false);
432 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
438 static HighlightInfo
checkAnonymousInheritFinal(PsiNewExpression expression
) {
439 PsiAnonymousClass aClass
= PsiTreeUtil
.getChildOfType(expression
, PsiAnonymousClass
.class);
440 if (aClass
== null) return null;
441 PsiClassType baseClassReference
= aClass
.getBaseClassType();
442 PsiClass baseClass
= baseClassReference
.resolve();
443 if (baseClass
== null) return null;
444 return checkCannotInheritFromFinal(baseClass
, aClass
.getBaseClassReference());
450 private static String
checkDefaultConstructorThrowsException(PsiMethod constructor
, @NotNull PsiClassType
[] handledExceptions
) {
451 PsiClassType
[] referencedTypes
= constructor
.getThrowsList().getReferencedTypes();
452 List
<PsiClassType
> exceptions
= new ArrayList
<PsiClassType
>();
453 for (PsiClassType referencedType
: referencedTypes
) {
454 if (!ExceptionUtil
.isUncheckedException(referencedType
) && !ExceptionUtil
.isHandledBy(referencedType
, handledExceptions
)) {
455 exceptions
.add(referencedType
);
458 if (!exceptions
.isEmpty()) {
459 return HighlightUtil
.getUnhandledExceptionsDescriptor(exceptions
);
465 public static HighlightInfo
checkClassDoesNotCallSuperConstructorOrHandleExceptions(PsiClass aClass
,
466 RefCountHolder refCountHolder
,
467 final PsiResolveHelper resolveHelper
) {
468 if (aClass
.isEnum()) return null;
469 // check only no-ctr classes. Problem with specific constructor will be highlighted inside it
470 if (aClass
.getConstructors().length
!= 0) return null;
471 // find no-args base class ctr
472 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
473 return checkBaseClassDefaultConstructorProblem(aClass
, refCountHolder
, resolveHelper
, textRange
, PsiClassType
.EMPTY_ARRAY
);
476 public static HighlightInfo
checkBaseClassDefaultConstructorProblem(PsiClass aClass
,
477 RefCountHolder refCountHolder
,
478 PsiResolveHelper resolveHelper
,
480 @NotNull PsiClassType
[] handledExceptions
) {
481 PsiClass baseClass
= aClass
.getSuperClass();
482 if (baseClass
== null) return null;
483 PsiMethod
[] constructors
= baseClass
.getConstructors();
484 if (constructors
.length
== 0) return null;
486 for (PsiMethod constructor
: constructors
) {
487 if (resolveHelper
.isAccessible(constructor
, aClass
, null)) {
488 if (constructor
.getParameterList().getParametersCount() == 0 ||
489 constructor
.getParameterList().getParametersCount() == 1 && constructor
.isVarArgs()
491 // it is an error if base ctr throws exceptions
492 String description
= checkDefaultConstructorThrowsException(constructor
, handledExceptions
);
493 if (description
!= null) {
494 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, description
);
496 if (refCountHolder
!= null) {
497 refCountHolder
.registerLocallyReferenced(constructor
);
504 String description
= JavaErrorMessages
.message("no.default.constructor.available", HighlightUtil
.formatClass(baseClass
));
506 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, description
);
507 QuickFixAction
.registerQuickFixAction(info
, new CreateConstructorMatchingSuperFix(aClass
));
513 static HighlightInfo
checkInterfaceCannotBeLocal(PsiClass aClass
) {
514 if (PsiUtil
.isLocalClass(aClass
)) {
515 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
516 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
518 JavaErrorMessages
.message("interface.cannot.be.local"));
524 public static HighlightInfo
checkCyclicInheritance(PsiClass aClass
) {
525 PsiClass circularClass
= getCircularClass(aClass
, new HashSet
<PsiClass
>());
526 if (circularClass
!= null) {
527 String description
= JavaErrorMessages
.message("cyclic.inheritance", HighlightUtil
.formatClass(circularClass
));
528 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
529 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, description
);
534 private static PsiClass
getCircularClass(PsiClass aClass
, Collection
<PsiClass
> usedClasses
) {
535 if (usedClasses
.contains(aClass
)) {
539 usedClasses
.add(aClass
);
540 PsiClass
[] superTypes
= aClass
.getSupers();
541 for (PsiElement superType
: superTypes
) {
542 while (superType
instanceof PsiClass
) {
543 if (!"java.lang.Object".equals(((PsiClass
)superType
).getQualifiedName())) {
544 PsiClass circularClass
= getCircularClass((PsiClass
)superType
, usedClasses
);
545 if (circularClass
!= null) return circularClass
;
547 // check class qualifier
548 superType
= superType
.getParent();
553 usedClasses
.remove(aClass
);
559 public static HighlightInfo
checkExtendsDuplicate(PsiJavaCodeReferenceElement element
, PsiElement resolved
) {
560 if (!(element
.getParent() instanceof PsiReferenceList
)) return null;
561 PsiReferenceList list
= (PsiReferenceList
)element
.getParent();
562 if (!(list
.getParent() instanceof PsiClass
)) return null;
563 if (!(resolved
instanceof PsiClass
)) return null;
564 PsiClass aClass
= (PsiClass
)resolved
;
565 PsiClassType
[] referencedTypes
= list
.getReferencedTypes();
567 for (PsiClassType referencedType
: referencedTypes
) {
568 PsiClass resolvedElement
= referencedType
.resolve();
569 if (resolvedElement
!= null && list
.getManager().areElementsEquivalent(resolvedElement
, aClass
)) {
574 String description
= JavaErrorMessages
.message("duplicate.class", HighlightUtil
.formatClass(aClass
));
575 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, element
, description
);
581 public static HighlightInfo
checkClassAlreadyImported(PsiClass aClass
, PsiElement elementToHighlight
) {
582 PsiFile file
= aClass
.getContainingFile();
583 if (!(file
instanceof PsiJavaFile
)) return null;
584 PsiJavaFile javaFile
= (PsiJavaFile
)file
;
585 // check only top-level classes conflicts
586 if (aClass
.getParent() != javaFile
) return null;
587 PsiImportList importList
= javaFile
.getImportList();
588 if (importList
== null) return null;
589 PsiImportStatementBase
[] importStatements
= importList
.getAllImportStatements();
590 for (PsiImportStatementBase importStatement
: importStatements
) {
591 if (importStatement
.isOnDemand()) continue;
592 PsiElement resolved
= importStatement
.resolve();
593 if (resolved
instanceof PsiClass
&& !resolved
.equals(aClass
) && Comparing
.equal(aClass
.getName(), ((PsiClass
)resolved
).getName(), true)) {
594 String description
= JavaErrorMessages
.message("class.already.imported", HighlightUtil
.formatClass(aClass
, false));
595 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
604 public static HighlightInfo
checkClassExtendsOnlyOneClass(PsiReferenceList list
) {
605 PsiClassType
[] referencedTypes
= list
.getReferencedTypes();
606 PsiElement parent
= list
.getParent();
607 if (!(parent
instanceof PsiClass
)) return null;
609 PsiClass aClass
= (PsiClass
)parent
;
610 if (!aClass
.isInterface()
611 && referencedTypes
.length
> 1
612 && aClass
.getExtendsList() == list
) {
613 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
615 JavaErrorMessages
.message("class.cannot.extend.multiple.classes"));
622 public static HighlightInfo
checkThingNotAllowedInInterface(PsiElement element
, PsiClass aClass
) {
623 if (aClass
== null || !aClass
.isInterface()) return null;
624 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
626 JavaErrorMessages
.message("not.allowed.in.interface"));
630 public static HighlightInfo
checkQualifiedNewOfStaticClass(PsiNewExpression expression
) {
631 PsiExpression qualifier
= expression
.getQualifier();
632 if (qualifier
== null) return null;
633 PsiType type
= expression
.getType();
634 if (type
instanceof PsiArrayType
) {
635 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
637 JavaErrorMessages
.message("invalid.qualified.new"));
638 QuickFixAction
.registerQuickFixAction(info
, new RemoveNewQualifierFix(expression
, null));
641 PsiClass aClass
= PsiUtil
.resolveClassInType(type
);
642 if (aClass
!= null && aClass
.hasModifierProperty(PsiModifier
.STATIC
)) {
643 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
645 JavaErrorMessages
.message("qualified.new.of.static.class"));
646 if (!aClass
.isEnum()) {
647 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.STATIC
, false, false);
648 QuickFixAction
.registerQuickFixAction(info
, fix
);
650 QuickFixAction
.registerQuickFixAction(info
, new RemoveNewQualifierFix(expression
, aClass
));
658 * class c extends foreign.inner {}
660 * @param extendRef points to the class in the extends list
661 * @param resolved extendRef resolved
663 public static HighlightInfo
checkClassExtendsForeignInnerClass(PsiJavaCodeReferenceElement extendRef
, PsiElement resolved
) {
664 PsiElement parent
= extendRef
.getParent();
665 if (!(parent
instanceof PsiReferenceList
)) {
668 PsiElement grand
= parent
.getParent();
669 if (!(grand
instanceof PsiClass
) || ((PsiClass
)grand
).getExtendsList() != parent
) {
672 if (!(resolved
instanceof PsiClass
)) {
673 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, extendRef
, JavaErrorMessages
.message("class.name.expected"));
675 PsiClass base
= (PsiClass
)resolved
;
676 // must be inner class
677 if (!PsiUtil
.isInnerClass(base
)) return null;
678 PsiClass baseClass
= base
.getContainingClass();
680 PsiClass aClass
= (PsiClass
)grand
;
681 if (!hasEnclosingInstanceInScope(baseClass
, extendRef
, true) && !qualifiedNewCalledInConstructors(aClass
, baseClass
)) {
682 String description
= JavaErrorMessages
.message("no.enclosing.instance.in.scope", HighlightUtil
.formatClass(baseClass
));
683 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, extendRef
, description
);
689 private static boolean qualifiedNewCalledInConstructors(final PsiClass aClass
, final PsiClass baseClass
) {
690 PsiMethod
[] constructors
= aClass
.getConstructors();
691 if (constructors
.length
== 0) return false;
692 for (PsiMethod constructor
: constructors
) {
693 PsiCodeBlock body
= constructor
.getBody();
694 if (body
== null) return false;
695 PsiStatement
[] statements
= body
.getStatements();
696 if (statements
.length
== 0) return false;
697 PsiStatement firstStatement
= statements
[0];
698 if (!(firstStatement
instanceof PsiExpressionStatement
)) return false;
699 PsiExpression expression
= ((PsiExpressionStatement
)firstStatement
).getExpression();
700 if (!HighlightUtil
.isSuperOrThisMethodCall(expression
)) return false;
701 PsiMethodCallExpression methodCallExpression
= (PsiMethodCallExpression
)expression
;
702 if (PsiKeyword
.THIS
.equals(methodCallExpression
.getMethodExpression().getReferenceName())) continue;
703 PsiReferenceExpression referenceExpression
= methodCallExpression
.getMethodExpression();
704 PsiExpression qualifierExpression
= referenceExpression
.getQualifierExpression();
705 if (!(qualifierExpression
instanceof PsiReferenceExpression
)) return false;
706 PsiType type
= qualifierExpression
.getType();
707 if (!(type
instanceof PsiClassType
)) return false;
708 PsiClass resolved
= ((PsiClassType
)type
).resolve();
709 if (resolved
!= baseClass
) return false;
715 static HighlightInfo
checkExternalizableHasPublicNoArgsConstructor(PsiClass aClass
, PsiElement context
) {
716 if (!isExternalizable(aClass
) || aClass
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
719 PsiMethod
[] constructors
= aClass
.getConstructors();
720 boolean hasPublicNoArgsConstructor
= constructors
.length
== 0;
721 for (PsiMethod constructor
: constructors
) {
722 if (constructor
.getParameterList().getParametersCount() == 0 && constructor
.hasModifierProperty(PsiModifier
.PUBLIC
)) {
723 hasPublicNoArgsConstructor
= true;
727 if (!hasPublicNoArgsConstructor
) {
728 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.WARNING
,
730 JavaErrorMessages
.message("externalizable.class.should.have.public.constructor"));
731 QuickFixAction
.registerQuickFixAction(highlightInfo
, QUICK_FIX_FACTORY
.createAddDefaultConstructorFix(aClass
));
732 return highlightInfo
;
737 private static boolean isExternalizable(PsiClass aClass
) {
738 PsiManager manager
= aClass
.getManager();
739 PsiClass externalizableClass
=
740 JavaPsiFacade
.getInstance(manager
.getProject()).findClass("java.io.Externalizable", aClass
.getResolveScope());
741 return externalizableClass
!= null && aClass
.isInheritor(externalizableClass
, true);
744 public static boolean hasEnclosingInstanceInScope(PsiClass aClass
, PsiElement scope
, final boolean isSuperClassAccepted
) {
745 PsiManager manager
= aClass
.getManager();
746 PsiElement place
= scope
;
747 while (place
!= null && place
!= aClass
&& !(place
instanceof PsiFile
)) {
748 if (place
instanceof PsiClass
) {
749 if (isSuperClassAccepted
) {
750 if (InheritanceUtil
.isInheritorOrSelf((PsiClass
)place
, aClass
, true)) return true;
753 if (manager
.areElementsEquivalent(place
, aClass
)) return true;
756 if (place
instanceof PsiModifierListOwner
&& ((PsiModifierListOwner
)place
).hasModifierProperty(PsiModifier
.STATIC
)) {
759 place
= place
.getParent();
761 return place
== aClass
;
764 public static HighlightInfo
checkCreateInnerClassFromStaticContext(PsiNewExpression expression
) {
765 PsiType type
= expression
.getType();
766 if (type
== null || type
instanceof PsiArrayType
|| type
instanceof PsiPrimitiveType
) return null;
767 PsiClass aClass
= PsiUtil
.resolveClassInType(type
);
768 if (aClass
== null) return null;
769 if (aClass
instanceof PsiAnonymousClass
) {
770 aClass
= ((PsiAnonymousClass
)aClass
).getBaseClassType().resolve();
771 if (aClass
== null) return null;
774 if (!PsiUtil
.isInnerClass(aClass
)) return null;
775 PsiClass outerClass
= aClass
.getContainingClass();
776 if (outerClass
== null) return null;
778 PsiElement placeToSearchEnclosingFrom
;
779 PsiExpression qualifier
= expression
.getQualifier();
780 if (qualifier
!= null) {
781 PsiType qtype
= qualifier
.getType();
782 placeToSearchEnclosingFrom
= PsiUtil
.resolveClassInType(qtype
);
785 placeToSearchEnclosingFrom
= expression
;
788 if (outerClass
instanceof JspClass
789 || hasEnclosingInstanceInScope(outerClass
, placeToSearchEnclosingFrom
, true)) return null;
790 return reportIllegalEnclosingUsage(placeToSearchEnclosingFrom
, aClass
, outerClass
, expression
);
793 public static HighlightInfo
checkSuperQualifierType(PsiMethodCallExpression superCall
) {
794 if (!HighlightUtil
.isSuperMethodCall(superCall
)) return null;
795 PsiMethod ctr
= PsiTreeUtil
.getParentOfType(superCall
, PsiMethod
.class, true, PsiMember
.class);
796 if (ctr
== null) return null;
797 PsiClass targetClass
= ctr
.getContainingClass().getSuperClass();
798 if (targetClass
== null) return null;
799 PsiExpression qualifier
= superCall
.getMethodExpression().getQualifierExpression();
800 if (qualifier
!= null && PsiUtil
.isInnerClass(targetClass
)) {
801 PsiClass outerClass
= targetClass
.getContainingClass();
802 PsiClassType outerType
= JavaPsiFacade
.getInstance(superCall
.getProject()).getElementFactory().createType(outerClass
);
803 return HighlightUtil
.checkAssignability(outerType
, null, qualifier
, qualifier
);
808 public static HighlightInfo
reportIllegalEnclosingUsage(PsiElement place
,
809 PsiClass aClass
, PsiClass outerClass
,
810 PsiElement elementToHighlight
) {
811 if (outerClass
!= null && !PsiTreeUtil
.isAncestor(outerClass
, place
, false)) {
812 String description
= JavaErrorMessages
.message("is.not.an.enclosing.class", HighlightUtil
.formatClass(outerClass
));
813 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, elementToHighlight
, description
);
815 PsiModifierListOwner staticParent
= PsiUtil
.getEnclosingStaticElement(place
, outerClass
);
816 if (staticParent
!= null) {
817 String description
= JavaErrorMessages
.message("cannot.be.referenced.from.static.context",
820 : HighlightUtil
.formatClass(outerClass
) + "." + PsiKeyword
.THIS
);
821 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, elementToHighlight
, description
);
822 // make context not static or referenced class static
823 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(staticParent
, PsiModifier
.STATIC
, false, false);
824 QuickFixAction
.registerQuickFixAction(highlightInfo
, fix
);
825 if (aClass
!= null && HighlightUtil
.getIncompatibleModifier(PsiModifier
.STATIC
, aClass
.getModifierList()) == null) {
826 IntentionAction fix2
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.STATIC
, true, false);
827 QuickFixAction
.registerQuickFixAction(highlightInfo
, fix2
);
829 return highlightInfo
;