mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-05 01:10:48 +00:00
Add lint detection for System.out.println add kotlin.io.println usage.
This commit is contained in:
committed by
Jeffrey Starke
parent
9e1cec7a60
commit
c901639ce8
@@ -196,7 +196,6 @@ class ConversationListFilterPullView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun openImmediate() {
|
||||
println("openImmediate from $state")
|
||||
if (state == FilterPullState.CLOSED) {
|
||||
setState(FilterPullState.OPEN_APEX, source)
|
||||
setState(FilterPullState.OPENING, source)
|
||||
|
||||
@@ -22,7 +22,9 @@ class Registry : IssueRegistry() {
|
||||
RecipientIdDatabaseDetector.RECIPIENT_ID_DATABASE_REFERENCE_ISSUE,
|
||||
ThreadIdDatabaseDetector.THREAD_ID_DATABASE_REFERENCE_ISSUE,
|
||||
StartForegroundServiceDetector.START_FOREGROUND_SERVICE_ISSUE,
|
||||
CardViewDetector.CARD_VIEW_USAGE
|
||||
CardViewDetector.CARD_VIEW_USAGE,
|
||||
SystemOutPrintLnDetector.SYSTEM_OUT_PRINTLN_USAGE,
|
||||
SystemOutPrintLnDetector.KOTLIN_IO_PRINTLN_USAGE
|
||||
)
|
||||
|
||||
override val api = CURRENT_API
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package org.signal.lint
|
||||
|
||||
import com.android.tools.lint.detector.api.Category.Companion.MESSAGES
|
||||
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.Companion.JAVA_FILE_SCOPE
|
||||
import com.android.tools.lint.detector.api.Severity.ERROR
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.uast.UCallExpression
|
||||
import org.jetbrains.uast.UExpression
|
||||
import org.jetbrains.uast.UQualifiedReferenceExpression
|
||||
|
||||
/**
|
||||
* Lint detector that flags usage of System.out.println and kotlin.io.println methods.
|
||||
*/
|
||||
class SystemOutPrintLnDetector : Detector(), Detector.UastScanner {
|
||||
|
||||
override fun getApplicableMethodNames(): List<String> {
|
||||
return listOf("println", "print")
|
||||
}
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
|
||||
val evaluator = context.evaluator
|
||||
|
||||
if (evaluator.isMemberInClass(method, "java.io.PrintStream")) {
|
||||
if (isSystemOutCall(node.receiver)) {
|
||||
context.report(
|
||||
issue = SYSTEM_OUT_PRINTLN_USAGE,
|
||||
scope = node,
|
||||
location = context.getLocation(node),
|
||||
message = "Using 'System.out.${method.name}' instead of Signal Logger",
|
||||
quickfixData = createQuickFix(node)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for kotlin.io.println (top-level function)
|
||||
if (method.name == "println" && evaluator.isMemberInClass(method, "kotlin.io.ConsoleKt")) {
|
||||
context.report(
|
||||
issue = KOTLIN_IO_PRINTLN_USAGE,
|
||||
scope = node,
|
||||
location = context.getLocation(node),
|
||||
message = "Using 'kotlin.io.println' instead of Signal Logger.",
|
||||
quickfixData = createQuickFix(node)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSystemOutCall(receiver: UExpression?): Boolean {
|
||||
return receiver is UQualifiedReferenceExpression &&
|
||||
receiver.selector.asRenderString() == "out" &&
|
||||
receiver.receiver.asRenderString().endsWith("System")
|
||||
}
|
||||
|
||||
private fun createQuickFix(node: UCallExpression): LintFix {
|
||||
val arguments = node.valueArguments
|
||||
val message = if (arguments.isNotEmpty()) arguments[0].asSourceString() else "\"\""
|
||||
|
||||
val fixSource = "org.signal.core.util.logging.Log.d(TAG, $message)"
|
||||
|
||||
return fix()
|
||||
.group()
|
||||
.add(
|
||||
fix()
|
||||
.replace()
|
||||
.text(node.sourcePsi?.text)
|
||||
.shortenNames()
|
||||
.reformat(true)
|
||||
.with(fixSource)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val SYSTEM_OUT_PRINTLN_USAGE: Issue = Issue.create(
|
||||
id = "SystemOutPrintLnUsage",
|
||||
briefDescription = "Usage of System.out.println/print",
|
||||
explanation = "System.out.println/print should not be used in production code. Use Signal Logger instead.",
|
||||
category = MESSAGES,
|
||||
priority = 5,
|
||||
severity = ERROR,
|
||||
implementation = Implementation(SystemOutPrintLnDetector::class.java, JAVA_FILE_SCOPE)
|
||||
)
|
||||
|
||||
val KOTLIN_IO_PRINTLN_USAGE: Issue = Issue.create(
|
||||
id = "KotlinIOPrintLnUsage",
|
||||
briefDescription = "Usage of kotlin.io.println",
|
||||
explanation = "kotlin.io.println should not be used in production code. Use proper logging instead.",
|
||||
category = MESSAGES,
|
||||
priority = 5,
|
||||
severity = ERROR,
|
||||
implementation = Implementation(SystemOutPrintLnDetector::class.java, JAVA_FILE_SCOPE)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package org.signal.lint
|
||||
|
||||
import com.android.tools.lint.checks.infrastructure.TestFiles.java
|
||||
import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin
|
||||
import com.android.tools.lint.checks.infrastructure.TestLintTask
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.util.Scanner
|
||||
|
||||
class SystemOutPrintLnDetectorTest {
|
||||
|
||||
@Test
|
||||
fun systemOutPrintlnUsed_Java() {
|
||||
TestLintTask.lint()
|
||||
.allowMissingSdk()
|
||||
.files(
|
||||
java(
|
||||
"""
|
||||
package foo;
|
||||
public class Example {
|
||||
public void log() {
|
||||
System.out.println("Hello World");
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
.issues(SystemOutPrintLnDetector.SYSTEM_OUT_PRINTLN_USAGE)
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/foo/Example.java:4: Error: Using 'System.out.println' instead of proper logging [SystemOutPrintLnUsage]
|
||||
System.out.println("Hello World");
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
1 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
.expectFixDiffs(
|
||||
"""
|
||||
Fix for src/foo/Example.java line 4: Replace with org.signal.core.util.logging.Log.d(TAG, "Hello World"):
|
||||
@@ -4 +4
|
||||
- System.out.println("Hello World");
|
||||
+ org.signal.core.util.logging.Log.d(TAG, "Hello World");
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun systemOutPrintUsed_Java() {
|
||||
TestLintTask.lint()
|
||||
.allowMissingSdk()
|
||||
.files(
|
||||
java(
|
||||
"""
|
||||
package foo;
|
||||
public class Example {
|
||||
public void log() {
|
||||
System.out.print("Hello");
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
.issues(SystemOutPrintLnDetector.SYSTEM_OUT_PRINTLN_USAGE)
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/foo/Example.java:4: Error: Using 'System.out.print' instead of proper logging [SystemOutPrintLnUsage]
|
||||
System.out.print("Hello");
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
1 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
.expectFixDiffs(
|
||||
"""
|
||||
Fix for src/foo/Example.java line 4: Replace with org.signal.core.util.logging.Log.d(TAG, "Hello"):
|
||||
@@ -4 +4
|
||||
- System.out.print("Hello");
|
||||
+ org.signal.core.util.logging.Log.d(TAG, "Hello");
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun kotlinIOPrintlnUsed_Kotlin() {
|
||||
TestLintTask.lint()
|
||||
.allowMissingSdk()
|
||||
.files(
|
||||
kotlinIOStub,
|
||||
kotlin(
|
||||
"""
|
||||
package foo
|
||||
import kotlin.io.println
|
||||
class Example {
|
||||
fun log() {
|
||||
println("Hello World")
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
.issues(SystemOutPrintLnDetector.KOTLIN_IO_PRINTLN_USAGE)
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/foo/Example.kt:5: Error: Using 'kotlin.io.println' instead of proper logging [KotlinIOPrintLnUsage]
|
||||
println("Hello World")
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
1 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
.expectFixDiffs(
|
||||
"""
|
||||
Fix for src/foo/Example.kt line 5: Replace with org.signal.core.util.logging.Log.d(TAG, "Hello World"):
|
||||
@@ -5 +5
|
||||
- println("Hello World")
|
||||
+ org.signal.core.util.logging.Log.d(TAG, "Hello World")
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun kotlinIOPrintlnUsed_TopLevel_Kotlin() {
|
||||
TestLintTask.lint()
|
||||
.allowMissingSdk()
|
||||
.files(
|
||||
kotlinIOStub,
|
||||
kotlin(
|
||||
"""
|
||||
package foo
|
||||
fun example() {
|
||||
println("Hello World")
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
.issues(SystemOutPrintLnDetector.KOTLIN_IO_PRINTLN_USAGE)
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/foo/test.kt:3: Error: Using 'kotlin.io.println' instead of proper logging [KotlinIOPrintLnUsage]
|
||||
println("Hello World")
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
1 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
.expectFixDiffs(
|
||||
"""
|
||||
Fix for src/foo/test.kt line 3: Replace with org.signal.core.util.logging.Log.d(TAG, "Hello World"):
|
||||
@@ -3 +3
|
||||
- println("Hello World")
|
||||
+ org.signal.core.util.logging.Log.d(TAG, "Hello World")
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun systemOutPrintlnWithNoArgs_Java() {
|
||||
TestLintTask.lint()
|
||||
.allowMissingSdk()
|
||||
.files(
|
||||
java(
|
||||
"""
|
||||
package foo;
|
||||
public class Example {
|
||||
public void log() {
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
.issues(SystemOutPrintLnDetector.SYSTEM_OUT_PRINTLN_USAGE)
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/foo/Example.java:4: Error: Using 'System.out.println' instead of proper logging [SystemOutPrintLnUsage]
|
||||
System.out.println();
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
1 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
.expectFixDiffs(
|
||||
"""
|
||||
Fix for src/foo/Example.java line 4: Replace with org.signal.core.util.logging.Log.d(TAG, ""):
|
||||
@@ -4 +4
|
||||
- System.out.println();
|
||||
+ org.signal.core.util.logging.Log.d(TAG, "");
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun regularPrintStreamMethodsNotFlagged() {
|
||||
TestLintTask.lint()
|
||||
.allowMissingSdk()
|
||||
.files(
|
||||
java(
|
||||
"""
|
||||
package foo;
|
||||
import java.io.PrintStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
public class Example {
|
||||
public void log() {
|
||||
PrintStream ps = new PrintStream(new ByteArrayOutputStream());
|
||||
ps.println("This should not be flagged");
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
.issues(
|
||||
SystemOutPrintLnDetector.SYSTEM_OUT_PRINTLN_USAGE,
|
||||
SystemOutPrintLnDetector.KOTLIN_IO_PRINTLN_USAGE
|
||||
)
|
||||
.run()
|
||||
.expectClean()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val kotlinIOStub = kotlin(readResourceAsString("KotlinIOStub.kt"))
|
||||
|
||||
private fun readResourceAsString(resourceName: String): String {
|
||||
val inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourceName)
|
||||
assertNotNull(inputStream)
|
||||
val scanner = Scanner(inputStream!!).useDelimiter("\\A")
|
||||
assertTrue(scanner.hasNext())
|
||||
return scanner.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
18
lintchecks/src/test/resources/KotlinIOStub.kt
Normal file
18
lintchecks/src/test/resources/KotlinIOStub.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
@file:JvmName("ConsoleKt")
|
||||
|
||||
package kotlin.io
|
||||
|
||||
/**
|
||||
* Stub for kotlin.io.println function for testing purposes
|
||||
*/
|
||||
fun println(message: Any?) {
|
||||
// Stub implementation
|
||||
}
|
||||
|
||||
fun println() {
|
||||
// Stub implementation
|
||||
}
|
||||
|
||||
fun print(message: Any?) {
|
||||
// Stub implementation
|
||||
}
|
||||
Reference in New Issue
Block a user