Diagnostics on 'infix' (declaration&use site)
This commit is contained in:
@@ -534,8 +534,10 @@ public interface Errors {
|
||||
DiagnosticFactory0<PsiElement> UNDERSCORE_IS_DEPRECATED = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_OPERATOR_MODIFIER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_INFIX_MODIFIER = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory2<PsiElement, FunctionDescriptor, String> OPERATOR_MODIFIER_REQUIRED = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<JetOperationReferenceExpression, FunctionDescriptor, String> INFIX_MODIFIER_REQUIRED = DiagnosticFactory2.create(WARNING);
|
||||
|
||||
// Labels
|
||||
|
||||
|
||||
+2
@@ -362,8 +362,10 @@ public class DefaultErrorMessages {
|
||||
MAP.put(UNDERSCORE_IS_DEPRECATED, "Names _, __, ___, ..., are deprecated");
|
||||
|
||||
MAP.put(INAPPLICABLE_OPERATOR_MODIFIER, "'operator' modifier is inapplicable on this function");
|
||||
MAP.put(INAPPLICABLE_INFIX_MODIFIER, "'infix' modifier is inapplicable on this function");
|
||||
|
||||
MAP.put(OPERATOR_MODIFIER_REQUIRED, "'operator' modifier is required on ''{0}'' in ''{1}''", NAME, STRING);
|
||||
MAP.put(INFIX_MODIFIER_REQUIRED, "'infix' 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 '{...}'");
|
||||
|
||||
@@ -20,6 +20,7 @@ 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.JetSingleValueToken
|
||||
import org.jetbrains.kotlin.lexer.JetToken
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
@@ -32,4 +33,6 @@ public class JetOperationReferenceExpression(node: ASTNode) : JetSimpleNameExpre
|
||||
return OperatorConventions.getNameForOperationSymbol(operator)
|
||||
}
|
||||
|
||||
fun isPredefinedOperator() = (firstChild as? TreeElement)?.elementType is JetSingleValueToken
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.JetDeclaration
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
||||
|
||||
public class InfixModifierChecker : DeclarationChecker {
|
||||
|
||||
override fun check(
|
||||
declaration: JetDeclaration,
|
||||
descriptor: DeclarationDescriptor,
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
bindingContext: BindingContext
|
||||
) {
|
||||
val functionDescriptor = descriptor as? FunctionDescriptor ?: return
|
||||
if (!functionDescriptor.isInfix) return
|
||||
val modifier = declaration.modifierList?.getModifier(JetTokens.INFIX_KEYWORD) ?: return
|
||||
|
||||
if (!isApplicable(functionDescriptor)) {
|
||||
diagnosticHolder.report(Errors.INAPPLICABLE_INFIX_MODIFIER.on(modifier))
|
||||
}
|
||||
}
|
||||
|
||||
private fun isApplicable(descriptor: FunctionDescriptor): Boolean {
|
||||
if (descriptor.dispatchReceiverParameter == null && descriptor.extensionReceiverParameter == null) return false
|
||||
val paramCount = descriptor.valueParameters.size()
|
||||
return when (paramCount) {
|
||||
0 -> false
|
||||
1 -> true
|
||||
else -> {
|
||||
val params = descriptor.valueParameters
|
||||
for (i in 1..params.lastIndex) {
|
||||
if (!params[i].hasDefaultValue()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,9 +24,7 @@ import org.jetbrains.kotlin.descriptors.ModuleParameters
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.*
|
||||
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.resolve.validation.*
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.types.DynamicTypesSettings
|
||||
|
||||
@@ -49,10 +47,16 @@ abstract class TargetPlatform(
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_DECLARATION_CHECKERS = listOf(DataClassAnnotationChecker(), ConstModifierChecker, UnderscoreChecker, OperatorModifierChecker())
|
||||
private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
DataClassAnnotationChecker(),
|
||||
ConstModifierChecker,
|
||||
UnderscoreChecker,
|
||||
OperatorModifierChecker(),
|
||||
InfixModifierChecker())
|
||||
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(), SafeCallChecker())
|
||||
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
||||
private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), OperatorValidator())
|
||||
private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), OperatorValidator(), InfixValidator())
|
||||
|
||||
|
||||
open class PlatformConfigurator(
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.Errors
|
||||
import org.jetbrains.kotlin.psi.JetBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.JetOperationReferenceExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
|
||||
public class InfixValidator : SymbolUsageValidator {
|
||||
|
||||
override fun validateCall(targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement) {
|
||||
val functionDescriptor = targetDescriptor as? FunctionDescriptor ?: return
|
||||
if (functionDescriptor.isDynamic() || ErrorUtils.isError(functionDescriptor)) return
|
||||
if (isInfixCall(element) && !functionDescriptor.isInfix) {
|
||||
val operationRefExpression = element as? JetOperationReferenceExpression ?: return
|
||||
val containingDeclarationName = functionDescriptor.containingDeclaration.fqNameUnsafe.asString()
|
||||
trace.report(Errors.INFIX_MODIFIER_REQUIRED.on(operationRefExpression, functionDescriptor, containingDeclarationName))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun isInfixCall(element: PsiElement?): Boolean {
|
||||
val operationRefExpression = element as? JetOperationReferenceExpression ?: return false
|
||||
val binaryExpression = operationRefExpression.parent as? JetBinaryExpression ?: return false
|
||||
return binaryExpression.operationReference === operationRefExpression && !operationRefExpression.isPredefinedOperator()
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
class Pair<out A, out B>(val first: A, val second: B)
|
||||
|
||||
class Example {
|
||||
infix fun to(other: Example) = Pair(this, other)
|
||||
fun toNonInfix(other: Example) = Pair(this, other)
|
||||
}
|
||||
|
||||
infix fun Example.toExt(other: Example) = Pair(this, other)
|
||||
fun Example.toExtNonInfix(other: Example) = Pair(this, other)
|
||||
|
||||
fun Example.withLambda(f: () -> Unit) = Pair(this, f)
|
||||
|
||||
fun test() {
|
||||
Example() to Example()
|
||||
Example() <!INFIX_MODIFIER_REQUIRED!>toNonInfix<!> Example()
|
||||
Example().toNonInfix(Example())
|
||||
|
||||
val a = Example()
|
||||
val b = Example()
|
||||
|
||||
a toExt b
|
||||
a <!INFIX_MODIFIER_REQUIRED!>toExtNonInfix<!> b
|
||||
a.toExtNonInfix(b)
|
||||
|
||||
a <!INFIX_MODIFIER_REQUIRED!>withLambda<!> { }
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package
|
||||
|
||||
public fun test(): kotlin.Unit
|
||||
public infix fun Example.toExt(/*0*/ other: Example): Pair<Example, Example>
|
||||
public fun Example.toExtNonInfix(/*0*/ other: Example): Pair<Example, Example>
|
||||
public fun Example.withLambda(/*0*/ f: () -> kotlin.Unit): Pair<Example, () -> kotlin.Unit>
|
||||
|
||||
public final class Example {
|
||||
public constructor Example()
|
||||
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 infix fun to(/*0*/ other: Example): Pair<Example, Example>
|
||||
public final fun toNonInfix(/*0*/ other: Example): Pair<Example, Example>
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class Pair</*0*/ out A, /*1*/ out B> {
|
||||
public constructor Pair</*0*/ out A, /*1*/ out B>(/*0*/ first: A, /*1*/ second: B)
|
||||
public final val first: A
|
||||
public final val second: B
|
||||
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,21 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
class Pair<A, B>(val a: A, val b: B)
|
||||
infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
|
||||
|
||||
infix fun String.o1(o: String) = o
|
||||
infix fun String.o2(o: String, o2: String? = null) = o
|
||||
|
||||
<!INAPPLICABLE_INFIX_MODIFIER!>infix<!> fun w1() {}
|
||||
<!INAPPLICABLE_INFIX_MODIFIER!>infix<!> fun w2(s: String) {}
|
||||
<!INAPPLICABLE_INFIX_MODIFIER!>infix<!> fun String.w3() {}
|
||||
<!INAPPLICABLE_INFIX_MODIFIER!>infix<!> fun String.w4(a: Int, b: Int) {}
|
||||
<!INAPPLICABLE_INFIX_MODIFIER!>infix<!> fun w5(a: Int, b: Int) {}
|
||||
|
||||
class Example {
|
||||
infix fun c1(s: String) {}
|
||||
infix fun c2(s: String, a: Int = 0) {}
|
||||
|
||||
<!INAPPLICABLE_INFIX_MODIFIER!>infix<!> fun cw1(s: String, a: Int) {}
|
||||
<!INAPPLICABLE_INFIX_MODIFIER!>infix<!> fun sw2() {}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package
|
||||
|
||||
public infix fun w1(): kotlin.Unit
|
||||
public infix fun w2(/*0*/ s: kotlin.String): kotlin.Unit
|
||||
public infix fun w5(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Unit
|
||||
public infix fun kotlin.String.o1(/*0*/ o: kotlin.String): kotlin.String
|
||||
public infix fun kotlin.String.o2(/*0*/ o: kotlin.String, /*1*/ o2: kotlin.String? = ...): kotlin.String
|
||||
public infix fun </*0*/ A, /*1*/ B> A.to(/*0*/ that: B): Pair<A, B>
|
||||
public infix fun kotlin.String.w3(): kotlin.Unit
|
||||
public infix fun kotlin.String.w4(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Unit
|
||||
|
||||
public final class Example {
|
||||
public constructor Example()
|
||||
public final infix fun c1(/*0*/ s: kotlin.String): kotlin.Unit
|
||||
public final infix fun c2(/*0*/ s: kotlin.String, /*1*/ a: kotlin.Int = ...): kotlin.Unit
|
||||
public final infix fun cw1(/*0*/ s: kotlin.String, /*1*/ a: kotlin.Int): kotlin.Unit
|
||||
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 infix fun sw2(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class Pair</*0*/ A, /*1*/ B> {
|
||||
public constructor Pair</*0*/ A, /*1*/ B>(/*0*/ a: A, /*1*/ b: B)
|
||||
public final val a: A
|
||||
public final val b: B
|
||||
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
|
||||
}
|
||||
@@ -271,6 +271,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Infix.kt")
|
||||
public void testInfix() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/Infix.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("InfixModifierApplicability.kt")
|
||||
public void testInfixModifierApplicability() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/InfixModifierApplicability.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("IsExpressions.kt")
|
||||
public void testIsExpressions() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/IsExpressions.kt");
|
||||
|
||||
Reference in New Issue
Block a user