在Kotlin中,我可以将lint应用于继承我指定的类的所有类的构造函数吗?

问题描述

class SomeDetector : Detector(),SourceCodeScanner {
    
    
    override fun getApplicableConstructorTypes(): List<String>? {
        return listof(PARENT_CLASS)
    }

    override fun visitConstructor(context: JavaContext,node: UCallExpression,constructor: PsiMethod) {
        // blabla...
    }
}

好吧,我甚至成功地将lint应用于每个类的各个构造函数

但是,我要验证的类有数百种,它们都继承了一个公共接口。

所以我想验证all classes inheriting the interface I specified的构造函数

我要验证的类具有android依赖关系,因此不能在像Linux库这样的lint模块中直接使用像反射之类的库。

您能帮我满足我的要求吗?

解决方法

我试了你的问题。我检查了批注中的值和所使用的参数是否相等。您可以调整代码以适合您的要求。这是一个示例Detector类,其中我已经使用注释提供了解释。您可以对此进行改进。

class InvalidConstructorCallDetector : Detector(),Detector.UastScanner {

    // Check for call expressions
    override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)

    override fun createUastHandler(context: JavaContext) = object : UElementHandler() {

        override fun visitCallExpression(node: UCallExpression) {
            // Check if call is constructor call and if the class referenced inherits your interface
            if (node.isConstructorCall() &&
                context.doesInherit(node,"com.example.android.YourInterface")
            ) {
                val constructor = node.resolve()!!

                // Get the first parameter. You may use a loop and check for all parameter or whatever you require
                val param = constructor.parameterList.parameters[0]

                // Get your annotation
                val paramAnnotation =
                    param.getAnnotation("com.example.android.YourAnnotation")!!

                // Get the value you specified in the annotation for the constructor declaration
                val attributeValue = paramAnnotation.findAttributeValue("value")!!.text.toInt()

                // Get the argument used for first parameter. Again,you can have this in a loop and use index
                val argumentValue = node.getArgumentForParameter(0)!!.evaluate().toString().toInt()

                if (attributeValue != argumentValue) // Checking for equality. Perform whatever check you want
                {
                    context.report(
                        ISSUE,node,context.getNameLocation(node),"Argument value($argumentValue) is invalid. Valid argument: $attributeValue"
                    )
                }
            }
        }
    }

    // Check to see if class referenced by constructor call implements interface
    private fun JavaContext.doesInherit(node: UCallExpression,typeName: String): Boolean {
        for (type in node.classReference!!.getExpressionType()!!.superTypes) {
            if (evaluator.typeMatches(type,typeName)) return true
        }

        return false
    }

    companion object {
        val ISSUE = Issue.create(
            "InvalidConstructorCall","Invalid arguments in constructor call","Only values defined in annotation in constructor declaration are allowed",Category.CORRECTNESS,10,Severity.ERROR,Implementation(
                InvalidConstructorCallDetector::class.java,EnumSet.of(Scope.JAVA_FILE)
            )
        )
    }
}