Report diagnostics about experimentality on unsigned literals
#KT-25580 Fixed
This commit is contained in:
@@ -255,6 +255,9 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory1<KtAnnotationEntry, String> ANNOTATION_TARGETS_NON_EXISTENT_ACCESSOR = DiagnosticFactory1.create(WARNING);
|
||||
|
||||
DiagnosticFactory1<PsiElement, FqName> EXPERIMENTAL_UNSIGNED_LITERALS = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, FqName> EXPERIMENTAL_UNSIGNED_LITERALS_ERROR = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
// Const
|
||||
DiagnosticFactory0<PsiElement> CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> CONST_VAL_WITH_GETTER = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
+3
@@ -156,6 +156,9 @@ public class DefaultErrorMessages {
|
||||
|
||||
MAP.put(ANNOTATION_TARGETS_NON_EXISTENT_ACCESSOR, "An accessor will not be generated for ''{0}'' so the annotation effectively has a ''SOURCE'' retention", STRING);
|
||||
|
||||
MAP.put(EXPERIMENTAL_UNSIGNED_LITERALS, "Unsigned literals are experimental and their usages should be marked with ''@{0}'' or ''@UseExperimental({0}::class)''", TO_STRING);
|
||||
MAP.put(EXPERIMENTAL_UNSIGNED_LITERALS_ERROR, "Unsigned literals are experimental and their usages must be marked with ''@{0}'' or ''@UseExperimental({0}::class)''", TO_STRING);
|
||||
|
||||
MAP.put(REDUNDANT_MODIFIER, "Modifier ''{0}'' is redundant because ''{1}'' is present", TO_STRING, TO_STRING);
|
||||
MAP.put(REDUNDANT_OPEN_IN_INTERFACE, "Modifier 'open' is redundant for abstract interface members");
|
||||
MAP.put(REDUNDANT_MODIFIER_IN_GETTER, "Visibility modifiers are redundant in getter");
|
||||
|
||||
+30
-7
@@ -21,7 +21,9 @@ import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.AnalysisFlag
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -45,7 +47,7 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
private val moduleAnnotationsResolver = ModuleAnnotationsResolver.getInstance(project)
|
||||
|
||||
internal data class Experimentality(val annotationFqName: FqName, val severity: Severity) {
|
||||
data class Experimentality(val annotationFqName: FqName, val severity: Severity) {
|
||||
enum class Severity { WARNING, ERROR }
|
||||
|
||||
companion object {
|
||||
@@ -53,6 +55,11 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
}
|
||||
}
|
||||
|
||||
data class ExperimentalityDiagnostics(
|
||||
val warning: DiagnosticFactory1<PsiElement, FqName>,
|
||||
val error: DiagnosticFactory1<PsiElement, FqName>
|
||||
)
|
||||
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
val experimentalities =
|
||||
resolvedCall.resultingDescriptor.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings)
|
||||
@@ -73,21 +80,37 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
private val EXPERIMENTAL_SHORT_NAME = EXPERIMENTAL_FQ_NAME.shortName()
|
||||
private val USE_EXPERIMENTAL_SHORT_NAME = USE_EXPERIMENTAL_FQ_NAME.shortName()
|
||||
|
||||
private fun reportNotAcceptedExperimentalities(
|
||||
private val EXPERIMENTAL_API_DIAGNOSTICS = ExperimentalityDiagnostics(
|
||||
Errors.EXPERIMENTAL_API_USAGE, Errors.EXPERIMENTAL_API_USAGE_ERROR
|
||||
)
|
||||
|
||||
fun reportNotAcceptedExperimentalities(
|
||||
experimentalities: Collection<Experimentality>, element: PsiElement, context: CheckerContext
|
||||
) {
|
||||
reportNotAcceptedExperimentalities(
|
||||
experimentalities, element, context.languageVersionSettings, context.trace, EXPERIMENTAL_API_DIAGNOSTICS
|
||||
)
|
||||
}
|
||||
|
||||
fun reportNotAcceptedExperimentalities(
|
||||
experimentalities: Collection<Experimentality>,
|
||||
element: PsiElement,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
trace: BindingTrace,
|
||||
diagnostics: ExperimentalityDiagnostics
|
||||
) {
|
||||
for ((annotationFqName, severity) in experimentalities) {
|
||||
if (!element.isExperimentalityAccepted(annotationFqName, context)) {
|
||||
if (!element.isExperimentalityAccepted(annotationFqName, languageVersionSettings, trace.bindingContext)) {
|
||||
val diagnostic = when (severity) {
|
||||
Experimentality.Severity.WARNING -> Errors.EXPERIMENTAL_API_USAGE
|
||||
Experimentality.Severity.ERROR -> Errors.EXPERIMENTAL_API_USAGE_ERROR
|
||||
Experimentality.Severity.WARNING -> diagnostics.warning
|
||||
Experimentality.Severity.ERROR -> diagnostics.error
|
||||
}
|
||||
context.trace.report(diagnostic.on(element, annotationFqName))
|
||||
trace.reportDiagnosticOnce(diagnostic.on(element, annotationFqName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.loadExperimentalities(
|
||||
fun DeclarationDescriptor.loadExperimentalities(
|
||||
moduleAnnotationsResolver: ModuleAnnotationsResolver,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): Set<Experimentality> {
|
||||
|
||||
+39
-1
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.constants.evaluate
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.TypeConversionUtil
|
||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -45,8 +47,11 @@ import java.util.*
|
||||
|
||||
class ConstantExpressionEvaluator(
|
||||
internal val module: ModuleDescriptor,
|
||||
internal val languageVersionSettings: LanguageVersionSettings
|
||||
internal val languageVersionSettings: LanguageVersionSettings,
|
||||
project: Project
|
||||
) {
|
||||
private val moduleAnnotationsResolver = ModuleAnnotationsResolver.getInstance(project)
|
||||
|
||||
fun updateNumberType(
|
||||
numberType: KotlinType,
|
||||
expression: KtExpression?,
|
||||
@@ -268,6 +273,8 @@ class ConstantExpressionEvaluator(
|
||||
val visitor = ConstantExpressionEvaluatorVisitor(this, trace)
|
||||
val constant = visitor.evaluate(expression, expectedType) ?: return null
|
||||
|
||||
checkExperimentalityOfConstantLiteral(expression, constant, expectedType, trace)
|
||||
|
||||
return if (!constant.isError) constant else null
|
||||
}
|
||||
|
||||
@@ -279,8 +286,39 @@ class ConstantExpressionEvaluator(
|
||||
return evaluateExpression(expression, trace, expectedType)?.toConstantValue(expectedType)
|
||||
}
|
||||
|
||||
private fun checkExperimentalityOfConstantLiteral(
|
||||
expression: KtExpression,
|
||||
constant: CompileTimeConstant<*>,
|
||||
expectedType: KotlinType?,
|
||||
trace: BindingTrace
|
||||
) {
|
||||
if (constant.isError) return
|
||||
if (!constant.parameters.isUnsignedNumberLiteral && !constant.parameters.isUnsignedLongNumberLiteral) return
|
||||
|
||||
val constantType = when {
|
||||
constant is TypedCompileTimeConstant<*> -> constant.type
|
||||
expectedType != null -> constant.toConstantValue(expectedType).getType(module)
|
||||
else -> return
|
||||
}
|
||||
|
||||
if (!UnsignedTypes.isUnsignedType(constantType)) return
|
||||
|
||||
|
||||
with(ExperimentalUsageChecker) {
|
||||
val descriptor = constantType.constructor.declarationDescriptor ?: return
|
||||
val experimentalities = descriptor.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings)
|
||||
|
||||
reportNotAcceptedExperimentalities(
|
||||
experimentalities, expression, languageVersionSettings, trace, EXPERIMENTAL_UNSIGNED_LITERALS_DIAGNOSTICS
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val EXPERIMENTAL_UNSIGNED_LITERALS_DIAGNOSTICS = ExperimentalUsageChecker.ExperimentalityDiagnostics(
|
||||
Errors.EXPERIMENTAL_UNSIGNED_LITERALS, Errors.EXPERIMENTAL_UNSIGNED_LITERALS_ERROR
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun getConstant(expression: KtExpression, bindingContext: BindingContext): CompileTimeConstant<*>? {
|
||||
val constant = getPossiblyErrorConstant(expression, bindingContext) ?: return null
|
||||
|
||||
Reference in New Issue
Block a user