001    /*
002     * Copyright 2010-2015 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    
017    package org.jetbrains.kotlin.resolve;
018    
019    import kotlin.collections.SetsKt;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023    import org.jetbrains.kotlin.descriptors.ClassDescriptor;
024    import org.jetbrains.kotlin.descriptors.VariableDescriptor;
025    import org.jetbrains.kotlin.psi.KtExpression;
026    import org.jetbrains.kotlin.psi.KtParameter;
027    import org.jetbrains.kotlin.psi.KtPsiUtil;
028    import org.jetbrains.kotlin.psi.KtTypeReference;
029    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
030    import org.jetbrains.kotlin.resolve.constants.BooleanValue;
031    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
032    import org.jetbrains.kotlin.resolve.constants.ConstantValue;
033    import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant;
034    import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
035    import org.jetbrains.kotlin.types.KotlinType;
036    import org.jetbrains.kotlin.types.TypeProjection;
037    import org.jetbrains.kotlin.types.TypeUtils;
038    
039    import java.util.List;
040    import java.util.Set;
041    
042    import static org.jetbrains.kotlin.diagnostics.Errors.INVALID_TYPE_OF_ANNOTATION_MEMBER;
043    import static org.jetbrains.kotlin.diagnostics.Errors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER;
044    import static org.jetbrains.kotlin.resolve.BindingContext.VALUE_PARAMETER;
045    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isAnnotationClass;
046    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass;
047    
048    public class CompileTimeConstantUtils {
049    
050        private final static Set<String> ARRAY_CALL_NAMES = SetsKt.hashSetOf(
051                "kotlin.arrayOf",
052                "kotlin.doubleArrayOf",
053                "kotlin.floatArrayOf",
054                "kotlin.longArrayOf",
055                "kotlin.intArrayOf",
056                "kotlin.charArrayOf",
057                "kotlin.shortArrayOf",
058                "kotlin.byteArrayOf",
059                "kotlin.booleanArrayOf"
060        );
061    
062        public static void checkConstructorParametersType(@NotNull List<KtParameter> parameters, @NotNull BindingTrace trace) {
063            for (KtParameter parameter : parameters) {
064                VariableDescriptor parameterDescriptor = trace.getBindingContext().get(VALUE_PARAMETER, parameter);
065                if (parameterDescriptor == null) continue;
066                KotlinType parameterType = parameterDescriptor.getType();
067                KtTypeReference typeReference = parameter.getTypeReference();
068                if (typeReference != null) {
069                    if (parameterType.isMarkedNullable()) {
070                        trace.report(NULLABLE_TYPE_OF_ANNOTATION_MEMBER.on(typeReference));
071                    }
072                    else if (!isAcceptableTypeForAnnotationParameter(parameterType)) {
073                        trace.report(INVALID_TYPE_OF_ANNOTATION_MEMBER.on(typeReference));
074                    }
075                }
076            }
077        }
078    
079        private static boolean isAcceptableTypeForAnnotationParameter(@NotNull KotlinType parameterType) {
080            ClassDescriptor typeDescriptor = TypeUtils.getClassDescriptor(parameterType);
081            if (typeDescriptor == null) {
082                return false;
083            }
084    
085            if (isEnumClass(typeDescriptor) ||
086                isAnnotationClass(typeDescriptor) ||
087                KotlinBuiltIns.isKClass(typeDescriptor) ||
088                KotlinBuiltIns.isPrimitiveArray(parameterType) ||
089                KotlinBuiltIns.isPrimitiveType(parameterType) ||
090                KotlinBuiltIns.isString(parameterType)) {
091                    return true;
092            }
093    
094            if (KotlinBuiltIns.isArray(parameterType)) {
095                List<TypeProjection> arguments = parameterType.getArguments();
096                if (arguments.size() == 1) {
097                    KotlinType arrayType = arguments.get(0).getType();
098                    if (arrayType.isMarkedNullable()) {
099                        return false;
100                    }
101                    ClassDescriptor arrayTypeDescriptor = TypeUtils.getClassDescriptor(arrayType);
102                    if (arrayTypeDescriptor != null) {
103                        return isEnumClass(arrayTypeDescriptor) ||
104                               isAnnotationClass(arrayTypeDescriptor) ||
105                               KotlinBuiltIns.isKClass(arrayTypeDescriptor) ||
106                               KotlinBuiltIns.isString(arrayType);
107                    }
108                }
109            }
110            return false;
111        }
112    
113        public static boolean isArrayMethodCall(@NotNull ResolvedCall<?> resolvedCall) {
114            return ARRAY_CALL_NAMES.contains(DescriptorUtils.getFqName(resolvedCall.getCandidateDescriptor()).asString());
115        }
116    
117        public static boolean canBeReducedToBooleanConstant(
118                @Nullable KtExpression expression,
119                @NotNull BindingContext context,
120                @Nullable Boolean expectedValue
121        ) {
122            KtExpression effectiveExpression = KtPsiUtil.deparenthesize(expression);
123    
124            if (effectiveExpression == null) return false;
125    
126            CompileTimeConstant<?> compileTimeConstant = ConstantExpressionEvaluator.getConstant(effectiveExpression, context);
127            if (!(compileTimeConstant instanceof TypedCompileTimeConstant) || compileTimeConstant.getUsesVariableAsConstant()) return false;
128    
129            ConstantValue constantValue = ((TypedCompileTimeConstant) compileTimeConstant).getConstantValue();
130    
131            if (!(constantValue instanceof BooleanValue)) return false;
132    
133            Boolean value = ((BooleanValue) constantValue).getValue();
134            return expectedValue == null || expectedValue.equals(value);
135        }
136    
137        private CompileTimeConstantUtils() {
138        }
139    }