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

com.android.tools.lint.checks.CallSuperDetector Maven / Gradle / Ivy

There is a newer version: 25.3.0
Show newest version
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.lint.checks;

import static com.android.SdkConstants.CLASS_VIEW;

import com.android.annotations.NonNull;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
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.Location;
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.io.File;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import lombok.ast.AstVisitor;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.MethodDeclaration;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.Super;

/**
 * Makes sure that methods call super when overriding methods
 */
public class CallSuperDetector extends Detector implements Detector.JavaScanner {

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

    /** Missing call to super */
    public static final Issue ISSUE = Issue.create(
            "MissingSuperCall", //$NON-NLS-1$
            "Missing Super Call",

            "Some methods, such as `View#onDetachedFromWindow`, require that you also " +
            "call the super implementation as part of your method.",

            Category.CORRECTNESS,
            9,
            Severity.WARNING,
            IMPLEMENTATION);

    static final String ON_DETACHED_FROM_WINDOW = "onDetachedFromWindow";   //$NON-NLS-1$

    /** Constructs a new {@link CallSuperDetector} check */
    public CallSuperDetector() {
    }

    @Override
    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
        return true;
    }

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

    // ---- Implements JavaScanner ----

    @Override
    public List> getApplicableNodeTypes() {
        return Collections.>singletonList(MethodDeclaration.class);
    }

    @Override
    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
        return new PerformanceVisitor(context);
    }

    private static class PerformanceVisitor extends ForwardingAstVisitor {
        private final JavaContext mContext;

        public PerformanceVisitor(JavaContext context) {
            mContext = context;
        }

        @Override
        public boolean visitMethodDeclaration(MethodDeclaration node) {
            // TODO: Check methods in Activity that require super as well
            if (node.astMethodName().astValue().equals(ON_DETACHED_FROM_WINDOW) &&
                    node.astParameters() != null && node.astParameters().isEmpty()) {
                if (!callsSuper(node, ON_DETACHED_FROM_WINDOW)) {
                    // Make sure the current class extends View, if type information is
                    // available
                    boolean isView = true; // Don't know without type information
                    ResolvedNode resolved = mContext.resolve(node);
                    if (resolved instanceof ResolvedMethod) {
                        ResolvedMethod method = (ResolvedMethod) resolved;
                        isView = method.getContainingClass().isSubclassOf(CLASS_VIEW, false);
                    }

                    if (isView) {
                        String message = "Overriding method should call `super."
                                + ON_DETACHED_FROM_WINDOW + "`";
                        Location location = mContext.getLocation(node.astMethodName());
                        mContext.report(ISSUE, node, location, message);
                    }
                }
            }

            return super.visitMethodDeclaration(node);
        }

        private boolean callsSuper(MethodDeclaration node, final String methodName) {
            final AtomicBoolean result = new AtomicBoolean();
            node.accept(new ForwardingAstVisitor() {
                @Override
                public boolean visitMethodInvocation(MethodInvocation node) {
                    if (node.astName().astValue().equals(methodName) &&
                            node.astOperand() instanceof Super) {
                        result.set(true);
                    }
                    return super.visitMethodInvocation(node);
                }
            });

            return result.get();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy