@Override public <F extends CallableDescriptor> void check( @NotNull ResolvedCall<F> resolvedCall, @NotNull BasicCallResolutionContext context) { JetExpression expression = context.call.getCalleeExpression(); if (expression == null) { return; } // checking that only invoke or inlinable extension called on function parameter CallableDescriptor targetDescriptor = resolvedCall.getResultingDescriptor(); checkCallWithReceiver( context, targetDescriptor, resolvedCall.getDispatchReceiver(), expression); checkCallWithReceiver( context, targetDescriptor, resolvedCall.getExtensionReceiver(), expression); if (inlinableParameters.contains(targetDescriptor)) { if (!isInsideCall(expression)) { context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(expression, expression, descriptor)); } } for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : resolvedCall.getValueArguments().entrySet()) { ResolvedValueArgument value = entry.getValue(); ValueParameterDescriptor valueDescriptor = entry.getKey(); if (!(value instanceof DefaultValueArgument)) { for (ValueArgument argument : value.getArguments()) { checkValueParameter(context, targetDescriptor, argument, valueDescriptor); } } } checkVisibility(targetDescriptor, expression, context); checkRecursion(context, targetDescriptor, expression); }
@Nullable private ConstructorDescriptor getDelegatedConstructor( @NotNull ConstructorDescriptor constructor) { ResolvedCall<ConstructorDescriptor> call = trace.get(CONSTRUCTOR_RESOLVED_DELEGATION_CALL, constructor); return call == null || !call.getStatus().isSuccess() ? null : call.getResultingDescriptor().getOriginal(); }
@NotNull private static FunctionDescriptor standardFunction( ClassDescriptor classDescriptor, String name, Project project, KotlinType... parameterTypes) { ModuleDescriptorImpl emptyModule = KotlinTestUtils.createEmptyModule(); ContainerForTests container = InjectionKt.createContainerForTests(project, emptyModule); emptyModule.setDependencies(emptyModule); emptyModule.initialize(PackageFragmentProvider.Empty.INSTANCE); LexicalScopeImpl lexicalScope = new LexicalScopeImpl( ImportingScope.Empty.INSTANCE, classDescriptor, false, classDescriptor.getThisAsReceiverParameter(), LexicalScopeKind.SYNTHETIC); ExpressionTypingContext context = ExpressionTypingContext.newContext( new BindingTraceContext(), lexicalScope, DataFlowInfoFactory.EMPTY, TypeUtils.NO_EXPECTED_TYPE); OverloadResolutionResults<FunctionDescriptor> functions = container .getFakeCallResolver() .resolveFakeCall( context, null, Name.identifier(name), null, null, FakeCallKind.OTHER, parameterTypes); for (ResolvedCall<? extends FunctionDescriptor> resolvedCall : functions.getResultingCalls()) { List<ValueParameterDescriptor> unsubstitutedValueParameters = resolvedCall.getResultingDescriptor().getValueParameters(); for (int i = 0, unsubstitutedValueParametersSize = unsubstitutedValueParameters.size(); i < unsubstitutedValueParametersSize; i++) { ValueParameterDescriptor unsubstitutedValueParameter = unsubstitutedValueParameters.get(i); if (unsubstitutedValueParameter.getType().equals(parameterTypes[i])) { return resolvedCall.getResultingDescriptor(); } } } throw new IllegalArgumentException( "Not found: kotlin::" + classDescriptor.getName() + "." + name + "(" + Arrays.toString(parameterTypes) + ")"); }
@Nullable private static CallableDescriptor getCalleeDescriptor( @NotNull CallCheckerContext context, @NotNull KtExpression expression, boolean unwrapVariableAsFunction) { if (!(expression instanceof KtSimpleNameExpression || expression instanceof KtThisExpression)) return null; ResolvedCall<?> thisCall = CallUtilKt.getResolvedCall(expression, context.getTrace().getBindingContext()); if (unwrapVariableAsFunction && thisCall instanceof VariableAsFunctionResolvedCall) { return ((VariableAsFunctionResolvedCall) thisCall).getVariableCall().getResultingDescriptor(); } return thisCall != null ? thisCall.getResultingDescriptor() : null; }
public LambdaInfo( @NotNull KtExpression expression, @NotNull KotlinTypeMapper typeMapper, boolean isCrossInline, boolean isBoundCallableReference) { this.isCrossInline = isCrossInline; this.expression = expression instanceof KtLambdaExpression ? ((KtLambdaExpression) expression).getFunctionLiteral() : expression; this.typeMapper = typeMapper; this.isBoundCallableReference = isBoundCallableReference; BindingContext bindingContext = typeMapper.getBindingContext(); FunctionDescriptor function = bindingContext.get(BindingContext.FUNCTION, this.expression); if (function == null && expression instanceof KtCallableReferenceExpression) { VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, this.expression); assert variableDescriptor instanceof VariableDescriptorWithAccessors : "Reference expression not resolved to variable descriptor with accessors: " + expression.getText(); classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor); closureClassType = typeMapper.mapClass(classDescriptor); SimpleFunctionDescriptor getFunction = PropertyReferenceCodegen.findGetFunction(variableDescriptor); functionDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor); ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert( ((KtCallableReferenceExpression) expression).getCallableReference(), bindingContext); propertyReferenceInfo = new PropertyReferenceInfo( (VariableDescriptor) resolvedCall.getResultingDescriptor(), getFunction); } else { propertyReferenceInfo = null; functionDescriptor = function; assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText(); classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor); closureClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor); } closure = bindingContext.get(CLOSURE, classDescriptor); assert closure != null : "Closure for lambda should be not null " + expression.getText(); labels = InlineCodegen.getDeclarationLabels(expression, functionDescriptor); }
protected void generateStub( @Nullable ResolvedCall<?> resolvedCall, @NotNull ExpressionCodegen codegen) { leaveTemps(); assert resolvedCall != null; String message = "Call is part of inline cycle: " + resolvedCall.getCall().getCallElement().getText(); AsmUtil.genThrow(codegen.v, "java/lang/UnsupportedOperationException", message); }
@Nullable private static JetFunction getFunctionDeclaration(@NotNull ResolvedCall<?> resolvedCall) { PsiElement result = QuickFixUtil.safeGetDeclaration(resolvedCall.getResultingDescriptor()); if (result instanceof JetFunction) { return (JetFunction) result; } return null; }
@NotNull public static JsNode translate( @NotNull JetCallExpression expression, @Nullable JsExpression receiver, @NotNull TranslationContext context) { ResolvedCall<? extends FunctionDescriptor> resolvedCall = getFunctionResolvedCallWithAssert(expression, context.bindingContext()); if (isJsCall(resolvedCall)) { return (new CallExpressionTranslator(expression, receiver, context)).translateJsCode(); } JsExpression callExpression = (new CallExpressionTranslator(expression, receiver, context)).translate(); if (!resolvedCall.isSafeCall() && shouldBeInlined(expression, context)) { setInlineCallMetadata(callExpression, expression, resolvedCall, context); } return callExpression; }
// this is hack for a[b]++ -> a.set(b, a.get(b) + 1). Frontend generate fake expression for // a.get(b) + 1. @NotNull private TranslationContext contextWithValueParameterAliasInArrayGetAccess( @NotNull JsExpression toSetTo) { ResolvedCall<FunctionDescriptor> resolvedCall = BindingUtils.getResolvedCallForArrayAccess( bindingContext(), expression, /*isGetter = */ false); List<ResolvedValueArgument> arguments = resolvedCall.getValueArgumentsByIndex(); if (arguments == null) { throw new IllegalStateException( "Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor()); } ResolvedValueArgument lastArgument = arguments.get(arguments.size() - 1); assert lastArgument instanceof ExpressionValueArgument : "Last argument of array-like setter must be ExpressionValueArgument: " + lastArgument; ValueArgument valueArgument = ((ExpressionValueArgument) lastArgument).getValueArgument(); assert valueArgument != null; JetExpression element = valueArgument.getArgumentExpression(); return context() .innerContextWithAliasesForExpressions(Collections.singletonMap(element, toSetTo)); }
@Nullable private KotlinType checkConventionForIterator( @NotNull ExpressionTypingContext context, @NotNull KtExpression loopRangeExpression, @NotNull KotlinType iteratorType, @NotNull String name, @NotNull DiagnosticFactory1<KtExpression, KotlinType> ambiguity, @NotNull DiagnosticFactory1<KtExpression, KotlinType> missing, @NotNull DiagnosticFactory1<KtExpression, KotlinType> noneApplicable, @NotNull WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> resolvedCallKey) { OverloadResolutionResults<FunctionDescriptor> nextResolutionResults = fakeCallResolver.resolveFakeCall( context, new TransientReceiver(iteratorType), Name.identifier(name), loopRangeExpression); if (nextResolutionResults.isAmbiguity()) { context.trace.report(ambiguity.on(loopRangeExpression, iteratorType)); } else if (nextResolutionResults.isNothing()) { context.trace.report(missing.on(loopRangeExpression, iteratorType)); } else if (!nextResolutionResults.isSuccess()) { context.trace.report(noneApplicable.on(loopRangeExpression, iteratorType)); } else { assert nextResolutionResults.isSuccess(); ResolvedCall<FunctionDescriptor> resolvedCall = nextResolutionResults.getResultingCall(); context.trace.record(resolvedCallKey, loopRangeExpression, resolvedCall); FunctionDescriptor functionDescriptor = resolvedCall.getResultingDescriptor(); symbolUsageValidator.validateCall( resolvedCall, functionDescriptor, context.trace, loopRangeExpression); checkIfOperatorModifierPresent(loopRangeExpression, functionDescriptor, context.trace); return functionDescriptor.getReturnType(); } return null; }
@Nullable private DataFlowInfo resolveSecondaryConstructorDelegationCall( @NotNull DataFlowInfo outerDataFlowInfo, @NotNull BindingTrace trace, @NotNull LexicalScope scope, @NotNull JetSecondaryConstructor constructor, @NotNull ConstructorDescriptor descriptor, @NotNull CallChecker callChecker) { OverloadResolutionResults<?> results = callResolver.resolveConstructorDelegationCall( trace, scope, outerDataFlowInfo, descriptor, constructor.getDelegationCall(), callChecker); if (results != null && results.isSingleResult()) { ResolvedCall<? extends CallableDescriptor> resolvedCall = results.getResultingCall(); recordConstructorDelegationCall(trace, descriptor, resolvedCall); return resolvedCall.getDataFlowInfoForArguments().getResultInfo(); } return null; }
@Nullable public KotlinType checkIterableConvention( @NotNull ExpressionReceiver loopRange, ExpressionTypingContext context) { KtExpression loopRangeExpression = loopRange.getExpression(); // Make a fake call loopRange.iterator(), and try to resolve it Name iterator = Name.identifier("iterator"); Pair<Call, OverloadResolutionResults<FunctionDescriptor>> calls = fakeCallResolver.makeAndResolveFakeCall( loopRange, context, Collections.<KtExpression>emptyList(), iterator, loopRangeExpression, FakeCallKind.ITERATOR, loopRangeExpression); OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = calls.getSecond(); if (iteratorResolutionResults.isSuccess()) { ResolvedCall<FunctionDescriptor> iteratorResolvedCall = iteratorResolutionResults.getResultingCall(); context.trace.record( LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRangeExpression, iteratorResolvedCall); FunctionDescriptor iteratorFunction = iteratorResolvedCall.getResultingDescriptor(); checkIfOperatorModifierPresent(loopRangeExpression, iteratorFunction, context.trace); symbolUsageValidator.validateCall( iteratorResolvedCall, iteratorFunction, context.trace, loopRangeExpression); KotlinType iteratorType = iteratorFunction.getReturnType(); KotlinType hasNextType = checkConventionForIterator( context, loopRangeExpression, iteratorType, "hasNext", HAS_NEXT_FUNCTION_AMBIGUITY, HAS_NEXT_MISSING, HAS_NEXT_FUNCTION_NONE_APPLICABLE, LOOP_RANGE_HAS_NEXT_RESOLVED_CALL); if (hasNextType != null && !builtIns.isBooleanOrSubtype(hasNextType)) { context.trace.report(HAS_NEXT_FUNCTION_TYPE_MISMATCH.on(loopRangeExpression, hasNextType)); } return checkConventionForIterator( context, loopRangeExpression, iteratorType, "next", NEXT_AMBIGUITY, NEXT_MISSING, NEXT_NONE_APPLICABLE, LOOP_RANGE_NEXT_RESOLVED_CALL); } else { if (iteratorResolutionResults.isAmbiguity()) { context.trace.report( ITERATOR_AMBIGUITY.on( loopRangeExpression, iteratorResolutionResults.getResultingCalls())); } else { context.trace.report(ITERATOR_MISSING.on(loopRangeExpression)); } } return null; }
public KotlinTypeInfo visitIfExpression( KtIfExpression ifExpression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); KtExpression condition = ifExpression.getCondition(); DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context); KtExpression elseBranch = ifExpression.getElse(); KtExpression thenBranch = ifExpression.getThen(); LexicalWritableScope thenScope = newWritableScopeImpl(context, "Then scope"); LexicalWritableScope elseScope = newWritableScopeImpl(context, "Else scope"); DataFlowInfo thenInfo = components .dataFlowAnalyzer .extractDataFlowInfoFromCondition(condition, true, context) .and(conditionDataFlowInfo); DataFlowInfo elseInfo = components .dataFlowAnalyzer .extractDataFlowInfoFromCondition(condition, false, context) .and(conditionDataFlowInfo); if (elseBranch == null) { if (thenBranch != null) { KotlinTypeInfo result = getTypeInfoWhenOnlyOneBranchIsPresent( thenBranch, thenScope, thenInfo, elseInfo, contextWithExpectedType, ifExpression, isStatement); // If jump was possible, take condition check info as the jump info return result.getJumpOutPossible() ? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo) : result; } return TypeInfoFactoryKt.createTypeInfo( components.dataFlowAnalyzer.checkImplicitCast( components.builtIns.getUnitType(), ifExpression, contextWithExpectedType, isStatement), thenInfo.or(elseInfo)); } if (thenBranch == null) { return getTypeInfoWhenOnlyOneBranchIsPresent( elseBranch, elseScope, elseInfo, thenInfo, contextWithExpectedType, ifExpression, isStatement); } KtPsiFactory psiFactory = KtPsiFactoryKt.KtPsiFactory(ifExpression); KtBlockExpression thenBlock = psiFactory.wrapInABlockWrapper(thenBranch); KtBlockExpression elseBlock = psiFactory.wrapInABlockWrapper(elseBranch); Call callForIf = createCallForSpecialConstruction( ifExpression, ifExpression, Lists.newArrayList(thenBlock, elseBlock)); MutableDataFlowInfoForArguments dataFlowInfoForArguments = createDataFlowInfoForArgumentsForIfCall(callForIf, thenInfo, elseInfo); ResolvedCall<FunctionDescriptor> resolvedCall = components.controlStructureTypingUtils.resolveSpecialConstructionAsCall( callForIf, ResolveConstruct.IF, Lists.newArrayList("thenBranch", "elseBranch"), Lists.newArrayList(false, false), contextWithExpectedType, dataFlowInfoForArguments); BindingContext bindingContext = context.trace.getBindingContext(); KotlinTypeInfo thenTypeInfo = BindingContextUtils.getRecordedTypeInfo(thenBranch, bindingContext); KotlinTypeInfo elseTypeInfo = BindingContextUtils.getRecordedTypeInfo(elseBranch, bindingContext); assert thenTypeInfo != null : "'Then' branch of if expression was not processed: " + ifExpression; assert elseTypeInfo != null : "'Else' branch of if expression was not processed: " + ifExpression; boolean loopBreakContinuePossible = thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible(); KotlinType thenType = thenTypeInfo.getType(); KotlinType elseType = elseTypeInfo.getType(); DataFlowInfo thenDataFlowInfo = thenTypeInfo.getDataFlowInfo(); DataFlowInfo elseDataFlowInfo = elseTypeInfo.getDataFlowInfo(); boolean jumpInThen = thenType != null && KotlinBuiltIns.isNothing(thenType); boolean jumpInElse = elseType != null && KotlinBuiltIns.isNothing(elseType); DataFlowInfo resultDataFlowInfo; if (thenType == null && elseType == null) { resultDataFlowInfo = thenDataFlowInfo.or(elseDataFlowInfo); } else if (thenType == null || (jumpInThen && !jumpInElse)) { resultDataFlowInfo = elseDataFlowInfo; } else if (elseType == null || (jumpInElse && !jumpInThen)) { resultDataFlowInfo = thenDataFlowInfo; } else { resultDataFlowInfo = thenDataFlowInfo.or(elseDataFlowInfo); } KotlinType resultType = resolvedCall.getResultingDescriptor().getReturnType(); // If break or continue was possible, take condition check info as the jump info return TypeInfoFactoryKt.createTypeInfo( components.dataFlowAnalyzer.checkImplicitCast( resultType, ifExpression, contextWithExpectedType, isStatement), resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo); }
@NotNull @Override protected List<IntentionAction> doCreateActions(@NotNull Diagnostic diagnostic) { List<IntentionAction> actions = new LinkedList<IntentionAction>(); BindingContext context = ResolutionUtils.analyzeFully((JetFile) diagnostic.getPsiFile()); PsiElement diagnosticElement = diagnostic.getPsiElement(); if (!(diagnosticElement instanceof JetExpression)) { LOG.error("Unexpected element: " + diagnosticElement.getText()); return Collections.emptyList(); } JetExpression expression = (JetExpression) diagnosticElement; JetType expectedType; JetType expressionType; if (diagnostic.getFactory() == Errors.TYPE_MISMATCH) { DiagnosticWithParameters2<JetExpression, JetType, JetType> diagnosticWithParameters = Errors.TYPE_MISMATCH.cast(diagnostic); expectedType = diagnosticWithParameters.getA(); expressionType = diagnosticWithParameters.getB(); } else if (diagnostic.getFactory() == Errors.NULL_FOR_NONNULL_TYPE) { DiagnosticWithParameters1<JetConstantExpression, JetType> diagnosticWithParameters = Errors.NULL_FOR_NONNULL_TYPE.cast(diagnostic); expectedType = diagnosticWithParameters.getA(); expressionType = TypeUtilsKt.makeNullable(expectedType); } else if (diagnostic.getFactory() == Errors.CONSTANT_EXPECTED_TYPE_MISMATCH) { DiagnosticWithParameters2<JetConstantExpression, String, JetType> diagnosticWithParameters = Errors.CONSTANT_EXPECTED_TYPE_MISMATCH.cast(diagnostic); expectedType = diagnosticWithParameters.getB(); expressionType = context.getType(expression); if (expressionType == null) { LOG.error("No type inferred: " + expression.getText()); return Collections.emptyList(); } } else { LOG.error("Unexpected diagnostic: " + DefaultErrorMessages.render(diagnostic)); return Collections.emptyList(); } // We don't want to cast a cast or type-asserted expression: if (!(expression instanceof JetBinaryExpressionWithTypeRHS) && !(expression.getParent() instanceof JetBinaryExpressionWithTypeRHS)) { actions.add(new CastExpressionFix(expression, expectedType)); } // Property initializer type mismatch property type: JetProperty property = PsiTreeUtil.getParentOfType(expression, JetProperty.class); if (property != null) { JetPropertyAccessor getter = property.getGetter(); JetExpression initializer = property.getInitializer(); if (QuickFixUtil.canEvaluateTo(initializer, expression) || (getter != null && QuickFixUtil.canFunctionOrGetterReturnExpression( property.getGetter(), expression))) { LexicalScope scope = CorePackage.getResolutionScope( property, context, ResolutionUtils.getResolutionFacade(property)); JetType typeToInsert = TypeUtils.approximateWithResolvableType(expressionType, scope, false); actions.add(new ChangeVariableTypeFix(property, typeToInsert)); } } PsiElement expressionParent = expression.getParent(); // Mismatch in returned expression: JetCallableDeclaration function = expressionParent instanceof JetReturnExpression ? BindingContextUtilPackage.getTargetFunction( (JetReturnExpression) expressionParent, context) : PsiTreeUtil.getParentOfType(expression, JetFunction.class, true); if (function instanceof JetFunction && QuickFixUtil.canFunctionOrGetterReturnExpression(function, expression)) { LexicalScope scope = CorePackage.getResolutionScope( function, context, ResolutionUtils.getResolutionFacade(function)); JetType typeToInsert = TypeUtils.approximateWithResolvableType(expressionType, scope, false); actions.add(new ChangeFunctionReturnTypeFix((JetFunction) function, typeToInsert)); } // Fixing overloaded operators: if (expression instanceof JetOperationExpression) { ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(expression, context); if (resolvedCall != null) { JetFunction declaration = getFunctionDeclaration(resolvedCall); if (declaration != null) { actions.add(new ChangeFunctionReturnTypeFix(declaration, expectedType)); } } } // Change function return type when TYPE_MISMATCH is reported on call expression: if (expression instanceof JetCallExpression) { ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(expression, context); if (resolvedCall != null) { JetFunction declaration = getFunctionDeclaration(resolvedCall); if (declaration != null) { actions.add(new ChangeFunctionReturnTypeFix(declaration, expectedType)); } } } ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilPackage.getParentResolvedCall(expression, context, true); if (resolvedCall != null) { // to fix 'type mismatch' on 'if' branches // todo: the same with 'when' JetExpression parentIf = QuickFixUtil.getParentIfForBranch(expression); JetExpression argumentExpression = (parentIf != null) ? parentIf : expression; ValueArgument valueArgument = CallUtilPackage.getValueArgumentForExpression(resolvedCall.getCall(), argumentExpression); if (valueArgument != null) { JetParameter correspondingParameter = QuickFixUtil.getParameterDeclarationForValueArgument(resolvedCall, valueArgument); JetType valueArgumentType = diagnostic.getFactory() == Errors.NULL_FOR_NONNULL_TYPE ? expressionType : context.getType(valueArgument.getArgumentExpression()); if (correspondingParameter != null && valueArgumentType != null) { JetCallableDeclaration callable = PsiTreeUtil.getParentOfType( correspondingParameter, JetCallableDeclaration.class, true); LexicalScope scope = callable != null ? CorePackage.getResolutionScope( callable, context, ResolutionUtils.getResolutionFacade(callable)) : null; JetType typeToInsert = TypeUtils.approximateWithResolvableType(valueArgumentType, scope, true); actions.add(new ChangeParameterTypeFix(correspondingParameter, typeToInsert)); } } } return actions; }