Skip to content

Commit

Permalink
Merge #7479
Browse files Browse the repository at this point in the history
7479: TY&RES: support const arguments that looks like type arguments r=mchernyavsky a=vlad20012

Related to #3985, fixes (partially, except `Extra error` case) #7420



Co-authored-by: vlad20012 <beskvlad@gmail.com>
  • Loading branch information
bors[bot] and vlad20012 committed Jul 23, 2021
2 parents 3f0d773 + a891b69 commit c5d9bbe
Show file tree
Hide file tree
Showing 10 changed files with 329 additions and 79 deletions.
19 changes: 12 additions & 7 deletions src/main/kotlin/org/rust/ide/annotator/RsErrorAnnotator.kt
Expand Up @@ -32,18 +32,17 @@ import org.rust.lang.core.macros.macroExpansionManager
import org.rust.lang.core.psi.*
import org.rust.lang.core.psi.RsElementTypes.IDENTIFIER
import org.rust.lang.core.psi.ext.*
import org.rust.lang.core.resolve.MACROS
import org.rust.lang.core.resolve.Namespace
import org.rust.lang.core.resolve.knownItems
import org.rust.lang.core.resolve.namespaces
import org.rust.lang.core.resolve.*
import org.rust.lang.core.resolve.ref.deepResolve
import org.rust.lang.core.types.*
import org.rust.lang.core.types.consts.asLong
import org.rust.lang.core.types.infer.containsTyOfClass
import org.rust.lang.core.types.infer.substitute
import org.rust.lang.core.types.ty.*
import org.rust.lang.utils.*
import org.rust.lang.utils.RsDiagnostic
import org.rust.lang.utils.RsDiagnostic.IncorrectFunctionArgumentCountError.FunctionType
import org.rust.lang.utils.RsErrorCode
import org.rust.lang.utils.addToHolder
import org.rust.lang.utils.evaluation.evaluate

