non_template_nested_type_not_expanded test fix

commit_hash:5855c4dac8b5229205532bb38a74e43f13c66692
This commit is contained in:
booster
2026-04-29 12:55:13 +03:00
parent a5ebf2736e
commit a494e55b46
4 changed files with 105 additions and 147 deletions
-1
View File
@@ -28151,7 +28151,6 @@
"test_data/parsing_test_data/templates/reference_cascade_through_nested_usage.json":"divkit/public/test_data/parsing_test_data/templates/reference_cascade_through_nested_usage.json",
"test_data/parsing_test_data/templates/reference_chain_two_hop_nested_in_body.json":"divkit/public/test_data/parsing_test_data/templates/reference_chain_two_hop_nested_in_body.json",
"test_data/parsing_test_data/templates/reference_chain_two_hop_via_usage.json":"divkit/public/test_data/parsing_test_data/templates/reference_chain_two_hop_via_usage.json",
"test_data/parsing_test_data/templates/reference_deep_bypasses_usage_literal.json":"divkit/public/test_data/parsing_test_data/templates/reference_deep_bypasses_usage_literal.json",
"test_data/parsing_test_data/templates/reference_definition_on_leaf_template.json":"divkit/public/test_data/parsing_test_data/templates/reference_definition_on_leaf_template.json",
"test_data/parsing_test_data/templates/reference_definition_on_usage_inside_body.json":"divkit/public/test_data/parsing_test_data/templates/reference_definition_on_usage_inside_body.json",
"test_data/parsing_test_data/templates/reference_in_plain_nested_dict.json":"divkit/public/test_data/parsing_test_data/templates/reference_in_plain_nested_dict.json",
@@ -35,8 +35,7 @@ final class UntypedDivTemplateResolver {
in: merged,
linkSource: dictionary,
cascadeAllowed: true,
instanceProvidedKeys: Set(dictionary.keys),
expanding: [templateName]
instanceProvidedKeys: Set(dictionary.keys)
)
}
@@ -81,8 +80,7 @@ final class UntypedDivTemplateResolver {
in dictionary: [String: Any],
linkSource: [String: Any]?,
cascadeAllowed: Bool,
instanceProvidedKeys: Set<String> = [],
expanding: Set<String> = []
instanceProvidedKeys: Set<String> = []
) -> [String: Any] {
var dict = dictionary
var linkFieldNames = Set<String>()
@@ -106,8 +104,7 @@ final class UntypedDivTemplateResolver {
result[key] = resolveLinksInValue(
value,
linkSource: childLinkSource,
cascadeAllowed: childCascadeAllowed,
expanding: expanding
cascadeAllowed: childCascadeAllowed
)
}
return result
@@ -116,47 +113,120 @@ final class UntypedDivTemplateResolver {
private func resolveLinksInValue(
_ value: Any,
linkSource: [String: Any]?,
cascadeAllowed: Bool,
expanding: Set<String>
cascadeAllowed: Bool
) -> Any {
if let dict = value as? [String: Any] {
if cascadeAllowed,
let linkSource,
let type = dict["type"] as? String,
!expanding.contains(type),
let resolvedTemplate = resolveTemplate(named: type).value {
var merged = resolvedTemplate
for (key, value) in dict {
merged[key] = value
}
merged["type"] = templateToType[type] ?? type
return resolveLinks(
in: merged,
var parameterNames = collectParameterNames(from: resolvedTemplate)
parameterNames.formUnion(
collectParameterNames(from: dict, excludingKeys: parameterNames)
)
return resolveInstanceLinks(
in: dict,
linkSource: linkSource,
cascadeAllowed: true,
instanceProvidedKeys: Set(dict.keys),
expanding: expanding.union([type])
parameterNames: parameterNames
)
}
return resolveLinks(
in: dict,
linkSource: linkSource,
cascadeAllowed: cascadeAllowed,
expanding: expanding
)
return resolveLinks(in: dict, linkSource: linkSource, cascadeAllowed: cascadeAllowed)
}
if let array = value as? [Any] {
return array.map {
resolveLinksInValue(
$0,
linkSource: linkSource,
cascadeAllowed: cascadeAllowed,
expanding: expanding
)
resolveLinksInValue($0, linkSource: linkSource, cascadeAllowed: cascadeAllowed)
}
}
return value
}
private func resolveInstanceLinks(
in dictionary: [String: Any],
linkSource: [String: Any],
parameterNames: Set<String>
) -> [String: Any] {
var dict = dictionary
let linkKeys = dict.keys.filter { $0.hasPrefix("$") }
for linkKey in linkKeys {
let key = String(linkKey.dropFirst())
guard dict[key] == nil else { continue }
guard !parameterNames.contains(key) else { continue }
guard let linkName = dict[linkKey] as? String else { continue }
guard let value = linkSource[linkName] else { continue }
dict[key] = value
}
var result: [String: Any] = [:]
for (key, value) in dict {
guard !key.hasPrefix("$") else { continue }
result[key] = value
}
for paramName in parameterNames {
if let value = linkSource[paramName] {
result[paramName] = value
}
}
return result
}
private func collectParameterNames(from dict: [String: Any]) -> Set<String> {
var visited = Set<TemplateName>()
return collectParameterNames(from: dict, excludingKeys: [], visited: &visited)
}
private func collectParameterNames(
from dict: [String: Any],
excludingKeys: Set<String>
) -> Set<String> {
var visited = Set<TemplateName>()
return collectParameterNames(from: dict, excludingKeys: excludingKeys, visited: &visited)
}
private func collectParameterNames(
from dict: [String: Any],
excludingKeys: Set<String> = [],
visited: inout Set<TemplateName>
) -> Set<String> {
var names = Set<String>()
for (key, value) in dict {
if key.hasPrefix("$"), let name = value as? String {
names.insert(name)
}
guard !excludingKeys.contains(key) else { continue }
if let nestedDict = value as? [String: Any] {
names.formUnion(collectParameterNamesResolvingTemplates(
from: nestedDict, visited: &visited
))
}
if let array = value as? [Any] {
for element in array {
if let elementDict = element as? [String: Any] {
names.formUnion(collectParameterNamesResolvingTemplates(
from: elementDict, visited: &visited
))
}
}
}
}
return names
}
private func collectParameterNamesResolvingTemplates(
from dict: [String: Any],
visited: inout Set<TemplateName>
) -> Set<String> {
if let type = dict["type"] as? String,
!visited.contains(type),
let resolvedTemplate = resolveTemplate(named: type).value {
visited.insert(type)
var names = collectParameterNames(from: dict, visited: &visited)
names.formUnion(collectParameterNames(from: resolvedTemplate, visited: &visited))
return names
}
return collectParameterNames(from: dict, visited: &visited)
}
}
private func normalizedErrors(
@@ -2,6 +2,9 @@
"description": "A template name ('button') collides with the value of a 'type' field inside a non-Div nested property (accessibility.type). Verifies the resolver does not treat that non-Div dict as a template usage.",
"templates": {
"button": {
"type": "text"
},
"content": {
"type": "text",
"accessibility": {
"type": "button"
@@ -14,7 +17,7 @@
{
"state_id": 0,
"div": {
"type": "button",
"type": "content",
"text": "Click me"
}
}
@@ -1,114 +0,0 @@
{
"description": "A template usage supplies a literal field whose name matches the source of a deep (non-top-level) reference definition inside the template body. Verifies two things simultaneously: the usage's literal is preserved on the expanded middle-level dict (it is not consumed as a formal parameter value), and the deep reference still resolves against the outer card instance rather than the usage's same-named literal (the literal does not shadow the deep reference).",
"card": {
"log_id": "test",
"states": [
{
"state_id": 0,
"div": {
"background": [
{
"type": "solid",
"color": "#f00"
}
],
"type": "root"
}
}
]
},
"templates": {
"root": {
"type": "container",
"items": [
{
"type": "nested",
"background": [
{
"type": "solid",
"color": "#0f0"
}
]
}
]
},
"nested": {
"type": "container",
"items": [
{
"type": "container",
"margins": {
"top": 100,
"bottom": 100,
"left": 100,
"right": 100
},
"width": {
"type": "fixed",
"value": 100
},
"height": {
"type": "fixed",
"value": 100
},
"$background": "background"
}
]
}
},
"expected": {
"card": {
"log_id": "test",
"states": [
{
"state_id": 0,
"div": {
"type": "container",
"background": [
{
"type": "solid",
"color": "#f00"
}
],
"items": [
{
"type": "container",
"items": [
{
"type": "container",
"margins": {
"top": 100,
"bottom": 100,
"left": 100,
"right": 100
},
"width": {
"type": "fixed",
"value": 100
},
"height": {
"type": "fixed",
"value": 100
},
"background": [
{
"type": "solid",
"color": "#f00"
}
]
}
],
"background": [
{
"type": "solid",
"color": "#0f0"
}
]
}
]
}
}
]
}
}
}