/* * Copyright 2010-2015 JetBrains s.r.o. * * 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 org.jetbrains.kotlin.resolve; import com.google.common.collect.Lists; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory; import org.jetbrains.kotlin.resolve.calls.tower.TowerLevelsKt; import org.jetbrains.kotlin.resolve.diagnostics.MutableDiagnosticsWithSuppression; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo; import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt; import org.jetbrains.kotlin.util.slicedMap.MutableSlicedMap; import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice; import java.util.Collection; import static org.jetbrains.kotlin.diagnostics.Errors.AMBIGUOUS_LABEL; import static org.jetbrains.kotlin.resolve.BindingContext.*; public class BindingContextUtils { private BindingContextUtils() { } @Nullable public static VariableDescriptor extractVariableFromResolvedCall( @NotNull BindingContext bindingContext, @Nullable KtElement callElement ) { ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(callElement, bindingContext); if (resolvedCall == null || !(resolvedCall.getResultingDescriptor() instanceof VariableDescriptor)) return null; return (VariableDescriptor) resolvedCall.getResultingDescriptor(); } @Nullable public static VariableDescriptor variableDescriptorForDeclaration(@Nullable DeclarationDescriptor descriptor) { if (descriptor instanceof VariableDescriptor) return (VariableDescriptor) descriptor; if (descriptor instanceof ClassDescriptor) { return TowerLevelsKt.getFakeDescriptorForObject((ClassDescriptor) descriptor); } return null; } @Nullable public static VariableDescriptor extractVariableDescriptorFromReference( @NotNull BindingContext bindingContext, @Nullable KtElement element ) { if (element instanceof KtSimpleNameExpression) { return variableDescriptorForDeclaration(bindingContext.get(BindingContext.REFERENCE_TARGET, (KtSimpleNameExpression) element)); } else if (element instanceof KtQualifiedExpression) { return extractVariableDescriptorFromReference(bindingContext, ((KtQualifiedExpression) element).getSelectorExpression()); } return null; } public static void recordFunctionDeclarationToDescriptor(@NotNull BindingTrace trace, @NotNull PsiElement psiElement, @NotNull SimpleFunctionDescriptor function) { trace.record(BindingContext.FUNCTION, psiElement, function); } @NotNull public static <K, V> V getNotNull( @NotNull BindingContext bindingContext, @NotNull ReadOnlySlice<K, V> slice, @NotNull K key ) { return getNotNull(bindingContext, slice, key, "Value at " + slice + " must not be null for " + key); } @NotNull public static KotlinType getTypeNotNull( @NotNull BindingContext bindingContext, @NotNull KtExpression expression ) { KotlinType result = bindingContext.getType(expression); if (result == null) { throw new IllegalStateException("Type must be not null for " + expression); } return result; } @NotNull public static <K, V> V getNotNull( @NotNull BindingContext bindingContext, @NotNull ReadOnlySlice<K, V> slice, @NotNull K key, @NotNull String messageIfNull ) { V value = bindingContext.get(slice, key); if (value == null) { throw new IllegalStateException(messageIfNull); } return value; } @NotNull public static DeclarationDescriptor getEnclosingDescriptor(@NotNull BindingContext context, @NotNull KtElement element) { KtNamedDeclaration declaration = PsiTreeUtil.getParentOfType(element, KtNamedDeclaration.class); if (declaration instanceof KtFunctionLiteral) { return getEnclosingDescriptor(context, declaration); } DeclarationDescriptor descriptor = context.get(DECLARATION_TO_DESCRIPTOR, declaration); assert descriptor != null : "No descriptor for named declaration: " + declaration.getText() + "\n(of type " + declaration.getClass() + ")"; return descriptor; } @Nullable public static FunctionDescriptor getEnclosingFunctionDescriptor(@NotNull BindingContext context, @NotNull KtElement element) { KtElement functionOrClass = PsiTreeUtil.getParentOfType(element, KtFunction.class, KtClassOrObject.class); DeclarationDescriptor descriptor = context.get(DECLARATION_TO_DESCRIPTOR, functionOrClass); if (functionOrClass instanceof KtFunction) { if (descriptor instanceof FunctionDescriptor) return (FunctionDescriptor) descriptor; return null; } else { if (descriptor instanceof ClassDescriptor) return ((ClassDescriptor) descriptor).getUnsubstitutedPrimaryConstructor(); return null; } } public static void reportAmbiguousLabel( @NotNull BindingTrace trace, @NotNull KtSimpleNameExpression targetLabel, @NotNull Collection<DeclarationDescriptor> declarationsByLabel ) { Collection<PsiElement> targets = Lists.newArrayList(); for (DeclarationDescriptor descriptor : declarationsByLabel) { PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(descriptor); assert element != null : "Label can only point to something in the same lexical scope"; targets.add(element); } if (!targets.isEmpty()) { trace.record(AMBIGUOUS_LABEL_TARGET, targetLabel, targets); } trace.report(AMBIGUOUS_LABEL.on(targetLabel)); } @Nullable public static KotlinType updateRecordedType( @Nullable KotlinType type, @NotNull KtExpression expression, @NotNull BindingTrace trace, boolean shouldBeMadeNullable ) { if (type == null) return null; if (shouldBeMadeNullable) { type = TypeUtils.makeNullable(type); } trace.recordType(expression, type); return type; } @Nullable public static KotlinTypeInfo getRecordedTypeInfo(@NotNull KtExpression expression, @NotNull BindingContext context) { // noinspection ConstantConditions if (context.get(BindingContext.PROCESSED, expression) != Boolean.TRUE) return null; // NB: should never return null if expression is already processed KotlinTypeInfo result = context.get(BindingContext.EXPRESSION_TYPE_INFO, expression); return result != null ? result : TypeInfoFactoryKt.noTypeInfo(DataFlowInfoFactory.EMPTY); } public static boolean isExpressionWithValidReference( @NotNull KtExpression expression, @NotNull BindingContext context ) { if (expression instanceof KtCallExpression) { ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(expression, context); return resolvedCall instanceof VariableAsFunctionResolvedCall; } return expression instanceof KtReferenceExpression; } public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) { if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false; VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor; return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar(); } @NotNull public static Pair<FunctionDescriptor, PsiElement> getContainingFunctionSkipFunctionLiterals( @Nullable DeclarationDescriptor startDescriptor, boolean strict ) { FunctionDescriptor containingFunctionDescriptor = DescriptorUtils.getParentOfType(startDescriptor, FunctionDescriptor.class, strict); PsiElement containingFunction = containingFunctionDescriptor != null ? DescriptorToSourceUtils.getSourceFromDescriptor(containingFunctionDescriptor) : null; while (containingFunction instanceof KtFunctionLiteral) { containingFunctionDescriptor = DescriptorUtils.getParentOfType(containingFunctionDescriptor, FunctionDescriptor.class); containingFunction = containingFunctionDescriptor != null ? DescriptorToSourceUtils .getSourceFromDescriptor(containingFunctionDescriptor) : null; } return new Pair<>(containingFunctionDescriptor, containingFunction); } @Nullable public static ResolvedCall<ConstructorDescriptor> getDelegationConstructorCall( @NotNull BindingContext bindingContext, @NotNull ConstructorDescriptor constructorDescriptor ) { return bindingContext.get(CONSTRUCTOR_RESOLVED_DELEGATION_CALL, constructorDescriptor); } static void addOwnDataTo( @NotNull BindingTrace trace, @Nullable TraceEntryFilter filter, boolean commitDiagnostics, @NotNull MutableSlicedMap map, MutableDiagnosticsWithSuppression diagnostics ) { map.forEach((slice, key, value) -> { if (filter == null || filter.accept(slice, key)) { trace.record(slice, key, value); } return null; }); if (!commitDiagnostics) return; for (Diagnostic diagnostic : diagnostics.getOwnDiagnostics()) { if (filter == null || filter.accept(null, diagnostic.getPsiElement())) { trace.report(diagnostic); } } } }