class RsErrorAnnotator : AnnotatorBase(), HighlightRangeExtension {
Expand Down Expand Up @@ -1271,7 +1270,7 @@ private fun checkDuplicates(
if (element.isCfgUnknown) return
val owner = if (scope is RsMembers) scope.parent else scope
val duplicates = holder.currentAnnotationSession.duplicatesByNamespace(scope, recursively)
val ns = element.namespaces.find { element in duplicates[it].orEmpty() }
val ns = element.namespacesForDuplicatesCheck.find { element in duplicates[it].orEmpty() }
?: return
val name = element.name!!

Expand Down Expand Up @@ -1359,6 +1358,12 @@ private fun PsiElement.nameOrImportedName(): String? =
else -> null
}

private val RsNamedElement.namespacesForDuplicatesCheck: Set<Namespace>
get() = when (this) {
is RsConstParameter -> TYPES_N_VALUES
else -> namespaces
}

private fun AnnotationSession.duplicatesByNamespace(
owner: PsiElement,
recursively: Boolean
Expand All @@ -1367,7 +1372,7 @@ private fun AnnotationSession.duplicatesByNamespace(

fun PsiElement.namespaced(): Sequence<Pair<Namespace, PsiElement>> =
when (this) {
is RsNamedElement -> namespaces
is RsNamedElement -> namespacesForDuplicatesCheck
is RsUseSpeck -> namespaces
else -> emptySet()
}.asSequence().map { Pair(it, this) }
Expand Down
5 changes: 4 additions & 1 deletion src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt
Expand Up @@ -610,7 +610,10 @@ open class PsiSubstitutingPsiRenderer(
}
is RsConstParameter -> when (val s = subst.constSubst[resolved]) {
is RsPsiSubstitution.Value.Present -> {
appendConstExpr(sb, s.value)
when (s.value) {
is RsExpr -> appendConstExpr(sb, s.value)
is RsTypeReference -> appendTypeReference(sb, s.value)
}
true
}
else -> false
Expand Down
11 changes: 10 additions & 1 deletion src/main/kotlin/org/rust/lang/core/psi/ext/RsPath.kt
Expand Up @@ -69,7 +69,16 @@ val RsPath.qualifier: RsPath?
}

fun RsPath.allowedNamespaces(isCompletion: Boolean = false): Set<Namespace> = when (val parent = parent) {
is RsPath, is RsTypeReference, is RsTraitRef, is RsStructLiteral, is RsPatStruct -> TYPES
is RsPath, is RsTraitRef, is RsStructLiteral, is RsPatStruct -> TYPES
is RsTypeReference -> when (parent.parent) {
is RsTypeArgumentList -> when {
// type A = Foo<T>
// ~ `T` can be either type or const argument
typeArgumentList == null && valueParameterList == null -> TYPES_N_VALUES
else -> TYPES
}
else -> TYPES
}
is RsUseSpeck -> when {
// use foo::bar::{self, baz};
// ~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/org/rust/lang/core/resolve/Namespace.kt
Expand Up @@ -38,6 +38,7 @@ val RsNamedElement.namespaces: Set<Namespace> get() = when (this) {
is RsTypeAlias -> TYPES

is RsPatBinding,
is RsConstParameter,
is RsConstant -> VALUES
is RsFunction -> if (this.isProcMacroDef) MACROS else VALUES

Expand Down
162 changes: 97 additions & 65 deletions src/main/kotlin/org/rust/lang/core/resolve/ref/RsPathReferenceImpl.kt
Expand Up @@ -11,16 +11,18 @@ import com.intellij.util.containers.map2Array
import org.rust.lang.core.psi.*
import org.rust.lang.core.psi.ext.*
import org.rust.lang.core.resolve.*
import org.rust.lang.core.types.*
import org.rust.lang.core.types.RsPsiSubstitution.TypeValue
import org.rust.lang.core.types.RsPsiSubstitution.Value
import org.rust.lang.core.types.*
import org.rust.lang.core.types.infer.ResolvedPath
import org.rust.lang.core.types.infer.foldTyInferWith
import org.rust.lang.core.types.infer.substitute
import org.rust.lang.core.types.ty.*
import org.rust.lang.core.types.ty.TyInfer
import org.rust.lang.core.types.ty.TyUnknown
import org.rust.lang.utils.evaluation.PathExprResolver
import org.rust.stdext.buildMap
import org.rust.stdext.intersects
import org.rust.stdext.mapNotNullToSet

class RsPathReferenceImpl(
element: RsPath
Expand Down Expand Up @@ -162,10 +164,29 @@ fun resolvePath(path: RsPath, lookup: ImplLookup? = null): List<BoundElementWith
processPathResolveVariants(lookup, path, false, it)
}

return when (result.size) {
// type A = Foo<T>
// ~ `T` can be either type or const argument.
// Prefer types if they are
val pathParent = path.parent
val result2 = if (pathParent is RsTypeReference && pathParent.parent is RsTypeArgumentList) {
when (result.size) {
0 -> emptyList()
1 -> result
else -> {
val withoutConstants = result.filter {
it.inner.element !is RsConstant && it.inner.element !is RsConstParameter
}
withoutConstants.ifEmpty { result }
}
}
} else {
result
}

return when (result2.size) {
0 -> emptyList()
1 -> listOf(result.single().map { instantiatePathGenerics(path, it) })
else -> result
1 -> listOf(result2.single().map { instantiatePathGenerics(path, it) })
else -> result2
}
}

Expand All @@ -181,54 +202,26 @@ fun <T : RsElement> instantiatePathGenerics(
return BoundElement(resolved.element, subst + newSubst, psiSubst.assoc.mapValues { it.value.type })
}

@Suppress("DuplicatedCode")
fun pathPsiSubst(path: RsPath, resolved: RsGenericDeclaration): RsPsiSubstitution {
val args = pathTypeParameters(path)

val typeArguments = when (args) {
is RsPsiPathParameters.InAngles -> args.args.map { TypeValue.Present.InAngles(it) }
is RsPsiPathParameters.FnSugar -> listOf(TypeValue.Present.FnSugar(args.inputArgs))
null -> null
}

val assocTypes = run {
if (resolved is RsTraitItem) {
when (args) {
// Iterator<Item=T>
is RsPsiPathParameters.InAngles -> buildMap {
args.assoc.forEach { binding ->
// We can't just use `binding.reference.resolve()` here because
// resolving of an assoc type depends on a parent path resolve,
// so we coming back here and entering the infinite recursion
resolveAssocTypeBinding(resolved, binding)?.let { assoc ->
binding.typeReference?.let { put(assoc, it) }
}

}
}
// Fn() -> T
is RsPsiPathParameters.FnSugar -> buildMap {
if (args.outputArg != null) {
val outputParam = path.knownItems.FnOnce?.findAssociatedType("Output")
if (outputParam != null) {
put(outputParam, args.outputArg)
}
}
}
null -> emptyMap()
}
} else {
emptyMap<RsTypeAlias, RsTypeReference>()
}
}

val parent = path.parent

// Generic arguments are optional in expression context, e.g.
// `let a = Foo::<u8>::bar::<u16>();` can be written as `let a = Foo::bar();`
// if it is possible to infer `u8` and `u16` during type inference
val areOptionalArgs = parent is RsExpr || parent is RsPath && parent.parent is RsExpr

val regionParameters = resolved.lifetimeParameters
val regionArguments = (args as? RsPsiPathParameters.InAngles)?.lifetimeArgs
val regionSubst = associateSubst(regionParameters, regionArguments, areOptionalArgs)

val typeArguments = when (args) {
is RsPsiPathParameters.InAngles -> args.typeOrConstArgs.filterIsInstance<RsTypeReference>().map { TypeValue.Present.InAngles(it) }
is RsPsiPathParameters.FnSugar -> listOf(TypeValue.Present.FnSugar(args.inputArgs))
null -> null
}

val typeSubst = resolved.typeParameters.withIndex().associate { (i, param) ->
val value = if (areOptionalArgs && typeArguments == null) {
// Args are optional and turbofish is not presend. E.g. `Vec::new()`
Expand Down Expand Up @@ -259,39 +252,70 @@ fun pathPsiSubst(path: RsPath, resolved: RsGenericDeclaration): RsPsiSubstitutio
param to value
}

val regionParameters = resolved.lifetimeParameters
val regionArguments = path.typeArgumentList?.lifetimeList
val regionSubst = regionParameters.withIndex().associate { (i, param) ->
val value = if (areOptionalArgs && regionArguments == null) {
Value.OptionalAbsent
} else if (regionArguments != null && i < regionArguments.size) {
Value.Present(regionArguments[i])
val usedTypeArguments = typeSubst.values.mapNotNullToSet { (it as? TypeValue.Present.InAngles)?.value }

val constParameters = resolved.constParameters
val constArguments = (args as? RsPsiPathParameters.InAngles)?.typeOrConstArgs
?.let { list -> list.filter { it !is RsTypeReference || it !in usedTypeArguments && it is RsBaseType} }
val constSubst = associateSubst(constParameters, constArguments, areOptionalArgs)

val assocTypes = run {
if (resolved is RsTraitItem) {
when (args) {
// Iterator<Item=T>
is RsPsiPathParameters.InAngles -> buildMap {
args.assoc.forEach { binding ->
// We can't just use `binding.reference.resolve()` here because
// resolving of an assoc type depends on a parent path resolve,
// so we coming back here and entering the infinite recursion
resolveAssocTypeBinding(resolved, binding)?.let { assoc ->
binding.typeReference?.let { put(assoc, it) }
}

}
}
// Fn() -> T
is RsPsiPathParameters.FnSugar -> buildMap {
if (args.outputArg != null) {
val outputParam = path.knownItems.FnOnce?.findAssociatedType("Output")
if (outputParam != null) {
put(outputParam, args.outputArg)
}
}
}
null -> emptyMap()
}
} else {
Value.RequiredAbsent
emptyMap<RsTypeAlias, RsTypeReference>()
}
param to value
}

val constParameters = resolved.constParameters
val constArguments = path.typeArgumentList?.exprList
val constSubst = constParameters.withIndex().associate { (i, param) ->
val value = if (areOptionalArgs && constArguments == null) {
return RsPsiSubstitution(typeSubst, regionSubst, constSubst, assocTypes)
}

private fun <Param, Arg> associateSubst(
parameters: List<Param>,
arguments: List<Arg>?,
areOptionalArgs: Boolean
): Map<Param, Value<Arg>> {
return parameters.withIndex().associate { (i, param) ->
val value = if (areOptionalArgs && arguments == null) {
Value.OptionalAbsent
} else if (constArguments != null && i < constArguments.size) {
Value.Present(constArguments[i])
} else if (arguments != null && i < arguments.size) {
Value.Present(arguments[i])
} else {
Value.RequiredAbsent
}
param to value
}

return RsPsiSubstitution(typeSubst, regionSubst, constSubst, assocTypes)
}

private sealed class RsPsiPathParameters {
/** Foo<Bar, Baz, Item=i32> */
/** `Foo<'a, Bar, Baz, 2+2, Item=i32>` */
class InAngles(
val args: List<RsTypeReference>,
val lifetimeArgs: List<RsLifetime>,
/** [RsTypeReference] or [RsExpr] */
val typeOrConstArgs: List<RsElement>,
val assoc: List<RsAssocTypeBinding>
) : RsPsiPathParameters()

Expand All @@ -307,9 +331,17 @@ private fun pathTypeParameters(path: RsPath): RsPsiPathParameters? {
val fnSugar = path.valueParameterList
return when {
inAngles != null -> {
val params = inAngles.typeReferenceList
val assoc = inAngles.assocTypeBindingList
RsPsiPathParameters.InAngles(params, assoc)
val typeOrConstArgs = mutableListOf<RsElement>()
val lifetimeArgs = mutableListOf<RsLifetime>()
val assoc = mutableListOf<RsAssocTypeBinding>()
for (child in inAngles.stubChildrenOfType<RsElement>()) {
when (child) {
is RsTypeReference, is RsExpr -> typeOrConstArgs.add(child as RsElement)
is RsLifetime -> lifetimeArgs += child
is RsAssocTypeBinding -> assoc += child
}
}
RsPsiPathParameters.InAngles(lifetimeArgs, typeOrConstArgs, assoc)
}
fnSugar != null -> {
RsPsiPathParameters.FnSugar(
Expand Down
21 changes: 19 additions & 2 deletions src/main/kotlin/org/rust/lang/core/types/RsPsiSubstitution.kt
Expand Up @@ -6,6 +6,8 @@
package org.rust.lang.core.types

import org.rust.lang.core.psi.*
import org.rust.lang.core.psi.ext.RsElement
import org.rust.lang.core.psi.ext.isConst
import org.rust.lang.core.types.consts.CtConstParameter
import org.rust.lang.core.types.consts.CtUnknown
import org.rust.lang.core.types.infer.resolve
Expand All @@ -22,7 +24,7 @@ import org.rust.lang.utils.evaluation.evaluate
open class RsPsiSubstitution(
val typeSubst: Map<RsTypeParameter, TypeValue> = emptyMap(),
val regionSubst: Map<RsLifetimeParameter, Value<RsLifetime>> = emptyMap(),
val constSubst: Map<RsConstParameter, Value<RsExpr>> = emptyMap(),
val constSubst: Map<RsConstParameter, Value<RsElement>> = emptyMap(),
val assoc: Map<RsTypeAlias, RsTypeReference> = emptyMap(),
) {
sealed class TypeValue {
Expand Down Expand Up @@ -76,7 +78,22 @@ fun RsPsiSubstitution.toSubst(resolver: PathExprResolver? = PathExprResolver.def
RsPsiSubstitution.Value.RequiredAbsent -> CtUnknown
is RsPsiSubstitution.Value.Present -> {
val expectedTy = param.parameter.typeReference?.type ?: TyUnknown
psiValue.value.evaluate(expectedTy, resolver)
when (val value = psiValue.value) {
is RsExpr -> value.evaluate(expectedTy, resolver)
is RsBaseType -> when (val resolved = value.path?.reference?.resolve()) {
is RsConstParameter -> CtConstParameter(resolved)
is RsConstant -> when {
resolved.isConst -> {
// TODO check types
val type = resolved.typeReference?.type ?: TyUnknown
resolved.expr?.evaluate(type, resolver) ?: CtUnknown
}
else -> CtUnknown
}
else -> CtUnknown
}
else -> CtUnknown
}
}
}

Expand Down
15 changes: 12 additions & 3 deletions src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt
Expand Up @@ -15,6 +15,7 @@ import org.rust.lang.core.psi.*
import org.rust.lang.core.psi.ext.*
import org.rust.lang.core.resolve.*
import org.rust.lang.core.resolve.ref.MethodResolveVariant
import org.rust.lang.core.resolve.ref.pathPsiSubst
import org.rust.lang.core.resolve.ref.resolvePathRaw
import org.rust.lang.core.types.*
import org.rust.lang.core.types.consts.*
Expand Down Expand Up @@ -188,9 +189,17 @@ class RsInferenceContext(
else -> null
}
val declaration = path?.let { resolvePathRaw(it, lookup) }?.singleOrNull()?.element as? RsGenericDeclaration
val constParameters = declaration?.constParameters.orEmpty()
val constArguments = path?.constArguments.orEmpty()
RsTypeInferenceWalker(this, TyUnknown).inferConstArgumentTypes(constParameters, constArguments)
if (path != null && declaration != null) {
val constParameters = mutableListOf<RsConstParameter>()
val constArguments = mutableListOf<RsExpr>()
for ((param, value) in pathPsiSubst(path, declaration).constSubst) {
if (value is RsPsiSubstitution.Value.Present && value.value is RsExpr) {
constParameters += param
constArguments += value.value
}
}
RsTypeInferenceWalker(this, TyUnknown).inferConstArgumentTypes(constParameters, constArguments)
}
}
else -> {
val (retTy, expr) = when (element) {
Expand Down

0 comments on commit c5d9bbe

Please sign in to comment.