Add diagnostics on convention operator calls without an "operator" modifier on declaration site
This commit is contained in:
@@ -524,6 +524,10 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory0<PsiElement> UNDESCORE_IS_DEPRECATED = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_OPERATOR_MODIFIER = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory2<PsiElement, FunctionDescriptor, String> OPERATOR_MODIFIER_REQUIRED = DiagnosticFactory2.create(WARNING);
|
||||
|
||||
// Labels
|
||||
|
||||
DiagnosticFactory0<JetSimpleNameExpression> LABEL_NAME_CLASH = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
+4
@@ -354,6 +354,10 @@ public class DefaultErrorMessages {
|
||||
|
||||
MAP.put(UNDESCORE_IS_DEPRECATED, "Names _, __, ___, ..., are deprecated");
|
||||
|
||||
MAP.put(INAPPLICABLE_OPERATOR_MODIFIER, "'operator' modifier is inapplicable on this function");
|
||||
|
||||
MAP.put(OPERATOR_MODIFIER_REQUIRED, "'operator' modifier is required on ''{0}'' in ''{1}''", NAME, STRING);
|
||||
|
||||
MAP.put(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY,
|
||||
"Returns are not allowed for functions with expression body. Use block body in '{...}'");
|
||||
MAP.put(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, "A 'return' expression required in a function with a block body ('{...}')");
|
||||
|
||||
@@ -19,7 +19,17 @@ package org.jetbrains.kotlin.psi
|
||||
import com.intellij.lang.ASTNode
|
||||
import org.jetbrains.kotlin.parsing.JetExpressionParsing
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.impl.source.tree.TreeElement
|
||||
import org.jetbrains.kotlin.lexer.JetToken
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
public class JetOperationReferenceExpression(node: ASTNode) : JetSimpleNameExpressionImpl(node) {
|
||||
override fun getReferencedNameElement() = (findChildByType(JetExpressionParsing.ALL_OPERATIONS): PsiElement?) ?: this
|
||||
|
||||
fun getNameForConventionalOperation(): Name? {
|
||||
val operator = (firstChild as? TreeElement)?.elementType as? JetToken ?: return null
|
||||
return OperatorConventions.getNameForOperationSymbol(operator)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.container.useInstance
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.*
|
||||
import org.jetbrains.kotlin.resolve.validation.AccessToPrivateTopLevelSymbolValidator
|
||||
import org.jetbrains.kotlin.resolve.validation.DeprecatedSymbolValidator
|
||||
import org.jetbrains.kotlin.resolve.validation.OperatorValidator
|
||||
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator
|
||||
import org.jetbrains.kotlin.types.DynamicTypesSettings
|
||||
|
||||
@@ -41,7 +42,7 @@ public abstract class TargetPlatform(
|
||||
private val DEFAULT_DECLARATION_CHECKERS = listOf(DataClassAnnotationChecker(), ConstModifierChecker, UnderscoreChecker)
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker())
|
||||
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
||||
private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), AccessToPrivateTopLevelSymbolValidator())
|
||||
private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), AccessToPrivateTopLevelSymbolValidator(), OperatorValidator())
|
||||
|
||||
|
||||
public open class PlatformConfigurator(
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.validation
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.JetArrayAccessExpression
|
||||
import org.jetbrains.kotlin.psi.JetElement
|
||||
import org.jetbrains.kotlin.psi.JetMultiDeclarationEntry
|
||||
import org.jetbrains.kotlin.psi.JetOperationReferenceExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
|
||||
public class OperatorValidator : SymbolUsageValidator {
|
||||
|
||||
override fun validateCall(targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement) {
|
||||
val functionDescriptor = targetDescriptor as? FunctionDescriptor ?: return
|
||||
if (functionDescriptor.isDynamic() || ErrorUtils.isError(functionDescriptor)) return
|
||||
|
||||
val jetElement = element as? JetElement ?: return
|
||||
val call = trace.bindingContext[BindingContext.CALL, jetElement]
|
||||
|
||||
fun isVariableAsFunctionCall(): Boolean {
|
||||
if (call == null) return false
|
||||
val resolvedCall = trace.bindingContext[BindingContext.RESOLVED_CALL, call] ?: return false
|
||||
return resolvedCall is VariableAsFunctionResolvedCall
|
||||
}
|
||||
|
||||
fun isMultiDeclaration(): Boolean {
|
||||
return call?.callElement is JetMultiDeclarationEntry
|
||||
}
|
||||
|
||||
fun isConventionOperator(): Boolean {
|
||||
if (jetElement !is JetOperationReferenceExpression) return false
|
||||
return jetElement.getNameForConventionalOperation() != null
|
||||
}
|
||||
|
||||
fun isArrayAccessExpression() = jetElement is JetArrayAccessExpression
|
||||
|
||||
if (isMultiDeclaration()) {
|
||||
if (!functionDescriptor.isOperator && call != null) {
|
||||
report(call.callElement, functionDescriptor, trace)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (isVariableAsFunctionCall() || isMultiDeclaration() || isConventionOperator() || isArrayAccessExpression()) {
|
||||
if (!functionDescriptor.isOperator) {
|
||||
report(jetElement, functionDescriptor, trace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun report(element: JetElement, descriptor: FunctionDescriptor, sink: DiagnosticSink) {
|
||||
val containingDeclaration = descriptor.containingDeclaration
|
||||
val containingDeclarationName = containingDeclaration.fqNameUnsafe.asString()
|
||||
sink.report(Errors.OPERATOR_MODIFIER_REQUIRED.on(element, descriptor, containingDeclarationName))
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
class Example {
|
||||
public fun plus(o: Example) = o
|
||||
public operator fun minus(o: Example) = o
|
||||
|
||||
public fun get(i: Int) = ""
|
||||
public operator fun get(s: String) = ""
|
||||
|
||||
public fun set(i: Int, v: String) {}
|
||||
public operator fun set(s: String, v: String) {}
|
||||
|
||||
public fun not() = false
|
||||
|
||||
public fun rangeTo(o: Example) = o
|
||||
public fun contains(o: Example) = false
|
||||
public fun compareTo(o: Example) = 0
|
||||
|
||||
public fun inc() = this
|
||||
public fun dec() = this
|
||||
|
||||
public fun invoke() {}
|
||||
}
|
||||
|
||||
class Example2 {
|
||||
public operator fun not() = true
|
||||
|
||||
public fun plusAssign(o: Example2) {}
|
||||
public operator fun minusAssign(o: Example2) {}
|
||||
|
||||
public operator fun rangeTo(o: Example2) = o
|
||||
public operator fun contains(o: Example2) = false
|
||||
public operator fun compareTo(o: Example2) = 0
|
||||
|
||||
public operator fun inc() = this
|
||||
public operator fun dec() = this
|
||||
|
||||
public operator fun invoke() {}
|
||||
}
|
||||
|
||||
fun a() {
|
||||
var a = Example()
|
||||
var b = Example()
|
||||
var c = Example2()
|
||||
var d = Example2()
|
||||
|
||||
Example() == Example()
|
||||
|
||||
a == b
|
||||
c != d
|
||||
|
||||
Example() <!OPERATOR_MODIFIER_REQUIRED!>+<!> Example()
|
||||
|
||||
a <!OPERATOR_MODIFIER_REQUIRED!>+<!> b
|
||||
a - b
|
||||
<!OPERATOR_MODIFIER_REQUIRED!>a[1]<!>
|
||||
a["str"]
|
||||
|
||||
<!OPERATOR_MODIFIER_REQUIRED!>a[1]<!> = "A"
|
||||
a["str"] = "str"
|
||||
|
||||
a.plus(b)
|
||||
a.minus(b)
|
||||
a.get(1)
|
||||
a.get("str")
|
||||
|
||||
c <!OPERATOR_MODIFIER_REQUIRED!>+=<!> d
|
||||
c -= d
|
||||
|
||||
a<!OPERATOR_MODIFIER_REQUIRED!>..<!>b
|
||||
c..d
|
||||
|
||||
Example()<!OPERATOR_MODIFIER_REQUIRED!>..<!>Example()
|
||||
Example2()..Example2()
|
||||
|
||||
a <!OPERATOR_MODIFIER_REQUIRED!><<!> b
|
||||
a <!OPERATOR_MODIFIER_REQUIRED!>>=<!> b
|
||||
c > d
|
||||
|
||||
a <!OPERATOR_MODIFIER_REQUIRED!>in<!> b
|
||||
c in d
|
||||
|
||||
a<!OPERATOR_MODIFIER_REQUIRED!>++<!>
|
||||
a<!OPERATOR_MODIFIER_REQUIRED!>--<!>
|
||||
c++
|
||||
c--
|
||||
|
||||
<!OPERATOR_MODIFIER_REQUIRED!>!<!>a
|
||||
!c
|
||||
|
||||
<!OPERATOR_MODIFIER_REQUIRED!>a<!>()
|
||||
c()
|
||||
}
|
||||
|
||||
abstract class Base {
|
||||
abstract operator fun plus(o: Base): Base
|
||||
abstract fun minus(o: Base): Base
|
||||
}
|
||||
|
||||
open class Anc : Base() {
|
||||
override fun plus(o: Base) = o
|
||||
override fun minus(o: Base) = o
|
||||
}
|
||||
|
||||
class Anc2 : Anc()
|
||||
|
||||
fun b() {
|
||||
Anc() + Anc()
|
||||
Anc() <!OPERATOR_MODIFIER_REQUIRED!>-<!> Anc()
|
||||
Anc2() + Anc2()
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package
|
||||
|
||||
public fun a(): kotlin.Unit
|
||||
public fun b(): kotlin.Unit
|
||||
|
||||
public open class Anc : Base {
|
||||
public constructor Anc()
|
||||
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*/ fun minus(/*0*/ o: Base): Base
|
||||
public open override /*1*/ fun plus(/*0*/ o: Base): Base
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class Anc2 : Anc {
|
||||
public constructor Anc2()
|
||||
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 minus(/*0*/ o: Base): Base
|
||||
public open override /*1*/ /*fake_override*/ fun plus(/*0*/ o: Base): Base
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public abstract class Base {
|
||||
public constructor Base()
|
||||
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 abstract fun minus(/*0*/ o: Base): Base
|
||||
public abstract operator fun plus(/*0*/ o: Base): Base
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class Example {
|
||||
public constructor Example()
|
||||
public final fun compareTo(/*0*/ o: Example): kotlin.Int
|
||||
public final fun contains(/*0*/ o: Example): kotlin.Boolean
|
||||
public final fun dec(): Example
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final fun get(/*0*/ i: kotlin.Int): kotlin.String
|
||||
public final operator fun get(/*0*/ s: kotlin.String): kotlin.String
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final fun inc(): Example
|
||||
public final fun invoke(): kotlin.Unit
|
||||
public final operator fun minus(/*0*/ o: Example): Example
|
||||
public final fun not(): kotlin.Boolean
|
||||
public final fun plus(/*0*/ o: Example): Example
|
||||
public final fun rangeTo(/*0*/ o: Example): Example
|
||||
public final fun set(/*0*/ i: kotlin.Int, /*1*/ v: kotlin.String): kotlin.Unit
|
||||
public final operator fun set(/*0*/ s: kotlin.String, /*1*/ v: kotlin.String): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class Example2 {
|
||||
public constructor Example2()
|
||||
public final operator fun compareTo(/*0*/ o: Example2): kotlin.Int
|
||||
public final operator fun contains(/*0*/ o: Example2): kotlin.Boolean
|
||||
public final operator fun dec(): Example2
|
||||
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 final operator fun inc(): Example2
|
||||
public final operator fun invoke(): kotlin.Unit
|
||||
public final operator fun minusAssign(/*0*/ o: Example2): kotlin.Unit
|
||||
public final operator fun not(): kotlin.Boolean
|
||||
public final fun plusAssign(/*0*/ o: Example2): kotlin.Unit
|
||||
public final operator fun rangeTo(/*0*/ o: Example2): Example2
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -337,6 +337,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Operators.kt")
|
||||
public void testOperators() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/Operators.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OverrideFunctionWithParamDefaultValue.kt")
|
||||
public void testOverrideFunctionWithParamDefaultValue() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/OverrideFunctionWithParamDefaultValue.kt");
|
||||
|
||||
Reference in New Issue
Block a user