Skip to content

Commit

Permalink
correctly handle case where module has the same name as named address (
Browse files Browse the repository at this point in the history
…#274)

* correctly handle case where module has the same name as named address

* correct unresolved reference error for this case
  • Loading branch information
mkurnikov authored Jan 25, 2025
1 parent 3d27474 commit f3c311a
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,21 @@ class MvUnresolvedReferenceInspection: MvLocalInspectionTool() {
// attribute values are special case
if (path.hasAncestor<MvAttrItem>()) 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,
is QualifiedPath.ModuleOrItem -> {
val qualifier = pathKind.qualifier
// qualifier is unresolved, no need to resolve current path
if (qualifier.reference?.resolve() == null) return
}
else -> Unit
}
tryMultiResolveOrRegisterError(path, holder)
}
Expand Down
10 changes: 1 addition & 9 deletions src/main/kotlin/org/move/lang/core/psi/ext/MvModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,7 @@ val MvModule.friendModules: Sequence<MvModule>
get() {
return this.friendDeclList
.asSequence()
.mapNotNull { it.path?.reference?.resolveFollowingAliases() as? MvModule }
// return sequence {
// }
// val friends = mutableSetOf<MvModule>()
// 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<MvFunction> {
Expand Down
31 changes: 20 additions & 11 deletions src/main/kotlin/org/move/lang/core/resolve2/PathKind.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ sealed class PathKind {
override val ns: Set<Namespace> 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<Namespace>
): PathKind()

// 0x1::
data class ValueAddress(val address: Address.Value): PathKind() {
override val ns: Set<Namespace> get() = NONE
Expand All @@ -35,10 +41,14 @@ sealed class PathKind {
val qualifier: MvPath,
override val ns: Set<Namespace>
): PathKind() {
// `0x1:foo` or `aptos_framework::foo` (where aptos_framework is known named address)
// `0x1:foo`
class Module(path: MvPath, qualifier: MvPath, ns: Set<Namespace>, 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<Namespace>, 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<Namespace>):
QualifiedPath(path, qualifier, ns)
Expand Down Expand Up @@ -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)
}
}
}
Expand All @@ -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) {
Expand All @@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -576,4 +576,25 @@ module 0x1::m {
}
}
""")

@NamedAddress("uq64x64", "0x1")
fun `test no error on module for unresolved module if the same name as address`() = checkByText("""
module 0x1::m {
fun main() {
uq64x64::call();
}
}
""")

@NamedAddress("uq64x64", "0x2")
fun `test error on known item of module with the same name as address`() = checkByText("""
module uq64x64::uq64x64 {
}
module 0x1::m {
use uq64x64::uq64x64;
fun main() {
uq64x64::<error descr="Unresolved function: `call`">call</error>();
}
}
""")
}
110 changes: 110 additions & 0 deletions src/test/kotlin/org/move/lang/resolve/ResolveItemsTreeProjectTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
""")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
//^
}
Expand Down

0 comments on commit f3c311a

Please sign in to comment.