/*
* Copyright 2010-2016 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.types.expressions;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
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.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.impl.FunctionExpressionDescriptor;
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.ObservableBindingTrace;
import org.jetbrains.kotlin.resolve.OverloadChecker;
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
import org.jetbrains.kotlin.resolve.scopes.TraceBasedLocalRedeclarationChecker;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.kotlin.diagnostics.Errors.TYPE_INFERENCE_ERRORS;
import static org.jetbrains.kotlin.resolve.BindingContext.PROCESSED;
public class ExpressionTypingUtils {
@Nullable
public static ExpressionReceiver getExpressionReceiver(
@NotNull ExpressionTypingFacade facade,
@NotNull KtExpression expression,
ExpressionTypingContext context
) {
KotlinType type = facade.getTypeInfo(expression, context).getType();
if (type == null) return null;
return ExpressionReceiver.Companion.create(expression, type, context.trace.getBindingContext());
}
@NotNull
public static ExpressionReceiver safeGetExpressionReceiver(
@NotNull ExpressionTypingFacade facade,
@NotNull KtExpression expression,
ExpressionTypingContext context
) {
KotlinType type = safeGetType(facade.safeGetTypeInfo(expression, context));
return ExpressionReceiver.Companion.create(expression, type, context.trace.getBindingContext());
}
@NotNull
public static KotlinType safeGetType(@NotNull KotlinTypeInfo typeInfo) {
KotlinType type = typeInfo.getType();
assert type != null : "safeGetType should be invoked on safe KotlinTypeInfo; safeGetTypeInfo should return @NotNull type";
return type;
}
@NotNull
public static LexicalWritableScope newWritableScopeImpl(
@NotNull ExpressionTypingContext context,
@NotNull LexicalScopeKind scopeKind,
@NotNull OverloadChecker overloadChecker
) {
return new LexicalWritableScope(context.scope, context.scope.getOwnerDescriptor(), false,
new TraceBasedLocalRedeclarationChecker(context.trace, overloadChecker), scopeKind);
}
public static KtExpression createFakeExpressionOfType(
@NotNull Project project,
@NotNull BindingTrace trace,
@NotNull String argumentName,
@NotNull KotlinType argumentType
) {
KtExpression fakeExpression = KtPsiFactoryKt.KtPsiFactory(project, false).createExpression(argumentName);
trace.recordType(fakeExpression, argumentType);
trace.record(PROCESSED, fakeExpression);
return fakeExpression;
}
public static void checkVariableShadowing(
@NotNull LexicalScope scope,
@NotNull BindingTrace trace,
@NotNull VariableDescriptor variableDescriptor
) {
VariableDescriptor oldDescriptor = ScopeUtilsKt.findLocalVariable(scope, variableDescriptor.getName());
if (oldDescriptor != null && isLocal(variableDescriptor.getContainingDeclaration(), oldDescriptor)) {
PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor);
if (declaration != null) {
if (declaration instanceof KtDestructuringDeclarationEntry && declaration.getParent().getParent() instanceof KtParameter) {
// foo { a, (a, b) -> } -- do not report NAME_SHADOWING on the second 'a', because REDECLARATION must be reported here
PsiElement oldElement = DescriptorToSourceUtils.descriptorToDeclaration(oldDescriptor);
if (oldElement != null && oldElement.getParent().equals(declaration.getParent().getParent().getParent())) return;
}
trace.report(Errors.NAME_SHADOWING.on(declaration, variableDescriptor.getName().asString()));
}
}
}
public static ObservableBindingTrace makeTraceInterceptingTypeMismatch(
@NotNull BindingTrace trace,
@NotNull KtElement expressionToWatch,
@NotNull boolean[] mismatchFound
) {
return new ObservableBindingTrace(trace) {
@Override
public void report(@NotNull Diagnostic diagnostic) {
DiagnosticFactory<?> factory = diagnostic.getFactory();
if (Errors.TYPE_MISMATCH_ERRORS.contains(factory) && diagnostic.getPsiElement() == expressionToWatch) {
mismatchFound[0] = true;
}
if (TYPE_INFERENCE_ERRORS.contains(factory) &&
PsiTreeUtil.isAncestor(expressionToWatch, diagnostic.getPsiElement(), false)) {
mismatchFound[0] = true;
}
super.report(diagnostic);
}
};
}
@NotNull
public static KotlinTypeInfo getTypeInfoOrNullType(
@Nullable KtExpression expression,
@NotNull ExpressionTypingContext context,
@NotNull ExpressionTypingInternals facade
) {
return expression != null
? facade.getTypeInfo(expression, context)
: TypeInfoFactoryKt.noTypeInfo(context);
}
@SuppressWarnings("SuspiciousMethodCalls")
public static boolean isBinaryExpressionDependentOnExpectedType(@NotNull KtBinaryExpression expression) {
IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
return (operationType == KtTokens.IDENTIFIER || OperatorConventions.BINARY_OPERATION_NAMES.containsKey(operationType)
|| operationType == KtTokens.ELVIS);
}
public static boolean isUnaryExpressionDependentOnExpectedType(@NotNull KtUnaryExpression expression) {
return expression.getOperationReference().getReferencedNameElementType() == KtTokens.EXCLEXCL;
}
public static boolean isExclExclExpression(@Nullable KtExpression expression) {
return expression instanceof KtUnaryExpression
&& ((KtUnaryExpression) expression).getOperationReference().getReferencedNameElementType() == KtTokens.EXCLEXCL;
}
@NotNull
public static List<KotlinType> getValueParametersTypes(@NotNull List<ValueParameterDescriptor> valueParameters) {
List<KotlinType> parameterTypes = new ArrayList<>(valueParameters.size());
for (ValueParameterDescriptor parameter : valueParameters) {
parameterTypes.add(parameter.getType());
}
return parameterTypes;
}
/**
* The primary case for local extensions is the following:
*
* I had a locally declared extension function or a local variable of function type called foo
* And I called it on my x
* Now, someone added function foo() to the class of x
* My code should not change
*
* thus
*
* local extension prevail over members (and members prevail over all non-local extensions)
*/
public static boolean isLocal(DeclarationDescriptor containerOfTheCurrentLocality, DeclarationDescriptor candidate) {
if (candidate instanceof ValueParameterDescriptor) {
return true;
}
DeclarationDescriptor parent = candidate.getContainingDeclaration();
if (!(parent instanceof FunctionDescriptor)) {
return false;
}
FunctionDescriptor functionDescriptor = (FunctionDescriptor) parent;
DeclarationDescriptor current = containerOfTheCurrentLocality;
while (current != null) {
if (current == functionDescriptor) {
return true;
}
current = current.getContainingDeclaration();
}
return false;
}
public static boolean dependsOnExpectedType(@Nullable KtExpression expression) {
KtExpression expr = KtPsiUtil.deparenthesize(expression);
if (expr == null) return false;
if (expr instanceof KtBinaryExpressionWithTypeRHS) {
return false;
}
if (expr instanceof KtBinaryExpression) {
return isBinaryExpressionDependentOnExpectedType((KtBinaryExpression) expr);
}
if (expr instanceof KtUnaryExpression) {
return isUnaryExpressionDependentOnExpectedType((KtUnaryExpression) expr);
}
return true;
}
private ExpressionTypingUtils() {
}
public static boolean isFunctionLiteral(@Nullable DeclarationDescriptor descriptor) {
return descriptor instanceof AnonymousFunctionDescriptor;
}
public static boolean isLocalFunction(@Nullable DeclarationDescriptor descriptor) {
if (descriptor != null && descriptor.getClass() == SimpleFunctionDescriptorImpl.class) {
return ((SimpleFunctionDescriptorImpl) descriptor).getVisibility() == Visibilities.LOCAL;
}
return false;
}
public static boolean isFunctionExpression(@Nullable DeclarationDescriptor descriptor) {
return descriptor instanceof FunctionExpressionDescriptor;
}
}