org.jetbrains.kotlin.resolve.inline.InlineAnalyzerExtension.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.inline
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.coroutines.hasSuspendFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
class InlineAnalyzerExtension(
private val reasonableInlineRules: Iterable,
private val languageVersionSettings: LanguageVersionSettings
) : AnalyzerExtensions.AnalyzerExtension {
override fun process(descriptor: CallableMemberDescriptor, functionOrProperty: KtCallableDeclaration, trace: BindingTrace) {
checkModalityAndOverrides(descriptor, functionOrProperty, trace)
notSupportedInInlineCheck(functionOrProperty, trace)
if (descriptor is FunctionDescriptor) {
assert(functionOrProperty is KtNamedFunction) {
"Function descriptor $descriptor should have corresponded KtNamedFunction, but has $functionOrProperty"
}
checkDefaults(descriptor, functionOrProperty as KtNamedFunction, trace)
checkHasInlinableAndNullability(descriptor, functionOrProperty, trace)
} else {
assert(descriptor is PropertyDescriptor) {
"PropertyDescriptor expected, but was $descriptor"
}
assert(functionOrProperty is KtProperty) {
"Property descriptor $descriptor should have corresponded KtProperty, but has $functionOrProperty"
}
val hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor as PropertyDescriptor) == true
if (hasBackingField || (functionOrProperty as KtProperty).delegateExpression != null) {
trace.report(Errors.INLINE_PROPERTY_WITH_BACKING_FIELD.on(functionOrProperty))
}
}
}
private fun notSupportedInInlineCheck(
functionOrProperty: KtCallableDeclaration,
trace: BindingTrace
) {
val visitor = object : KtVisitorVoid() {
override fun visitKtElement(element: KtElement) {
super.visitKtElement(element)
element.acceptChildren(this)
}
override fun visitClass(klass: KtClass) {
trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(klass, "Local classes"))
}
override fun visitNamedFunction(function: KtNamedFunction) {
if (function.parent.parent is KtObjectDeclaration) {
super.visitNamedFunction(function)
} else {
trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(function, "Local functions"))
}
}
}
functionOrProperty.acceptChildren(visitor)
}
private fun checkDefaults(
functionDescriptor: FunctionDescriptor,
function: KtFunction,
trace: BindingTrace
) {
val ktParameters = function.valueParameters
for (parameter in functionDescriptor.valueParameters) {
if (parameter.hasDefaultValue()) {
val ktParameter = ktParameters[parameter.index]
//Always report unsupported error on functional parameter with inherited default (there are some problems with inlining)
val inheritsDefaultValue = !parameter.declaresDefaultValue() && parameter.declaresOrInheritsDefaultValue()
if (checkInlinableParameter(parameter, ktParameter, functionDescriptor, null) || inheritsDefaultValue) {
if (inheritsDefaultValue || !languageVersionSettings.supportsFeature(LanguageFeature.InlineDefaultFunctionalParameters)) {
trace.report(
Errors.NOT_YET_SUPPORTED_IN_INLINE.on(
ktParameter,
"Functional parameters with inherited default values"
)
)
} else {
checkDefaultValue(trace, parameter, ktParameter)
}
}
// Report unsupported error on inline/crossinline suspend lambdas with default values.
if (functionDescriptor.isSuspend &&
InlineUtil.isInlineParameterExceptNullability(parameter) &&
parameter.hasSuspendFunctionType
) {
trace.report(
Errors.NOT_YET_SUPPORTED_IN_INLINE.on(
ktParameter,
"Suspend functional parameters with default values"
)
)
}
}
}
}
private fun checkDefaultValue(trace: BindingTrace, parameterDescriptor: ValueParameterDescriptor, ktParameter: KtParameter) {
ktParameter.defaultValue?.let { defaultValue ->
if (!InlineUtil.isInlinableParameterExpression(KtPsiUtil.deparenthesize(defaultValue))) {
trace.report(Errors.INVALID_DEFAULT_FUNCTIONAL_PARAMETER_FOR_INLINE.on(defaultValue, defaultValue, parameterDescriptor))
}
}
}
private fun checkModalityAndOverrides(
callableDescriptor: CallableMemberDescriptor,
functionOrProperty: KtCallableDeclaration,
trace: BindingTrace
) {
if (callableDescriptor.containingDeclaration is PackageFragmentDescriptor) {
return
}
if (DescriptorVisibilities.isPrivate(callableDescriptor.visibility)) {
return
}
val overridesAnything = callableDescriptor.overriddenDescriptors.isNotEmpty()
if (overridesAnything) {
val ktTypeParameters = functionOrProperty.typeParameters
for (typeParameter in callableDescriptor.typeParameters) {
if (typeParameter.isReified) {
val ktTypeParameter = ktTypeParameters[typeParameter.index]
val reportOn = ktTypeParameter.modifierList?.getModifier(KtTokens.REIFIED_KEYWORD) ?: ktTypeParameter
trace.report(Errors.REIFIED_TYPE_PARAMETER_IN_OVERRIDE.on(reportOn))
}
}
}
if (callableDescriptor.isEffectivelyFinal(ignoreEnumClassFinality = true)) {
if (overridesAnything) {
trace.report(Errors.OVERRIDE_BY_INLINE.on(functionOrProperty))
}
if (!callableDescriptor.isEffectivelyFinal(ignoreEnumClassFinality = false)) {
trace.report(Errors.DECLARATION_CANT_BE_INLINED_WARNING.on(functionOrProperty))
}
return
}
trace.report(Errors.DECLARATION_CANT_BE_INLINED.on(functionOrProperty))
}
private fun checkHasInlinableAndNullability(functionDescriptor: FunctionDescriptor, function: KtFunction, trace: BindingTrace) {
var hasInlineArgs = false
function.valueParameters.zip(functionDescriptor.valueParameters).forEach { (parameter, descriptor) ->
hasInlineArgs = hasInlineArgs or checkInlinableParameter(descriptor, parameter, functionDescriptor, trace)
}
if (hasInlineArgs) return
if (functionDescriptor.isInlineWithReified() || functionDescriptor.isInlineOnly() || functionDescriptor.isExpect ||
functionDescriptor.isSuspend
) return
if (reasonableInlineRules.any { it.isInlineReasonable(functionDescriptor, function, trace.bindingContext) }) return
if (functionDescriptor.returnType?.needsMfvcFlattening() == true) return
val reportOn = function.modifierList?.getModifier(KtTokens.INLINE_KEYWORD) ?: function
trace.report(Errors.NOTHING_TO_INLINE.on(reportOn))
}
private fun checkInlinableParameter(
parameter: ParameterDescriptor,
expression: KtElement,
functionDescriptor: CallableDescriptor,
trace: BindingTrace?
): Boolean {
if (InlineUtil.isInlineParameterExceptNullability(parameter)) {
if (parameter.type.isMarkedNullable) {
trace?.report(Errors.NULLABLE_INLINE_PARAMETER.on(expression, expression, functionDescriptor))
} else {
return true
}
}
return false
}
}