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
;
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
) {
103 ((GroovyPsiElement
)element
).accept(this);
109 public void visitElement(GroovyPsiElement element
) {
110 if (element
.getParent() instanceof GrDocReferenceElement
) {
111 checkGrDocReferenceElement(myHolder
, element
);
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());
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) {
137 checkSingleResolvedElement(myHolder
, refElement
, resolveResult
, true);
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()));
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
);
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
);
199 registerStaticImportFix(referenceExpression
, annotation
);
203 if (qualifier
.getType() == null) {
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
)) {
219 annotation
.registerFix(new GroovyStaticImportMethodFix((GrCall
)referenceExpression
.getParent()));
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
);
233 public void visitMethod(GrMethod method
) {
234 checkMethodDefinitionModifiers(myHolder
, method
);
235 checkInnerMethod(myHolder
, method
);
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"));
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
)) {
276 .createWarningAnnotation(variable
.getNameIdentifierGroovy(), GroovyBundle
.message("field.already.defined", variable
.getName()));
279 final String key
= duplicate
instanceof GrField ?
"field.already.defined" : "variable.already.defined";
280 myHolder
.createErrorAnnotation(variable
.getNameIdentifierGroovy(), GroovyBundle
.message(key
, variable
.getName()));
286 public void visitAssignmentExpression(GrAssignmentExpression expression
) {
287 GrExpression lValue
= expression
.getLValue();
288 if (!PsiUtil
.mightBeLVlaue(lValue
)) {
289 myHolder
.createErrorAnnotation(lValue
, GroovyBundle
.message("invalid.lvalue"));
294 public void visitReturnStatement(GrReturnStatement returnStatement
) {
295 final GrExpression value
= returnStatement
.getReturnValue();
297 final PsiType type
= value
.getType();
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"));
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"));
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
);
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
);
364 if (newExpression
.getQualifier() != null) {
365 if (clazz
.hasModifierProperty(GrModifier
.STATIC
)) {
366 myHolder
.createErrorAnnotation(newExpression
, GroovyBundle
.message("qualified.new.of.static.class"));
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
);
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
);
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
);
406 public void visitDocMethodReference(GrDocMethodReference reference
) {
407 checkGrDocMemberReference(reference
, myHolder
);
411 public void visitDocFieldReference(GrDocFieldReference reference
) {
412 checkGrDocMemberReference(reference
, myHolder
);
416 public void visitConstructorInvocation(GrConstructorInvocation invocation
) {
417 final GroovyResolveResult resolveResult
= invocation
.resolveConstructorGenerics();
418 if (resolveResult
!= null) {
419 checkMethodApplicability(resolveResult
, invocation
.getThisOrSuperKeyword(), myHolder
);
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
);
429 final PsiClass clazz
= invocation
.getDelegatedClass();
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
);
443 public void visitBreakStatement(GrBreakStatement breakStatement
) {
444 checkFlowInterruptStatement(breakStatement
, myHolder
);
448 public void visitContinueStatement(GrContinueStatement continueStatement
) {
449 checkFlowInterruptStatement(continueStatement
, myHolder
);
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
));
461 public void visitPackageDefinition(GrPackageDefinition packageDefinition
) {
462 //todo: if reference isn't resolved it construct package definition
463 final PsiFile file
= packageDefinition
.getContainingFile();
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"));
482 public void visitSuperExpression(GrSuperReferenceExpression superExpression
) {
483 checkThisOrSuperReferenceExpression(superExpression
, myHolder
);
487 public void visitThisExpression(GrThisReferenceExpression thisExpression
) {
488 checkThisOrSuperReferenceExpression(thisExpression
, myHolder
);
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"));
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
));
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
);
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();
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"));
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()));
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()));
590 holder
.createErrorAnnotation(expression
, GroovyBundle
.message("is.not.enclosing.class", ((PsiClass
)resolved
).getQualifiedName()));
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"));
613 private static PsiElement
findModifierStatic(GrMember grMember
) {
614 final GrModifierList list
= grMember
.getModifierList();
619 for (PsiElement modifier
: list
.getModifiers()) {
620 if (GrModifier
.STATIC
.equals(modifier
.getText())) {
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
);
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());
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()) {
718 else if (containingTypeDef
.isAnnotationType()) {
722 else if (containingTypeDef
.isAnonymous()) {
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"));
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;
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();
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
));
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
;
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
));
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();
996 if (containingFile
!= null) {
997 file
= containingFile
.getVirtualFile();
998 if (file
== null) return;
1004 if (QuickfixUtil
.isCall(referenceExpression
)) {
1005 annotation
.registerFix(new DynamicMethodFix(referenceExpression
), referenceExpression
.getTextRange());
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
);
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
);
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
);
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
);
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
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
) {
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
);
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"));
1145 holder
.createWarningAnnotation(nameElement
, GroovyBundle
.message("property.name.expected"));
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
,
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;
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))) {
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
));
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
++) {
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;
1271 public boolean execute(PsiElement element
, ResolveState state
) {
1275 return super.execute(element
, state
);
1279 public void handleEvent(Event event
, Object associated
) {
1280 if (event
== ResolveUtil
.DECLARATION_SCOPE_PASSED
) {
1281 borderPassed
= true;
1283 super.handleEvent(event
, associated
);