Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cloning DocumentId results in impossibility to get the valid result of GetSourceGeneratedDocumentAsync #71581

Open
lsoft opened this issue Jan 11, 2024 · 10 comments

Comments

@lsoft
Copy link

lsoft commented Jan 11, 2024

Version Used: Latest.

Steps to Reproduce:

  1. have in hand a real DocumentId of source-generated document.
  2. make sure GetSourceGeneratedDocumentAsync returns a valid SG-document with this DocumentId.
  3. clone DocumentId via public-available constructors. (this is inevitable in some scenarios, like working with VS codelenses).
  4. make GetSourceGeneratedDocumentAsync returns null.

I suspect it is because DocumentId.IsSourceGenerated is internal, and cannot be set outside of Roslyn.

Workaround exists: (await project.GetSourceGeneratedDocumentsAsync(CancellationToken.None).First(d => d.Id.Equals(clonedDocumentId));. But it is a VERY VERY confusion behavior.

Expected Behavior:

Cloning DocumentId does not break GetSourceGeneratedDocumentAsync.

Actual Behavior:

GetSourceGeneratedDocumentAsync returns null.

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Jan 11, 2024
@sharwell
Copy link
Member

this is inevitable in some scenarios, like working with VS codelenses

Can you clarify this?

@CyrusNajmabadi
Copy link
Member

Note: the design here was intentional. No other parties can make source-generated-docs, so there should be no need to create the IDs for them. I too would need more info on why cloning is needed or is happening.

@lsoft
Copy link
Author

lsoft commented Jan 12, 2024

@sharwell

Can you clarify this?

working with codelenses inside VSIX means one need to serialize data between processes (main VS process, and some codelens process, I don't know details), so project guid and document guid from "valid" DocumentId are both serialized in order to send into codelens (or back), then new instance DocumentId is created and VSIXer can't grab source generated document with this "cloned" DocumentId as described.

There is also some theoretical (at now) scenarios where DocumentId (its project+document guids) will be saved to the disk, or some small db inside the VSIX.

do I provide enough details? if no, please, give me a hint what you want to know exactly!

@CyrusNajmabadi

could I ask for explanation for the following question: source generated documents are here for years, and they will not disappear, it is big part of Roslyn ecosystem and we all are happy to see it; so, why they are not a part of public API? why Roslyn team do not want to see it as a part of public API? we are talking about one bool inside DocumentId, which we know will not lose its value in the future.

Thanks!

@lsoft
Copy link
Author

lsoft commented Jan 12, 2024

The issue I described is a real issue for my VSIX https://github.com/lsoft/SyncToAsyncExtension.

@sharwell
Copy link
Member

sharwell commented Jan 12, 2024

@CyrusNajmabadi We need default the IsSourceGenerated to true for the existing public constructors for back compat (this will not impact the code paths that request a non-source generated document), and optionally create a new public constructor that exposes IsSourceGenerated so it can be manually set to false.

This is a 17.8 regression introduced by #69952.

@CyrusNajmabadi
Copy link
Member

so, why they are not a part of public API?

Because this may not be the representation we want in the future. Making something public means we cannot change it.

@CyrusNajmabadi
Copy link
Member

We need default the IsSourceGenerated to true for the existing public constructors

I don't think this is appropriate.

What we could do is make the internal representation tri-state. So the existing constructors would say "I don't know" (versus saying that it actually is a generated document).

@lsoft
Copy link
Author

lsoft commented Jan 12, 2024

@CyrusNajmabadi

Because this may not be the representation we want in the future. Making something public means we cannot change it.

I understand this. But please take a look at this topic from the user's persprective.

From user's perspective, user need to grab a Document having in a hand a DocumentId. Now one need to write this:

            var document = project.GetDocument(
                documentId
            );
            if(document != null)
            {
                return document;
            }

            document = (await project.GetSourceGeneratedDocumentsAsync(CancellationToken.None))
                .First(d => d.Id.Equals(documentId));
            return document;

after fixing this issue this code will be transformed into the following:

            var document = project.GetDocument(
                documentId
            );
            if(document != null)
            {
                return document;
            }

            document = (await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None);
            return document;

but having bool IsSourceGenerated will result into a more efficient code like:

       if(!documentId.IsSourceGenerated)
            return project.GetDocument(
                documentId
            );
      else
           return (await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None);

This is, probably, good.

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Jan 12, 2024

@lsoft i'm not saying i don't get the use cases. I'm saying that public APIs come with significant costs. Especially in the case where we decide later it's not the representation we want. As an internal API we can trivially change things, as a public API, it is much much harder. :)

@CyrusNajmabadi
Copy link
Member

I'm amenable to do this in two phases. Revert the regression (by making the existing constructor initialize things in a tri-state). Then in the future decide on a public, supported, representation.

@jasonmalinowski jasonmalinowski removed the untriaged Issues and PRs which have not yet been triaged by a lead label Jan 17, 2024
@arunchndr arunchndr added this to the 18.0 milestone Nov 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants