diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index 25588db24afb..8d15fc37c8a8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -62,6 +62,7 @@ import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicMethodFi import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicPropertyFix; import org.jetbrains.plugins.groovy.config.GroovyConfigUtils; import org.jetbrains.plugins.groovy.debugger.fragments.GroovyCodeFragment; +import org.jetbrains.plugins.groovy.dsl.toplevel.AnnotatedContextFilter; import org.jetbrains.plugins.groovy.extensions.GroovyUnresolvedHighlightFilter; import org.jetbrains.plugins.groovy.highlighter.DefaultHighlighter; import org.jetbrains.plugins.groovy.lang.documentation.GroovyPresentationUtil; @@ -101,12 +102,14 @@ import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatem import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.types.*; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner; +import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; import org.jetbrains.plugins.groovy.lang.psi.impl.TypeInferenceHelper; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass; import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil; +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; @@ -324,6 +327,7 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { PsiElement resolved = resolveResult.getElement(); final PsiElement parent = referenceExpression.getParent(); + if (resolved != null) { if (resolved instanceof PsiMember) { highlightMemberResolved(myHolder, referenceExpression, ((PsiMember)resolved)); @@ -337,7 +341,7 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { Annotation annotation = myHolder.createInfoAnnotation(referenceExpression, GroovyBundle.message("cannot.reference.nonstatic", referenceExpression.getReferenceName())); - annotation.setTextAttributes(DefaultHighlighter.UNRESOLVED_ACCESS); + annotation.setTextAttributes(isStaticallyCompiled(referenceExpression)?DefaultHighlighter.BAD_CHARACTER:DefaultHighlighter.UNRESOLVED_ACCESS); } } } @@ -377,8 +381,22 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { PsiElement refNameElement = referenceExpression.getReferenceNameElement(); PsiElement elt = refNameElement == null ? referenceExpression : refNameElement; - Annotation annotation = myHolder.createInfoAnnotation(elt, null); final GrExpression qualifier = referenceExpression.getQualifierExpression(); + + Annotation annotation; + + boolean compileStatic = isCompileStatic(referenceExpression); + if (compileStatic) { + annotation = myHolder.createInfoAnnotation(elt, GroovyBundle.message("cannot.resolve", referenceExpression.getReferenceName())); + annotation.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL); + } + else { + if (qualifier != null && qualifier.getType() == null) return; + + annotation = myHolder.createInfoAnnotation(elt, null); + annotation.setTextAttributes(DefaultHighlighter.UNRESOLVED_ACCESS); + } + if (qualifier == null) { if (parent instanceof GrMethodCall) { registerStaticImportFix(referenceExpression, annotation); @@ -388,20 +406,22 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { registerAddImportFixes(referenceExpression, annotation); } } - else { - if (qualifier.getType() == null) { - return; - } - } - registerReferenceFixes(referenceExpression, annotation); + registerReferenceFixes(referenceExpression, annotation, compileStatic); UnresolvedReferenceQuickFixProvider.registerReferenceFixes(referenceExpression, new QuickFixActionRegistrarAdapter(annotation)); OrderEntryFix.registerFixes(new QuickFixActionRegistrarAdapter(annotation), referenceExpression); - - annotation.setTextAttributes(DefaultHighlighter.UNRESOLVED_ACCESS); } } + private static boolean isCompileStatic(GrReferenceExpression referenceExpression) { + PsiMember containingMember = PsiTreeUtil.getParentOfType(referenceExpression, PsiMember.class); + return containingMember != null && GroovyPsiManager.getInstance(containingMember.getProject()).isCompileStatic(containingMember); + } + + private static boolean isStaticallyCompiled(GrReferenceExpression referenceExpression) { + return AnnotatedContextFilter.findContextAnnotation(referenceExpression, GroovyCommonClassNames.GROOVY_TRANSFORM_COMPILE_STATIC)!=null; + } + private void highlightVariable(GrVariable variable, PsiElement toHighlight) { Annotation annotation = myHolder.createInfoAnnotation(toHighlight, null); boolean reassigned = isReassigned(variable); @@ -1772,11 +1792,13 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { } - private static void registerReferenceFixes(GrReferenceExpression refExpr, Annotation annotation) { - PsiClass targetClass = QuickfixUtil.findTargetClass(refExpr); + private static void registerReferenceFixes(GrReferenceExpression refExpr, Annotation annotation, boolean compileStatic) { + PsiClass targetClass = QuickfixUtil.findTargetClass(refExpr, compileStatic); if (targetClass == null) return; - addDynamicAnnotation(annotation, refExpr); + if (!compileStatic) { + addDynamicAnnotation(annotation, refExpr); + } if (targetClass.isWritable()) { if (!(targetClass instanceof GroovyScriptClass)) { if (targetClass instanceof GrMemberOwner) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/QuickfixUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/QuickfixUtil.java index 0bdf81f41459..e64cd56068b1 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/QuickfixUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/QuickfixUtil.java @@ -26,7 +26,6 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.SuggestedNameInfo; import com.intellij.psi.codeStyle.VariableKind; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTypesUtil; import com.intellij.util.ArrayUtil; import gnu.trove.THashSet; @@ -34,13 +33,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.ParamInfo; import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.ui.DynamicElementSettings; -import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrReferenceResolveUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; @@ -53,22 +52,13 @@ import java.util.*; */ public class QuickfixUtil { @Nullable - public static PsiClass findTargetClass(GrReferenceExpression refExpr) { - final PsiClass psiClass; - if (refExpr.isQualified()) { - GrExpression qualifier = refExpr.getQualifierExpression(); - PsiType type = qualifier.getType(); - if (!(type instanceof PsiClassType)) return null; - - psiClass = ((PsiClassType)type).resolve(); - } else { - GroovyPsiElement context = PsiTreeUtil.getParentOfType(refExpr, GrTypeDefinition.class, GroovyFileBase.class); - if (context instanceof GrTypeDefinition) { - return (PsiClass)context; - } else if (context instanceof GroovyFileBase) return ((GroovyFileBase)context).getScriptClass(); - return null; + public static PsiClass findTargetClass(GrReferenceExpression refExpr, boolean compileStatic) { + PsiType type = GrReferenceResolveUtil.getQualifierType(refExpr); + if (type == null && compileStatic) { + return GroovyPsiManager.getInstance(refExpr.getProject()).findClassWithCache(CommonClassNames.JAVA_LANG_OBJECT, refExpr.getResolveScope()); } - return psiClass; + if (!(type instanceof PsiClassType)) return null; + return ((PsiClassType)type).resolve(); } public static boolean isStaticCall(GrReferenceExpression refExpr) { @@ -184,7 +174,7 @@ public class QuickfixUtil { public static DynamicElementSettings createSettings(GrReferenceExpression referenceExpression) { DynamicElementSettings settings = new DynamicElementSettings(); - final PsiClass containingClass = findTargetClass(referenceExpression); + final PsiClass containingClass = findTargetClass(referenceExpression, false); assert containingClass != null; String className = containingClass.getQualifiedName(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java index c6e9c8cfe03e..f14ad91f7f9c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java @@ -65,6 +65,7 @@ public class GroovyPsiManager { private final ConcurrentMap myCalculatedTypes = new ConcurrentWeakHashMap(); private final ConcurrentMap>> myClassCache = new ConcurrentHashMap>>(); + private final ConcurrentMap myCompileStatic = new ConcurrentHashMap(); private static final RecursionGuard ourGuard = RecursionManager.createGuard("groovyPsiManager"); @@ -92,6 +93,7 @@ public class GroovyPsiManager { public void dropTypesCache() { myCalculatedTypes.clear(); + myCompileStatic.clear(); } public static boolean isInheritorCached(@Nullable PsiClass aClass, @NotNull String baseClassName) { @@ -122,6 +124,25 @@ public class GroovyPsiManager { return JavaPsiFacade.getElementFactory(myProject).createTypeByFQClassName(fqName, resolveScope); } + public boolean isCompileStatic(PsiMember member) { + Boolean aBoolean = myCompileStatic.get(member); + if (aBoolean == null) { + aBoolean = ConcurrencyUtil.cacheOrGet(myCompileStatic, member, isCompileStaticInner(member)); + } + return aBoolean; + } + + private boolean isCompileStaticInner(PsiMember member) { + PsiModifierList list = member.getModifierList(); + if (list != null) { + PsiAnnotation annotation = list.findAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_COMPILE_STATIC); + if (annotation != null) return true; + } + PsiClass aClass = member.getContainingClass(); + if (aClass != null) return isCompileStatic(aClass); + return false; + } + @Nullable public PsiClass findClassWithCache(String fqName, GlobalSearchScope resolveScope) { SoftReference> reference = myClassCache.get(fqName); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java index 3af8dccbb9b1..f6e040e36eff 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java @@ -50,7 +50,8 @@ public final class GroovyCommonClassNames { @NonNls public static final String GROOVY_LANG_USE = "groovy.lang.Use"; @NonNls public static final String GROOVY_LANG_MIXIN = "groovy.lang.Mixin"; @NonNls public static final String GROOVY_UTIL_TEST_CASE = "groovy.util.GroovyTestCase"; - public static final String GROOVY_LANG_SINGLETON = "groovy.lang.Singleton"; + @NonNls public static final String GROOVY_LANG_SINGLETON = "groovy.lang.Singleton"; + @NonNls public static final String GROOVY_TRANSFORM_COMPILE_STATIC = "groovy.transform.CompileStatic"; private GroovyCommonClassNames() { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy index 704262585a0d..2a0f9a818d25 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy @@ -1009,4 +1009,28 @@ use(Ca) { ''', GroovyAssignabilityCheckInspection) } + void testCompileStatic() { + myFixture.addClass('''\ +package groovy.transform; +public @interface CompileStatic { +}''') + + myFixture.configureByText('_.groovy', '''\ +import groovy.transform.CompileStatic + +class A { + +def foo() { +print abc +} + +@CompileStatic +def bar() { +print abc +} +} +''') + myFixture.testHighlighting(true, true, true) + } + } \ No newline at end of file