Skip to content

Commit 3219ef6

Browse files
mchernyavskyyopox
authored andcommitted
INSP: Detect E0747 error
1 parent bb02245 commit 3219ef6

19 files changed

+312
-123
lines changed

src/main/kotlin/org/rust/ide/annotator/fixes/EncloseExprInBracesFix.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import com.intellij.openapi.editor.Editor
1010
import com.intellij.openapi.project.Project
1111
import com.intellij.psi.PsiElement
1212
import com.intellij.psi.PsiFile
13-
import org.rust.lang.core.psi.RsExpr
1413
import org.rust.lang.core.psi.RsPsiFactory
14+
import org.rust.lang.core.psi.ext.RsElement
1515

16-
class EncloseExprInBracesFix(expr: RsExpr) : LocalQuickFixAndIntentionActionOnPsiElement(expr) {
16+
class EncloseExprInBracesFix(element: RsElement) : LocalQuickFixAndIntentionActionOnPsiElement(element) {
1717
override fun getFamilyName(): String = text
1818
override fun getText(): String = "Enclose the expression in braces"
1919

src/main/kotlin/org/rust/ide/inspections/RsWrongGenericArgumentsNumberInspection.kt

Lines changed: 25 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,18 @@ import org.rust.lang.utils.addToHolder
2222
class RsWrongGenericArgumentsNumberInspection : RsLocalInspectionTool() {
2323
override fun buildVisitor(holder: RsProblemsHolder, isOnTheFly: Boolean): RsVisitor =
2424
object : RsVisitor() {
25-
override fun visitBaseType(type: RsBaseType) {
26-
if (!isPathValid(type.path)) return
27-
checkTypeArguments(holder, type)
25+
override fun visitMethodCall(methodCall: RsMethodCall) = checkTypeArguments(holder, methodCall)
26+
override fun visitPath(path: RsPath) {
27+
if (!isPathValid(path)) return
28+
checkTypeArguments(holder, path)
2829
}
29-
30-
override fun visitTraitRef(trait: RsTraitRef) {
31-
if (!isPathValid(trait.path)) return
32-
checkTypeArguments(holder, trait)
33-
}
34-
35-
override fun visitCallExpr(o: RsCallExpr) = checkTypeArguments(holder, o)
36-
override fun visitMethodCall(o: RsMethodCall) = checkTypeArguments(holder, o)
3730
}
3831

3932
// Don't apply generic declaration checks to Fn-traits and `Self`
4033
private fun isPathValid(path: RsPath?): Boolean = path?.valueParameterList == null && path?.cself == null
4134

42-
private fun checkTypeArguments(holder: RsProblemsHolder, o: RsElement) {
43-
val (actualArguments, declaration) = getTypeArgumentsAndDeclaration(o) ?: return
35+
private fun checkTypeArguments(holder: RsProblemsHolder, element: RsElement) {
36+
val (actualArguments, declaration) = getTypeArgumentsAndDeclaration(element) ?: return
4437

4538
val actualTypeArgs = actualArguments?.typeReferenceList?.size ?: 0
4639
val expectedTotalTypeParams = declaration.typeParameters.size
@@ -51,15 +44,21 @@ class RsWrongGenericArgumentsNumberInspection : RsLocalInspectionTool() {
5144

5245
val actualArgs = actualTypeArgs + actualConstArgs
5346
val expectedTotalParams = expectedTotalTypeParams + expectedTotalConstParams
54-
val expectedRequiredParams = expectedRequiredTypeParams + expectedTotalConstParams
47+
val maxExpectedRequiredParams = expectedRequiredTypeParams + expectedTotalConstParams
48+
val minExpectedRequiredParams = when (element.parent) {
49+
is RsBaseType, is RsTraitRef -> 0
50+
else -> 1
51+
}
5552

5653
if (actualArgs == expectedTotalParams) return
5754

58-
val errorText = when (o) {
59-
is RsBaseType, is RsTraitRef -> checkTypeReference(actualArgs, expectedRequiredParams, expectedTotalParams)
60-
is RsMethodCall, is RsCallExpr -> checkFunctionCall(actualArgs, expectedRequiredParams, expectedTotalParams)
61-
else -> null
62-
} ?: return
55+
val errorText = when {
56+
actualArgs > expectedTotalParams ->
57+
if (maxExpectedRequiredParams != expectedTotalParams) "at most $expectedTotalParams" else "$expectedTotalParams"
58+
actualArgs in minExpectedRequiredParams until maxExpectedRequiredParams ->
59+
if (maxExpectedRequiredParams != expectedTotalParams) "at least $maxExpectedRequiredParams" else "$expectedTotalParams"
60+
else -> return
61+
}
6362

6463
val haveTypeParams = expectedTotalTypeParams > 0 || actualTypeArgs > 0
6564
val haveConstParams = expectedTotalConstParams > 0 || actualConstArgs > 0
@@ -70,29 +69,9 @@ class RsWrongGenericArgumentsNumberInspection : RsLocalInspectionTool() {
7069
}
7170

7271
val problemText = "Wrong number of $argumentName arguments: expected $errorText, found $actualArgs"
73-
val fixes = getFixes(o, actualTypeArgs, expectedTotalTypeParams)
74-
75-
RsDiagnostic.WrongNumberOfTypeArguments(o, problemText, fixes).addToHolder(holder)
76-
}
77-
}
78-
79-
private fun checkTypeReference(actualArgs: Int, expectedRequiredParams: Int, expectedTotalParams: Int): String? {
80-
return when {
81-
actualArgs > expectedTotalParams ->
82-
if (expectedRequiredParams != expectedTotalParams) "at most $expectedTotalParams" else "$expectedTotalParams"
83-
actualArgs < expectedRequiredParams ->
84-
if (expectedRequiredParams != expectedTotalParams) "at least $expectedRequiredParams" else "$expectedTotalParams"
85-
else -> null
86-
}
87-
}
72+
val fixes = getFixes(element, actualTypeArgs, expectedTotalTypeParams)
8873

89-
private fun checkFunctionCall(actualArgs: Int, expectedRequiredParams: Int, expectedTotalParams: Int): String? {
90-
return when {
91-
actualArgs > expectedTotalParams ->
92-
if (expectedRequiredParams != expectedTotalParams) "at most $expectedTotalParams" else "$expectedTotalParams"
93-
actualArgs in 1 until expectedRequiredParams ->
94-
if (expectedRequiredParams != expectedTotalParams) "at least $expectedRequiredParams" else "$expectedTotalParams"
95-
else -> null
74+
RsDiagnostic.WrongNumberOfTypeArguments(element, problemText, fixes).addToHolder(holder)
9675
}
9776
}
9877

@@ -103,14 +82,12 @@ private fun getFixes(element: RsElement, actualArgs: Int, expectedTotalParams: I
10382
else -> emptyList()
10483
}
10584

106-
fun getTypeArgumentsAndDeclaration(element: RsElement): Pair<RsTypeArgumentList?, RsGenericDeclaration>? {
107-
val (arguments, resolved) = when (element) {
108-
is RsMethodCall -> element.typeArgumentList to element.reference.resolve()
109-
is RsCallExpr -> (element.expr as? RsPathExpr)?.path?.typeArgumentList to (element.expr as? RsPathExpr)?.path?.reference?.resolve()
110-
is RsBaseType -> element.path?.typeArgumentList to element.path?.reference?.resolve()
111-
is RsTraitRef -> element.path.typeArgumentList to element.path.reference?.resolve()
85+
fun getTypeArgumentsAndDeclaration(pathOrMethodCall: RsElement): Pair<RsTypeArgumentList?, RsGenericDeclaration>? {
86+
val (arguments, resolved) = when (pathOrMethodCall) {
87+
is RsPath -> pathOrMethodCall.typeArgumentList to pathOrMethodCall.reference?.resolve()
88+
is RsMethodCall -> pathOrMethodCall.typeArgumentList to pathOrMethodCall.reference.resolve()
11289
else -> return null
11390
}
11491
if (resolved !is RsGenericDeclaration) return null
115-
return Pair(arguments, resolved)
92+
return arguments to resolved
11693
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Use of this source code is governed by the MIT license that can be
3+
* found in the LICENSE file.
4+
*/
5+
6+
package org.rust.ide.inspections
7+
8+
import org.rust.ide.annotator.fixes.EncloseExprInBracesFix
9+
import org.rust.lang.core.psi.*
10+
import org.rust.lang.core.psi.ext.*
11+
import org.rust.lang.utils.RsDiagnostic
12+
import org.rust.lang.utils.addToHolder
13+
14+
/**
15+
* Inspection that detects the E0747 error.
16+
*/
17+
class RsWrongGenericArgumentsOrderInspection : RsLocalInspectionTool() {
18+
override fun buildVisitor(holder: RsProblemsHolder, isOnTheFly: Boolean): RsVisitor =
19+
object : RsVisitor() {
20+
override fun visitMethodCall(methodCall: RsMethodCall) = checkGenericArguments(holder, methodCall)
21+
override fun visitPath(path: RsPath) {
22+
if (!isPathValid(path)) return
23+
checkGenericArguments(holder, path)
24+
}
25+
}
26+
27+
// Don't apply generic declaration checks to Fn-traits and `Self`
28+
private fun isPathValid(path: RsPath?): Boolean = path?.valueParameterList == null && path?.cself == null
29+
30+
private fun checkGenericArguments(holder: RsProblemsHolder, element: RsElement) {
31+
val (actualArguments, declaration) = getTypeArgumentsAndDeclaration(element) ?: return
32+
if (actualArguments == null) return
33+
val parameterList = declaration.typeParameterList ?: return
34+
35+
val params = parameterList.stubChildrenOfType<RsGenericParameter>().filterNot { it is RsLifetimeParameter }
36+
val args = actualArguments.stubChildrenOfType<RsElement>().filter { it is RsTypeReference || it is RsExpr }
37+
38+
val typeArguments = actualArguments.typeArguments
39+
val constArguments = actualArguments.constArguments
40+
41+
fun kindName(arg: RsElement): String = when (arg) {
42+
in typeArguments -> "Type"
43+
in constArguments -> "Constant"
44+
else -> error("impossible")
45+
}
46+
47+
for ((param, arg) in params.zip(args)) {
48+
val text = when {
49+
param is RsTypeParameter && arg !in typeArguments -> "${kindName(arg)} provided when a type was expected"
50+
param is RsConstParameter && arg !in constArguments -> "${kindName(arg)} provided when a constant was expected"
51+
else -> continue
52+
}
53+
val fixes = if (param is RsConstParameter && arg is RsTypeReference) {
54+
listOf(EncloseExprInBracesFix(arg))
55+
} else {
56+
emptyList()
57+
}
58+
RsDiagnostic.WrongGenericArgumentsNumber(arg, text, fixes).addToHolder(holder)
59+
}
60+
}
61+
}

src/main/kotlin/org/rust/ide/inspections/fixes/AddTypeArguments.kt

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ import com.intellij.psi.PsiElement
1212
import com.intellij.psi.PsiFile
1313
import org.rust.ide.inspections.getTypeArgumentsAndDeclaration
1414
import org.rust.ide.utils.template.buildAndRunTemplate
15-
import org.rust.lang.core.psi.*
1615
import org.rust.lang.core.psi.RsElementTypes.COMMA
1716
import org.rust.lang.core.psi.RsElementTypes.LT
17+
import org.rust.lang.core.psi.RsPath
18+
import org.rust.lang.core.psi.RsPsiFactory
19+
import org.rust.lang.core.psi.RsTypeArgumentList
20+
import org.rust.lang.core.psi.RsTypeReference
1821
import org.rust.lang.core.psi.ext.RsElement
1922
import org.rust.lang.core.psi.ext.elementType
2023
import org.rust.lang.core.psi.ext.getNextNonCommentSibling
@@ -41,16 +44,16 @@ class AddTypeArguments(element: RsElement) : LocalQuickFixAndIntentionActionOnPs
4144
/**
4245
* Inserts type arguments if they are needed and returns a list of inserted type arguments.
4346
*/
44-
fun insertTypeArgumentsIfNeeded(element: RsElement): List<RsTypeReference>? {
45-
val (typeArguments, declaration) = getTypeArgumentsAndDeclaration(element) ?: return null
47+
fun insertTypeArgumentsIfNeeded(pathOrMethodCall: RsElement): List<RsTypeReference>? {
48+
val (typeArguments, declaration) = getTypeArgumentsAndDeclaration(pathOrMethodCall) ?: return null
4649

4750
val requiredParameters = declaration.requiredGenericParameters
4851
if (requiredParameters.isEmpty()) return null
4952

5053
val argumentCount = typeArguments?.typeReferenceList?.size ?: 0
5154
if (argumentCount >= requiredParameters.size) return null
5255

53-
val factory = RsPsiFactory(element.project)
56+
val factory = RsPsiFactory(pathOrMethodCall.project)
5457
val missingTypes = requiredParameters.drop(argumentCount).map { it.name ?: "_" }
5558

5659
val replaced = if (typeArguments != null) {
@@ -79,17 +82,11 @@ fun insertTypeArgumentsIfNeeded(element: RsElement): List<RsTypeReference>? {
7982
val newArgumentList = factory.createTypeArgumentList(missingTypes)
8083

8184
// this can only happen for type references (base types/trait refs)
82-
val path = getPath(element) ?: return null
83-
path.addAfter(newArgumentList, path.identifier) as RsTypeArgumentList
85+
if (pathOrMethodCall !is RsPath) return null
86+
pathOrMethodCall.addAfter(newArgumentList, pathOrMethodCall.identifier) as RsTypeArgumentList
8487
}
8588
return replaced.typeReferenceList.drop(argumentCount)
8689
}
8790

88-
private fun getPath(element: PsiElement): RsPath? = when (element) {
89-
is RsBaseType -> element.path
90-
is RsTraitRef -> element.path
91-
else -> null
92-
}
93-
9491
private val PsiElement.isComma: Boolean
9592
get() = elementType == COMMA

src/main/kotlin/org/rust/ide/inspections/fixes/RemoveTypeArguments.kt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ package org.rust.ide.inspections.fixes
88
import com.intellij.codeInspection.LocalQuickFix
99
import com.intellij.codeInspection.ProblemDescriptor
1010
import com.intellij.openapi.project.Project
11-
import org.rust.lang.core.psi.*
11+
import org.rust.ide.inspections.getTypeArgumentsAndDeclaration
12+
import org.rust.lang.core.psi.RsTypeArgumentList
1213
import org.rust.lang.core.psi.ext.RsElement
1314
import org.rust.lang.core.psi.ext.deleteWithSurroundingComma
1415
import org.rust.lang.core.psi.ext.getNextNonCommentSibling
@@ -22,15 +23,8 @@ class RemoveTypeArguments(
2223

2324
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
2425
val element = descriptor.psiElement as? RsElement ?: return
25-
when (element) {
26-
is RsBaseType ->
27-
element.path?.typeArgumentList
28-
is RsMethodCall ->
29-
element.typeArgumentList
30-
is RsCallExpr ->
31-
(element.expr as? RsPathExpr)?.path?.typeArgumentList
32-
else -> null
33-
}?.removeTypeParameters() ?: return
26+
val (typeArguments) = getTypeArgumentsAndDeclaration(element) ?: return
27+
typeArguments?.removeTypeParameters()
3428
}
3529

3630
private fun RsTypeArgumentList.removeTypeParameters() {

src/main/kotlin/org/rust/ide/intentions/AddImplTraitIntention.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class AddImplTraitIntention : RsElementBaseIntentionAction<AddImplTraitIntention
6464
val trait = traitRef.resolveToBoundTrait() ?: return
6565

6666
val insertedTypeArgumentsPtr = if (trait.element.requiredGenericParameters.isNotEmpty()) {
67-
insertTypeArgumentsIfNeeded(traitRef)?.map { it.createSmartPointer() }
67+
insertTypeArgumentsIfNeeded(traitRef.path)?.map { it.createSmartPointer() }
6868
} else {
6969
null
7070
}

src/main/kotlin/org/rust/lang/core/psi/ext/RsMethodCall.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,18 @@ package org.rust.lang.core.psi.ext
88
import com.intellij.lang.ASTNode
99
import com.intellij.openapi.util.TextRange
1010
import com.intellij.psi.PsiElement
11-
import org.rust.lang.core.psi.RsExpr
1211
import org.rust.lang.core.psi.RsLifetime
1312
import org.rust.lang.core.psi.RsMethodCall
1413
import org.rust.lang.core.psi.RsTypeReference
1514
import org.rust.lang.core.resolve.ref.RsMethodCallReferenceImpl
1615
import org.rust.lang.core.resolve.ref.RsReference
1716

1817
@Suppress("unused")
19-
val RsMethodCall.lifetimeArguments: List<RsLifetime> get() = typeArgumentList?.lifetimeList.orEmpty()
18+
val RsMethodCall.lifetimeArguments: List<RsLifetime> get() = typeArgumentList?.lifetimeArguments.orEmpty()
2019

21-
val RsMethodCall.typeArguments: List<RsTypeReference> get() = typeArgumentList?.typeReferenceList.orEmpty()
20+
val RsMethodCall.typeArguments: List<RsTypeReference> get() = typeArgumentList?.typeArguments.orEmpty()
2221

23-
val RsMethodCall.constArguments: List<RsExpr> get() = typeArgumentList?.exprList.orEmpty()
22+
val RsMethodCall.constArguments: List<RsElement> get() = typeArgumentList?.constArguments.orEmpty()
2423

2524
val RsMethodCall.textRangeWithoutValueArguments: TextRange
2625
get() = TextRange(startOffset, typeArgumentList?.endOffset ?: identifier.endOffset)

src/main/kotlin/org/rust/lang/core/psi/ext/RsPath.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@ enum class PathResolveStatus {
105105
RESOLVED, UNRESOLVED, NO_REFERENCE
106106
}
107107

108-
val RsPath.lifetimeArguments: List<RsLifetime> get() = typeArgumentList?.lifetimeList.orEmpty()
108+
val RsPath.lifetimeArguments: List<RsLifetime> get() = typeArgumentList?.lifetimeArguments.orEmpty()
109109

110-
val RsPath.typeArguments: List<RsTypeReference> get() = typeArgumentList?.typeReferenceList.orEmpty()
110+
val RsPath.typeArguments: List<RsTypeReference> get() = typeArgumentList?.typeArguments.orEmpty()
111111

112-
val RsPath.constArguments: List<RsExpr> get() = typeArgumentList?.exprList.orEmpty()
112+
val RsPath.constArguments: List<RsElement> get() = typeArgumentList?.constArguments.orEmpty()
113113

114114
abstract class RsPathImplMixin : RsStubbedElementImpl<RsPathStub>,
115115
RsPath {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Use of this source code is governed by the MIT license that can be
3+
* found in the LICENSE file.
4+
*/
5+
6+
package org.rust.lang.core.psi.ext
7+
8+
import org.rust.lang.core.psi.*
9+
10+
val RsTypeArgumentList.lifetimeArguments: List<RsLifetime> get() = lifetimeList
11+
12+
val RsTypeArgumentList.typeArguments: List<RsTypeReference>
13+
get() = typeReferenceList.filter { ref ->
14+
val type = ref as? RsBaseType
15+
val element = type?.path?.reference?.resolve()
16+
element !is RsConstant && element !is RsConstParameter
17+
}
18+
19+
val RsTypeArgumentList.constArguments: List<RsElement>
20+
get() {
21+
val typeArguments = typeArguments
22+
return stubChildrenOfType<RsElement>().filter {
23+
it is RsExpr || it is RsTypeReference && it !in typeArguments
24+
}
25+
}

src/main/kotlin/org/rust/lang/core/types/RsPsiSubstitution.kt

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@ package org.rust.lang.core.types
77

88
import org.rust.lang.core.psi.*
99
import org.rust.lang.core.psi.ext.RsElement
10-
import org.rust.lang.core.psi.ext.isConst
1110
import org.rust.lang.core.types.consts.CtConstParameter
1211
import org.rust.lang.core.types.consts.CtUnknown
1312
import org.rust.lang.core.types.infer.resolve
1413
import org.rust.lang.core.types.infer.substitute
1514
import org.rust.lang.core.types.regions.ReEarlyBound
1615
import org.rust.lang.core.types.ty.*
1716
import org.rust.lang.utils.evaluation.PathExprResolver
18-
import org.rust.lang.utils.evaluation.evaluate
17+
import org.rust.lang.utils.evaluation.toConst
1918

2019
/** Similar to [Substitution], but maps PSI to PSI instead of [Ty] to [Ty] */
2120
open class RsPsiSubstitution(
@@ -78,26 +77,12 @@ fun RsPsiSubstitution.toSubst(resolver: PathExprResolver? = PathExprResolver.def
7877
RsPsiSubstitution.Value.OptionalAbsent -> param
7978
RsPsiSubstitution.Value.RequiredAbsent -> CtUnknown
8079
is RsPsiSubstitution.Value.Present -> {
81-
val expectedTy = param.parameter.typeReference?.type ?: TyUnknown
82-
when (val value = psiValue.value) {
83-
is RsExpr -> value.evaluate(expectedTy, resolver) // TODO check types
84-
is RsBaseType -> when (val resolved = value.path?.reference?.resolve()) {
85-
is RsConstParameter -> CtConstParameter(resolved)
86-
is RsConstant -> when {
87-
resolved.isConst -> {
88-
// TODO check types
89-
val type = resolved.typeReference?.type ?: TyUnknown
90-
resolved.expr?.evaluate(type, resolver) ?: CtUnknown
91-
}
92-
else -> CtUnknown
93-
}
94-
else -> CtUnknown
95-
}
96-
else -> CtUnknown
97-
}
80+
val expectedTy = psiParam.typeReference?.type ?: TyUnknown
81+
psiValue.value.toConst(expectedTy, resolver)
9882
}
9983
is RsPsiSubstitution.Value.DefaultValue -> {
100-
psiValue.value.evaluate(psiParam.typeReference?.type ?: TyUnknown)
84+
val expectedTy = psiParam.typeReference?.type ?: TyUnknown
85+
psiValue.value.toConst(expectedTy, resolver)
10186
}
10287
}
10388

0 commit comments

Comments
 (0)