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.types.expressions;
018
019import com.google.common.collect.Lists;
020import com.intellij.openapi.project.Project;
021import com.intellij.openapi.util.Pair;
022import com.intellij.psi.PsiElement;
023import com.intellij.psi.tree.IElementType;
024import com.intellij.psi.tree.TokenSet;
025import com.intellij.psi.util.PsiTreeUtil;
026import org.jetbrains.annotations.NotNull;
027import org.jetbrains.annotations.Nullable;
028import org.jetbrains.jet.JetNodeTypes;
029import org.jetbrains.jet.lang.descriptors.*;
030import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory;
031import org.jetbrains.jet.lang.diagnostics.Diagnostic;
032import org.jetbrains.jet.lang.diagnostics.Errors;
033import org.jetbrains.jet.lang.psi.*;
034import org.jetbrains.jet.lang.resolve.*;
035import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
036import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition;
037import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
038import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
039import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
040import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil;
041import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
042import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
043import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
044import org.jetbrains.jet.lang.resolve.name.FqName;
045import org.jetbrains.jet.lang.resolve.name.Name;
046import org.jetbrains.jet.lang.resolve.scopes.JetScope;
047import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
048import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
049import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
050import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
051import org.jetbrains.jet.lang.types.*;
052import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
053import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
054import org.jetbrains.jet.util.slicedmap.WritableSlice;
055
056import java.util.*;
057
058import static org.jetbrains.jet.lang.diagnostics.Errors.*;
059import static org.jetbrains.jet.lang.resolve.BindingContext.*;
060
061public class ExpressionTypingUtils {
062
063    private ExpressionTypingUtils() {
064    }
065
066    public static final JetType CANT_INFER_LAMBDA_PARAM_TYPE = ErrorUtils.createErrorType("Cannot be inferred");
067
068    @Nullable
069    protected static ExpressionReceiver getExpressionReceiver(@NotNull JetExpression expression, @Nullable JetType type) {
070        if (type == null) return null;
071        return new ExpressionReceiver(expression, type);
072    }
073
074    @Nullable
075    protected static ExpressionReceiver getExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) {
076        return getExpressionReceiver(expression, facade.getTypeInfo(expression, context).getType());
077    }
078
079    @NotNull
080    protected static ExpressionReceiver safeGetExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) {
081        return new ExpressionReceiver(expression, facade.safeGetTypeInfo(expression, context).getType());
082    }
083
084    @NotNull
085    public static WritableScopeImpl newWritableScopeImpl(ExpressionTypingContext context, @NotNull String scopeDebugName) {
086        WritableScopeImpl scope = new WritableScopeImpl(
087                context.scope, context.scope.getContainingDeclaration(), new TraceBasedRedeclarationHandler(context.trace), scopeDebugName);
088        scope.changeLockLevel(WritableScope.LockLevel.BOTH);
089        return scope;
090    }
091
092    public static boolean isBoolean(@NotNull JetType type) {
093        return JetTypeChecker.INSTANCE.isSubtypeOf(type, KotlinBuiltIns.getInstance().getBooleanType());
094    }
095
096    public static boolean ensureBooleanResult(JetExpression operationSign, Name name, JetType resultType, ExpressionTypingContext context) {
097        return ensureBooleanResultWithCustomSubject(operationSign, resultType, "'" + name + "'", context);
098    }
099
100    public static boolean ensureBooleanResultWithCustomSubject(JetExpression operationSign, JetType resultType, String subjectName, ExpressionTypingContext context) {
101        if (resultType != null) {
102            // TODO : Relax?
103            if (!isBoolean(resultType)) {
104                context.trace.report(RESULT_TYPE_MISMATCH.on(operationSign, subjectName, KotlinBuiltIns.getInstance().getBooleanType(), resultType));
105                return false;
106            }
107        }
108        return true;
109    }
110
111    @NotNull
112    public static JetType getDefaultType(IElementType constantType) {
113        if (constantType == JetNodeTypes.INTEGER_CONSTANT) {
114            return KotlinBuiltIns.getInstance().getIntType();
115        }
116        else if (constantType == JetNodeTypes.FLOAT_CONSTANT) {
117            return KotlinBuiltIns.getInstance().getDoubleType();
118        }
119        else if (constantType == JetNodeTypes.BOOLEAN_CONSTANT) {
120            return KotlinBuiltIns.getInstance().getBooleanType();
121        }
122        else if (constantType == JetNodeTypes.CHARACTER_CONSTANT) {
123            return KotlinBuiltIns.getInstance().getCharType();
124        }
125        else if (constantType == JetNodeTypes.NULL) {
126            return KotlinBuiltIns.getInstance().getNullableNothingType();
127        }
128        else {
129            throw new IllegalArgumentException("Unsupported constant type: " + constantType);
130        }
131    }
132
133    public static boolean isTypeFlexible(@Nullable JetExpression expression) {
134        if (expression == null) return false;
135
136        return TokenSet.create(
137                JetNodeTypes.INTEGER_CONSTANT,
138                JetNodeTypes.FLOAT_CONSTANT
139        ).contains(expression.getNode().getElementType());
140    }
141
142    private static boolean isCapturedInInline(
143            @NotNull BindingContext context,
144            @NotNull DeclarationDescriptor scopeContainer,
145            @NotNull DeclarationDescriptor variableParent
146    ) {
147        PsiElement scopeDeclaration = BindingContextUtils.descriptorToDeclaration(context, scopeContainer);
148        if (!(scopeDeclaration instanceof JetFunctionLiteral)) {
149            return false;
150        }
151
152        PsiElement parent = scopeDeclaration.getParent();
153        assert parent instanceof JetFunctionLiteralExpression : "parent of JetFunctionLiteral is " + parent;
154        JetCallExpression callExpression = getCallExpression((JetFunctionLiteralExpression) parent);
155        if (callExpression == null) {
156            return false;
157        }
158
159        ResolvedCall<? extends CallableDescriptor> call = context.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
160        if (call == null) {
161            return false;
162        }
163
164        CallableDescriptor callable = call.getResultingDescriptor();
165        if (callable instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) callable).isInline()) {
166            DeclarationDescriptor scopeContainerParent = scopeContainer.getContainingDeclaration();
167            assert scopeContainerParent != null : "parent is null for " + scopeContainer;
168            return scopeContainerParent == variableParent || isCapturedInInline(context, scopeContainerParent, variableParent);
169        }
170        else {
171            return false;
172        }
173    }
174
175    @Nullable
176    private static JetCallExpression getCallExpression(@NotNull JetFunctionLiteralExpression functionLiteralExpression) {
177        PsiElement parent = functionLiteralExpression.getParent();
178        if (parent instanceof JetValueArgument) {
179            // foo({ ... })    or     foo(f = { ... })
180
181            PsiElement valueArgumentList = parent.getParent();
182            assert valueArgumentList instanceof JetValueArgumentList : "parent of value argument is " + valueArgumentList;
183
184            if (valueArgumentList.getParent() instanceof JetCallExpression) { // may be argument list of annotation
185                return (JetCallExpression) valueArgumentList.getParent();
186            }
187        }
188        else if (parent instanceof JetCallExpression) {
189            // foo { ... }
190
191            return  (JetCallExpression) parent;
192        }
193        return null;
194    }
195    public static void checkCapturingInClosure(JetSimpleNameExpression expression, BindingTrace trace, JetScope scope) {
196        VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
197        if (variable != null) {
198            DeclarationDescriptor variableParent = variable.getContainingDeclaration();
199            DeclarationDescriptor scopeContainer = scope.getContainingDeclaration();
200            if (scopeContainer != variableParent && variableParent instanceof CallableDescriptor) {
201                if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) {
202                    boolean inline = isCapturedInInline(trace.getBindingContext(), scopeContainer, variableParent);
203                    trace.record(CAPTURED_IN_CLOSURE, variable, inline ? CaptureKind.INLINE_ONLY : CaptureKind.NOT_INLINE);
204                }
205            }
206        }
207    }
208
209    public static boolean isVariableIterable(@NotNull ExpressionTypingServices expressionTypingServices,
210            @NotNull Project project, @NotNull VariableDescriptor variableDescriptor, @NotNull JetScope scope) {
211        JetExpression expression = JetPsiFactory.createExpression(project, "fake");
212        ExpressionReceiver expressionReceiver = new ExpressionReceiver(expression, variableDescriptor.getType());
213        ExpressionTypingContext context = ExpressionTypingContext.newContext(
214                expressionTypingServices,
215                new BindingTraceContext(),
216                scope,
217                DataFlowInfo.EMPTY,
218                TypeUtils.NO_EXPECTED_TYPE,
219                ExpressionPosition.FREE
220        );
221        return ControlStructureTypingVisitor.checkIterableConvention(expressionReceiver, context) != null;
222    }
223
224    /**
225     * Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
226     *
227     * @param callableFQN
228     * @param project
229     * @param scope
230     * @return
231     */
232    public static List<CallableDescriptor> canFindSuitableCall(
233            @NotNull FqName callableFQN,
234            @NotNull Project project,
235            @NotNull JetExpression receiverExpression,
236            @NotNull JetType receiverType,
237            @NotNull JetScope scope,
238            @NotNull ModuleDescriptor module
239    ) {
240        JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, callableFQN.asString());
241
242        Collection<? extends DeclarationDescriptor> declarationDescriptors = new QualifiedExpressionResolver()
243                .analyseImportReference(importDirective, scope, new BindingTraceContext(), module);
244
245        List<CallableDescriptor> callableExtensionDescriptors = new ArrayList<CallableDescriptor>();
246        ReceiverValue receiverValue = new ExpressionReceiver(receiverExpression, receiverType);
247
248        for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
249            if (declarationDescriptor instanceof CallableDescriptor) {
250                CallableDescriptor callableDescriptor = (CallableDescriptor) declarationDescriptor;
251
252                if (checkIsExtensionCallable(receiverValue, callableDescriptor)) {
253                    callableExtensionDescriptors.add(callableDescriptor);
254                }
255            }
256        }
257
258        return callableExtensionDescriptors;
259    }
260
261    /*
262    * Checks if receiver declaration could be resolved to call expected receiver.
263    */
264    public static boolean checkIsExtensionCallable (
265            @NotNull ReceiverValue receiverArgument,
266            @NotNull CallableDescriptor callableDescriptor
267    ) {
268        JetType type = receiverArgument.getType();
269
270        if (type instanceof NamespaceType) {
271            // This fake class ruins standard algorithms
272            return false;
273        }
274
275        if (checkReceiverResolution(receiverArgument, type, callableDescriptor)) return true;
276        if (type.isNullable()) {
277            JetType notNullableType = TypeUtils.makeNotNullable(type);
278            if (checkReceiverResolution(receiverArgument, notNullableType, callableDescriptor)) return true;
279        }
280        return false;
281    }
282
283    private static boolean checkReceiverResolution (
284            @NotNull ReceiverValue receiverArgument,
285            @NotNull JetType receiverType,
286            @NotNull CallableDescriptor callableDescriptor
287    ) {
288        ReceiverParameterDescriptor receiverParameter = callableDescriptor.getReceiverParameter();
289
290        if (!receiverArgument.exists() && receiverParameter == null) {
291            // Both receivers do not exist
292            return true;
293        }
294
295        if (!(receiverArgument.exists() && receiverParameter != null)) {
296            return false;
297        }
298
299        Set<Name> typeNamesInReceiver = collectUsedTypeNames(receiverParameter.getType());
300
301        ConstraintSystem constraintSystem = new ConstraintSystemImpl();
302        for (TypeParameterDescriptor typeParameterDescriptor : callableDescriptor.getTypeParameters()) {
303            if (typeNamesInReceiver.contains(typeParameterDescriptor.getName())) {
304                constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT);
305            }
306        }
307
308        constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION);
309        return constraintSystem.isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true);
310    }
311
312    private static Set<Name> collectUsedTypeNames(@NotNull JetType jetType) {
313        Set<Name> typeNames = new HashSet<Name>();
314
315        ClassifierDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
316        if (descriptor != null) {
317            typeNames.add(descriptor.getName());
318        }
319
320        for (TypeProjection argument : jetType.getArguments()) {
321            typeNames.addAll(collectUsedTypeNames(argument.getType()));
322        }
323
324        return typeNames;
325    }
326
327    @NotNull
328    public static OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
329            @NotNull ExpressionTypingContext context,
330            @NotNull ReceiverValue receiver,
331            @NotNull Name name,
332            @NotNull JetType... argumentTypes
333    ) {
334        TemporaryBindingTrace traceWithFakeArgumentInfo = TemporaryBindingTrace.create(context.trace, "trace to store fake argument for",
335                                                                                       name);
336        List<JetExpression> fakeArguments = Lists.newArrayList();
337        for (JetType type : argumentTypes) {
338            fakeArguments.add(createFakeExpressionOfType(context.expressionTypingServices.getProject(), traceWithFakeArgumentInfo,
339                                                         "fakeArgument" + fakeArguments.size(), type));
340        }
341        return makeAndResolveFakeCall(receiver, context.replaceBindingTrace(traceWithFakeArgumentInfo), fakeArguments, name).getSecond();
342    }
343
344    public static JetExpression createFakeExpressionOfType(
345            @NotNull Project project,
346            @NotNull BindingTrace trace,
347            @NotNull String argumentName,
348            @NotNull JetType argumentType
349    ) {
350        JetExpression fakeExpression = JetPsiFactory.createExpression(project, argumentName);
351        trace.record(EXPRESSION_TYPE, fakeExpression, argumentType);
352        trace.record(PROCESSED, fakeExpression);
353        return fakeExpression;
354    }
355
356    @NotNull
357    public static OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
358            @NotNull ExpressionTypingContext context,
359            @NotNull ReceiverValue receiver,
360            @NotNull Name name
361    ) {
362        return makeAndResolveFakeCall(receiver, context, Collections.<JetExpression>emptyList(), name).getSecond();
363    }
364
365    @NotNull
366    public static Pair<Call, OverloadResolutionResults<FunctionDescriptor>> makeAndResolveFakeCall(
367            @NotNull ReceiverValue receiver,
368            @NotNull ExpressionTypingContext context,
369            @NotNull List<JetExpression> valueArguments,
370            @NotNull Name name
371    ) {
372        final JetReferenceExpression fake = JetPsiFactory.createSimpleName(context.expressionTypingServices.getProject(), "fake");
373        TemporaryBindingTrace fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name);
374        Call call = CallMaker.makeCallWithExpressions(fake, receiver, null, fake, valueArguments);
375        OverloadResolutionResults<FunctionDescriptor> results =
376                context.replaceBindingTrace(fakeTrace).resolveCallWithGivenName(call, fake, name);
377        if (results.isSuccess()) {
378            fakeTrace.commit(new TraceEntryFilter() {
379                @Override
380                public boolean accept(@NotNull WritableSlice<?, ?> slice, Object key) {
381                    // excluding all entries related to fake expression
382                    return key != fake;
383                }
384            }, false);
385        }
386        return Pair.create(call, results);
387    }
388
389    public static void defineLocalVariablesFromMultiDeclaration(
390            @NotNull WritableScope writableScope,
391            @NotNull JetMultiDeclaration multiDeclaration,
392            @NotNull ReceiverValue receiver,
393            @NotNull JetExpression reportErrorsOn,
394            @NotNull ExpressionTypingContext context
395    ) {
396        int componentIndex = 1;
397        for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) {
398            Name componentName = Name.identifier(DescriptorResolver.COMPONENT_FUNCTION_NAME_PREFIX + componentIndex);
399            componentIndex++;
400
401            JetType expectedType = getExpectedTypeForComponent(context, entry);
402            OverloadResolutionResults<FunctionDescriptor> results =
403                    resolveFakeCall(context.replaceExpectedType(expectedType), receiver, componentName);
404
405            JetType componentType = null;
406            if (results.isSuccess()) {
407                context.trace.record(COMPONENT_RESOLVED_CALL, entry, results.getResultingCall());
408                componentType = results.getResultingDescriptor().getReturnType();
409                if (componentType != null && expectedType != TypeUtils.NO_EXPECTED_TYPE
410                       && !JetTypeChecker.INSTANCE.isSubtypeOf(componentType, expectedType)) {
411
412                    context.trace.report(
413                            COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.on(reportErrorsOn, componentName, componentType, expectedType));
414                }
415            }
416            else if (results.isAmbiguity()) {
417                context.trace.report(COMPONENT_FUNCTION_AMBIGUITY.on(reportErrorsOn, componentName, results.getResultingCalls()));
418            }
419            else {
420                context.trace.report(COMPONENT_FUNCTION_MISSING.on(reportErrorsOn, componentName, receiver.getType()));
421            }
422            if (componentType == null) {
423                componentType = ErrorUtils.createErrorType(componentName + "() return type");
424            }
425            VariableDescriptor variableDescriptor = context.expressionTypingServices.getDescriptorResolver().
426                resolveLocalVariableDescriptorWithType(writableScope, entry, componentType, context.trace);
427
428            VariableDescriptor olderVariable = writableScope.getLocalVariable(variableDescriptor.getName());
429            checkVariableShadowing(context, variableDescriptor, olderVariable);
430
431            writableScope.addVariableDescriptor(variableDescriptor);
432        }
433    }
434
435    public static void checkVariableShadowing(@NotNull ExpressionTypingContext context, @NotNull VariableDescriptor variableDescriptor, VariableDescriptor oldDescriptor) {
436        if (oldDescriptor != null && DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), oldDescriptor)) {
437            PsiElement declaration = BindingContextUtils.descriptorToDeclaration(context.trace.getBindingContext(), variableDescriptor);
438            if (declaration != null) {
439                context.trace.report(Errors.NAME_SHADOWING.on(declaration, variableDescriptor.getName().asString()));
440            }
441        }
442    }
443
444    @NotNull
445    private static JetType getExpectedTypeForComponent(ExpressionTypingContext context, JetMultiDeclarationEntry entry) {
446        JetTypeReference entryTypeRef = entry.getTypeRef();
447        if (entryTypeRef != null) {
448            return context.expressionTypingServices.getTypeResolver().resolveType(context.scope, entryTypeRef, context.trace, true);
449        }
450        else {
451            return TypeUtils.NO_EXPECTED_TYPE;
452        }
453    }
454
455    public static ObservableBindingTrace makeTraceInterceptingTypeMismatch(@NotNull BindingTrace trace, @NotNull final JetElement expressionToWatch, @NotNull final boolean[] mismatchFound) {
456        return new ObservableBindingTrace(trace) {
457
458            @Override
459            public void report(@NotNull Diagnostic diagnostic) {
460                AbstractDiagnosticFactory factory = diagnostic.getFactory();
461                if ((factory == TYPE_MISMATCH || factory == ERROR_COMPILE_TIME_VALUE)
462                        && diagnostic.getPsiElement() == expressionToWatch) {
463                    mismatchFound[0] = true;
464                }
465                if (TYPE_INFERENCE_ERRORS.contains(factory) &&
466                    PsiTreeUtil.isAncestor(expressionToWatch, diagnostic.getPsiElement(), false)) {
467                    mismatchFound[0] = true;
468                }
469                super.report(diagnostic);
470            }
471        };
472    }
473
474    @NotNull
475    public static JetTypeInfo getTypeInfoOrNullType(
476            @Nullable JetExpression expression,
477            @NotNull ExpressionTypingContext context,
478            @NotNull ExpressionTypingInternals facade
479    ) {
480        return expression != null
481               ? facade.getTypeInfo(expression, context)
482               : JetTypeInfo.create(null, context.dataFlowInfo);
483    }
484}