good code is red (in presence of raw overriding)
[fedora-idea.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / annotator / GroovyAnnotator.java
blobf04ee3a7935f43be7f60d74232ca8166ddcb03f0
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.
17 package org.jetbrains.plugins.groovy.annotator;
19 import com.intellij.codeInspection.ProblemHighlightType;
20 import com.intellij.lang.ASTNode;
21 import com.intellij.lang.annotation.Annotation;
22 import com.intellij.lang.annotation.AnnotationHolder;
23 import com.intellij.lang.annotation.Annotator;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.util.TextRange;
26 import com.intellij.openapi.util.text.StringUtil;
27 import com.intellij.openapi.vfs.VirtualFile;
28 import com.intellij.psi.*;
29 import com.intellij.psi.search.GlobalSearchScope;
30 import com.intellij.psi.util.ClassUtil;
31 import com.intellij.psi.util.PsiTreeUtil;
32 import gnu.trove.TObjectHashingStrategy;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
35 import org.jetbrains.plugins.groovy.GroovyBundle;
36 import org.jetbrains.plugins.groovy.annotator.intentions.*;
37 import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicMethodFix;
38 import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicPropertyFix;
39 import org.jetbrains.plugins.groovy.codeInspection.GroovyImportsTracker;
40 import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
41 import org.jetbrains.plugins.groovy.highlighter.DefaultHighlighter;
42 import org.jetbrains.plugins.groovy.intentions.utils.DuplicatesUtil;
43 import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.*;
44 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
45 import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
46 import org.jetbrains.plugins.groovy.lang.psi.*;
47 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
48 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
49 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
50 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
51 import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
52 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
53 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
54 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
55 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
56 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
57 import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrBreakStatement;
58 import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrContinueStatement;
59 import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrFlowInterruptingStatement;
60 import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
61 import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrForInClause;
62 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
63 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
64 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
65 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.*;
66 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
67 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
68 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
69 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition;
70 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
71 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameter;
72 import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner;
73 import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
74 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
75 import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
76 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
77 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
78 import org.jetbrains.plugins.groovy.lang.resolve.processors.PropertyResolverProcessor;
79 import org.jetbrains.plugins.groovy.overrideImplement.quickFix.ImplementMethodsQuickFix;
81 import java.util.ArrayList;
82 import java.util.HashSet;
83 import java.util.List;
84 import java.util.Map;
86 /**
87 * @author ven
89 public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
90 private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.annotator.GroovyAnnotator");
92 private AnnotationHolder myHolder;
94 private static boolean isDocCommentElement(PsiElement element) {
95 if (element == null) return false;
96 ASTNode node = element.getNode();
97 return node != null && PsiTreeUtil.getParentOfType(element, GrDocComment.class) != null || element instanceof GrDocComment;
100 public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
101 if (element instanceof GroovyPsiElement) {
102 myHolder = holder;
103 ((GroovyPsiElement)element).accept(this);
104 myHolder = null;
108 @Override
109 public void visitElement(GroovyPsiElement element) {
110 if (element.getParent() instanceof GrDocReferenceElement) {
111 checkGrDocReferenceElement(myHolder, element);
113 else {
114 final ASTNode node = element.getNode();
115 if (!(element instanceof PsiWhiteSpace) &&
116 !GroovyTokenTypes.COMMENT_SET.contains(node.getElementType()) &&
117 element.getContainingFile() instanceof GroovyFile &&
118 !isDocCommentElement(element)) {
119 GroovyImportsTracker.getInstance(element.getProject()).markFileAnnotated((GroovyFile)element.getContainingFile());
124 @Override
125 public void visitCodeReferenceElement(GrCodeReferenceElement refElement) {
126 final PsiElement parent = refElement.getParent();
127 GroovyResolveResult resolveResult = refElement.advancedResolve();
128 highlightAnnotation(myHolder, refElement, resolveResult);
129 registerUsedImport(refElement, resolveResult);
130 highlightAnnotation(myHolder, refElement, resolveResult);
131 if (refElement.getReferenceName() != null) {
133 if (parent instanceof GrImportStatement && ((GrImportStatement)parent).isStatic() && refElement.multiResolve(false).length > 0) {
134 return;
137 checkSingleResolvedElement(myHolder, refElement, resolveResult, true);
141 @Override
142 public void visitReferenceExpression(GrReferenceExpression referenceExpression) {
143 GroovyResolveResult resolveResult = referenceExpression.advancedResolve();
144 GroovyResolveResult[] results = referenceExpression.multiResolve(false); //cached
145 for (GroovyResolveResult result : results) {
146 registerUsedImport(referenceExpression, result);
149 PsiElement resolved = resolveResult.getElement();
150 final PsiElement parent = referenceExpression.getParent();
151 if (resolved != null) {
152 if (resolved instanceof PsiMember) {
153 highlightMemberResolved(myHolder, referenceExpression, ((PsiMember)resolved));
155 if (!resolveResult.isAccessible()) {
156 String message = GroovyBundle.message("cannot.access", referenceExpression.getReferenceName());
157 myHolder.createWarningAnnotation(referenceExpression.getReferenceNameElement(), message);
159 if (!resolveResult.isStaticsOK() && resolved instanceof PsiModifierListOwner) {
160 if (!((PsiModifierListOwner)resolved).hasModifierProperty(GrModifier.STATIC)) {
161 myHolder.createWarningAnnotation(referenceExpression, GroovyBundle.message("cannot.reference.nonstatic", referenceExpression.getReferenceName()));
165 else {
166 GrExpression qualifier = referenceExpression.getQualifierExpression();
167 if (qualifier == null && isDeclarationAssignment(referenceExpression)) return;
169 if (parent instanceof GrReferenceExpression && "class".equals(((GrReferenceExpression)parent).getReferenceName())) {
170 checkSingleResolvedElement(myHolder, referenceExpression, resolveResult, false);
174 if (parent instanceof GrCall) {
175 if (resolved == null && results.length > 0) {
176 resolved = results[0].getElement();
177 resolveResult = results[0];
179 if (resolved instanceof PsiMethod && resolved.getUserData(GrMethod.BUILDER_METHOD) == null) {
180 checkMethodApplicability(resolveResult, referenceExpression, myHolder);
182 else {
183 checkClosureApplicability(resolveResult, referenceExpression.getType(), referenceExpression, myHolder);
186 if (isDeclarationAssignment(referenceExpression) || resolved instanceof PsiPackage) return;
188 if (resolved == null) {
189 PsiElement refNameElement = referenceExpression.getReferenceNameElement();
190 PsiElement elt = refNameElement == null ? referenceExpression : refNameElement;
191 Annotation annotation = myHolder.createInfoAnnotation(elt, null);
192 final GrExpression qualifier = referenceExpression.getQualifierExpression();
193 if (qualifier == null) {
194 if (!(parent instanceof GrCall)) {
195 registerCreateClassByTypeFix(referenceExpression, annotation);
196 registerAddImportFixes(referenceExpression, annotation);
198 else {
199 registerStaticImportFix(referenceExpression, annotation);
202 else {
203 if (qualifier.getType() == null) {
204 return;
207 registerReferenceFixes(referenceExpression, annotation);
208 annotation.setTextAttributes(DefaultHighlighter.UNRESOLVED_ACCESS);
212 private static void registerStaticImportFix(GrReferenceExpression referenceExpression, Annotation annotation) {
213 final String referenceName = referenceExpression.getReferenceName();
214 //noinspection ConstantConditions
215 if (StringUtil.isEmpty(referenceName)) {
216 return;
219 annotation.registerFix(new GroovyStaticImportMethodFix((GrCall)referenceExpression.getParent()));
222 @Override
223 public void visitTypeDefinition(GrTypeDefinition typeDefinition) {
224 checkTypeDefinition(myHolder, typeDefinition);
225 checkTypeDefinitionModifiers(myHolder, typeDefinition);
227 final GrTypeDefinitionBody body = typeDefinition.getBody();
228 if (body != null) checkDuplicateMethod(body.getGroovyMethods(), myHolder);
229 checkImplementedMethodsOfClass(myHolder, typeDefinition);
232 @Override
233 public void visitMethod(GrMethod method) {
234 checkMethodDefinitionModifiers(myHolder, method);
235 checkInnerMethod(myHolder, method);
238 @Override
239 public void visitVariableDeclaration(GrVariableDeclaration variableDeclaration) {
241 PsiElement parent = variableDeclaration.getParent();
242 assert parent != null;
244 PsiElement typeDef = parent.getParent();
245 if (typeDef != null && typeDef instanceof GrTypeDefinition) {
246 PsiModifierList modifiersList = variableDeclaration.getModifierList();
247 checkAccessModifiers(myHolder, modifiersList);
249 if (modifiersList.hasExplicitModifier(GrModifier.VOLATILE) && modifiersList.hasExplicitModifier(GrModifier.FINAL)) {
250 myHolder.createErrorAnnotation(modifiersList, GroovyBundle.message("illegal.combination.of.modifiers.volatile.and.final"));
253 if (modifiersList.hasExplicitModifier(GrModifier.NATIVE)) {
254 myHolder.createErrorAnnotation(modifiersList, GroovyBundle.message("variable.cannot.be.native"));
257 if (modifiersList.hasExplicitModifier(GrModifier.ABSTRACT)) {
258 myHolder.createErrorAnnotation(modifiersList, GroovyBundle.message("variable.cannot.be.abstract"));
263 @Override
264 public void visitVariable(GrVariable variable) {
265 if (variable instanceof GrMember) {
266 highlightMember(myHolder, ((GrMember)variable));
267 checkStaticDeclarationsInInnerClass((GrMember)variable, myHolder);
269 PropertyResolverProcessor processor = new DuplicateVariablesProcessor(variable);
270 final GroovyPsiElement duplicate =
271 ResolveUtil.resolveExistingElement(variable, processor, GrVariable.class, GrReferenceExpression.class);
273 if (duplicate instanceof GrVariable) {
274 if (duplicate instanceof GrField && !(variable instanceof GrField)) {
275 myHolder
276 .createWarningAnnotation(variable.getNameIdentifierGroovy(), GroovyBundle.message("field.already.defined", variable.getName()));
278 else {
279 final String key = duplicate instanceof GrField ? "field.already.defined" : "variable.already.defined";
280 myHolder.createErrorAnnotation(variable.getNameIdentifierGroovy(), GroovyBundle.message(key, variable.getName()));
285 @Override
286 public void visitAssignmentExpression(GrAssignmentExpression expression) {
287 GrExpression lValue = expression.getLValue();
288 if (!PsiUtil.mightBeLVlaue(lValue)) {
289 myHolder.createErrorAnnotation(lValue, GroovyBundle.message("invalid.lvalue"));
293 @Override
294 public void visitReturnStatement(GrReturnStatement returnStatement) {
295 final GrExpression value = returnStatement.getReturnValue();
296 if (value != null) {
297 final PsiType type = value.getType();
298 if (type != null) {
299 final GrParametersOwner owner = PsiTreeUtil.getParentOfType(returnStatement, GrMethod.class, GrClosableBlock.class);
300 if (owner instanceof PsiMethod) {
301 final PsiMethod method = (PsiMethod)owner;
302 if (method.isConstructor()) {
303 myHolder.createErrorAnnotation(value, GroovyBundle.message("cannot.return.from.constructor"));
305 else {
306 final PsiType methodType = method.getReturnType();
307 if (methodType != null) {
308 if (PsiType.VOID.equals(methodType)) {
309 myHolder.createErrorAnnotation(value, GroovyBundle.message("cannot.return.from.void.method"));
318 @Override
319 public void visitListOrMap(GrListOrMap listOrMap) {
320 final Map<GrNamedArgument, List<GrNamedArgument>> map = DuplicatesUtil.factorDuplicates(listOrMap.getNamedArguments(), new TObjectHashingStrategy<GrNamedArgument>() {
321 public int computeHashCode(GrNamedArgument arg) {
322 final GrArgumentLabel label = arg.getLabel();
323 if (label == null) return 0;
324 final String name = label.getName();
325 if (name == null) return 0;
326 return name.hashCode();
329 public boolean equals(GrNamedArgument arg1, GrNamedArgument arg2) {
330 final GrArgumentLabel label1 = arg1.getLabel();
331 final GrArgumentLabel label2 = arg2.getLabel();
332 if (label1 == null || label2 == null) {
333 return label1 == null && label2 == null;
335 final String name1 = label1.getName();
336 final String name2 = label2.getName();
337 if (name1 == null || name2 == null) {
338 return name1 == null && name2 == null;
340 return name1.equals(name2);
344 processDuplicates(map, myHolder);
347 @Override
348 public void visitNewExpression(GrNewExpression newExpression) {
349 if (newExpression.getArrayCount() > 0) return;
350 GrCodeReferenceElement refElement = newExpression.getReferenceElement();
351 if (refElement == null) return;
352 final PsiElement element = refElement.resolve();
353 if (element instanceof PsiClass) {
354 PsiClass clazz = (PsiClass)element;
355 if (clazz.hasModifierProperty(GrModifier.ABSTRACT)) {
356 if (newExpression.getAnonymousClassDefinition() == null) {
357 String message = clazz.isInterface()
358 ? GroovyBundle.message("cannot.instantiate.interface", clazz.getName())
359 : GroovyBundle.message("cannot.instantiate.abstract.class", clazz.getName());
360 myHolder.createErrorAnnotation(refElement, message);
362 return;
364 if (newExpression.getQualifier() != null) {
365 if (clazz.hasModifierProperty(GrModifier.STATIC)) {
366 myHolder.createErrorAnnotation(newExpression, GroovyBundle.message("qualified.new.of.static.class"));
368 } else {
369 final PsiClass outerClass = clazz.getContainingClass();
370 if (com.intellij.psi.util.PsiUtil.isInnerClass(clazz) && !PsiUtil.hasEnclosingInstanceInScope(outerClass, newExpression, true)) {
371 myHolder.createErrorAnnotation(newExpression, GroovyBundle.message("cannot.reference.nonstatic", clazz.getQualifiedName()));
376 final GroovyResolveResult constructorResolveResult = newExpression.resolveConstructorGenerics();
377 if (constructorResolveResult.getElement() != null) {
378 checkMethodApplicability(constructorResolveResult, refElement, myHolder);
379 final GrArgumentList argList = newExpression.getArgumentList();
380 if (argList != null && argList.getExpressionArguments().length == 0) checkDefaultMapConstructor(myHolder, argList, constructorResolveResult);
382 else {
383 final GroovyResolveResult[] results = newExpression.multiResolveConstructor();
384 final GrArgumentList argList = newExpression.getArgumentList();
385 PsiElement toHighlight = argList != null ? argList : refElement.getReferenceNameElement();
387 if (results.length > 0) {
388 String message = GroovyBundle.message("ambiguous.constructor.call");
389 myHolder.createWarningAnnotation(toHighlight, message);
391 else {
392 if (element instanceof PsiClass) {
393 //default constructor invocation
394 PsiType[] argumentTypes = PsiUtil.getArgumentTypes(refElement, true, true);
395 if (argumentTypes != null && argumentTypes.length > 0) {
396 String message = GroovyBundle.message("cannot.find.default.constructor", ((PsiClass)element).getName());
397 myHolder.createWarningAnnotation(toHighlight, message);
399 else checkDefaultMapConstructor(myHolder, argList, constructorResolveResult);
405 @Override
406 public void visitDocMethodReference(GrDocMethodReference reference) {
407 checkGrDocMemberReference(reference, myHolder);
410 @Override
411 public void visitDocFieldReference(GrDocFieldReference reference) {
412 checkGrDocMemberReference(reference, myHolder);
415 @Override
416 public void visitConstructorInvocation(GrConstructorInvocation invocation) {
417 final GroovyResolveResult resolveResult = invocation.resolveConstructorGenerics();
418 if (resolveResult != null) {
419 checkMethodApplicability(resolveResult, invocation.getThisOrSuperKeyword(), myHolder);
421 else {
422 final GroovyResolveResult[] results = invocation.multiResolveConstructor();
423 final GrArgumentList argList = invocation.getArgumentList();
424 if (results.length > 0) {
425 String message = GroovyBundle.message("ambiguous.constructor.call");
426 myHolder.createWarningAnnotation(argList, message);
428 else {
429 final PsiClass clazz = invocation.getDelegatedClass();
430 if (clazz != null) {
431 //default constructor invocation
432 PsiType[] argumentTypes = PsiUtil.getArgumentTypes(invocation.getThisOrSuperKeyword(), true, true);
433 if (argumentTypes != null && argumentTypes.length > 0) {
434 String message = GroovyBundle.message("cannot.find.default.constructor", clazz.getName());
435 myHolder.createWarningAnnotation(argList, message);
442 @Override
443 public void visitBreakStatement(GrBreakStatement breakStatement) {
444 checkFlowInterruptStatement(breakStatement, myHolder);
447 @Override
448 public void visitContinueStatement(GrContinueStatement continueStatement) {
449 checkFlowInterruptStatement(continueStatement, myHolder);
452 @Override
453 public void visitLabeledStatement(GrLabeledStatement labeledStatement) {
454 final String name = labeledStatement.getLabelName();
455 if (ResolveUtil.resolveLabeledStatement(name, labeledStatement, true) != null) {
456 myHolder.createWarningAnnotation(labeledStatement.getLabel(), GroovyBundle.message("label.already.used", name));
460 @Override
461 public void visitPackageDefinition(GrPackageDefinition packageDefinition) {
462 //todo: if reference isn't resolved it construct package definition
463 final PsiFile file = packageDefinition.getContainingFile();
464 assert file != null;
466 PsiDirectory psiDirectory = file.getContainingDirectory();
467 if (psiDirectory != null) {
468 PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory);
469 if (aPackage != null) {
470 String packageName = aPackage.getQualifiedName();
471 if (!packageDefinition.getPackageName().equals(packageName)) {
472 final Annotation annotation = myHolder.createWarningAnnotation(packageDefinition, "wrong package name");
473 annotation.registerFix(new ChangePackageQuickFix((GroovyFile)packageDefinition.getContainingFile(), packageName));
477 final GrModifierList modifierList = packageDefinition.getAnnotationList();
478 checkAnnotationList(myHolder, modifierList, GroovyBundle.message("package.definition.cannot.have.modifiers"));
481 @Override
482 public void visitSuperExpression(GrSuperReferenceExpression superExpression) {
483 checkThisOrSuperReferenceExpression(superExpression, myHolder);
486 @Override
487 public void visitThisExpression(GrThisReferenceExpression thisExpression) {
488 checkThisOrSuperReferenceExpression(thisExpression, myHolder);
491 @Override
492 public void visitLiteralExpression(GrLiteral literal) {
493 String text = literal.getText();
494 if (text.startsWith("'''")) {
495 if (text.length() < 6 || !text.endsWith("'''")) {
496 myHolder.createErrorAnnotation(literal, GroovyBundle.message("string.end.expected"));
499 else if (text.startsWith("'")) {
500 if (text.length() < 2 || !text.endsWith("'")) {
501 myHolder.createErrorAnnotation(literal, GroovyBundle.message("string.end.expected"));
506 @Override
507 public void visitForInClause(GrForInClause forInClause) {
508 final GrVariable[] declaredVariables = forInClause.getDeclaredVariables();
509 if (declaredVariables.length < 1) return;
510 final GrVariable variable = declaredVariables[0];
511 final GrModifierList modifierList = ((GrModifierList)variable.getModifierList());
512 if (modifierList == null) return;
513 final PsiElement[] modifiers = modifierList.getModifiers();
514 for (PsiElement modifier : modifiers) {
515 if (modifier instanceof PsiAnnotation) continue;
516 final String modifierText = modifier.getText();
517 if (GrModifier.FINAL.equals(modifierText)) continue;
518 if (GrModifier.DEF.equals(modifierText)) continue;
519 myHolder.createErrorAnnotation(modifier, GroovyBundle.message("not.allowed.modifier.in.forin", modifierText));
523 @Override
524 public void visitFile(GroovyFileBase file) {
525 if (!file.isScript()) return;
527 List<GrMethod> methods = new ArrayList<GrMethod>();
529 for (GrTopLevelDefintion topLevelDefinition : file.getTopLevelDefinitions()) {
530 if (topLevelDefinition instanceof GrMethod) {
531 methods.add(((GrMethod)topLevelDefinition));
535 checkDuplicateMethod(methods.toArray(new GrMethod[methods.size()]), myHolder);
538 @Override
539 public void visitImportStatement(GrImportStatement importStatement) {
540 checkAnnotationList(myHolder, importStatement.getAnnotationList(), GroovyBundle.message("import.statement.cannot.have.modifiers"));
543 private static void checkFlowInterruptStatement(GrFlowInterruptingStatement statement, AnnotationHolder holder) {
544 final PsiElement label = statement.getLabelIdentifier();
546 if (label != null) {
547 final GrLabeledStatement resolved = statement.resolveLabel();
548 if (resolved == null) {
549 holder.createErrorAnnotation(label, GroovyBundle.message("undefined.label", statement.getLabelName()));
553 final GrStatement targetStatement = statement.findTargetStatement();
554 if (targetStatement == null) {
555 if (statement instanceof GrContinueStatement && label == null) {
556 holder.createErrorAnnotation(statement, GroovyBundle.message("continue.outside.loop"));
558 else if (statement instanceof GrBreakStatement && label == null) {
559 holder.createErrorAnnotation(statement, GroovyBundle.message("break.outside.loop.or.switch"));
562 if (statement instanceof GrBreakStatement && label != null && findFirstLoop(statement) == null) {
563 holder.createErrorAnnotation(statement, GroovyBundle.message("break.outside.loop"));
567 @Nullable
568 private static GrLoopStatement findFirstLoop(GrFlowInterruptingStatement statement) {
569 return PsiTreeUtil.getParentOfType(statement, GrLoopStatement.class, true, GrClosableBlock.class, GrMember.class, GroovyFile.class);
572 private static void checkThisOrSuperReferenceExpression(GrExpression expression, AnnotationHolder holder) {
573 final GrReferenceExpression qualifier = expression instanceof GrThisReferenceExpression
574 ? ((GrThisReferenceExpression)expression).getQualifier()
575 : ((GrSuperReferenceExpression)expression).getQualifier();
576 if (qualifier == null) {
577 final GrMethod method = PsiTreeUtil.getParentOfType(expression, GrMethod.class);
578 if (method != null && method.hasModifierProperty(GrModifier.STATIC)) {
579 holder.createErrorAnnotation(expression, GroovyBundle.message("cannot.reference.nonstatic", expression.getText()));
582 else {
583 final PsiElement resolved = qualifier.resolve();
584 if (resolved instanceof PsiClass) {
585 if (PsiTreeUtil.isAncestor(resolved, expression, true)) {
586 if (!PsiUtil.hasEnclosingInstanceInScope((PsiClass)resolved, expression, true)) {
587 holder.createErrorAnnotation(expression, GroovyBundle.message("cannot.reference.nonstatic", expression.getText()));
589 } else {
590 holder.createErrorAnnotation(expression, GroovyBundle.message("is.not.enclosing.class", ((PsiClass)resolved).getQualifiedName()));
593 else {
594 holder.createErrorAnnotation(qualifier, GroovyBundle.message("unknown.class", qualifier.getText()));
599 private static void checkStaticDeclarationsInInnerClass(GrMember classMember, AnnotationHolder holder) {
600 final PsiClass containingClass = classMember.getContainingClass();
601 if (containingClass == null) return;
602 if (com.intellij.psi.util.PsiUtil.isInnerClass(containingClass)) {
603 if (classMember.hasModifierProperty(GrModifier.STATIC)) {
604 final PsiElement modifier = findModifierStatic(classMember);
605 if (modifier != null) {
606 holder.createErrorAnnotation(modifier, GroovyBundle.message("cannot.have.static.declarations"));
612 @Nullable
613 private static PsiElement findModifierStatic(GrMember grMember) {
614 final GrModifierList list = grMember.getModifierList();
615 if (list == null) {
616 return null;
619 for (PsiElement modifier : list.getModifiers()) {
620 if (GrModifier.STATIC.equals(modifier.getText())) {
621 return modifier;
624 return null;
627 private static void checkGrDocReferenceElement(AnnotationHolder holder, PsiElement element) {
628 ASTNode node = element.getNode();
629 if (node != null && TokenSets.BUILT_IN_TYPE.contains(node.getElementType())) {
630 Annotation annotation = holder.createInfoAnnotation(element, null);
631 annotation.setTextAttributes(DefaultHighlighter.KEYWORD);
635 private static void checkAnnotationList(AnnotationHolder holder, @Nullable GrModifierList modifierList, String message) {
636 if (modifierList == null) return;
637 final PsiElement[] modifiers = modifierList.getModifiers();
638 for (PsiElement modifier : modifiers) {
639 if (!(modifier instanceof PsiAnnotation)) {
640 holder.createErrorAnnotation(modifier, message);
645 private static void checkImplementedMethodsOfClass(AnnotationHolder holder, GrTypeDefinition typeDefinition) {
646 if (typeDefinition.hasModifierProperty(GrModifier.ABSTRACT)) return;
647 if (typeDefinition.isEnum() || typeDefinition.isAnnotationType()) return;
648 if (typeDefinition instanceof GrTypeParameter) return;
650 PsiMethod abstractMethod = ClassUtil.getAnyAbstractMethod(typeDefinition);
652 if (abstractMethod == null) return;
654 String notImplementedMethodName = abstractMethod.getName();
656 final int startOffset = typeDefinition.getTextOffset();
657 int endOffset = typeDefinition.getNameIdentifierGroovy().getTextRange().getEndOffset();
658 final Annotation annotation = holder.createErrorAnnotation(new TextRange(startOffset, endOffset),
659 GroovyBundle.message("method.is.not.implemented", notImplementedMethodName));
660 registerImplementsMethodsFix(typeDefinition, annotation);
663 private static void registerImplementsMethodsFix(GrTypeDefinition typeDefinition, Annotation annotation) {
664 annotation.registerFix(new ImplementMethodsQuickFix(typeDefinition));
667 private static void checkInnerMethod(AnnotationHolder holder, GrMethod grMethod) {
668 final PsiElement parent = grMethod.getParent();
669 if (parent instanceof GrOpenBlock || parent instanceof GrClosableBlock) {
670 holder.createErrorAnnotation(grMethod.getNameIdentifierGroovy(), GroovyBundle.message("Inner.methods.are.not.supported"));
674 protected static void processDuplicates(Map<GrNamedArgument, List<GrNamedArgument>> map, AnnotationHolder holder) {
675 for (List<GrNamedArgument> args : map.values()) {
676 for (int i = 1; i < args.size(); i++) {
677 GrNamedArgument namedArgument = args.get(i);
678 holder.createWarningAnnotation(namedArgument, GroovyBundle.message("duplicate.element.in.the.map"));
683 private static void checkMethodDefinitionModifiers(AnnotationHolder holder, GrMethod method) {
684 final PsiModifierList modifiersList = method.getModifierList();
685 checkAccessModifiers(holder, modifiersList);
687 //script methods
688 boolean isMethodAbstract = modifiersList.hasExplicitModifier(GrModifier.ABSTRACT);
689 final boolean isMethodStatic = modifiersList.hasExplicitModifier(GrModifier.STATIC);
690 if (method.getParent() instanceof GroovyFileBase) {
691 if (isMethodAbstract) {
692 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("script.cannot.have.modifier.abstract"));
695 if (modifiersList.hasExplicitModifier(GrModifier.NATIVE)) {
696 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("script.cannot.have.modifier.native"));
699 else //type definition methods
700 if (method.getParent() != null && method.getParent().getParent() instanceof GrTypeDefinition) {
701 GrTypeDefinition containingTypeDef = ((GrTypeDefinition)method.getParent().getParent());
703 //interface
704 if (containingTypeDef.isInterface()) {
705 if (isMethodStatic) {
706 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("interface.must.have.no.static.method"));
709 if (modifiersList.hasExplicitModifier(GrModifier.PRIVATE)) {
710 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("interface.must.have.no.private.method"));
714 else if (containingTypeDef.isEnum()) {
715 //enumeration
716 //todo
718 else if (containingTypeDef.isAnnotationType()) {
719 //annotation
720 //todo
722 else if (containingTypeDef.isAnonymous()) {
723 //anonymous class
724 if (isMethodStatic) {
725 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("static.declaration.in.inner.class"));
727 if (method.isConstructor()) {
728 holder.createErrorAnnotation(method.getNameIdentifierGroovy(),
729 GroovyBundle.message("constructors.are.not.allowed.in.anonymous.class"));
731 if (isMethodAbstract) {
732 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("not.abstract.class.cannot.have.abstract.method"));
735 else {
736 //class
737 PsiModifierList typeDefModifiersList = containingTypeDef.getModifierList();
738 LOG.assertTrue(typeDefModifiersList != null, "modifiers list must be not null");
740 if (!typeDefModifiersList.hasExplicitModifier(GrModifier.ABSTRACT)) {
741 if (isMethodAbstract) {
742 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("not.abstract.class.cannot.have.abstract.method"));
746 if (!isMethodAbstract) {
747 if (method.getBlock() == null) {
748 holder.createErrorAnnotation(method.getNameIdentifierGroovy(), GroovyBundle.message("not.abstract.method.should.have.body"));
751 if (isMethodStatic) {
752 checkStaticDeclarationsInInnerClass(method, holder);
758 private static void checkTypeDefinitionModifiers(AnnotationHolder holder, GrTypeDefinition typeDefinition) {
759 PsiModifierList modifiersList = typeDefinition.getModifierList();
761 if (modifiersList == null) return;
763 /**** class ****/
764 checkAccessModifiers(holder, modifiersList);
766 PsiClassType[] extendsListTypes = typeDefinition.getExtendsListTypes();
768 for (PsiClassType classType : extendsListTypes) {
769 PsiClass psiClass = classType.resolve();
771 if (psiClass != null) {
772 PsiModifierList modifierList = psiClass.getModifierList();
773 if (modifierList != null) {
774 if (modifierList.hasExplicitModifier(GrModifier.FINAL)) {
775 holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(), GroovyBundle.message("final.class.cannot.be.extended"));
781 if (modifiersList.hasExplicitModifier(GrModifier.ABSTRACT) && modifiersList.hasExplicitModifier(GrModifier.FINAL)) {
782 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("illegal.combination.of.modifiers.abstract.and.final"));
785 if (modifiersList.hasExplicitModifier(GrModifier.TRANSIENT)) {
786 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.transient.not.allowed.here"));
788 if (modifiersList.hasExplicitModifier(GrModifier.VOLATILE)) {
789 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.volatile.not.allowed.here"));
792 /**** interface ****/
793 if (typeDefinition.isInterface()) {
794 if (modifiersList.hasExplicitModifier(GrModifier.FINAL)) {
795 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("intarface.cannot.have.modifier.final"));
798 if (modifiersList.hasExplicitModifier(GrModifier.VOLATILE)) {
799 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.volatile.not.allowed.here"));
802 if (modifiersList.hasExplicitModifier(GrModifier.TRANSIENT)) {
803 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.transient.not.allowed.here"));
807 checkStaticDeclarationsInInnerClass(typeDefinition, holder);
810 private static void checkAccessModifiers(AnnotationHolder holder, @NotNull PsiModifierList modifierList) {
811 boolean hasPrivate = modifierList.hasExplicitModifier(GrModifier.PRIVATE);
812 boolean hasPublic = modifierList.hasExplicitModifier(GrModifier.PUBLIC);
813 boolean hasProtected = modifierList.hasExplicitModifier(GrModifier.PROTECTED);
815 if (hasPrivate && hasPublic || hasPrivate && hasProtected || hasPublic && hasProtected) {
816 holder.createErrorAnnotation(modifierList, GroovyBundle.message("illegal.combination.of.modifiers"));
820 private static void checkDuplicateMethod(GrMethod[] methods, AnnotationHolder holder) {
821 final Map<GrMethod, List<GrMethod>> map = DuplicatesUtil.factorDuplicates(methods, new TObjectHashingStrategy<GrMethod>() {
822 public int computeHashCode(GrMethod method) {
823 return method.getSignature(PsiSubstitutor.EMPTY).hashCode();
826 public boolean equals(GrMethod method1, GrMethod method2) {
827 return method1.getSignature(PsiSubstitutor.EMPTY).equals(method2.getSignature(PsiSubstitutor.EMPTY));
830 processMethodDuplicates(map, holder);
833 protected static void processMethodDuplicates(Map<GrMethod, List<GrMethod>> map, AnnotationHolder holder) {
834 HashSet<GrMethod> duplicateMethodsWarning = new HashSet<GrMethod>();
835 HashSet<GrMethod> duplicateMethodsErrors = new HashSet<GrMethod>();
837 DuplicatesUtil.collectMethodDuplicates(map, duplicateMethodsWarning, duplicateMethodsErrors);
839 for (GrMethod duplicateMethod : duplicateMethodsErrors) {
840 holder.createErrorAnnotation(duplicateMethod.getNameIdentifierGroovy(),
841 GroovyBundle.message("repetitive.method.name.signature.and.return.type"));
844 for (GrMethod duplicateMethod : duplicateMethodsWarning) {
845 holder.createWarningAnnotation(duplicateMethod.getNameIdentifierGroovy(), GroovyBundle.message("repetitive.method.name.signature"));
850 private static void checkTypeDefinition(AnnotationHolder holder, GrTypeDefinition typeDefinition) {
851 final GroovyConfigUtils configUtils = GroovyConfigUtils.getInstance();
852 if (typeDefinition.isAnnotationType()) {
853 Annotation annotation = holder.createInfoAnnotation(typeDefinition.getNameIdentifierGroovy(), null);
854 annotation.setTextAttributes(DefaultHighlighter.ANNOTATION);
856 else if (typeDefinition.isAnonymous()) {
857 if (!configUtils.isAtLeastGroovy1_7(typeDefinition)) {
858 holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
859 GroovyBundle.message("anonymous.classes.are.not.supported", configUtils.getSDKVersion(typeDefinition)));
862 else if (typeDefinition.getContainingClass() != null && !(typeDefinition instanceof GrEnumTypeDefinition)) {
863 if (!configUtils.isAtLeastGroovy1_7(typeDefinition)) {
864 holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
865 GroovyBundle.message("inner.classes.are.not.supported", configUtils.getSDKVersion(typeDefinition)));
869 final GrImplementsClause implementsClause = typeDefinition.getImplementsClause();
870 final GrExtendsClause extendsClause = typeDefinition.getExtendsClause();
872 if (implementsClause != null) {
873 checkForImplementingClass(holder, extendsClause, implementsClause, ((GrTypeDefinition)implementsClause.getParent()));
876 if (extendsClause != null) {
877 checkForExtendingInterface(holder, extendsClause, implementsClause, ((GrTypeDefinition)extendsClause.getParent()));
880 checkDuplicateClass(typeDefinition, holder);
883 private static void checkDuplicateClass(GrTypeDefinition typeDefinition, AnnotationHolder holder) {
884 final PsiClass containingClass = typeDefinition.getContainingClass();
885 if (containingClass != null) {
886 final String containingClassName = containingClass.getName();
887 if (containingClassName != null && containingClassName.equals(typeDefinition.getName())) {
888 holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
889 GroovyBundle.message("duplicate.inner.class", typeDefinition.getName()));
892 final String qName = typeDefinition.getQualifiedName();
893 if (qName != null) {
894 final PsiClass[] classes =
895 JavaPsiFacade.getInstance(typeDefinition.getProject()).findClasses(qName, typeDefinition.getResolveScope());
896 if (classes.length > 1) {
897 String packageName = getPackageName(typeDefinition);
899 if (!isScriptGeneratedClass(classes)) {
900 holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
901 GroovyBundle.message("duplicate.class", typeDefinition.getName(), packageName));
903 else {
904 holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
905 GroovyBundle.message("script.generated.with.same.name", qName));
911 private static String getPackageName(GrTypeDefinition typeDefinition) {
912 final PsiFile file = typeDefinition.getContainingFile();
913 String packageName = "<default package>";
914 if (file instanceof GroovyFile) {
915 final String name = ((GroovyFile)file).getPackageName();
916 if (name.length() > 0) packageName = name;
918 return packageName;
921 private static boolean isScriptGeneratedClass(PsiClass[] allClasses) {
922 return allClasses.length == 2 && (allClasses[0] instanceof GroovyScriptClass || allClasses[1] instanceof GroovyScriptClass);
925 private static void checkForExtendingInterface(AnnotationHolder holder,
926 GrExtendsClause extendsClause,
927 GrImplementsClause implementsClause,
928 GrTypeDefinition myClass) {
929 for (GrCodeReferenceElement ref : extendsClause.getReferenceElements()) {
930 final PsiElement clazz = ref.resolve();
931 if (clazz == null) continue;
933 if (myClass.isInterface() && clazz instanceof PsiClass && !((PsiClass)clazz).isInterface()) {
934 final Annotation annotation = holder.createErrorAnnotation(ref, GroovyBundle.message("class.is.not.expected.here"));
935 annotation.registerFix(new ChangeExtendsImplementsQuickFix(extendsClause, implementsClause));
940 private static void checkForImplementingClass(AnnotationHolder holder,
941 GrExtendsClause extendsClause,
942 GrImplementsClause implementsClause,
943 GrTypeDefinition myClass) {
944 if (myClass.isInterface()) {
945 final Annotation annotation =
946 holder.createErrorAnnotation(implementsClause, GroovyBundle.message("interface.cannot.contain.implements.clause"));
947 annotation.registerFix(new ChangeExtendsImplementsQuickFix(extendsClause, implementsClause));
948 return;
951 for (GrCodeReferenceElement ref : implementsClause.getReferenceElements()) {
952 final PsiElement clazz = ref.resolve();
953 if (clazz == null) continue;
955 if (!((PsiClass)clazz).isInterface()) {
956 final Annotation annotation = holder.createErrorAnnotation(ref, GroovyBundle.message("interface.expected.here"));
957 annotation.registerFix(new ChangeExtendsImplementsQuickFix(extendsClause, implementsClause));
962 private static void checkGrDocMemberReference(final GrDocMemberReference reference, AnnotationHolder holder) {
963 PsiElement resolved = reference.resolve();
964 if (resolved == null) {
965 Annotation annotation = holder.createErrorAnnotation(reference, GroovyBundle.message("cannot.resolve", reference.getReferenceName()));
966 annotation.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
970 private static void registerReferenceFixes(GrReferenceExpression refExpr, Annotation annotation) {
971 PsiClass targetClass = QuickfixUtil.findTargetClass(refExpr);
972 if (targetClass == null) return;
974 addDynamicAnnotation(annotation, refExpr);
975 if (targetClass instanceof GrMemberOwner) {
976 if (!(targetClass instanceof GroovyScriptClass)) {
977 annotation.registerFix(new CreateFieldFromUsageFix(refExpr, (GrMemberOwner)targetClass));
980 if (refExpr.getParent() instanceof GrCall && refExpr.getParent() instanceof GrExpression) {
981 annotation.registerFix(new CreateMethodFromUsageFix(refExpr, (GrMemberOwner)targetClass));
985 if (!refExpr.isQualified()) {
986 GrVariableDeclarationOwner owner = PsiTreeUtil.getParentOfType(refExpr, GrVariableDeclarationOwner.class);
987 if (!(owner instanceof GroovyFileBase) || ((GroovyFileBase)owner).isScript()) {
988 annotation.registerFix(new CreateLocalVariableFromUsageFix(refExpr, owner));
993 private static void addDynamicAnnotation(Annotation annotation, GrReferenceExpression referenceExpression) {
994 final PsiFile containingFile = referenceExpression.getContainingFile();
995 VirtualFile file;
996 if (containingFile != null) {
997 file = containingFile.getVirtualFile();
998 if (file == null) return;
1000 else {
1001 return;
1004 if (QuickfixUtil.isCall(referenceExpression)) {
1005 annotation.registerFix(new DynamicMethodFix(referenceExpression), referenceExpression.getTextRange());
1007 else {
1008 annotation.registerFix(new DynamicPropertyFix(referenceExpression), referenceExpression.getTextRange());
1012 private static void highlightMemberResolved(AnnotationHolder holder, GrReferenceExpression refExpr, PsiMember member) {
1013 boolean isStatic = member.hasModifierProperty(GrModifier.STATIC);
1014 Annotation annotation = holder.createInfoAnnotation(refExpr.getReferenceNameElement(), null);
1016 if (member instanceof PsiField ) {
1017 annotation.setTextAttributes(isStatic ? DefaultHighlighter.STATIC_FIELD : DefaultHighlighter.INSTANCE_FIELD);
1018 return;
1020 if (member instanceof PsiMethod) {
1021 annotation.setTextAttributes(!isStatic ? DefaultHighlighter.METHOD_CALL : DefaultHighlighter.STATIC_METHOD_ACCESS);
1026 private static void registerUsedImport(GrReferenceElement referenceElement, GroovyResolveResult resolveResult) {
1027 GroovyPsiElement context = resolveResult.getCurrentFileResolveContext();
1028 if (context instanceof GrImportStatement) {
1029 PsiFile file = referenceElement.getContainingFile();
1030 if (file instanceof GroovyFile) {
1031 GroovyImportsTracker importsTracker = GroovyImportsTracker.getInstance(referenceElement.getProject());
1032 importsTracker.registerImportUsed((GrImportStatement)context);
1037 private static void checkMethodApplicability(GroovyResolveResult methodResolveResult, PsiElement place, AnnotationHolder holder) {
1038 final PsiElement element = methodResolveResult.getElement();
1039 if (!(element instanceof PsiMethod)) return;
1040 final PsiMethod method = (PsiMethod)element;
1041 PsiType[] argumentTypes = PsiUtil.getArgumentTypes(place, method.isConstructor(), true);
1042 if (argumentTypes != null &&
1043 !PsiUtil.isApplicable(argumentTypes, method, methodResolveResult.getSubstitutor(),
1044 methodResolveResult.getCurrentFileResolveContext() instanceof GrMethodCallExpression)) {
1045 PsiElement elementToHighlight = PsiUtil.getArgumentsElement(place);
1046 if (elementToHighlight == null) {
1047 elementToHighlight = place;
1050 final String typesString = buildArgTypesList(argumentTypes);
1051 String message;
1052 final PsiClass containingClass = method.getContainingClass();
1053 if (containingClass != null) {
1054 final PsiClassType containingType = JavaPsiFacade.getInstance(method.getProject()).getElementFactory()
1055 .createType(containingClass, methodResolveResult.getSubstitutor());
1056 message = GroovyBundle.message("cannot.apply.method1", method.getName(), containingType.getInternalCanonicalText(), typesString);
1058 else {
1059 message = GroovyBundle.message("cannot.apply.method.or.closure", method.getName(), typesString);
1061 holder.createWarningAnnotation(elementToHighlight, message);
1065 public static boolean isDeclarationAssignment(GrReferenceExpression refExpr) {
1066 if (isAssignmentLhs(refExpr)) {
1067 return isExpandoQualified(refExpr);
1069 return false;
1072 private static boolean isAssignmentLhs(GrReferenceExpression refExpr) {
1073 return refExpr.getParent() instanceof GrAssignmentExpression &&
1074 refExpr.equals(((GrAssignmentExpression)refExpr.getParent()).getLValue());
1077 private static boolean isExpandoQualified(GrReferenceExpression refExpr) {
1078 final GrExpression qualifier = refExpr.getQualifierExpression();
1079 if (qualifier == null) {
1080 final PsiClass clazz = PsiTreeUtil.getParentOfType(refExpr, PsiClass.class);
1081 if (clazz == null) { //script
1082 return true;
1084 return false; //in class, a property should normally be defined, so it's not a declaration
1087 final PsiType type = qualifier.getType();
1088 if (type instanceof PsiClassType) {
1089 final PsiClassType classType = (PsiClassType)type;
1090 final PsiClass psiClass = classType.resolve();
1091 if (psiClass instanceof GroovyScriptClass) {
1092 return true;
1095 return false;
1098 private static void checkSingleResolvedElement(AnnotationHolder holder, GrReferenceElement refElement, GroovyResolveResult resolveResult, boolean highlightError) {
1099 final PsiElement resolved = resolveResult.getElement();
1100 if (resolved == null) {
1101 String message = GroovyBundle.message("cannot.resolve", refElement.getReferenceName());
1103 // Register quickfix
1104 final PsiElement nameElement = refElement.getReferenceNameElement();
1105 final PsiElement toHighlight = nameElement != null ? nameElement : refElement;
1107 final Annotation annotation;
1108 if (highlightError) {
1109 annotation = holder.createErrorAnnotation(toHighlight, message);
1110 annotation.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
1112 else {
1113 annotation = holder.createInfoAnnotation(toHighlight, message);
1115 // todo implement for nested classes
1116 if (refElement.getQualifier() == null) {
1117 registerCreateClassByTypeFix(refElement, annotation);
1118 registerAddImportFixes(refElement, annotation);
1121 else if (!resolveResult.isAccessible()) {
1122 String message = GroovyBundle.message("cannot.access", refElement.getReferenceName());
1123 holder.createWarningAnnotation(refElement.getReferenceNameElement(), message);
1127 private static void checkDefaultMapConstructor(AnnotationHolder holder,
1128 GrArgumentList argList,
1129 GroovyResolveResult constructorResolveResult) {
1130 if (argList != null) {
1131 final GrNamedArgument[] args = argList.getNamedArguments();
1132 for (GrNamedArgument arg : args) {
1133 final GrArgumentLabel label = arg.getLabel();
1134 if (label == null) continue;
1135 if (label.getName() == null) {
1136 final PsiElement nameElement = label.getNameElement();
1137 if (nameElement instanceof GrExpression) {
1138 final PsiType stringType =
1139 JavaPsiFacade.getElementFactory(arg.getProject()).createTypeFromText(CommonClassNames.JAVA_LANG_STRING, arg);
1140 if (!TypesUtil.isAssignable(stringType, ((GrExpression)nameElement).getType(), arg.getManager(), arg.getResolveScope())) {
1141 holder.createWarningAnnotation(nameElement, GroovyBundle.message("property.name.expected"));
1144 else {
1145 holder.createWarningAnnotation(nameElement, GroovyBundle.message("property.name.expected"));
1148 else {
1149 final PsiElement resolved = label.resolve();
1150 if (resolved == null) {
1151 final Annotation annotation = holder.createWarningAnnotation(label, GroovyBundle.message("no.such.property", label.getName()));
1153 PsiElement element = constructorResolveResult.getElement();
1154 if (element instanceof PsiMember) {
1155 element = ((PsiMember)element).getContainingClass();
1157 if (element instanceof GrMemberOwner) {
1158 annotation.registerFix(new CreateFieldFromConstructorLabelFix((GrMemberOwner)element, label.getNamedArgument()));
1160 if (element instanceof PsiClass) {
1161 annotation.registerFix(new DynamicPropertyFix(label, (PsiClass)element));
1169 private static void checkClosureApplicability(GroovyResolveResult resolveResult, PsiType type, PsiElement place, AnnotationHolder holder) {
1170 final PsiElement element = resolveResult.getElement();
1171 if (!(element instanceof GrVariable)) return;
1172 if (!(type instanceof GrClosureType)) return;
1173 final GrVariable variable = (GrVariable)element;
1174 PsiType[] argumentTypes = PsiUtil.getArgumentTypes(place, false, true);
1175 if (argumentTypes == null) return;
1177 final PsiType[] paramTypes = PsiUtil.skipOptionalClosureParameters(argumentTypes.length, (GrClosureType)type);
1178 if (!areTypesCompatibleForCallingClosure(argumentTypes, paramTypes, place.getManager(), place.getResolveScope())) {
1179 final String typesString = buildArgTypesList(argumentTypes);
1180 String message = GroovyBundle.message("cannot.apply.method.or.closure", variable.getName(), typesString);
1181 PsiElement elementToHighlight = PsiUtil.getArgumentsElement(place);
1182 if (elementToHighlight == null) elementToHighlight = place;
1183 holder.createWarningAnnotation(elementToHighlight, message);
1187 private static boolean areTypesCompatibleForCallingClosure(PsiType[] argumentTypes,
1188 PsiType[] paramTypes,
1189 PsiManager manager,
1190 GlobalSearchScope resolveScope) {
1191 if (argumentTypes.length != paramTypes.length) return false;
1192 for (int i = 0; i < argumentTypes.length; i++) {
1193 final PsiType paramType = TypesUtil.boxPrimitiveType(paramTypes[i], manager, resolveScope);
1194 final PsiType argType = argumentTypes[i];
1195 if (!TypesUtil.isAssignableByMethodCallConversion(paramType, argType, manager, resolveScope)) return false;
1197 return true;
1200 private static void registerAddImportFixes(GrReferenceElement refElement, Annotation annotation) {
1201 final String referenceName = refElement.getReferenceName();
1202 //noinspection ConstantConditions
1203 if (StringUtil.isEmpty(referenceName) || Character.isLowerCase(referenceName.charAt(0))) {
1204 return;
1207 annotation.registerFix(new GroovyAddImportAction(refElement));
1210 private static void registerCreateClassByTypeFix(GrReferenceElement refElement, Annotation annotation) {
1211 GrPackageDefinition packageDefinition = PsiTreeUtil.getParentOfType(refElement, GrPackageDefinition.class);
1212 if (packageDefinition == null && refElement.getQualifier() == null) {
1213 PsiElement parent = refElement.getParent();
1214 if (parent instanceof GrNewExpression) {
1215 annotation.registerFix(CreateClassFix.createClassFromNewAction((GrNewExpression)parent));
1217 else {
1218 annotation.registerFix(CreateClassFix.createClassFixAction(refElement));
1223 private static void highlightMember(AnnotationHolder holder, GrMember member) {
1224 if (member instanceof GrField) {
1225 GrField field = (GrField)member;
1226 PsiElement identifier = field.getNameIdentifierGroovy();
1227 final boolean isStatic = field.hasModifierProperty(GrModifier.STATIC);
1228 holder.createInfoAnnotation(identifier, null).setTextAttributes(isStatic ? DefaultHighlighter.STATIC_FIELD : DefaultHighlighter.INSTANCE_FIELD);
1232 private static void highlightAnnotation(AnnotationHolder holder, PsiElement refElement, GroovyResolveResult result) {
1233 PsiElement element = result.getElement();
1234 PsiElement parent = refElement.getParent();
1235 if (element instanceof PsiClass && ((PsiClass)element).isAnnotationType() && !(parent instanceof GrImportStatement)) {
1236 Annotation annotation = holder.createInfoAnnotation(parent, null);
1237 annotation.setTextAttributes(DefaultHighlighter.ANNOTATION);
1238 GroovyPsiElement context = result.getCurrentFileResolveContext();
1239 if (context instanceof GrImportStatement) {
1240 annotation = holder.createInfoAnnotation(((GrImportStatement)context).getImportReference(), null);
1241 annotation.setTextAttributes(DefaultHighlighter.ANNOTATION);
1248 private static String buildArgTypesList(PsiType[] argTypes) {
1249 StringBuilder builder = new StringBuilder();
1250 builder.append("(");
1251 for (int i = 0; i < argTypes.length; i++) {
1252 if (i > 0) {
1253 builder.append(", ");
1255 PsiType argType = argTypes[i];
1256 builder.append(argType != null ? argType.getInternalCanonicalText() : "?");
1258 builder.append(")");
1259 return builder.toString();
1262 private static class DuplicateVariablesProcessor extends PropertyResolverProcessor {
1263 boolean borderPassed;
1265 public DuplicateVariablesProcessor(GrVariable variable) {
1266 super(variable.getName(), variable);
1267 borderPassed = false;
1270 @Override
1271 public boolean execute(PsiElement element, ResolveState state) {
1272 if (borderPassed) {
1273 return false;
1275 return super.execute(element, state);
1278 @Override
1279 public void handleEvent(Event event, Object associated) {
1280 if (event == ResolveUtil.DECLARATION_SCOPE_PASSED) {
1281 borderPassed = true;
1283 super.handleEvent(event, associated);