diff --git a/Sources/SWBMacro/MacroNamespace.swift b/Sources/SWBMacro/MacroNamespace.swift index eb42c54b..b26b17cc 100644 --- a/Sources/SWBMacro/MacroNamespace.swift +++ b/Sources/SWBMacro/MacroNamespace.swift @@ -41,11 +41,17 @@ public final class MacroNamespace: CustomDebugStringConvertible, Encodable, Send /// Descriptive label, for diagnostic purposes. public let debugDescription: String - /// Looks up and returns the macro declaration that's associated with ‘name’, if any. The name is not allowed to be the empty string. + /// Looks up and returns the macro declaration that's associated with 'name', if any. The name is not allowed to be the empty string. public func lookupMacroDeclaration(_ name: String) -> MacroDeclaration? { - return macroRegistry.withLock { macroRegistry in - return _lookupMacroDeclarationUnlocked(name, in: macroRegistry) + precondition(name != "") + var currentNamespace: MacroNamespace? = self + while let namespace = currentNamespace { + if let macroDecl = namespace.macroRegistry.withLock({ $0[name] }) { + return macroDecl + } + currentNamespace = namespace.parentNamespace } + return nil } /// Perform an unlocked macro lookup. @@ -137,11 +143,17 @@ public final class MacroNamespace: CustomDebugStringConvertible, Encodable, Send /// Maps condition parameter names to condition parameters. Each declaration is an instance of MacroConditionParameter. private let conditionParameters = LockedValue>([:]) - /// Looks up and returns the macro condition parameter that's associated with ‘name’, if any. The name is not allowed to be the empty string. + /// Looks up and returns the macro condition parameter that's associated with 'name', if any. The name is not allowed to be the empty string. public func lookupConditionParameter(_ name: String) -> MacroConditionParameter? { - conditionParameters.withLock { conditionParameters in - _lookupConditionParameterUnlocked(name, in: conditionParameters) + precondition(name != "") + var currentNamespace: MacroNamespace? = self + while let namespace = currentNamespace { + if let condParam = namespace.conditionParameters.withLock({ $0[name] }) { + return condParam + } + currentNamespace = namespace.parentNamespace } + return nil } private func _lookupConditionParameterUnlocked(_ name: String, in conditionParameters: [String: MacroConditionParameter]) -> MacroConditionParameter? { diff --git a/Tests/SWBMacroTests/MacroBasicTests.swift b/Tests/SWBMacroTests/MacroBasicTests.swift index bc4c3ea0..f8f4326d 100644 --- a/Tests/SWBMacroTests/MacroBasicTests.swift +++ b/Tests/SWBMacroTests/MacroBasicTests.swift @@ -183,5 +183,32 @@ import SWBMacro let middleLookedupCondParam = middleNamespace.lookupConditionParameter("upper") #expect(middleLookedupCondParam == nil) } + + @Test + func deepNamespaceHierarchy() throws { + let depth = 600 + var namespaces: [MacroNamespace] = [] + + let root = MacroNamespace(debugDescription: "root") + let rootMacro = try root.declareStringMacro("ROOT_MACRO") + namespaces.append(root) + + for i in 1..