[go: nahoru, domu]

blob: 74296ff90fd6ca71b314744f8afc52294dd31d49 [file] [log] [blame]
/*
* Copyright 2020 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.
*/
@file:Suppress("UnstableApiUsage")
package androidx.compose.runtime.lint
import androidx.compose.lint.isComposable
import androidx.compose.lint.returnsUnit
import com.android.tools.lint.client.api.UElementHandler
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.LintFix
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 org.jetbrains.uast.UMethod
import java.util.EnumSet
import java.util.Locale
/**
* [Detector] that checks the naming of @Composable functions for consistency with guidelines.
*
* - @Composable functions that return Unit should follow typical class naming (PascalCase)
* - @Composable functions with a return type should follow typical function naming (camelCase)
*/
class ComposableNamingDetector : Detector(), SourceCodeScanner {
override fun getApplicableUastTypes() = listOf(UMethod::class.java)
override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
override fun visitMethod(node: UMethod) {
// Ignore non-composable functions
if (!node.isComposable) return
val name = node.name
val capitalizedFunctionName = name.first().isUpperCase()
if (node.returnsUnit) {
if (!capitalizedFunctionName) {
@Suppress("DEPRECATION") // b/187985877
val capitalizedName = name.capitalize(Locale.getDefault())
context.report(
ComposableNaming,
node,
context.getNameLocation(node),
"Composable functions that return Unit should start with an " +
"uppercase letter",
LintFix.create()
.replace()
.name("Change to $capitalizedName")
.text(name)
.with(capitalizedName)
.autoFix()
.build()
)
}
} else {
if (capitalizedFunctionName) {
@Suppress("DEPRECATION") // b/187985877
val lowercaseName = name.decapitalize(Locale.getDefault())
context.report(
ComposableNaming,
node,
context.getNameLocation(node),
"Composable functions with a return type should start with a " +
"lowercase letter",
LintFix.create()
.replace()
.name("Change to $lowercaseName")
.text(name)
.with(lowercaseName)
.autoFix()
.build()
)
}
}
}
}
companion object {
val ComposableNaming = Issue.create(
"ComposableNaming",
"Incorrect naming for @Composable functions",
"@Composable functions without a return type should use similar naming to " +
"classes, starting with an uppercase letter and ending with a noun. @Composable " +
"functions with a return type should be treated as normal Kotlin functions, " +
"starting with a lowercase letter.",
Category.CORRECTNESS, 3, Severity.WARNING,
Implementation(
ComposableNamingDetector::class.java,
EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
)
}
}