Skip to content

Commit

Permalink
COMP: Substitute types in completion variants
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikhail Chernyavsky committed Aug 2, 2019
1 parent da4207b commit 989536c
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 86 deletions.
69 changes: 43 additions & 26 deletions src/main/kotlin/org/rust/ide/presentation/RsPsiRendering.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,25 @@ package org.rust.ide.presentation

import org.rust.lang.core.psi.*
import org.rust.lang.core.psi.ext.*
import org.rust.lang.core.types.Substitution
import org.rust.lang.core.types.emptySubstitution
import org.rust.lang.core.types.ty.TyTypeParameter
import org.rust.lang.core.types.type
import org.rust.stdext.joinToWithBuffer

/** Return text of the element without switching to AST (loses non-stubbed parts of PSI) */
val RsTypeReference.stubOnlyText: String
get() = renderTypeReference(this)
fun RsTypeReference.getStubOnlyText(subst: Substitution = emptySubstitution): String =
renderTypeReference(this, subst)

/** Return text of the element without switching to AST (loses non-stubbed parts of PSI) */
val RsValueParameterList.stubOnlyText: String
get() = renderValueParameterList(this)
fun RsValueParameterList.getStubOnlyText(subst: Substitution = emptySubstitution): String =
renderValueParameterList(this, subst)

