001/*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.jetbrains.jet.lang.resolve.java.resolver;
018
019import com.intellij.psi.*;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022
023public class RawTypesCheck {
024    private static boolean isPartiallyRawType(@NotNull PsiType type) {
025        return type.accept(new PsiTypeVisitor<Boolean>() {
026            @Nullable
027            @Override
028            public Boolean visitPrimitiveType(PsiPrimitiveType primitiveType) {
029                return false;
030            }
031
032            @Nullable
033            @Override
034            public Boolean visitClassType(PsiClassType classType) {
035                if (classType.isRaw()) {
036                    return true;
037                }
038
039                for (PsiType argument : classType.getParameters()) {
040                    if (argument.accept(this)) {
041                        return true;
042                    }
043                }
044
045                return false;
046            }
047
048            @Nullable
049            @Override
050            public Boolean visitArrayType(PsiArrayType arrayType) {
051                return arrayType.getComponentType().accept(this);
052            }
053
054            @Nullable
055            @Override
056            public Boolean visitWildcardType(PsiWildcardType wildcardType) {
057                PsiType bound = wildcardType.getBound();
058                return bound == null ? false : bound.accept(this);
059            }
060
061            @Nullable
062            @Override
063            public Boolean visitType(PsiType type) {
064                throw new IllegalStateException(type.getClass().getSimpleName() + " is unexpected");
065            }
066        });
067    }
068
069    private static boolean hasRawTypesInSignature(@NotNull PsiMethod method) {
070        PsiType returnType = method.getReturnType();
071        if (returnType != null && isPartiallyRawType(returnType)) {
072            return true;
073        }
074
075        for (PsiParameter parameter : method.getParameterList().getParameters()) {
076            if (isPartiallyRawType(parameter.getType())) {
077                return true;
078            }
079        }
080
081        for (PsiTypeParameter typeParameter : method.getTypeParameters()) {
082            for (PsiClassType upperBound : typeParameter.getExtendsList().getReferencedTypes()) {
083                if (isPartiallyRawType(upperBound)) {
084                    return true;
085                }
086            }
087        }
088
089        return false;
090    }
091
092    static boolean hasRawTypesInHierarchicalSignature(@NotNull PsiMethod method) {
093        // This is a very important optimization: package-classes are big and full of static methods
094        // building method hierarchies for such classes takes a very long time
095        if (method.hasModifierProperty(PsiModifier.STATIC)) return false;
096
097        if (hasRawTypesInSignature(method)) {
098            return true;
099        }
100
101        for (HierarchicalMethodSignature superSignature : method.getHierarchicalMethodSignature().getSuperSignatures()) {
102            PsiMethod superMethod = superSignature.getMethod();
103            if (superSignature.isRaw() || typeParameterIsErased(method, superMethod) || hasRawTypesInSignature(superMethod)) {
104                return true;
105            }
106        }
107
108        return false;
109    }
110
111    private static boolean typeParameterIsErased(@NotNull PsiMethod a, @NotNull PsiMethod b) {
112        // Java allows you to write
113        //   <T extends Foo> T foo(), in the superclass and then
114        //   Foo foo(), in the subclass
115        // this is a valid Java override, but in fact it is an erasure
116        return a.getTypeParameters().length != b.getTypeParameters().length;
117    }
118
119    private RawTypesCheck() {
120    }
121}