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 }