private fun renderValueParameterList(list: RsValueParameterList): String {
/** Return text of the element without switching to AST (loses non-stubbed parts of PSI) */
fun RsTraitRef.getStubOnlyText(subst: Substitution = emptySubstitution): String =
buildString { appendPath(path, subst) }

private fun renderValueParameterList(list: RsValueParameterList, subst: Substitution): String {
return buildString {
append("(")
val selfParameter = list.selfParameter
Expand All @@ -26,7 +34,7 @@ private fun renderValueParameterList(list: RsValueParameterList): String {
val typeReference = selfParameter.typeReference
if (typeReference != null) {
append("self: ")
appendTypeReference(typeReference)
appendTypeReference(typeReference, subst)
} else {
if (selfParameter.isRef) {
append("&")
Expand All @@ -45,24 +53,33 @@ private fun renderValueParameterList(list: RsValueParameterList): String {
valueParameterList.joinToWithBuffer(this, separator = ", ") { sb ->
sb.append(patText ?: "_")
sb.append(": ")
typeReference?.let { sb.appendTypeReference(it) }
typeReference?.let { sb.appendTypeReference(it, subst) }
}
append(")")
}
}

private fun renderTypeReference(ref: RsTypeReference): String =
buildString { appendTypeReference(ref) }
private fun renderTypeReference(ref: RsTypeReference, subst: Substitution): String =
buildString { appendTypeReference(ref, subst) }

private fun renderTraitRef(ref: RsTraitRef, subst: Substitution): String =
buildString { appendPath(ref.path, subst) }

private fun StringBuilder.appendTypeReference(ref: RsTypeReference, subst: Substitution) {
val ty = ref.type
if (ty is TyTypeParameter && subst[ty] != null) {
append(ref.substAndGetText(subst))
return
}

private fun StringBuilder.appendTypeReference(ref: RsTypeReference) {
when (val type = ref.typeElement) {
is RsTupleType -> type.typeReferenceList.joinToWithBuffer(this, ", ", "(", ")") { it.appendTypeReference(this) }
is RsTupleType -> type.typeReferenceList.joinToWithBuffer(this, ", ", "(", ")") { it.appendTypeReference(this, subst) }

is RsBaseType -> when (val kind = type.kind) {
RsBaseTypeKind.Unit -> append("()")
RsBaseTypeKind.Never -> append("!")
RsBaseTypeKind.Underscore -> append("_")
is RsBaseTypeKind.Path -> appendPath(kind.path)
is RsBaseTypeKind.Path -> appendPath(kind.path, subst)
}

is RsRefLikeType -> {
Expand All @@ -76,12 +93,12 @@ private fun StringBuilder.appendTypeReference(ref: RsTypeReference) {
}
if (type.mutability.isMut) append("mut ")
}
appendTypeReference(type.typeReference)
appendTypeReference(type.typeReference, subst)
}

is RsArrayType -> {
append("[")
appendTypeReference(type.typeReference)
appendTypeReference(type.typeReference, subst)
if (!type.isSlice) {
append("; ")
append(type.arraySize) // may trigger resolve
Expand All @@ -91,8 +108,8 @@ private fun StringBuilder.appendTypeReference(ref: RsTypeReference) {

is RsFnPointerType -> {
append("fn")
appendValueParameterListTypes(type.valueParameterList.valueParameterList)
appendRetType(type.retType)
appendValueParameterListTypes(type.valueParameterList.valueParameterList, subst)
appendRetType(type.retType, subst)
}

is RsTraitType -> {
Expand All @@ -112,14 +129,14 @@ private fun StringBuilder.appendTypeReference(ref: RsTypeReference) {
if (lifetime != null) {
sb.append(lifetime.referenceName)
} else {
bound.traitRef?.path?.let { sb.appendPath(it) }
bound.traitRef?.path?.let { sb.appendPath(it, subst) }
}
}
}
}
}

private fun StringBuilder.appendPath(path: RsPath) {
private fun StringBuilder.appendPath(path: RsPath, subst: Substitution) {
append(path.referenceName)
val inAngles = path.typeArgumentList // Foo<...>
val fnSugar = path.valueParameterList // &dyn FnOnce(...) -> i32
Expand All @@ -135,33 +152,33 @@ private fun StringBuilder.appendPath(path: RsPath) {
}
}
if (typeReferenceList.isNotEmpty()) {
typeReferenceList.joinToWithBuffer(this, ", ") { it.appendTypeReference(this) }
typeReferenceList.joinToWithBuffer(this, ", ") { it.appendTypeReference(this, subst) }
if (assocTypeBindingList.isNotEmpty()) {
append(", ")
}
}
assocTypeBindingList.joinToWithBuffer(this, ", ") { sb ->
sb.append(referenceName)
sb.append("=")
typeReference?.let { sb.appendTypeReference(it) }
typeReference?.let { sb.appendTypeReference(it, subst) }
}
append(">")
} else if (fnSugar != null) {
appendValueParameterListTypes(fnSugar.valueParameterList)
appendRetType(path.retType)
appendValueParameterListTypes(fnSugar.valueParameterList, subst)
appendRetType(path.retType, subst)
}
}

private fun StringBuilder.appendRetType(retType: RsRetType?) {
private fun StringBuilder.appendRetType(retType: RsRetType?, subst: Substitution) {
val retTypeRef = retType?.typeReference
if (retTypeRef != null) {
append(" -> ")
appendTypeReference(retTypeRef)
appendTypeReference(retTypeRef, subst)
}
}

private fun StringBuilder.appendValueParameterListTypes(list: List<RsValueParameter>) {
private fun StringBuilder.appendValueParameterListTypes(list: List<RsValueParameter>, subst: Substitution) {
list.joinToWithBuffer(this, separator = ", ", prefix = "(", postfix = ")") { sb ->
typeReference?.let { sb.appendTypeReference(it) }
typeReference?.let { sb.appendTypeReference(it, subst) }
}
}
4 changes: 2 additions & 2 deletions src/main/kotlin/org/rust/ide/utils/CallInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package org.rust.ide.utils

import org.rust.ide.presentation.stubOnlyText
import org.rust.ide.presentation.getStubOnlyText
import org.rust.lang.core.psi.RsCallExpr
import org.rust.lang.core.psi.RsFunction
import org.rust.lang.core.psi.RsMethodCall
Expand Down Expand Up @@ -45,7 +45,7 @@ class CallInfo private constructor(
append("self")
}
},
fn.valueParameters.map { Parameter(it.patText ?: "_", it.typeReference?.stubOnlyText ?: "?") }
fn.valueParameters.map { Parameter(it.patText ?: "_", it.typeReference?.getStubOnlyText() ?: "?") }
)

private constructor(fn: TyFunction) : this(
Expand Down
69 changes: 46 additions & 23 deletions src/main/kotlin/org/rust/lang/core/completion/LookupElements.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ import com.intellij.openapi.editor.EditorModificationUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.rust.ide.icons.RsIcons
import org.rust.ide.presentation.stubOnlyText
import org.rust.ide.presentation.getStubOnlyText
import org.rust.ide.refactoring.RsNamesValidator
import org.rust.lang.core.psi.*
import org.rust.lang.core.psi.ext.*
import org.rust.lang.core.resolve.AssocItemScopeEntryBase
import org.rust.lang.core.resolve.ImplLookup
import org.rust.lang.core.resolve.ScopeEntry
import org.rust.lang.core.resolve.ref.FieldResolveVariant
import org.rust.lang.core.types.Substitution
import org.rust.lang.core.types.emptySubstitution
import org.rust.lang.core.types.implLookup
import org.rust.lang.core.types.infer.TypeFolder
import org.rust.lang.core.types.ty.Ty
Expand All @@ -42,14 +47,13 @@ private const val EXPECTED_TYPE_PRIORITY_OFFSET = 20.0
private const val LOCAL_PRIORITY_OFFSET = 10.0

fun createLookupElement(
element: RsElement,
scopeName: String,
scopeEntry: ScopeEntry,
context: RsCompletionContext,
locationString: String? = null,
forSimplePath: Boolean = false,
expectedTy: Ty? = null,
insertHandler: InsertHandler<LookupElement> = RsDefaultInsertHandler()
): LookupElement {
val base = element.getLookupElementBuilder(scopeName)
val element = checkNotNull(scopeEntry.element) { "Invalid scope entry" }
val base = element.getLookupElementBuilder(scopeEntry.name, getSubstitution(scopeEntry))
.withInsertHandler(insertHandler)
.let { if (locationString != null) it.appendTailText(" ($locationString)", true) else it }

Expand All @@ -63,18 +67,36 @@ fun createLookupElement(
else -> DEFAULT_PRIORITY
}

if (forSimplePath && !element.canBeExported) {
if (context.isSimplePath && !element.canBeExported) {
// It's visible and can't be exported = it's local
priority += LOCAL_PRIORITY_OFFSET
}

if (isCompatibleTypes(element.implLookup, element.asTy, expectedTy)) {
if (isCompatibleTypes(element.implLookup, element.asTy, context.expectedTy)) {
priority += EXPECTED_TYPE_PRIORITY_OFFSET
}

return base.withPriority(priority)
}

private fun getSubstitution(scopeEntry: ScopeEntry): Substitution =
when (scopeEntry) {
is AssocItemScopeEntryBase<*> -> {
var subst = scopeEntry.selfTy.typeParameterValues
val implSubst = scopeEntry.source.impl?.typeReference?.type?.typeParameterValues
if (implSubst != null) {
subst = subst.substituteInKeys(implSubst)
}
val traitSubst = scopeEntry.source.implementedTrait?.subst
if (traitSubst != null) {
subst = traitSubst.substituteInValues(subst) + subst
}
subst
}
is FieldResolveVariant -> scopeEntry.selfTy.typeParameterValues
else -> emptySubstitution
}

private val RsElement.asTy: Ty?
get() = when (this) {
is RsConstant -> typeReference?.type
Expand All @@ -90,7 +112,7 @@ private val RsElement.asTy: Ty?
fun LookupElementBuilder.withPriority(priority: Double): LookupElement =
if (priority == DEFAULT_PRIORITY) this else PrioritizedLookupElement.withPriority(this, priority)

private fun RsElement.getLookupElementBuilder(scopeName: String): LookupElementBuilder {
private fun RsElement.getLookupElementBuilder(scopeName: String, subst: Substitution): LookupElementBuilder {
val base = LookupElementBuilder.createWithSmartPointer(scopeName, this)
.withIcon(if (this is RsFile) RsIcons.MODULE else this.getIcon(0))
.withStrikeoutness(this is RsDocAndAttributeOwner && queryAttributes.deprecatedAttribute != null)
Expand All @@ -103,24 +125,24 @@ private fun RsElement.getLookupElementBuilder(scopeName: String): LookupElementB
}

is RsConstant -> base
.withTypeText(typeReference?.stubOnlyText)
.withTypeText(typeReference?.getStubOnlyText(subst))
is RsConstParameter -> base
.withTypeText(typeReference?.stubOnlyText)
.withTypeText(typeReference?.getStubOnlyText(subst))
is RsFieldDecl -> base
.withTypeText(typeReference?.stubOnlyText)
.withTypeText(typeReference?.getStubOnlyText(subst))
is RsTraitItem -> base

is RsFunction -> base
.withTypeText(retType?.typeReference?.stubOnlyText ?: "()")
.withTailText(valueParameterList?.stubOnlyText ?: "()")
.appendTailText(extraTailText, true)
.withTypeText(retType?.typeReference?.getStubOnlyText(subst) ?: "()")
.withTailText(valueParameterList?.getStubOnlyText(subst) ?: "()")
.appendTailText(getExtraTailText(subst), true)

is RsStructItem -> base
.withTailText(getFieldsOwnerTailText(this))
.withTailText(getFieldsOwnerTailText(this, subst))

is RsEnumVariant -> base
.withTypeText(ancestorStrict<RsEnumItem>()?.name ?: "")
.withTailText(getFieldsOwnerTailText(this))
.withTypeText(stubAncestorStrict<RsEnumItem>()?.name ?: "")
.withTailText(getFieldsOwnerTailText(this, subst))

is RsPatBinding -> base
.withTypeText(type.let {
Expand All @@ -138,10 +160,10 @@ private fun RsElement.getLookupElementBuilder(scopeName: String): LookupElementB
}
}

private fun getFieldsOwnerTailText(owner: RsFieldsOwner): String = when {
private fun getFieldsOwnerTailText(owner: RsFieldsOwner, subst: Substitution): String = when {
owner.blockFields != null -> " { ... }"
owner.tupleFields != null ->
owner.positionalFields.joinToString(prefix = "(", postfix = ")") { it.typeReference.stubOnlyText }
owner.positionalFields.joinToString(prefix = "(", postfix = ")") { it.typeReference.getStubOnlyText(subst) }
else -> ""
}

Expand Down Expand Up @@ -251,9 +273,10 @@ private val InsertionContext.alreadyHasCallParens: Boolean
private val InsertionContext.alreadyHasStructBraces: Boolean
get() = nextCharIs('{')

private val RsFunction.extraTailText: String
get() = ancestorStrict<RsImplItem>()?.traitRef?.text?.let { " of $it" } ?: ""

private fun RsFunction.getExtraTailText(subst: Substitution): String {
val traitRef = stubAncestorStrict<RsImplItem>()?.traitRef ?: return ""
return " of ${traitRef.getStubOnlyText(subst)}"
}

fun InsertionContext.nextCharIs(c: Char): Boolean =
document.charsSequence.indexOfSkippingSpace(c, tailOffset) != null
Expand Down

0 comments on commit 989536c

Please sign in to comment.