From 7943627425ea12c8669000bfe83680980a83d118 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Fri, 24 Jan 2025 22:20:45 +0100 Subject: [PATCH] correctly handle case where module has the same name as named address --- .../MvUnresolvedReferenceInspection.kt | 16 ++- .../org/move/lang/core/psi/ext/MvModule.kt | 10 +- .../org/move/lang/core/resolve2/PathKind.kt | 31 +++-- .../core/resolve2/ref/MvPath2ReferenceImpl.kt | 5 +- .../resolve/ResolveItemsTreeProjectTest.kt | 110 ++++++++++++++++++ .../move/lang/resolve/ResolveModulesTest.kt | 2 +- 6 files changed, 144 insertions(+), 30 deletions(-) diff --git a/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt index 432ed679d..c3a3448ea 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt @@ -31,16 +31,20 @@ class MvUnresolvedReferenceInspection: MvLocalInspectionTool() { // attribute values are special case if (path.hasAncestor()) return -// val pathReference = path.reference ?: return val pathKind = path.pathKind() when (pathKind) { - is NamedAddress, is ValueAddress -> return + is NamedAddressOrUnqualifiedPath, is NamedAddress, is ValueAddress -> return is UnqualifiedPath -> tryMultiResolveOrRegisterError(path, holder) is QualifiedPath -> { - if (pathKind !is QualifiedPath.Module) { - val qualifier = pathKind.qualifier - // qualifier is unresolved, no need to resolve current path - if (qualifier.reference?.resolve() == null) return + when (pathKind) { + is QualifiedPath.ModuleItem, + is QualifiedPath.FQModuleItem, + is QualifiedPath.UseGroupItem -> { + val qualifier = pathKind.qualifier + // qualifier is unresolved, no need to resolve current path + if (qualifier.reference?.resolve() == null) return + } + else -> Unit } tryMultiResolveOrRegisterError(path, holder) } diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvModule.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvModule.kt index 1d6e32603..af12860a3 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvModule.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvModule.kt @@ -33,15 +33,7 @@ val MvModule.friendModules: Sequence get() { return this.friendDeclList .asSequence() - .mapNotNull { it.path?.reference?.resolveFollowingAliases() as? MvModule } -// return sequence { -// } -// val friends = mutableSetOf() -// for (modulePath in friendModulePaths) { -// val module = modulePath.reference?.resolveFollowingAliases() as? MvModule ?: continue -// friends.add(module) -// } -// return friends + .mapNotNull { it.path.reference?.resolveFollowingAliases() as? MvModule } } fun MvModule.allFunctions(): List { diff --git a/src/main/kotlin/org/move/lang/core/resolve2/PathKind.kt b/src/main/kotlin/org/move/lang/core/resolve2/PathKind.kt index 37c10a7da..1d3850bcd 100644 --- a/src/main/kotlin/org/move/lang/core/resolve2/PathKind.kt +++ b/src/main/kotlin/org/move/lang/core/resolve2/PathKind.kt @@ -21,6 +21,12 @@ sealed class PathKind { override val ns: Set get() = NONE } + // aptos_std:: where aptos_std is a existing named address in a project + data class NamedAddressOrUnqualifiedPath( + val address: Address.Named, + override val ns: Set + ): PathKind() + // 0x1:: data class ValueAddress(val address: Address.Value): PathKind() { override val ns: Set get() = NONE @@ -35,10 +41,14 @@ sealed class PathKind { val qualifier: MvPath, override val ns: Set ): PathKind() { - // `0x1:foo` or `aptos_framework::foo` (where aptos_framework is known named address) + // `0x1:foo` class Module(path: MvPath, qualifier: MvPath, ns: Set, val address: Address): QualifiedPath(path, qualifier, ns) + // `aptos_framework::foo` (where aptos_framework is known named address, but it can still be a module) + class ModuleOrItem(path: MvPath, qualifier: MvPath, ns: Set, val address: Address): + QualifiedPath(path, qualifier, ns) + // bar in foo::bar, where foo is not a named address class ModuleItem(path: MvPath, qualifier: MvPath, ns: Set): QualifiedPath(path, qualifier, ns) @@ -96,7 +106,7 @@ fun MvPath.pathKind(isCompletion: Boolean = false): PathKind { if (this.isColonColonNext) { val namedAddress = moveProject.getNamedAddressTestAware(referenceName) if (namedAddress != null) { - return PathKind.NamedAddress(namedAddress) + return PathKind.NamedAddressOrUnqualifiedPath(namedAddress, MODULES) } } } @@ -107,6 +117,7 @@ fun MvPath.pathKind(isCompletion: Boolean = false): PathKind { } val qualifierOfQualifier = qualifier.path + val ns = this.allowedNamespaces(isCompletion) // two-element paths if (qualifierOfQualifier == null) { @@ -123,26 +134,24 @@ fun MvPath.pathKind(isCompletion: Boolean = false): PathKind { // ^ moveProject != null && qualifierItemName != null -> { val namedAddress = moveProject.getNamedAddressTestAware(qualifierItemName) - if (namedAddress != null) { - // known named address, can be module path - return PathKind.QualifiedPath.Module(this, qualifier, MODULES, namedAddress) + if (this.isUseSpeck) { + val address = + namedAddress ?: Address.Named(qualifierItemName, null) + return PathKind.QualifiedPath.Module(this, qualifier, MODULES, address) } // `use std::main` // ^ - // , where std is the unknown named address - if (this.isUseSpeck) { - val address = Address.Named(qualifierItemName, null) - return PathKind.QualifiedPath.Module(this, qualifier, MODULES, address) + if (namedAddress != null) { + // known named address, can be module path, or module item path too + return PathKind.QualifiedPath.ModuleOrItem(this, qualifier, MODULES + ns, namedAddress) } } } // module::name // ^ - val ns = this.allowedNamespaces(isCompletion) return PathKind.QualifiedPath.ModuleItem(this, qualifier, ns) } // three-element path - val ns = this.allowedNamespaces(isCompletion) return PathKind.QualifiedPath.FQModuleItem(this, qualifier, ns) } diff --git a/src/main/kotlin/org/move/lang/core/resolve2/ref/MvPath2ReferenceImpl.kt b/src/main/kotlin/org/move/lang/core/resolve2/ref/MvPath2ReferenceImpl.kt index e857f5fa1..66c5b35c8 100644 --- a/src/main/kotlin/org/move/lang/core/resolve2/ref/MvPath2ReferenceImpl.kt +++ b/src/main/kotlin/org/move/lang/core/resolve2/ref/MvPath2ReferenceImpl.kt @@ -9,7 +9,6 @@ import org.move.lang.core.resolve.* import org.move.lang.core.resolve.ref.* import org.move.lang.core.resolve.ref.Namespace.* import org.move.lang.core.resolve2.* -import org.move.lang.core.resolve2.PathKind.NamedAddress import org.move.lang.core.resolve2.PathKind.ValueAddress import org.move.lang.core.types.infer.inference import org.move.lang.core.types.ty.Ty @@ -151,8 +150,8 @@ fun processPathResolveVariants( processor: RsResolveProcessor ): Boolean { return when (pathKind) { - is NamedAddress, is ValueAddress -> false - is PathKind.UnqualifiedPath -> { + is PathKind.NamedAddress, is ValueAddress -> false + is PathKind.NamedAddressOrUnqualifiedPath, is PathKind.UnqualifiedPath -> { if (MODULE in pathKind.ns) { // Self:: if (processor.lazy("Self", MODULES) { ctx.containingModule }) return true diff --git a/src/test/kotlin/org/move/lang/resolve/ResolveItemsTreeProjectTest.kt b/src/test/kotlin/org/move/lang/resolve/ResolveItemsTreeProjectTest.kt index fdabe7b63..90a38ac7f 100644 --- a/src/test/kotlin/org/move/lang/resolve/ResolveItemsTreeProjectTest.kt +++ b/src/test/kotlin/org/move/lang/resolve/ResolveItemsTreeProjectTest.kt @@ -543,4 +543,114 @@ module 0x1::main { """) } } + + fun `test resolve module that has the same name as address`() = checkByFileTree { + moveToml(""" + [package] + name = "Main" + + [dependencies] + UQ64x64 = { local = "./uq64x64" } + """) + sources { + main(""" + module 0x1::m { + use uq64x64::uq64x64; + //^ + } + """) + } + dir("uq64x64") { + moveToml(""" + [package] + name = "UQ64x64" + + [addresses] + uq64x64 = "0x4e9fce03284c0ce0b86c88dd5a46f050cad2f4f33c4cdd29d98f501868558c81" + """) + sources { + move("uq64x64.move", """ + module uq64x64::uq64x64 { + //X + } + """) + } + } + } + + fun `test resolve module in path from module that has the same name as address`() = checkByFileTree { + moveToml(""" + [package] + name = "Main" + + [dependencies] + UQ64x64 = { local = "./uq64x64" } + """) + sources { + main(""" + module 0x1::m { + use uq64x64::uq64x64; + fun main() { + uq64x64::call(); + //^ + } + } + """) + } + dir("uq64x64") { + moveToml(""" + [package] + name = "UQ64x64" + + [addresses] + uq64x64 = "0x4e9fce03284c0ce0b86c88dd5a46f050cad2f4f33c4cdd29d98f501868558c81" + """) + sources { + move("uq64x64.move", """ + module uq64x64::uq64x64 { + //X + public fun call() {} + } + """) + } + } + } + + fun `test resolve function from module that has the same name as address`() = checkByFileTree { + moveToml(""" + [package] + name = "Main" + + [dependencies] + UQ64x64 = { local = "./uq64x64" } + """) + sources { + main(""" + module 0x1::m { + use uq64x64::uq64x64; + fun main() { + uq64x64::call(); + //^ + } + } + """) + } + dir("uq64x64") { + moveToml(""" + [package] + name = "UQ64x64" + + [addresses] + uq64x64 = "0x4e9fce03284c0ce0b86c88dd5a46f050cad2f4f33c4cdd29d98f501868558c81" + """) + sources { + move("uq64x64.move", """ + module uq64x64::uq64x64 { + public fun call() {} + //X + } + """) + } + } + } } diff --git a/src/test/kotlin/org/move/lang/resolve/ResolveModulesTest.kt b/src/test/kotlin/org/move/lang/resolve/ResolveModulesTest.kt index 776357994..cc75413b3 100644 --- a/src/test/kotlin/org/move/lang/resolve/ResolveModulesTest.kt +++ b/src/test/kotlin/org/move/lang/resolve/ResolveModulesTest.kt @@ -342,7 +342,7 @@ class ResolveModulesTest : ResolveTestCase() { fun `test resolve friend module with named address`() = checkByCode(""" module aptos_std::myfriend {} //X - module 0x1::main { + module aptos_std::main { friend aptos_std::myfriend; //^ }