All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.mu.errorprone.StringFormatPlaceholderNamesCheck Maven / Gradle / Ivy

package com.google.mu.errorprone;


import static com.google.mu.util.Substring.after;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static com.google.errorprone.matchers.Matchers.constructor;
import static com.google.errorprone.matchers.Matchers.staticMethod;

import com.google.auto.service.AutoService;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableSet;
import com.google.mu.util.CharPredicate;
import com.google.mu.util.Substring;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.NewClassTree;

/** Restricts the placeholder names used in StringFormat and ResourceNamePattern. */
@BugPattern(
    summary = "Checks that valid placeholder names are used in string format.",
    link = "https://github.com/google/mug/wiki/StringFormat-Explained",
    linkType = LinkType.CUSTOM,
    severity = ERROR)
@AutoService(BugChecker.class)
public final class StringFormatPlaceholderNamesCheck extends AbstractBugChecker
    implements AbstractBugChecker.MethodInvocationCheck, AbstractBugChecker.ConstructorCallCheck {
  private static final Matcher MATCHER =
      Matchers.anyOf(
          constructor().forClass("com.google.mu.util.StringFormat"),
          staticMethod().onClass("com.google.mu.util.StringFormat"));
  private static final CharPredicate ALPHA =
      CharPredicate.range('a', 'z').orRange('A', 'Z');
  private static final CharPredicate VALID_CHARS =
      ALPHA.orRange('0', '9').or(CharMatcher.anyOf(".*_-")::matches);

  /** Currently allowed special placeholder names. */
  private static final ImmutableSet SPECIAL_PLACEHOLDER_NAMES = ImmutableSet.of("...");

  @Override
  public void checkMethodInvocation(MethodInvocationTree tree, VisitorState state)
      throws ErrorReport {
    doCheck(tree, state);
  }

  @Override
  public void checkConstructorCall(NewClassTree tree, VisitorState state) throws ErrorReport {
    doCheck(tree, state);
  }

  private void doCheck(ExpressionTree tree, VisitorState state) throws ErrorReport {
    if (!MATCHER.matches(tree, state)) {
      return;
    }
    String formatString = FormatStringUtils.findFormatString(tree, state).orElse(null);
    if (formatString == null) {
      return;
    }
    for (String placeholderName : FormatStringUtils.placeholderVariableNames(formatString)) {
      checkingOn(tree)
          .require(
              SPECIAL_PLACEHOLDER_NAMES.contains(placeholderName)
                  || isNormalPlaceholderName(placeholderName),
              "Invalid placeholder name {%s}",
              placeholderName);
    }
  }

  private static boolean isNormalPlaceholderName(String placeholderName) {
    return after(Substring.leading(ALPHA))
        .in(placeholderName)
        .filter(VALID_CHARS::matchesAllOf)
        .isPresent();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy