Skip to content

Commit

Permalink
Merge pull request #143 from pontem-network/colon-colon-type-args
Browse files Browse the repository at this point in the history
Receiver style functions require `::<>` type args
  • Loading branch information
mkurnikov authored May 1, 2024
2 parents 56fb59b + deb76fa commit 96322a0
Show file tree
Hide file tree
Showing 14 changed files with 329 additions and 217 deletions.
11 changes: 9 additions & 2 deletions src/main/grammars/MoveParser.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -596,9 +596,15 @@ private TypeParamBound_items ::= Ability ( '+' Ability )*
}
private TypeParamBound_items_recover ::= !('>' | ',')

TypeArgumentList ::= '<' !'=' <<non_empty_comma_sep_items (TypeArgument &(','|'>'))>>? '>' {
TypeArgumentList ::= TypeArgumentListImpl {
name = "type arguments"
}
ColonTypeArgumentList ::= &'::' TypeArgumentListImpl { elementType = TypeArgumentList }

//private TypeArgumentListImpl ::= '::'? '<' !'=' TypeArgumentImpl* '>' { pin = 3 }
//private TypeArgumentImpl ::= !'>' TypeArgument (',' | &'>') { pin = 2 }
private TypeArgumentListImpl ::= '::'? '<' !'=' <<non_empty_comma_sep_items ( TypeArgument &(',' | '>') )>>? '>'

TypeArgument ::= Type

///////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -989,7 +995,7 @@ StructDotField ::= IDENTIFIER !('(' | '::' | '!' | '{')
implements = ["org.move.lang.core.psi.ext.MvMethodOrField"]
mixin = "org.move.lang.core.psi.ext.MvStructDotFieldMixin"
}
MethodCall ::= IDENTIFIER TypeArgumentList? ValueArgumentList
MethodCall ::= IDENTIFIER ColonTypeArgumentList? ValueArgumentList
{
implements = [
"org.move.lang.core.psi.ext.MvMethodOrField"
Expand Down Expand Up @@ -1530,3 +1536,4 @@ private lt_eqeq_gt ::= <<lteqeqgtImpl>>
private dotdot ::= <<dotdotImpl>>

private meta non_empty_comma_sep_items ::= <<param>> ( ',' <<param>> )* ','?
private meta list_element ::= !'>' <<param>> (',' | &'>') { pin = 2 }
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import org.move.ide.inspections.DiagnosticFix
import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.typeArguments
import org.move.lang.core.psi.ext.valueArguments
import org.move.stdext.notEmptyOrLet

class ReplaceWithMethodCallFix(callExpr: MvCallExpr): DiagnosticFix<MvCallExpr>(callExpr) {
override fun getText(): String = "Replace with method call"
Expand Down Expand Up @@ -38,12 +40,18 @@ class ReplaceWithMethodCallFix(callExpr: MvCallExpr): DiagnosticFix<MvCallExpr>(
}
}

val dotExpr = psiFactory.expr<MvDotExpr>("1.${element.path.referenceName}<T>()")
val fakeTypeArgs =
element.path.typeArguments.map { "T" }.toList()
.notEmptyOrLet { listOf("T") }.joinToString(", ")
val dotExpr = psiFactory.expr<MvDotExpr>("1.${element.path.referenceName}::<$fakeTypeArgs>()")
dotExpr.expr.replace(selfArgExpr)

val typeArgumentList = element.path.typeArgumentList
if (typeArgumentList != null) {
dotExpr.methodCall?.typeArgumentList?.replace(typeArgumentList)
val dotExprList = dotExpr.methodCall?.typeArgumentList?.typeArgumentList!!
for ((dotExprTypeArgument, typeArgument) in dotExprList.zip(typeArgumentList.typeArgumentList)) {
dotExprTypeArgument.replace(typeArgument)
}
} else {
dotExpr.methodCall?.typeArgumentList?.delete()
}
Expand Down
60 changes: 39 additions & 21 deletions src/main/kotlin/org/move/lang/core/completion/LookupElements.kt
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ fun InsertionContext.addSuffix(suffix: String) {
EditorModificationUtil.moveCaretRelatively(editor, suffix.length)
}

val InsertionContext.hasCallParens: Boolean
val InsertionContext.alreadyHasCallParens: Boolean
get() = nextCharIs('(')

val InsertionContext.alreadyHasColonColon: Boolean
Expand All @@ -166,7 +166,7 @@ val InsertionContext.alreadyHasColonColon: Boolean
val InsertionContext.alreadyHasSpace: Boolean
get() = nextCharIs(' ')

val InsertionContext.hasAngleBrackets: Boolean
val InsertionContext.alreadyHasAngleBrackets: Boolean
get() = nextCharIs('<')

fun InsertionContext.nextCharIs(c: Char): Boolean =
Expand All @@ -192,7 +192,7 @@ class AngleBracketsInsertHandler: InsertHandler<LookupElement> {

override fun handleInsert(context: InsertionContext, item: LookupElement) {
val document = context.document
if (!context.hasAngleBrackets) {
if (!context.alreadyHasAngleBrackets) {
document.insertString(context.selectionEndOffset, "<>")
}
EditorModificationUtil.moveCaretRelatively(context.editor, 1)
Expand All @@ -212,30 +212,48 @@ open class DefaultInsertHandler(val completionCtx: CompletionContext? = null): I
item: LookupElement
) {
val document = context.document

when (element) {
is MvFunctionLike -> {
val requiresExplicitTypeArguments =
element.requiresExplicitlyProvidedTypeArguments(completionCtx)
var suffix = ""
if (!context.hasAngleBrackets && requiresExplicitTypeArguments) {
suffix += "<>"
}
if (!context.hasAngleBrackets && !context.hasCallParens) {
suffix += "()"
}
val isMethodCall = context.getElementOfType<MvMethodOrField>() != null
val fnParameters = if (isMethodCall) element.parameters.drop(1) else element.parameters
val caretShift = when {
requiresExplicitTypeArguments -> 1
fnParameters.isNotEmpty() -> 1
else -> 2
val requiresExplicitTypes =
element.requiresExplicitlyProvidedTypeArguments(completionCtx)
if (isMethodCall) {
var suffix = ""
if (requiresExplicitTypes && !context.alreadyHasColonColon) {
suffix += "::<>"
}
if (!context.alreadyHasColonColon && !context.alreadyHasCallParens) {
suffix += "()"
}
val caretShift = when {
context.alreadyHasColonColon || requiresExplicitTypes -> 3
// drop first for self
element.parameters.drop(1).isNotEmpty() -> 1
else -> 2
}
context.document.insertString(context.selectionEndOffset, suffix)
EditorModificationUtil.moveCaretRelatively(context.editor, caretShift)
} else {
var suffix = ""
if (requiresExplicitTypes && !context.alreadyHasAngleBrackets) {
suffix += "<>"
}
if (!context.alreadyHasAngleBrackets && !context.alreadyHasCallParens) {
suffix += "()"
}
val caretShift = when {
requiresExplicitTypes -> 1
element.parameters.isNotEmpty() -> 1
else -> 2
}
context.document.insertString(context.selectionEndOffset, suffix)
EditorModificationUtil.moveCaretRelatively(context.editor, caretShift)
}
context.document.insertString(context.selectionEndOffset, suffix)
EditorModificationUtil.moveCaretRelatively(context.editor, caretShift)
}
is MvSchema -> {
if (element.hasTypeParameters) {
if (!context.hasAngleBrackets) {
if (!context.alreadyHasAngleBrackets) {
document.insertString(context.selectionEndOffset, "<>")
}
EditorModificationUtil.moveCaretRelatively(context.editor, 1)
Expand All @@ -247,7 +265,7 @@ open class DefaultInsertHandler(val completionCtx: CompletionContext? = null): I
.findElementAt(context.startOffset)
?.ancestorOrSelf<MvAcquiresType>() != null
if (element.hasTypeParameters && !insideAcquiresType) {
if (!context.hasAngleBrackets) {
if (!context.alreadyHasAngleBrackets) {
document.insertString(context.selectionEndOffset, "<>")
}
EditorModificationUtil.moveCaretRelatively(context.editor, 1)
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/org/move/lang/core/psi/MvFunctionLike.kt
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ fun MvFunctionLike.requiresExplicitlyProvidedTypeArguments(completionContext: Co
val callTy = this.declaredType(msl).substitute(this.tyInfers) as TyFunction

val inferenceCtx = InferenceContext(msl)

callTy.paramTypes.forEach {
inferenceCtx.combineTypes(it, it.foldTyInferWith { TyUnknown })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class TypeParametersNumberErrorTest: AnnotatorTestCase(MvErrorAnnotator::class)
param
}
fun main(s: S<u8>) {
let b = s.receiver<error descr="Invalid instantiation of '0x1::main::receiver'. Expected 2 type argument(s) but got 1"><u8></error>(1);
let b = s.receiver<error descr="Invalid instantiation of '0x1::main::receiver'. Expected 2 type argument(s) but got 1">::<u8></error>(1);
}
}
""")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ class ReplaceWithMethodCallInspectionTest: InspectionTestBase(ReplaceWithMethodC
struct S<T> { field: u8 }
native fun get_type<U, T>(self: &S<U>): T;
fun main<T>(s: S<T>) {
s.get_type<T, u8>();
s.get_type::<T, u8>();
}
}
""")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,25 @@ module 0x1::M {
struct S { field: u8 }
fun receiver<Z>(self: &S): Z {}
fun main(s: S) {
s.receiver</*caret*/>();
s.receiver::</*caret*/>();
}
}
""")

fun `test receiver style function completion type annotation required with angle brackets present`() = doSingleCompletion("""
module 0x1::main {
struct S { field: u8 }
fun receiver<Z>(self: &S): Z {}
fun main(s: S) {
s.rece/*caret*/::<>()
}
}
""", """
module 0x1::main {
struct S { field: u8 }
fun receiver<Z>(self: &S): Z {}
fun main(s: S) {
s.receiver::</*caret*/>()
}
}
""")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class CompleteParsingTest : MvParsingTestCase("complete") {
// expressions
fun `test strings`() = doTest()
fun `test vectors`() = doTest()
fun `test dot expressions`() = doTest()
fun `test expressions`() = doTest()
fun `test expressions assignments`() = doTest()
fun `test expressions if else as`() = doTest()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module 0x1::dot_expressions {
fun dot() {
bin.field1.field2;
bin.field < 1;
bin.receiver_func();
bin.field.call();

bin.call::<T>();
bin.call::<T, U>();
vector<u8>[1].length::<u8>();
}
spec dot {
bin.field[1];
}
}
Loading

0 comments on commit 96322a0

Please sign in to comment.