update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / analysis / HighlightClassUtil.java
blob467ca79f95bb93c2467062c2b346eb6adae6d27e
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 * Checks and Highlights problems with classes
19 * User: cdr
20 * Date: Aug 19, 2002
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();
58 /**
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);
77 return highlightInfo;
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",
88 baseClassName,
89 methodName,
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));
96 return highlightInfo;
98 return null;
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);
114 return errorResult;
118 static HighlightInfo checkClassMustBeAbstract(PsiClass aClass) {
119 if (aClass.hasModifierProperty(PsiModifier.ABSTRACT) || aClass.getRBrace() == null ||
120 aClass.isEnum() && hasEnumConstants(aClass)
122 return null;
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);
144 return errorResult;
147 private static boolean hasEnumConstants(PsiClass aClass) {
148 PsiField[] fields = aClass.getFields();
149 for (PsiField field : fields) {
150 if (field instanceof PsiEnumConstant) return true;
152 return false;
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) {
173 // do not use equals
174 if (dupClass != aClass) {
175 VirtualFile file = dupClass.getContainingFile().getVirtualFile();
176 if (file != null && manager.isInProject(dupClass)) {
177 dupFileName = FileUtil.toSystemDependentName(file.getPath());
178 break;
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();
205 // JLS 14.3:
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;
215 parent = element;
217 if (element instanceof PsiDeclarationStatement) element = PsiTreeUtil.getChildOfType(element, PsiClass.class);
218 if (element instanceof PsiClass && name.equals(((PsiClass)element).getName())) {
219 duplicateFound = true;
220 break;
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);
229 return null;
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) {
243 return 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())) {
262 return errorResult;
265 QuickFixAction.registerQuickFixAction(errorResult, new RenameFileFix(aClass.getName() + "." + StdFileTypes.JAVA.getDefaultExtension()));
266 QuickFixAction.registerQuickFixAction(errorResult, new RenameElementFix(aClass));
268 return errorResult;
272 private static HighlightInfo checkStaticFieldDeclarationInInnerClass(PsiKeyword keyword) {
273 if (getEnclosingStaticClass(keyword, PsiField.class) == null) {
274 return 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)) {
280 return null;
282 HighlightInfo errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
283 keyword,
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);
289 return errorResult;
293 private static HighlightInfo checkStaticMethodDeclarationInInnerClass(PsiKeyword keyword) {
294 if (getEnclosingStaticClass(keyword, PsiMethod.class) == null) {
295 return null;
297 PsiMethod method = (PsiMethod)keyword.getParent().getParent();
298 if (PsiUtilBase.hasErrorElementChild(method)) return null;
299 HighlightInfo errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
300 keyword,
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);
306 return errorResult;
310 private static HighlightInfo checkStaticInitializerDeclarationInInnerClass(PsiKeyword keyword) {
311 if (getEnclosingStaticClass(keyword, PsiClassInitializer.class) == null) {
312 return null;
314 PsiClassInitializer initializer = (PsiClassInitializer)keyword.getParent().getParent();
315 if (PsiUtilBase.hasErrorElementChild(initializer)) return null;
316 HighlightInfo errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
317 keyword,
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);
324 return errorResult;
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}))
335 .getElement();
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) {
348 return 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)) {
358 context = element;
359 break;
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);
373 return errorResult;
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;
386 return null;
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());
395 if (isImplements) {
396 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, list, JavaErrorMessages.message("implements.after.interface"));
400 return null;
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,
416 context,
417 mustBeInterface ? INTERFACE_EXPECTED : CLASS_EXPECTED);
418 PsiClassType type =
419 JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(extendFrom, resolveResult.getSubstitutor());
420 QuickFixAction.registerQuickFixAction(errorResult, new ChangeExtendsToImplementsFix(aClass, type));
422 return errorResult;
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);
434 return errorResult;
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);
461 return null;
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,
479 TextRange textRange,
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);
499 return null;
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));
509 return info;
513 static HighlightInfo checkInterfaceCannotBeLocal(PsiClass aClass) {
514 if (PsiUtil.isLocalClass(aClass)) {
515 TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
516 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
517 textRange,
518 JavaErrorMessages.message("interface.cannot.be.local"));
520 return null;
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);
531 return null;
534 private static PsiClass getCircularClass(PsiClass aClass, Collection<PsiClass> usedClasses) {
535 if (usedClasses.contains(aClass)) {
536 return aClass;
538 try {
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();
552 finally {
553 usedClasses.remove(aClass);
555 return null;
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();
566 int dupCount = 0;
567 for (PsiClassType referencedType : referencedTypes) {
568 PsiClass resolvedElement = referencedType.resolve();
569 if (resolvedElement != null && list.getManager().areElementsEquivalent(resolvedElement, aClass)) {
570 dupCount++;
573 if (dupCount > 1) {
574 String description = JavaErrorMessages.message("duplicate.class", HighlightUtil.formatClass(aClass));
575 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, element, description);
577 return null;
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,
596 elementToHighlight,
597 description);
600 return null;
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,
614 list,
615 JavaErrorMessages.message("class.cannot.extend.multiple.classes"));
618 return null;
622 public static HighlightInfo checkThingNotAllowedInInterface(PsiElement element, PsiClass aClass) {
623 if (aClass == null || !aClass.isInterface()) return null;
624 return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
625 element,
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,
636 expression,
637 JavaErrorMessages.message("invalid.qualified.new"));
638 QuickFixAction.registerQuickFixAction(info, new RemoveNewQualifierFix(expression, null));
639 return info;
641 PsiClass aClass = PsiUtil.resolveClassInType(type);
642 if (aClass != null && aClass.hasModifierProperty(PsiModifier.STATIC)) {
643 HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
644 expression,
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));
651 return info;
653 return null;
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)) {
666 return null;
668 PsiElement grand = parent.getParent();
669 if (!(grand instanceof PsiClass) || ((PsiClass)grand).getExtendsList() != parent) {
670 return null;
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);
686 return null;
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;
711 return true;
715 static HighlightInfo checkExternalizableHasPublicNoArgsConstructor(PsiClass aClass, PsiElement context) {
716 if (!isExternalizable(aClass) || aClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
717 return null;
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;
724 break;
727 if (!hasPublicNoArgsConstructor) {
728 HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.WARNING,
729 context,
730 JavaErrorMessages.message("externalizable.class.should.have.public.constructor"));
731 QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createAddDefaultConstructorFix(aClass));
732 return highlightInfo;
734 return null;
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;
752 else {
753 if (manager.areElementsEquivalent(place, aClass)) return true;
756 if (place instanceof PsiModifierListOwner && ((PsiModifierListOwner)place).hasModifierProperty(PsiModifier.STATIC)) {
757 return false;
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);
784 else {
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);
805 return null;
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",
818 outerClass == null
819 ? ""
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;
831 return null;