001    /*
002     * Copyright 2010-2015 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.kotlin.resolve.calls;
018    
019    import com.intellij.lang.ASTNode;
020    import com.intellij.psi.PsiElement;
021    import com.intellij.psi.util.PsiTreeUtil;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
025    import org.jetbrains.kotlin.descriptors.*;
026    import org.jetbrains.kotlin.lexer.JetTokens;
027    import org.jetbrains.kotlin.psi.*;
028    import org.jetbrains.kotlin.resolve.BindingContext;
029    import org.jetbrains.kotlin.resolve.BindingTrace;
030    import org.jetbrains.kotlin.resolve.DescriptorUtils;
031    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
032    import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext;
033    import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode;
034    import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
035    import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
036    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
037    import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
038    import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil;
039    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
040    import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
041    import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
042    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
043    import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
044    import org.jetbrains.kotlin.resolve.scopes.receivers.*;
045    import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
046    import org.jetbrains.kotlin.types.ErrorUtils;
047    import org.jetbrains.kotlin.types.JetType;
048    import org.jetbrains.kotlin.types.TypeUtils;
049    import org.jetbrains.kotlin.types.expressions.DataFlowAnalyzer;
050    import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
051    import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
052    import org.jetbrains.kotlin.types.expressions.JetTypeInfo;
053    import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage;
054    
055    import javax.inject.Inject;
056    import java.util.Collections;
057    import java.util.List;
058    
059    import static org.jetbrains.kotlin.diagnostics.Errors.*;
060    import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
061    import static org.jetbrains.kotlin.resolve.scopes.receivers.ReceiversPackage.createQualifier;
062    import static org.jetbrains.kotlin.resolve.scopes.receivers.ReceiversPackage.resolveAsStandaloneExpression;
063    import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
064    
065    public class CallExpressionResolver {
066    
067        private final CallResolver callResolver;
068        private final ConstantExpressionEvaluator constantExpressionEvaluator;
069        private final SymbolUsageValidator symbolUsageValidator;
070        private final DataFlowAnalyzer dataFlowAnalyzer;
071    
072        public CallExpressionResolver(
073                @NotNull CallResolver callResolver,
074                @NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
075                @NotNull SymbolUsageValidator symbolUsageValidator,
076                @NotNull DataFlowAnalyzer dataFlowAnalyzer
077        ) {
078            this.callResolver = callResolver;
079            this.constantExpressionEvaluator = constantExpressionEvaluator;
080            this.symbolUsageValidator = symbolUsageValidator;
081            this.dataFlowAnalyzer = dataFlowAnalyzer;
082        }
083    
084        private ExpressionTypingServices expressionTypingServices;
085    
086        // component dependency cycle
087        @Inject
088        public void setExpressionTypingServices(@NotNull ExpressionTypingServices expressionTypingServices) {
089            this.expressionTypingServices = expressionTypingServices;
090        }
091    
092        @Nullable
093        public ResolvedCall<FunctionDescriptor> getResolvedCallForFunction(
094                @NotNull Call call, @NotNull JetExpression callExpression,
095                @NotNull ResolutionContext context, @NotNull CheckArgumentTypesMode checkArguments,
096                @NotNull boolean[] result
097        ) {
098            OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveFunctionCall(
099                    BasicCallResolutionContext.create(context, call, checkArguments));
100            if (!results.isNothing()) {
101                result[0] = true;
102                return OverloadResolutionResultsUtil.getResultingCall(results, context.contextDependency);
103            }
104            result[0] = false;
105            return null;
106        }
107    
108        @Nullable
109        private JetType getVariableType(
110                @NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
111                @Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context, @NotNull boolean[] result
112        ) {
113            TemporaryTraceAndCache temporaryForVariable = TemporaryTraceAndCache.create(
114                    context, "trace to resolve as local variable or property", nameExpression);
115            Call call = CallMaker.makePropertyCall(receiver, callOperationNode, nameExpression);
116            BasicCallResolutionContext contextForVariable = BasicCallResolutionContext.create(
117                    context.replaceTraceAndCache(temporaryForVariable),
118                    call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS);
119            OverloadResolutionResults<VariableDescriptor> resolutionResult = callResolver.resolveSimpleProperty(contextForVariable);
120    
121            // if the expression is a receiver in a qualified expression, it should be resolved after the selector is resolved
122            boolean isLHSOfDot = JetPsiUtil.isLHSOfDot(nameExpression);
123            if (!resolutionResult.isNothing() && resolutionResult.getResultCode() != OverloadResolutionResults.Code.CANDIDATES_WITH_WRONG_RECEIVER) {
124                boolean isQualifier = isLHSOfDot && resolutionResult.isSingleResult()
125                                      && resolutionResult.getResultingDescriptor() instanceof FakeCallableDescriptorForObject;
126                if (!isQualifier) {
127                    result[0] = true;
128                    temporaryForVariable.commit();
129                    return resolutionResult.isSingleResult() ? resolutionResult.getResultingDescriptor().getReturnType() : null;
130                }
131            }
132    
133            QualifierReceiver qualifier = createQualifier(nameExpression, receiver, context);
134            if (qualifier != null) {
135                result[0] = true;
136                if (!isLHSOfDot) {
137                    resolveAsStandaloneExpression(qualifier, context, symbolUsageValidator);
138                }
139                return null;
140            }
141            temporaryForVariable.commit();
142            result[0] = !resolutionResult.isNothing();
143            return resolutionResult.isSingleResult() ? resolutionResult.getResultingDescriptor().getReturnType() : null;
144        }
145    
146        @NotNull
147        public JetTypeInfo getSimpleNameExpressionTypeInfo(
148                @NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
149                @Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
150        ) {
151            boolean[] result = new boolean[1];
152    
153            TemporaryTraceAndCache temporaryForVariable = TemporaryTraceAndCache.create(
154                    context, "trace to resolve as variable", nameExpression);
155            JetType type =
156                    getVariableType(nameExpression, receiver, callOperationNode, context.replaceTraceAndCache(temporaryForVariable), result);
157            // TODO: for a safe call, it's necessary to set receiver != null here, as inside ArgumentTypeResolver.analyzeArgumentsAndRecordTypes
158            // Unfortunately it provokes problems with x?.y!!.foo() with the following x!!.bar():
159            // x != null proceeds to successive statements
160    
161            if (result[0]) {
162                temporaryForVariable.commit();
163                return TypeInfoFactoryPackage.createTypeInfo(type, context);
164            }
165    
166            Call call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, Collections.<ValueArgument>emptyList());
167            TemporaryTraceAndCache temporaryForFunction = TemporaryTraceAndCache.create(
168                    context, "trace to resolve as function", nameExpression);
169            ResolutionContext newContext = context.replaceTraceAndCache(temporaryForFunction);
170            ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
171                    call, nameExpression, newContext, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result);
172            if (result[0]) {
173                FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
174                temporaryForFunction.commit();
175                boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0;
176                context.trace.report(FUNCTION_CALL_EXPECTED.on(nameExpression, nameExpression, hasValueParameters));
177                type = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
178                return TypeInfoFactoryPackage.createTypeInfo(type, context);
179            }
180    
181            temporaryForVariable.commit();
182            return TypeInfoFactoryPackage.noTypeInfo(context);
183        }
184    
185        @NotNull
186        public JetTypeInfo getCallExpressionTypeInfo(
187                @NotNull JetCallExpression callExpression, @NotNull ReceiverValue receiver,
188                @Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
189        ) {
190            JetTypeInfo typeInfo = getCallExpressionTypeInfoWithoutFinalTypeCheck(callExpression, receiver, callOperationNode, context);
191            if (context.contextDependency == INDEPENDENT) {
192                dataFlowAnalyzer.checkType(typeInfo.getType(), callExpression, context);
193            }
194            return typeInfo;
195        }
196    
197        /**
198         * Visits a call expression and its arguments.
199         * Determines the result type and data flow information after the call.
200         */
201        @NotNull
202        public JetTypeInfo getCallExpressionTypeInfoWithoutFinalTypeCheck(
203                @NotNull JetCallExpression callExpression, @NotNull ReceiverValue receiver,
204                @Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
205        ) {
206            boolean[] result = new boolean[1];
207            Call call = CallMaker.makeCall(receiver, callOperationNode, callExpression);
208    
209            TemporaryTraceAndCache temporaryForFunction = TemporaryTraceAndCache.create(
210                    context, "trace to resolve as function call", callExpression);
211            ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
212                    call, callExpression,
213                    // It's possible start of a call so we should reset safe call chain
214                    context.replaceTraceAndCache(temporaryForFunction).replaceInsideCallChain(false),
215                    CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result);
216            if (result[0]) {
217                FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
218                temporaryForFunction.commit();
219                if (callExpression.getValueArgumentList() == null && callExpression.getFunctionLiteralArguments().isEmpty()) {
220                    // there are only type arguments
221                    boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0;
222                    context.trace.report(FUNCTION_CALL_EXPECTED.on(callExpression, callExpression, hasValueParameters));
223                }
224                if (functionDescriptor == null) {
225                    return TypeInfoFactoryPackage.noTypeInfo(context);
226                }
227                if (functionDescriptor instanceof ConstructorDescriptor) {
228                    DeclarationDescriptor containingDescriptor = functionDescriptor.getContainingDeclaration();
229                    if (DescriptorUtils.isAnnotationClass(containingDescriptor)
230                        && !canInstantiateAnnotationClass(callExpression, context.trace)) {
231                        context.trace.report(ANNOTATION_CLASS_CONSTRUCTOR_CALL.on(callExpression));
232                    }
233                    if (DescriptorUtils.isEnumClass(containingDescriptor)) {
234                        context.trace.report(ENUM_CLASS_CONSTRUCTOR_CALL.on(callExpression));
235                    }
236                    if (containingDescriptor instanceof ClassDescriptor
237                        && ((ClassDescriptor) containingDescriptor).getModality() == Modality.SEALED) {
238                        context.trace.report(SEALED_CLASS_CONSTRUCTOR_CALL.on(callExpression));
239                    }
240                }
241    
242                JetType type = functionDescriptor.getReturnType();
243                // Extracting jump out possible and jump point flow info from arguments, if any
244                List<? extends ValueArgument> arguments = callExpression.getValueArguments();
245                DataFlowInfo resultFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo();
246                DataFlowInfo jumpFlowInfo = resultFlowInfo;
247                boolean jumpOutPossible = false;
248                for (ValueArgument argument: arguments) {
249                    JetTypeInfo argTypeInfo = context.trace.get(BindingContext.EXPRESSION_TYPE_INFO, argument.getArgumentExpression());
250                    if (argTypeInfo != null && argTypeInfo.getJumpOutPossible()) {
251                        jumpOutPossible = true;
252                        jumpFlowInfo = argTypeInfo.getJumpFlowInfo();
253                        break;
254                    }
255                }
256                return TypeInfoFactoryPackage.createTypeInfo(type, resultFlowInfo, jumpOutPossible, jumpFlowInfo);
257            }
258    
259            JetExpression calleeExpression = callExpression.getCalleeExpression();
260            if (calleeExpression instanceof JetSimpleNameExpression && callExpression.getTypeArgumentList() == null) {
261                TemporaryTraceAndCache temporaryForVariable = TemporaryTraceAndCache.create(
262                        context, "trace to resolve as variable with 'invoke' call", callExpression);
263                JetType type = getVariableType((JetSimpleNameExpression) calleeExpression, receiver, callOperationNode,
264                                               context.replaceTraceAndCache(temporaryForVariable), result);
265                Qualifier qualifier = temporaryForVariable.trace.get(BindingContext.QUALIFIER, calleeExpression);
266                if (result[0] && (qualifier == null || qualifier.getPackageView() == null)) {
267                    temporaryForVariable.commit();
268                    context.trace.report(FUNCTION_EXPECTED.on(calleeExpression, calleeExpression,
269                                                              type != null ? type : ErrorUtils.createErrorType("")));
270                    return TypeInfoFactoryPackage.noTypeInfo(context);
271                }
272            }
273            temporaryForFunction.commit();
274            return TypeInfoFactoryPackage.noTypeInfo(context);
275        }
276    
277        private static boolean canInstantiateAnnotationClass(@NotNull JetCallExpression expression, @NotNull BindingTrace trace) {
278            //noinspection unchecked
279            PsiElement parent = PsiTreeUtil.getParentOfType(expression, JetValueArgument.class, JetParameter.class);
280            if (parent instanceof JetValueArgument) {
281                return PsiTreeUtil.getParentOfType(parent, JetAnnotationEntry.class) != null;
282            }
283            else if (parent instanceof JetParameter) {
284                JetClass jetClass = PsiTreeUtil.getParentOfType(parent, JetClass.class);
285                if (jetClass != null) {
286                    DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, jetClass);
287                    return DescriptorUtils.isAnnotationClass(descriptor);
288                }
289            }
290            return false;
291        }
292    
293        @NotNull
294        private JetTypeInfo getSelectorReturnTypeInfo(
295                @NotNull ReceiverValue receiver,
296                @Nullable ASTNode callOperationNode,
297                @Nullable JetExpression selectorExpression,
298                @NotNull ExpressionTypingContext context
299        ) {
300            if (selectorExpression instanceof JetCallExpression) {
301                return getCallExpressionTypeInfoWithoutFinalTypeCheck((JetCallExpression) selectorExpression, receiver,
302                                                                      callOperationNode, context);
303            }
304            else if (selectorExpression instanceof JetSimpleNameExpression) {
305                return getSimpleNameExpressionTypeInfo((JetSimpleNameExpression) selectorExpression, receiver, callOperationNode, context);
306            }
307            else if (selectorExpression != null) {
308                context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression, selectorExpression.getText()));
309            }
310            return TypeInfoFactoryPackage.noTypeInfo(context);
311        }
312    
313        /**
314         * Extended variant of JetTypeInfo stores additional information
315         * about data flow info from the left-more receiver, e.g. x != null for
316         * foo(x!!)?.bar(y!!)?.baz()
317         */
318        private static class JetTypeInfoInsideSafeCall extends JetTypeInfo {
319    
320            private final DataFlowInfo safeCallChainInfo;
321    
322            private JetTypeInfoInsideSafeCall(@NotNull JetTypeInfo typeInfo, @Nullable DataFlowInfo safeCallChainInfo) {
323                super(typeInfo.getType(), typeInfo.getDataFlowInfo(), typeInfo.getJumpOutPossible(), typeInfo.getJumpFlowInfo());
324                this.safeCallChainInfo = safeCallChainInfo;
325            }
326    
327            /**
328             * Returns safe call chain information which is taken from the left-most receiver of a chain
329             * foo(x!!)?.bar(y!!)?.gav() ==> x != null is safe call chain information
330             */
331            @Nullable
332            public DataFlowInfo getSafeCallChainInfo() {
333                return safeCallChainInfo;
334            }
335        }
336    
337    
338        /**
339         * Visits a qualified expression like x.y or x?.z controlling data flow information changes.
340         *
341         * @return qualified expression type together with data flow information
342         */
343        @NotNull
344        public JetTypeInfo getQualifiedExpressionTypeInfo(
345                @NotNull JetQualifiedExpression expression, @NotNull ExpressionTypingContext context
346        ) {
347            // TODO : functions as values
348            JetExpression selectorExpression = expression.getSelectorExpression();
349            JetExpression receiverExpression = expression.getReceiverExpression();
350            boolean safeCall = (expression.getOperationSign() == JetTokens.SAFE_ACCESS);
351            ResolutionContext contextForReceiver = context.replaceExpectedType(NO_EXPECTED_TYPE).
352                    replaceContextDependency(INDEPENDENT).
353                    replaceInsideCallChain(true); // Enter call chain
354            // Visit receiver (x in x.y or x?.z) here. Recursion is possible.
355            JetTypeInfo receiverTypeInfo = expressionTypingServices.getTypeInfo(receiverExpression, contextForReceiver);
356            JetType receiverType = receiverTypeInfo.getType();
357            QualifierReceiver qualifierReceiver = (QualifierReceiver) context.trace.get(BindingContext.QUALIFIER, receiverExpression);
358    
359            if (receiverType == null) receiverType = ErrorUtils.createErrorType("Type for " + expression.getText());
360    
361            ReceiverValue receiver = qualifierReceiver == null ? new ExpressionReceiver(receiverExpression, receiverType) : qualifierReceiver;
362            DataFlowInfo receiverDataFlowInfo = receiverTypeInfo.getDataFlowInfo();
363            // Receiver changes should be always applied, at least for argument analysis
364            context = context.replaceDataFlowInfo(receiverDataFlowInfo);
365    
366            // Visit selector (y in x.y) here. Recursion is also possible.
367            JetTypeInfo selectorReturnTypeInfo = getSelectorReturnTypeInfo(
368                    receiver, expression.getOperationTokenNode(), selectorExpression, context);
369            JetType selectorReturnType = selectorReturnTypeInfo.getType();
370    
371            resolveDeferredReceiverInQualifiedExpression(qualifierReceiver, expression, context);
372            checkNestedClassAccess(expression, context);
373    
374            //TODO move further
375            if (safeCall) {
376                if (selectorReturnType != null) {
377                    if (TypeUtils.isNullableType(receiverType)) {
378                        selectorReturnType = TypeUtils.makeNullable(selectorReturnType);
379                        selectorReturnTypeInfo = selectorReturnTypeInfo.replaceType(selectorReturnType);
380                    }
381                }
382            }
383            // TODO : this is suspicious: remove this code?
384            if (selectorExpression != null && selectorReturnType != null) {
385                context.trace.recordType(selectorExpression, selectorReturnType);
386            }
387    
388            CompileTimeConstant<?> value = constantExpressionEvaluator.evaluateExpression(expression, context.trace, context.expectedType);
389            if (value != null && value.getIsPure()) {
390                return dataFlowAnalyzer.createCompileTimeConstantTypeInfo(value, expression, context);
391            }
392    
393            JetTypeInfo typeInfo;
394            DataFlowInfo safeCallChainInfo;
395            if (receiverTypeInfo instanceof JetTypeInfoInsideSafeCall) {
396                safeCallChainInfo = ((JetTypeInfoInsideSafeCall) receiverTypeInfo).getSafeCallChainInfo();
397            }
398            else {
399                safeCallChainInfo = null;
400            }
401            if (safeCall) {
402                if (safeCallChainInfo == null) safeCallChainInfo = receiverDataFlowInfo;
403                if (context.insideCallChain) {
404                    // If we are inside safe call chain, we SHOULD take arguments into account, for example
405                    // x?.foo(y!!)?.bar(x.field)?.gav(y.field) (like smartCasts\safecalls\longChain)
406                    // Also, we should provide further safe call chain data flow information or
407                    // if we are in the most left safe call then just take it from receiver
408                    typeInfo = new JetTypeInfoInsideSafeCall(selectorReturnTypeInfo, safeCallChainInfo);
409                }
410                else {
411                    // Here we should not take selector data flow info into account because it's only one branch, see KT-7204
412                    // x?.foo(y!!) // y becomes not-nullable during argument analysis
413                    // y.bar()     // ERROR: y is nullable at this point
414                    // So we should just take safe call chain data flow information, e.g. foo(x!!)?.bar()?.gav()
415                    // If it's null, we must take receiver normal info, it's a chain with length of one like foo(x!!)?.bar() and
416                    // safe call chain information does not yet exist
417                    typeInfo = selectorReturnTypeInfo.replaceDataFlowInfo(safeCallChainInfo);
418                }
419            }
420            else {
421                // It's not a safe call, so we can take selector data flow information
422                // Safe call chain information also should be provided because it's can be a part of safe call chain
423                if (context.insideCallChain || safeCallChainInfo == null) {
424                    // Not a safe call inside call chain with safe calls OR
425                    // call chain without safe calls at all
426                    typeInfo = new JetTypeInfoInsideSafeCall(selectorReturnTypeInfo, selectorReturnTypeInfo.getDataFlowInfo());
427                }
428                else {
429                    // Exiting call chain with safe calls -- take data flow info from the lest-most receiver
430                    // foo(x!!)?.bar().gav()
431                    typeInfo = selectorReturnTypeInfo.replaceDataFlowInfo(safeCallChainInfo);
432                }
433            }
434            if (context.contextDependency == INDEPENDENT) {
435                dataFlowAnalyzer.checkType(typeInfo.getType(), expression, context);
436            }
437            return typeInfo;
438        }
439    
440        private void resolveDeferredReceiverInQualifiedExpression(
441                @Nullable QualifierReceiver qualifierReceiver,
442                @NotNull JetQualifiedExpression qualifiedExpression,
443                @NotNull ExpressionTypingContext context
444        ) {
445            if (qualifierReceiver == null) return;
446            JetExpression calleeExpression =
447                    JetPsiUtil.deparenthesize(CallUtilPackage.getCalleeExpressionIfAny(qualifiedExpression.getSelectorExpression()), false);
448            DeclarationDescriptor selectorDescriptor =
449                    calleeExpression instanceof JetReferenceExpression
450                    ? context.trace.get(BindingContext.REFERENCE_TARGET, (JetReferenceExpression) calleeExpression) : null;
451            ReceiversPackage.resolveAsReceiverInQualifiedExpression(qualifierReceiver, context, symbolUsageValidator, selectorDescriptor);
452        }
453    
454        private static void checkNestedClassAccess(
455                @NotNull JetQualifiedExpression expression,
456                @NotNull ExpressionTypingContext context
457        ) {
458            JetExpression selectorExpression = expression.getSelectorExpression();
459            if (selectorExpression == null) return;
460    
461            // A.B - if B is a nested class accessed by outer class, 'A' and 'A.B' were marked as qualifiers
462            // a.B - if B is a nested class accessed by instance reference, 'a.B' was marked as a qualifier, but 'a' was not (it's an expression)
463    
464            Qualifier expressionQualifier = context.trace.get(BindingContext.QUALIFIER, expression);
465            Qualifier receiverQualifier = context.trace.get(BindingContext.QUALIFIER, expression.getReceiverExpression());
466    
467            if (receiverQualifier == null && expressionQualifier != null) {
468                assert expressionQualifier.getClassifier() instanceof ClassDescriptor :
469                        "Only class can (package cannot) be accessed by instance reference: " + expressionQualifier;
470                context.trace.report(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE
471                                             .on(selectorExpression, (ClassDescriptor) expressionQualifier.getClassifier()));
472            }
473        }
474    }