Skip to content

Fully qualify references to generated interfaces#1

Open
simonmckenzie wants to merge 8 commits into
ChristianSauer:masterfrom
simonmckenzie:feature/fully-qualify-references-to-generated-interfaces
Open

Fully qualify references to generated interfaces#1
simonmckenzie wants to merge 8 commits into
ChristianSauer:masterfrom
simonmckenzie:feature/fully-qualify-references-to-generated-interfaces

Conversation

@simonmckenzie
Copy link
Copy Markdown

This is a copy of codecentric#89, reopening after the sunsetting of that project.

This addresses an issue where, when a class references a generated interface, those references are not fully qualified, resulting in an interface that doesn't compile. See codecentric#87.

The change is to precalculate the list of interface names that will be generated, and, during symbol string generation, replace unrecognised symbols with generated names where a single unambiguous match can be made between the symbol and the list of interfaces being generated.

For the example provided in codecentric#87, the code will now generate this output:

//--------------------------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>
//--------------------------------------------------------------------------------------------------

namespace Test
{
    [global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
    public partial interface ITest1
    {
        /// <inheritdoc cref="Test.Test1.Get(ITest2)" />
        void Get(global::Test.Tests.ITest2 test);
        
    }
}


//--------------------------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>
//--------------------------------------------------------------------------------------------------

namespace Test.Tests
{
    [global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
    public partial interface ITest2
    {
    }
}

@simonmckenzie simonmckenzie force-pushed the feature/fully-qualify-references-to-generated-interfaces branch 2 times, most recently from 8e7605b to 640a9bb Compare April 23, 2026 22:17
@simonmckenzie simonmckenzie marked this pull request as draft April 23, 2026 22:19
This addresses an issue where, when a class references a generated interface, those references are not fully qualified, resulting in an interface that doesn't compile.

The change is to precalculate the list of interface names that will be generated, and, during symbol string generation, replace unrecognised symbols with generated names _where a single unambiguous match can be made between the symbol and the list of interfaces being generated_.
- Change approach to use `ToDisplayParts`, which removes the need for regex parsing of generated code
- Fix bug in `ReplaceWithInferredInterfaceName` where dots weren't being escaped
- Add tests to ensure partially qualified references will be resolved correctly
…class; refactor

This centralises the rendering logic plus simplifies the implementation to use `ITypeSymbol.WithNullableAnnotation` and hands the rendering off to the default `ToDisplayString` implementation
@simonmckenzie simonmckenzie force-pushed the feature/fully-qualify-references-to-generated-interfaces branch from 640a9bb to 44b679e Compare April 23, 2026 22:22
@simonmckenzie simonmckenzie changed the title Feature/fully qualify references to generated interfaces Fully qualify references to generated interfaces Apr 23, 2026
@simonmckenzie simonmckenzie marked this pull request as ready for review April 23, 2026 22:35
@simonmckenzie simonmckenzie marked this pull request as draft April 23, 2026 22:36
@simonmckenzie
Copy link
Copy Markdown
Author

simonmckenzie commented Apr 23, 2026

Resolved - I've done the split as I suggested below.

Previous body:

Hi @ChristianSauer ,

I see the RoslynExtensions class is now marked with an explicit comment:

Source: https://github.com/dominikjeske/Samples/blob/main/SourceGenerators/HomeCenter.SourceGenerators/Extensions/RoslynExtensions.cs

Does this mean any customisations I want to apply should be moved into a different extensions class, to keep RoslynExtensions as a clean direct copy...?

{
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.TryStartTransaction(int?, int, string, Func{int, int})" />
bool TryStartTransaction(int? param, int param2 = 0, string? data = null, Func<int, int>? func = null);
bool TryStartTransaction(int? param, int param2 = 0, string? data = null, Func<int, int> func = null);
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better null management now it's centralised - the func really wasn't nullable in the original definition.

{
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.CMethod{T, T1, T2, T3, T4, T5}(string, string)" />
string CMethod<T, T1, T2, T3, T4, T5>(string x, string y) where T : class where T1 : struct where T3 : global::AutomaticInterfaceExample.DemoClass where T4 : IDemoClass where T5 : new();
string CMethod<T, T1, T2, T3, T4, T5>(string x, string y) where T : class where T1 : struct where T3 : global::AutomaticInterfaceExample.DemoClass where T4 : global::AutomaticInterfaceExample.IDemoClass where T5 : new();
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result of the new interface name resolution - correct qualification and no ambiguity in generated interfaces that reference generated interfaces.

var sourceErrors = sourceDiagnostics
.Where(d => d.Severity == DiagnosticSeverity.Error)
.Where(x => x.Id != "CS0246") // missing references are ok
.Where(x => x.Id != "CS0246" && x.Id != "CS0234") // missing references are ok
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed now we're referencing interfaces that don't yet exist.

Comment on lines +48 to +52
// The first syntax tree is the input code, the second two are the two generated attribute classes, and the rest is the generated code.
return string.Join(
Environment.NewLine + Environment.NewLine,
outputCompilation.SyntaxTrees.Skip(3)
);
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't just export the first syntax tree, export them all. We now have tests that generate two interfaces and we need them both in the output.

Comment thread README.md
Comment on lines +192 to +194
### 6.0.2

- Added feature to allow generated interfaces to reference other generated interfaces
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly should be 7.0.0 - I'll leave the versioning to you @ChristianSauer 🙂

@simonmckenzie simonmckenzie marked this pull request as ready for review May 13, 2026 04:21
@simonmckenzie
Copy link
Copy Markdown
Author

Hi @ChristianSauer ,

I've resolved the issue around how to work with RoslynExtensions and have split out the customisations into a new class, RosylnExtensionsAutomaticInterface.

This PR is now ready for review.

I hope all is going well with you, and if there are ways I can help with this project, I'd be happy to contribute

@simonmckenzie simonmckenzie force-pushed the feature/fully-qualify-references-to-generated-interfaces branch 2 times, most recently from 6418e24 to 3248feb Compare May 13, 2026 04:26
…cements/extensions into a new project-specific class, `RoslynExtensionsAutomaticInterface`
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.

1 participant