Есть ли простой способ написать детектор FindBugs для сравнения?

В настоящее время я хотел бы написать некоторый инструмент статического анализа, который анализирует базу кода Java и помогает обнаружить любую ситуацию, в которой вы сравниваете java Необязательный и нулевой.

Так что возможный код вроде:

    Optional result = method();
    if (result != null) {
         //do something
    } else {
         //never reached because method should never return null
    }

Когда я посмотрел на возможное расширение FindBugs, основной детектор сравнения, который я нашел, был здесь: https://github.com/findbugsproject/findbugs/blob/d1e60f8dbeda0a454f2d497ef8dcb878fa8e3852/findbugs/src/java/edu/umd/cs/findbugs/detect/FindRefComparison.java

Это очень много кода, чтобы понять, как написать что-то, что могло бы сделать это обнаружение сравнения, и которое, кажется, не так легко расширить.

Хотя этот вид инструмента статического анализа был бы полезен для нас, он помог бы предотвратить ситуации, когда кто-то решил изменить сигнатуру метода для функции с объекта, который может быть или не быть нулевым, на объект, который всегда возвращает java Необязательно, но не обновил все места, где он использовался, но это не так ценно, я могу потратить время на то, чтобы тратить время на попытки реализовать его в FindBugs.

  1. Достаточно ли я разбираю кодовую базу FindBugs, чтобы для добавления такого рода проверки потребовались какие-то усилия?
  2. Есть ли более простой способ получить то, что я хочу?

1 ответ

Решение
private static final ImmutableSet<String> OPTIONAL_CLASSES =
      ImmutableSet.of(com.google.common.base.Optional.class.getName(), "java.util.Optional");

private static final Set<Kind> COMPARISON_OPERATORS =
          EnumSet.of(Kind.EQUAL_TO, Kind.NOT_EQUAL_TO);

private static boolean isNull(ExpressionTree tree) {
    return tree.getKind() == Kind.NULL_LITERAL;
}

private static boolean isOptional(ExpressionTree tree, VisitorState state) {
        Type type = ASTHelpers.getType(tree);
    for (String className : OPTIONAL_CLASSES) {
            if (ASTHelpers.isSameType(type, state.getTypeFromString(className), state)) {
                    return true;
            }
    }
    return false;
}

private boolean isSuppressed(Tree tree, String suppression) {
    SuppressWarnings annotation = ASTHelpers.getAnnotation(ASTHelpers.getSymbol(tree), SuppressWarnings.class);
    return annotation != null && Arrays.stream(annotation.value()).anyMatch(suppression::equals);
}

@Override
public Description matchBinary(BinaryTree tree, VisitorState state) {
    if (!COMPARISON_OPERATORS.contains(tree.getKind())) {
        return Description.NO_MATCH;
    }

    ExpressionTree leftOperand = tree.getLeftOperand();
    ExpressionTree rightOperand = tree.getRightOperand();
    if (isNull(leftOperand) && isOptional(rightOperand, state) ||
            isNull(rightOperand) && isOptional(leftOperand, state)) {
            return describeMatch(tree);
    }

    return Description.NO_MATCH;
}

Я думаю, что я получил это работает в ErrorProne. Если я получу несколько тестов для этого, я передам их сообществу.

Другие вопросы по тегам