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 com.google.common.collect.Lists;
020    import com.intellij.openapi.util.Pair;
021    import com.intellij.psi.PsiElement;
022    import com.intellij.psi.util.PsiTreeUtil;
023    import kotlin.jvm.functions.Function3;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.kotlin.descriptors.*;
027    import org.jetbrains.kotlin.diagnostics.Diagnostic;
028    import org.jetbrains.kotlin.diagnostics.Severity;
029    import org.jetbrains.kotlin.psi.*;
030    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
031    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
032    import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
033    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory;
034    import org.jetbrains.kotlin.resolve.diagnostics.MutableDiagnosticsWithSuppression;
035    import org.jetbrains.kotlin.types.KotlinType;
036    import org.jetbrains.kotlin.types.TypeUtils;
037    import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo;
038    import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
039    import org.jetbrains.kotlin.util.slicedMap.*;
040    
041    import java.util.Collection;
042    import java.util.EnumMap;
043    import java.util.Map;
044    
045    import static org.jetbrains.kotlin.diagnostics.Errors.AMBIGUOUS_LABEL;
046    import static org.jetbrains.kotlin.resolve.BindingContext.*;
047    
048    public class BindingContextUtils {
049        private BindingContextUtils() {
050        }
051    
052        @Nullable
053        public static VariableDescriptor extractVariableFromResolvedCall(
054                @NotNull BindingContext bindingContext,
055                @Nullable KtElement callElement
056        ) {
057            ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(callElement, bindingContext);
058            if (resolvedCall == null || !(resolvedCall.getResultingDescriptor() instanceof VariableDescriptor)) return null;
059            return (VariableDescriptor) resolvedCall.getResultingDescriptor();
060        }
061    
062        @Nullable
063        public static VariableDescriptor extractVariableDescriptorIfAny(@NotNull BindingContext bindingContext, @Nullable KtElement element, boolean onlyReference) {
064            DeclarationDescriptor descriptor = null;
065            if (!onlyReference && (element instanceof KtVariableDeclaration || element instanceof KtParameter)) {
066                descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
067            }
068            else if (element instanceof KtSimpleNameExpression) {
069                descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, (KtSimpleNameExpression) element);
070            }
071            else if (element instanceof KtQualifiedExpression) {
072                descriptor = extractVariableDescriptorIfAny(bindingContext, ((KtQualifiedExpression) element).getSelectorExpression(), onlyReference);
073            }
074            if (descriptor instanceof VariableDescriptor) {
075                return (VariableDescriptor) descriptor;
076            }
077            return null;
078        }
079    
080        public static void recordFunctionDeclarationToDescriptor(@NotNull BindingTrace trace,
081                @NotNull PsiElement psiElement, @NotNull SimpleFunctionDescriptor function) {
082            trace.record(BindingContext.FUNCTION, psiElement, function);
083        }
084    
085        @NotNull
086        public static <K, V> V getNotNull(
087            @NotNull BindingContext bindingContext,
088            @NotNull ReadOnlySlice<K, V> slice,
089            @NotNull K key
090        ) {
091            return getNotNull(bindingContext, slice, key, "Value at " + slice + " must not be null for " + key);
092        }
093    
094        @NotNull
095        public static KotlinType getTypeNotNull(
096                @NotNull BindingContext bindingContext,
097                @NotNull KtExpression expression
098        ) {
099            KotlinType result = bindingContext.getType(expression);
100            if (result == null) {
101                throw new IllegalStateException("Type must be not null for " + expression);
102            }
103            return result;
104        }
105    
106        @NotNull
107        public static <K, V> V getNotNull(
108                @NotNull BindingContext bindingContext,
109                @NotNull ReadOnlySlice<K, V> slice,
110                @NotNull K key,
111                @NotNull String messageIfNull
112        ) {
113            V value = bindingContext.get(slice, key);
114            if (value == null) {
115                throw new IllegalStateException(messageIfNull);
116            }
117            return value;
118        }
119    
120        @NotNull
121        public static DeclarationDescriptor getEnclosingDescriptor(@NotNull BindingContext context, @NotNull KtElement element) {
122            KtNamedDeclaration declaration = PsiTreeUtil.getParentOfType(element, KtNamedDeclaration.class);
123            if (declaration instanceof KtFunctionLiteral) {
124                return getEnclosingDescriptor(context, declaration);
125            }
126            DeclarationDescriptor descriptor = context.get(DECLARATION_TO_DESCRIPTOR, declaration);
127            assert descriptor != null : "No descriptor for named declaration: " + declaration.getText() + "\n(of type " + declaration.getClass() + ")";
128            return descriptor;
129        }
130    
131        public static FunctionDescriptor getEnclosingFunctionDescriptor(@NotNull BindingContext context, @NotNull KtElement element) {
132            KtFunction function = PsiTreeUtil.getParentOfType(element, KtFunction.class);
133            return (FunctionDescriptor)context.get(DECLARATION_TO_DESCRIPTOR, function);
134        }
135    
136        public static void reportAmbiguousLabel(
137                @NotNull BindingTrace trace,
138                @NotNull KtSimpleNameExpression targetLabel,
139                @NotNull Collection<DeclarationDescriptor> declarationsByLabel
140        ) {
141            Collection<PsiElement> targets = Lists.newArrayList();
142            for (DeclarationDescriptor descriptor : declarationsByLabel) {
143                PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
144                assert element != null : "Label can only point to something in the same lexical scope";
145                targets.add(element);
146            }
147            if (!targets.isEmpty()) {
148                trace.record(AMBIGUOUS_LABEL_TARGET, targetLabel, targets);
149            }
150            trace.report(AMBIGUOUS_LABEL.on(targetLabel));
151        }
152    
153        @Nullable
154        public static KotlinType updateRecordedType(
155                @Nullable KotlinType type,
156                @NotNull KtExpression expression,
157                @NotNull BindingTrace trace,
158                boolean shouldBeMadeNullable
159        ) {
160            if (type == null) return null;
161            if (shouldBeMadeNullable) {
162                type = TypeUtils.makeNullable(type);
163            }
164            trace.recordType(expression, type);
165            return type;
166        }
167    
168        @Nullable
169        public static KotlinTypeInfo getRecordedTypeInfo(@NotNull KtExpression expression, @NotNull BindingContext context) {
170            // noinspection ConstantConditions
171            if (!context.get(BindingContext.PROCESSED, expression)) return null;
172            // NB: should never return null if expression is already processed
173            KotlinTypeInfo result = context.get(BindingContext.EXPRESSION_TYPE_INFO, expression);
174            return result != null ? result : TypeInfoFactoryKt.noTypeInfo(DataFlowInfoFactory.EMPTY);
175        }
176    
177        public static boolean isExpressionWithValidReference(
178                @NotNull KtExpression expression,
179                @NotNull BindingContext context
180        ) {
181            if (expression instanceof KtCallExpression) {
182                ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(expression, context);
183                return resolvedCall instanceof VariableAsFunctionResolvedCall;
184            }
185            return expression instanceof KtReferenceExpression;
186        }
187    
188        public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
189            if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
190            VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
191            return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar();
192        }
193    
194        @NotNull
195        public static Pair<FunctionDescriptor, PsiElement> getContainingFunctionSkipFunctionLiterals(
196                @Nullable DeclarationDescriptor startDescriptor,
197                boolean strict
198        ) {
199            FunctionDescriptor containingFunctionDescriptor = DescriptorUtils.getParentOfType(startDescriptor, FunctionDescriptor.class, strict);
200            PsiElement containingFunction =
201                    containingFunctionDescriptor != null ? DescriptorToSourceUtils.getSourceFromDescriptor(containingFunctionDescriptor) : null;
202            while (containingFunction instanceof KtFunctionLiteral) {
203                containingFunctionDescriptor = DescriptorUtils.getParentOfType(containingFunctionDescriptor, FunctionDescriptor.class);
204                containingFunction = containingFunctionDescriptor != null ? DescriptorToSourceUtils
205                        .getSourceFromDescriptor(containingFunctionDescriptor) : null;
206            }
207    
208            return new Pair<FunctionDescriptor, PsiElement>(containingFunctionDescriptor, containingFunction);
209        }
210    
211        @Nullable
212        public static ResolvedCall<ConstructorDescriptor> getDelegationConstructorCall(
213                @NotNull BindingContext bindingContext,
214                @NotNull ConstructorDescriptor constructorDescriptor
215        ) {
216            return bindingContext.get(CONSTRUCTOR_RESOLVED_DELEGATION_CALL, constructorDescriptor);
217        }
218    
219        static void addOwnDataTo(
220                @NotNull final BindingTrace trace, @Nullable final TraceEntryFilter filter, boolean commitDiagnostics,
221                @NotNull MutableSlicedMap map, MutableDiagnosticsWithSuppression diagnostics
222        ) {
223            map.forEach(new Function3<WritableSlice, Object, Object, Void>() {
224                @Override
225                public Void invoke(WritableSlice slice, Object key, Object value) {
226                    if (filter == null || filter.accept(slice, null, key)) {
227                        trace.record(slice, key, value);
228                    }
229    
230                    return null;
231                }
232            });
233    
234            if (!commitDiagnostics) return;
235    
236            for (Diagnostic diagnostic : diagnostics.getOwnDiagnostics()) {
237                if (filter == null || filter.accept(null, diagnostic, diagnostic.getPsiElement())) {
238                    trace.report(diagnostic);
239                }
240            }
241    
242        }
243    }