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