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