[Assign plugin] Add a compiler plugin for overloading assign ('=') operator

This commit is contained in:
Anže Sodja
2022-09-01 07:51:38 +02:00
committed by Dmitriy Novozhilov
parent 6052962f50
commit 09d6dfc8bf
62 changed files with 2822 additions and 6 deletions
+2 -1
View File
@@ -84,7 +84,8 @@
<option value="$PROJECT_DIR$/plugins/lombok/testData" />
<option value="$PROJECT_DIR$/plugins/sam-with-receiver/testData" />
<option value="$PROJECT_DIR$/plugins/kotlinx-serialization/testData" />
<option value="$PROJECT_DIR$/plugins/assign-plugin/testData" />
</array>
</option>
</component>
</project>
</project>
@@ -27,7 +27,10 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.*
import com.intellij.openapi.vfs.PersistentFSConstants
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileSystem
import com.intellij.openapi.vfs.impl.ZipHandler
import com.intellij.psi.PsiElementFinder
import com.intellij.psi.PsiManager
@@ -44,7 +47,9 @@ import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.cli.common.*
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
import org.jetbrains.kotlin.cli.common.config.ContentRoot
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots
@@ -54,6 +59,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.toBooleanLenient
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem
import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.cli.jvm.index.*
+1
View File
@@ -66,6 +66,7 @@ dependencies {
testApi(projectTests(":kotlin-noarg-compiler-plugin"))
testApi(projectTests(":kotlin-lombok-compiler-plugin"))
testApi(projectTests(":kotlin-sam-with-receiver-compiler-plugin"))
testApi(projectTests(":kotlin-assignment-compiler-plugin"))
testApi(projectTests(":kotlinx-serialization-compiler-plugin"))
testApi(projectTests(":kotlinx-atomicfu-compiler-plugin"))
testApi(projectTests(":plugins:fir-plugin-prototype"))
@@ -16,6 +16,10 @@ import org.jetbrains.kotlin.android.synthetic.test.AbstractAndroidBoxTest
import org.jetbrains.kotlin.android.synthetic.test.AbstractAndroidBytecodeShapeTest
import org.jetbrains.kotlin.android.synthetic.test.AbstractAndroidIrBoxTest
import org.jetbrains.kotlin.android.synthetic.test.AbstractAndroidSyntheticPropertyDescriptorTest
import org.jetbrains.kotlin.assignment.plugin.AbstractFirBlackBoxCodegenTestForAssignmentPlugin
import org.jetbrains.kotlin.assignment.plugin.AbstractFirAssignmentPluginDiagnosticTest
import org.jetbrains.kotlin.assignment.plugin.AbstractIrBlackBoxCodegenTestAssignmentPlugin
import org.jetbrains.kotlin.assignment.plugin.AbstractAssignmentPluginDiagnosticTest
import org.jetbrains.kotlin.fir.plugin.runners.AbstractFirPluginBlackBoxCodegenTest
import org.jetbrains.kotlin.fir.plugin.runners.AbstractFirPluginDiagnosticTest
import org.jetbrains.kotlin.generators.TestGroup
@@ -361,5 +365,19 @@ fun main(args: Array<String>) {
}
}
testGroup("plugins/assign-plugin/tests-gen", "plugins/assign-plugin/testData") {
testClass<AbstractAssignmentPluginDiagnosticTest> {
model("diagnostics", excludedPattern = excludedFirTestdataPattern)
}
testClass<AbstractFirAssignmentPluginDiagnosticTest> {
model("diagnostics", excludedPattern = excludedFirTestdataPattern)
}
testClass<AbstractIrBlackBoxCodegenTestAssignmentPlugin> {
model("codegen", excludedPattern = excludedFirTestdataPattern)
}
testClass<AbstractFirBlackBoxCodegenTestForAssignmentPlugin> {
model("codegen", excludedPattern = excludedFirTestdataPattern)
}
}
}
}
+3 -3
View File
@@ -223,7 +223,7 @@ if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
inheritOutputDirs = true
}
}
if (this != rootProject) {
evaluationDependsOn(path)
}
@@ -418,7 +418,7 @@ fun NamedDomainObjectContainer<TopLevelArtifact>.kotlinc() {
directory("license") {
directoryContent("$rootDir/license")
}
file("$rootDir/bootstrap/build.txt")
}
}
@@ -520,7 +520,7 @@ fun RecursiveArtifact.sourceJarsFromConfiguration(configuration: Configuration,
.resolvedArtifacts
jarsFromExternalModules(resolvedArtifacts, renamer)
resolvedArtifacts
.map { it.id.componentIdentifier }
.filterIsInstance<ProjectComponentIdentifier>()
@@ -12,6 +12,7 @@
package foo
@JsName("Object")
external interface WeirdInterface {
val constructor: dynamic
@@ -0,0 +1,27 @@
description = "Kotlin Assignment Compiler Plugin (CLI)"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
api(project(":kotlin-assignment-compiler-plugin.common"))
api(project(":kotlin-assignment-compiler-plugin.k1"))
api(project(":kotlin-assignment-compiler-plugin.k2"))
compileOnly(project(":compiler:util"))
compileOnly(project(":compiler:plugin-api"))
compileOnly(project(":compiler:fir:entrypoint"))
compileOnly(intellijCore())
}
optInToExperimentalCompilerApi()
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
runtimeJar()
sourcesJar()
javadocJar()
@@ -0,0 +1 @@
org.jetbrains.kotlin.assignment.plugin.AssignmentCommandLineProcessor
@@ -0,0 +1 @@
org.jetbrains.kotlin.assignment.plugin.AssignmentComponentRegistrar
@@ -0,0 +1,69 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin
import org.jetbrains.kotlin.compiler.plugin.*
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.assignment.plugin.AssignmentConfigurationKeys.ANNOTATION
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ANNOTATION_OPTION_NAME
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.PLUGIN_ID
import org.jetbrains.kotlin.assignment.plugin.diagnostics.AssignmentPluginDeclarationChecker
import org.jetbrains.kotlin.assignment.plugin.k2.FirAssignmentPluginExtensionRegistrar
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.extensions.internal.InternalNonStableExtensionPoints
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.resolve.extensions.AssignResolutionAltererExtension
object AssignmentConfigurationKeys {
val ANNOTATION: CompilerConfigurationKey<List<String>> = CompilerConfigurationKey.create("annotation qualified name")
}
class AssignmentCommandLineProcessor : CommandLineProcessor {
companion object {
val ANNOTATION_OPTION = CliOption(
ANNOTATION_OPTION_NAME, "<fqname>", "Annotation qualified names",
required = false, allowMultipleOccurrences = true
)
}
override val pluginId = PLUGIN_ID
override val pluginOptions = listOf(ANNOTATION_OPTION)
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) = when (option) {
ANNOTATION_OPTION -> configuration.appendList(ANNOTATION, value)
else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
}
}
class AssignmentComponentRegistrar : CompilerPluginRegistrar() {
@OptIn(InternalNonStableExtensionPoints::class)
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
val annotations = configuration.getList(ANNOTATION)
if (annotations.isNotEmpty()) {
AssignResolutionAltererExtension.Companion.registerExtension(CliAssignPluginResolutionAltererExtension(annotations))
StorageComponentContainerContributor.registerExtension(AssignmentComponentContainerContributor(annotations))
FirExtensionRegistrarAdapter.registerExtension(FirAssignmentPluginExtensionRegistrar(annotations))
}
}
override val supportsK2: Boolean
get() = true
}
class AssignmentComponentContainerContributor(private val annotations: List<String>) : StorageComponentContainerContributor {
override fun registerModuleComponents(
container: StorageComponentContainer,
platform: TargetPlatform,
moduleDescriptor: ModuleDescriptor,
) {
container.useInstance(AssignmentPluginDeclarationChecker(annotations))
}
}
@@ -0,0 +1,20 @@
description = "Kotlin Assignment Compiler Plugin (Common)"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":compiler:util"))
compileOnly(project(":core:compiler.common"))
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
runtimeJar()
javadocJar()
sourcesJar()
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin
import org.jetbrains.kotlin.name.Name
object AssignmentPluginNames {
const val PLUGIN_ID = "org.jetbrains.kotlin.assignment"
const val ANNOTATION_OPTION_NAME = "annotation"
val ASSIGN_METHOD = Name.identifier("assign")
}
@@ -0,0 +1,23 @@
description = "Kotlin Assignment Compiler Plugin (K1)"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
implementation(project(":kotlin-assignment-compiler-plugin.common"))
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:frontend.java"))
compileOnly(intellijCore())
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
runtimeJar()
sourcesJar()
javadocJar()
@@ -0,0 +1,101 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin
import org.jetbrains.kotlin.cfg.getElementParentDeclaration
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.assignment.plugin.diagnostics.ErrorsAssignmentPlugin.CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.assignment.plugin.diagnostics.ErrorsAssignmentPlugin.NO_APPLICABLE_ASSIGN_METHOD
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.extensions.internal.InternalNonStableExtensionPoints
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil
import org.jetbrains.kotlin.resolve.calls.util.CallMaker.makeCallWithExpressions
import org.jetbrains.kotlin.resolve.extensions.AssignResolutionAltererExtension
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver.Companion.create
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.ExpressionTypingComponents
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import java.util.*
class CliAssignPluginResolutionAltererExtension(
private val annotations: List<String>
) : AbstractAssignPluginResolutionAltererExtension() {
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> = annotations
}
@OptIn(InternalNonStableExtensionPoints::class)
abstract class AbstractAssignPluginResolutionAltererExtension : AssignResolutionAltererExtension {
override fun needOverloadAssign(expression: KtBinaryExpression, leftType: KotlinType?, bindingContext: BindingContext): Boolean {
return expression.isValPropertyAssignment(bindingContext) && leftType.hasSpecialAnnotation(expression)
}
private fun KtBinaryExpression.isValPropertyAssignment(bindingContext: BindingContext): Boolean {
val descriptor: VariableDescriptor? = BindingContextUtils.extractVariableFromResolvedCall(bindingContext, this.left)
return descriptor is PropertyDescriptor && !descriptor.isVar
}
private fun KotlinType?.hasSpecialAnnotation(expression: KtBinaryExpression): Boolean =
this?.constructor?.declarationDescriptor?.hasSpecialAnnotation(expression.getElementParentDeclaration()) ?: false
override fun resolveAssign(
bindingContext: BindingContext,
expression: KtBinaryExpression,
leftOperand: KtExpression,
left: KtExpression,
leftInfo: KotlinTypeInfo,
context: ExpressionTypingContext,
components: ExpressionTypingComponents,
scope: LexicalWritableScope
): KotlinTypeInfo? {
var leftType: KotlinType = leftInfo.type!!
if (leftOperand is KtSafeQualifiedExpression) {
leftType = leftType.makeNotNullable()
}
val receiver = create(left, leftType, context.trace.bindingContext)
val operationSign: KtSimpleNameExpression = expression.operationReference
val temporaryForAssignmentOperation: TemporaryTraceAndCache =
TemporaryTraceAndCache.create(context, "trace to check assignment operation like '=' for", expression)
val temporaryContext = context.replaceTraceAndCache(temporaryForAssignmentOperation).replaceScope(scope)
val methodDescriptors: OverloadResolutionResults<FunctionDescriptor> =
components.callResolver.resolveMethodCall(temporaryContext, receiver, expression)
val methodReturnType: KotlinType? = OverloadResolutionResultsUtil.getResultingType(methodDescriptors, context)
if (methodDescriptors.isSuccess && methodReturnType != null) {
temporaryForAssignmentOperation.commit()
return if (methodReturnType.isUnit()) {
leftInfo.replaceType(methodReturnType)
} else {
context.trace.report(CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT.on(operationSign))
null
}
}
context.trace.report(NO_APPLICABLE_ASSIGN_METHOD.on(operationSign))
return null
}
private fun CallResolver.resolveMethodCall(
context: ExpressionTypingContext, receiver: ExpressionReceiver, binaryExpression: KtBinaryExpression
): OverloadResolutionResults<FunctionDescriptor> {
val call = makeCallWithExpressions(
binaryExpression, receiver, null, binaryExpression.operationReference, Collections.singletonList(binaryExpression.right)
)
return resolveCallWithGivenName(context, call, binaryExpression.operationReference, ASSIGN_METHOD)
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.diagnostics
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.assignment.plugin.diagnostics.ErrorsAssignmentPlugin.DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.extensions.AnnotationBasedExtension
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
class AssignmentPluginDeclarationChecker(private val annotations: List<String>) : DeclarationChecker {
private val annotationMatchingService = AnnotationMatchingService(annotations)
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor is SimpleFunctionDescriptor) {
if (!descriptor.isAssignMethod()) return
val receiverClass = if (descriptor.isExtension) {
descriptor.extensionReceiverParameter?.type?.constructor?.declarationDescriptor as? ClassDescriptor
} else {
descriptor.containingDeclaration as? ClassDescriptor
}
val ktFunction = declaration as? KtFunction
if (receiverClass != null && ktFunction != null) {
checkAssignMethod(descriptor, receiverClass, ktFunction, context.trace)
}
}
}
private fun checkAssignMethod(
method: SimpleFunctionDescriptor,
receiverClass: ClassDescriptor,
declaration: KtFunction,
diagnosticHolder: DiagnosticSink
) {
if (!annotationMatchingService.isAnnotated(receiverClass)) {
return
}
if (method.returnType?.let { KotlinBuiltIns.isUnit(it) } != true) {
diagnosticHolder.report(DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT.on(declaration))
}
}
private fun SimpleFunctionDescriptor.isAssignMethod(): Boolean {
return valueParameters.size == 1 && name == ASSIGN_METHOD
}
private class AnnotationMatchingService(val annotations: List<String>) : AnnotationBasedExtension {
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> = annotations
fun isAnnotated(descriptor: ClassDescriptor): Boolean = descriptor.hasSpecialAnnotation(null)
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.diagnostics
import org.jetbrains.kotlin.assignment.plugin.diagnostics.ErrorsAssignmentPlugin.*
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
object DefaultErrorMessagesAssignmentPlugin : DefaultErrorMessages.Extension {
private val MAP = DiagnosticFactoryToRendererMap("ValueContainerAssignment")
override fun getMap() = MAP
init {
MAP.put(
DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT,
"Function 'assign' used for '=' overload should return 'Unit'"
)
MAP.put(
CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT,
"Function 'assign' used for '=' overload should return 'Unit'"
)
MAP.put(
NO_APPLICABLE_ASSIGN_METHOD,
"No applicable 'assign' function found for '=' overload"
)
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.diagnostics;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.diagnostics.PositioningStrategies;
import org.jetbrains.kotlin.diagnostics.Severity;
import org.jetbrains.kotlin.psi.KtDeclaration;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.DECLARATION_RETURN_TYPE;
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
public interface ErrorsAssignmentPlugin {
DiagnosticFactory0<KtDeclaration> DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT =
DiagnosticFactory0.create(Severity.ERROR, DECLARATION_RETURN_TYPE);
DiagnosticFactory0<KtSimpleNameExpression> CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT =
DiagnosticFactory0.create(ERROR, PositioningStrategies.CALL_EXPRESSION);
DiagnosticFactory0<KtSimpleNameExpression> NO_APPLICABLE_ASSIGN_METHOD =
DiagnosticFactory0.create(ERROR, PositioningStrategies.CALL_EXPRESSION);
@SuppressWarnings("unused")
Object _initializer = new Object() {
{
Errors.Initializer.initializeFactoryNamesAndDefaultErrorMessages(
ErrorsAssignmentPlugin.class,
DefaultErrorMessagesAssignmentPlugin.INSTANCE
);
}
};
}
@@ -0,0 +1,29 @@
description = "Kotlin Assignment Compiler Plugin (K2)"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
implementation(project(":kotlin-assignment-compiler-plugin.common"))
compileOnly(project(":compiler:fir:cones"))
compileOnly(project(":compiler:fir:tree"))
compileOnly(project(":compiler:fir:resolve"))
compileOnly(project(":compiler:fir:checkers"))
compileOnly(project(":compiler:ir.backend.common"))
compileOnly(project(":compiler:fir:entrypoint"))
compileOnly(intellijCore())
runtimeOnly(kotlinStdlib())
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
runtimeJar()
sourcesJar()
javadocJar()
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.k2
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.caches.FirCache
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.expressions.classId
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent.Factory
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.name.ClassId
internal class FirAssignAnnotationMatchingService(
session: FirSession,
private val annotationClassIds: List<ClassId>
) : FirExtensionSessionComponent(session) {
companion object {
fun getFactory(annotations: List<String>): Factory {
return Factory { session -> FirAssignAnnotationMatchingService(session, annotations.map { ClassId.fromString(it) }) }
}
}
private val cache: FirCache<FirRegularClassSymbol, Boolean, Nothing?> = session.firCachesFactory.createCache { symbol, _ ->
symbol.annotated()
}
fun isAnnotated(symbol: FirRegularClassSymbol?): Boolean {
if (symbol == null) {
return false
}
return cache.getValue(symbol)
}
private fun FirRegularClassSymbol.annotated(): Boolean {
if (this.annotations.any { it.classId in annotationClassIds }) return true
return resolvedSuperTypeRefs.any {
val symbol = it.type.fullyExpandedType(session).toRegularClassSymbol(session) ?: return@any false
symbol.annotations.any { it.classId in annotationClassIds }
}
}
}
internal val FirSession.annotationMatchingService: FirAssignAnnotationMatchingService by FirSession.sessionComponentAccessor()
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.k2
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildFunctionCall
import org.jetbrains.kotlin.fir.expressions.builder.buildPropertyAccessExpression
import org.jetbrains.kotlin.fir.extensions.FirAssignExpressionAltererExtension
import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference
import org.jetbrains.kotlin.fir.resolvedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFieldSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.fir.types.upperBoundIfFlexible
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class FirAssignmentPluginAssignAltererExtension(
session: FirSession
) : FirAssignExpressionAltererExtension(session) {
override fun transformVariableAssignment(variableAssignment: FirVariableAssignment): FirStatement? {
return runIf(variableAssignment.supportsTransformVariableAssignment()) {
buildFunctionCall(variableAssignment)
}
}
private fun FirVariableAssignment.supportsTransformVariableAssignment(): Boolean {
return when (val lSymbol = lValue.resolvedSymbol as? FirVariableSymbol<*>) {
is FirPropertySymbol -> lSymbol.isVal && !lSymbol.isLocal && lSymbol.hasSpecialAnnotation()
is FirBackingFieldSymbol -> lSymbol.isVal && lSymbol.hasSpecialAnnotation()
is FirFieldSymbol -> lSymbol.isVal && lSymbol.hasSpecialAnnotation()
else -> false
}
}
private fun FirVariableSymbol<*>.hasSpecialAnnotation(): Boolean =
session.annotationMatchingService.isAnnotated(resolvedReturnType.upperBoundIfFlexible().toRegularClassSymbol(session))
private fun buildFunctionCall(variableAssignment: FirVariableAssignment): FirFunctionCall {
val leftArgument = variableAssignment.lValue
val leftSymbol = leftArgument.resolvedSymbol as FirVariableSymbol<*>
val leftResolvedType = leftSymbol.resolvedReturnTypeRef
val rightArgument = variableAssignment.rValue
return buildFunctionCall {
source = variableAssignment.source?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
explicitReceiver = buildPropertyAccessExpression {
source = leftArgument.source
typeRef = leftResolvedType
calleeReference = variableAssignment.calleeReference
typeArguments += variableAssignment.typeArguments
annotations += variableAssignment.annotations
explicitReceiver = variableAssignment.explicitReceiver
dispatchReceiver = variableAssignment.dispatchReceiver
extensionReceiver = variableAssignment.extensionReceiver
contextReceiverArguments += variableAssignment.contextReceiverArguments
}
argumentList = buildUnaryArgumentList(rightArgument)
calleeReference = buildSimpleNamedReference {
source = variableAssignment.source
name = ASSIGN_METHOD
candidateSymbol = null
}
origin = FirFunctionCallOrigin.Regular
}
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.k2
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirAssignmentPluginFunctionCallChecker
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirAssignmentPluginFunctionChecker
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirSimpleFunctionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
class FirAssignmentPluginCheckersExtension(
session: FirSession
) : FirAdditionalCheckersExtension(session) {
override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers() {
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
get() = setOf(FirAssignmentPluginFunctionChecker)
}
override val expressionCheckers: ExpressionCheckers = object : ExpressionCheckers() {
override val functionCallCheckers: Set<FirFunctionCallChecker>
get() = setOf(FirAssignmentPluginFunctionCallChecker)
}
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.k2
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
class FirAssignmentPluginExtensionRegistrar(private val annotations: List<String>) : FirExtensionRegistrar() {
override fun ExtensionRegistrarContext.configurePlugin() {
+::FirAssignmentPluginAssignAltererExtension
+::FirAssignmentPluginCheckersExtension
+FirAssignAnnotationMatchingService.getFactory(annotations)
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.k2.diagnostics
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.assignment.plugin.k2.annotationMatchingService
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirErrorsAssignmentPlugin.CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirErrorsAssignmentPlugin.NO_APPLICABLE_ASSIGN_METHOD
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.arguments
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnosticWithSingleCandidate
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.types.isUnit
object FirAssignmentPluginFunctionCallChecker : FirFunctionCallChecker() {
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
if (!expression.isOverloadAssignCallCandidate()) return
if (expression.isFunctionResolveError()) {
if (expression.isOverloadedAssignCallError(context.session)) {
reporter.reportOn(expression.source, NO_APPLICABLE_ASSIGN_METHOD, context)
}
} else if (expression.isOverloadedAssignCall(context.session) && !expression.isReturnTypeUnit()) {
reporter.reportOn(expression.source, CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT, context)
}
}
private fun FirFunctionCall.isOverloadAssignCallCandidate() =
arguments.size == 1 && source?.kind == KtFakeSourceElementKind.DesugaredCompoundAssignment
private fun FirFunctionCall.isFunctionResolveError() = calleeReference is FirErrorNamedReference
private fun FirFunctionCall.isOverloadedAssignCallError(session: FirSession): Boolean {
val functionName = when (val diagnostic = (calleeReference as? FirErrorNamedReference)?.diagnostic) {
is ConeAmbiguityError -> diagnostic.name
is ConeDiagnosticWithSingleCandidate -> diagnostic.candidate.callInfo.name
is ConeUnresolvedNameError -> diagnostic.name
else -> calleeReference.name
}
return functionName == ASSIGN_METHOD && isAnnotated(session)
}
private fun FirFunctionCall.isOverloadedAssignCall(session: FirSession) =
calleeReference.name == ASSIGN_METHOD && isAnnotated(session)
private fun FirFunctionCall.isAnnotated(session: FirSession): Boolean =
session.annotationMatchingService.isAnnotated(explicitReceiver?.typeRef?.toRegularClassSymbol(session))
private fun FirFunctionCall.isReturnTypeUnit() = toResolvedCallableSymbol()?.resolvedReturnType?.isUnit ?: false
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.k2.diagnostics
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.assignment.plugin.k2.annotationMatchingService
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirErrorsAssignmentPlugin.DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirSimpleFunctionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.symbols.impl.isExtension
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isUnit
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
object FirAssignmentPluginFunctionChecker : FirSimpleFunctionChecker() {
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.origin != FirDeclarationOrigin.Source) return
if (!declaration.isAssignMethod()) return
val receiverClassSymbol = if (declaration.symbol.isExtension) {
declaration.symbol.resolvedReceiverTypeRef?.toRegularClassSymbol(context.session)
} else {
declaration.dispatchReceiverType?.toRegularClassSymbol(context.session)
}
if (!context.session.annotationMatchingService.isAnnotated(receiverClassSymbol)) return
if (!declaration.returnTypeRef.coneType.isUnit) {
reporter.reportOn(declaration.source, DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT, context)
}
}
private fun FirSimpleFunction.isAssignMethod(): Boolean {
return valueParameters.size == 1 && this.name == ASSIGN_METHOD
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.k2.diagnostics
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirErrorsAssignmentPlugin.CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirErrorsAssignmentPlugin.DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirErrorsAssignmentPlugin.NO_APPLICABLE_ASSIGN_METHOD
import org.jetbrains.kotlin.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderer
object FirDefaultErrorMessagesAssignmentPlugin {
fun getRendererForDiagnostic(diagnostic: KtDiagnostic): KtDiagnosticRenderer {
val factory = diagnostic.factory
return MAP[factory] ?: factory.ktRenderer
}
val MAP = KtDiagnosticFactoryToRendererMap("ValueContainerAssignment").also { map ->
map.put(
DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT,
"Function 'assign' used for '=' overload should return 'Unit'"
)
map.put(
CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT,
"Function 'assign' used for '=' overload should return 'Unit'"
)
map.put(
NO_APPLICABLE_ASSIGN_METHOD,
"No applicable 'assign' function found for '=' overload"
)
}
}
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin.k2.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies.OPERATOR
import org.jetbrains.kotlin.diagnostics.error0
object FirErrorsAssignmentPlugin {
val DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT by error0<PsiElement>(DECLARATION_RETURN_TYPE)
val CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT by error0<PsiElement>(OPERATOR)
val NO_APPLICABLE_ASSIGN_METHOD by error0<PsiElement>(OPERATOR)
}
+54
View File
@@ -0,0 +1,54 @@
description = "Kotlin Assignment Compiler Plugin"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
embedded(project(":kotlin-assignment-compiler-plugin.common"))
embedded(project(":kotlin-assignment-compiler-plugin.k1"))
embedded(project(":kotlin-assignment-compiler-plugin.k2"))
embedded(project(":kotlin-assignment-compiler-plugin.cli"))
testApi(project(":compiler:backend"))
testApi(project(":compiler:cli"))
testApi(project(":kotlin-assignment-compiler-plugin.cli"))
testCompileOnly(project(":kotlin-compiler"))
testImplementation(project(":kotlin-scripting-jvm-host-unshaded"))
testApi(projectTests(":compiler:tests-common-new"))
testImplementation(projectTests(":compiler:tests-common"))
testImplementation(commonDependency("junit:junit"))
testCompileOnly(project(":kotlin-reflect-api"))
testRuntimeOnly(project(":kotlin-reflect"))
testRuntimeOnly(project(":core:descriptors.runtime"))
testRuntimeOnly(project(":compiler:fir:fir-serialization"))
testApi(intellijCore())
}
optInToExperimentalCompilerApi()
sourceSets {
"main" { none() }
"test" {
projectDefault()
generatedTestDir()
}
}
publish()
runtimeJar()
sourcesJar()
javadocJar()
testsJar()
projectTest(parallel = true) {
dependsOn(":dist")
workingDir = rootDir
useJUnitPlatform()
}
@@ -0,0 +1,87 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.assignment.plugin.k2.FirAssignmentPluginExtensionRegistrar
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.extensions.internal.InternalNonStableExtensionPoints
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
import org.jetbrains.kotlin.resolve.extensions.AssignResolutionAltererExtension
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.RENDER_DIAGNOSTICS_FULL_TEXT
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.runners.AbstractDiagnosticTest
import org.jetbrains.kotlin.test.runners.AbstractFirDiagnosticTest
import org.jetbrains.kotlin.test.runners.codegen.AbstractFirBlackBoxCodegenTest
import org.jetbrains.kotlin.test.runners.codegen.AbstractIrBlackBoxCodegenTest
import org.jetbrains.kotlin.test.runners.configurationForClassicAndFirTestsAlongside
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
import org.jetbrains.kotlin.test.services.TestServices
// ------------------------ diagnostics ------------------------
abstract class AbstractAssignmentPluginDiagnosticTest : AbstractDiagnosticTest() {
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
builder.configurePlugin()
builder.configureDiagnostics()
}
}
abstract class AbstractFirAssignmentPluginDiagnosticTest : AbstractFirDiagnosticTest() {
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
builder.configurePlugin()
builder.configureDiagnostics()
builder.configurationForClassicAndFirTestsAlongside()
}
}
// ------------------------ codegen ------------------------
open class AbstractIrBlackBoxCodegenTestAssignmentPlugin : AbstractIrBlackBoxCodegenTest() {
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
builder.configurePlugin()
}
}
open class AbstractFirBlackBoxCodegenTestForAssignmentPlugin : AbstractFirBlackBoxCodegenTest() {
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
builder.configurePlugin()
}
}
// ------------------------ configuration ------------------------
fun TestConfigurationBuilder.configurePlugin() {
useConfigurators(::AssignmentPluginEnvironmentConfigurator)
}
fun TestConfigurationBuilder.configureDiagnostics() {
defaultDirectives {
+RENDER_DIAGNOSTICS_FULL_TEXT
}
}
class AssignmentPluginEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
companion object {
private val TEST_ANNOTATIONS = listOf("ValueContainer")
}
@OptIn(InternalNonStableExtensionPoints::class)
override fun CompilerPluginRegistrar.ExtensionStorage.registerCompilerExtensions(
module: TestModule,
configuration: CompilerConfiguration
) {
AssignResolutionAltererExtension.Companion.registerExtension(CliAssignPluginResolutionAltererExtension(TEST_ANNOTATIONS))
StorageComponentContainerContributor.registerExtension(AssignmentComponentContainerContributor(TEST_ANNOTATIONS))
FirExtensionRegistrarAdapter.registerExtension(FirAssignmentPluginExtensionRegistrar(TEST_ANNOTATIONS))
}
}
+120
View File
@@ -0,0 +1,120 @@
// FILE: JavaProperty.java
@ValueContainer
public interface JavaProperty<T> {
void assign(T argument);
T get();
}
// FILE: JavaClassStringProperty.java
@ValueContainer
public class JavaClassStringProperty {
private String v;
public JavaClassStringProperty(String v) {
this.v = v;
}
public void assign(String v) {
this.v = v;
}
public String get() {
return v;
}
}
// FILE: test.kt
annotation class ValueContainer
data class JavaStringProperty(private var v: String): JavaProperty<String> {
override fun assign(v: String) {
this.v = v
}
override fun get() = this.v
}
@ValueContainer
interface KotlinProperty<T> {
fun assign(argument: T);
fun get(): T;
}
data class KotlinStringProperty(private var v: String): KotlinProperty<String> {
override fun assign(v: String) {
this.v = v
}
override fun get() = this.v
}
@ValueContainer
data class KotlinClassStringProperty(private var v: String) {
fun assign(v: String) {
this.v = v
}
fun get(): String {
return v
}
}
fun `should work with annotation on Java interface`(): String {
data class Task(val input: JavaStringProperty)
val task = Task(JavaStringProperty("Fail"))
task.input = "OK"
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with annotation on Java class`(): String {
data class Task(val input: JavaClassStringProperty)
val task = Task(JavaClassStringProperty("Fail"))
task.input = "OK"
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with annotation on Kotlin interface`(): String {
data class Task(val input: KotlinStringProperty)
val task = Task(KotlinStringProperty("Fail"))
task.input = "OK"
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with annotation on Kotlin class`(): String {
data class Task(val input: KotlinClassStringProperty)
val task = Task(KotlinClassStringProperty("Fail"))
task.input = "OK"
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun box(): String {
var result = `should work with annotation on Java interface`()
if (result != "OK") return result
result = `should work with annotation on Java class`()
if (result != "OK") return result
result = `should work with annotation on Kotlin interface`()
if (result != "OK") return result
result = `should work with annotation on Kotlin class`()
if (result != "OK") return result
return "OK"
}
+105
View File
@@ -0,0 +1,105 @@
annotation class ValueContainer
@ValueContainer
class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get(): String {
return v
}
}
data class Task(val input: StringProperty)
var result = "Fail"
operator fun StringProperty.plusAssign(v: String) {
result = v
}
operator fun StringProperty.plusAssign(v: StringProperty) {
result = v.get()
}
operator fun StringProperty.set(i: Int, v: String) {
result = v
}
operator fun StringProperty.set(i: Int, v: StringProperty) {
result = v.get()
}
operator fun StringProperty.set(i: Int, j: Int, v: String) {
result = v
}
operator fun StringProperty.set(i: Int, j: Int, v: StringProperty) {
result = v.get()
}
operator fun StringProperty.set(i: Int, j: Int, k: Int, v: String) {
result = v
}
operator fun StringProperty.set(i: Int, j: Int, k: Int, v: StringProperty) {
result = v.get()
}
operator fun StringProperty.compareTo(v: String): Int {
result = v
return 0
}
operator fun StringProperty.compareTo(v: StringProperty): Int {
result = v.get()
return 0
}
fun box(): String {
val task = Task(StringProperty("Fail"))
// Double check that assign is correctly setup
task.input = "OK"
if (task.input.get() != "OK") return task.input.get()
task?.input = "OK"
if (task.input.get() != "OK") return task.input.get()
result = "Fail"
task.input += "OK"
if (result != "OK") return result
result = "Fail"
task.input += StringProperty("OK")
if (result != "OK") return result
result = "Fail"
task.input >= "OK"
if (result != "OK") return result
result = "Fail"
task.input >= StringProperty("OK")
if (result != "OK") return result
result = "Fail"
task.input <= "OK"
if (result != "OK") return result
result = "Fail"
task.input <= StringProperty("OK")
if (result != "OK") return result
result = "Fail"
task.input[0] = "OK"
if (result != "OK") return result
result = "Fail"
task.input[0] = StringProperty("OK")
if (result != "OK") return result
result = "Fail"
task.input[0, 0] = "OK"
if (result != "OK") return result
result = "Fail"
task.input[0, 0] = StringProperty("OK")
if (result != "OK") return result
result = "Fail"
task.input[0, 0, 0] = "OK"
if (result != "OK") return result
result = "Fail"
task.input[0, 0, 0] = StringProperty("OK")
if (result != "OK") return result
return result
}
@@ -0,0 +1,108 @@
annotation class ValueContainer
abstract class AbstractStringProperty(protected var v: String) {
fun get(): String {
return v
}
}
@ValueContainer
class StringProperty(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
}
@ValueContainer
class StringPropertyWithPlus(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(o: StringPropertyWithPlus) {
this.v = o.get()
}
operator fun plus(v: String) =
StringPropertyWithPlus(this.v + v)
}
@ValueContainer
class StringPropertyWithPlusAssign(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(o: StringPropertyWithPlusAssign) {
this.v = o.get()
}
operator fun plusAssign(v: String) {
this.v += v
}
}
@ValueContainer
class StringPropertyWithPlusAndPlusAssign(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(o: StringPropertyWithPlusAndPlusAssign) {
this.v = o.get()
}
operator fun plus(v: String) =
StringPropertyWithPlusAndPlusAssign(this.v + v)
operator fun plusAssign(v: String) {
this.v += v
}
}
data class Task(
val valInput: StringProperty,
var varInput: StringProperty,
val valInputWithPlus: StringPropertyWithPlus,
var varInputWithPlus: StringPropertyWithPlus,
val valInputWithPlusAssign: StringPropertyWithPlusAssign,
var varInputWithPlusAssign: StringPropertyWithPlusAssign,
val valInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign,
var varInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign,
)
fun box(): String {
val task = Task(
StringProperty("O"),
StringProperty("O"),
StringPropertyWithPlus("O"),
StringPropertyWithPlus("O"),
StringPropertyWithPlusAssign("O"),
StringPropertyWithPlusAssign("O"),
StringPropertyWithPlusAndPlusAssign("O"),
StringPropertyWithPlusAndPlusAssign("O")
)
task.varInputWithPlus += "K"
if (task.varInputWithPlus.get() != "OK") return task.varInputWithPlus.get()
task.valInputWithPlusAssign += "K"
if (task.valInputWithPlusAssign.get() != "OK") return task.valInputWithPlusAssign.get()
task.varInputWithPlusAssign += "K"
if (task.varInputWithPlusAssign.get() != "OK") return task.varInputWithPlusAssign.get()
task.valInputWithPlusAndPlusAssign += "K"
if (task.valInputWithPlusAndPlusAssign.get() != "OK") return task.valInputWithPlusAndPlusAssign.get()
return "OK"
}
+200
View File
@@ -0,0 +1,200 @@
// FILE: Property.java
@ValueContainer
public interface Property<T> {
void set(T v);
T get();
}
// FILE: JavaTaskWithField.java
public class JavaTaskWithField {
public final StringProperty input;
public JavaTaskWithField(StringProperty input) {
this.input = input;
}
}
// FILE: JavaTaskWithProperty.java
public class JavaTaskWithProperty {
private final StringProperty input;
public JavaTaskWithProperty(StringProperty input) {
this.input = input;
}
public StringProperty getInput() {
return input;
}
}
// FILE: test.kt
annotation class ValueContainer
data class StringProperty(var v: String): Property<String> {
override fun set(v: String) {
this.v = v
}
fun assign(v: String) {
this.v = v
}
fun assign(v: Property<String>) {
this.v = v.get()
}
override fun get(): String {
return v
}
}
data class Task(val input: StringProperty)
fun `should work with assignment for raw type`(): String {
val task = Task(StringProperty("Fail"))
task.input = "OK"
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with assignment for wrapped type`(): String {
val task = Task(StringProperty("Fail"))
task.input = StringProperty("OK")
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with assignment with apply for raw type`(): String {
val task = Task(StringProperty("Fail"))
task.apply {
input = "OK"
}
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with assignment with apply for wrapped type`(): String {
val task = Task(StringProperty("Fail"))
task.apply {
input = StringProperty("OK")
}
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with extension function`(): String {
fun StringProperty.assign(v: Int) = this.assign("OK")
val task = Task(StringProperty("Fail"))
task.input = 42
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with extension function for interface type`(): String {
fun Property<String>.assign(v: Int) = this.set("OK")
val task = Task(StringProperty("Fail"))
task.input = 42
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with generic extension function`(): String {
fun <T> Property<T>.assign(v: T) = this.set(v)
data class IntProperty(var v: Int): Property<Int> {
override fun set(v: Int) {
this.v = v
}
override fun get() = this.v
}
data class IntTask(val input: IntProperty)
val task = IntTask(IntProperty(0))
task.input = 42
return if (task.input.get() != 42) {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with callable receiver`(): String {
fun StringProperty.assign(r: StringProperty.() -> Unit) = r.invoke(this)
val task = Task(StringProperty("Fail"))
task.input = { this.set("OK") }
return if (task.input.get() != "OK") {
"Fail: ${task.input.get()}"
} else {
"OK"
}
}
fun `should work with Java classes`(): String {
var taskWithField = JavaTaskWithField(StringProperty("Fail"))
taskWithField.input = "OK"
if (taskWithField.input.get() != "OK") return "Fail for Java: ${taskWithField.input.get()}"
taskWithField = JavaTaskWithField(StringProperty("Fail"))
taskWithField.input = StringProperty("OK")
if (taskWithField.input.get() != "OK") return "Fail for Java: ${taskWithField.input.get()}"
var taskWithProperty = JavaTaskWithProperty(StringProperty("Fail"))
taskWithProperty.input = "OK"
if (taskWithProperty.input.get() != "OK") return "Fail for Java: ${taskWithProperty.input.get()}"
taskWithProperty = JavaTaskWithProperty(StringProperty("Fail"))
taskWithProperty.input = StringProperty("OK")
if (taskWithProperty.input.get() != "OK") return "Fail for Java: ${taskWithProperty.input.get()}"
return "OK"
}
fun box(): String {
var result = `should work with assignment for raw type`()
if (result != "OK") return result
result = `should work with assignment for wrapped type`()
if (result != "OK") return result
result = `should work with assignment with apply for raw type`()
if (result != "OK") return result
result = `should work with assignment with apply for wrapped type`()
if (result != "OK") return result
result = `should work with extension function`()
if (result != "OK") return result
result = `should work with extension function for interface type`()
if (result != "OK") return result
result = `should work with generic extension function`()
if (result != "OK") return result
result = `should work with callable receiver`()
if (result != "OK") return result
result = `should work with Java classes`()
if (result != "OK") return result
return "OK"
}
+65
View File
@@ -0,0 +1,65 @@
annotation class ValueContainer
@ValueContainer
data class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get() = v
}
data class Task(var input: StringProperty)
fun `test local var reference and value`(): String {
var property = StringProperty("OK")
var originalProperty = property
property = StringProperty("Fail")
return when {
originalProperty.get() != "OK" -> "Fail: ${originalProperty.get()}"
originalProperty == property -> "Fail: originalProperty == property"
else -> "OK"
}
}
fun `test class property var reference and value`(): String {
val task = Task(StringProperty("OK"))
val originalProperty = task.input
task.input = StringProperty("Fail")
return when {
originalProperty.get() != "OK" -> "Fail: ${originalProperty.get()}"
originalProperty == task.input -> "Fail: originalProperty == task.input"
else -> "OK"
}
}
fun `test class property var reference and value with apply`(): String {
val task = Task(StringProperty("OK"))
val originalProperty = task.input
task.apply {
input = StringProperty("Fail")
}
return when {
originalProperty.get() != "OK" -> "Fail: ${originalProperty.get()}"
originalProperty == task.input -> "Fail: originalProperty == task.input"
else -> "OK"
}
}
fun box(): String {
var result = `test local var reference and value`()
if (result != "OK") return result
result = `test class property var reference and value`()
if (result != "OK") return result
result = `test class property var reference and value with apply`()
if (result != "OK") return result
return "OK"
}
@@ -0,0 +1,19 @@
/incorrectUsage.kt:18:5: error: val cannot be reassigned
task.input = 42
^
/incorrectUsage.kt:18:16: error: no applicable 'assign' function found for '=' overload
task.input = 42
^
/incorrectUsage.kt:18:18: error: the integer literal does not conform to the expected type StringProperty
task.input = 42
^
/incorrectUsage.kt:24:9: error: val cannot be reassigned
input = 42
^
/incorrectUsage.kt:24:15: error: no applicable 'assign' function found for '=' overload
input = 42
^
/incorrectUsage.kt:24:17: error: the integer literal does not conform to the expected type StringProperty
input = 42
^
@@ -0,0 +1,26 @@
annotation class ValueContainer
@ValueContainer
data class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get(): String = v
}
data class Task(val input: StringProperty)
fun `should report error if type doesn't match`() {
val task = Task(StringProperty("Fail"))
task.<!NONE_APPLICABLE!>input<!> <!NO_APPLICABLE_ASSIGN_METHOD!>=<!> 42
}
fun `should report error if type doesn't match with apply`() {
val task = Task(StringProperty("Fail"))
task.apply {
<!NONE_APPLICABLE!>input<!> <!NO_APPLICABLE_ASSIGN_METHOD!>=<!> 42
}
}
@@ -0,0 +1,27 @@
annotation class ValueContainer
@ValueContainer
data class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get(): String = v
}
data class Task(val input: StringProperty)
fun `should report error if type doesn't match`() {
val task = Task(StringProperty("Fail"))
<!VAL_REASSIGNMENT!>task.input<!> <!NO_APPLICABLE_ASSIGN_METHOD!>=<!> <!CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!>
}
fun `should report error if type doesn't match with apply`() {
val task = Task(StringProperty("Fail"))
task.apply {
<!VAL_REASSIGNMENT!>input<!> <!NO_APPLICABLE_ASSIGN_METHOD!>=<!> <!CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!>
}
}
@@ -0,0 +1,35 @@
package
public fun `should report error if type doesn't match`(): kotlin.Unit
public fun `should report error if type doesn't match with apply`(): kotlin.Unit
@ValueContainer public final data class StringProperty {
public constructor StringProperty(/*0*/ v: kotlin.String)
public final var v: kotlin.String
public final fun assign(/*0*/ v: StringProperty): kotlin.Unit
public final fun assign(/*0*/ v: kotlin.String): kotlin.Unit
public final operator /*synthesized*/ fun component1(): kotlin.String
public final /*synthesized*/ fun copy(/*0*/ v: kotlin.String = ...): StringProperty
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun get(): kotlin.String
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final data class Task {
public constructor Task(/*0*/ input: StringProperty)
public final val input: StringProperty
public final operator /*synthesized*/ fun component1(): StringProperty
public final /*synthesized*/ fun copy(/*0*/ input: StringProperty = ...): Task
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final annotation class ValueContainer : kotlin.Annotation {
public constructor ValueContainer()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,22 @@
/localVariables.kt:18:5: error: val cannot be reassigned
property = "Fail"
^
/localVariables.kt:18:16: error: type mismatch: inferred type is String but StringProperty was expected
property = "Fail"
^
/localVariables.kt:23:5: error: val cannot be reassigned
property = StringProperty("Fail")
^
/localVariables.kt:28:16: error: type mismatch: inferred type is String but StringProperty was expected
property = "Fail"
^
/localVariables.kt:38:9: error: val cannot be reassigned
property = "Fail"
^
/localVariables.kt:38:20: error: type mismatch: inferred type is String but StringProperty was expected
property = "Fail"
^
/localVariables.kt:42:9: error: val cannot be reassigned
property = StringProperty("Fail")
^
@@ -0,0 +1,44 @@
// !DIAGNOSTICS: -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
annotation class ValueContainer
@ValueContainer
data class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get(): String = v
}
fun `should not work with local val for different type`() {
val property = StringProperty("OK")
<!VAL_REASSIGNMENT!>property<!> = <!ASSIGNMENT_TYPE_MISMATCH!>"Fail"<!>
}
fun `should not work with local val for same type`() {
val property = StringProperty("OK")
<!VAL_REASSIGNMENT!>property<!> = StringProperty("Fail")
}
fun `should not work with local var for different type`() {
var property = StringProperty("OK")
property = <!ASSIGNMENT_TYPE_MISMATCH!>"Fail"<!>
}
fun `should work with local var for same type`() {
var property = StringProperty("OK")
property = StringProperty("Fail")
}
fun `should not work with method parameters`() {
fun m1(property: StringProperty): Unit {
<!VAL_REASSIGNMENT!>property<!> = <!ASSIGNMENT_TYPE_MISMATCH!>"Fail"<!>
}
fun m2(property: StringProperty): Unit {
<!VAL_REASSIGNMENT!>property<!> = StringProperty("Fail")
}
}
@@ -0,0 +1,44 @@
// !DIAGNOSTICS: -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
annotation class ValueContainer
@ValueContainer
data class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get(): String = v
}
fun `should not work with local val for different type`() {
val property = StringProperty("OK")
<!VAL_REASSIGNMENT!>property<!> = <!TYPE_MISMATCH!>"Fail"<!>
}
fun `should not work with local val for same type`() {
val property = StringProperty("OK")
<!VAL_REASSIGNMENT!>property<!> = StringProperty("Fail")
}
fun `should not work with local var for different type`() {
var property = StringProperty("OK")
property = <!TYPE_MISMATCH!>"Fail"<!>
}
fun `should work with local var for same type`() {
var property = StringProperty("OK")
property = StringProperty("Fail")
}
fun `should not work with method parameters`() {
fun m1(property: StringProperty): Unit {
<!VAL_REASSIGNMENT!>property<!> = <!TYPE_MISMATCH!>"Fail"<!>
}
fun m2(property: StringProperty): Unit {
<!VAL_REASSIGNMENT!>property<!> = StringProperty("Fail")
}
}
@@ -0,0 +1,27 @@
package
public fun `should not work with local val for different type`(): kotlin.Unit
public fun `should not work with local val for same type`(): kotlin.Unit
public fun `should not work with local var for different type`(): kotlin.Unit
public fun `should not work with method parameters`(): kotlin.Unit
public fun `should work with local var for same type`(): kotlin.Unit
@ValueContainer public final data class StringProperty {
public constructor StringProperty(/*0*/ v: kotlin.String)
public final var v: kotlin.String
public final fun assign(/*0*/ v: StringProperty): kotlin.Unit
public final fun assign(/*0*/ v: kotlin.String): kotlin.Unit
public final operator /*synthesized*/ fun component1(): kotlin.String
public final /*synthesized*/ fun copy(/*0*/ v: kotlin.String = ...): StringProperty
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun get(): kotlin.String
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final annotation class ValueContainer : kotlin.Annotation {
public constructor ValueContainer()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,28 @@
/methodDeclaration.kt:5:28: error: function 'assign' used for '=' overload should return 'Unit'
fun assign(v: String): String {
^
/methodDeclaration.kt:9:36: error: function 'assign' used for '=' overload should return 'Unit'
fun assign(v: StringProperty): String {
^
/methodDeclaration.kt:16:36: error: function 'assign' used for '=' overload should return 'Unit'
fun StringProperty.assign(v: Int): String {
^
/methodDeclaration.kt:25:5: error: val cannot be reassigned
task.input = "42"
^
/methodDeclaration.kt:25:16: error: function 'assign' used for '=' overload should return 'Unit'
task.input = "42"
^
/methodDeclaration.kt:26:5: error: val cannot be reassigned
task.input = 42
^
/methodDeclaration.kt:26:16: error: function 'assign' used for '=' overload should return 'Unit'
task.input = 42
^
/methodDeclaration.kt:35:5: error: val cannot be reassigned
task.input = 42
^
/methodDeclaration.kt:35:18: error: the integer literal does not conform to the expected type IntProperty
task.input = 42
^
@@ -0,0 +1,36 @@
annotation class ValueContainer
@ValueContainer
data class StringProperty(var v: String) {
fun assign(v: String): <!DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>String<!> {
this.v = v
return ""
}
fun assign(v: StringProperty): <!DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>String<!> {
this.v = v.get()
return ""
}
fun get(): String = v
}
fun StringProperty.assign(v: Int): <!DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>String<!> {
this.v = "OK"
return ""
}
data class Task(val input: StringProperty)
fun `should report an error for assign method return type on assignment for annotated class`() {
val task = Task(StringProperty("Fail"))
task.input <!CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>=<!> "42"
task.input <!CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>=<!> 42
}
fun `should not report an error for assign return type for unannotated class`() {
data class IntProperty(var v: Int)
fun IntProperty.assign(v: Int): String = "OK"
data class IntTask(val input: IntProperty)
val task = IntTask(IntProperty(42))
task.input = <!ASSIGNMENT_TYPE_MISMATCH!>42<!>
}
@@ -0,0 +1,36 @@
annotation class ValueContainer
@ValueContainer
data class StringProperty(var v: String) {
fun assign(v: String): <!DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>String<!> {
this.v = v
return ""
}
fun assign(v: StringProperty): <!DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>String<!> {
this.v = v.get()
return ""
}
fun get(): String = v
}
fun StringProperty.assign(v: Int): <!DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>String<!> {
this.v = "OK"
return ""
}
data class Task(val input: StringProperty)
fun `should report an error for assign method return type on assignment for annotated class`() {
val task = Task(StringProperty("Fail"))
<!VAL_REASSIGNMENT!>task.input<!> <!CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>=<!> "42"
<!VAL_REASSIGNMENT!>task.input<!> <!CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT!>=<!> 42
}
fun `should not report an error for assign return type for unannotated class`() {
data class IntProperty(var v: Int)
fun IntProperty.assign(v: Int): String = "OK"
data class IntTask(val input: IntProperty)
val task = IntTask(IntProperty(42))
<!VAL_REASSIGNMENT!>task.input<!> = <!CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!>
}
@@ -0,0 +1,36 @@
package
public fun `should not report an error for assign return type for unannotated class`(): kotlin.Unit
public fun `should report an error for assign method return type on assignment for annotated class`(): kotlin.Unit
public fun StringProperty.assign(/*0*/ v: kotlin.Int): kotlin.String
@ValueContainer public final data class StringProperty {
public constructor StringProperty(/*0*/ v: kotlin.String)
public final var v: kotlin.String
public final fun assign(/*0*/ v: StringProperty): kotlin.String
public final fun assign(/*0*/ v: kotlin.String): kotlin.String
public final operator /*synthesized*/ fun component1(): kotlin.String
public final /*synthesized*/ fun copy(/*0*/ v: kotlin.String = ...): StringProperty
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun get(): kotlin.String
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final data class Task {
public constructor Task(/*0*/ input: StringProperty)
public final val input: StringProperty
public final operator /*synthesized*/ fun component1(): StringProperty
public final /*synthesized*/ fun copy(/*0*/ input: StringProperty = ...): Task
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final annotation class ValueContainer : kotlin.Annotation {
public constructor ValueContainer()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,31 @@
/noAnnotation.kt:17:5: error: val cannot be reassigned
task.input = "OK"
^
/noAnnotation.kt:17:18: error: type mismatch: inferred type is String but StringProperty was expected
task.input = "OK"
^
/noAnnotation.kt:18:5: error: val cannot be reassigned
task.input = StringProperty("OK")
^
/noAnnotation.kt:20:9: error: val cannot be reassigned
input = "OK"
^
/noAnnotation.kt:20:17: error: type mismatch: inferred type is String but StringProperty was expected
input = "OK"
^
/noAnnotation.kt:23:9: error: val cannot be reassigned
input = StringProperty("OK")
^
/noAnnotation.kt:25:5: error: val cannot be reassigned
task.input = 42
^
/noAnnotation.kt:25:18: error: the integer literal does not conform to the expected type StringProperty
task.input = 42
^
/noAnnotation.kt:27:9: error: val cannot be reassigned
input = 42
^
/noAnnotation.kt:27:17: error: the integer literal does not conform to the expected type StringProperty
input = 42
^
@@ -0,0 +1,29 @@
data class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get(): String = v
}
fun StringProperty.assign(v: Int) = this.assign("OK")
data class Task(val input: StringProperty)
fun `should not work with assignment when there is no annotation on a type`() {
val task = Task(StringProperty("Fail"))
task.input = <!ASSIGNMENT_TYPE_MISMATCH!>"OK"<!>
task.input = StringProperty("OK")
task.apply {
input = <!ASSIGNMENT_TYPE_MISMATCH!>"OK"<!>
}
task.apply {
input = StringProperty("OK")
}
task.input = <!ASSIGNMENT_TYPE_MISMATCH!>42<!>
task.apply {
input = <!ASSIGNMENT_TYPE_MISMATCH!>42<!>
}
}
@@ -0,0 +1,29 @@
data class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get(): String = v
}
fun StringProperty.assign(v: Int) = this.assign("OK")
data class Task(val input: StringProperty)
fun `should not work with assignment when there is no annotation on a type`() {
val task = Task(StringProperty("Fail"))
<!VAL_REASSIGNMENT!>task.input<!> = <!TYPE_MISMATCH!>"OK"<!>
<!VAL_REASSIGNMENT!>task.input<!> = StringProperty("OK")
task.apply {
<!VAL_REASSIGNMENT!>input<!> = <!TYPE_MISMATCH!>"OK"<!>
}
task.apply {
<!VAL_REASSIGNMENT!>input<!> = StringProperty("OK")
}
<!VAL_REASSIGNMENT!>task.input<!> = <!CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!>
task.apply {
<!VAL_REASSIGNMENT!>input<!> = <!CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!>
}
}
@@ -0,0 +1,28 @@
package
public fun `should not work with assignment when there is no annotation on a type`(): kotlin.Unit
public fun StringProperty.assign(/*0*/ v: kotlin.Int): kotlin.Unit
public final data class StringProperty {
public constructor StringProperty(/*0*/ v: kotlin.String)
public final var v: kotlin.String
public final fun assign(/*0*/ v: StringProperty): kotlin.Unit
public final fun assign(/*0*/ v: kotlin.String): kotlin.Unit
public final operator /*synthesized*/ fun component1(): kotlin.String
public final /*synthesized*/ fun copy(/*0*/ v: kotlin.String = ...): StringProperty
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun get(): kotlin.String
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final data class Task {
public constructor Task(/*0*/ input: StringProperty)
public final val input: StringProperty
public final operator /*synthesized*/ fun component1(): StringProperty
public final /*synthesized*/ fun copy(/*0*/ input: StringProperty = ...): Task
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@@ -0,0 +1,89 @@
/otherOperators.kt:21:16: error: unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public operator fun String?.plus(other: Any?): String defined in kotlin
task.input += StringProperty("Fail")
^
/otherOperators.kt:22:21: error: unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public operator fun String?.plus(other: Any?): String defined in kotlin
nullTask?.input += StringProperty("Fail")
^
/otherOperators.kt:25:16: error: unresolved reference: <=
task.input <= StringProperty("Fail")
^
/otherOperators.kt:26:21: error: unresolved reference: <=
nullTask?.input <= StringProperty("Fail")
^
/otherOperators.kt:29:16: error: unresolved reference: >=
task.input >= StringProperty("Fail")
^
/otherOperators.kt:30:21: error: unresolved reference: >=
nullTask?.input >= StringProperty("Fail")
^
/otherOperators.kt:33:15: error: unresolved reference: task.input[0]
task.input[0] = StringProperty("Fail")
^
/otherOperators.kt:33:15: error: no set method providing array access
task.input[0] = StringProperty("Fail")
^
/otherOperators.kt:34:20: error: unresolved reference: nullTask?.input[0]
nullTask?.input[0] = StringProperty("Fail")
^
/otherOperators.kt:34:20: error: no set method providing array access
nullTask?.input[0] = StringProperty("Fail")
^
/otherOperators.kt:37:15: error: unresolved reference: task.input[0, 0]
task.input[0, 0] = StringProperty("Fail")
^
/otherOperators.kt:37:15: error: no set method providing array access
task.input[0, 0] = StringProperty("Fail")
^
/otherOperators.kt:38:20: error: unresolved reference: nullTask?.input[0, 0]
nullTask?.input[0, 0] = StringProperty("Fail")
^
/otherOperators.kt:38:20: error: no set method providing array access
nullTask?.input[0, 0] = StringProperty("Fail")
^
/otherOperators.kt:41:15: error: unresolved reference: task.input[0, 0, 0]
task.input[0, 0, 0] = StringProperty("Fail")
^
/otherOperators.kt:41:15: error: no set method providing array access
task.input[0, 0, 0] = StringProperty("Fail")
^
/otherOperators.kt:42:20: error: unresolved reference: nullTask?.input[0, 0, 0]
nullTask?.input[0, 0, 0] = StringProperty("Fail")
^
/otherOperators.kt:42:20: error: no set method providing array access
nullTask?.input[0, 0, 0] = StringProperty("Fail")
^
/otherOperators.kt:45:5: error: 'operator' modifier is required on 'get' in 'StringProperty'
task.input[0] += StringProperty("Fail")
^
/otherOperators.kt:45:15: error: no set method providing array access
task.input[0] += StringProperty("Fail")
^
/otherOperators.kt:45:16: error: too many arguments for public final fun get(): String defined in StringProperty
task.input[0] += StringProperty("Fail")
^
/otherOperators.kt:46:5: error: 'operator' modifier is required on 'get' in 'StringProperty'
nullTask?.input[0] += StringProperty("Fail")
^
/otherOperators.kt:46:5: error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type StringProperty?
nullTask?.input[0] += StringProperty("Fail")
^
/otherOperators.kt:46:20: error: no set method providing array access
nullTask?.input[0] += StringProperty("Fail")
^
/otherOperators.kt:46:21: error: too many arguments for public final fun get(): String defined in StringProperty
nullTask?.input[0] += StringProperty("Fail")
^
/otherOperators.kt:50:9: error: unresolved reference: task[0]
task[0] = StringProperty("Fail")
^
/otherOperators.kt:50:9: error: no set method providing array access
task[0] = StringProperty("Fail")
^
/otherOperators.kt:53:10: error: variable expected
task.get(0) = StringProperty("Fail")
^
/otherOperators.kt:54:15: error: variable expected
nullTask?.get(0) = StringProperty("Fail")
^
@@ -0,0 +1,55 @@
annotation class ValueContainer
@ValueContainer
data class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get(): String = v
}
data class Task(val input: StringProperty)
fun `should not effect error reporting for other operators`() {
val task = Task(StringProperty("Fail"))
val nullTask: Task? = null
// a.b += c
task.input <!UNRESOLVED_REFERENCE!>+=<!> StringProperty("Fail")
nullTask?.input <!UNRESOLVED_REFERENCE!>+=<!> StringProperty("Fail")
// a.b <= c
task.input <!UNRESOLVED_REFERENCE!><=<!> StringProperty("Fail")
nullTask?.input <!UNRESOLVED_REFERENCE!><=<!> StringProperty("Fail")
// a.b >= c
task.input <!UNRESOLVED_REFERENCE!>>=<!> StringProperty("Fail")
nullTask?.input <!UNRESOLVED_REFERENCE!>>=<!> StringProperty("Fail")
// a.b[c] = d
task.input<!NO_SET_METHOD!>[0]<!> = StringProperty("Fail")
nullTask?.input<!NO_SET_METHOD!>[0]<!> = StringProperty("Fail")
// a.b[c, d] = e
task.input<!NO_SET_METHOD!>[0, 0]<!> = StringProperty("Fail")
nullTask?.input<!NO_SET_METHOD!>[0, 0]<!> = StringProperty("Fail")
// a.b[c,..,d] = e
task.input<!NO_SET_METHOD!>[0, 0, 0]<!> = StringProperty("Fail")
nullTask?.input<!NO_SET_METHOD!>[0, 0, 0]<!> = StringProperty("Fail")
// a?.b[c] += d
task.input[<!TOO_MANY_ARGUMENTS!>0<!>] <!UNRESOLVED_REFERENCE!>+=<!> StringProperty("Fail")
nullTask?.input[<!TOO_MANY_ARGUMENTS!>0<!>] <!UNRESOLVED_REFERENCE!>+=<!> StringProperty("Fail")
// a[i] = b should not be translated to a.get(i).assign(b)
operator fun Task.get(i: Int) = this.input
task<!NO_SET_METHOD!>[0]<!> = StringProperty("Fail")
// a.get(i) = b should not be translated to a.get(i).assign(b)
task.<!FUNCTION_CALL_EXPECTED!>get<!>(0) = StringProperty("Fail")
nullTask?.<!FUNCTION_CALL_EXPECTED!>get<!>(0) = StringProperty("Fail")
}
@@ -0,0 +1,55 @@
annotation class ValueContainer
@ValueContainer
data class StringProperty(var v: String) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
fun get(): String = v
}
data class Task(val input: StringProperty)
fun `should not effect error reporting for other operators`() {
val task = Task(StringProperty("Fail"))
val nullTask: Task? = null
// a.b += c
task.input <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>+=<!> StringProperty("Fail")
nullTask?.input <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>+=<!> StringProperty("Fail")
// a.b <= c
task.input <!UNRESOLVED_REFERENCE!><=<!> StringProperty("Fail")
nullTask?.input <!UNRESOLVED_REFERENCE!><=<!> StringProperty("Fail")
// a.b >= c
task.input <!UNRESOLVED_REFERENCE!>>=<!> StringProperty("Fail")
nullTask?.input <!UNRESOLVED_REFERENCE!>>=<!> StringProperty("Fail")
// a.b[c] = d
task.input<!NO_SET_METHOD!><!UNRESOLVED_REFERENCE!>[<!>0<!UNRESOLVED_REFERENCE!>]<!><!> = StringProperty("Fail")
nullTask?.input<!NO_SET_METHOD!><!UNRESOLVED_REFERENCE!>[<!>0<!UNRESOLVED_REFERENCE!>]<!><!> = StringProperty("Fail")
// a.b[c, d] = e
task.input<!NO_SET_METHOD!><!UNRESOLVED_REFERENCE!>[<!>0, 0<!UNRESOLVED_REFERENCE!>]<!><!> = StringProperty("Fail")
nullTask?.input<!NO_SET_METHOD!><!UNRESOLVED_REFERENCE!>[<!>0, 0<!UNRESOLVED_REFERENCE!>]<!><!> = StringProperty("Fail")
// a.b[c,..,d] = e
task.input<!NO_SET_METHOD!><!UNRESOLVED_REFERENCE!>[<!>0, 0, 0<!UNRESOLVED_REFERENCE!>]<!><!> = StringProperty("Fail")
nullTask?.input<!NO_SET_METHOD!><!UNRESOLVED_REFERENCE!>[<!>0, 0, 0<!UNRESOLVED_REFERENCE!>]<!><!> = StringProperty("Fail")
// a?.b[c] += d
<!OPERATOR_MODIFIER_REQUIRED!>task.input<!NO_SET_METHOD!>[<!TOO_MANY_ARGUMENTS!>0<!>]<!><!> += StringProperty("Fail")
<!OPERATOR_MODIFIER_REQUIRED, UNSAFE_CALL!>nullTask?.input<!NO_SET_METHOD!>[<!TOO_MANY_ARGUMENTS!>0<!>]<!><!> += StringProperty("Fail")
// a[i] = b should not be translated to a.get(i).assign(b)
operator fun Task.get(i: Int) = this.input
task<!NO_SET_METHOD!><!UNRESOLVED_REFERENCE!>[<!>0<!UNRESOLVED_REFERENCE!>]<!><!> = StringProperty("Fail")
// a.get(i) = b should not be translated to a.get(i).assign(b)
task.<!VARIABLE_EXPECTED!>get(0)<!> = StringProperty("Fail")
nullTask?.<!VARIABLE_EXPECTED!>get(0)<!> = StringProperty("Fail")
}
@@ -0,0 +1,33 @@
package
public fun `should not effect error reporting for other operators`(): kotlin.Unit
@ValueContainer public final data class StringProperty {
public constructor StringProperty(/*0*/ v: kotlin.String)
public final var v: kotlin.String
public final fun assign(/*0*/ v: StringProperty): kotlin.Unit
public final fun assign(/*0*/ v: kotlin.String): kotlin.Unit
public final operator /*synthesized*/ fun component1(): kotlin.String
public final /*synthesized*/ fun copy(/*0*/ v: kotlin.String = ...): StringProperty
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun get(): kotlin.String
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final data class Task {
public constructor Task(/*0*/ input: StringProperty)
public final val input: StringProperty
public final operator /*synthesized*/ fun component1(): StringProperty
public final /*synthesized*/ fun copy(/*0*/ input: StringProperty = ...): Task
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final annotation class ValueContainer : kotlin.Annotation {
public constructor ValueContainer()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,17 @@
/plusAssignPrecedence.kt:96:19: error: unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public operator fun String?.plus(other: Any?): String defined in kotlin
task.valInput += "K"
^
/plusAssignPrecedence.kt:97:19: error: unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public operator fun String?.plus(other: Any?): String defined in kotlin
task.varInput += "K"
^
/plusAssignPrecedence.kt:99:5: error: val cannot be reassigned
task.valInputWithPlus += "K"
^
/plusAssignPrecedence.kt:101:40: error: assignment operators ambiguity:
public final operator fun plus(v: String): StringPropertyWithPlusAndPlusAssign defined in StringPropertyWithPlusAndPlusAssign
public final operator fun plusAssign(v: String): Unit defined in StringPropertyWithPlusAndPlusAssign
task.varInputWithPlusAndPlusAssign += "K"
^
@@ -0,0 +1,104 @@
annotation class ValueContainer
abstract class AbstractStringProperty(protected var v: String) {
fun get(): String {
return v
}
}
@ValueContainer
class StringProperty(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
}
@ValueContainer
class StringPropertyWithPlus(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(o: StringPropertyWithPlus) {
this.v = o.get()
}
operator fun plus(v: String) =
StringPropertyWithPlus(this.v + v)
}
@ValueContainer
class StringPropertyWithPlusAssign(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(o: StringPropertyWithPlusAssign) {
this.v = o.get()
}
operator fun plusAssign(v: String) {
this.v += v
}
}
@ValueContainer
class StringPropertyWithPlusAndPlusAssign(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(o: StringPropertyWithPlusAndPlusAssign) {
this.v = o.get()
}
operator fun plus(v: String) =
StringPropertyWithPlusAndPlusAssign(this.v + v)
operator fun plusAssign(v: String) {
this.v += v
}
}
data class Task(
val valInput: StringProperty,
var varInput: StringProperty,
val valInputWithPlus: StringPropertyWithPlus,
var varInputWithPlus: StringPropertyWithPlus,
val valInputWithPlusAssign: StringPropertyWithPlusAssign,
var varInputWithPlusAssign: StringPropertyWithPlusAssign,
val valInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign,
var varInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign,
)
fun box(): String {
val task = Task(
StringProperty("O"),
StringProperty("O"),
StringPropertyWithPlus("O"),
StringPropertyWithPlus("O"),
StringPropertyWithPlusAssign("O"),
StringPropertyWithPlusAssign("O"),
StringPropertyWithPlusAndPlusAssign("O"),
StringPropertyWithPlusAndPlusAssign("O")
)
task.valInput <!UNRESOLVED_REFERENCE!>+=<!> "K"
task.varInput <!UNRESOLVED_REFERENCE!>+=<!> "K"
task.<!VAL_REASSIGNMENT!>valInputWithPlus<!> += "K"
task.varInputWithPlusAndPlusAssign <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> "K"
return "OK"
}
@@ -0,0 +1,104 @@
annotation class ValueContainer
abstract class AbstractStringProperty(protected var v: String) {
fun get(): String {
return v
}
}
@ValueContainer
class StringProperty(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(v: StringProperty) {
this.v = v.get()
}
}
@ValueContainer
class StringPropertyWithPlus(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(o: StringPropertyWithPlus) {
this.v = o.get()
}
operator fun plus(v: String) =
StringPropertyWithPlus(this.v + v)
}
@ValueContainer
class StringPropertyWithPlusAssign(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(o: StringPropertyWithPlusAssign) {
this.v = o.get()
}
operator fun plusAssign(v: String) {
this.v += v
}
}
@ValueContainer
class StringPropertyWithPlusAndPlusAssign(v: String) : AbstractStringProperty(v) {
fun assign(v: String) {
this.v = v
}
fun assign(o: StringPropertyWithPlusAndPlusAssign) {
this.v = o.get()
}
operator fun plus(v: String) =
StringPropertyWithPlusAndPlusAssign(this.v + v)
operator fun plusAssign(v: String) {
this.v += v
}
}
data class Task(
val valInput: StringProperty,
var varInput: StringProperty,
val valInputWithPlus: StringPropertyWithPlus,
var varInputWithPlus: StringPropertyWithPlus,
val valInputWithPlusAssign: StringPropertyWithPlusAssign,
var varInputWithPlusAssign: StringPropertyWithPlusAssign,
val valInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign,
var varInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign,
)
fun box(): String {
val task = Task(
StringProperty("O"),
StringProperty("O"),
StringPropertyWithPlus("O"),
StringPropertyWithPlus("O"),
StringPropertyWithPlusAssign("O"),
StringPropertyWithPlusAssign("O"),
StringPropertyWithPlusAndPlusAssign("O"),
StringPropertyWithPlusAndPlusAssign("O")
)
task.valInput <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>+=<!> "K"
task.varInput <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>+=<!> "K"
<!VAL_REASSIGNMENT!>task.valInputWithPlus<!> += "K"
task.varInputWithPlusAndPlusAssign <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> "K"
return "OK"
}
@@ -0,0 +1,92 @@
package
public fun box(): kotlin.String
public abstract class AbstractStringProperty {
public constructor AbstractStringProperty(/*0*/ v: kotlin.String)
protected final var v: kotlin.String
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun get(): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@ValueContainer public final class StringProperty : AbstractStringProperty {
public constructor StringProperty(/*0*/ v: kotlin.String)
protected final override /*1*/ /*fake_override*/ var v: kotlin.String
public final fun assign(/*0*/ v: StringProperty): kotlin.Unit
public final fun assign(/*0*/ v: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun get(): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@ValueContainer public final class StringPropertyWithPlus : AbstractStringProperty {
public constructor StringPropertyWithPlus(/*0*/ v: kotlin.String)
protected final override /*1*/ /*fake_override*/ var v: kotlin.String
public final fun assign(/*0*/ o: StringPropertyWithPlus): kotlin.Unit
public final fun assign(/*0*/ v: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun get(): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final operator fun plus(/*0*/ v: kotlin.String): StringPropertyWithPlus
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@ValueContainer public final class StringPropertyWithPlusAndPlusAssign : AbstractStringProperty {
public constructor StringPropertyWithPlusAndPlusAssign(/*0*/ v: kotlin.String)
protected final override /*1*/ /*fake_override*/ var v: kotlin.String
public final fun assign(/*0*/ o: StringPropertyWithPlusAndPlusAssign): kotlin.Unit
public final fun assign(/*0*/ v: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun get(): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final operator fun plus(/*0*/ v: kotlin.String): StringPropertyWithPlusAndPlusAssign
public final operator fun plusAssign(/*0*/ v: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@ValueContainer public final class StringPropertyWithPlusAssign : AbstractStringProperty {
public constructor StringPropertyWithPlusAssign(/*0*/ v: kotlin.String)
protected final override /*1*/ /*fake_override*/ var v: kotlin.String
public final fun assign(/*0*/ o: StringPropertyWithPlusAssign): kotlin.Unit
public final fun assign(/*0*/ v: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun get(): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final operator fun plusAssign(/*0*/ v: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final data class Task {
public constructor Task(/*0*/ valInput: StringProperty, /*1*/ varInput: StringProperty, /*2*/ valInputWithPlus: StringPropertyWithPlus, /*3*/ varInputWithPlus: StringPropertyWithPlus, /*4*/ valInputWithPlusAssign: StringPropertyWithPlusAssign, /*5*/ varInputWithPlusAssign: StringPropertyWithPlusAssign, /*6*/ valInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign, /*7*/ varInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign)
public final val valInput: StringProperty
public final val valInputWithPlus: StringPropertyWithPlus
public final val valInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign
public final val valInputWithPlusAssign: StringPropertyWithPlusAssign
public final var varInput: StringProperty
public final var varInputWithPlus: StringPropertyWithPlus
public final var varInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign
public final var varInputWithPlusAssign: StringPropertyWithPlusAssign
public final operator /*synthesized*/ fun component1(): StringProperty
public final operator /*synthesized*/ fun component2(): StringProperty
public final operator /*synthesized*/ fun component3(): StringPropertyWithPlus
public final operator /*synthesized*/ fun component4(): StringPropertyWithPlus
public final operator /*synthesized*/ fun component5(): StringPropertyWithPlusAssign
public final operator /*synthesized*/ fun component6(): StringPropertyWithPlusAssign
public final operator /*synthesized*/ fun component7(): StringPropertyWithPlusAndPlusAssign
public final operator /*synthesized*/ fun component8(): StringPropertyWithPlusAndPlusAssign
public final /*synthesized*/ fun copy(/*0*/ valInput: StringProperty = ..., /*1*/ varInput: StringProperty = ..., /*2*/ valInputWithPlus: StringPropertyWithPlus = ..., /*3*/ varInputWithPlus: StringPropertyWithPlus = ..., /*4*/ valInputWithPlusAssign: StringPropertyWithPlusAssign = ..., /*5*/ varInputWithPlusAssign: StringPropertyWithPlusAssign = ..., /*6*/ valInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign = ..., /*7*/ varInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign = ...): Task
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final annotation class ValueContainer : kotlin.Annotation {
public constructor ValueContainer()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("plugins/assign-plugin/testData/diagnostics")
@TestDataPath("$PROJECT_ROOT")
public class AssignmentPluginDiagnosticTestGenerated extends AbstractAssignmentPluginDiagnosticTest {
@Test
public void testAllFilesPresentInDiagnostics() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/assign-plugin/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("incorrectUsage.kt")
public void testIncorrectUsage() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/incorrectUsage.kt");
}
@Test
@TestMetadata("localVariables.kt")
public void testLocalVariables() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/localVariables.kt");
}
@Test
@TestMetadata("methodDeclaration.kt")
public void testMethodDeclaration() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/methodDeclaration.kt");
}
@Test
@TestMetadata("noAnnotation.kt")
public void testNoAnnotation() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/noAnnotation.kt");
}
@Test
@TestMetadata("otherOperators.kt")
public void testOtherOperators() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/otherOperators.kt");
}
@Test
@TestMetadata("plusAssignPrecedence.kt")
public void testPlusAssignPrecedence() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/plusAssignPrecedence.kt");
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("plugins/assign-plugin/testData/diagnostics")
@TestDataPath("$PROJECT_ROOT")
public class FirAssignmentPluginDiagnosticTestGenerated extends AbstractFirAssignmentPluginDiagnosticTest {
@Test
public void testAllFilesPresentInDiagnostics() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/assign-plugin/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("incorrectUsage.kt")
public void testIncorrectUsage() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/incorrectUsage.kt");
}
@Test
@TestMetadata("localVariables.kt")
public void testLocalVariables() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/localVariables.kt");
}
@Test
@TestMetadata("methodDeclaration.kt")
public void testMethodDeclaration() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/methodDeclaration.kt");
}
@Test
@TestMetadata("noAnnotation.kt")
public void testNoAnnotation() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/noAnnotation.kt");
}
@Test
@TestMetadata("otherOperators.kt")
public void testOtherOperators() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/otherOperators.kt");
}
@Test
@TestMetadata("plusAssignPrecedence.kt")
public void testPlusAssignPrecedence() throws Exception {
runTest("plugins/assign-plugin/testData/diagnostics/plusAssignPrecedence.kt");
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("plugins/assign-plugin/testData/codegen")
@TestDataPath("$PROJECT_ROOT")
public class FirBlackBoxCodegenTestForAssignmentPluginGenerated extends AbstractFirBlackBoxCodegenTestForAssignmentPlugin {
@Test
public void testAllFilesPresentInCodegen() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/assign-plugin/testData/codegen"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("annotation.kt")
public void testAnnotation() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/annotation.kt");
}
@Test
@TestMetadata("otherOperators.kt")
public void testOtherOperators() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/otherOperators.kt");
}
@Test
@TestMetadata("plusAssignPrecedence.kt")
public void testPlusAssignPrecedence() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/plusAssignPrecedence.kt");
}
@Test
@TestMetadata("supportedUsage.kt")
public void testSupportedUsage() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/supportedUsage.kt");
}
@Test
@TestMetadata("varBehaviour.kt")
public void testVarBehaviour() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/varBehaviour.kt");
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.assignment.plugin;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("plugins/assign-plugin/testData/codegen")
@TestDataPath("$PROJECT_ROOT")
public class IrBlackBoxCodegenTestAssignmentPluginGenerated extends AbstractIrBlackBoxCodegenTestAssignmentPlugin {
@Test
public void testAllFilesPresentInCodegen() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/assign-plugin/testData/codegen"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("annotation.kt")
public void testAnnotation() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/annotation.kt");
}
@Test
@TestMetadata("otherOperators.kt")
public void testOtherOperators() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/otherOperators.kt");
}
@Test
@TestMetadata("plusAssignPrecedence.kt")
public void testPlusAssignPrecedence() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/plusAssignPrecedence.kt");
}
@Test
@TestMetadata("supportedUsage.kt")
public void testSupportedUsage() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/supportedUsage.kt");
}
@Test
@TestMetadata("varBehaviour.kt")
public void testVarBehaviour() throws Exception {
runTest("plugins/assign-plugin/testData/codegen/varBehaviour.kt");
}
}
+12
View File
@@ -210,6 +210,12 @@ include ":kotlin-sam-with-receiver-compiler-plugin",
":kotlin-sam-with-receiver-compiler-plugin.k2",
":kotlin-sam-with-receiver-compiler-plugin.cli"
include ":kotlin-assignment-compiler-plugin",
":kotlin-assignment-compiler-plugin.common",
":kotlin-assignment-compiler-plugin.k1",
":kotlin-assignment-compiler-plugin.k2",
":kotlin-assignment-compiler-plugin.cli"
include ":kotlin-imports-dumper-compiler-plugin",
":kotlin-script-runtime",
":plugins:fir-plugin-prototype",
@@ -736,6 +742,12 @@ project(':kotlin-sam-with-receiver-compiler-plugin.k1').projectDir = "$rootDir/p
project(':kotlin-sam-with-receiver-compiler-plugin.k2').projectDir = "$rootDir/plugins/sam-with-receiver/sam-with-receiver.k2" as File
project(':kotlin-sam-with-receiver-compiler-plugin.cli').projectDir = "$rootDir/plugins/sam-with-receiver/sam-with-receiver.cli" as File
project(':kotlin-assignment-compiler-plugin').projectDir = "$rootDir/plugins/assign-plugin" as File
project(':kotlin-assignment-compiler-plugin.common').projectDir = "$rootDir/plugins/assign-plugin/assign-plugin.common" as File
project(':kotlin-assignment-compiler-plugin.k1').projectDir = "$rootDir/plugins/assign-plugin/assign-plugin.k1" as File
project(':kotlin-assignment-compiler-plugin.k2').projectDir = "$rootDir/plugins/assign-plugin/assign-plugin.k2" as File
project(':kotlin-assignment-compiler-plugin.cli').projectDir = "$rootDir/plugins/assign-plugin/assign-plugin.cli" as File
project(':tools:kotlinp').projectDir = "$rootDir/libraries/tools/kotlinp" as File
project(':kotlin-project-model').projectDir = "$rootDir/libraries/tools/kotlin-project-model" as File
project(':kotlin-project-model-tests-generator').projectDir = "$rootDir/libraries/tools/kotlin-project-model-tests-generator" as File