Skip to content

Commit

Permalink
(FIX): add quick fix for unimplemented methods
Browse files Browse the repository at this point in the history
fixes #536
  • Loading branch information
matklad committed Jul 19, 2016
1 parent 50760f5 commit 1bb65c4
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 2 deletions.
38 changes: 36 additions & 2 deletions src/main/kotlin/org/rust/ide/annotator/RustItemsAnnotator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,12 @@ class RustItemsAnnotator : Annotator {

val notImplemented = mustImplement.keys - implemented.keys
if (!notImplemented.isEmpty()) {
val toImplement = trait.traitBody.traitMethodMemberList.filter { it.name in notImplemented }

holder.createErrorAnnotation(implHeaderTextRange,
"Not all trait items implemented, missing: `${notImplemented.first()}`")
"Not all trait items implemented, missing: `${notImplemented.first()}`"
).registerFix(ImplementMethods(impl, toImplement))

}

val notMembers = implemented.filterKeys { it !in canImplement }
Expand All @@ -86,7 +90,6 @@ private class AddModuleFile(

override fun getFamilyName(): String = text


override fun invoke(
project: Project,
file: PsiFile,
Expand All @@ -103,3 +106,34 @@ private class AddModuleFile(
}

}

private class ImplementMethods(
implBody: RustImplItemElement,
private val methods: List<RustTraitMethodMemberElement>
) : LocalQuickFixAndIntentionActionOnPsiElement(implBody) {
init {
check(methods.isNotEmpty())
}

override fun getText(): String = "Implement methods"

override fun getFamilyName(): String = text

override fun invoke(
project: Project,
file: PsiFile,
editor: Editor?,
startElement: PsiElement,
endElement: PsiElement
) {
val implBody = (startElement as RustImplItemElement).implBody ?: return

val templateImpl = RustElementFactory.createImplBody(project, methods) ?: return
val firstToAdd = templateImpl.implMethodMemberList.firstOrNull() ?: return
val lastToAdd = templateImpl.implMethodMemberList.lastOrNull() ?: return

val lastMethodOrBrace = implBody.implMethodMemberList.lastOrNull() ?: implBody.lbrace
implBody.addRangeAfter(firstToAdd, lastToAdd, lastMethodOrBrace)
}

}
26 changes: 26 additions & 0 deletions src/main/kotlin/org/rust/lang/core/psi/RustElementFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,34 @@ object RustElementFactory {
return createFromText(project, "fn main() { S { $fields }; }")
}

fun createImplBody(project: Project, traitMethods: List<RustTraitMethodMemberElement>): RustImplBodyElement? {
val m = traitMethods.first()
m.copy()
val methods = traitMethods
.mapNotNull { " ${it.signatureText} {\nunimplemented!()\n}" }
.joinToString("\n\n")
return createFromText(project, "impl T for S { $methods }")
}

private inline fun <reified T : RustCompositeElement> createFromText(project: Project, code: String): T? =
PsiFileFactory.getInstance(project)
.createFileFromText("DUMMY.rs", RustLanguage, code)
?.childOfType<T>()

private val RustTraitMethodMemberElement.signatureText: String? get() {
// We can't simply take a substring of original method declaration
// because of anonymous parameters.
val name = name ?: return null
val generics = genericParams?.text ?: ""

val parameters = parameters ?: return null
val allArguments = listOfNotNull(parameters.selfArgument?.text) + parameters.parameterList.map {
// fix possible anon parameter
"${it.pat?.text ?: "_"}: ${it.type?.text ?: "()"}"
}

val ret = retType?.text ?: ""
val where = whereClause?.text ?: ""
return "fn $name $generics (${allArguments.joinToString(",")}) $ret $where"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ class RustItemAnnotatorTest : RustAnnotatorTestBase() {
}

fun testInvalidTraitImpl() = doTest()
fun testInvalidTraitImplFix() = checkQuickFix("Implement methods")

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
trait T {
fn foo();
fn eggs();
fn bar() {}
fn baz<T>(x: i32, y: String) -> Vec<T> where T: Clone;
fn quux(&self) -> Self;
fn annon_arg(Arg);
}

impl T for<caret> () {
fn foo() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
trait T {
fn foo();
fn eggs();
fn bar() {}
fn baz<T>(x: i32, y: String) -> Vec<T> where T: Clone;
fn quux(&self) -> Self;
fn annon_arg(Arg);
}

impl T for () {
fn foo() {}
fn eggs() {
unimplemented!()
}

fn baz<T>(x: i32, y: String) -> Vec<T> where T: Clone {
unimplemented!()
}

fn quux(&self) -> Self {
unimplemented!()
}

fn annon_arg(_: Arg) {
unimplemented!()
}
}

0 comments on commit 1bb65c4

Please sign in to comment.