diff --git a/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionManager.kt b/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionManager.kt index 722747af8fb..12f7a199480 100644 --- a/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionManager.kt +++ b/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionManager.kt @@ -26,9 +26,9 @@ import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValueProvider -import com.intellij.testFramework.PlatformTestUtil import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiModificationTracker +import com.intellij.testFramework.PlatformTestUtil import com.intellij.util.io.DataOutputStream import com.intellij.util.io.createDirectories import com.intellij.util.io.delete @@ -45,16 +45,14 @@ import org.rust.cargo.project.model.cargoProjects import org.rust.cargo.project.settings.RustProjectSettingsService import org.rust.cargo.project.settings.rustSettings import org.rust.cargo.project.workspace.PackageOrigin -import org.rust.ide.experiments.RsExperiments +import org.rust.ide.experiments.RsExperiments.EVALUATE_BUILD_SCRIPTS +import org.rust.ide.experiments.RsExperiments.PROC_MACROS import org.rust.lang.RsFileType import org.rust.lang.core.crate.Crate import org.rust.lang.core.crate.CratePersistentId import org.rust.lang.core.crate.crateGraph import org.rust.lang.core.indexing.RsIndexableSetContributor -import org.rust.lang.core.macros.errors.GetMacroExpansionError -import org.rust.lang.core.macros.errors.MacroExpansionAndParsingError -import org.rust.lang.core.macros.errors.ProcMacroExpansionError -import org.rust.lang.core.macros.errors.toExpansionError +import org.rust.lang.core.macros.errors.* import org.rust.lang.core.psi.* import org.rust.lang.core.psi.RsProcMacroKind.DERIVE import org.rust.lang.core.psi.RsProcMacroKind.FUNCTION_LIKE @@ -65,6 +63,7 @@ import org.rust.lang.core.resolve2.RsModInfoBase.RsModInfo import org.rust.openapiext.* import org.rust.stdext.* import org.rust.stdext.RsResult.Err +import org.rust.stdext.RsResult.Ok import org.rust.taskQueue import java.io.IOException import java.nio.file.Files @@ -219,7 +218,7 @@ class MacroExpansionManagerImpl( val includingFile = call.findIncludingFile() ?: return@run Err(GetMacroExpansionError.IncludingFileNotFound) val items = includingFile.stubChildrenOfType() - RsResult.Ok(MacroExpansion.Items(includingFile, items)) + Ok(MacroExpansion.Items(includingFile, items)) } return CachedValueProvider.Result.create(expansion, call.rustStructureOrAnyPsiModificationTracker) } @@ -711,10 +710,10 @@ private class MacroExpansionServiceImplInner( val info = getModInfo(call.containingMod) as? RsModInfo ?: return everChanged(Err(GetMacroExpansionError.ModDataNotFound)) val macroIndex = info.getMacroIndex(call, info.crate) - ?: return everChanged(Err(getReasonWhyExpansionFileNotFound(call, info.defMap, null))) + ?: return everChanged(Err(getReasonWhyExpansionFileNotFound(call, info.crate, info.defMap, null))) val expansionFile = getExpansionFile(info.defMap, macroIndex) - ?: return everChanged(Err(getReasonWhyExpansionFileNotFound(call, info.defMap, macroIndex))) - val expansion = RsResult.Ok(getExpansionFromExpandedFile(MacroExpansionContext.ITEM, expansionFile)!!) + ?: return everChanged(Err(getReasonWhyExpansionFileNotFound(call, info.crate, info.defMap, macroIndex))) + val expansion = Ok(getExpansionFromExpandedFile(MacroExpansionContext.ITEM, expansionFile)!!) return if (call is RsMacroCall) { CachedValueProvider.Result.create(expansion, modificationTracker, call.modificationTracker) } else { @@ -805,17 +804,25 @@ private class MacroExpansionServiceImplInner( private fun getReasonWhyExpansionFileNotFound( call: RsPossibleMacroCall, + crate: Crate, defMap: CrateDefMap, callIndex: MacroIndex? ): GetMacroExpansionError { - if (!isFeatureEnabled(RsExperiments.EVALUATE_BUILD_SCRIPTS) || !isFeatureEnabled(RsExperiments.PROC_MACROS)) { - return GetMacroExpansionError.ExpansionError(ProcMacroExpansionError.ProcMacroExpansionIsDisabled) - } - if (!call.existsAfterExpansion) { + if (!call.existsAfterExpansion(crate)) { return GetMacroExpansionError.CfgDisabled } - call.resolveToMacroWithoutPsiWithErr() - .unwrapOrElse { return it.toExpansionError() } + + val resolveResult = call.resolveToMacroWithoutPsiWithErr() + + val isProcMacro = resolveResult is Ok && resolveResult.ok.data is RsProcMacroData + || resolveResult is Err && resolveResult.err is ResolveMacroWithoutPsiError.NoProcMacroArtifact + val procMacroExpansionIsDisabled = isProcMacro + && (!isFeatureEnabled(EVALUATE_BUILD_SCRIPTS) || !isFeatureEnabled(PROC_MACROS)) + if (procMacroExpansionIsDisabled) { + return GetMacroExpansionError.ExpansionError(ProcMacroExpansionError.ProcMacroExpansionIsDisabled) + } + + resolveResult.unwrapOrElse { return it.toExpansionError() } if (callIndex == null) { return GetMacroExpansionError.NoMacroIndex diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/CfgUtils.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/CfgUtils.kt index 9148ea3ab4a..a5210c0d4e0 100644 --- a/src/main/kotlin/org/rust/lang/core/psi/ext/CfgUtils.kt +++ b/src/main/kotlin/org/rust/lang/core/psi/ext/CfgUtils.kt @@ -25,10 +25,12 @@ val PsiElement.isEnabledByCfg: Boolean get() = isEnabledByCfgInner(null) * ``` */ val PsiElement.existsAfterExpansion: Boolean - get() { - val status = getCodeStatus(null) - return status == RsCodeStatus.CODE || status == RsCodeStatus.CFG_UNKNOWN - } + get() = existsAfterExpansion(null) + +fun PsiElement.existsAfterExpansion(crate: Crate?): Boolean { + val status = getCodeStatus(crate) + return status == RsCodeStatus.CODE || status == RsCodeStatus.CFG_UNKNOWN +} fun PsiElement.isEnabledByCfg(crate: Crate): Boolean = isEnabledByCfgInner(crate) diff --git a/src/test/kotlin/org/rust/RustProjectDescriptors.kt b/src/test/kotlin/org/rust/RustProjectDescriptors.kt index 815238e2345..6c88b8e22d6 100644 --- a/src/test/kotlin/org/rust/RustProjectDescriptors.kt +++ b/src/test/kotlin/org/rust/RustProjectDescriptors.kt @@ -372,12 +372,14 @@ object WithDependencyRustProjectDescriptor : RustProjectDescriptorBase() { ) val depProcMacro2 = externalPackage("$contentRoot/dep-proc-macro-2", "lib.rs", "dep-proc-macro-2", libKind = LibKind.PROC_MACRO, procMacroArtifact = testProcMacroArtifact2) + val depProcMacro3 = externalPackage("$contentRoot/dep-proc-macro-unsuccessfully-compiled", "lib.rs", "dep-proc-unsuccessfully-compiled", libKind = LibKind.PROC_MACRO, + procMacroArtifact = null) val cyclicDepLibDevDep = externalPackage("$contentRoot/cyclic-dep-lib-dev-dep", "lib.rs", "cyclic-dep-lib-dev-dep") val packages = listOf( testPackage, depLib, depLibNew, depLib2, depLibWithCyclicDep, depLibToBeRenamed, noSrcLib, noSourceLib, transLib, transLib2, transCommonLib, rawIdentifierLib, depProcMacro, depProcMacro2, - cyclicDepLibDevDep + depProcMacro3, cyclicDepLibDevDep ) return CargoWorkspace.deserialize(Paths.get("/my-crate/Cargo.toml"), CargoWorkspaceData(packages, mapOf( @@ -389,6 +391,7 @@ object WithDependencyRustProjectDescriptor : RustProjectDescriptorBase() { dep(noSourceLib.id), dep(depProcMacro.id), dep(depProcMacro2.id), + dep(depProcMacro3.id), dep(rawIdentifierLib.id), ), depLib.id to setOf( diff --git a/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionErrorTestBase.kt b/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionErrorTestBase.kt new file mode 100644 index 00000000000..5d7416ba266 --- /dev/null +++ b/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionErrorTestBase.kt @@ -0,0 +1,64 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rust.lang.core.macros + +import org.intellij.lang.annotations.Language +import org.rust.RsTestBase +import org.rust.fileTreeFromText +import org.rust.lang.core.macros.errors.GetMacroExpansionError +import org.rust.lang.core.psi.ext.RsPossibleMacroCall +import org.rust.lang.core.psi.ext.descendantsOfType +import org.rust.lang.core.psi.ext.expansionResult +import org.rust.lang.core.psi.ext.isMacroCall +import org.rust.stdext.RsResult + +abstract class RsMacroExpansionErrorTestBase : RsTestBase() { + protected inline fun checkError( + @Language("Rust") code: String + ) { + checkError(code, T::class.java) + } + + protected fun checkError(code: String, errorClass: Class<*>) { + InlineFile(code) + doCheck(errorClass) + } + + protected inline fun checkErrorByTree( + @Language("Rust") code: String + ) { + checkError(code, T::class.java) + } + + protected fun checkErrorByTree(code: String, errorClass: Class<*>) { + fileTreeFromText(code).createAndOpenFileWithCaretMarker() + doCheck(errorClass) + } + + private fun doCheck(errorClass: Class<*>) { + val markers = findElementsWithDataAndOffsetInEditor() + val (macro, expectedErrorMessage) = if (markers.isEmpty()) { + myFixture.file + .descendantsOfType() + .single { it.isMacroCall } to null + } else { + val (macro, message, _) = markers.single() + check(macro.isMacroCall) + macro to message + } + + val err = when (val result = macro.expansionResult) { + is RsResult.Err -> result.err + is RsResult.Ok -> error("Expected a macro expansion error, got a successfully expanded macro") + } + + check(errorClass.isInstance(err)) { "Expected error $errorClass, got $err" } + + if (expectedErrorMessage != null) { + assertEquals(expectedErrorMessage, err.toUserViewableMessage()) + } + } +} diff --git a/src/test/kotlin/org/rust/lang/core/macros/decl/RsMacroErrorTest.kt b/src/test/kotlin/org/rust/lang/core/macros/decl/RsMacroErrorTest.kt index 559e2f87e01..41c58efb7df 100644 --- a/src/test/kotlin/org/rust/lang/core/macros/decl/RsMacroErrorTest.kt +++ b/src/test/kotlin/org/rust/lang/core/macros/decl/RsMacroErrorTest.kt @@ -5,24 +5,27 @@ package org.rust.lang.core.macros.decl -import org.intellij.lang.annotations.Language -import org.rust.RsTestBase -import org.rust.lang.core.psi.RsMacroCall -import org.rust.lang.core.psi.ext.descendantsOfType -import org.rust.lang.core.psi.ext.expansion - -class RsMacroErrorTestBase : RsTestBase() { - private fun checkNotExpanded(@Language("Rust") code: String) { - InlineFile(code) - val calls = myFixture.file.descendantsOfType() - check(calls.all { it.expansion == null }) - } +import org.rust.ExpandMacros +import org.rust.MockAdditionalCfgOptions +import org.rust.lang.core.macros.RsMacroExpansionErrorTestBase +import org.rust.lang.core.macros.errors.GetMacroExpansionError +@ExpandMacros +class RsMacroErrorTest : RsMacroExpansionErrorTestBase() { // https://github.com/intellij-rust/intellij-rust/pull/2583 - fun `test empty group definition`() = checkNotExpanded(""" + fun `test empty group definition`() = checkError(""" macro_rules! foo { ($()* $ i:tt) => { } } foo! { bar baz } """) + + @MockAdditionalCfgOptions("intellij_rust") + fun `test cfg disabled top level macro call`() = checkError(""" + macro_rules! foo { + () => { fn foo() {} } + } + #[cfg(not(intellij_rust))] + foo! {} + """) } diff --git a/src/test/kotlin/org/rust/lang/core/macros/proc/RsProcMacroErrorTest.kt b/src/test/kotlin/org/rust/lang/core/macros/proc/RsProcMacroErrorTest.kt index c27aff2d7a3..54ded2bfe6c 100644 --- a/src/test/kotlin/org/rust/lang/core/macros/proc/RsProcMacroErrorTest.kt +++ b/src/test/kotlin/org/rust/lang/core/macros/proc/RsProcMacroErrorTest.kt @@ -5,18 +5,12 @@ package org.rust.lang.core.macros.proc -import junit.framework.TestCase -import org.intellij.lang.annotations.Language import org.rust.* import org.rust.ide.experiments.RsExperiments.EVALUATE_BUILD_SCRIPTS import org.rust.ide.experiments.RsExperiments.PROC_MACROS import org.rust.lang.core.macros.MacroExpansionScope +import org.rust.lang.core.macros.RsMacroExpansionErrorTestBase import org.rust.lang.core.macros.errors.GetMacroExpansionError -import org.rust.lang.core.psi.ext.RsPossibleMacroCall -import org.rust.lang.core.psi.ext.descendantsOfType -import org.rust.lang.core.psi.ext.expansionResult -import org.rust.lang.core.psi.ext.isMacroCall -import org.rust.stdext.RsResult /** * A test for [org.rust.lang.core.macros.errors.GetMacroExpansionError.toUserViewableMessage] @@ -27,7 +21,7 @@ import org.rust.stdext.RsResult @ExpandMacros(MacroExpansionScope.WORKSPACE) @ProjectDescriptor(WithProcMacroRustProjectDescriptor::class) @WithExperimentalFeatures(EVALUATE_BUILD_SCRIPTS, PROC_MACROS) -class RsProcMacroErrorTest : RsTestBase() { +class RsProcMacroErrorTest : RsMacroExpansionErrorTestBase() { @WithExperimentalFeatures() fun `test macro expansion is disabled`() = checkError(""" use test_proc_macros::attr_as_is; @@ -37,6 +31,45 @@ class RsProcMacroErrorTest : RsTestBase() { fn foo() {} """) + @ProjectDescriptor(WithDependencyRustProjectDescriptor::class) + @WithExperimentalFeatures() + fun `test macro expansion is disabled with unsuccessfully compiled proc macro crate`() = checkErrorByTree(""" + //- dep-proc-macro-unsuccessfully-compiled/lib.rs + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro_attribute] + pub fn attr_as_is(_attr: TokenStream, item: TokenStream) -> TokenStream { + item + } + compile_error!("The crate with the macro is not compiled successfully"); + //- main.rs + use dep_proc_macro_unsuccessfully_compiled::attr_as_is; + + #[attr_as_is] + //^ procedural macro expansion is not enabled + fn foo() {} + """) + + @ProjectDescriptor(WithDependencyRustProjectDescriptor::class) + fun `test unsuccessfully compiled proc macro crate`() = checkErrorByTree(""" + //- dep-proc-macro-unsuccessfully-compiled/lib.rs + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro_attribute] + pub fn attr_as_is(_attr: TokenStream, item: TokenStream) -> TokenStream { + item + } + compile_error!("The crate with the macro is not compiled successfully"); + //- main.rs + use dep_proc_macro_unsuccessfully_compiled::attr_as_is; + + #[attr_as_is] + //^ the procedural macro is not compiled successfully + fn foo() {} + """) + fun `test unresolved function-like macro`() = checkError(""" unresolved_macro! {} """) @@ -76,35 +109,4 @@ class RsProcMacroErrorTest : RsTestBase() { #[function_like_as_is] fn foo() {} //^ `FUNCTION_LIKE` proc macro can't be called as `ATTRIBUTE` """) - - private inline fun checkError( - @Language("Rust") code: String - ) { - checkError(code, T::class.java) - } - - private fun checkError(code: String, errorClass: Class<*>) { - InlineFile(code) - val markers = findElementsWithDataAndOffsetInEditor() - val (macro, expectedErrorMessage) = if (markers.isEmpty()) { - myFixture.file - .descendantsOfType() - .single { it.isMacroCall } to null - } else { - val (macro, message, _) = markers.single() - check(macro.isMacroCall) - macro to message - } - - val err = when (val result = macro.expansionResult) { - is RsResult.Err -> result.err - is RsResult.Ok -> error("Expected a macro expansion error, got a successfully expanded macro") - } - - check(errorClass.isInstance(err)) { "Expected error $errorClass, got $err" } - - if (expectedErrorMessage != null) { - TestCase.assertEquals(expectedErrorMessage, err.toUserViewableMessage()) - } - } }