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:
@@ -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
|
||||
|
||||
+4
-2
@@ -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;
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
|
||||
+12
-6
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user