diff --git a/app/lint.xml b/app/lint.xml
index 520c72361..78c972738 100644
--- a/app/lint.xml
+++ b/app/lint.xml
@@ -19,6 +19,8 @@
+
+
diff --git a/lintchecks/src/main/java/org/signal/lint/Registry.java b/lintchecks/src/main/java/org/signal/lint/Registry.java
index c03c3d672..56446cadc 100644
--- a/lintchecks/src/main/java/org/signal/lint/Registry.java
+++ b/lintchecks/src/main/java/org/signal/lint/Registry.java
@@ -4,7 +4,7 @@ import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.ApiKt;
import com.android.tools.lint.detector.api.Issue;
-import java.util.Collections;
+import java.util.Arrays;
import java.util.List;
@SuppressWarnings("UnstableApiUsage")
@@ -12,7 +12,9 @@ public final class Registry extends IssueRegistry {
@Override
public List getIssues() {
- return Collections.singletonList(SignalLogDetector.LOG_NOT_SIGNAL);
+ return Arrays.asList(SignalLogDetector.LOG_NOT_SIGNAL,
+ SignalLogDetector.LOG_NOT_APP,
+ SignalLogDetector.INLINE_TAG);
}
@Override
diff --git a/lintchecks/src/main/java/org/signal/lint/SignalLogDetector.java b/lintchecks/src/main/java/org/signal/lint/SignalLogDetector.java
index dad320ffe..42e6e914b 100644
--- a/lintchecks/src/main/java/org/signal/lint/SignalLogDetector.java
+++ b/lintchecks/src/main/java/org/signal/lint/SignalLogDetector.java
@@ -14,6 +14,7 @@ import com.intellij.psi.PsiMethod;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.UCallExpression;
import org.jetbrains.uast.UExpression;
+import org.jetbrains.uast.java.JavaUSimpleNameReferenceExpression;
import java.util.Arrays;
import java.util.List;
@@ -29,6 +30,22 @@ public final class SignalLogDetector extends Detector implements Detector.UastSc
Severity.ERROR,
new Implementation(SignalLogDetector.class, Scope.JAVA_FILE_SCOPE));
+ static final Issue LOG_NOT_APP = Issue.create("LogNotAppSignal",
+ "Logging call to Signal Service Log instead of App level Logger",
+ "Signal app layer has its own logger which must be used.",
+ Category.MESSAGES,
+ 5,
+ Severity.ERROR,
+ new Implementation(SignalLogDetector.class, Scope.JAVA_FILE_SCOPE));
+
+ static final Issue INLINE_TAG = Issue.create("LogTagInlined",
+ "Use of an inline string in a TAG",
+ "Often a sign of left in temporary log statements, always use a tag constant.",
+ Category.MESSAGES,
+ 5,
+ Severity.ERROR,
+ new Implementation(SignalLogDetector.class, Scope.JAVA_FILE_SCOPE));
+
@Override
public List getApplicableMethodNames() {
return Arrays.asList("v", "d", "i", "w", "e", "wtf");
@@ -42,6 +59,19 @@ public final class SignalLogDetector extends Detector implements Detector.UastSc
LintFix fix = quickFixIssueLog(call);
context.report(LOG_NOT_SIGNAL, call, context.getLocation(call), "Using 'android.util.Log' instead of a Signal Logger", fix);
}
+
+ if (evaluator.isMemberInClass(method, "org.whispersystems.libsignal.logging.Log")) {
+ LintFix fix = quickFixIssueLog(call);
+ context.report(LOG_NOT_APP, call, context.getLocation(call), "Using Signal server logger instead of app level Logger", fix);
+ }
+
+ if (evaluator.isMemberInClass(method, "org.thoughtcrime.securesms.logging.Log")) {
+ List arguments = call.getValueArguments();
+ UExpression tag = arguments.get(0);
+ if (!(tag instanceof JavaUSimpleNameReferenceExpression)) {
+ context.report(INLINE_TAG, call, context.getLocation(call), "Not using a tag constant");
+ }
+ }
}
private LintFix quickFixIssueLog(@NotNull UCallExpression logCall) {
@@ -64,7 +94,7 @@ public final class SignalLogDetector extends Detector implements Detector.UastSc
break;
default:
- throw new IllegalStateException("android.util.Log overloads should have 2 or 3 arguments");
+ throw new IllegalStateException("Log overloads should have 2 or 3 arguments");
}
String logCallSource = logCall.asSourceString();
diff --git a/lintchecks/src/test/java/org/signal/lint/LogDetectorTest.java b/lintchecks/src/test/java/org/signal/lint/LogDetectorTest.java
index 77b8404b3..08f693ffd 100644
--- a/lintchecks/src/test/java/org/signal/lint/LogDetectorTest.java
+++ b/lintchecks/src/test/java/org/signal/lint/LogDetectorTest.java
@@ -1,12 +1,22 @@
package org.signal.lint;
+import com.android.tools.lint.checks.infrastructure.TestFile;
+
import org.junit.Test;
+import java.io.InputStream;
+import java.util.Scanner;
+
import static com.android.tools.lint.checks.infrastructure.TestFiles.java;
import static com.android.tools.lint.checks.infrastructure.TestLintTask.lint;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
public final class LogDetectorTest {
+ private static final TestFile serviceLogStub = java(readResourceAsString("ServiceLogStub.java"));
+ private static final TestFile appLogStub = java(readResourceAsString("AppLogStub.java"));
+
@Test
public void androidLogUsed_LogNotSignal_2_args() {
lint()
@@ -54,4 +64,99 @@ public final class LogDetectorTest {
"- Log.w(\"TAG\", \"msg\", new Exception());\n" +
"+ org.thoughtcrime.securesms.logging.Log.w(\"TAG\", \"msg\", new Exception());");
}
+
+ @Test
+ public void signalServiceLogUsed_LogNotApp_2_args() {
+ lint()
+ .files(serviceLogStub,
+ java("package foo;\n" +
+ "import org.whispersystems.libsignal.logging.Log;\n" +
+ "public class Example {\n" +
+ " public void log() {\n" +
+ " Log.d(\"TAG\", \"msg\");\n" +
+ " }\n" +
+ "}")
+ )
+ .issues(SignalLogDetector.LOG_NOT_APP)
+ .run()
+ .expect("src/foo/Example.java:5: Error: Using Signal server logger instead of app level Logger [LogNotAppSignal]\n" +
+ " Log.d(\"TAG\", \"msg\");\n" +
+ " ~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings")
+ .expectFixDiffs("Fix for src/foo/Example.java line 5: Replace with org.thoughtcrime.securesms.logging.Log.d(\"TAG\", \"msg\"):\n" +
+ "@@ -5 +5\n" +
+ "- Log.d(\"TAG\", \"msg\");\n" +
+ "+ org.thoughtcrime.securesms.logging.Log.d(\"TAG\", \"msg\");");
+ }
+
+ @Test
+ public void signalServiceLogUsed_LogNotApp_3_args() {
+ lint()
+ .files(serviceLogStub,
+ java("package foo;\n" +
+ "import org.whispersystems.libsignal.logging.Log;\n" +
+ "public class Example {\n" +
+ " public void log() {\n" +
+ " Log.w(\"TAG\", \"msg\", new Exception());\n" +
+ " }\n" +
+ "}")
+ )
+ .issues(SignalLogDetector.LOG_NOT_APP)
+ .run()
+ .expect("src/foo/Example.java:5: Error: Using Signal server logger instead of app level Logger [LogNotAppSignal]\n" +
+ " Log.w(\"TAG\", \"msg\", new Exception());\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings")
+ .expectFixDiffs("Fix for src/foo/Example.java line 5: Replace with org.thoughtcrime.securesms.logging.Log.w(\"TAG\", \"msg\", new Exception()):\n" +
+ "@@ -5 +5\n" +
+ "- Log.w(\"TAG\", \"msg\", new Exception());\n" +
+ "+ org.thoughtcrime.securesms.logging.Log.w(\"TAG\", \"msg\", new Exception());");
+ }
+
+ @Test
+ public void log_uses_tag_constant() {
+ lint()
+ .files(appLogStub,
+ java("package foo;\n" +
+ "import org.thoughtcrime.securesms.logging.Log;\n" +
+ "public class Example {\n" +
+ " private static final String TAG = Log.tag(Example.class);\n" +
+ " public void log() {\n" +
+ " Log.d(TAG, \"msg\");\n" +
+ " }\n" +
+ "}")
+ )
+ .issues(SignalLogDetector.INLINE_TAG)
+ .run()
+ .expectClean();
+ }
+
+ @Test
+ public void log_uses_inline_tag() {
+ lint()
+ .files(appLogStub,
+ java("package foo;\n" +
+ "import org.thoughtcrime.securesms.logging.Log;\n" +
+ "public class Example {\n" +
+ " public void log() {\n" +
+ " Log.d(\"TAG\", \"msg\");\n" +
+ " }\n" +
+ "}")
+ )
+ .issues(SignalLogDetector.INLINE_TAG)
+ .run()
+ .expect("src/foo/Example.java:5: Error: Not using a tag constant [LogTagInlined]\n" +
+ " Log.d(\"TAG\", \"msg\");\n" +
+ " ~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings")
+ .expectFixDiffs("");
+ }
+
+ private static String readResourceAsString(String resourceName) {
+ InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourceName);
+ assertNotNull(inputStream);
+ Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
+ assertTrue(scanner.hasNext());
+ return scanner.next();
+ }
}
diff --git a/lintchecks/src/test/resources/AppLogStub.java b/lintchecks/src/test/resources/AppLogStub.java
new file mode 100644
index 000000000..94f3113d3
--- /dev/null
+++ b/lintchecks/src/test/resources/AppLogStub.java
@@ -0,0 +1,41 @@
+package org.thoughtcrime.securesms.logging;
+
+public class Log {
+
+ public static String tag(Class> clazz) {
+ return "";
+ }
+
+ public static void v(String tag, String msg) {
+ }
+
+ public static void v(String tag, String msg, Throwable tr) {
+ }
+
+ public static void d(String tag, String msg) {
+ }
+
+ public static void d(String tag, String msg, Throwable tr) {
+ }
+
+ public static void i(String tag, String msg) {
+ }
+
+ public static void i(String tag, String msg, Throwable tr) {
+ }
+
+ public static void w(String tag, String msg) {
+ }
+
+ public static void w(String tag, String msg, Throwable tr) {
+ }
+
+ public static void w(String tag, Throwable tr) {
+ }
+
+ public static void e(String tag, String msg) {
+ }
+
+ public static void e(String tag, String msg, Throwable tr) {
+ }
+}
diff --git a/lintchecks/src/test/resources/ServiceLogStub.java b/lintchecks/src/test/resources/ServiceLogStub.java
new file mode 100644
index 000000000..d68b230d7
--- /dev/null
+++ b/lintchecks/src/test/resources/ServiceLogStub.java
@@ -0,0 +1,37 @@
+package org.whispersystems.libsignal.logging;
+
+public class Log {
+
+ public static void v(String tag, String msg) {
+ }
+
+ public static void v(String tag, String msg, Throwable tr) {
+ }
+
+ public static void d(String tag, String msg) {
+ }
+
+ public static void d(String tag, String msg, Throwable tr) {
+ }
+
+ public static void i(String tag, String msg) {
+ }
+
+ public static void i(String tag, String msg, Throwable tr) {
+ }
+
+ public static void w(String tag, String msg) {
+ }
+
+ public static void w(String tag, String msg, Throwable tr) {
+ }
+
+ public static void w(String tag, Throwable tr) {
+ }
+
+ public static void e(String tag, String msg) {
+ }
+
+ public static void e(String tag, String msg, Throwable tr) {
+ }
+}