lutaml-xsd 1.2.81 → 1.2.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 467b4c2343707af1d200289a1666d12e3ab984c438f0a29ef601d92143914562
4
- data.tar.gz: d249de5b32c7fb201c8025cf56af088bde07acb6e1f76d2cd3ae1f15bd39cf29
3
+ metadata.gz: 8c3da66790f16686077f84ff27947241c3fd9290e4a36f92d5b26ee70d340af4
4
+ data.tar.gz: 205af1e6a170523b054c34ba0cf43b86203877784bfe471ef88e47bcdbf60ae9
5
5
  SHA512:
6
- metadata.gz: 0f9c9c9c71bf3e064dd93ab6e007df3f9d90aaad2478bf77d8f7b7915804b5e8218a6d8adb97be02293560dc261ed957a6fe09af5404a30b18ee7a0d5d1a941e
7
- data.tar.gz: 539a1503358510cde20356dc3d6a0a1a2c0b9daaebf77bda9ef45bc54f45222a0eaf2d97477b05f1347b1d35dcc22742963b0fc388823217bc6cd45ceb900bdf
6
+ metadata.gz: 6901b1b6a2fc09e00325fe3e24df146097e6bf6967493e46e04d7eabe8b7a9183cdccc2cc8ccf39cfd9d0f3cf90157b0647c772f61512a68085c041fbf1329be
7
+ data.tar.gz: 221ce2f9ce160b0524a99cb9dfb029ccc10040adbe8282e8cb37ecbc94cc9f689e1a6b2e28a6ca27cd069ef2c6547a5d728f37a3254b5fc501b9cce50d27118c
data/TODO.conformance.md CHANGED
@@ -120,7 +120,7 @@ pinned commit) is measured via `rake conformance:w3c`:
120
120
 
121
121
  | mode | pass-rate |
122
122
  |---|---|
123
- | pipeline | 4573/4696 (97.38%) |
123
+ | pipeline | 4578/4696 (97.49%) |
124
124
 
125
125
  (2026-11-02, bundled-stack measurement with the escape,
126
126
  hint-following, pattern-alternative, identity-crash, engine-set,
@@ -142,7 +142,8 @@ lockfile refresh) → 4411 (93.9%) → 4430 (94.3%) → 4435 (94.4%) →
142
142
  4436 (94.5%) → 4439 (94.5%) → 4454 (94.9%) → 4495 (95.7%) → 4524
143
143
  (96.3%) → 4534 (96.6%) → 4540 (96.7%) → 4542 (96.7%) → 4546 (96.8%)
144
144
  → 4557 (97.0%) → 4565 (97.2%) → 4569 (97.3%) → 4562 (drift raw) →
