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