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

com.android.tools.lint.psi.EcjPsiJavaEvaluator Maven / Gradle / Ivy

Go to download

Lint tools. Both a Command line tool and a library to add lint features to other tools

The newest version!
/*
 * Copyright (C) 2016 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.psi;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.tools.lint.ExternalAnnotationRepository;
import com.android.tools.lint.client.api.JavaEvaluator;
import com.google.common.collect.Lists;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifierList;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiParameterList;
import com.intellij.psi.util.PsiTreeUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;

public class EcjPsiJavaEvaluator extends JavaEvaluator {
    private final EcjPsiManager mManager;

    public EcjPsiJavaEvaluator(@NonNull EcjPsiManager manager) {
        mManager = manager;
    }

    @Override
    public boolean extendsClass(
            @Nullable PsiClass cls,
            @NonNull String className,
            boolean strict) {
        ReferenceBinding binding;
        if (cls instanceof EcjPsiClass) {
            binding = ((EcjPsiClass)cls).getBinding();
        } else if (cls instanceof EcjPsiBinaryClass) {
            binding = ((EcjPsiBinaryClass)cls).getTypeBinding();
        } else if (cls instanceof EcjPsiTypeParameter) {
            binding = ((EcjPsiTypeParameter)cls).getBinding();
        } else {
            return false;
        }
        if (strict && binding != null) {
            try {
                binding = binding.superclass();
            } catch (AbortCompilation ignore) {
                // Encountered symbol that couldn't be resolved (e.g. compiled class references
                // class not found on the classpath
                return false;
            }
        }

        for (; binding != null; binding = binding.superclass()) {
            if (equalsCompound(className, binding.compoundName)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public boolean implementsInterface(
            @NonNull PsiClass cls,
            @NonNull String interfaceName,
            boolean strict) {
        ReferenceBinding binding;
        if (cls instanceof EcjPsiClass) {
            binding = ((EcjPsiClass) cls).getBinding();
        } else if (cls instanceof EcjPsiBinaryClass) {
            binding = ((EcjPsiBinaryClass)cls).getTypeBinding();
        } else {
            return false;
        }
        if (strict && binding != null) {
            try {
                binding = binding.superclass();
            } catch (AbortCompilation ignore) {
                // Encountered symbol that couldn't be resolved (e.g. compiled class references
                // class not found on the classpath
                return false;
            }
        }
        return isInheritor(binding, interfaceName);
    }

    @Override
    public boolean inheritsFrom(
            @NonNull PsiClass cls,
            @NonNull String className,
            boolean strict) {
        return /*extendsClass(cls, className, strict) || */implementsInterface(cls, className, strict);
    }

    @VisibleForTesting
    static boolean equalsCompound(@NonNull String name, @NonNull char[][] compoundName) {
        int length = name.length();
        if (length == 0) {
            return false;
        }
        int index = 0;
        for (int i = 0, n = compoundName.length; i < n; i++) {
            char[] o = compoundName[i];
            //noinspection ForLoopReplaceableByForEach
            for (int j = 0, m = o.length; j < m; j++) {
                if (index == length) {
                    return false; // Don't allow prefix in a compound name
                }
                if (name.charAt(index) != o[j]
                        // Allow using . as an inner class separator whereas the
                        // symbol table will always use $
                        && !(o[j] == '$' && name.charAt(index) == '.')) {
                    return false;
                }
                index++;
            }
            if (i < n - 1) {
                if (index == length) {
                    return false;
                }
                if (name.charAt(index) != '.') {
                    return false;
                }
                index++;
                if (index == length) {
                    return false;
                }
            }
        }

        return index == length;
    }

    /** Checks whether the given class extends or implements a class with the given name */
    private static boolean isInheritor(@Nullable ReferenceBinding cls, @NonNull String name) {
        while (cls != null) {
            ReferenceBinding[] interfaces = cls.superInterfaces();
            for (ReferenceBinding binding : interfaces) {
                if (isInheritor(binding, name)) {
                    return true;
                }
            }

            if (equalsCompound(name, cls.compoundName)) {
                return true;
            }

            try {
                cls = cls.superclass();
            } catch (AbortCompilation ignore) {
                // Encountered symbol that couldn't be resolved (e.g. compiled class references
                // class not found on the classpath
                break;
            }
        }

        return false;
    }

    @Nullable
    @Override
    public String getInternalName(@NonNull PsiClass psiClass) {
        ReferenceBinding binding = null;
        if (psiClass instanceof EcjPsiClass) {
            //noinspection ConstantConditions
            Object nativeNode = ((EcjPsiClass) psiClass).getNativeNode();
            binding = ((EcjPsiClass) psiClass).getBinding();
            if (nativeNode instanceof TypeDeclaration) {
                binding = ((TypeDeclaration) nativeNode).binding;
            }
        } else if (psiClass instanceof EcjPsiBinaryClass) {
            Binding binaryBinding = ((EcjPsiBinaryClass) psiClass).getBinding();
            if (binaryBinding instanceof ReferenceBinding) {
                binding = (ReferenceBinding) binaryBinding;
            }
        } else if (psiClass instanceof EcjPsiTypeParameter) {
            Object nativeNode = ((EcjPsiTypeParameter) psiClass).getNativeNode();
            if (nativeNode instanceof TypeParameter) {
                TypeVariableBinding b = ((TypeParameter) nativeNode).binding;
                if (b.superclass != null) {
                    binding = b.superclass;
                }
            }
        }
        if (binding == null) {
            return super.getInternalName(psiClass);
        }

        return EcjPsiManager.getInternalName(binding.compoundName);
    }

    @Nullable
    @Override
    public String getInternalName(@NonNull PsiClassType psiClassType) {
        if (psiClassType instanceof EcjPsiClassType) {
            ReferenceBinding binding = ((EcjPsiClassType) psiClassType).getBinding();
            if (binding.compoundName != null) {
                return EcjPsiManager.getInternalName(binding.compoundName);
            }
        }
        return super.getInternalName(psiClassType);
    }

    @Override
    @Nullable
    public PsiClass findClass(@NonNull String fullyQualifiedName) {
        return mManager.findClass(fullyQualifiedName);
    }

    @Nullable
    @Override
    public PsiClassType getClassType(@Nullable PsiClass psiClass) {
        if (psiClass != null) {
            return mManager.getClassType(psiClass);
        }
        return null;
    }

    @NonNull
    @Override
    public PsiAnnotation[] getAllAnnotations(@NonNull PsiModifierListOwner owner,
            boolean inHierarchy) {
        if (!inHierarchy) {
            return getDirectAnnotations(owner);
        }

        PsiModifierList modifierList = owner.getModifierList();
        if (modifierList == null) {
            return getDirectAnnotations(owner);
        }

        if (owner instanceof PsiMethod) {
            MethodBinding method;
            if (owner instanceof EcjPsiMethod) {
                EcjPsiMethod psiMethod = (EcjPsiMethod) owner;
                AbstractMethodDeclaration declaration = (AbstractMethodDeclaration) psiMethod.getNativeNode();
                assert declaration != null;
                method = declaration.binding;
            } else if (owner instanceof EcjPsiBinaryMethod) {
                method = ((EcjPsiBinaryMethod) owner).getBinding();
            } else {
                assert false : owner.getClass();
                return PsiAnnotation.EMPTY_ARRAY;
            }

            List all = Lists.newArrayListWithExpectedSize(2);
            ExternalAnnotationRepository manager = mManager.getAnnotationRepository();

            while (method != null) {
                if (method.declaringClass == null) {
                    // for example, for unresolved problem bindings
                    break;
                }
                PsiMethod methodElement = mManager.findMethod(method);
                // Try to get source annotation, if possible, since they can carry more
                // info than binary annotations (such as the IntDef annotation)
                if (methodElement instanceof EcjPsiMethod) {
                    PsiModifierList methodModifiers = methodElement.getModifierList();
                    Collections.addAll(all, methodModifiers.getAnnotations());
                } else {
                    AnnotationBinding[] annotations = method.getAnnotations();
                    int count = annotations.length;
                    if (count > 0) {
                        all = Lists.newArrayListWithExpectedSize(count);
                        for (AnnotationBinding annotation : annotations) {
                            if (annotation != null) {
                                if (annotation != null) {
                                    PsiAnnotation a;
                                    if (modifierList != null) {
                                        a = new EcjPsiBinaryAnnotation(mManager, modifierList,
                                                annotation);
                                    } else {
                                        a = new EcjPsiBinaryAnnotation(mManager, method,
                                                annotation);
                                    }
                                    all.add(a);
                                }
                            }
                        }
                    }
                }

                // Look for external annotations
                if (manager != null) {
                    Collection external = manager.getAnnotations(method);
                    if (external != null) {
                        all.addAll(external);
                    }
                }

                method = EcjPsiManager.findSuperMethodBinding(method, false, false);
                modifierList = null;
            }

            return EcjPsiManager.ensureUnique(all);
        } else if (owner instanceof PsiClass) {
            ReferenceBinding cls;
            if (owner instanceof EcjPsiClass) {
                EcjPsiClass psiClass = (EcjPsiClass) owner;
                cls = psiClass.getBinding();
            } else if (owner instanceof EcjPsiBinaryClass) {
                cls = ((EcjPsiBinaryClass) owner).getTypeBinding();
            } else {
                assert false : owner.getClass();
                return PsiAnnotation.EMPTY_ARRAY;
            }

            List all = Lists.newArrayListWithExpectedSize(2);
            ExternalAnnotationRepository manager = mManager.getAnnotationRepository();

            while (cls != null) {
                PsiClass clsElement = mManager.findClass(cls);
                // Try to get source annotation, if possible, since they can carry more
                // info than binary annotations (such as the IntDef annotation)
                if (clsElement instanceof EcjPsiClass) {
                    PsiModifierList clsModifiers = clsElement.getModifierList();
                    if (clsModifiers != null) {
                        Collections.addAll(all, clsModifiers.getAnnotations());
                    }
                } else {
                    AnnotationBinding[] annotations = cls.getAnnotations();
                    int count = annotations.length;
                    if (count > 0) {
                        all = Lists.newArrayListWithExpectedSize(count);
                        for (AnnotationBinding annotation : annotations) {
                            if (annotation != null) {
                                PsiAnnotation a;
                                if (modifierList != null) {
                                    a = new EcjPsiBinaryAnnotation(mManager, modifierList,
                                            annotation);
                                } else {
                                    a = new EcjPsiBinaryAnnotation(mManager, cls, annotation);
                                }
                                all.add(a);
                            }
                        }
                    }
                }

                // Look for external annotations
                if (manager != null) {
                    Collection external = manager.getAnnotations(cls);
                    if (external != null) {
                        all.addAll(external);
                    }
                }

                try {
                    cls = cls.superclass();
                    modifierList = null;
                } catch (AbortCompilation ignore) {
                    // Encountered symbol that couldn't be resolved (e.g. compiled class references
                    // class not found on the classpath
                    break;
                }
            }

            return EcjPsiManager.ensureUnique(all);
        } else if (owner instanceof PsiParameter) {
            MethodBinding method;
            int index;

            if (owner instanceof EcjPsiBinaryParameter) {
                EcjPsiBinaryParameter parameter = (EcjPsiBinaryParameter) owner;
                method = parameter.getOwnerMethod().getBinding();
                index = parameter.getIndex();
            } else if (owner instanceof EcjPsiParameter) {
                EcjPsiParameter parameter = (EcjPsiParameter) owner;
                if (parameter.getParent() instanceof PsiParameterList) {
                    EcjPsiMethod psiMethod = (EcjPsiMethod)PsiTreeUtil.getParentOfType(
                            parameter.getParent(), PsiMethod.class, true);
                    if (psiMethod == null) {
                        return getDirectAnnotations(owner);
                    }
                    index = ((PsiParameterList)parameter.getParent()).getParameterIndex(parameter);
                    AbstractMethodDeclaration declaration = (AbstractMethodDeclaration) psiMethod.getNativeNode();
                    assert declaration != null;
                    method = declaration.binding;
                } else {
                    // For each block, catch block
                    return getDirectAnnotations(owner);
                }
            } else {
                // Unexpected method type
                assert false : owner.getClass();
                return getDirectAnnotations(owner);
            }

            List all = Lists.newArrayListWithExpectedSize(2);
            ExternalAnnotationRepository manager = mManager.getAnnotationRepository();

            while (method != null) {
                if (method.declaringClass == null) {
                    // for example, for unresolved problem bindings
                    break;
                }
                PsiMethod methodElement = mManager.findMethod(method);
                // Try to get source annotation, if possible, since they can carry more
                // info than binary annotations (such as the IntDef annotation)
                if (methodElement instanceof EcjPsiMethod) {
                    PsiParameter[] parameters = methodElement.getParameterList().getParameters();
                    if (index < parameters.length) {
                        PsiModifierList parameterModifiers = parameters[index].getModifierList();
                        if (parameterModifiers != null) {
                            Collections.addAll(all, parameterModifiers.getAnnotations());
                        }
                    }
                } else {
                    AnnotationBinding[][] parameterAnnotations = method.getParameterAnnotations();
                    if (parameterAnnotations != null && index < parameterAnnotations.length) {
                        AnnotationBinding[] annotations = parameterAnnotations[index];
                        int count = annotations.length;
                        if (count > 0) {
                            for (AnnotationBinding annotation : annotations) {
                                if (annotation != null) {
                                    if (annotation != null) {
                                        PsiAnnotation a;
                                        if (modifierList != null) {
                                            a = new EcjPsiBinaryAnnotation(mManager, modifierList,
                                                    annotation);
                                        } else {
                                            a = new EcjPsiBinaryAnnotation(mManager, method,
                                                    annotation);
                                        }
                                        all.add(a);
                                    }
                                }
                            }
                        }
                    }
                }

                // Look for external annotations
                if (manager != null) {
                    Collection external = manager.getParameterAnnotations(method,
                            index);
                    if (external != null) {
                        all.addAll(external);
                    }
                }

                method = EcjPsiManager.findSuperMethodBinding(method, false, false);
                modifierList = null;
            }

            return EcjPsiManager.ensureUnique(all);
        } else {
            // PsiField, PsiLocalVariable etc: no inheritance
            return getDirectAnnotations(owner);
        }
    }

    @NonNull
    private PsiAnnotation[] getDirectAnnotations(@NonNull PsiModifierListOwner owner) {
        // Look up external annotations to merge in
        ExternalAnnotationRepository repository = mManager.getAnnotationRepository();
        Collection annotations = null;
        if (repository != null) {
            if (owner instanceof EcjPsiMethod) {
                MethodBinding binding = ((EcjPsiMethod) owner).getBinding();
                if (binding != null) {
                    annotations = repository.getAnnotations(binding);
                }
            } else if (owner instanceof EcjPsiBinaryMethod) {
                MethodBinding binding = ((EcjPsiBinaryMethod) owner).getBinding();
                annotations = repository.getAnnotations(binding);
            } else if (owner instanceof EcjPsiClass) {
                ReferenceBinding binding = ((EcjPsiClass) owner).getBinding();
                if (binding != null) {
                    annotations = repository.getAnnotations(binding);
                }
            } else if (owner instanceof EcjPsiBinaryClass) {
                ReferenceBinding binding = ((EcjPsiBinaryClass) owner).getTypeBinding();
                annotations = repository.getAnnotations(binding);
            } else if (owner instanceof EcjPsiField) {
                FieldBinding binding = ((EcjPsiField) owner).getFieldBinding();
                if (binding != null) {
                    annotations = repository.getAnnotations(binding);
                }
            } else if (owner instanceof EcjPsiParameter) {
                EcjPsiParameter parameter = (EcjPsiParameter) owner;
                EcjPsiSourceElement parent = parameter.getParent();
                LocalVariableBinding binding = parameter.getVariableBinding();
                if (parent instanceof PsiParameterList && binding != null) {
                    int index = ((PsiParameterList) parent).getParameterIndex(parameter);
                    MethodBinding enclosingMethod = binding.getEnclosingMethod();
                    if (enclosingMethod != null) {
                        if (index != -1) {
                            annotations = repository.getParameterAnnotations(enclosingMethod,
                                    index);
                        }
                    }
                }
            } else if (owner instanceof EcjPsiBinaryParameter) {
                EcjPsiBinaryParameter parameter = (EcjPsiBinaryParameter) owner;
                PsiElement parent = parameter.getParent();
                EcjPsiBinaryMethod method = parameter.getOwnerMethod();
                MethodBinding enclosingMethod = method.getBinding();
                if (parent instanceof PsiParameterList) {
                    int index = ((PsiParameterList) parent).getParameterIndex(parameter);
                    if (index != -1) {
                        annotations = repository.getParameterAnnotations(enclosingMethod, index);
                    }
                }
            } else if (owner instanceof EcjPsiBinaryField) {
                FieldBinding binding = ((EcjPsiBinaryField) owner).getBinding();
                if (binding != null) {
                    annotations = repository.getAnnotations(binding);
                }
            } else if (owner instanceof EcjPsiPackage) {
                PackageBinding binding = ((EcjPsiPackage) owner).getPackageBinding();
                if (binding != null) {
                    annotations = repository.getAnnotations(binding);
                }
            }
        }

        PsiModifierList modifierList = owner.getModifierList();
        if (modifierList != null) {
            PsiAnnotation[] modifierAnnotations = modifierList.getAnnotations();
            if (modifierAnnotations.length > 0) {
                if (annotations != null && !annotations.isEmpty()) {
                    List combined = Lists.newArrayList(modifierAnnotations);
                    combined.addAll(annotations);
                    return combined.toArray(PsiAnnotation.EMPTY_ARRAY);
                } else {
                    return modifierAnnotations;
                }
            } else if (annotations != null && !annotations.isEmpty()) {
                return annotations.toArray(PsiAnnotation.EMPTY_ARRAY);
            }
            return modifierAnnotations;
        } else if (annotations != null && !annotations.isEmpty()) {
            return annotations.toArray(PsiAnnotation.EMPTY_ARRAY);
        } else {
            return PsiAnnotation.EMPTY_ARRAY;
        }
    }

    @Nullable
    @Override
    public PsiAnnotation findAnnotationInHierarchy(@NonNull PsiModifierListOwner listOwner,
            @NonNull String... annotationNames) {
        throw new UnimplementedLintPsiApiException();
    }

    @Nullable
    @Override
    public PsiAnnotation findAnnotation(@Nullable PsiModifierListOwner listOwner,
            @NonNull String... annotationNames) {
        throw new UnimplementedLintPsiApiException();
    }

    @Nullable
    @Override
    public String findJarPath(@NonNull PsiElement element) {
        while (true) {
            PsiElement cls = PsiTreeUtil.getParentOfType(element, PsiClass.class, true);
            if (cls == null) {
                if (element instanceof EcjPsiBinaryClass) {
                    ReferenceBinding binding = ((EcjPsiBinaryClass) element).getTypeBinding();
                    String jarFile = mManager.getJarFile(binding);
                    if (jarFile != null) {
                        return jarFile;
                    }
                }
                break;
            }

            element = cls;
        }

        return null;
    }

    @Override
    @Nullable
    public PsiPackage getPackage(@NonNull PsiElement node) {
        PsiClass cls = PsiTreeUtil.getParentOfType(node, PsiClass.class, false);
        ReferenceBinding binding;
        if (cls instanceof EcjPsiClass) {
            EcjPsiClass ecjPsiClass = (EcjPsiClass) cls;
            binding = ecjPsiClass.getBinding();
        } else if (cls instanceof EcjPsiBinaryClass) {
            EcjPsiBinaryClass ecjPsiClass = (EcjPsiBinaryClass) cls;
            binding = ecjPsiClass.getTypeBinding();
        } else {
            return null;
        }

        return binding != null ? mManager.findPackage(binding.fPackage) : null;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy