[http-client-csharp] Orphan namespace left out in generated model factory
In our generator, we have a post processor to trim unused models. When it does this, it will also remove the corresponding entry in the model factory.
For instance, if we have a model:
namespace Something;
public partial class Foo {}
it will have an entry in model factory:
using Something;
public static partial class ModelFactory
{
public static Foo Foo() {}
}
When the processor removes the entry, it does not clean up its using statements. If the removed model is the only model in that namespace, we get a compilation error about - the namespace does not exist.
using Something; // this will have compilation errors because the only model exists in this namespace has been deleted therefore in the assembly we do not really have this namespace.
public static partial class ModelFactory
{
// entries for other existing models
}
This issue actually exists before we have the "namespace is namespace" feature, but since everyone is in the same namespace, when we remove one model, that namespace still exists. When we have the "namespace is namespace" feature, we have more possibilities when "this namespace only has one model". Especially for those cases that the namespace comes from a template defined in a place like "Typespec.Http`
post processor actually cannot figure who introduced the using statement.
I wonder if we could have a "trim using statements" after we have done the removal part in post processor.
Or we could come up with a reference map builder using our output types, so that we do not need the post processor in roslyn, we do not write those entries that would be removed later, and we will never have a case that we introduce the using by a method, but that method gets removed later.
@ArcturusZhang, can you please include repro instructions?
Reproed the issue using the Todo App here.
So this is what I am thinking:
If we would like to build the system that could walk the reference tree from the root nodes (for instance, clients), we will have to know which TypeProvider a CSharpType is representing. Because the signatures, such as method signature, property, are using CSharpType to represent the type instead of TypeProviders.
But we no longer have that ability - autorest.csharp has that which might be problematic (The CSharpType.Implemenation property).
Now the output library is iterating everything from the code model, constructing them one by one - we could try to change this process. Let us ignore the unreferenced-type-handling flag for simplicity first.
We first make the OutputLibrary to be stateful with a list of things, and trigger the ignition by creating clients (the roots documents).
Because everything is lazily loaded, the methods, properties, and fields will not be constructed until we call them.
In the meantime we could have a writer system which has a blocking queue - once the output library built something, it could send them to write - once the clients are written, the method signature would be created and the model referenced are created and added to the queue.
Once the clients are written, the queue is still not empty, then it would write the models in queue, where their properties are called and more models are created. And this process goes on and on, during the writing file process, we build the reference map.
Now when the queue finally gets empty, we have written everything that is directly or indirectly referenced by the root nodes (clients), those not referenced are not even created. In this way, the process has changed from
- we create everything no matter they are used or not
to
- we only create things that are referenced
And the issue here should be solved. What do you think about this?
This is an issue in the Azure Spector scenarios as well.
And the issue here should be solved. What do you think about this?
I think this could work. How would the different options for UnreferencedTypeHandling work with this new system?
@ArcturusZhang can you assign this to the sprint in which you will be working on this?