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