/*
 * Use of this source code is governed by the MIT license that can be
 * found in the LICENSE file.
 */

package org.rust.ide.inspections

import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import org.rust.lang.core.psi.RsMacroCall
import org.rust.lang.core.psi.RsPsiFactory
import org.rust.lang.core.psi.RsTryExpr
import org.rust.lang.core.psi.RsVisitor
import org.rust.lang.core.psi.ext.macroName

/**
 * Change `try!` macro to `?` operator.
 */
class RsTryMacroInspection : RsLocalInspectionTool() {
    override fun getDisplayName() = "try! macro usage"

    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : RsVisitor() {
        override fun visitMacroCall(o: RsMacroCall) {
            if (o.macroName?.text != "try" || o.tryMacroArgument == null) return
            holder.registerProblem(
                o,
                "try! macro can be replaced with ? operator",
                object : LocalQuickFix {
                    override fun getName() = "Change try! to ?"

                    override fun getFamilyName() = name

                    override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
                        val macro = descriptor.psiElement as RsMacroCall
                        val body = macro.tryMacroArgument!!.expr
                        val tryExpr = RsPsiFactory(project).createExpression("${body.text}?") as RsTryExpr
                        macro.replace(tryExpr)
                    }
                }
            )
        }
    }
}

/*
 * Use of this source code is governed by the MIT license that can be
 * found in the LICENSE file.
 */

package org.rust.ide.inspections

import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import org.rust.lang.core.psi.*
import org.rust.lang.core.psi.ext.macroName

/**
 * Change `try!` macro to `?` operator.
 */
class RsTryMacroInspection : RsLocalInspectionTool() {
    override fun getDisplayName() = "try! macro usage"

    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : RsVisitor() {
        override fun visitMacroExpr(o: RsMacroExpr) {
            if (o.macroCall.macroName?.text != "try" || o.macroCall.tryMacroArgument == null) return
            holder.registerProblem(
                o,
                "try! macro can be replaced with ? operator",
                object : LocalQuickFix {
                    override fun getName() = "Change try! to ?"

                    override fun getFamilyName() = name

                    override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
                        val macro = descriptor.psiElement as RsMacroExpr
                        val body = macro.macroCall.tryMacroArgument!!.expr
                        val tryExpr = RsPsiFactory(project).createExpression("${body.text}?") as RsTryExpr
                        macro.replace(tryExpr)
                    }
                }
            )
        }
    }
}

Fix exceptions after running the "Change try! to ?" inspection
Fix exceptions after running the "Change try! to ?" inspection

A try!() invocation looks like this in PSI:
RsMacroExpr
-RsMacroCall
--identifier
--!
--RsTryMacroArgument

RsTryMacroInspection registers the problem and the fix on the RsMacroCall.
When the fix is applied, it replaces the RsMacroCall node with a RsTryExpr,
leaving back a corrupt RsMacroExpr missing a required RsMacroCall child.

Later some other code trips over this corrupt PSI node
triggering an exception in PsiElementBase.notNullChild().

This problem was introduced by commit 2e25378cc0
which reorganized macro related grammar.

Fix the problem by visiting and working on the RsMacroExpr instead.

Undin/intellij-rust,alygin/intellij-rust,Undin/intellij-rust,alygin/intellij-rust,anton-okolelov/intellij-rust,Undin/intellij-rust,alygin/intellij-rust,intellij-rust/intellij-rust,Undin/intellij-rust,alygin/intellij-rust,intellij-rust/intellij-rust,Undin/intellij-rust,intellij-rust/intellij-rust,anton-okolelov/intellij-rust,Undin/intellij-rust,intellij-rust/intellij-rust,anton-okolelov/intellij-rust,anton-okolelov/intellij-rust,intellij-rust/intellij-rust,anton-okolelov/intellij-rust,alygin/intellij-rust,intellij-rust/intellij-rust