mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[groovy] support @SelfType (IDEA-141131)
This commit is contained in:
@@ -384,3 +384,4 @@ anonymous.classes.cannot.be.created.from.traits=Anonymous classes cannot be crea
|
||||
trait.method.cannot.be.protected=Trait methods are not allowed to be protected
|
||||
traits.are.not.supported.in.groovy.0=Traits are not supported in Groovy {0}
|
||||
non.static.classes.not.allowed=Non-static inner classes are not allowed in traits
|
||||
selfType.class.does.not.inherit=@SelfType: Class ''{0}'' does not inherit ''{1}''
|
||||
|
||||
+12
@@ -416,6 +416,18 @@ public class GroovyAnnotator extends GroovyElementVisitor {
|
||||
checkAnnotationCollector(myHolder, typeDefinition);
|
||||
|
||||
checkSameNameMethodsWithDifferentAccessModifiers(myHolder, typeDefinition.getCodeMethods());
|
||||
checkInheritorOfSelfTypes(myHolder, typeDefinition);
|
||||
}
|
||||
|
||||
private static void checkInheritorOfSelfTypes(AnnotationHolder holder, GrTypeDefinition definition) {
|
||||
if (!(definition instanceof GrClassDefinition)) return;
|
||||
List<PsiClass> selfTypeClasses = GrTraitUtil.getSelfTypeClasses(definition);
|
||||
for (PsiClass selfClass : selfTypeClasses) {
|
||||
if (InheritanceUtil.isInheritorOrSelf(definition, selfClass, true)) continue;
|
||||
String message = GroovyBundle.message("selfType.class.does.not.inherit", definition.getQualifiedName(), selfClass.getQualifiedName());
|
||||
holder.createErrorAnnotation(GrHighlightUtil.getClassHeaderTextRange(definition), message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkSameNameMethodsWithDifferentAccessModifiers(AnnotationHolder holder, GrMethod[] methods) {
|
||||
|
||||
+22
-1
@@ -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.
|
||||
@@ -16,11 +16,19 @@
|
||||
package org.jetbrains.plugins.groovy.lang.psi.impl.statements.typedef;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.ResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTraitTypeDefinition;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.stubs.GrTypeDefinitionStub;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Max Medvedev on 09/04/14
|
||||
@@ -54,4 +62,17 @@ public class GrTraitTypeDefinitionImpl extends GrTypeDefinitionImpl implements G
|
||||
public void accept(GroovyElementVisitor visitor) {
|
||||
visitor.visitTraitDefinition(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
@Nullable PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
if (!super.processDeclarations(processor, state, lastParent, place)) return false;
|
||||
List<PsiClass> classes = GrTraitUtil.getSelfTypeClasses(this);
|
||||
for (PsiClass clazz : classes) {
|
||||
if (!clazz.processDeclarations(processor, state, lastParent, place)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+26
-3
@@ -27,6 +27,7 @@ import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.indexing.FileBasedIndex;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@@ -34,15 +35,14 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightField;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.GroovyTraitFieldsFileIndex;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.GroovyTraitFieldsFileIndex.TraitFieldDescriptor;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.GroovyTraitMethodsFileIndex;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.psi.PsiModifier.ABSTRACT;
|
||||
import static org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierFlags.*;
|
||||
@@ -71,6 +71,29 @@ public class GrTraitUtil {
|
||||
return method.getModifierList().hasExplicitModifier(ABSTRACT) || isInterface(method.getContainingClass());
|
||||
}
|
||||
|
||||
public static List<PsiClass> getSelfTypeClasses(@NotNull PsiClass trait) {
|
||||
return CachedValuesManager.getCachedValue(trait, () -> {
|
||||
List<PsiClass> result = ContainerUtil.newArrayList();
|
||||
Queue<PsiClass> queue = new ArrayDeque<>();
|
||||
Set<PsiClass> visited = ContainerUtil.newHashSet();
|
||||
queue.offer(trait);
|
||||
while (!queue.isEmpty()) {
|
||||
PsiClass clazz = queue.poll();
|
||||
if (!visited.add(clazz)) continue;
|
||||
ContainerUtil.addAll(queue, clazz.getSupers());
|
||||
if (isTrait(clazz)) {
|
||||
PsiAnnotation annotation = AnnotationUtil.findAnnotation(clazz, "groovy.transform.SelfType");
|
||||
if (annotation != null) {
|
||||
result.addAll(
|
||||
GrAnnotationUtil.getClassArrayValue(annotation, "value")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return CachedValueProvider.Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getTraitFieldPrefix(@NotNull PsiClass aClass) {
|
||||
String qname = aClass.getQualifiedName();
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.lang.resolve
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import groovy.transform.CompileStatic
|
||||
import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor
|
||||
import org.jetbrains.plugins.groovy.LightGroovyTestCase
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod
|
||||
|
||||
@CompileStatic
|
||||
class SelfTypeSupportTest extends LightGroovyTestCase {
|
||||
|
||||
LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST
|
||||
String defaultImports = '''\
|
||||
import groovy.transform.SelfType
|
||||
'''
|
||||
|
||||
void 'test resolve from within trait'() {
|
||||
def resolved = resolveByText('''\
|
||||
interface I { def foo() }
|
||||
@SelfType(I)
|
||||
trait T {
|
||||
def bar() {
|
||||
fo<caret>o()
|
||||
}
|
||||
}
|
||||
''')
|
||||
assert (resolved as GrMethod).containingClass.name == 'I'
|
||||
}
|
||||
|
||||
void 'test resolve outside trait'() {
|
||||
def resolved = resolveByText('''\
|
||||
interface I { def foo() }
|
||||
@SelfType(I)
|
||||
trait T {}
|
||||
def bar(T t) {
|
||||
t.f<caret>oo()
|
||||
}
|
||||
''')
|
||||
assert (resolved as GrMethod).containingClass.name == 'I'
|
||||
}
|
||||
|
||||
void 'test resolve inside trait extending trait'() {
|
||||
def resolved = resolveByText('''\
|
||||
interface I { def foo() }
|
||||
@SelfType(I)
|
||||
trait T {}
|
||||
trait T2 extends T {
|
||||
def bar() {
|
||||
f<caret>oo()
|
||||
}
|
||||
}
|
||||
''')
|
||||
assert (resolved as GrMethod).containingClass.name == 'I'
|
||||
}
|
||||
|
||||
void 'test do not count @SelfType on interfaces in hierarchy'() {
|
||||
assert resolveByText('''\
|
||||
interface I { def foo() }
|
||||
@SelfType(I)
|
||||
interface II {}
|
||||
trait T implements II {
|
||||
def bar() {
|
||||
fo<caret>o()
|
||||
}
|
||||
}
|
||||
''') == null
|
||||
}
|
||||
|
||||
void 'test highlighting'() {
|
||||
testHighlighting '''\
|
||||
interface I {}
|
||||
@SelfType(I)
|
||||
trait T {}
|
||||
<error descr="@SelfType: Class 'A' does not inherit 'I'">class A implements T</error> {}
|
||||
|
||||
trait T2 extends T {}
|
||||
<error descr="@SelfType: Class 'B' does not inherit 'I'">class B implements T2</error> {}
|
||||
'''
|
||||
}
|
||||
|
||||
PsiElement resolveByText(String text) {
|
||||
fixture.configureByText '_.groovy', "$defaultImports$text"
|
||||
file.findReferenceAt(editor.caretModel.offset).resolve()
|
||||
}
|
||||
|
||||
void testHighlighting(String text) {
|
||||
fixture.configureByText '_.groovy', "$defaultImports$text"
|
||||
fixture.checkHighlighting()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user