diff --git a/src/main/grammars/RustParser.bnf b/src/main/grammars/RustParser.bnf index c0bb81ff5d3..acb99ac08d9 100644 --- a/src/main/grammars/RustParser.bnf +++ b/src/main/grammars/RustParser.bnf @@ -798,7 +798,7 @@ private ScalarTypeReferenceInner ::= ArrayType TypeReference ::= TypeReferenceInner { implements = "org.rust.lang.core.macros.RsExpandedElement" stubClass = "org.rust.lang.core.stubs.RsPlaceholderStub" - extends = "org.rust.lang.core.psi.ext.RsStubbedElementImpl" + mixin = "org.rust.lang.core.psi.ext.RsTypeReferenceImplMixin" elementTypeFactory = "org.rust.lang.core.stubs.StubImplementationsKt.factory" } @@ -1303,8 +1303,17 @@ private SpecialExprContextMacroNames ::= "try" | "await" | "dbg" | "format" | "f private MacroHead ::= AttrsAndVis &(PathWithoutTypes '!') (SpecialMacro | CommonMacro) -MacroArgument ::= <> -// `CompactTT` differs from `TT` in that it does not create an additional `TT` nodes for each unpaired token +MacroArgument ::= <> +MacroArgumentTT ::= (<> | MacroBodyIdent | MacroBodyQuoteIdent | <>)* +MacroBodyIdent ::= identifier { + implements = "org.rust.lang.core.psi.ext.RsReferenceElementBase" + mixin = "org.rust.lang.core.psi.ext.RsMacroBodyIdentMixin" +} +MacroBodyQuoteIdent ::= QUOTE_IDENTIFIER { + implements = "org.rust.lang.core.psi.ext.RsReferenceElementBase" + mixin = "org.rust.lang.core.psi.ext.RsMacroBodyQuoteIdentMixin" +} + CompactTT ::= (<> | <>)* // Used only manually in (external) macro matching code diff --git a/src/main/kotlin/org/rust/ide/navigation/goto/RsTargetElementEvaluator.kt b/src/main/kotlin/org/rust/ide/navigation/goto/RsTargetElementEvaluator.kt index 95c5032dc4f..440bb0bc696 100644 --- a/src/main/kotlin/org/rust/ide/navigation/goto/RsTargetElementEvaluator.kt +++ b/src/main/kotlin/org/rust/ide/navigation/goto/RsTargetElementEvaluator.kt @@ -10,11 +10,9 @@ import com.intellij.codeInsight.TargetElementUtil import com.intellij.psi.PsiElement import com.intellij.psi.PsiReference import com.intellij.util.BitUtil +import org.rust.lang.core.macros.findExpansionElements import org.rust.lang.core.macros.findNavigationTargetIfMacroExpansion -import org.rust.lang.core.psi.RsBinaryExpr -import org.rust.lang.core.psi.RsBinaryOp -import org.rust.lang.core.psi.RsMethodCall -import org.rust.lang.core.psi.RsPath +import org.rust.lang.core.psi.* import org.rust.lang.core.psi.ext.* import org.rust.lang.core.resolve.ref.RsReference import org.rust.lang.core.resolve.ref.deepResolve @@ -70,4 +68,23 @@ class RsTargetElementEvaluator : TargetElementEvaluatorEx2() { */ override fun getGotoDeclarationTarget(element: PsiElement, navElement: PsiElement?): PsiElement? = element.findNavigationTargetIfMacroExpansion() + + /** + * Used to get parent named element when [element] is a name identifier + * + * Note that if this method returns null, it means "use default logic" + */ + override fun getNamedElement(element: PsiElement): PsiElement? { + // This hack enables some actions (e.g. "find usages") when the [element] is inside a macro + // call and this element expands to name identifier of some named element. + if (element.elementType == RsElementTypes.IDENTIFIER && element.parent is RsMacroBodyIdent) { + val delegate = element.findExpansionElements()?.firstOrNull() ?: return null + val delegateParent = delegate.parent + if (delegateParent is RsNameIdentifierOwner && delegateParent.nameIdentifier == delegate) { + return delegateParent + } + } + + return null + } } diff --git a/src/main/kotlin/org/rust/ide/refactoring/RsRefactoringSupportProvider.kt b/src/main/kotlin/org/rust/ide/refactoring/RsRefactoringSupportProvider.kt index 9c1f5a159dd..198dd378050 100644 --- a/src/main/kotlin/org/rust/ide/refactoring/RsRefactoringSupportProvider.kt +++ b/src/main/kotlin/org/rust/ide/refactoring/RsRefactoringSupportProvider.kt @@ -11,15 +11,16 @@ import com.intellij.refactoring.RefactoringActionHandler import org.rust.ide.refactoring.extractFunction.RsExtractFunctionHandler import org.rust.ide.refactoring.introduceParameter.RsIntroduceParameterHandler import org.rust.ide.refactoring.introduceVariable.RsIntroduceVariableHandler +import org.rust.lang.core.macros.isExpandedFromMacro import org.rust.lang.core.psi.RsPatBinding import org.rust.lang.core.psi.ext.RsNameIdentifierOwner class RsRefactoringSupportProvider : RefactoringSupportProvider() { override fun isInplaceRenameAvailable(element: PsiElement, context: PsiElement?): Boolean = - element is RsPatBinding + element is RsPatBinding && !element.isExpandedFromMacro override fun isMemberInplaceRenameAvailable(element: PsiElement, context: PsiElement?): Boolean = - element is RsNameIdentifierOwner + element is RsNameIdentifierOwner && !element.isExpandedFromMacro override fun getIntroduceVariableHandler(): RefactoringActionHandler = RsIntroduceVariableHandler() diff --git a/src/main/kotlin/org/rust/ide/search/RsUsageTypeProvider.kt b/src/main/kotlin/org/rust/ide/search/RsUsageTypeProvider.kt index 1fc2ac67ce3..d332a1e1faa 100644 --- a/src/main/kotlin/org/rust/ide/search/RsUsageTypeProvider.kt +++ b/src/main/kotlin/org/rust/ide/search/RsUsageTypeProvider.kt @@ -9,6 +9,7 @@ import com.intellij.psi.PsiElement import com.intellij.usages.UsageTarget import com.intellij.usages.impl.rules.UsageType import com.intellij.usages.impl.rules.UsageTypeProviderEx +import org.rust.lang.core.macros.findExpansionElements import org.rust.lang.core.psi.* object RsUsageTypeProvider : UsageTypeProviderEx { @@ -41,7 +42,8 @@ object RsUsageTypeProvider : UsageTypeProviderEx { override fun getUsageType(element: PsiElement?): UsageType? = getUsageType(element, UsageTarget.EMPTY_ARRAY) override fun getUsageType(element: PsiElement?, targets: Array): UsageType? { - val parent = element?.goUp() ?: return null + val refinedElement = element?.findExpansionElements()?.firstOrNull()?.parent ?: element + val parent = refinedElement?.goUp() ?: return null if (parent is RsBaseType) { return when (val context = parent.goUp()) { is RsTypeReference -> { 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 493a30071f5..590e77f6d66 100644 --- a/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionManager.kt +++ b/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionManager.kt @@ -6,6 +6,7 @@ package org.rust.lang.core.macros import com.intellij.AppTopics +import com.intellij.codeInsight.template.TemplateManager import com.intellij.openapi.Disposable import com.intellij.openapi.application.* import com.intellij.openapi.application.impl.LaterInvocator @@ -14,6 +15,8 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Document import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileDocumentManagerListener +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.fileEditor.TextEditor import com.intellij.openapi.progress.* import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator import com.intellij.openapi.progress.impl.ProgressManagerImpl @@ -559,6 +562,10 @@ private class MacroExpansionServiceImplInner( private fun processChangedMacros(workspaceOnly: Boolean) { MACRO_LOG.trace("processChangedMacros") if (!isExpansionModeNew) return + + // Fixes inplace rename when the renamed element is referenced from a macro call body + if (isTemplateActiveInAnyEditor()) return + class ProcessModifiedMacrosTask(private val workspaceOnly: Boolean) : MacroExpansionTaskBase( project, storage, @@ -579,6 +586,15 @@ private class MacroExpansionServiceImplInner( taskQueue.run(task) } + private fun isTemplateActiveInAnyEditor(): Boolean { + val tm = TemplateManager.getInstance(project) + for (editor in FileEditorManager.getInstance(project).allEditors) { + if (editor is TextEditor && tm.getActiveTemplate(editor.editor) != null) return true + } + + return false + } + fun ensureUpToDate() { if (!isExpansionModeNew) return ProgressManager.checkCanceled() diff --git a/src/main/kotlin/org/rust/lang/core/psi/RsPsiImplUtil.kt b/src/main/kotlin/org/rust/lang/core/psi/RsPsiImplUtil.kt index d0bf8894034..bbd5cd69caa 100644 --- a/src/main/kotlin/org/rust/lang/core/psi/RsPsiImplUtil.kt +++ b/src/main/kotlin/org/rust/lang/core/psi/RsPsiImplUtil.kt @@ -5,9 +5,11 @@ package org.rust.lang.core.psi +import com.intellij.psi.PsiElement import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.SearchScope import com.intellij.psi.util.PsiTreeUtil +import org.rust.lang.core.macros.findMacroCallExpandedFrom import org.rust.lang.core.psi.ext.* /** @@ -36,7 +38,7 @@ object RsPsiImplUtil { */ fun getParameterUseScope(element: RsElement): SearchScope? { val owner = element.contextStrict() - if (owner != null) return LocalSearchScope(owner) + if (owner != null) return localOrMacroSearchScope(owner) return null } @@ -83,7 +85,7 @@ object RsPsiImplUtil { is RsForeignModItem -> getTopLevelDeclarationUseScope(element, owner.containingMod, restrictedVis) // In this case `owner` is function or code block, i.e. it's local scope - else -> LocalSearchScope(owner) + else -> localOrMacroSearchScope(owner) } } @@ -98,12 +100,21 @@ object RsPsiImplUtil { is RsVisibility.Restricted -> visibility.inMod } - if (!restrictedMod.hasChildModules()) return LocalSearchScope(containingMod) + if (!restrictedMod.hasChildModules()) return localOrMacroSearchScope(containingMod) // TODO restrict scope to [restrictedMod]. We can't use `DirectoryScope` b/c file from any // directory can be included via `#[path]` attribute. return null } + + /** + * If the [scope] is inside a macro expansion, we can't use it as a local search scope + * because elements inside the scope can be referenced from the macro call body via + * [org.rust.lang.core.resolve.ref.RsMacroBodyReferenceDelegateImpl]. We use the macro + * call as a search scope in this case + */ + fun localOrMacroSearchScope(scope: PsiElement): LocalSearchScope = + LocalSearchScope(scope.findMacroCallExpandedFrom() ?: scope) } private fun RsMod.hasChildModules(): Boolean = diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/PsiElement.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/PsiElement.kt index e181be99491..153516cdafa 100644 --- a/src/main/kotlin/org/rust/lang/core/psi/ext/PsiElement.kt +++ b/src/main/kotlin/org/rust/lang/core/psi/ext/PsiElement.kt @@ -26,6 +26,10 @@ val PsiElement.ancestors: Sequence get() = generateSequence(this) { if (it is PsiFile) null else it.parent } +val PsiElement.contexts: Sequence get() = generateSequence(this) { + if (it is PsiFile) null else it.context +} + val PsiElement.ancestorPairs: Sequence> get() { val parent = this.parent ?: return emptySequence() return generateSequence(Pair(this, parent)) { (_, parent) -> diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBinding.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBinding.kt index 24a8769eaec..8519e95902b 100644 --- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBinding.kt +++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBinding.kt @@ -7,10 +7,10 @@ package org.rust.lang.core.psi.ext import com.intellij.lang.ASTNode import com.intellij.psi.PsiElement -import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.SearchScope import org.rust.lang.core.psi.RsMacro import org.rust.lang.core.psi.RsMacroBinding +import org.rust.lang.core.psi.RsPsiImplUtil.localOrMacroSearchScope abstract class RsMacroBindingImplMixin(node: ASTNode) : RsNamedElementImpl(node), RsMacroBinding { @@ -18,7 +18,7 @@ abstract class RsMacroBindingImplMixin(node: ASTNode) : RsNamedElementImpl(node) override fun getUseScope(): SearchScope { val owner = contextStrict() ?: error("Macro binding outside of a macro") - return LocalSearchScope(owner) + return localOrMacroSearchScope(owner) } } diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBodyIdent.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBodyIdent.kt new file mode 100644 index 00000000000..eadc33e7520 --- /dev/null +++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBodyIdent.kt @@ -0,0 +1,19 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rust.lang.core.psi.ext + +import com.intellij.lang.ASTNode +import com.intellij.psi.PsiElement +import org.rust.lang.core.psi.RsMacroBodyIdent +import org.rust.lang.core.resolve.ref.RsMacroBodyReferenceDelegateImpl +import org.rust.lang.core.resolve.ref.RsReference + +abstract class RsMacroBodyIdentMixin(node: ASTNode) : RsElementImpl(node), RsMacroBodyIdent { + override val referenceNameElement: PsiElement + get() = identifier + + override fun getReference(): RsReference? = RsMacroBodyReferenceDelegateImpl(this) +} diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBodyQuoteIdent.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBodyQuoteIdent.kt new file mode 100644 index 00000000000..ae4532b51dc --- /dev/null +++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsMacroBodyQuoteIdent.kt @@ -0,0 +1,19 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rust.lang.core.psi.ext + +import com.intellij.lang.ASTNode +import com.intellij.psi.PsiElement +import org.rust.lang.core.psi.RsMacroBodyQuoteIdent +import org.rust.lang.core.resolve.ref.RsMacroBodyReferenceDelegateImpl +import org.rust.lang.core.resolve.ref.RsReference + +abstract class RsMacroBodyQuoteIdentMixin(node: ASTNode) : RsElementImpl(node), RsMacroBodyQuoteIdent { + override val referenceNameElement: PsiElement + get() = quoteIdentifier + + override fun getReference(): RsReference? = RsMacroBodyReferenceDelegateImpl(this) +} diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsPatBinding.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsPatBinding.kt index fffc5ddbfa7..8ab8859c462 100644 --- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsPatBinding.kt +++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsPatBinding.kt @@ -8,7 +8,6 @@ package org.rust.lang.core.psi.ext import com.intellij.lang.ASTNode import com.intellij.navigation.ItemPresentation import com.intellij.psi.PsiElement -import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.SearchScope import com.intellij.psi.util.PsiTreeUtil import org.rust.ide.icons.RsIcons @@ -81,7 +80,7 @@ abstract class RsPatBindingImplMixin(node: ASTNode) : RsNamedElementImpl(node), RsLambdaExpr::class.java ) - if (owner != null) return LocalSearchScope(owner) + if (owner != null) return RsPsiImplUtil.localOrMacroSearchScope(owner) return super.getUseScope() } diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsReferenceElement.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsReferenceElement.kt index f9f3f5f4e53..e30e98f9c22 100644 --- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsReferenceElement.kt +++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsReferenceElement.kt @@ -9,16 +9,27 @@ import com.intellij.psi.PsiElement import org.rust.lang.core.psi.unescapedText import org.rust.lang.core.resolve.ref.RsReference -interface RsWeakReferenceElement : RsElement { - +/** + * Provides basic methods for reference implementation ([org.rust.lang.core.resolve.ref.RsReferenceBase]). + * This interface should not be used in any analysis. + */ +interface RsReferenceElementBase : RsElement { val referenceNameElement: PsiElement? @JvmDefault val referenceName: String? get() = referenceNameElement?.unescapedText +} +/** + * Marks an element that optionally can have a reference. + */ +interface RsWeakReferenceElement : RsReferenceElementBase { override fun getReference(): RsReference? } +/** + * Marks an element that has a reference. + */ interface RsReferenceElement : RsWeakReferenceElement { override val referenceNameElement: PsiElement diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeReference.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeReference.kt index a72efaeab1e..efcd059264b 100644 --- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeReference.kt +++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeReference.kt @@ -5,9 +5,23 @@ package org.rust.lang.core.psi.ext +import com.intellij.lang.ASTNode +import com.intellij.psi.PsiElement +import com.intellij.psi.stubs.IStubElementType import com.intellij.psi.util.PsiTreeUtil +import org.rust.lang.core.macros.RsExpandedElement import org.rust.lang.core.psi.RsTypeReference +import org.rust.lang.core.stubs.RsPlaceholderStub val RsTypeReference.typeElement: RsTypeElement? get() = PsiTreeUtil.getStubChildOfType(this, RsTypeElement::class.java) + +abstract class RsTypeReferenceImplMixin : RsStubbedElementImpl, RsTypeReference { + + constructor(node: ASTNode) : super(node) + + constructor(stub: RsPlaceholderStub, nodeType: IStubElementType<*, *>) : super(stub, nodeType) + + override fun getContext(): PsiElement? = RsExpandedElement.getContextImpl(this) +} diff --git a/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt b/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt index f7daeffa456..1dad8915e25 100644 --- a/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt +++ b/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt @@ -584,7 +584,7 @@ fun processLabelResolveVariants(label: RsLabel, processor: RsResolveProcessor): fun processLifetimeResolveVariants(lifetime: RsLifetime, processor: RsResolveProcessor): Boolean { if (lifetime.isPredefined) return false - loop@ for (scope in lifetime.ancestors) { + loop@ for (scope in lifetime.contexts) { val lifetimeParameters = when (scope) { is RsGenericDeclaration -> scope.lifetimeParameters is RsWhereClause -> scope.wherePredList.mapNotNull { it.forLifetimes }.flatMap { it.lifetimeParameterList } diff --git a/src/main/kotlin/org/rust/lang/core/resolve/ref/RsMacroBodyReferenceDelegateImpl.kt b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsMacroBodyReferenceDelegateImpl.kt new file mode 100644 index 00000000000..22afa973235 --- /dev/null +++ b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsMacroBodyReferenceDelegateImpl.kt @@ -0,0 +1,35 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rust.lang.core.resolve.ref + +import com.intellij.psi.PsiElement +import org.rust.lang.core.macros.findExpansionElements +import org.rust.lang.core.psi.ext.RsElement +import org.rust.lang.core.psi.ext.RsReferenceElementBase +import org.rust.lang.core.psi.ext.ancestors + +class RsMacroBodyReferenceDelegateImpl( + element: RsReferenceElementBase +) : RsReferenceBase(element) { + override val RsReferenceElementBase.referenceAnchor: PsiElement? + get() = element.referenceNameElement + + private val delegates: List + get() { + return element.findExpansionElements()?.mapNotNull { delegated -> + delegated.ancestors + .mapNotNull { it.reference } + .firstOrNull() as? RsReference + }.orEmpty() + } + + override fun isReferenceTo(element: PsiElement): Boolean { + return delegates.any { it.isReferenceTo(element) } + } + + override fun multiResolve(): List = + delegates.flatMap { it.multiResolve() }.distinct() +} diff --git a/src/main/kotlin/org/rust/lang/core/resolve/ref/RsReferenceBase.kt b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsReferenceBase.kt index 1c945f8b033..9b08af9acfc 100644 --- a/src/main/kotlin/org/rust/lang/core/resolve/ref/RsReferenceBase.kt +++ b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsReferenceBase.kt @@ -17,10 +17,10 @@ import org.rust.lang.core.psi.RsElementTypes.QUOTE_IDENTIFIER import org.rust.lang.core.psi.RsPsiFactory import org.rust.lang.core.psi.escapeIdentifierIfNeeded import org.rust.lang.core.psi.ext.RsElement -import org.rust.lang.core.psi.ext.RsWeakReferenceElement +import org.rust.lang.core.psi.ext.RsReferenceElementBase import org.rust.lang.core.psi.ext.elementType -abstract class RsReferenceBase( +abstract class RsReferenceBase( element: T ) : PsiPolyVariantReferenceBase(element), RsReference { diff --git a/src/test/kotlin/org/rust/ide/refactoring/RenameTest.kt b/src/test/kotlin/org/rust/ide/refactoring/RenameTest.kt index 7cd4c12db29..fc399ca41bf 100644 --- a/src/test/kotlin/org/rust/ide/refactoring/RenameTest.kt +++ b/src/test/kotlin/org/rust/ide/refactoring/RenameTest.kt @@ -415,6 +415,16 @@ class RenameTest : RsTestBase() { myFixture.renameElement(file, "foo.rs") } + fun `test rename reference inside a macro call`() = doTest("Spam", """ + macro_rules! foo { ($ i:item) => { $ i }; } + struct Foo; + foo! { type T = /*caret*/Foo; } + """, """ + macro_rules! foo { ($ i:item) => { $ i }; } + struct Spam; + foo! { type T = Spam; } + """) + private fun doTest( newName: String, @Language("Rust") before: String, diff --git a/src/test/kotlin/org/rust/ide/search/RsFindUsagesTest.kt b/src/test/kotlin/org/rust/ide/search/RsFindUsagesTest.kt index 3528ba91c96..762f39e7f46 100644 --- a/src/test/kotlin/org/rust/ide/search/RsFindUsagesTest.kt +++ b/src/test/kotlin/org/rust/ide/search/RsFindUsagesTest.kt @@ -5,6 +5,7 @@ package org.rust.ide.search +import com.intellij.codeInsight.TargetElementUtil import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.TestDialog import com.intellij.psi.PsiElement @@ -161,6 +162,16 @@ class RsFindUsagesTest : RsTestBase() { foo!();// - macro call """) + fun `test struct defined by macro`() = doTestByText(""" + macro_rules! foo { ($($ i:item)*) => { $( $ i )* }; } + foo! { + struct X; + //^ + type T1 = X; // - type reference + } + type T2 = X; // - type reference + """) + fun `test method from trait`() = doTestByText(""" struct B1; struct B2; trait A { fn foo(self, x: i32); } @@ -209,7 +220,13 @@ class RsFindUsagesTest : RsTestBase() { private fun doTestByText(@Language("Rust") code: String) { InlineFile(code) - val source = findElementInEditor() + + val (_, _, offset) = findElementWithDataAndOffsetInEditor() + val source = TargetElementUtil.getInstance().findTargetElement( + myFixture.editor, + TargetElementUtil.ELEMENT_NAME_ACCEPTED, + offset + ) as? RsNamedElement ?: error("Element not found") val actual = markersActual(source) val expected = markersFrom(code) diff --git a/src/test/kotlin/org/rust/lang/core/macros/RsMacroCallReferenceDelegationTest.kt b/src/test/kotlin/org/rust/lang/core/macros/RsMacroCallReferenceDelegationTest.kt new file mode 100644 index 00000000000..7b02932c338 --- /dev/null +++ b/src/test/kotlin/org/rust/lang/core/macros/RsMacroCallReferenceDelegationTest.kt @@ -0,0 +1,102 @@ +/* + * 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.rust.ExpandMacros +import org.rust.lang.core.resolve.RsResolveTestBase + +@ExpandMacros +class RsMacroCallReferenceDelegationTest : RsResolveTestBase() { + fun `test item context`() = checkByCode(""" + struct X; + //X + macro_rules! foo { ($($ i:item)*) => { $( $ i )* }; } + foo! { + type T = X; + } //^ + """) + + fun `test statement context`() = checkByCode(""" + struct X; + //X + macro_rules! foo { ($($ i:item)*) => { $( $ i )* }; } + fn main () { + foo! { + type T = X; + }; //^ + } + """) + + // TODO adjust type inference to take into account macros + fun `test expression context`() = expect { + checkByCode(""" + struct X; + //X + macro_rules! foo { ($($ i:tt)*) => { $( $ i )* }; } + fn main () { + let a = foo!(X); + } //^ + """) + } + + fun `test type context`() = checkByCode(""" + struct X; + //X + macro_rules! foo { ($($ i:tt)*) => { $( $ i )* }; } + type T = foo!(X); + //^ + """) + + // TODO implement `getContext()` in all RsPat PSI elements + fun `test pattern context`() = expect { + checkByCode(""" + const X: i32 = 0; + //X + macro_rules! foo { ($($ i:tt)*) => { $( $ i )* }; } + fn main() { + match 0 { + foo!(X) => {} + //^ + _ => {} + } + } + """) + } + + fun `test lifetime`() = checkByCode(""" + macro_rules! foo { + ($ i:item) => { $ i }; + } + struct S<'a>(&'a u8); + impl<'a> S<'a> { + //X + foo! { + fn foo(&self) -> &'a u8 {} + } //^ + } + """) + + fun `test 2-segment path 1`() = checkByCode(""" + mod foo { + //X + pub struct X; + } + macro_rules! foo { ($($ i:item)*) => { $( $ i )* }; } + foo! { + type T = foo::X; + } //^ + """) + + fun `test 2-segment path 2`() = checkByCode(""" + mod foo { + pub struct X; + } //X + macro_rules! foo { ($($ i:item)*) => { $( $ i )* }; } + foo! { + type T = foo::X; + } //^ + """) +} diff --git a/src/test/kotlin/org/rust/lang/core/resolve/RsResolveTestBase.kt b/src/test/kotlin/org/rust/lang/core/resolve/RsResolveTestBase.kt index 459d3978e2f..e45594c0849 100644 --- a/src/test/kotlin/org/rust/lang/core/resolve/RsResolveTestBase.kt +++ b/src/test/kotlin/org/rust/lang/core/resolve/RsResolveTestBase.kt @@ -15,10 +15,7 @@ import org.intellij.lang.annotations.Language import org.rust.FileTree import org.rust.RsTestBase import org.rust.fileTreeFromText -import org.rust.lang.core.psi.ext.RsNamedElement -import org.rust.lang.core.psi.ext.RsWeakReferenceElement -import org.rust.lang.core.psi.ext.contextualFile -import org.rust.lang.core.psi.ext.startOffset +import org.rust.lang.core.psi.ext.* import org.rust.lang.core.resolve.ref.RsReference import org.rust.openapiext.Testmark @@ -35,7 +32,7 @@ abstract class RsResolveTestBase : RsTestBase() { ) { InlineFile(code, fileName) - val (refElement, data, offset) = findElementWithDataAndOffsetInEditor("^") + val (refElement, data, offset) = findElementWithDataAndOffsetInEditor("^") if (data == "unresolved") { val resolved = refElement.reference?.resolve() diff --git a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/async_await.txt b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/async_await.txt index c809eb7876c..37cb08b0213 100644 --- a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/async_await.txt +++ b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/async_await.txt @@ -139,7 +139,7 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement())(')') PsiElement(;)(';') diff --git a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/attributes.txt b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/attributes.txt index aec893ca2dd..a694eabf408 100644 --- a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/attributes.txt +++ b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/attributes.txt @@ -356,8 +356,9 @@ FILE RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement({)('{') PsiWhiteSpace('\n ') - RsCompactTTImpl(COMPACT_TT) - PsiElement(identifier)('hello_world') + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('hello_world') PsiWhiteSpace('\n') PsiElement(})('}') PsiWhiteSpace('\n\n') diff --git a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/impls.txt b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/impls.txt index 68930f4c19e..1bf29ce496a 100644 --- a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/impls.txt +++ b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/impls.txt @@ -348,7 +348,7 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement())(')') PsiElement(;)(';') diff --git a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/macros.txt b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/macros.txt index b977594dff7..f565d988940 100644 --- a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/macros.txt +++ b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/macros.txt @@ -7,7 +7,7 @@ FILE PsiElement(identifier)('parser_definition') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement(RAW_STRING_LITERAL)('r#"\n"#') PsiElement())(')') PsiElement(;)(';') @@ -179,8 +179,9 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) - PsiElement(identifier)('String') + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('String') PsiElement())(')') PsiElement(;)(';') PsiWhiteSpace('\n\n') @@ -190,19 +191,22 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement(static)('static') PsiWhiteSpace(' ') - PsiElement(identifier)('HANDLE') + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('HANDLE') PsiElement(:)(':') PsiWhiteSpace(' ') - PsiElement(identifier)('Handle') + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('Handle') PsiWhiteSpace(' ') PsiElement(=)('=') PsiWhiteSpace(' ') - PsiElement(identifier)('Handle') + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('Handle') PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement(INTEGER_LITERAL)('0') PsiElement())(')') PsiElement())(')') @@ -226,7 +230,7 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement())(')') PsiElement(;)(';') @@ -261,7 +265,7 @@ FILE PsiWhiteSpace(' ') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement({)('{') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement(})('}') PsiWhiteSpace('\n ') @@ -345,7 +349,7 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement())(')') PsiWhiteSpace(' ') @@ -359,7 +363,7 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement())(')') PsiElement(;)(';') @@ -458,10 +462,11 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) - PsiElement(identifier)('Foo') + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('Foo') PsiElement([)('[') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement(])(']') PsiElement())(')') @@ -618,10 +623,11 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) - PsiElement(identifier)('Foo') + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('Foo') PsiElement([)('[') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement(])(']') PsiElement())(')') @@ -721,10 +727,11 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) - PsiElement(identifier)('Foo') + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('Foo') PsiElement([)('[') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement(])(']') PsiElement())(')') @@ -819,8 +826,9 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) - PsiElement(identifier)('log') + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('log') PsiElement(,)(',') PsiWhiteSpace(' ') PsiElement(STRING_LITERAL)('"debug values"') @@ -1186,10 +1194,11 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) - PsiElement(identifier)('Foo') + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('Foo') PsiElement([)('[') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement(])(']') PsiElement())(')') diff --git a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/patterns.txt b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/patterns.txt index 3b4beb3fb6b..df0ace4de39 100644 --- a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/patterns.txt +++ b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/patterns.txt @@ -284,8 +284,9 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) - PsiElement(identifier)('x') + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) + RsMacroBodyIdentImpl(MACRO_BODY_IDENT) + PsiElement(identifier)('x') PsiElement())(')') PsiWhiteSpace(' ') PsiElement(=)('=') diff --git a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/type.txt b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/type.txt index a679b5f2ad5..05967099582 100644 --- a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/type.txt +++ b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/type.txt @@ -817,7 +817,7 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement())(')') PsiWhiteSpace(' ') @@ -840,7 +840,7 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement())(')') PsiWhiteSpace(' ') @@ -860,7 +860,7 @@ FILE PsiElement(!)('!') RsMacroArgumentImpl(MACRO_ARGUMENT) PsiElement(()('(') - RsCompactTTImpl(COMPACT_TT) + RsMacroArgumentTTImpl(MACRO_ARGUMENT_TT) PsiElement())(')') PsiElement(;)(';')