[groovy] unify handling of class literals

This commit is contained in:
Daniil Ovchinnikov
2016-03-24 18:36:13 +03:00
parent 0142864da7
commit b6be69e4a9
15 changed files with 164 additions and 82 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,6 +54,14 @@ public class QuickfixUtil {
if (type == null && compileStatic) {
return GroovyPsiManager.getInstance(refExpr.getProject()).findClassWithCache(CommonClassNames.JAVA_LANG_OBJECT, refExpr.getResolveScope());
}
if (ResolveUtil.resolvesToClass(refExpr.getQualifierExpression())) {
PsiType classType = ResolveUtil.unwrapClassType(type);
if (classType != null) {
type = classType;
}
}
if (!(type instanceof PsiClassType)) return null;
return ((PsiClassType)type).resolve();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -274,12 +274,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
}
return factory.createType((PsiClass)resolved);
}
if (getParent() instanceof GrReferenceExpression) {
return factory.createType((PsiClass)resolved);
}
else {
return TypesUtil.createJavaLangClassType(factory.createType((PsiClass)resolved), getProject(), getResolveScope());
}
return TypesUtil.createJavaLangClassType(factory.createType((PsiClass)resolved), getProject(), getResolveScope());
}
if (resolved instanceof GrVariable) {
@@ -387,7 +382,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
if (ResolveUtil.isClassReference(refExpr)) {
GrExpression qualifier = refExpr.getQualifier();
LOG.assertTrue(qualifier != null);
return TypesUtil.createJavaLangClassType(qualifier.getType(), refExpr.getProject(), refExpr.getResolveScope());
return qualifier.getType();
}
if (PsiUtil.isCompileStatic(refExpr)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -74,8 +74,8 @@ public class GrReferenceResolveRunner {
}
else {
if (ResolveUtil.isClassReference(place)) return;
if (!processQualifier(qualifier)) return;
processJavaLangClass(qualifier);
if (!processJavaLangClass(qualifier)) return;
processQualifier(qualifier);
}
}
}
@@ -84,24 +84,14 @@ public class GrReferenceResolveRunner {
if (!(qualifier instanceof GrReferenceExpression)) return true;
//optimization: only 'class' or 'this' in static context can be an alias of java.lang.Class
if (!("class".equals(((GrReferenceExpression)qualifier).getReferenceName()) ||
PsiUtil.isThisReference(qualifier))) {
if (!"class".equals(((GrReferenceExpression)qualifier).getReferenceName()) &&
!PsiUtil.isThisReference(qualifier) &&
!(((GrReferenceExpression)qualifier).resolve() instanceof PsiClass)) {
return true;
}
PsiType type = qualifier.getType();
if (!(type instanceof PsiClassType)) return true;
final PsiClass psiClass = ((PsiClassType)type).resolve();
if (psiClass == null || !CommonClassNames.JAVA_LANG_CLASS.equals(psiClass.getQualifiedName())) return true;
final PsiType[] params = ((PsiClassType)type).getParameters();
if (params.length != 1) return true;
if (!processQualifierType(params[0], ResolveState.initial().put(ClassHint.RESOLVE_CONTEXT, qualifier))) {
return false;
}
return true;
PsiType classType = ResolveUtil.unwrapClassType(qualifier.getType());
return classType == null || processQualifierType(classType, ResolveState.initial().put(ClassHint.RESOLVE_CONTEXT, qualifier));
}
private boolean processQualifier(@NotNull GrExpression qualifier) {
@@ -122,38 +112,10 @@ public class GrReferenceResolveRunner {
}
else {
if (!processQualifierType(qualifierType, state)) return false;
if (qualifier instanceof GrReferenceExpression && !PsiUtil.isSuperReference(qualifier) && !PsiUtil.isInstanceThisRef(qualifier)) {
PsiElement resolved = ((GrReferenceExpression)qualifier).resolve();
if (resolved instanceof PsiClass) {
if (!processJavaLangClass(qualifierType, state)) return false;
}
}
}
return true;
}
private boolean processJavaLangClass(@NotNull PsiType qualifierType,
@NotNull ResolveState state) {
//omitted .class
PsiClass javaLangClass = PsiUtil.getJavaLangClass(place, place.getResolveScope());
if (javaLangClass == null) return true;
PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters();
PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
if (typeParameters.length == 1) {
substitutor = substitutor.put(typeParameters[0], qualifierType);
state = state.put(PsiSubstitutor.KEY, substitutor);
}
if (!ResolveUtil.processClassDeclarations(javaLangClass, processor, state, null, place)) return false;
PsiType javaLangClassType = JavaPsiFacade.getElementFactory(place.getProject()).createType(javaLangClass, substitutor);
if (!ResolveUtil.processNonCodeMembers(javaLangClassType, processor, place, state)) return false;
return true;
}
private boolean processQualifierType(@NotNull PsiType originalQualifierType,
@NotNull ResolveState state) {
PsiType qualifierType = originalQualifierType instanceof PsiDisjunctionType
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrRefere
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
/**
* @author Max Medvedev
@@ -209,7 +210,7 @@ public class GrStaticChecker {
else if (PsiUtil.isThisReference(qualifier)) { //instance 'this' already is processed. So it static 'this'
return true;
}
return qualifier instanceof GrQualifiedReference && ((GrQualifiedReference)qualifier).resolve() instanceof PsiClass;
return qualifier instanceof GrQualifiedReference && ResolveUtil.resolvesToClass(qualifier);
}
@@ -1158,7 +1158,7 @@ public class PsiUtil {
public static boolean isExpressionUsed(PsiElement expr) {
while (expr.getParent() instanceof GrParenthesizedExpression) expr = expr.getParent();
final PsiElement parent = expr.getParent();
PsiElement parent = expr.getParent();
if (parent instanceof GrBinaryExpression ||
parent instanceof GrUnaryExpression ||
parent instanceof GrConditionalExpression ||
@@ -1174,6 +1174,20 @@ public class PsiUtil {
parent instanceof GrVariable) {
return true;
}
if (parent instanceof GrReferenceExpression) {
if (ResolveUtil.isClassReference(parent)) {
parent = parent.getParent();
}
if (parent instanceof GrReferenceExpression) {
PsiElement resolved = ((GrReferenceExpression)parent).resolve();
if (resolved instanceof PsiMember) {
PsiClass containingClass = ((PsiMember)resolved).getContainingClass();
return containingClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(containingClass.getQualifiedName());
}
}
return true;
}
return isReturnStatement(expr);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyLanguage;
import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference;
import org.jetbrains.plugins.groovy.lang.psi.GrQualifiedReference;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
@@ -1052,7 +1053,14 @@ public class ResolveUtil {
}, state, null, place);
}
public static boolean isClassReference(@NotNull GrReferenceExpression ref) {
public static boolean resolvesToClass(@Nullable PsiElement expression) {
if (!(expression instanceof GrQualifiedReference)) return false;
return isClassReference(expression) || ((GrQualifiedReference)expression).resolve() instanceof PsiClass;
}
public static boolean isClassReference(@NotNull PsiElement expression) {
if (!(expression instanceof GrReferenceExpression)) return false;
GrReferenceExpression ref = (GrReferenceExpression)expression;
GrExpression qualifier = ref.getQualifier();
return "class".equals(ref.getReferenceName()) &&
qualifier instanceof GrReferenceExpression &&
@@ -1060,6 +1068,19 @@ public class ResolveUtil {
!org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isThisReference(qualifier);
}
@Nullable
public static PsiType unwrapClassType(@Nullable PsiType type) {
if (!(type instanceof PsiClassType)) return null;
PsiClass psiClass = ((PsiClassType)type).resolve();
if (psiClass == null || !CommonClassNames.JAVA_LANG_CLASS.equals(psiClass.getQualifiedName())) return null;
final PsiType[] params = ((PsiClassType)type).getParameters();
if (params.length != 1) return null;
return params[0];
}
private static class DuplicateVariablesProcessor extends PropertyResolverProcessor {
private boolean myBorderPassed;
private final boolean myHasVisibilityModifier;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -167,12 +167,14 @@ public class CompleteReferenceExpression {
}
else {
if (myRefExpr.getDotTokenType() != GroovyTokenTypes.mSPREAD_DOT) {
getVariantsFromQualifier(qualifier);
if (qualifier instanceof GrReferenceExpression &&
("class".equals(((GrReferenceExpression)qualifier).getReferenceName()) || PsiUtil.isThisReference(qualifier) && !PsiUtil.isInstanceThisRef(qualifier))) {
if (qualifier instanceof GrReferenceExpression && (
"class".equals(((GrReferenceExpression)qualifier).getReferenceName()) ||
PsiUtil.isThisReference(qualifier) && !PsiUtil.isInstanceThisRef(qualifier)
|| ((GrReferenceExpression)qualifier).resolve() instanceof PsiClass
)) {
processIfJavaLangClass(qualifier.getType());
}
getVariantsFromQualifier(qualifier);
}
else {
getVariantsFromQualifierForSpreadOperator(qualifier);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -56,6 +56,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameterList;
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;
import org.jetbrains.plugins.groovy.refactoring.DefaultGroovyVariableNameValidator;
import org.jetbrains.plugins.groovy.refactoring.GroovyNameSuggestionUtil;
import org.jetbrains.plugins.groovy.refactoring.inline.InlineMethodConflictSolver;
@@ -212,7 +213,7 @@ public class GrMainCompletionProvider extends CompletionProvider<CompletionParam
final Map<PsiModifierListOwner, LookupElement> staticMembers = ContainerUtil.newHashMap();
final PsiElement qualifier = reference.getQualifier();
final PsiType qualifierType = qualifier instanceof GrExpression ? ((GrExpression)qualifier).getType() : null;
final PsiType qualifierType = getQualifierType(qualifier);
if (reference instanceof GrReferenceExpression && (qualifier instanceof GrExpression || qualifier == null)) {
for (String string : CompleteReferencesWithSameQualifier.getVariantsWithSameQualifier((GrReferenceExpression)reference, matcher, (GrExpression)qualifier)) {
@@ -284,6 +285,18 @@ public class GrMainCompletionProvider extends CompletionProvider<CompletionParam
return EmptyRunnable.INSTANCE;
}
@Nullable
private static PsiType getQualifierType(PsiElement qualifier) {
PsiType qualifierType = qualifier instanceof GrExpression ? ((GrExpression)qualifier).getType() : null;
if (ResolveUtil.resolvesToClass(qualifier)) {
PsiType type = ResolveUtil.unwrapClassType(qualifierType);
if (type != null) {
qualifierType = type;
}
}
return qualifierType;
}
private static boolean isLightElementDeclaredDuringCompletion(Object object) {
if (!(object instanceof LightElement && object instanceof PsiNamedElement)) return false;
final String name = ((PsiNamedElement)object).getName();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -952,16 +952,17 @@ public class ExpressionGenerator extends Generator {
}
if (ResolveUtil.isClassReference(referenceExpression)) {
// just delegate to qualifier
LOG.assertTrue(qualifier != null);
qualifier.accept(this);
builder.append(".class");
return;
}
//class name used as expression. Should be converted to <className>.class
if (resolved instanceof PsiClass && PsiUtil.isExpressionUsed(referenceExpression)) {
if (resolved instanceof PsiClass) {
builder.append(((PsiClass)resolved).getQualifiedName());
builder.append(".class");
if (PsiUtil.isExpressionUsed(referenceExpression)) {
builder.append(".class");
}
return;
}
@@ -1036,9 +1037,6 @@ public class ExpressionGenerator extends Generator {
builder.append(".get()");
}
}
else if (resolved instanceof PsiClass) {
TypeWriter.writeType(builder, referenceExpression.getType(), referenceExpression);
}
else {
builder.append(refName);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -517,12 +517,24 @@ public class GenerationUtil {
if (declared == null) return false;
final CheckProcessElement checker = new CheckProcessElement(member);
ResolveUtil.processAllDeclarationsSeparately(declared, checker, new BaseScopeProcessor() {
final BaseScopeProcessor processor = new BaseScopeProcessor() {
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
return false;
}
}, ResolveState.initial(), qualifier);
};
if (ResolveUtil.resolvesToClass(qualifier)) {
PsiType type = ResolveUtil.unwrapClassType(declared);
if (type != null) {
ResolveUtil.processAllDeclarationsSeparately(type, checker, processor, ResolveState.initial(), qualifier);
if (checker.isFound()) {
return false;
}
}
}
ResolveUtil.processAllDeclarationsSeparately(declared, checker, processor, ResolveState.initial(), qualifier);
return !checker.isFound();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -125,5 +125,17 @@ print A.object
''')
}
void testClassRef() {
doTextTest '''\
class A {
}
print A.class.obj<caret>ect
''', '''\
class A {
static def object
}
print A.class.object
'''
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -2245,4 +2245,29 @@ SourceConcrete.someOtherStatic<caret>Method()
assert !computer.computed
}
}
void 'test resolve method with class qualifier'() {
myFixture.addClass '''\
package foo.bar;
public class A {
public static void foo() {}
public static String getCanonicalName() {return "";}
}
'''
def data = [
'A.fo<caret>o()' : 'foo.bar.A',
'A.class.fo<caret>o()' : 'foo.bar.A',
'A.simpleN<caret>ame' : 'java.lang.Class',
'A.class.simpleN<caret>ame' : 'java.lang.Class',
'A.canonicalN<caret>ame' : 'foo.bar.A',
'A.class.canonicalN<caret>ame': 'foo.bar.A'
]
data.each { expression, expectedClass ->
def ref = configureByText "import foo.bar.A; $expression"
def element = ref.resolve()
assert element instanceof PsiMember : "$expression -> $expectedClass"
assert element.containingClass.qualifiedName == expectedClass
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@ public class CodeBlockGenerationTest extends LightGroovyTestCase {
PostprocessReformattingAspect.getInstance(project).doPostponedFormatting()
final String text = result.text
final String expected = psiManager.findFile(myFixture.copyFileToProject(testName + '.java')).text
assertEquals expected, text
assertEquals expected.trim(), text.trim()
}
private addFile(String text) {
@@ -190,6 +190,8 @@ public final class Matcher {
void testClassReference() { doTest() }
void testClassMethod() { doTest() }
void testEquals() { doTest() }
void testSelfNavigatingOperator() { doTest() }
@@ -0,0 +1,11 @@
class A {
static foo() {}
static getCanonicalName() { "" }
}
print A.foo()
print A.class.foo()
print A.simpleName
print A.class.simpleName
print A.canonicalName
print A.class.canonicalName
@@ -0,0 +1,6 @@
print(A.foo());
print(A.foo());
print(A.class.getSimpleName());
print(A.class.getSimpleName());
print(A.getCanonicalName());
print(A.getCanonicalName());