Uast: Allow to search for a static members (in Kotlin static members are located inside the companion objects).

Companion objects support.
This commit is contained in:
Yan Zhulanow
2016-03-10 19:42:17 +03:00
parent f1e5c85ba3
commit ed10829091
15 changed files with 160 additions and 61 deletions
@@ -36,6 +36,7 @@ import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.kinds.UastClassKind;
/**
* Checks that Fragment subclasses can be instantiated via
@@ -90,7 +91,7 @@ public class FragmentDetector extends Detector implements UastScanner {
@Override
public void visitClass(UastAndroidContext context, UClass cls) {
if (cls.hasModifier(UastModifier.ABSTRACT) || cls.isInterface()) {
if (cls.hasModifier(UastModifier.ABSTRACT) || cls.getKind() != UastClassKind.CLASS) {
return;
}
@@ -101,7 +102,7 @@ public class FragmentDetector extends Detector implements UastScanner {
return;
}
if (UastUtils.getContainingClass(cls) != null && cls.hasModifier(UastModifier.INNER)) {
if (UastUtils.getContainingClass(cls) != null && !cls.hasModifier(UastModifier.STATIC)) {
String message = String.format(
"This fragment inner class should be static (%1$s)", cls.getName());
context.report(ISSUE, cls, UastAndroidUtils.getLocation(cls.getNameElement()), message);
@@ -84,8 +84,7 @@ public class HandlerDetector extends Detector implements UastScanner {
@Override
public void visitClass(UastAndroidContext context, UClass node) {
boolean x = node.hasModifier(UastModifier.INNER);
if (!node.hasModifier(UastModifier.INNER)) {
if (node.hasModifier(UastModifier.STATIC)) {
return;
}
@@ -30,6 +30,7 @@ import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.kinds.UastClassKind;
import org.jetbrains.uast.visitor.UastVisitor;
/**
@@ -82,14 +83,14 @@ public class ParcelDetector extends Detector implements UastScanner {
@Override
public boolean visitClass(@NotNull UClass node) {
// Only applies to concrete classes
if (node.isInterface() || node.hasModifier(UastModifier.ABSTRACT)) {
if (node.getKind() != UastClassKind.CLASS || node.hasModifier(UastModifier.ABSTRACT)) {
return true;
}
for (UType reference : node.getSuperTypes()) {
String name = reference.getName();
if (name.equals("Parcelable")) {
UVariable field = UastUtils.findProperty(node, "CREATOR");
UVariable field = UastUtils.findStaticMemberOfType(node, "CREATOR", UVariable.class);
if (field == null) {
// Make doubly sure that we're really implementing
// android.os.Parcelable
@@ -38,6 +38,7 @@ import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.kinds.UastClassKind;
/**
* Looks for custom views that do not define the view constructors needed by UI builders
@@ -114,11 +115,12 @@ public class ViewConstructorDetector extends Detector implements UastScanner {
@Override
public void visitClass(UastAndroidContext context, UClass node) {
// Only applies to concrete and not abstract classes
if (node.isObject() || node.isInterface() || node.isEnum() || node.hasModifier(UastModifier.ABSTRACT)) {
UastClassKind kind = node.getKind();
if (kind != UastClassKind.CLASS || node.hasModifier(UastModifier.ABSTRACT)) {
return;
}
if (UastUtils.getContainingClass(node) != null && node.hasModifier(UastModifier.INNER)) {
if (UastUtils.getContainingClass(node) != null && !node.hasModifier(UastModifier.STATIC)) {
// Ignore inner classes that aren't static: we can't create these
// anyway since we'd need the outer instance
return;
@@ -29,6 +29,7 @@ import org.jetbrains.uast.UastUtils;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.kinds.UastClassKind;
import org.jetbrains.uast.visitor.UastVisitor;
import java.util.Collection;
@@ -74,7 +75,7 @@ public class IntellijRegistrationDetector extends RegistrationDetector implement
private void check(UastAndroidContext context, UClass clz) {
for (UClass current = clz.getSuperClass(context); current != null; current = current.getSuperClass(context)) {
// Ignore abstract classes
if (clz.hasModifier(UastModifier.ABSTRACT) || clz.isObject() || clz.isInterface() || clz.isEnum() || clz.isAnnotation()) {
if (clz.hasModifier(UastModifier.ABSTRACT) || clz.getKind() != UastClassKind.CLASS) {
continue;
}
String fqcn = current.getFqName();
@@ -16,6 +16,8 @@
@file:JvmName("UastUtils")
package org.jetbrains.uast
import org.jetbrains.uast.kinds.UastClassKind
tailrec fun UElement?.getContainingClass(): UClass? {
val parent = this?.parent ?: return null
if (parent is UClass) return parent
@@ -126,4 +128,21 @@ fun <T: UElement> UElement.getParentOfType(clazz: Class<T>): T? {
@Suppress("UNCHECKED_CAST")
return findParent(this) as T
}
fun <T> UClass.findStaticMemberOfType(name: String, type: Class<out T>): T? {
for (companion in companions) {
val classKind = companion.kind as? UastClassKind.UastCompanionObject ?: continue
if (!classKind.default) continue
val member = companion.declarations.firstOrNull { it.name == name && type.isInstance(it) }
@Suppress("UNCHECKED_CAST")
if (member != null) return member as T
}
@Suppress("UNCHECKED_CAST")
return declarations.firstOrNull {
it.name == name && it is UModifierOwner
&& it.hasModifier(UastModifier.STATIC) && type.isInstance(it)
} as T
}
@@ -15,13 +15,14 @@
*/
package org.jetbrains.uast
import org.jetbrains.uast.kinds.UastClassKind
interface UClass : UDeclaration, UFqNamed, UModifierOwner, UAnnotated {
val isEnum: Boolean
val isInterface: Boolean
val isObject: Boolean
val isAnonymous: Boolean
val isAnnotation: Boolean
val visibility: UastVisibility
val kind: UastClassKind
val companions: List<UClass>
val internalName: String?
@@ -52,14 +53,7 @@ interface UClass : UDeclaration, UFqNamed, UModifierOwner, UAnnotated {
}
override fun renderString(): String {
val keyword = when {
isEnum -> "enum"
isInterface -> "interface"
isObject -> "object"
else -> "class"
}
val modifiers = listOf(UastModifier.ABSTRACT, UastModifier.FINAL, UastModifier.INNER)
val modifiers = listOf(UastModifier.ABSTRACT, UastModifier.FINAL, UastModifier.STATIC)
.filter { hasModifier(it) }.joinToString(" ") { it.name }.let { if (it.isBlank()) it else "$it " }
val name = if (isAnonymous) "" else " $name"
@@ -70,22 +64,20 @@ interface UClass : UDeclaration, UFqNamed, UModifierOwner, UAnnotated {
append("\n}")
}
return "${visibility.name} " + modifiers + keyword + name + " " + declarations
return "${visibility.name} " + modifiers + kind.text + name + " " + declarations
}
override fun logString() = "UClass ($name, enum = $isEnum, interface = $isInterface, object = $isObject)\n" + declarations.logString()
override fun logString() = "UClass ($name, kind = ${kind.text})\n" + declarations.logString()
}
object UClassNotResolved : UClass {
override val isEnum = false
override val isInterface = false
override val isObject = false
override val isAnonymous = true
override val isAnnotation = false
override val kind = UastClassKind.CLASS
override val visibility = UastVisibility.PRIVATE
override val superTypes = emptyList<UType>()
override val declarations = emptyList<UDeclaration>()
override fun isSubclassOf(name: String) = false
override val companions = emptyList<UClass>()
override val nameElement = null
override val parent = null
@@ -0,0 +1,38 @@
/*
* Copyright 2010-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.uast.kinds
open class UastClassKind(val text: String) {
class UastCompanionObject(val default: Boolean) : UastClassKind("companion object")
companion object {
@JvmField
val CLASS = UastClassKind("class")
@JvmField
val INTERFACE = UastClassKind("interface")
@JvmField
val ANNOTATION = UastClassKind("annotation")
@JvmField
val ENUM = UastClassKind("enum")
@JvmField
val OBJECT = UastClassKind("object")
}
}
@@ -20,7 +20,7 @@ class UastModifier(val name: String) {
@JvmField
val ABSTRACT = UastModifier("abstract")
@JvmField
val INNER = UastModifier("inner")
val STATIC = UastModifier("static")
@JvmField
val FINAL = UastModifier("final")
}
@@ -17,6 +17,7 @@ package org.jetbrains.uast.java
import com.intellij.psi.*
import org.jetbrains.uast.*
import org.jetbrains.uast.kinds.UastClassKind
import org.jetbrains.uast.psi.PsiElementBacked
import java.util.*
@@ -39,17 +40,18 @@ class JavaUClass(
override val fqName: String?
get() = psi.qualifiedName
override val isEnum: Boolean
get() = psi.isEnum
override val kind by lz {
when {
psi.isEnum -> UastClassKind.ENUM
psi.isAnnotationType -> UastClassKind.ANNOTATION
psi.isInterface -> UastClassKind.INTERFACE
psi is PsiAnonymousClass -> UastClassKind.OBJECT
else -> UastClassKind.CLASS
}
}
override val isInterface: Boolean
get() = psi.isInterface
override val isAnnotation: Boolean
get() = psi.isAnnotationType
override val isObject: Boolean
get() = psi is PsiAnonymousClass
override val companions: List<UClass>
get() = emptyList()
override val isAnonymous: Boolean
get() = psi is PsiAnonymousClass
@@ -65,11 +67,7 @@ class JavaUClass(
override val visibility: UastVisibility
get() = psi.getVisibility()
override fun hasModifier(modifier: UastModifier) = when (modifier) {
UastModifier.INNER -> !psi.hasModifierProperty(PsiModifier.STATIC) && !isTopLevel()
else -> psi.hasModifier(modifier)
}
override fun hasModifier(modifier: UastModifier) = psi.hasModifier(modifier)
override val annotations by lz { psi.modifierList.getAnnotations(this) }
override val declarations by lz {
@@ -22,7 +22,8 @@ import org.jetbrains.uast.*
private val MODIFIER_MAP = mapOf(
UastModifier.ABSTRACT to PsiModifier.ABSTRACT,
UastModifier.FINAL to PsiModifier.FINAL
UastModifier.FINAL to PsiModifier.FINAL,
UastModifier.STATIC to PsiModifier.STATIC
)
internal fun PsiModifierListOwner.hasModifier(modifier: UastModifier): Boolean {
@@ -70,7 +70,7 @@ internal object KotlinConverter : UastConverter {
}
internal fun convert(element: KtDeclaration, parent: UElement): UDeclaration? = when (element) {
is KtClassOrObject -> KotlinUClass(element, parent)
is KtClassOrObject -> convert(element, parent)
is KtAnonymousInitializer -> KotlinAnonymousInitializerUFunction(element, parent)
is KtConstructor<*> -> KotlinConstructorUFunction(element, parent)
is KtFunction -> KotlinUFunction(element, parent)
@@ -160,6 +160,10 @@ internal object KotlinConverter : UastConverter {
return KotlinParameterUVariable(element, parent)
}
internal fun convert(element: KtClassOrObject, parent: UElement) : UClass {
return KotlinUClass(element, parent)
}
internal fun convert(element: KotlinType, project: Project, parent: UElement?): UType {
return KotlinUType(element, project, parent)
}
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtObjectDeclaration
@@ -34,6 +35,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.uast.*
import org.jetbrains.uast.kinds.UastClassKind
import org.jetbrains.uast.psi.PsiElementBacked
class KotlinUClass(
@@ -48,17 +50,25 @@ class KotlinUClass(
override val fqName: String?
get() = psi.fqName?.asString()
override val isEnum: Boolean
get() = (psi as? KtClass)?.isEnum() ?: false
override val kind by lz {
when {
psi.isAnnotation() -> UastClassKind.ANNOTATION
(psi as? KtObjectDeclaration)?.isCompanion() ?: false -> {
if (psi.name == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT.toString())
KotlinClassKinds.DEFAULT_COMPANION_OBJECT
else
KotlinClassKinds.NAMED_COMPANION_OBJECT
}
psi is KtObjectDeclaration -> UastClassKind.OBJECT
(psi as? KtClass)?.isInterface() ?: false -> UastClassKind.INTERFACE
(psi as? KtClass)?.isEnum() ?: false -> UastClassKind.ENUM
else -> UastClassKind.CLASS
}
}
override val isInterface: Boolean
get() = (psi as? KtClass)?.isInterface() ?: false
override val isObject: Boolean
get() = psi is KtObjectDeclaration
override val isAnnotation: Boolean
get() = psi.isAnnotation()
override val companions by lz {
(psi as? KtClass)?.getCompanionObjects()?.map { KotlinConverter.convert(it, this) } ?: emptyList()
}
override val isAnonymous = false
@@ -20,10 +20,7 @@ import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtAnnotated
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -32,8 +29,7 @@ import org.jetbrains.kotlin.uast.kinds.KotlinUastVisibilities
import org.jetbrains.uast.*
private val MODIFIER_MAP = mapOf(
UastModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD,
UastModifier.INNER to KtTokens.INNER_KEYWORD
UastModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD
)
internal fun KtDeclaration.getVisibility() = when (visibilityModifierType()) {
@@ -44,6 +40,16 @@ internal fun KtDeclaration.getVisibility() = when (visibilityModifierType()) {
}
internal fun KtModifierListOwner.hasModifier(modifier: UastModifier): Boolean {
if (modifier == UastModifier.STATIC) {
return this is KtObjectDeclaration || !hasModifier(KtTokens.INNER_KEYWORD)
}
if (modifier == UastModifier.FINAL) {
if (hasModifier(KtTokens.FINAL_KEYWORD)) return true
if (this is KtProperty && !this.isVar) return true
return false
}
val javaModifier = MODIFIER_MAP[modifier] ?: return false
return hasModifier(javaModifier)
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-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.kotlin.uast
import org.jetbrains.uast.kinds.UastClassKind
object KotlinClassKinds {
@JvmField
val DEFAULT_COMPANION_OBJECT = UastClassKind.UastCompanionObject(true)
@JvmField
val NAMED_COMPANION_OBJECT = UastClassKind.UastCompanionObject(false)
}