mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-20 02:58:45 +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() {
|
fun openImmediate() {
|
||||||
println("openImmediate from $state")
|
|
||||||
if (state == FilterPullState.CLOSED) {
|
if (state == FilterPullState.CLOSED) {
|
||||||
setState(FilterPullState.OPEN_APEX, source)
|
setState(FilterPullState.OPEN_APEX, source)
|
||||||
setState(FilterPullState.OPENING, source)
|
setState(FilterPullState.OPENING, source)
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ class Registry : IssueRegistry() {
|
|||||||
RecipientIdDatabaseDetector.RECIPIENT_ID_DATABASE_REFERENCE_ISSUE,
|
RecipientIdDatabaseDetector.RECIPIENT_ID_DATABASE_REFERENCE_ISSUE,
|
||||||
ThreadIdDatabaseDetector.THREAD_ID_DATABASE_REFERENCE_ISSUE,
|
ThreadIdDatabaseDetector.THREAD_ID_DATABASE_REFERENCE_ISSUE,
|
||||||
StartForegroundServiceDetector.START_FOREGROUND_SERVICE_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
|
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