blob: 93632375562c25740dc9592ebae135ffa600052c [file]
/*
* Copyright 2024 The Android Open Source Project
*
* 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 androidx.navigation.lint
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.getParameterForArgument
abstract class BaseWrongStartDestinationTypeDetector(
private val methodNames: List<String>,
private val parameterNames: List<String>,
) : Detector(), SourceCodeScanner {
final override fun getApplicableMethodNames(): List<String> = methodNames
final override fun visitMethodCall(
context: JavaContext,
node: UCallExpression,
method: PsiMethod,
) {
val startNode =
node.valueArguments.find {
parameterNames.contains(node.getParameterForArgument(it)?.name)
} ?: return
val (isClassType, name) = startNode.isClassReference()
if (isClassType) {
context.report(
getIssue(),
startNode,
context.getNameLocation(startNode as UElement),
"""
StartDestination should not be a simple class name reference.
Did you mean to call its constructor $name(...)?
If the class $name does not contain arguments,
you can also pass in its KClass reference $name::class
"""
.trimIndent(),
)
}
}
abstract fun getIssue(): Issue
}
fun createWrongStartDestinationTypeIssue(
detectorClass: Class<out BaseWrongStartDestinationTypeDetector>
) =
Issue.create(
id = "WrongStartDestinationType",
briefDescription =
"If the startDestination points to a Class with arguments, the " +
"startDestination must be an instance of that class. If it points to a " +
"Class without arguments, startDestination can be a KClass literal, such as" +
" StartClass::class.",
explanation =
"If the startDestination contains arguments, the arguments must be " +
"provided to navigation via a fully formed route (a class instance with arguments" +
"filled in), or else it will be treated as a case of missing arguments.",
category = Category.CORRECTNESS,
severity = Severity.ERROR,
implementation = Implementation(detectorClass, Scope.JAVA_FILE_SCOPE),
)