Prohibit using kotlin.Result as a return type in most cases

#KT-26659 In Progress
This commit is contained in:
Mikhail Zarechenskiy
2018-09-10 05:57:22 +03:00
parent 48add96e79
commit b1cd49dd7b
8 changed files with 299 additions and 1 deletions
@@ -334,6 +334,10 @@ public interface Errors {
DiagnosticFactory1<PsiElement, String> RESERVED_MEMBER_INSIDE_INLINE_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS = DiagnosticFactory0.create(ERROR);
// Result class
DiagnosticFactory0<PsiElement> RESULT_CLASS_IN_RETURN_TYPE = DiagnosticFactory0.create(ERROR);
// Secondary constructors
DiagnosticFactory0<KtConstructorDelegationReferenceExpression> CYCLIC_CONSTRUCTOR_DELEGATION_CALL = DiagnosticFactory0.create(ERROR);
@@ -665,6 +665,8 @@ public class DefaultErrorMessages {
MAP.put(RESERVED_MEMBER_INSIDE_INLINE_CLASS, "Member with the name ''{0}'' is reserved for future releases", STRING);
MAP.put(SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS, "Secondary constructors with bodies are reserved for for future releases");
MAP.put(RESULT_CLASS_IN_RETURN_TYPE, "'kotlin.Result' cannot be used as a return type");
MAP.put(VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED, "Variance annotations are only allowed for type parameters of classes and interfaces");
MAP.put(BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED, "Bounds are not allowed on type alias parameters");
@@ -120,7 +120,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
InlineClassDeclarationChecker,
PropertiesWithBackingFieldsInsideInlineClass(),
AnnotationClassTargetAndRetentionChecker(),
ReservedMembersAndConstructsForInlineClass()
ReservedMembersAndConstructsForInlineClass(),
ResultClassInReturnTypeChecker()
)
private val DEFAULT_CALL_CHECKERS = listOf(
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.types.KotlinType
class ResultClassInReturnTypeChecker : DeclarationChecker {
companion object {
private const val RESULT_NAME = "Result"
}
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (declaration !is KtCallableDeclaration || descriptor !is CallableMemberDescriptor) return
val returnType = descriptor.returnType ?: return
if (isForbiddenReturnType(returnType, descriptor)) {
val typeReferenceOrDeclarationName = declaration.typeReference ?: declaration.nameIdentifier ?: return
context.trace.reportDiagnosticOnce(Errors.RESULT_CLASS_IN_RETURN_TYPE.on(typeReferenceOrDeclarationName))
}
}
private fun isForbiddenReturnType(returnType: KotlinType, declarationDescriptor: DeclarationDescriptor): Boolean {
val descriptor = returnType.constructor.declarationDescriptor ?: return false
if (!descriptor.isResultClass()) return false
if (declarationDescriptor is PropertyDescriptor || declarationDescriptor is PropertyGetterDescriptor) {
val visibility = (declarationDescriptor as DeclarationDescriptorWithVisibility).effectiveVisibility()
return when (visibility) {
is EffectiveVisibility.Private, is EffectiveVisibility.Local,
is EffectiveVisibility.InternalOrPackage, is EffectiveVisibility.InternalProtected,
is EffectiveVisibility.InternalProtectedBound -> false
is EffectiveVisibility.Public, is EffectiveVisibility.Protected,
is EffectiveVisibility.ProtectedBound -> true
}
}
return true
}
private fun DeclarationDescriptor.isResultClass(): Boolean {
val container = containingDeclaration ?: return false
return container is PackageFragmentDescriptor &&
container.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME &&
name.asString() == RESULT_NAME
}
}