Skip to content

Conversation

@Bergmann89
Copy link
Owner

@Bergmann89 Bergmann89 commented Dec 30, 2025

Some XML schemas reuse the same type name in different files. While this pattern isn't fully spec-compliant, it occurs in real world schemas so we have to support it too. This also makes implementing future support for xs:redefine and xs:override easier.

This commit restructures identifier handling:

  • Split the previous Ident type into distinct TypeIdent, NodeIdent, and PropertyIdent, reducing ambiguity between kinds of identifiers.
  • Make TypeIdent include a SchemaId, so the generator knows which schema the type came from in addition to its namespace.
  • Update the Interpreter and related logic to correctly resolve and differentiate types even when they share the same name across files.

These changes allow xsd-parser to handle duplicated type names across schema files in a coherent way and lay groundwork for future override support.

This is a fork/rewrite of the original PR provided by @main-- in #186.

Related to #156.

@Bergmann89 Bergmann89 force-pushed the support-duplicate-types branch from 060ab89 to 2668cdf Compare December 30, 2025 22:51
@Bergmann89 Bergmann89 force-pushed the support-duplicate-types branch 2 times, most recently from 7618ca3 to baf8c07 Compare December 30, 2025 23:29
@main--
Copy link
Contributor

main-- commented Jan 5, 2026

Looks good! I noticed two things:

My override for a built-in type

    config.interpreter.types.push((IdentTriple::from((IdentType::BuildIn, None, "bool")), MetaType {
        display_name: None,
        documentation: vec![],
        form: None,
        schema: None,
        variant: MetaTypeVariant::Custom(CustomMeta::new("MsBool")),
    }));

stopped working and I had to change it to:

    config.interpreter.types.push((IdentQuadruple::from((IdentType::BuildIn, NamespaceIdent::Id(NamespaceId::UNKNOWN), None::<SchemaIdent>, "bool")), MetaType {

The second observation concerns groups. Given the following schema:

foo.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:bar="Bar" xmlns:baz="Baz">
  <xs:import schemaLocation="bar.xsd" namespace="Bar" />
  <xs:import schemaLocation="baz.xsd" namespace="Baz" />
  <xs:complexType name="Outer">
    <xs:sequence>
      <xs:group ref="bar:Inner" />
      <xs:group ref="baz:Inner" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>

bar.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="Bar" targetNamespace="Bar" elementFormDefault="qualified">
    <xs:group name="Inner">
        <xs:sequence>
            <xs:element name="A" type="xs:string" />
        </xs:sequence>
    </xs:group>
</xs:schema>

baz.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="Baz" targetNamespace="Baz">
    <xs:group name="Inner">
        <xs:sequence>
            <xs:element name="B" type="xs:string" />
        </xs:sequence>
    </xs:group>
</xs:schema>

Previously this would create the OuterBarInnerType and OuterBazInnerType in the parent (root) module. Effectively each usage site of a group would instantiate its own copy of the group. This appears to still be the case, except that now the OuterBarInnerType is moved into the bar module. This seems a bit strange to me however. because it means that a group reference from outside bar.xsd will end up creating a type inside the bar module. Ideally there would just be one bar::Inner but I'm not sure if this is possible.

@Bergmann89
Copy link
Owner Author

Bergmann89 commented Jan 5, 2026

My override for a built-in type

Strange, I though I've added fallbacks for the old type 🤔 I will check this.
BTW: You don't have to set all information if you don't have it. In your case IdentQuadruple::from((IdentType::BuildIn, "bool")) should also work.

The second observation concerns groups.

Ok. I'll have a look too and add a suitable test to catch this. Might be possible that the new stack entry broke some stuff.

Some XML schemas reuse the same type name in different files. While this
pattern isn't fully spec-compliant, it occurs in real world schemas so we
have to support it too. This also makes implementing future support for
`xs:redefine` and `xs:override` easier.

This commit restructures identifier handling:

- Split the previous `Ident` type into distinct `TypeIdent`, `NodeIdent`,
  and `PropertyIdent`, reducing ambiguity between kinds of identifiers.
- Make `TypeIdent` include a `SchemaId`, so the generator knows which
  schema the type came from in addition to its namespace.
- Update the `Interpreter` and related logic to correctly resolve and
  differentiate types even when they share the same name across files.

These changes allow `xsd-parser` to handle duplicated type names across
schema files in a coherent way and lay groundwork for future override
support.
@Bergmann89 Bergmann89 force-pushed the support-duplicate-types branch from baf8c07 to 6791d55 Compare January 11, 2026 14:34
@Bergmann89
Copy link
Owner Author

Hey @main--,

I had some time today to address your findings.

My override for a built-in type

This is cause because I changed this:

impl<N, X> From<(IdentType, N, X)> for IdentTriple
where
    N: Into<Option<NamespaceIdent>>,
    X: Into<String>,
{
    fn from((type_, ns, name): (IdentType, N, X)) -> Self {
        /* ... */
    }
}

Into this:

impl<N, X> From<(IdentType, N, X)> for IdentQuadruple
where
    N: Into<NamespaceIdent>,
    X: Into<String>,
{
    fn from((type_, ns, name): (IdentType, N, X)) -> Self {
        /* ... */
    }
}

impl<N, X> From<(IdentType, Option<N>, X)> for IdentQuadruple
where
    N: Into<NamespaceIdent>,
    X: Into<String>,
{
    fn from((type_, ns, name): (IdentType, Option<N>, X)) -> Self {
        /* ... */
    }
}

So you now have to either specifiy the exact type of your passed None like so: IdentTriple::from((IdentType::BuildIn, None::<Namespace>, "bool")) or - as already mentioned above - you can drop the namespace information and use a tuple of two: IdentTriple::from((IdentType::BuildIn, "bool")).

I known this is some kind of breaking change of the public API, but I would like to keep it as is, because the new implementation gives you the option to construct the value like so IdentTriple::from((IdentType::BuildIn, Namespace::new_const(b"fuuu"), "bool")) instead of forcing the user to use the Option. If you still need the option you can use it, but the compiler is not able to guess the correct type for the None anymore.

The second observation concerns groups.

I fixed that and added a suitable test.

@main--
Copy link
Contributor

main-- commented Jan 12, 2026

So you now have to either specifiy the exact type of your passed None like so: IdentTriple::from((IdentType::BuildIn, None::<Namespace>, "bool")) or - as already mentioned above - you can drop the namespace information and use a tuple of two: IdentTriple::from((IdentType::BuildIn, "bool")).

That's not what I meant. The problem is that IdentTriple::from((IdentType::BuildIn, None::<Namespace>, "bool")) compiles but does not resolve to NamespaceId::UNKNOWN and therefore fails to override the built-in boolean type.

I can confirm that the groups are back to the old behavior now. 👍

@Bergmann89
Copy link
Owner Author

Ah, Ok. Now I got it. The new approach makes a difference between the "anonymous" namespace and "this namespace is unknown", which were both covered by None in the old code. In the new code resolving the "unknown" namespace can be considered to be an error, but I still used for the build-in types. I fixed that and assigned the "anonymous" namespace to them. Now your IdentTriple::from((IdentType::BuildIn, None::<Namespace>, "bool")) should be resolved correctly :)

@Bergmann89 Bergmann89 merged commit 1f8e1ef into master Jan 13, 2026
2 checks passed
@Bergmann89 Bergmann89 deleted the support-duplicate-types branch January 13, 2026 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants