Как создать проверку Lint, чтобы убедиться, что Activity / Fragment прикреплен? - PullRequest
0 голосов
/ 24 ноября 2018

Я пытаюсь создать пользовательскую проверку Lint, чтобы определенные аннотированные методы должны сначала убедиться, что фрагмент / действие прикреплено / видимо, прежде чем выполнять какую-либо работу.

Например,

class MyFragment extends Fragment {

    @CheckIsActive
    void lint_fail() {
        // This would throw a lint warning
    }

    @CheckIsActive
    void lint_succeed() {
        if(isAdded()) {
            // This wouldn't throw a lint warning
        }
    }

}

Для этого я создал IssueRegistry, аннотацию @CheckIsActive и следующий пользовательский класс Detector.

public class CheckActiveDetector extends Detector implements Detector.UastScanner {

    private static final String CHECK_ACTIVE_ANNOTATION = Constants.ANNOTATIONS_PREFIX + "CheckIsActive";

    private static final Implementation IMPLEMENTATION = new Implementation(
            CheckActiveDetector.class,
            Scope.JAVA_FILE_SCOPE);

    public static final Issue ISSUE = Issue.create(
            "CheckActive",
            "Method should check if the activity/fragment is active",
            "This method should ensure the Activity/Fragment is active before continuing",
            Category.CORRECTNESS,
            9,
            Severity.WARNING,
            IMPLEMENTATION
    );


    public CheckActiveDetector() {}

    @Nullable
    @Override
    public List<Class<? extends UElement>> getApplicableUastTypes() {
        return Collections.<Class<? extends UElement>>singletonList(UMethod.class);
    }

    @Nullable
    @Override
    public UElementHandler createUastHandler(@NotNull final JavaContext context) {
        return new UElementHandler() {
            @Override
            public void visitMethod(@NotNull UMethod node) {


                UExpression body = node.getUastBody();
                if(body != null && node.findAnnotation(CHECK_ACTIVE_ANNOTATION) != null) {

                    String methodName = node.getName();
                    String message = "Overriding method should call `isAdded()"
                            + methodName + "`";
                    Location location = context.getLocation(node);
                    context.report(ISSUE, node, location, message);

                }
            }
        };
    }
}

Я изо всех сил пытался понять, как перейти от состояния, в котором я сейчас нахожусь, к которому (я верю) входил метод и правильно проверял, был ли он аннотирован.Я не уверен с методами детектора, как

  1. Проверить, расширяет ли содержащий класс Activity / Fragment
  2. Проверить, был ли вызван isAdded() для Fragment или isDestroyed() для Activity

Кто-нибудь знает, как это сделать, или знает, где что-то из этого задокументировано?Похоже, Google не использует последнюю версию lint в своем источнике (например, CallSuper source ), так что это не слишком помогло.

Спасибо

1 Ответ

0 голосов
/ 27 декабря 2018

Я подозреваю, что само правило Lint будет очень сложным, но в очень простом случае оно может быть реализовано следующим образом:

@Override
public void visitMethod(@NotNull UMethod node) {
    if (node.findAnnotation(CHECK_ACTIVE_ANNOTATION) == null) {
        return;
    }
    UExpression body = node.getUastBody();
    if (!(body instanceof UBlockExpression)) {
        return;
    }
    List<UExpression> expressions = ((UBlockExpression) body).getExpressions();
    UExpression firstExpression = expressions.get(0);
    // check if the first expression in method body is 'if' expression
    if (!(firstExpression instanceof UIfExpression)) {
        // probably it is not okay
        return;
    }
    UExpression condition = ((UIfExpression) firstExpression).getCondition();
    if (!(condition instanceof UCallExpression)) {
        // isAdded() is a method call, so we need a UCallExpression
        // probably not ok
        return;
    }
    if ("isAdded".equals(((UCallExpression) condition).getMethodName())) {
        // it is ok

        // you can also check argument count and the owner of the method to
        // ensure it is the correct one
    } else {
        // it is not ok
    }
}
...