IDEA-51893: Quick Fix "​Create Field" when using Groovy named parameters
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / annotator / GroovyAnnotator.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.jetbrains.plugins.groovy.annotator;
18
19 import com.intellij.codeInsight.ClassUtil;
20 import com.intellij.codeInspection.ProblemHighlightType;
21 import com.intellij.lang.ASTNode;
22 import com.intellij.lang.annotation.Annotation;
23 import com.intellij.lang.annotation.AnnotationHolder;
24 import com.intellij.lang.annotation.Annotator;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.util.TextRange;
27 import com.intellij.openapi.util.text.StringUtil;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.psi.*;
30 import com.intellij.psi.search.GlobalSearchScope;
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;
80
81 import java.util.*;
82
83 /**
84  * @author ven
85  */
86 public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
87   private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.annotator.GroovyAnnotator");
88
89   private AnnotationHolder myHolder;
90
91   private static boolean isDocCommentElement(PsiElement element) {
92     if (element == null) return false;
93     ASTNode node = element.getNode();
94     return node != null && PsiTreeUtil.getParentOfType(element, GrDocComment.class) != null || element instanceof GrDocComment;
95   }
96
97   public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
98     if (element instanceof GroovyPsiElement) {
99       myHolder = holder;
100       ((GroovyPsiElement)element).accept(this);
101       myHolder = null;
102     }
103   }
104
105   @Override
106   public void visitElement(GroovyPsiElement element) {
107     if (element.getParent() instanceof GrDocReferenceElement) {
108       checkGrDocReferenceElement(myHolder, element);
109     }
110     else {
111       final ASTNode node = element.getNode();
112       if (!(element instanceof PsiWhiteSpace) &&
113           !GroovyTokenTypes.COMMENT_SET.contains(node.getElementType()) &&
114           element.getContainingFile() instanceof GroovyFile &&
115           !isDocCommentElement(element)) {
116         GroovyImportsTracker.getInstance(element.getProject()).markFileAnnotated((GroovyFile)element.getContainingFile());
117       }
118     }
119   }
120
121   @Override
122   public void visitCodeReferenceElement(GrCodeReferenceElement refElement) {
123     final PsiElement parent = refElement.getParent();
124     GroovyResolveResult resolveResult = refElement.advancedResolve();
125     highlightAnnotation(myHolder, refElement, resolveResult);
126     registerUsedImport(refElement, resolveResult);
127     highlightAnnotation(myHolder, refElement, resolveResult);
128     if (refElement.getReferenceName() != null) {
129
130       if (parent instanceof GrImportStatement && ((GrImportStatement)parent).isStatic() && refElement.multiResolve(false).length > 0) {
131         return;
132       }
133
134       checkSingleResolvedElement(myHolder, refElement, resolveResult);
135     }
136
137   }
138
139   @Override
140   public void visitReferenceExpression(GrReferenceExpression referenceExpression) {
141     GroovyResolveResult resolveResult = referenceExpression.advancedResolve();
142     GroovyResolveResult[] results = referenceExpression.multiResolve(false); //cached
143     for (GroovyResolveResult result : results) {
144       registerUsedImport(referenceExpression, result);
145     }
146
147     PsiElement resolved = resolveResult.getElement();
148     final PsiElement parent = referenceExpression.getParent();
149     if (resolved != null) {
150       if (resolved instanceof PsiMember) {
151         highlightMemberResolved(myHolder, referenceExpression, ((PsiMember)resolved));
152       }
153       if (!resolveResult.isAccessible()) {
154         String message = GroovyBundle.message("cannot.access", referenceExpression.getReferenceName());
155         myHolder.createWarningAnnotation(referenceExpression.getReferenceNameElement(), message);
156       }
157       if (!resolveResult.isStaticsOK() && resolved instanceof PsiModifierListOwner) {
158         if (!((PsiModifierListOwner)resolved).hasModifierProperty(GrModifier.STATIC)) {
159           myHolder.createWarningAnnotation(referenceExpression, GroovyBundle.message("cannot.reference.nonstatic", referenceExpression.getReferenceName()));
160         }
161       }
162     }
163     else {
164       GrExpression qualifier = referenceExpression.getQualifierExpression();
165       if (qualifier == null && isDeclarationAssignment(referenceExpression)) return;
166
167       if (parent instanceof GrReferenceExpression && "class".equals(((GrReferenceExpression)parent).getReferenceName())) {
168         checkSingleResolvedElement(myHolder, referenceExpression, resolveResult);
169       }
170     }
171
172     if (parent instanceof GrCall) {
173       if (resolved == null && results.length > 0) {
174         resolved = results[0].getElement();
175         resolveResult = results[0];
176       }
177       if (resolved instanceof PsiMethod && resolved.getUserData(GrMethod.BUILDER_METHOD) == null) {
178         checkMethodApplicability(resolveResult, referenceExpression, myHolder);
179       }
180       else {
181         checkClosureApplicability(resolveResult, referenceExpression.getType(), referenceExpression, myHolder);
182       }
183     }
184     if (isDeclarationAssignment(referenceExpression) || resolved instanceof PsiPackage) return;
185
186     if (resolved == null) {
187       PsiElement refNameElement = referenceExpression.getReferenceNameElement();
188       PsiElement elt = refNameElement == null ? referenceExpression : refNameElement;
189       Annotation annotation = myHolder.createInfoAnnotation(elt, null);
190       final GrExpression qualifier = referenceExpression.getQualifierExpression();
191       if (qualifier == null) {
192         if (!(parent instanceof GrCall)) {
193           registerCreateClassByTypeFix(referenceExpression, annotation);
194           registerAddImportFixes(referenceExpression, annotation);
195         }
196         else {
197           registerStaticImportFix(referenceExpression, annotation);
198         }
199       }
200       else {
201         if (qualifier.getType() == null) {
202           return;
203         }
204       }
205       registerReferenceFixes(referenceExpression, annotation);
206       annotation.setTextAttributes(DefaultHighlighter.UNRESOLVED_ACCESS);
207     }
208   }
209
210   private static void registerStaticImportFix(GrReferenceExpression referenceExpression, Annotation annotation) {
211     final String referenceName = referenceExpression.getReferenceName();
212     //noinspection ConstantConditions
213     if (StringUtil.isEmpty(referenceName)) {
214       return;
215     }
216
217     annotation.registerFix(new GroovyStaticImportMethodFix((GrCall)referenceExpression.getParent()));
218   }
219
220   @Override
221   public void visitTypeDefinition(GrTypeDefinition typeDefinition) {
222     checkTypeDefinition(myHolder, typeDefinition);
223     checkTypeDefinitionModifiers(myHolder, typeDefinition);
224
225     final GrTypeDefinitionBody body = typeDefinition.getBody();
226     if (body != null) checkDuplicateMethod(body.getGroovyMethods(), myHolder);
227     checkImplementedMethodsOfClass(myHolder, typeDefinition);
228   }
229
230   @Override
231   public void visitMethod(GrMethod method) {
232     checkMethodDefinitionModifiers(myHolder, method);
233     checkInnerMethod(myHolder, method);
234   }
235
236   @Override
237   public void visitVariableDeclaration(GrVariableDeclaration variableDeclaration) {
238
239     PsiElement parent = variableDeclaration.getParent();
240     assert parent != null;
241
242     PsiElement typeDef = parent.getParent();
243     if (typeDef != null && typeDef instanceof GrTypeDefinition) {
244       PsiModifierList modifiersList = variableDeclaration.getModifierList();
245       checkAccessModifiers(myHolder, modifiersList);
246
247       if (modifiersList.hasExplicitModifier(GrModifier.VOLATILE) && modifiersList.hasExplicitModifier(GrModifier.FINAL)) {
248         myHolder.createErrorAnnotation(modifiersList, GroovyBundle.message("illegal.combination.of.modifiers.volatile.and.final"));
249       }
250
251       if (modifiersList.hasExplicitModifier(GrModifier.NATIVE)) {
252         myHolder.createErrorAnnotation(modifiersList, GroovyBundle.message("variable.cannot.be.native"));
253       }
254
255       if (modifiersList.hasExplicitModifier(GrModifier.ABSTRACT)) {
256         myHolder.createErrorAnnotation(modifiersList, GroovyBundle.message("variable.cannot.be.abstract"));
257       }
258     }
259   }
260
261   @Override
262   public void visitVariable(GrVariable variable) {
263     if (variable instanceof GrMember) {
264       highlightMember(myHolder, ((GrMember)variable));
265       checkStaticDeclarationsInInnerClass((GrMember)variable, myHolder);
266     }
267     PropertyResolverProcessor processor = new DuplicateVariablesProcessor(variable);
268     final GroovyPsiElement duplicate =
269       ResolveUtil.resolveExistingElement(variable, processor, GrVariable.class, GrReferenceExpression.class);
270
271     if (duplicate instanceof GrVariable) {
272       if (duplicate instanceof GrField && !(variable instanceof GrField)) {
273         myHolder
274           .createWarningAnnotation(variable.getNameIdentifierGroovy(), GroovyBundle.message("field.already.defined", variable.getName()));
275       }
276       else {
277         final String key = duplicate instanceof GrField ? "field.already.defined" : "variable.already.defined";
278         myHolder.createErrorAnnotation(variable.getNameIdentifierGroovy(), GroovyBundle.message(key, variable.getName()));
279       }
280     }
281   }
282
283   @Override
284   public void visitAssignmentExpression(GrAssignmentExpression expression) {
285     GrExpression lValue = expression.getLValue();
286     if (!PsiUtil.mightBeLVlaue(lValue)) {
287       myHolder.createErrorAnnotation(lValue, GroovyBundle.message("invalid.lvalue"));
288     }
289   }
290
291   @Override
292   public void visitReturnStatement(GrReturnStatement returnStatement) {
293     final GrExpression value = returnStatement.getReturnValue();
294     if (value != null) {
295       final PsiType type = value.getType();
296       if (type != null) {
297         final GrParametersOwner owner = PsiTreeUtil.getParentOfType(returnStatement, GrMethod.class, GrClosableBlock.class);
298         if (owner instanceof PsiMethod) {
299           final PsiMethod method = (PsiMethod)owner;
300           if (method.isConstructor()) {
301             myHolder.createErrorAnnotation(value, GroovyBundle.message("cannot.return.from.constructor"));
302           }
303           else {
304             final PsiType methodType = method.getReturnType();
305             if (methodType != null) {
306               if (PsiType.VOID.equals(methodType)) {
307                 myHolder.createErrorAnnotation(value, GroovyBundle.message("cannot.return.from.void.method"));
308               }
309             }
310           }
311         }
312       }
313     }
314   }
315
316   @Override
317   public void visitListOrMap(GrListOrMap listOrMap) {
318     final Map<GrNamedArgument, List<GrNamedArgument>> map = DuplicatesUtil.factorDuplicates(listOrMap.getNamedArguments(), new TObjectHashingStrategy<GrNamedArgument>() {
319       public int computeHashCode(GrNamedArgument arg) {
320         final GrArgumentLabel label = arg.getLabel();
321         if (label == null) return 0;
322         final String name = label.getName();
323         if (name == null) return 0;
324         return name.hashCode();
325       }
326
327       public boolean equals(GrNamedArgument arg1, GrNamedArgument arg2) {
328         final GrArgumentLabel label1 = arg1.getLabel();
329         final GrArgumentLabel label2 = arg2.getLabel();
330         if (label1 == null || label2 == null) {
331           return label1 == null && label2 == null;
332         }
333         final String name1 = label1.getName();
334         final String name2 = label2.getName();
335         if (name1 == null || name2 == null) {
336           return name1 == null && name2 == null;
337         }
338         return name1.equals(name2);
339       }
340     });
341
342     processDuplicates(map, myHolder);
343   }
344
345   @Override
346   public void visitNewExpression(GrNewExpression newExpression) {
347     if (newExpression.getArrayCount() > 0) return;
348     GrCodeReferenceElement refElement = newExpression.getReferenceElement();
349     if (refElement == null) return;
350     final PsiElement element = refElement.resolve();
351     if (element instanceof PsiClass) {
352       PsiClass clazz = (PsiClass)element;
353       if (clazz.hasModifierProperty(GrModifier.ABSTRACT)) {
354         if (newExpression.getAnonymousClassDefinition() == null) {
355           String message = clazz.isInterface()
356                            ? GroovyBundle.message("cannot.instantiate.interface", clazz.getName())
357                            : GroovyBundle.message("cannot.instantiate.abstract.class", clazz.getName());
358           myHolder.createErrorAnnotation(refElement, message);
359         }
360         return;
361       }
362       if (newExpression.getQualifier() != null) {
363         if (clazz.hasModifierProperty(GrModifier.STATIC)) {
364           myHolder.createErrorAnnotation(newExpression, GroovyBundle.message("qualified.new.of.static.class"));
365         }
366       } else {
367         final PsiClass outerClass = clazz.getContainingClass();
368         if (com.intellij.psi.util.PsiUtil.isInnerClass(clazz) && !PsiUtil.hasEnclosingInstanceInScope(outerClass, newExpression, true)) {
369           myHolder.createErrorAnnotation(newExpression, GroovyBundle.message("cannot.reference.nonstatic", clazz.getQualifiedName()));
370         }
371       }
372     }
373
374     final GroovyResolveResult constructorResolveResult = newExpression.resolveConstructorGenerics();
375     if (constructorResolveResult.getElement() != null) {
376       checkMethodApplicability(constructorResolveResult, refElement, myHolder);
377       final GrArgumentList argList = newExpression.getArgumentList();
378       if (argList != null && argList.getExpressionArguments().length == 0) checkDefaultMapConstructor(myHolder, argList, constructorResolveResult);
379     }
380     else {
381       final GroovyResolveResult[] results = newExpression.multiResolveConstructor();
382       final GrArgumentList argList = newExpression.getArgumentList();
383       PsiElement toHighlight = argList != null ? argList : refElement.getReferenceNameElement();
384
385       if (results.length > 0) {
386         String message = GroovyBundle.message("ambiguous.constructor.call");
387         myHolder.createWarningAnnotation(toHighlight, message);
388       }
389       else {
390         if (element instanceof PsiClass) {
391           //default constructor invocation
392           PsiType[] argumentTypes = PsiUtil.getArgumentTypes(refElement, true, true);
393           if (argumentTypes != null && argumentTypes.length > 0) {
394             String message = GroovyBundle.message("cannot.find.default.constructor", ((PsiClass)element).getName());
395             myHolder.createWarningAnnotation(toHighlight, message);
396           }
397           else checkDefaultMapConstructor(myHolder, argList, constructorResolveResult);
398         }
399       }
400     }
401   }
402
403   @Override
404   public void visitDocMethodReference(GrDocMethodReference reference) {
405     checkGrDocMemberReference(reference, myHolder);
406   }
407
408   @Override
409   public void visitDocFieldReference(GrDocFieldReference reference) {
410     checkGrDocMemberReference(reference, myHolder);
411   }
412
413   @Override
414   public void visitConstructorInvocation(GrConstructorInvocation invocation) {
415     final GroovyResolveResult resolveResult = invocation.resolveConstructorGenerics();
416     if (resolveResult != null) {
417       checkMethodApplicability(resolveResult, invocation.getThisOrSuperKeyword(), myHolder);
418     }
419     else {
420       final GroovyResolveResult[] results = invocation.multiResolveConstructor();
421       final GrArgumentList argList = invocation.getArgumentList();
422       if (results.length > 0) {
423         String message = GroovyBundle.message("ambiguous.constructor.call");
424         myHolder.createWarningAnnotation(argList, message);
425       }
426       else {
427         final PsiClass clazz = invocation.getDelegatedClass();
428         if (clazz != null) {
429           //default constructor invocation
430           PsiType[] argumentTypes = PsiUtil.getArgumentTypes(invocation.getThisOrSuperKeyword(), true, true);
431           if (argumentTypes != null && argumentTypes.length > 0) {
432             String message = GroovyBundle.message("cannot.find.default.constructor", clazz.getName());
433             myHolder.createWarningAnnotation(argList, message);
434           }
435         }
436       }
437     }
438   }
439
440   @Override
441   public void visitBreakStatement(GrBreakStatement breakStatement) {
442     checkFlowInterruptStatement(breakStatement, myHolder);
443   }
444
445   @Override
446   public void visitContinueStatement(GrContinueStatement continueStatement) {
447     checkFlowInterruptStatement(continueStatement, myHolder);
448   }
449
450   @Override
451   public void visitLabeledStatement(GrLabeledStatement labeledStatement) {
452     final String name = labeledStatement.getLabelName();
453     if (ResolveUtil.resolveLabeledStatement(name, labeledStatement, true) != null) {
454       myHolder.createWarningAnnotation(labeledStatement.getLabel(), GroovyBundle.message("label.already.used", name));
455     }
456   }
457
458   @Override
459   public void visitPackageDefinition(GrPackageDefinition packageDefinition) {
460     //todo: if reference isn't resolved it construct package definition
461     final PsiFile file = packageDefinition.getContainingFile();
462     assert file != null;
463
464     PsiDirectory psiDirectory = file.getContainingDirectory();
465     if (psiDirectory != null) {
466       PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory);
467       if (aPackage != null) {
468         String packageName = aPackage.getQualifiedName();
469         if (!packageDefinition.getPackageName().equals(packageName)) {
470           final Annotation annotation = myHolder.createWarningAnnotation(packageDefinition, "wrong package name");
471           annotation.registerFix(new ChangePackageQuickFix((GroovyFile)packageDefinition.getContainingFile(), packageName));
472         }
473       }
474     }
475     final GrModifierList modifierList = packageDefinition.getAnnotationList();
476     checkAnnotationList(myHolder, modifierList, GroovyBundle.message("package.definition.cannot.have.modifiers"));
477   }
478
479   @Override
480   public void visitSuperExpression(GrSuperReferenceExpression superExpression) {
481     checkThisOrSuperReferenceExpression(superExpression, myHolder);
482   }
483
484   @Override
485   public void visitThisExpression(GrThisReferenceExpression thisExpression) {
486     checkThisOrSuperReferenceExpression(thisExpression, myHolder);
487   }
488
489   @Override
490   public void visitLiteralExpression(GrLiteral literal) {
491     String text = literal.getText();
492     if (text.startsWith("'''")) {
493       if (text.length() < 6 || !text.endsWith("'''")) {
494         myHolder.createErrorAnnotation(literal, GroovyBundle.message("string.end.expected"));
495       }
496     }
497     else if (text.startsWith("'")) {
498       if (text.length() < 2 || !text.endsWith("'")) {
499         myHolder.createErrorAnnotation(literal, GroovyBundle.message("string.end.expected"));
500       }
501     }
502   }
503
504   @Override
505   public void visitForInClause(GrForInClause forInClause) {
506     final GrVariable[] declaredVariables = forInClause.getDeclaredVariables();
507     if (declaredVariables.length < 1) return;
508     final GrVariable variable = declaredVariables[0];
509     final GrModifierList modifierList = ((GrModifierList)variable.getModifierList());
510     if (modifierList == null) return;
511     final PsiElement[] modifiers = modifierList.getModifiers();
512     for (PsiElement modifier : modifiers) {
513       if (modifier instanceof PsiAnnotation) continue;
514       final String modifierText = modifier.getText();
515       if (GrModifier.FINAL.equals(modifierText)) continue;
516       if (GrModifier.DEF.equals(modifierText)) continue;
517       myHolder.createErrorAnnotation(modifier, GroovyBundle.message("not.allowed.modifier.in.forin", modifierText));
518     }
519   }
520
521   @Override
522   public void visitFile(GroovyFileBase file) {
523     if (!file.isScript()) return;
524
525     List<GrMethod> methods = new ArrayList<GrMethod>();
526
527     for (GrTopLevelDefintion topLevelDefinition : file.getTopLevelDefinitions()) {
528       if (topLevelDefinition instanceof GrMethod) {
529         methods.add(((GrMethod)topLevelDefinition));
530       }
531     }
532
533     checkDuplicateMethod(methods.toArray(new GrMethod[methods.size()]), myHolder);
534   }
535
536   @Override
537   public void visitImportStatement(GrImportStatement importStatement) {
538     checkAnnotationList(myHolder, importStatement.getAnnotationList(), GroovyBundle.message("import.statement.cannot.have.modifiers"));
539   }
540
541   private static void checkFlowInterruptStatement(GrFlowInterruptingStatement statement, AnnotationHolder holder) {
542     final PsiElement label = statement.getLabelIdentifier();
543
544     if (label != null) {
545       final GrLabeledStatement resolved = statement.resolveLabel();
546       if (resolved == null) {
547         holder.createErrorAnnotation(label, GroovyBundle.message("undefined.label", statement.getLabelName()));
548       }
549     }
550
551     final GrStatement targetStatement = statement.findTargetStatement();
552     if (targetStatement == null) {
553       if (statement instanceof GrContinueStatement && label == null) {
554         holder.createErrorAnnotation(statement, GroovyBundle.message("continue.outside.loop"));
555       }
556       else if (statement instanceof GrBreakStatement && label == null) {
557         holder.createErrorAnnotation(statement, GroovyBundle.message("break.outside.loop.or.switch"));
558       }
559     }
560     if (statement instanceof GrBreakStatement && label != null && findFirstLoop(statement) == null) {
561       holder.createErrorAnnotation(statement, GroovyBundle.message("break.outside.loop"));
562     }
563   }
564
565   @Nullable
566   private static GrLoopStatement findFirstLoop(GrFlowInterruptingStatement statement) {
567     return PsiTreeUtil.getParentOfType(statement, GrLoopStatement.class, true, GrClosableBlock.class, GrMember.class, GroovyFile.class);
568   }
569
570   private static void checkThisOrSuperReferenceExpression(GrExpression expression, AnnotationHolder holder) {
571     final GrReferenceExpression qualifier = expression instanceof GrThisReferenceExpression
572                                             ? ((GrThisReferenceExpression)expression).getQualifier()
573                                             : ((GrSuperReferenceExpression)expression).getQualifier();
574     if (qualifier == null) {
575       final GrMethod method = PsiTreeUtil.getParentOfType(expression, GrMethod.class);
576       if (method != null && method.hasModifierProperty(GrModifier.STATIC)) {
577         holder.createErrorAnnotation(expression, GroovyBundle.message("cannot.reference.nonstatic", expression.getText()));
578       }
579     }
580     else {
581       final PsiElement resolved = qualifier.resolve();
582       if (resolved instanceof PsiClass) {
583         if (PsiTreeUtil.isAncestor(resolved, expression, true)) {
584           if (!PsiUtil.hasEnclosingInstanceInScope((PsiClass)resolved, expression, true)) {
585             holder.createErrorAnnotation(expression, GroovyBundle.message("cannot.reference.nonstatic", expression.getText()));
586           }
587         } else {
588           holder.createErrorAnnotation(expression, GroovyBundle.message("is.not.enclosing.class", ((PsiClass)resolved).getQualifiedName()));
589         }
590       }
591       else {
592         holder.createErrorAnnotation(qualifier, GroovyBundle.message("unknown.class", qualifier.getText()));
593       }
594     }
595   }
596
597   private static void checkStaticDeclarationsInInnerClass(GrMember classMember, AnnotationHolder holder) {
598     final PsiClass containingClass = classMember.getContainingClass();
599     if (containingClass == null) return;
600     if (com.intellij.psi.util.PsiUtil.isInnerClass(containingClass)) {
601       if (classMember.hasModifierProperty(GrModifier.STATIC)) {
602         final PsiElement modifier = findModifierStatic(classMember);
603         if (modifier != null) {
604           holder.createErrorAnnotation(modifier, GroovyBundle.message("cannot.have.static.declarations"));
605         }
606       }
607     }
608   }
609
610   @Nullable
611   private static PsiElement findModifierStatic(GrMember grMember) {
612     final GrModifierList list = grMember.getModifierList();
613     if (list == null) {
614       return null;
615     }
616
617     for (PsiElement modifier : list.getModifiers()) {
618       if (GrModifier.STATIC.equals(modifier.getText())) {
619         return modifier;
620       }
621     }
622     return null;
623   }
624
625   private static void checkGrDocReferenceElement(AnnotationHolder holder, PsiElement element) {
626     ASTNode node = element.getNode();
627     if (node != null && TokenSets.BUILT_IN_TYPE.contains(node.getElementType())) {
628       Annotation annotation = holder.createInfoAnnotation(element, null);
629       annotation.setTextAttributes(DefaultHighlighter.KEYWORD);
630     }
631   }
632
633   private static void checkAnnotationList(AnnotationHolder holder, @Nullable GrModifierList modifierList, String message) {
634     if (modifierList == null) return;
635     final PsiElement[] modifiers = modifierList.getModifiers();
636     for (PsiElement modifier : modifiers) {
637       if (!(modifier instanceof PsiAnnotation)) {
638         holder.createErrorAnnotation(modifier, message);
639       }
640     }
641   }
642
643   private static void checkImplementedMethodsOfClass(AnnotationHolder holder, GrTypeDefinition typeDefinition) {
644     if (typeDefinition.hasModifierProperty(GrModifier.ABSTRACT)) return;
645     if (typeDefinition.isEnum() || typeDefinition.isAnnotationType()) return;
646     if (typeDefinition instanceof GrTypeParameter) return;
647
648     Collection<HierarchicalMethodSignature> allMethods = typeDefinition.getVisibleSignatures();
649     PsiMethod abstractMethod = ClassUtil.getAnyAbstractMethod(typeDefinition, allMethods);
650
651     if (abstractMethod == null) return;
652
653     String notImplementedMethodName = abstractMethod.getName();
654
655     final int startOffset = typeDefinition.getTextOffset();
656     int endOffset = typeDefinition.getNameIdentifierGroovy().getTextRange().getEndOffset();
657     final Annotation annotation = holder.createErrorAnnotation(new TextRange(startOffset, endOffset),
658                                                                GroovyBundle.message("method.is.not.implemented", notImplementedMethodName));
659     registerImplementsMethodsFix(typeDefinition, annotation);
660   }
661
662   private static void registerImplementsMethodsFix(GrTypeDefinition typeDefinition, Annotation annotation) {
663     annotation.registerFix(new ImplementMethodsQuickFix(typeDefinition));
664   }
665
666   private static void checkInnerMethod(AnnotationHolder holder, GrMethod grMethod) {
667     final PsiElement parent = grMethod.getParent();
668     if (parent instanceof GrOpenBlock || parent instanceof GrClosableBlock) {
669       holder.createErrorAnnotation(grMethod.getNameIdentifierGroovy(), GroovyBundle.message("Inner.methods.are.not.supported"));
670     }
671   }
672
673   protected static void processDuplicates(Map<GrNamedArgument, List<GrNamedArgument>> map, AnnotationHolder holder) {
674     for (List<GrNamedArgument> args : map.values()) {
675       for (int i = 1; i < args.size(); i++) {
676         GrNamedArgument namedArgument = args.get(i);
677         holder.createWarningAnnotation(namedArgument, GroovyBundle.message("duplicate.element.in.the.map"));
678       }
679     }
680   }
681
682   private static void checkMethodDefinitionModifiers(AnnotationHolder holder, GrMethod method) {
683     final PsiModifierList modifiersList = method.getModifierList();
684     checkAccessModifiers(holder, modifiersList);
685
686     //script methods
687     boolean isMethodAbstract = modifiersList.hasExplicitModifier(GrModifier.ABSTRACT);
688     final boolean isMethodStatic = modifiersList.hasExplicitModifier(GrModifier.STATIC);
689     if (method.getParent() instanceof GroovyFileBase) {
690       if (isMethodAbstract) {
691         holder.createErrorAnnotation(modifiersList, GroovyBundle.message("script.cannot.have.modifier.abstract"));
692       }
693
694       if (modifiersList.hasExplicitModifier(GrModifier.NATIVE)) {
695         holder.createErrorAnnotation(modifiersList, GroovyBundle.message("script.cannot.have.modifier.native"));
696       }
697     }
698     else  //type definition methods
699       if (method.getParent() != null && method.getParent().getParent() instanceof GrTypeDefinition) {
700         GrTypeDefinition containingTypeDef = ((GrTypeDefinition)method.getParent().getParent());
701
702         //interface
703         if (containingTypeDef.isInterface()) {
704           if (isMethodStatic) {
705             holder.createErrorAnnotation(modifiersList, GroovyBundle.message("interface.must.have.no.static.method"));
706           }
707
708           if (modifiersList.hasExplicitModifier(GrModifier.PRIVATE)) {
709             holder.createErrorAnnotation(modifiersList, GroovyBundle.message("interface.must.have.no.private.method"));
710           }
711
712         }
713         else if (containingTypeDef.isEnum()) {
714           //enumeration
715           //todo
716         }
717         else if (containingTypeDef.isAnnotationType()) {
718           //annotation
719           //todo
720         }
721         else if (containingTypeDef.isAnonymous()) {
722           //anonymous class
723           if (isMethodStatic) {
724             holder.createErrorAnnotation(modifiersList, GroovyBundle.message("static.declaration.in.inner.class"));
725           }
726           if (method.isConstructor()) {
727             holder.createErrorAnnotation(method.getNameIdentifierGroovy(),
728                                          GroovyBundle.message("constructors.are.not.allowed.in.anonymous.class"));
729           }
730           if (isMethodAbstract) {
731             holder.createErrorAnnotation(modifiersList, GroovyBundle.message("not.abstract.class.cannot.have.abstract.method"));
732           }
733         }
734         else {
735           //class
736           PsiModifierList typeDefModifiersList = containingTypeDef.getModifierList();
737           LOG.assertTrue(typeDefModifiersList != null, "modifiers list must be not null");
738
739           if (!typeDefModifiersList.hasExplicitModifier(GrModifier.ABSTRACT)) {
740             if (isMethodAbstract) {
741               holder.createErrorAnnotation(modifiersList, GroovyBundle.message("not.abstract.class.cannot.have.abstract.method"));
742             }
743           }
744
745           if (!isMethodAbstract) {
746             if (method.getBlock() == null) {
747               holder.createErrorAnnotation(method.getNameIdentifierGroovy(), GroovyBundle.message("not.abstract.method.should.have.body"));
748             }
749           }
750           if (isMethodStatic) {
751             checkStaticDeclarationsInInnerClass(method, holder);
752           }
753         }
754       }
755   }
756
757   private static void checkTypeDefinitionModifiers(AnnotationHolder holder, GrTypeDefinition typeDefinition) {
758     PsiModifierList modifiersList = typeDefinition.getModifierList();
759
760     if (modifiersList == null) return;
761
762     /**** class ****/
763     checkAccessModifiers(holder, modifiersList);
764
765     PsiClassType[] extendsListTypes = typeDefinition.getExtendsListTypes();
766
767     for (PsiClassType classType : extendsListTypes) {
768       PsiClass psiClass = classType.resolve();
769
770       if (psiClass != null) {
771         PsiModifierList modifierList = psiClass.getModifierList();
772         if (modifierList != null) {
773           if (modifierList.hasExplicitModifier(GrModifier.FINAL)) {
774             holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(), GroovyBundle.message("final.class.cannot.be.extended"));
775           }
776         }
777       }
778     }
779
780     if (modifiersList.hasExplicitModifier(GrModifier.ABSTRACT) && modifiersList.hasExplicitModifier(GrModifier.FINAL)) {
781       holder.createErrorAnnotation(modifiersList, GroovyBundle.message("illegal.combination.of.modifiers.abstract.and.final"));
782     }
783
784     if (modifiersList.hasExplicitModifier(GrModifier.TRANSIENT)) {
785       holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.transient.not.allowed.here"));
786     }
787     if (modifiersList.hasExplicitModifier(GrModifier.VOLATILE)) {
788       holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.volatile.not.allowed.here"));
789     }
790
791     /**** interface ****/
792     if (typeDefinition.isInterface()) {
793       if (modifiersList.hasExplicitModifier(GrModifier.FINAL)) {
794         holder.createErrorAnnotation(modifiersList, GroovyBundle.message("intarface.cannot.have.modifier.final"));
795       }
796
797       if (modifiersList.hasExplicitModifier(GrModifier.VOLATILE)) {
798         holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.volatile.not.allowed.here"));
799       }
800
801       if (modifiersList.hasExplicitModifier(GrModifier.TRANSIENT)) {
802         holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.transient.not.allowed.here"));
803       }
804     }
805
806     checkStaticDeclarationsInInnerClass(typeDefinition, holder);
807   }
808
809   private static void checkAccessModifiers(AnnotationHolder holder, @NotNull PsiModifierList modifierList) {
810     boolean hasPrivate = modifierList.hasExplicitModifier(GrModifier.PRIVATE);
811     boolean hasPublic = modifierList.hasExplicitModifier(GrModifier.PUBLIC);
812     boolean hasProtected = modifierList.hasExplicitModifier(GrModifier.PROTECTED);
813
814     if (hasPrivate && hasPublic || hasPrivate && hasProtected || hasPublic && hasProtected) {
815       holder.createErrorAnnotation(modifierList, GroovyBundle.message("illegal.combination.of.modifiers"));
816     }
817   }
818
819   private static void checkDuplicateMethod(GrMethod[] methods, AnnotationHolder holder) {
820     final Map<GrMethod, List<GrMethod>> map = DuplicatesUtil.factorDuplicates(methods, new TObjectHashingStrategy<GrMethod>() {
821       public int computeHashCode(GrMethod method) {
822         return method.getSignature(PsiSubstitutor.EMPTY).hashCode();
823       }
824
825       public boolean equals(GrMethod method1, GrMethod method2) {
826         return method1.getSignature(PsiSubstitutor.EMPTY).equals(method2.getSignature(PsiSubstitutor.EMPTY));
827       }
828     });
829     processMethodDuplicates(map, holder);
830   }
831
832   protected static void processMethodDuplicates(Map<GrMethod, List<GrMethod>> map, AnnotationHolder holder) {
833     HashSet<GrMethod> duplicateMethodsWarning = new HashSet<GrMethod>();
834     HashSet<GrMethod> duplicateMethodsErrors = new HashSet<GrMethod>();
835
836     DuplicatesUtil.collectMethodDuplicates(map, duplicateMethodsWarning, duplicateMethodsErrors);
837
838     for (GrMethod duplicateMethod : duplicateMethodsErrors) {
839       holder.createErrorAnnotation(duplicateMethod.getNameIdentifierGroovy(),
840                                    GroovyBundle.message("repetitive.method.name.signature.and.return.type"));
841     }
842
843     for (GrMethod duplicateMethod : duplicateMethodsWarning) {
844       holder.createWarningAnnotation(duplicateMethod.getNameIdentifierGroovy(), GroovyBundle.message("repetitive.method.name.signature"));
845     }
846   }
847
848
849   private static void checkTypeDefinition(AnnotationHolder holder, GrTypeDefinition typeDefinition) {
850     final GroovyConfigUtils configUtils = GroovyConfigUtils.getInstance();
851     if (typeDefinition.isAnnotationType()) {
852       Annotation annotation = holder.createInfoAnnotation(typeDefinition.getNameIdentifierGroovy(), null);
853       annotation.setTextAttributes(DefaultHighlighter.ANNOTATION);
854     }
855     else if (typeDefinition.isAnonymous()) {
856       if (!configUtils.isAtLeastGroovy1_7(typeDefinition)) {
857         holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
858                                      GroovyBundle.message("anonymous.classes.are.not.supported", configUtils.getSDKVersion(typeDefinition)));
859       }
860     }
861     else if (typeDefinition.getContainingClass() != null && !(typeDefinition instanceof GrEnumTypeDefinition)) {
862       if (!configUtils.isAtLeastGroovy1_7(typeDefinition)) {
863         holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
864                                      GroovyBundle.message("inner.classes.are.not.supported", configUtils.getSDKVersion(typeDefinition)));
865       }
866     }
867
868     final GrImplementsClause implementsClause = typeDefinition.getImplementsClause();
869     final GrExtendsClause extendsClause = typeDefinition.getExtendsClause();
870
871     if (implementsClause != null) {
872       checkForImplementingClass(holder, extendsClause, implementsClause, ((GrTypeDefinition)implementsClause.getParent()));
873     }
874
875     if (extendsClause != null) {
876       checkForExtendingInterface(holder, extendsClause, implementsClause, ((GrTypeDefinition)extendsClause.getParent()));
877     }
878
879     checkDuplicateClass(typeDefinition, holder);
880   }
881
882   private static void checkDuplicateClass(GrTypeDefinition typeDefinition, AnnotationHolder holder) {
883     final PsiClass containingClass = typeDefinition.getContainingClass();
884     if (containingClass != null) {
885       final String containingClassName = containingClass.getName();
886       if (containingClassName != null && containingClassName.equals(typeDefinition.getName())) {
887         holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
888                                      GroovyBundle.message("duplicate.inner.class", typeDefinition.getName()));
889       }
890     }
891     final String qName = typeDefinition.getQualifiedName();
892     if (qName != null) {
893       final PsiClass[] classes =
894         JavaPsiFacade.getInstance(typeDefinition.getProject()).findClasses(qName, typeDefinition.getResolveScope());
895       if (classes.length > 1) {
896         String packageName = getPackageName(typeDefinition);
897
898         if (!isScriptGeneratedClass(classes)) {
899           holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
900                                        GroovyBundle.message("duplicate.class", typeDefinition.getName(), packageName));
901         }
902         else {
903           holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
904                                        GroovyBundle.message("script.generated.with.same.name", qName));
905         }
906       }
907     }
908   }
909
910   private static String getPackageName(GrTypeDefinition typeDefinition) {
911     final PsiFile file = typeDefinition.getContainingFile();
912     String packageName = "<default package>";
913     if (file instanceof GroovyFile) {
914       final String name = ((GroovyFile)file).getPackageName();
915       if (name.length() > 0) packageName = name;
916     }
917     return packageName;
918   }
919
920   private static boolean isScriptGeneratedClass(PsiClass[] allClasses) {
921     return allClasses.length == 2 && (allClasses[0] instanceof GroovyScriptClass || allClasses[1] instanceof GroovyScriptClass);
922   }
923
924   private static void checkForExtendingInterface(AnnotationHolder holder,
925                                           GrExtendsClause extendsClause,
926                                           GrImplementsClause implementsClause,
927                                           GrTypeDefinition myClass) {
928     for (GrCodeReferenceElement ref : extendsClause.getReferenceElements()) {
929       final PsiElement clazz = ref.resolve();
930       if (clazz == null) continue;
931
932       if (myClass.isInterface() && clazz instanceof PsiClass && !((PsiClass)clazz).isInterface()) {
933         final Annotation annotation = holder.createErrorAnnotation(ref, GroovyBundle.message("class.is.not.expected.here"));
934         annotation.registerFix(new ChangeExtendsImplementsQuickFix(extendsClause, implementsClause));
935       }
936     }
937   }
938
939   private static void checkForImplementingClass(AnnotationHolder holder,
940                                          GrExtendsClause extendsClause,
941                                          GrImplementsClause implementsClause,
942                                          GrTypeDefinition myClass) {
943     if (myClass.isInterface()) {
944       final Annotation annotation =
945         holder.createErrorAnnotation(implementsClause, GroovyBundle.message("interface.cannot.contain.implements.clause"));
946       annotation.registerFix(new ChangeExtendsImplementsQuickFix(extendsClause, implementsClause));
947       return;
948     }
949
950     for (GrCodeReferenceElement ref : implementsClause.getReferenceElements()) {
951       final PsiElement clazz = ref.resolve();
952       if (clazz == null) continue;
953
954       if (!((PsiClass)clazz).isInterface()) {
955         final Annotation annotation = holder.createErrorAnnotation(ref, GroovyBundle.message("interface.expected.here"));
956         annotation.registerFix(new ChangeExtendsImplementsQuickFix(extendsClause, implementsClause));
957       }
958     }
959   }
960
961   private static void checkGrDocMemberReference(final GrDocMemberReference reference, AnnotationHolder holder) {
962     PsiElement resolved = reference.resolve();
963     if (resolved == null) {
964       Annotation annotation = holder.createErrorAnnotation(reference, GroovyBundle.message("cannot.resolve", reference.getReferenceName()));
965       annotation.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
966     }
967   }
968
969   private static void registerReferenceFixes(GrReferenceExpression refExpr, Annotation annotation) {
970     PsiClass targetClass = QuickfixUtil.findTargetClass(refExpr);
971     if (targetClass == null) return;
972
973     addDynamicAnnotation(annotation, refExpr);
974     if (targetClass instanceof GrMemberOwner) {
975       if (!(targetClass instanceof GroovyScriptClass)) {
976         annotation.registerFix(new CreateFieldFromUsageFix(refExpr, (GrMemberOwner)targetClass));
977       }
978
979       if (refExpr.getParent() instanceof GrCall && refExpr.getParent() instanceof GrExpression) {
980         annotation.registerFix(new CreateMethodFromUsageFix(refExpr, (GrMemberOwner)targetClass));
981       }
982     }
983
984     if (!refExpr.isQualified()) {
985       GrVariableDeclarationOwner owner = PsiTreeUtil.getParentOfType(refExpr, GrVariableDeclarationOwner.class);
986       if (!(owner instanceof GroovyFileBase) || ((GroovyFileBase)owner).isScript()) {
987         annotation.registerFix(new CreateLocalVariableFromUsageFix(refExpr, owner));
988       }
989     }
990   }
991
992   private static void addDynamicAnnotation(Annotation annotation, GrReferenceExpression referenceExpression) {
993     final PsiFile containingFile = referenceExpression.getContainingFile();
994     VirtualFile file;
995     if (containingFile != null) {
996       file = containingFile.getVirtualFile();
997       if (file == null) return;
998     }
999     else {
1000       return;
1001     }
1002
1003     if (QuickfixUtil.isCall(referenceExpression)) {
1004       annotation.registerFix(new DynamicMethodFix(referenceExpression), referenceExpression.getTextRange());
1005     }
1006     else {
1007       annotation.registerFix(new DynamicPropertyFix(referenceExpression), referenceExpression.getTextRange());
1008     }
1009   }
1010
1011   private static void highlightMemberResolved(AnnotationHolder holder, GrReferenceExpression refExpr, PsiMember member) {
1012     boolean isStatic = member.hasModifierProperty(GrModifier.STATIC);
1013     Annotation annotation = holder.createInfoAnnotation(refExpr.getReferenceNameElement(), null);
1014
1015     if (member instanceof PsiField ) {
1016       annotation.setTextAttributes(isStatic ? DefaultHighlighter.STATIC_FIELD : DefaultHighlighter.INSTANCE_FIELD);
1017       return;
1018     }
1019     if (member instanceof PsiMethod) {
1020       annotation.setTextAttributes(!isStatic ? DefaultHighlighter.METHOD_CALL : DefaultHighlighter.STATIC_METHOD_ACCESS);
1021     }
1022   }
1023
1024
1025   private static void registerUsedImport(GrReferenceElement referenceElement, GroovyResolveResult resolveResult) {
1026     GroovyPsiElement context = resolveResult.getCurrentFileResolveContext();
1027     if (context instanceof GrImportStatement) {
1028       PsiFile file = referenceElement.getContainingFile();
1029       if (file instanceof GroovyFile) {
1030         GroovyImportsTracker importsTracker = GroovyImportsTracker.getInstance(referenceElement.getProject());
1031         importsTracker.registerImportUsed((GrImportStatement)context);
1032       }
1033     }
1034   }
1035
1036   private static void checkMethodApplicability(GroovyResolveResult methodResolveResult, PsiElement place, AnnotationHolder holder) {
1037     final PsiElement element = methodResolveResult.getElement();
1038     if (!(element instanceof PsiMethod)) return;
1039     final PsiMethod method = (PsiMethod)element;
1040     PsiType[] argumentTypes = PsiUtil.getArgumentTypes(place, method.isConstructor(), true);
1041     if (argumentTypes != null &&
1042         !PsiUtil.isApplicable(argumentTypes, method, methodResolveResult.getSubstitutor(),
1043                               methodResolveResult.getCurrentFileResolveContext() instanceof GrMethodCallExpression)) {
1044       PsiElement elementToHighlight = PsiUtil.getArgumentsElement(place);
1045       if (elementToHighlight == null) {
1046         elementToHighlight = place;
1047       }
1048
1049       final String typesString = buildArgTypesList(argumentTypes);
1050       String message;
1051       final PsiClass containingClass = method.getContainingClass();
1052       if (containingClass != null) {
1053         final PsiClassType containingType = JavaPsiFacade.getInstance(method.getProject()).getElementFactory()
1054           .createType(containingClass, methodResolveResult.getSubstitutor());
1055         message = GroovyBundle.message("cannot.apply.method1", method.getName(), containingType.getInternalCanonicalText(), typesString);
1056       }
1057       else {
1058         message = GroovyBundle.message("cannot.apply.method.or.closure", method.getName(), typesString);
1059       }
1060       holder.createWarningAnnotation(elementToHighlight, message);
1061     }
1062   }
1063
1064   public static boolean isDeclarationAssignment(GrReferenceExpression refExpr) {
1065     if (isAssignmentLhs(refExpr)) {
1066       return isExpandoQualified(refExpr);
1067     }
1068     return false;
1069   }
1070
1071   private static boolean isAssignmentLhs(GrReferenceExpression refExpr) {
1072     return refExpr.getParent() instanceof GrAssignmentExpression &&
1073         refExpr.equals(((GrAssignmentExpression)refExpr.getParent()).getLValue());
1074   }
1075
1076   private static boolean isExpandoQualified(GrReferenceExpression refExpr) {
1077     final GrExpression qualifier = refExpr.getQualifierExpression();
1078     if (qualifier == null) {
1079       final PsiClass clazz = PsiTreeUtil.getParentOfType(refExpr, PsiClass.class);
1080       if (clazz == null) { //script
1081         return true;
1082       }
1083       return false; //in class, a property should normally be defined, so it's not a declaration
1084     }
1085
1086     final PsiType type = qualifier.getType();
1087     if (type instanceof PsiClassType) {
1088       final PsiClassType classType = (PsiClassType)type;
1089       final PsiClass psiClass = classType.resolve();
1090       if (psiClass instanceof GroovyScriptClass) {
1091         return true;
1092       }
1093     }
1094     return false;
1095   }
1096
1097   private static void checkSingleResolvedElement(AnnotationHolder holder, GrReferenceElement refElement, GroovyResolveResult resolveResult) {
1098     final PsiElement resolved = resolveResult.getElement();
1099     if (resolved == null) {
1100       String message = GroovyBundle.message("cannot.resolve", refElement.getReferenceName());
1101
1102       // Register quickfix
1103       final PsiElement nameElement = refElement.getReferenceNameElement();
1104       final PsiElement toHighlight = nameElement != null ? nameElement : refElement;
1105       final Annotation annotation = holder.createErrorAnnotation(toHighlight, message);
1106       // todo implement for nested classes
1107       if (refElement.getQualifier() == null) {
1108         registerCreateClassByTypeFix(refElement, annotation);
1109         registerAddImportFixes(refElement, annotation);
1110       }
1111       annotation.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
1112     }
1113     else if (!resolveResult.isAccessible()) {
1114       String message = GroovyBundle.message("cannot.access", refElement.getReferenceName());
1115       holder.createWarningAnnotation(refElement.getReferenceNameElement(), message);
1116     }
1117   }
1118
1119   private static void checkDefaultMapConstructor(AnnotationHolder holder,
1120                                                  GrArgumentList argList,
1121                                                  GroovyResolveResult constructorResolveResult) {
1122     if (argList != null) {
1123       final GrNamedArgument[] args = argList.getNamedArguments();
1124       for (GrNamedArgument arg : args) {
1125         final GrArgumentLabel label = arg.getLabel();
1126         if (label == null) continue;
1127         if (label.getName() == null) {
1128           final PsiElement nameElement = label.getNameElement();
1129           if (nameElement instanceof GrExpression) {
1130             final PsiType stringType =
1131               JavaPsiFacade.getElementFactory(arg.getProject()).createTypeFromText(CommonClassNames.JAVA_LANG_STRING, arg);
1132             if (!TypesUtil.isAssignable(stringType, ((GrExpression)nameElement).getType(), arg.getManager(), arg.getResolveScope())) {
1133               holder.createWarningAnnotation(nameElement, GroovyBundle.message("property.name.expected"));
1134             }
1135           }
1136           else {
1137             holder.createWarningAnnotation(nameElement, GroovyBundle.message("property.name.expected"));
1138           }
1139         }
1140         else {
1141           final PsiElement resolved = label.resolve();
1142           if (resolved == null) {
1143             final Annotation annotation = holder.createWarningAnnotation(label, GroovyBundle.message("no.such.property", label.getName()));
1144
1145             PsiElement element = constructorResolveResult.getElement();
1146             if (element instanceof PsiMember) {
1147               element = ((PsiMember)element).getContainingClass();
1148             }
1149             if (element instanceof GrMemberOwner) {
1150               annotation.registerFix(new CreateFieldFromConstructorLabelFix((GrMemberOwner)element, label.getNamedArgument()));
1151             }
1152             if (element instanceof PsiClass) {
1153               annotation.registerFix(new DynamicPropertyFix(label, (PsiClass)element));
1154             }
1155           }
1156         }
1157       }
1158     }
1159   }
1160
1161   private static void checkClosureApplicability(GroovyResolveResult resolveResult, PsiType type, PsiElement place, AnnotationHolder holder) {
1162     final PsiElement element = resolveResult.getElement();
1163     if (!(element instanceof GrVariable)) return;
1164     if (!(type instanceof GrClosureType)) return;
1165     final GrVariable variable = (GrVariable)element;
1166     PsiType[] argumentTypes = PsiUtil.getArgumentTypes(place, false, true);
1167     if (argumentTypes == null) return;
1168
1169     final PsiType[] paramTypes = PsiUtil.skipOptionalClosureParameters(argumentTypes.length, (GrClosureType)type);
1170     if (!areTypesCompatibleForCallingClosure(argumentTypes, paramTypes, place.getManager(), place.getResolveScope())) {
1171       final String typesString = buildArgTypesList(argumentTypes);
1172       String message = GroovyBundle.message("cannot.apply.method.or.closure", variable.getName(), typesString);
1173       PsiElement elementToHighlight = PsiUtil.getArgumentsElement(place);
1174       if (elementToHighlight == null) elementToHighlight = place;
1175       holder.createWarningAnnotation(elementToHighlight, message);
1176     }
1177   }
1178
1179   private static boolean areTypesCompatibleForCallingClosure(PsiType[] argumentTypes,
1180                                                       PsiType[] paramTypes,
1181                                                       PsiManager manager,
1182                                                       GlobalSearchScope resolveScope) {
1183     if (argumentTypes.length != paramTypes.length) return false;
1184     for (int i = 0; i < argumentTypes.length; i++) {
1185       final PsiType paramType = TypesUtil.boxPrimitiveType(paramTypes[i], manager, resolveScope);
1186       final PsiType argType = argumentTypes[i];
1187       if (!TypesUtil.isAssignableByMethodCallConversion(paramType, argType, manager, resolveScope)) return false;
1188     }
1189     return true;
1190   }
1191
1192   private static void registerAddImportFixes(GrReferenceElement refElement, Annotation annotation) {
1193     final String referenceName = refElement.getReferenceName();
1194     //noinspection ConstantConditions
1195     if (StringUtil.isEmpty(referenceName) || Character.isLowerCase(referenceName.charAt(0))) {
1196       return;
1197     }
1198
1199     annotation.registerFix(new GroovyAddImportAction(refElement));
1200   }
1201
1202   private static void registerCreateClassByTypeFix(GrReferenceElement refElement, Annotation annotation) {
1203     GrPackageDefinition packageDefinition = PsiTreeUtil.getParentOfType(refElement, GrPackageDefinition.class);
1204     if (packageDefinition == null && refElement.getQualifier() == null) {
1205       PsiElement parent = refElement.getParent();
1206       if (parent instanceof GrNewExpression) {
1207         annotation.registerFix(CreateClassFix.createClassFromNewAction((GrNewExpression)parent));
1208       }
1209       else {
1210         annotation.registerFix(CreateClassFix.createClassFixAction(refElement));
1211       }
1212     }
1213   }
1214
1215   private static void highlightMember(AnnotationHolder holder, GrMember member) {
1216     if (member instanceof GrField) {
1217       GrField field = (GrField)member;
1218       PsiElement identifier = field.getNameIdentifierGroovy();
1219       final boolean isStatic = field.hasModifierProperty(GrModifier.STATIC);
1220       holder.createInfoAnnotation(identifier, null).setTextAttributes(isStatic ? DefaultHighlighter.STATIC_FIELD : DefaultHighlighter.INSTANCE_FIELD);
1221     }
1222   }
1223
1224   private static void highlightAnnotation(AnnotationHolder holder, PsiElement refElement, GroovyResolveResult result) {
1225     PsiElement element = result.getElement();
1226     PsiElement parent = refElement.getParent();
1227     if (element instanceof PsiClass && ((PsiClass)element).isAnnotationType() && !(parent instanceof GrImportStatement)) {
1228       Annotation annotation = holder.createInfoAnnotation(parent, null);
1229       annotation.setTextAttributes(DefaultHighlighter.ANNOTATION);
1230       GroovyPsiElement context = result.getCurrentFileResolveContext();
1231       if (context instanceof GrImportStatement) {
1232         annotation = holder.createInfoAnnotation(((GrImportStatement)context).getImportReference(), null);
1233         annotation.setTextAttributes(DefaultHighlighter.ANNOTATION);
1234       }
1235     }
1236
1237   }
1238
1239
1240   private static String buildArgTypesList(PsiType[] argTypes) {
1241     StringBuilder builder = new StringBuilder();
1242     builder.append("(");
1243     for (int i = 0; i < argTypes.length; i++) {
1244       if (i > 0) {
1245         builder.append(", ");
1246       }
1247       PsiType argType = argTypes[i];
1248       builder.append(argType != null ? argType.getInternalCanonicalText() : "?");
1249     }
1250     builder.append(")");
1251     return builder.toString();
1252   }
1253
1254   private static class DuplicateVariablesProcessor extends PropertyResolverProcessor {
1255     boolean borderPassed;
1256
1257     public DuplicateVariablesProcessor(GrVariable variable) {
1258       super(variable.getName(), variable);
1259       borderPassed = false;
1260     }
1261
1262     @Override
1263     public boolean execute(PsiElement element, ResolveState state) {
1264       if (borderPassed) {
1265         return false;
1266       }
1267       return super.execute(element, state);
1268     }
1269
1270     @Override
1271     public void handleEvent(Event event, Object associated) {
1272       if (event == ResolveUtil.DECLARATION_SCOPE_PASSED) {
1273         borderPassed = true;
1274       }
1275       super.handleEvent(event, associated);
1276     }
1277   }
1278 }
1279