From 5dcadcb498ea5692a80a6ccc58ca6faf68708ccc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 15 May 2016 04:05:46 +0300 Subject: [PATCH] Add custom refactoring Will help to fix [cargo-issue] [cargo-issue]: https://github.com/rust-lang/cargo/pull/2693 --- .../ide/inspections/CargoTestInspection.kt | 69 +++++++++++++++++++ .../org/rust/lang/core/grammar/rust.bnf | 7 +- src/main/resources/META-INF/plugin.xml | 2 + 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/org/rust/ide/inspections/CargoTestInspection.kt diff --git a/src/main/kotlin/org/rust/ide/inspections/CargoTestInspection.kt b/src/main/kotlin/org/rust/ide/inspections/CargoTestInspection.kt new file mode 100644 index 00000000000..b883c645406 --- /dev/null +++ b/src/main/kotlin/org/rust/ide/inspections/CargoTestInspection.kt @@ -0,0 +1,69 @@ +package org.rust.ide.inspections + +import com.intellij.codeInspection.LocalQuickFixBase +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import org.rust.lang.core.psi.* +import org.rust.lang.core.psi.util.childOfType + +val macroRe = """^\[\w+\].*""".toRegex() + +class CargoTestInspection : RustLocalInspectionTool() { + override fun getDisplayName(): String = "Fix Cargo test" + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return object : RustVisitor() { + override fun visitMethodCallExpr(o: RustMethodCallExpr) { + if (o.identifier?.text != "with_stdout") return + val firstArg = o.argList?.exprList?.firstOrNull() ?: return + val stringLit = firstArg.childOfType(strict = false) ?: return + if (stringLit.text.lines().any { it.matches(macroRe) }) { + holder.registerProblem(o, "Status is printed to stdout", WithStdout2WithStderr(o, stringLit)) + + } + + } + } + } + + class WithStdout2WithStderr(val o: RustMethodCallExpr, val stringLit: PsiElement) : LocalQuickFixBase("Fix Cargo test") { + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val (stderr, stdout) = stringLit.text.trim('"').lines().partition { it.matches(macroRe) } + val stderrText = "\\\n" + stderr.joinToString("\n") + val stderrLiteral = stringLiteral(stderrText) + var stdoutText = stdout.joinToString("\n") + if (stdoutText.startsWith("\\\n\n")) { + stdoutText = stdoutText.drop(2) + } + val stdoutLiteral = stringLiteral(stdoutText) + stringLit.replace(stderrLiteral) + o.identifier!!.replace(identifier("with_stderr")) + + val newExpr = methodCall(o, "with_stdout", stdoutLiteral) + o.replace(newExpr) + } + + private fun identifier(text: String): PsiElement { + val fileText = "struct $text;" + return elementFromText(fileText).identifier + } + + private fun methodCall(o: RustMethodCallExpr, methodName: String, arg: PsiElement): PsiElement { + val fileText = "fn main() { ${o.text}.$methodName(${arg.text})}}" + return elementFromText(fileText) + } + + private fun stringLiteral(text: String): PsiElement { + return elementFromText("fn main() { \"$text\" }") + } + + private inline fun elementFromText(text: String): T { + val file = RustElementFactory.createFileFromText(o.project, text) + return file!!.childOfType()!! + } + } + +} diff --git a/src/main/kotlin/org/rust/lang/core/grammar/rust.bnf b/src/main/kotlin/org/rust/lang/core/grammar/rust.bnf index e97f3bdc663..c4df68dd410 100644 --- a/src/main/kotlin/org/rust/lang/core/grammar/rust.bnf +++ b/src/main/kotlin/org/rust/lang/core/grammar/rust.bnf @@ -1009,6 +1009,7 @@ item_like_macro ::= macro_invocation item_macro_arg try_macro ::= try_macro_invocation try_macro_args { pin = 1 } format_like_macro ::= format_like_macro_invocation format_macro_args { pin = 1 } +cargo_test_macro ::= cargo_test_macro_invocation cargo_test_macro_args { pin = 1 } macro_definition ::= macro_rules_invocation IDENTIFIER item_macro_arg { pin = 1 @@ -1019,7 +1020,7 @@ private build_in_macro ::= try_macro | format_like_macro private zz_macro_call ::= build_in_macro | expr_like_macro { pin(".*") = macro_invocation } -private zz_macro_item ::= macro_definition | item_like_macro { pin(".*") = macro_invocation } +private zz_macro_item ::= cargo_test_macro ';' | macro_definition | item_like_macro { pin(".*") = macro_invocation } // Invocations macro_invocation ::= IDENTIFIER '!' @@ -1041,6 +1042,8 @@ format_like_macro_invocation ::= ( "format" | "trace" | "warn" ) '!' +cargo_test_macro_invocation ::= "test" '!' + // Arguments macro_arg ::= <> @@ -1056,6 +1059,8 @@ try_macro_args ::= <> format_macro_args ::= <> ] >> format_macro_arg ::= [ IDENTIFIER '=' ] any_expr +cargo_test_macro_args ::= <> + private meta macro_args_delim ::= '(' <> ')' | '{' <> '}' | '[' <> ']' { pin = 1 } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index bd1fb8bcc8a..88a4c424bc9 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -115,6 +115,8 @@ +