145
- 4563 (97.17%, ref-namespace round) → 4573 (97.38%, group-content round), +612 net over the 1.2.43 baseline. The escapes round (PR #197 — \d→\p{Nd},
145
+ 4563 (97.17%, ref-namespace round) → 4573 (97.38%, group-content
146
+ round) → 4578 (97.49%, attribute-prohibition round), +617 net over the 1.2.43 baseline. The escapes round (PR #197 — \d→\p{Nd},
146
147
  \w/\W/\s to their XSD value spaces) collapses Regex 120→69;
147
148
  same-step pattern facets now behave as alternatives and
148
149
  whitespace-only text reaches the facets intact (PR #200), Regex
@@ -232,15 +233,19 @@ corners.
232
233
 
233
234
  | Class | Cases | Filed |
234
235
  |---|---|---|
235
- | Facade strict re-check on composed schemas — remaining shapes (ctE019 attribute re-check, addB116 substitution admission) | ~8 | moxml#510 (+2 follow-up comments) |
236
- | Redefine self-extension drops the extension content model | 0 — FIXED in leptris 1.9.396 | leptris#1926 (closed) |
237
- | Built-in lexical tables reject valid forms (char-reference shapes; gMonth FIXED 1.9.398, \d legacy-Nd FIXED 1.9.399, stG/stH list lexicals FIXED 1.9.400/401) | ~5 | leptris#1936 |
238
- | Automaton size cap surfaces as an instance error instead of engine-inert silence | 0 — FIXED in leptris 1.9.398 | moxml#514 (follow-up to #473) |
239
- | Regex dialect remainder (engine `\d` vintage, `\p{Is...}` blocks, char-reference shapes) | ~30 | engine-side of leptris#1936 |
240
- | Empty content against a fixed element value rejected (1.9.402.0 regression — isDefault001/002/076, ctZ013a, elemQ019, stE065/066/096, particlesZ040) | 9 | leptris-rs#25 |
236
+ | Empty content against a fixed element value rejected (1.9.402.0 regression — isDefault001/002/076, ctZ013a, elemQ019, stE065/066/096, particlesZ040; attribute leg addB104, attO006) | 15 | leptris-rs#25 (+ case-list comment) |
237
+ | Content model enforced on NILLED elements (QFE1700g2, particlesIa002) | 2 | leptris-rs#26 |
238
+ | dateTime enumeration rejects timezone-equal forms (dateTime_enumeration005a/b) | 2 | leptris-rs#27 |
239
+ | "-0" rejected against nonNegativeInteger (errE002) | 1 | leptris-rs#28 |
240
+ | xsi:type override / substitution-member type binding false rejects (elemZ002, elemZ020) | 2 | leptris-rs#29 |
241
+ | Built-in lexical tables (char-reference shapes reZ003v/004v/005v/006v/006i, reK88; \D vintage reT51) | 7 | leptris/leptris#1936 (+ case-list comment) |
242
+ | Facade strict re-check on composed schemas — attributes (attgD005/019/020/021/022/025/027/029/031/033/034, attJ018) and composed type binding (schF1/G2/G7/P2) | 15 | moxml#510 (+ consolidated evidence comment) |
241
243
  | Unresolvable include/import/redefine schemaLocation raises at parse instead of warning (anyURI_a001/a002, schD7, schG8) | 4 | lutaml-model#1087 |
242
- | Strict wildcard/attribute admission on further compositions | ~5 | moxml#510 thread |
243
- | Ruby-side under-detections (Group 16, DataTypes 7, IC 6, Attribute 5, Additional 4, Particles 4, SimpleType 4, Element 1, Wildcards 1 — minus what the regex round banked) | ~60 | OURS — the remaining Ruby work queue |
244
+ | Automaton size cap surfaces as an instance error instead of engine-inert silence | 0 — FIXED in leptris 1.9.398 | moxml#514 (follow-up to #473) |
245
+ | Redefine self-extension drops the extension content model | 0 — FIXED in leptris 1.9.396 | leptris#1926 (closed) |
246
+ | Ruby-side under-detections and false rejects (Regex dialect 18 — reJ/reS/reDH disputed families excluded; DataTypes corners duration028-030/dateTime013/gMonthDay006/dtZ86723; IC idK/idZ singletons; SimpleType stZ; Wildcards wildZ013; Group/Particles stragglers; Schema schT/D10/F2/errC007; Q032 chameleon import; particlesA013/A002/Ie004/Ig003/Ob012/015/Z001; mgO006/034; attF001/attJ002/003/attLa005; isDefault079) | ~55 | OURS — the remaining Ruby work queue |
247
+
248
+ Engine-attribution census (2026-11-03): of the 123 current failures, 50 carry engine findings (all attributed above), 73 are Ruby-side.
244
249
 
245
250
  Suite history (same-stack where known): 71.8% baseline → 83.5%
246
251
  (pre-NFA) → 83.6% (NFA + guard, 1.2.41) → 84.3% (#180) → 86.0%
@@ -450,6 +455,21 @@ tuple keys, node-set field violations and absent-field rows handled, cross-eleme
450
455
  import; our index keeps them no-namespace so the four e2 refs
451
456
  mis-resolve). Zero regressions; the drift entries above remain
452
457
  (fixed-empty 9, reU6).
458
+ - ATTRIBUTE-PROHIBITION ROUND (2026-11-03): +5 Attribute (9→4,
459
+ attF001/J002/J003/Z007i under-detections + attLa005 fixed-facet;
460
+ suite 4573→4578 = 97.49%, ZERO regressions, rspec green).
461
+ use=prohibited was never enforced — instances could carry a
462
+ prohibited attribute unchecked. The declaration binds now, with
463
+ THREE suite-arbitrated exemptions for incoherent shapes: a
464
+ wildcard admitting the attribute shadows it (attZ002); fixed +
465
+ prohibited is an illegal combo treated as inert (attP031); a
466
+ prohibited member inside an attributeGroup DEFINITION is illegal
467
+ placement the suite ignores (attZ015). A derived type's OWN
468
+ declaration overrides the base chain's prohibited (attZ007v) while
469
+ a restriction body's own prohibited stands (attZ007i). A ref's
470
+ use-site fixed overlays the global declaration in the value check
471
+ (attLa005). Attribute remainder (4): attJ018 + attO009/011
472
+ (engine-silent — queue), attO006 (leptris-rs#25 attribute leg).
453
473
  - REGEX SEMANTICS ROUND (2026-10-31, PR #208): \w/\W now carry their
454
474
  Appendix F complements (\w = [^\p{P}\p{Z}\p{C}], class interiors
455
475
  via the exact positive partition L/M/N/S), \I/\C complements added
@@ -94,11 +94,24 @@ module Lutaml
94
94
  resolved_attributes,
95
95
  xml_attributes, collector)
96
96
 
97
+ # Step 1b: Check prohibited attributes are absent — the
98
+ # declaration exists, its USE does not (attF001). Three
99
+ # suite-arbitrated exemptions: a wildcard that admits the
100
+ # attribute shadows the prohibition (attZ002); fixed plus
101
+ # prohibited is an illegal declaration combo and the suite
102
+ # treats the whole declaration as inert (attP031); and a
103
+ # prohibition arriving through DERIVATION is refinement
104
+ # incoherence the suite ignores (attZ007v, attZ015).
105
+ validate_prohibited_attributes(xml_element, schema_attributes,
106
+ xml_attributes, collector,
107
+ wildcards: wildcards)
108
+
97
109
  # Step 2: Validate attribute values
98
110
  validate_attribute_values(xml_element, resolved_attributes,
99
111
  xml_attributes, collector,
100
112
  repository: repository,
101
- schema_element: schema_element)
113
+ schema_element: schema_element,
114
+ raw_attrs: schema_attributes)
102
115
 
103
116
  # Step 3: Check for unexpected attributes
104
117
  validate_no_unexpected_attributes(xml_element,
@@ -224,9 +237,35 @@ module Lutaml
224
237
  attributes.concat(inherited_attributes(part.base, repository)) if part
225
238
  end
226
239
 
240
+ override_base_prohibitions(schema_def, attributes)
227
241
  attributes
228
242
  end
229
243
 
244
+ # A derived type's OWN (type-level or derivation-body)
245
+ # non-prohibited declaration for a name overrides the base
246
+ # chain's prohibited (attZ007v: the extension re-adds att1
247
+ # required over Two's prohibited). A restriction body's own
248
+ # prohibited stands against the inherited plain declaration
249
+ # (attZ007i: e2 carrying att1 is invalid).
250
+ #
251
+ # @return [void]
252
+ def override_base_prohibitions(schema_def, attributes)
253
+ own_declarations = Array(schema_def.attribute)
254
+ content = schema_def.complex_content || schema_def.simple_content
255
+ CONTENT_PART_KINDS.each do |kind|
256
+ part = content && Array(content.public_send(kind)).first
257
+ own_declarations += Array(part&.attribute) if part
258
+ end
259
+ own_names = own_declarations
260
+ .reject { |a| a.use.to_s == "prohibited" }
261
+ .map { |a| a.name.to_s }
262
+ attributes.reject! do |a|
263
+ a.use.to_s == "prohibited" &&
264
+ own_names.include?(a.name.to_s) &&
265
+ !own_declarations.include?(a)
266
+ end
267
+ end
268
+
230
269
  # The base type's attribute set flows into the derived type
231
270
  # through extension and restriction alike.
232
271
  #
@@ -275,7 +314,11 @@ module Lutaml
275
314
  return [] unless group
276
315
 
277
316
  own = (group.name || group.ref).to_s.split(":").last
278
- attrs = Array(group.attribute)
317
+ # use=prohibited is illegal inside an attributeGroup
318
+ # DEFINITION (attZ015): the suite ignores such members.
319
+ attrs = Array(group.attribute).reject do |attr|
320
+ attr.use.to_s == "prohibited"
321
+ end
279
322
 
280
323
  # An attributeGroup may reference other attributeGroups —
281
324
  # their declarations flow into the expansion (attgD004). A
@@ -393,6 +436,44 @@ module Lutaml
393
436
  schema_attr.use == "required"
394
437
  end
395
438
 
439
+ # A prohibited attribute is DECLARED but its use is banned:
440
+ # any instance occurrence violates the schema (attF001).
441
+ #
442
+ # @return [void]
443
+ def validate_prohibited_attributes(xml_element, schema_attrs,
444
+ xml_attrs, collector,
445
+ wildcards: [])
446
+ schema_attrs.each do |schema_attr|
447
+ next unless schema_attr.use.to_s == "prohibited"
448
+ # fixed + prohibited is an illegal declaration combo
449
+ # (attP031): the suite treats the declaration as inert.
450
+ next if schema_attr.fixed
451
+
452
+ attr_name = schema_attr.name.to_s
453
+ next if attr_name.empty?
454
+
455
+ xml_attr = xml_attrs.find { |xa| xa.name == attr_name }
456
+ next unless xml_attr
457
+ next if wildcards.any? do |wildcard|
458
+ wildcard_permits_attribute?(wildcard, xml_element, xml_attr,
459
+ nil)
460
+ end
461
+
462
+ report_error(
463
+ collector,
464
+ code: "prohibited_attribute_present",
465
+ message: "Attribute '#{attr_name}' is prohibited here " \
466
+ "but present",
467
+ location: xml_element.xpath,
468
+ context: {
469
+ attribute: attr_name,
470
+ element: xml_element.name,
471
+ },
472
+ suggestion: "Remove attribute: #{attr_name}",
473
+ )
474
+ end
475
+ end
476
+
396
477
  # Validate attribute values
397
478
  #
398
479
  # @param xml_element [XmlElement] The element
@@ -402,14 +483,23 @@ module Lutaml
402
483
  # @return [void]
403
484
  def validate_attribute_values(xml_element, schema_attrs, xml_attrs,
404
485
  collector, repository: nil,
405
- schema_element: nil)
486
+ schema_element: nil, raw_attrs: [])
406
487
  xml_attrs.each do |xml_attr|
407
488
  schema_attr = find_schema_attribute(schema_attrs, xml_attr,
408
489
  repository, schema_element)
409
490
  next unless schema_attr
410
491
 
492
+ # A ref's use site may carry facets the global declaration
493
+ # lacks (attLa005: ref="x:ga1" fixed="abc" at the use
494
+ # site); the instance value is checked against them too.
495
+ use_site = raw_attrs.find do |r|
496
+ r.name == schema_attr.name ||
497
+ r.ref.to_s.split(":").last == schema_attr.name
498
+ end
411
499
  validate_attribute_value(xml_element, xml_attr, schema_attr,
412
- collector, repository: repository)
500
+ collector,
501
+ repository: repository,
502
+ use_site: use_site)
413
503
  end
414
504
  end
415
505
 
@@ -461,10 +551,11 @@ module Lutaml
461
551
  # "123").
462
552
  #
463
553
  # @return [Boolean]
464
- def fixed_value_matches?(value, schema_attr)
554
+ def fixed_value_matches?(value, schema_attr, fixed_override = nil)
555
+ fixed = fixed_override || schema_attr.fixed
465
556
  rule = attribute_whitespace_rule(schema_attr)
466
557
  canonical_lexical(value, rule) ==
467
- canonical_lexical(schema_attr.fixed, rule)
558
+ canonical_lexical(fixed, rule)
468
559
  end
469
560
 
470
561
  # @return [Symbol] :preserve, :replace or :collapse
@@ -499,21 +590,27 @@ module Lutaml
499
590
  # @param collector [ResultCollector] Result collector
500
591
  # @return [void]
501
592
  def validate_attribute_value(xml_element, xml_attr, schema_attr,
502
- collector, repository: nil)
593
+ collector, repository: nil,
594
+ use_site: nil)
503
595
  value = xml_attr.value
596
+ fixed = if use_site && !use_site.fixed.nil?
597
+ use_site.fixed
598
+ else
599
+ schema_attr.fixed
600
+ end
504
601
 
505
602
  # Check fixed value constraint
506
- if schema_attr.fixed
507
- unless fixed_value_matches?(value, schema_attr)
603
+ if fixed
604
+ unless fixed_value_matches?(value, schema_attr, fixed)
508
605
  report_error(
509
606
  collector,
510
607
  code: "fixed_attribute_value_mismatch",
511
608
  message: "Attribute '#{xml_attr.name}' must have fixed " \
512
- "value '#{schema_attr.fixed}'",
609
+ "value '#{fixed}'",
513
610
  location: xml_element.xpath,
514
611
  context: {
515
612
  attribute: xml_attr.name,
516
- expected: schema_attr.fixed,
613
+ expected: fixed,
517
614
  actual: value,
518
615
  },
519
616
  )
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Xsd
5
- VERSION = "1.2.81"
5
+ VERSION = "1.2.82"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-xsd
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.81
4
+ version: 1.2.82
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-11-02 00:00:00.000000000 Z
11
+ date: 2026-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liquid