2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.jetbrains.plugins.groovy.annotator;
19 import com.intellij.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;
86 public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
87 private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.annotator.GroovyAnnotator");
89 private AnnotationHolder myHolder;
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;
97 public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
98 if (element instanceof GroovyPsiElement) {
100 ((GroovyPsiElement)element).accept(this);
106 public void visitElement(GroovyPsiElement element) {
107 if (element.getParent() instanceof GrDocReferenceElement) {
108 checkGrDocReferenceElement(myHolder, element);
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());
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) {
130 if (parent instanceof GrImportStatement && ((GrImportStatement)parent).isStatic() && refElement.multiResolve(false).length > 0) {
134 checkSingleResolvedElement(myHolder, refElement, resolveResult);
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);
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));
153 if (!resolveResult.isAccessible()) {
154 String message = GroovyBundle.message("cannot.access", referenceExpression.getReferenceName());
155 myHolder.createWarningAnnotation(referenceExpression.getReferenceNameElement(), message);
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()));
164 GrExpression qualifier = referenceExpression.getQualifierExpression();
165 if (qualifier == null && isDeclarationAssignment(referenceExpression)) return;
167 if (parent instanceof GrReferenceExpression && "class".equals(((GrReferenceExpression)parent).getReferenceName())) {
168 checkSingleResolvedElement(myHolder, referenceExpression, resolveResult);
172 if (parent instanceof GrCall) {
173 if (resolved == null && results.length > 0) {
174 resolved = results[0].getElement();
175 resolveResult = results[0];
177 if (resolved instanceof PsiMethod && resolved.getUserData(GrMethod.BUILDER_METHOD) == null) {
178 checkMethodApplicability(resolveResult, referenceExpression, myHolder);
181 checkClosureApplicability(resolveResult, referenceExpression.getType(), referenceExpression, myHolder);
184 if (isDeclarationAssignment(referenceExpression) || resolved instanceof PsiPackage) return;
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);
197 registerStaticImportFix(referenceExpression, annotation);
201 if (qualifier.getType() == null) {
205 registerReferenceFixes(referenceExpression, annotation);
206 annotation.setTextAttributes(DefaultHighlighter.UNRESOLVED_ACCESS);
210 private static void registerStaticImportFix(GrReferenceExpression referenceExpression, Annotation annotation) {
211 final String referenceName = referenceExpression.getReferenceName();
212 //noinspection ConstantConditions
213 if (StringUtil.isEmpty(referenceName)) {
217 annotation.registerFix(new GroovyStaticImportMethodFix((GrCall)referenceExpression.getParent()));
221 public void visitTypeDefinition(GrTypeDefinition typeDefinition) {
222 checkTypeDefinition(myHolder, typeDefinition);
223 checkTypeDefinitionModifiers(myHolder, typeDefinition);
225 final GrTypeDefinitionBody body = typeDefinition.getBody();
226 if (body != null) checkDuplicateMethod(body.getGroovyMethods(), myHolder);
227 checkImplementedMethodsOfClass(myHolder, typeDefinition);
231 public void visitMethod(GrMethod method) {
232 checkMethodDefinitionModifiers(myHolder, method);
233 checkInnerMethod(myHolder, method);
237 public void visitVariableDeclaration(GrVariableDeclaration variableDeclaration) {
239 PsiElement parent = variableDeclaration.getParent();
240 assert parent != null;
242 PsiElement typeDef = parent.getParent();
243 if (typeDef != null && typeDef instanceof GrTypeDefinition) {
244 PsiModifierList modifiersList = variableDeclaration.getModifierList();
245 checkAccessModifiers(myHolder, modifiersList);
247 if (modifiersList.hasExplicitModifier(GrModifier.VOLATILE) && modifiersList.hasExplicitModifier(GrModifier.FINAL)) {
248 myHolder.createErrorAnnotation(modifiersList, GroovyBundle.message("illegal.combination.of.modifiers.volatile.and.final"));
251 if (modifiersList.hasExplicitModifier(GrModifier.NATIVE)) {
252 myHolder.createErrorAnnotation(modifiersList, GroovyBundle.message("variable.cannot.be.native"));
255 if (modifiersList.hasExplicitModifier(GrModifier.ABSTRACT)) {
256 myHolder.createErrorAnnotation(modifiersList, GroovyBundle.message("variable.cannot.be.abstract"));
262 public void visitVariable(GrVariable variable) {
263 if (variable instanceof GrMember) {
264 highlightMember(myHolder, ((GrMember)variable));
265 checkStaticDeclarationsInInnerClass((GrMember)variable, myHolder);
267 PropertyResolverProcessor processor = new DuplicateVariablesProcessor(variable);
268 final GroovyPsiElement duplicate =
269 ResolveUtil.resolveExistingElement(variable, processor, GrVariable.class, GrReferenceExpression.class);
271 if (duplicate instanceof GrVariable) {
272 if (duplicate instanceof GrField && !(variable instanceof GrField)) {
274 .createWarningAnnotation(variable.getNameIdentifierGroovy(), GroovyBundle.message("field.already.defined", variable.getName()));
277 final String key = duplicate instanceof GrField ? "field.already.defined" : "variable.already.defined";
278 myHolder.createErrorAnnotation(variable.getNameIdentifierGroovy(), GroovyBundle.message(key, variable.getName()));
284 public void visitAssignmentExpression(GrAssignmentExpression expression) {
285 GrExpression lValue = expression.getLValue();
286 if (!PsiUtil.mightBeLVlaue(lValue)) {
287 myHolder.createErrorAnnotation(lValue, GroovyBundle.message("invalid.lvalue"));
292 public void visitReturnStatement(GrReturnStatement returnStatement) {
293 final GrExpression value = returnStatement.getReturnValue();
295 final PsiType type = value.getType();
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"));
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"));
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();
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;
333 final String name1 = label1.getName();
334 final String name2 = label2.getName();
335 if (name1 == null || name2 == null) {
336 return name1 == null && name2 == null;
338 return name1.equals(name2);
342 processDuplicates(map, myHolder);
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);
362 if (newExpression.getQualifier() != null) {
363 if (clazz.hasModifierProperty(GrModifier.STATIC)) {
364 myHolder.createErrorAnnotation(newExpression, GroovyBundle.message("qualified.new.of.static.class"));
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()));
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);
381 final GroovyResolveResult[] results = newExpression.multiResolveConstructor();
382 final GrArgumentList argList = newExpression.getArgumentList();
383 PsiElement toHighlight = argList != null ? argList : refElement.getReferenceNameElement();
385 if (results.length > 0) {
386 String message = GroovyBundle.message("ambiguous.constructor.call");
387 myHolder.createWarningAnnotation(toHighlight, message);
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);
397 else checkDefaultMapConstructor(myHolder, argList, constructorResolveResult);
404 public void visitDocMethodReference(GrDocMethodReference reference) {
405 checkGrDocMemberReference(reference, myHolder);
409 public void visitDocFieldReference(GrDocFieldReference reference) {
410 checkGrDocMemberReference(reference, myHolder);
414 public void visitConstructorInvocation(GrConstructorInvocation invocation) {
415 final GroovyResolveResult resolveResult = invocation.resolveConstructorGenerics();
416 if (resolveResult != null) {
417 checkMethodApplicability(resolveResult, invocation.getThisOrSuperKeyword(), myHolder);
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);
427 final PsiClass clazz = invocation.getDelegatedClass();
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);
441 public void visitBreakStatement(GrBreakStatement breakStatement) {
442 checkFlowInterruptStatement(breakStatement, myHolder);
446 public void visitContinueStatement(GrContinueStatement continueStatement) {
447 checkFlowInterruptStatement(continueStatement, myHolder);
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));
459 public void visitPackageDefinition(GrPackageDefinition packageDefinition) {
460 //todo: if reference isn't resolved it construct package definition
461 final PsiFile file = packageDefinition.getContainingFile();
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));
475 final GrModifierList modifierList = packageDefinition.getAnnotationList();
476 checkAnnotationList(myHolder, modifierList, GroovyBundle.message("package.definition.cannot.have.modifiers"));
480 public void visitSuperExpression(GrSuperReferenceExpression superExpression) {
481 checkThisOrSuperReferenceExpression(superExpression, myHolder);
485 public void visitThisExpression(GrThisReferenceExpression thisExpression) {
486 checkThisOrSuperReferenceExpression(thisExpression, myHolder);
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"));
497 else if (text.startsWith("'")) {
498 if (text.length() < 2 || !text.endsWith("'")) {
499 myHolder.createErrorAnnotation(literal, GroovyBundle.message("string.end.expected"));
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));
522 public void visitFile(GroovyFileBase file) {
523 if (!file.isScript()) return;
525 List<GrMethod> methods = new ArrayList<GrMethod>();
527 for (GrTopLevelDefintion topLevelDefinition : file.getTopLevelDefinitions()) {
528 if (topLevelDefinition instanceof GrMethod) {
529 methods.add(((GrMethod)topLevelDefinition));
533 checkDuplicateMethod(methods.toArray(new GrMethod[methods.size()]), myHolder);
537 public void visitImportStatement(GrImportStatement importStatement) {
538 checkAnnotationList(myHolder, importStatement.getAnnotationList(), GroovyBundle.message("import.statement.cannot.have.modifiers"));
541 private static void checkFlowInterruptStatement(GrFlowInterruptingStatement statement, AnnotationHolder holder) {
542 final PsiElement label = statement.getLabelIdentifier();
545 final GrLabeledStatement resolved = statement.resolveLabel();
546 if (resolved == null) {
547 holder.createErrorAnnotation(label, GroovyBundle.message("undefined.label", statement.getLabelName()));
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"));
556 else if (statement instanceof GrBreakStatement && label == null) {
557 holder.createErrorAnnotation(statement, GroovyBundle.message("break.outside.loop.or.switch"));
560 if (statement instanceof GrBreakStatement && label != null && findFirstLoop(statement) == null) {
561 holder.createErrorAnnotation(statement, GroovyBundle.message("break.outside.loop"));
566 private static GrLoopStatement findFirstLoop(GrFlowInterruptingStatement statement) {
567 return PsiTreeUtil.getParentOfType(statement, GrLoopStatement.class, true, GrClosableBlock.class, GrMember.class, GroovyFile.class);
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()));
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()));
588 holder.createErrorAnnotation(expression, GroovyBundle.message("is.not.enclosing.class", ((PsiClass)resolved).getQualifiedName()));
592 holder.createErrorAnnotation(qualifier, GroovyBundle.message("unknown.class", qualifier.getText()));
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"));
611 private static PsiElement findModifierStatic(GrMember grMember) {
612 final GrModifierList list = grMember.getModifierList();
617 for (PsiElement modifier : list.getModifiers()) {
618 if (GrModifier.STATIC.equals(modifier.getText())) {
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);
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);
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;
648 Collection<HierarchicalMethodSignature> allMethods = typeDefinition.getVisibleSignatures();
649 PsiMethod abstractMethod = ClassUtil.getAnyAbstractMethod(typeDefinition, allMethods);
651 if (abstractMethod == null) return;
653 String notImplementedMethodName = abstractMethod.getName();
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);
662 private static void registerImplementsMethodsFix(GrTypeDefinition typeDefinition, Annotation annotation) {
663 annotation.registerFix(new ImplementMethodsQuickFix(typeDefinition));
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"));
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"));
682 private static void checkMethodDefinitionModifiers(AnnotationHolder holder, GrMethod method) {
683 final PsiModifierList modifiersList = method.getModifierList();
684 checkAccessModifiers(holder, modifiersList);
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"));
694 if (modifiersList.hasExplicitModifier(GrModifier.NATIVE)) {
695 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("script.cannot.have.modifier.native"));
698 else //type definition methods
699 if (method.getParent() != null && method.getParent().getParent() instanceof GrTypeDefinition) {
700 GrTypeDefinition containingTypeDef = ((GrTypeDefinition)method.getParent().getParent());
703 if (containingTypeDef.isInterface()) {
704 if (isMethodStatic) {
705 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("interface.must.have.no.static.method"));
708 if (modifiersList.hasExplicitModifier(GrModifier.PRIVATE)) {
709 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("interface.must.have.no.private.method"));
713 else if (containingTypeDef.isEnum()) {
717 else if (containingTypeDef.isAnnotationType()) {
721 else if (containingTypeDef.isAnonymous()) {
723 if (isMethodStatic) {
724 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("static.declaration.in.inner.class"));
726 if (method.isConstructor()) {
727 holder.createErrorAnnotation(method.getNameIdentifierGroovy(),
728 GroovyBundle.message("constructors.are.not.allowed.in.anonymous.class"));
730 if (isMethodAbstract) {
731 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("not.abstract.class.cannot.have.abstract.method"));
736 PsiModifierList typeDefModifiersList = containingTypeDef.getModifierList();
737 LOG.assertTrue(typeDefModifiersList != null, "modifiers list must be not null");
739 if (!typeDefModifiersList.hasExplicitModifier(GrModifier.ABSTRACT)) {
740 if (isMethodAbstract) {
741 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("not.abstract.class.cannot.have.abstract.method"));
745 if (!isMethodAbstract) {
746 if (method.getBlock() == null) {
747 holder.createErrorAnnotation(method.getNameIdentifierGroovy(), GroovyBundle.message("not.abstract.method.should.have.body"));
750 if (isMethodStatic) {
751 checkStaticDeclarationsInInnerClass(method, holder);
757 private static void checkTypeDefinitionModifiers(AnnotationHolder holder, GrTypeDefinition typeDefinition) {
758 PsiModifierList modifiersList = typeDefinition.getModifierList();
760 if (modifiersList == null) return;
763 checkAccessModifiers(holder, modifiersList);
765 PsiClassType[] extendsListTypes = typeDefinition.getExtendsListTypes();
767 for (PsiClassType classType : extendsListTypes) {
768 PsiClass psiClass = classType.resolve();
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"));
780 if (modifiersList.hasExplicitModifier(GrModifier.ABSTRACT) && modifiersList.hasExplicitModifier(GrModifier.FINAL)) {
781 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("illegal.combination.of.modifiers.abstract.and.final"));
784 if (modifiersList.hasExplicitModifier(GrModifier.TRANSIENT)) {
785 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.transient.not.allowed.here"));
787 if (modifiersList.hasExplicitModifier(GrModifier.VOLATILE)) {
788 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.volatile.not.allowed.here"));
791 /**** interface ****/
792 if (typeDefinition.isInterface()) {
793 if (modifiersList.hasExplicitModifier(GrModifier.FINAL)) {
794 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("intarface.cannot.have.modifier.final"));
797 if (modifiersList.hasExplicitModifier(GrModifier.VOLATILE)) {
798 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.volatile.not.allowed.here"));
801 if (modifiersList.hasExplicitModifier(GrModifier.TRANSIENT)) {
802 holder.createErrorAnnotation(modifiersList, GroovyBundle.message("modifier.transient.not.allowed.here"));
806 checkStaticDeclarationsInInnerClass(typeDefinition, holder);
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);
814 if (hasPrivate && hasPublic || hasPrivate && hasProtected || hasPublic && hasProtected) {
815 holder.createErrorAnnotation(modifierList, GroovyBundle.message("illegal.combination.of.modifiers"));
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();
825 public boolean equals(GrMethod method1, GrMethod method2) {
826 return method1.getSignature(PsiSubstitutor.EMPTY).equals(method2.getSignature(PsiSubstitutor.EMPTY));
829 processMethodDuplicates(map, holder);
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>();
836 DuplicatesUtil.collectMethodDuplicates(map, duplicateMethodsWarning, duplicateMethodsErrors);
838 for (GrMethod duplicateMethod : duplicateMethodsErrors) {
839 holder.createErrorAnnotation(duplicateMethod.getNameIdentifierGroovy(),
840 GroovyBundle.message("repetitive.method.name.signature.and.return.type"));
843 for (GrMethod duplicateMethod : duplicateMethodsWarning) {
844 holder.createWarningAnnotation(duplicateMethod.getNameIdentifierGroovy(), GroovyBundle.message("repetitive.method.name.signature"));
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);
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)));
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)));
868 final GrImplementsClause implementsClause = typeDefinition.getImplementsClause();
869 final GrExtendsClause extendsClause = typeDefinition.getExtendsClause();
871 if (implementsClause != null) {
872 checkForImplementingClass(holder, extendsClause, implementsClause, ((GrTypeDefinition)implementsClause.getParent()));
875 if (extendsClause != null) {
876 checkForExtendingInterface(holder, extendsClause, implementsClause, ((GrTypeDefinition)extendsClause.getParent()));
879 checkDuplicateClass(typeDefinition, holder);
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()));
891 final String qName = typeDefinition.getQualifiedName();
893 final PsiClass[] classes =
894 JavaPsiFacade.getInstance(typeDefinition.getProject()).findClasses(qName, typeDefinition.getResolveScope());
895 if (classes.length > 1) {
896 String packageName = getPackageName(typeDefinition);
898 if (!isScriptGeneratedClass(classes)) {
899 holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
900 GroovyBundle.message("duplicate.class", typeDefinition.getName(), packageName));
903 holder.createErrorAnnotation(typeDefinition.getNameIdentifierGroovy(),
904 GroovyBundle.message("script.generated.with.same.name", qName));
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;
920 private static boolean isScriptGeneratedClass(PsiClass[] allClasses) {
921 return allClasses.length == 2 && (allClasses[0] instanceof GroovyScriptClass || allClasses[1] instanceof GroovyScriptClass);
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;
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));
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));
950 for (GrCodeReferenceElement ref : implementsClause.getReferenceElements()) {
951 final PsiElement clazz = ref.resolve();
952 if (clazz == null) continue;
954 if (!((PsiClass)clazz).isInterface()) {
955 final Annotation annotation = holder.createErrorAnnotation(ref, GroovyBundle.message("interface.expected.here"));
956 annotation.registerFix(new ChangeExtendsImplementsQuickFix(extendsClause, implementsClause));
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);
969 private static void registerReferenceFixes(GrReferenceExpression refExpr, Annotation annotation) {
970 PsiClass targetClass = QuickfixUtil.findTargetClass(refExpr);
971 if (targetClass == null) return;
973 addDynamicAnnotation(annotation, refExpr);
974 if (targetClass instanceof GrMemberOwner) {
975 if (!(targetClass instanceof GroovyScriptClass)) {
976 annotation.registerFix(new CreateFieldFromUsageFix(refExpr, (GrMemberOwner)targetClass));
979 if (refExpr.getParent() instanceof GrCall && refExpr.getParent() instanceof GrExpression) {
980 annotation.registerFix(new CreateMethodFromUsageFix(refExpr, (GrMemberOwner)targetClass));
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));
992 private static void addDynamicAnnotation(Annotation annotation, GrReferenceExpression referenceExpression) {
993 final PsiFile containingFile = referenceExpression.getContainingFile();
995 if (containingFile != null) {
996 file = containingFile.getVirtualFile();
997 if (file == null) return;
1003 if (QuickfixUtil.isCall(referenceExpression)) {
1004 annotation.registerFix(new DynamicMethodFix(referenceExpression), referenceExpression.getTextRange());
1007 annotation.registerFix(new DynamicPropertyFix(referenceExpression), referenceExpression.getTextRange());
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);
1015 if (member instanceof PsiField ) {
1016 annotation.setTextAttributes(isStatic ? DefaultHighlighter.STATIC_FIELD : DefaultHighlighter.INSTANCE_FIELD);
1019 if (member instanceof PsiMethod) {
1020 annotation.setTextAttributes(!isStatic ? DefaultHighlighter.METHOD_CALL : DefaultHighlighter.STATIC_METHOD_ACCESS);
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);
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;
1049 final String typesString = buildArgTypesList(argumentTypes);
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);
1058 message = GroovyBundle.message("cannot.apply.method.or.closure", method.getName(), typesString);
1060 holder.createWarningAnnotation(elementToHighlight, message);
1064 public static boolean isDeclarationAssignment(GrReferenceExpression refExpr) {
1065 if (isAssignmentLhs(refExpr)) {
1066 return isExpandoQualified(refExpr);
1071 private static boolean isAssignmentLhs(GrReferenceExpression refExpr) {
1072 return refExpr.getParent() instanceof GrAssignmentExpression &&
1073 refExpr.equals(((GrAssignmentExpression)refExpr.getParent()).getLValue());
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
1083 return false; //in class, a property should normally be defined, so it's not a declaration
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) {
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());
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);
1111 annotation.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
1113 else if (!resolveResult.isAccessible()) {
1114 String message = GroovyBundle.message("cannot.access", refElement.getReferenceName());
1115 holder.createWarningAnnotation(refElement.getReferenceNameElement(), message);
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"));
1137 holder.createWarningAnnotation(nameElement, GroovyBundle.message("property.name.expected"));
1141 final PsiElement resolved = label.resolve();
1142 if (resolved == null) {
1143 final Annotation annotation = holder.createWarningAnnotation(label, GroovyBundle.message("no.such.property", label.getName()));
1145 PsiElement element = constructorResolveResult.getElement();
1146 if (element instanceof PsiMember) {
1147 element = ((PsiMember)element).getContainingClass();
1149 if (element instanceof GrMemberOwner) {
1150 annotation.registerFix(new CreateFieldFromConstructorLabelFix((GrMemberOwner)element, label.getNamedArgument()));
1152 if (element instanceof PsiClass) {
1153 annotation.registerFix(new DynamicPropertyFix(label, (PsiClass)element));
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;
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);
1179 private static boolean areTypesCompatibleForCallingClosure(PsiType[] argumentTypes,
1180 PsiType[] paramTypes,
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;
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))) {
1199 annotation.registerFix(new GroovyAddImportAction(refElement));
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));
1210 annotation.registerFix(CreateClassFix.createClassFixAction(refElement));
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);
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);
1240 private static String buildArgTypesList(PsiType[] argTypes) {
1241 StringBuilder builder = new StringBuilder();
1242 builder.append("(");
1243 for (int i = 0; i < argTypes.length; i++) {
1245 builder.append(", ");
1247 PsiType argType = argTypes[i];
1248 builder.append(argType != null ? argType.getInternalCanonicalText() : "?");
1250 builder.append(")");
1251 return builder.toString();
1254 private static class DuplicateVariablesProcessor extends PropertyResolverProcessor {
1255 boolean borderPassed;
1257 public DuplicateVariablesProcessor(GrVariable variable) {
1258 super(variable.getName(), variable);
1259 borderPassed = false;
1263 public boolean execute(PsiElement element, ResolveState state) {
1267 return super.execute(element, state);
1271 public void handleEvent(Event event, Object associated) {
1272 if (event == ResolveUtil.DECLARATION_SCOPE_PASSED) {
1273 borderPassed = true;
1275 super.handleEvent(event, associated);