Support platform/impl modifiers for functions

Also add a new capability for ModuleDescriptor, which is used to obtain the
platform in the multi-platform scenario in tests.

Suppress the following errors for platform functions: "function has no body"
and "nothing to inline". Also do not report redeclaration between platform and
non-platform functions because this is the case when the common +
platform-specific code are analyzed together.

Note that some diagnostics reported in tests are not yet implemented in this
commit, they appear in subsequent commits
This commit is contained in:
Alexander Udalov
2016-10-28 11:20:29 +03:00
parent 6e2ef9b1d2
commit af1264a46d
32 changed files with 463 additions and 23 deletions
@@ -489,6 +489,11 @@ public interface Errors {
DiagnosticFactory0<KtParameter> DATA_CLASS_VARARG_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> DATA_CLASS_NOT_PROPERTY_PARAMETER = DiagnosticFactory0.create(ERROR);
// Multi-platform projects
DiagnosticFactory0<KtDeclaration> PLATFORM_DECLARATION_WITH_BODY = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtParameter> PLATFORM_DECLARATION_WITH_DEFAULT_PARAMETER = DiagnosticFactory0.create(ERROR);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Errors/warnings inside code blocks
@@ -257,6 +257,9 @@ public class DefaultErrorMessages {
MAP.put(USELESS_VARARG_ON_PARAMETER, "Vararg on this parameter is useless");
MAP.put(MULTIPLE_VARARG_PARAMETERS, "Multiple vararg-parameters are prohibited");
MAP.put(PLATFORM_DECLARATION_WITH_BODY, "Platform declaration must not have a body");
MAP.put(PLATFORM_DECLARATION_WITH_DEFAULT_PARAMETER, "Platform declaration cannot have parameters with default values");
MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties");
MAP.put(SUPERTYPE_NOT_INITIALIZED, "This type has a constructor, and thus must be initialized here");
MAP.put(NOTHING_TO_OVERRIDE, "''{0}'' overrides nothing", NAME);
@@ -732,10 +732,26 @@ class DeclarationsChecker(
}
}
else /* top-level only */ {
if (!function.hasBody() && !hasAbstractModifier && !hasExternalModifier) {
if (!function.hasBody() && !hasAbstractModifier && !hasExternalModifier && !functionDescriptor.isPlatform) {
trace.report(NON_MEMBER_FUNCTION_NO_BODY.on(function, functionDescriptor))
}
}
if (functionDescriptor.isPlatform) {
checkPlatformFunction(function)
}
}
private fun checkPlatformFunction(function: KtNamedFunction) {
if (function.hasBody()) {
trace.report(PLATFORM_DECLARATION_WITH_BODY.on(function))
}
for (parameter in function.valueParameters) {
if (parameter.hasDefaultValue()) {
trace.report(PLATFORM_DECLARATION_WITH_DEFAULT_PARAMETER.on(parameter))
}
}
}
private fun checkImplicitCallableType(declaration: KtCallableDeclaration, descriptor: CallableDescriptor) {
@@ -184,6 +184,10 @@ class FunctionDescriptorResolver(
functionDescriptor.isInline = function.hasModifier(KtTokens.INLINE_KEYWORD)
functionDescriptor.isTailrec = function.hasModifier(KtTokens.TAILREC_KEYWORD)
functionDescriptor.isSuspend = function.hasModifier(KtTokens.SUSPEND_KEYWORD)
functionDescriptor.isPlatform = function.hasModifier(KtTokens.PLATFORM_KEYWORD) ||
containingDescriptor is ClassDescriptor && containingDescriptor.isPlatform
functionDescriptor.isImpl = function.hasModifier(KtTokens.IMPL_KEYWORD)
receiverType?.let { ForceResolveUtil.forceResolveAllContents(it.annotations) }
for (valueParameterDescriptor in valueParameterDescriptors) {
ForceResolveUtil.forceResolveAllContents(valueParameterDescriptor.type.annotations)
@@ -293,6 +297,12 @@ class FunctionDescriptorResolver(
isPrimary,
declarationToTrace.toSourceElement()
)
if (classDescriptor.isPlatform) {
constructorDescriptor.isPlatform = true
}
if (classDescriptor.isImpl) {
constructorDescriptor.isImpl = true
}
trace.record(BindingContext.CONSTRUCTOR, declarationToTrace, constructorDescriptor)
val parameterScope = LexicalWritableScope(
scope,
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2016 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
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.module
sealed class MultiTargetPlatform {
object Common : MultiTargetPlatform()
data class Specific(val platform: String) : MultiTargetPlatform()
companion object {
@JvmField
val CAPABILITY = ModuleDescriptor.Capability<MultiTargetPlatform>("MULTI_TARGET_PLATFORM")
}
}
fun ModuleDescriptor.getMultiTargetPlatform(): MultiTargetPlatform? =
module.getCapability(MultiTargetPlatform.CAPABILITY)
fun MemberDescriptor.getMultiTargetPlatform(): String? =
(module.getMultiTargetPlatform() as? MultiTargetPlatform.Specific)?.platform
@@ -252,6 +252,8 @@ class OverloadResolver(
if (member1 == member2) continue
if (isConstructorsOfDifferentRedeclaredClasses(member1, member2)) continue
if (isTopLevelMainInDifferentFiles(member1, member2)) continue
if (isDefinitionsForDifferentPlatforms(member1, member2)) continue
if (isPlatformDeclarationAndDefinition(member1, member2) || isPlatformDeclarationAndDefinition(member2, member1)) continue
if (!overloadChecker.isOverloadable(member1, member2)) {
redeclarations.add(member1)
@@ -281,6 +283,18 @@ class OverloadResolver(
return file1 == null || file2 == null || file1 !== file2
}
private fun isPlatformDeclarationAndDefinition(declaration: DeclarationDescriptor, definition: DeclarationDescriptor): Boolean {
return declaration is MemberDescriptor && declaration.isPlatform &&
definition is MemberDescriptor && !definition.isPlatform
}
private fun isDefinitionsForDifferentPlatforms(member1: DeclarationDescriptorNonRoot, member2: DeclarationDescriptorNonRoot): Boolean {
if (member1 !is MemberDescriptor || member2 !is MemberDescriptor) return false
return member1.isImpl && member2.isImpl &&
member1.getMultiTargetPlatform() != member2.getMultiTargetPlatform()
}
private fun reportRedeclarations(redeclarations: Collection<DeclarationDescriptorNonRoot>) {
if (redeclarations.isEmpty()) return
@@ -134,25 +134,17 @@ object InlineAnalyzerExtension : AnalyzerExtensions.AnalyzerExtension {
containingDeclaration is ClassDescriptor && containingDeclaration.modality == Modality.FINAL
}
private fun checkHasInlinableAndNullability(
functionDescriptor: FunctionDescriptor,
function: KtFunction,
trace: BindingTrace) {
var hasInlinable = false
val parameters = functionDescriptor.valueParameters
var index = 0
for (parameter in parameters) {
hasInlinable = hasInlinable or checkInlinableParameter(parameter, function.valueParameters[index++], functionDescriptor, trace)
private fun checkHasInlinableAndNullability(functionDescriptor: FunctionDescriptor, function: KtFunction, trace: BindingTrace) {
for ((parameter, descriptor) in function.valueParameters.zip(functionDescriptor.valueParameters)) {
if (checkInlinableParameter(descriptor, parameter, functionDescriptor, trace)) return
}
hasInlinable = hasInlinable or InlineUtil.containsReifiedTypeParameters(functionDescriptor)
if (InlineUtil.containsReifiedTypeParameters(functionDescriptor) ||
functionDescriptor.isInlineOnlyOrReified() ||
functionDescriptor.isPlatform) return
if (!hasInlinable && !functionDescriptor.isInlineOnlyOrReified()) {
val modifierList = function.modifierList
val inlineModifier = modifierList?.getModifier(KtTokens.INLINE_KEYWORD)
val reportOn = inlineModifier ?: function
trace.report(Errors.NOTHING_TO_INLINE.on(reportOn, functionDescriptor))
}
val reportOn = function.modifierList?.getModifier(KtTokens.INLINE_KEYWORD) ?: function
trace.report(Errors.NOTHING_TO_INLINE.on(reportOn, functionDescriptor))
}
fun checkInlinableParameter(