<commit_before>package timber.lint;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.checks.StringFormatDetector;
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ClassContext;
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.Speed;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import lombok.ast.AstVisitor;
import lombok.ast.BinaryExpression;
import lombok.ast.BinaryOperator;
import lombok.ast.BooleanLiteral;
import lombok.ast.CharLiteral;
import lombok.ast.Expression;
import lombok.ast.ExpressionStatement;
import lombok.ast.FloatingPointLiteral;
import lombok.ast.If;
import lombok.ast.InlineIfExpression;
import lombok.ast.IntegralLiteral;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.NullLiteral;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import lombok.ast.VariableReference;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;

import static com.android.tools.lint.client.api.JavaParser.TYPE_BOOLEAN;
import static com.android.tools.lint.client.api.JavaParser.TYPE_BYTE;
import static com.android.tools.lint.client.api.JavaParser.TYPE_CHAR;
import static com.android.tools.lint.client.api.JavaParser.TYPE_DOUBLE;
import static com.android.tools.lint.client.api.JavaParser.TYPE_FLOAT;
import static com.android.tools.lint.client.api.JavaParser.TYPE_INT;
import static com.android.tools.lint.client.api.JavaParser.TYPE_LONG;
import static com.android.tools.lint.client.api.JavaParser.TYPE_NULL;
import static com.android.tools.lint.client.api.JavaParser.TYPE_OBJECT;
import static com.android.tools.lint.client.api.JavaParser.TYPE_SHORT;
import static com.android.tools.lint.client.api.JavaParser.TYPE_STRING;

public final class WrongTimberUsageDetector extends Detector implements Detector.JavaScanner,
    Detector.ClassScanner {
  private final static String GET_STRING_METHOD = "getString";

  @NonNull @Override public Speed getSpeed() {
    return Speed.NORMAL;
  }

  @Override public List<String> getApplicableCallNames() {
    return Arrays.asList("v", "d", "i", "w", "e", "wtf");
  }

  @Override public List<String> getApplicableMethodNames() {
    return Arrays.asList("tag", "format", "v", "d", "i", "w", "e", "wtf");
  }

  @Override public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode,
      @NonNull MethodNode method, @NonNull MethodInsnNode call) {
    String owner = call.owner;
    if (owner.startsWith("android/util/Log")) {
      context.report(ISSUE_LOG, method, call, context.getLocation(call),
          "Using 'Log' instead of 'Timber'");
    }
  }

  @Override public void visitMethod(@NonNull JavaContext context, AstVisitor visitor,
      @NonNull MethodInvocation node) {
    String methodName = node.astName().getDescription();
    if ("format".equals(methodName)) {
      if (!(node.astOperand() instanceof VariableReference)) {
        return;
      }
      VariableReference ref = (VariableReference) node.astOperand();
      if (!"String".equals(ref.astIdentifier().astValue())) {
        return;
      }
      // Found a String.format call
      // Look outside to see if we inside of a Timber call
      Node current = node.getParent();
      while (current != null && !(current instanceof ExpressionStatement)) {
        current = current.getParent();
      }
      if (current == null) {
        return;
      }
      ExpressionStatement statement = (ExpressionStatement) current;
      if (!statement.toString().startsWith("Timber.")) {
        return;
      }
      context.report(ISSUE_FORMAT, node, context.getLocation(node),
          "Using 'String#format' inside of 'Timber'");
    } else if ("tag".equals(methodName)) {
      Node argument = node.astArguments().iterator().next();
      String tag = findLiteralValue(context, argument);
      if (tag.length() > 23) {
        String message = String.format(
            "The logging tag can be at most 23 characters, was %1$d (%2$s)",
            tag.length(), tag);
        context.report(ISSUE_TAG_LENGTH, node, context.getLocation(argument), message);
      }
    } else {
      if (node.astOperand() instanceof VariableReference) {
        VariableReference ref = (VariableReference) node.astOperand();
        if (!"Timber".equals(ref.astIdentifier().astValue())) {
          return;
        }
        checkThrowablePosition(context, node);
        checkArguments(context, node);
      }
    }
  }

  private static void checkArguments(JavaContext context, MethodInvocation node) {
    StrictListAccessor<Expression, MethodInvocation> astArguments = node.astArguments();
    Iterator<Expression> iterator = astArguments.iterator();
    if (!iterator.hasNext()) {<commit_msg>Null check tag, there may be no literal value.<commit_after>package timber.lint;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.checks.StringFormatDetector;
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ClassContext;
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.Speed;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import lombok.ast.AstVisitor;
import lombok.ast.BinaryExpression;
import lombok.ast.BinaryOperator;
import lombok.ast.BooleanLiteral;
import lombok.ast.CharLiteral;
import lombok.ast.Expression;
import lombok.ast.ExpressionStatement;
import lombok.ast.FloatingPointLiteral;
import lombok.ast.If;
import lombok.ast.InlineIfExpression;
import lombok.ast.IntegralLiteral;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.NullLiteral;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import lombok.ast.VariableReference;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;

import static com.android.tools.lint.client.api.JavaParser.TYPE_BOOLEAN;
import static com.android.tools.lint.client.api.JavaParser.TYPE_BYTE;
import static com.android.tools.lint.client.api.JavaParser.TYPE_CHAR;
import static com.android.tools.lint.client.api.JavaParser.TYPE_DOUBLE;
import static com.android.tools.lint.client.api.JavaParser.TYPE_FLOAT;
import static com.android.tools.lint.client.api.JavaParser.TYPE_INT;
import static com.android.tools.lint.client.api.JavaParser.TYPE_LONG;
import static com.android.tools.lint.client.api.JavaParser.TYPE_NULL;
import static com.android.tools.lint.client.api.JavaParser.TYPE_OBJECT;
import static com.android.tools.lint.client.api.JavaParser.TYPE_SHORT;
import static com.android.tools.lint.client.api.JavaParser.TYPE_STRING;

public final class WrongTimberUsageDetector extends Detector implements Detector.JavaScanner,
    Detector.ClassScanner {
  private final static String GET_STRING_METHOD = "getString";

  @NonNull @Override public Speed getSpeed() {
    return Speed.NORMAL;
  }

  @Override public List<String> getApplicableCallNames() {
    return Arrays.asList("v", "d", "i", "w", "e", "wtf");
  }

  @Override public List<String> getApplicableMethodNames() {
    return Arrays.asList("tag", "format", "v", "d", "i", "w", "e", "wtf");
  }

  @Override public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode,
      @NonNull MethodNode method, @NonNull MethodInsnNode call) {
    String owner = call.owner;
    if (owner.startsWith("android/util/Log")) {
      context.report(ISSUE_LOG, method, call, context.getLocation(call),
          "Using 'Log' instead of 'Timber'");
    }
  }

  @Override public void visitMethod(@NonNull JavaContext context, AstVisitor visitor,
      @NonNull MethodInvocation node) {
    String methodName = node.astName().getDescription();
    if ("format".equals(methodName)) {
      if (!(node.astOperand() instanceof VariableReference)) {
        return;
      }
      VariableReference ref = (VariableReference) node.astOperand();
      if (!"String".equals(ref.astIdentifier().astValue())) {
        return;
      }
      // Found a String.format call
      // Look outside to see if we inside of a Timber call
      Node current = node.getParent();
      while (current != null && !(current instanceof ExpressionStatement)) {
        current = current.getParent();
      }
      if (current == null) {
        return;
      }
      ExpressionStatement statement = (ExpressionStatement) current;
      if (!statement.toString().startsWith("Timber.")) {
        return;
      }
      context.report(ISSUE_FORMAT, node, context.getLocation(node),
          "Using 'String#format' inside of 'Timber'");
    } else if ("tag".equals(methodName)) {
      Node argument = node.astArguments().iterator().next();
      String tag = findLiteralValue(context, argument);
      if (tag != null && tag.length() > 23) {
        String message = String.format(
            "The logging tag can be at most 23 characters, was %1$d (%2$s)",
            tag.length(), tag);
        context.report(ISSUE_TAG_LENGTH, node, context.getLocation(argument), message);
      }
    } else {
      if (node.astOperand() instanceof VariableReference) {
        VariableReference ref = (VariableReference) node.astOperand();
        if (!"Timber".equals(ref.astIdentifier().astValue())) {
          return;
        }
        checkThrowablePosition(context, node);
        checkArguments(context, node);
      }
    }
  }

  private static void checkArguments(JavaContext context, MethodInvocation node) {
    StrictListAccessor<Expression, MethodInvocation> astArguments = node.astArguments();
    Iterator<Expression> iterator = astArguments.iterator();
    if (!iterator.hasNext()) {
{'fraction_non_alphanumeric': 0.12011225444340505, 'fraction_numerical': 0.0011225444340505146, 'mean_word_length': 4.1697292069632494, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1463951', 'n_tokens_mistral': 3176, 'n_tokens_neox': 3167, 'n_words': 660}