diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/checkers/BaseScriptAnnotationChecker.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/checkers/BaseScriptAnnotationChecker.java index 7aa4f530d19b..b0b1efb08d3a 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/checkers/BaseScriptAnnotationChecker.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/checkers/BaseScriptAnnotationChecker.java @@ -1,32 +1,19 @@ -/* - * Copyright 2000-2014 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. - */ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.groovy.annotator.checkers; import com.intellij.lang.annotation.AnnotationHolder; -import com.intellij.psi.CommonClassNames; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiType; +import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyBundle; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; public class BaseScriptAnnotationChecker extends CustomAnnotationChecker { @@ -50,8 +37,31 @@ public class BaseScriptAnnotationChecker extends CustomAnnotationChecker { return true; } } + else if (pparent instanceof GrPackageDefinition || pparent instanceof GrImportStatement) { + PsiClass clazz = GrAnnotationUtil.inferClassAttribute(annotation, "value"); + if (!InheritanceUtil.isInheritor(clazz, GroovyCommonClassNames.GROOVY_LANG_SCRIPT)) { + String typeText = getTypeText(clazz); + holder.createErrorAnnotation(annotation, GroovyBundle.message("declared.type.0.have.to.extend.script", typeText)); + } + } } return false; } + + @NotNull + public String getTypeText(@Nullable PsiClass clazz) { + if (clazz == null) { + return CommonClassNames.JAVA_LANG_OBJECT; + } + String fqn = clazz.getQualifiedName(); + if (fqn != null) { + return fqn; + } + String name = clazz.getName(); + if (name != null) { + return name; + } + return CommonClassNames.JAVA_LANG_OBJECT; + } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrAnnotationUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrAnnotationUtil.java index dcf86ffb484f..1996011d9e9b 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrAnnotationUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrAnnotationUtil.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.groovy.lang.psi.impl; import com.intellij.codeInsight.AnnotationUtil; @@ -67,7 +67,7 @@ public class GrAnnotationUtil { } @Nullable - private static PsiClass getPsiClass(@Nullable PsiAnnotationMemberValue targetValue) { + public static PsiClass getPsiClass(@Nullable PsiAnnotationMemberValue targetValue) { if (targetValue instanceof PsiClassObjectAccessExpression) { PsiType type = ((PsiClassObjectAccessExpression)targetValue).getOperand().getType(); if (type instanceof PsiClassType) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/impl/BaseScriptTransformationSupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/impl/BaseScriptTransformationSupport.java index 31c2e5f8a6de..4fce9826ed7a 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/impl/BaseScriptTransformationSupport.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/impl/BaseScriptTransformationSupport.java @@ -1,10 +1,7 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.groovy.transformations.impl; -import com.intellij.psi.PsiArrayType; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiModifier; -import com.intellij.psi.PsiType; +import com.intellij.psi.*; import com.intellij.psi.impl.light.LightMethodBuilder; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -13,8 +10,12 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyLanguage; import org.jetbrains.plugins.groovy.dsl.GroovyDslFileIndex; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; @@ -22,6 +23,8 @@ import org.jetbrains.plugins.groovy.transformations.AstTransformationSupport; import org.jetbrains.plugins.groovy.transformations.TransformationContext; import static com.intellij.psi.impl.cache.impl.id.IdIndex.hasIdentifierInFile; +import static org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtilKt.findDeclaredDetachedValue; +import static org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames.GROOVY_TRANSFORM_BASE_SCRIPT; public class BaseScriptTransformationSupport implements AstTransformationSupport { @@ -47,7 +50,9 @@ public class BaseScriptTransformationSupport implements AstTransformationSupport @NotNull private static PsiClassType getBaseClassType(@NotNull GroovyScriptClass scriptClass) { - PsiClassType type = getSuperClassTypeFromBaseScriptAnnotatedVariable(scriptClass); + PsiClassType type = CachedValuesManager.getCachedValue(scriptClass, () -> CachedValueProvider.Result.create( + getSuperClassTypeFromBaseScriptAnnotation(scriptClass), scriptClass.getContainingFile() + )); if (type != null) return type; final PsiClassType superClassFromDSL = GroovyDslFileIndex.processScriptSuperClasses(scriptClass.getContainingFile()); @@ -57,13 +62,70 @@ public class BaseScriptTransformationSupport implements AstTransformationSupport } @Nullable - private static PsiClassType getSuperClassTypeFromBaseScriptAnnotatedVariable(GroovyScriptClass scriptClass) { - return CachedValuesManager.getCachedValue(scriptClass, () -> CachedValueProvider.Result.create( - doGetSuperClassType(scriptClass), scriptClass.getContainingFile() - )); + private static PsiClassType getSuperClassTypeFromBaseScriptAnnotation(@NotNull GroovyScriptClass scriptClass) { + //Groovy BaseScriptASTTransformation works exactly with this priorities + PsiClassType fromVariable = getSuperClassTypeFromBaseScriptAnnotatedVariable(scriptClass); + if (fromVariable != null) { + return fromVariable; + } + + PsiClassType fromImport = getSuperClassTypeFromBaseScriptAnnotatedImportDefinition(scriptClass); + if (fromImport != null) { + return fromImport; + } + + return getSuperClassTypeFromBaseScriptAnnotatedPackageDefinition(scriptClass); } - private static PsiClassType doGetSuperClassType(GroovyScriptClass scriptClass) { + @Nullable + private static PsiClassType getSuperClassTypeFromBaseScriptAnnotatedImportDefinition(@NotNull GroovyScriptClass scriptClass) { + GrImportStatement[] importStatements = scriptClass.getContainingFile().getImportStatements(); + for (GrImportStatement importStatement : importStatements) { + GrModifierList annotations = importStatement.getAnnotationList(); + PsiAnnotation baseScriptAnnotation = annotations.findAnnotation(GROOVY_TRANSFORM_BASE_SCRIPT); + if (baseScriptAnnotation == null) { + continue; + } + PsiClassType superClassType = getSuperClassTypeFromAnnotationValue(scriptClass, baseScriptAnnotation); + if (superClassType != null) { + return superClassType; + } + } + return null; + } + + @Nullable + private static PsiClassType getSuperClassTypeFromBaseScriptAnnotatedPackageDefinition(@NotNull GroovyScriptClass scriptClass) { + GrPackageDefinition packageDefinition = scriptClass.getContainingFile().getPackageDefinition(); + if (packageDefinition == null) { + return null; + } + PsiModifierList modifierList = packageDefinition.getModifierList(); + if (modifierList == null) { + return null; + } + PsiAnnotation baseScriptAnnotation = modifierList.findAnnotation(GROOVY_TRANSFORM_BASE_SCRIPT); + if (baseScriptAnnotation == null) { + return null; + } + return getSuperClassTypeFromAnnotationValue(scriptClass, baseScriptAnnotation); + } + + @Nullable + private static PsiClassType getSuperClassTypeFromAnnotationValue(GroovyScriptClass scriptClass, PsiAnnotation baseScriptAnnotation) { + PsiClass clazz = GrAnnotationUtil.getPsiClass(findDeclaredDetachedValue(baseScriptAnnotation, "value")); + if (clazz != null) { + String className = clazz.getQualifiedName(); + if (className == null) clazz.getName(); + if (className != null) { + return TypesUtil.createTypeByFQClassName(className, scriptClass); + } + } + return null; + } + + @Nullable + private static PsiClassType getSuperClassTypeFromBaseScriptAnnotatedVariable(GroovyScriptClass scriptClass) { GrVariableDeclaration declaration = findDeclaration(scriptClass.getContainingFile()); if (declaration != null) { GrTypeElement typeElement = declaration.getTypeElementGroovy(); @@ -83,7 +145,7 @@ public class BaseScriptTransformationSupport implements AstTransformationSupport return null; } for (GrVariableDeclaration declaration : file.getScriptDeclarations(false)) { - if (declaration.getModifierList().hasAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_BASE_SCRIPT)) { + if (declaration.getModifierList().hasAnnotation(GROOVY_TRANSFORM_BASE_SCRIPT)) { return declaration; } }