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

Do not go back to Workspace in SyntaxEditor #62910

Merged

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Jul 25, 2022

We intend to add solutionInstance.Workspace to the banned api list for roslyn itself. It's highly problematic for OOP, as we have to try to replicate that concept, which isn't something sensical in that world. First, OOP cannot mutation things, so exposing the mutable workspace doesn't make much sense. Second, in VS you can have many workspaces, but in OOP we map that all back to one, which means we don't have a proper representation of VS state (for example, where you might have the VSWorkspace and the Metadata-as-source workspace). This means those features either cannot use OOP, or they try to use OOP and can end up with non-sensical results.

This is a series of PRs to get us off of that API so we can move to a much cleaner and simpler OOP model for the solution and services.

@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner July 25, 2022 18:03
{
}

internal SolutionState State => _state;

internal int WorkspaceVersion => _state.WorkspaceVersion;

internal SolutionServices Services => _state.Services;
public HostWorkspaceServices Services => _state.Services;
Copy link
Member

Choose a reason for hiding this comment

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

Is this intended to be public? Need API review approval if so.

Copy link
Member Author

Choose a reason for hiding this comment

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

yes. this will go through API review.

Copy link
Member Author

Choose a reason for hiding this comment

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

API review tracked through #62914

Copy link
Member

@genlu genlu left a comment

Choose a reason for hiding this comment

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

Approved pending API review approval for the public API. Or you could change the accessibility back to internal for now and merge this before API review.

@CyrusNajmabadi
Copy link
Member Author

Approved pending API review approval for the public API. Or you could change the accessibility back to internal for now and merge this before API review.

so it can't currently be internal, because it's accessed in CodeStyle layer. I think we'll run this like several high confidence APIs. Merge in, and get approval on thursday. If anything needs to change, we can do so then. Are you ok with that @tmat ?

@@ -176,7 +172,7 @@ public SolutionState WithNewWorkspace(Workspace workspace, int workspaceVersion)
/// <summary>
/// The Workspace this solution is associated with.
/// </summary>
public Workspace Workspace => _solutionServices.Workspace;
public Workspace Workspace => Services.Workspace;
Copy link
Member

Choose a reason for hiding this comment

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

public Workspace Workspace => Services.Workspace;

For follow-up: Remove to avoid leaking Workspace here.

Copy link
Member Author

Choose a reason for hiding this comment

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

yup.

Copy link
Member

@tmat tmat left a comment

Choose a reason for hiding this comment

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

:shipit:

@CyrusNajmabadi
Copy link
Member Author

Not merging this. Feedback from @jasonmalinowski is making us reconsider this.

@CyrusNajmabadi
Copy link
Member Author

Ok, i changed the approach we're taking here to use a new immutable type that doesn't expose the workspace. Please take a look
@genlu @jasonmalinowski @tmat @dibarbet . I will also update the API propposal to reflect this new form.

@@ -48,7 +48,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
public static async Task<Document> FixAllAsync(Document document, ImmutableArray<Diagnostic> diagnostics, CodeActionOptionsProvider codeActionOptionsProvider, CancellationToken cancellationToken)
{
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(root, document.Project.Solution.Workspace.Services);
var editor = document.GetSyntaxEditor(root);
Copy link
Member Author

Choose a reason for hiding this comment

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

these are at a layer (code_style) that cannot access internal apis. So while the API is in flux and hasn't been fully signed off on, this code has to go through an indirection (an extension method) so it can do the right thing in Roslyn vs CodeStyle.

@@ -115,7 +115,7 @@ private async Task<Document> ApplyAsync(Document document, TTypeDeclarationSynta
var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

var editor = new SyntaxEditor(syntaxRoot, document.Project.Solution.Workspace.Services);
var editor = new SyntaxEditor(syntaxRoot, document.Project.Solution.Services);
Copy link
Member

Choose a reason for hiding this comment

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

SyntaxEditor

Why not use document.GetSyntaxEditor here too?

Copy link
Member Author

Choose a reason for hiding this comment

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

this code isn't shared in the CODE_STYLE layer. so it wasn't necessary. a lot of the changes were done mechanically.

@@ -34,6 +34,14 @@ public SyntaxEditor(SyntaxNode root, Workspace workspace)
/// Creates a new <see cref="SyntaxEditor"/> instance.
/// </summary>
public SyntaxEditor(SyntaxNode root, HostWorkspaceServices services)
Copy link
Member

Choose a reason for hiding this comment

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

SyntaxEditor(SyntaxNode root, HostWorkspaceServices services)

Add this to BannedSymbols?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes. will do.

namespace Microsoft.CodeAnalysis.Host
{
// TODO(cyrusn): Make public. Tracked through https://github.com/dotnet/roslyn/issues/62914
internal sealed class HostProjectServices
Copy link
Member

Choose a reason for hiding this comment

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

class

Can/should this be a struct?

Copy link
Member Author

Choose a reason for hiding this comment

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

i'm very very hesitant on structs as it is a breaking change to make them classes and it's hard to tell up front if being a struct will be an issue for us (for example, having to deal with potentially default values).

Copy link
Member

Choose a reason for hiding this comment

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

default value is the same as null in this case, in public APIs we would need to check either way


// This ensures a single instance of this type associated with each HostLanguageServices.
[Obsolete("Do not call directly. Use HostLanguageServices.ProjectServices to acquire an instance")]
internal HostProjectServices(HostLanguageServices services)
Copy link
Member

@tmat tmat Jul 26, 2022

Choose a reason for hiding this comment

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

Obsolete wouldn't be needed if this was a struct.

Copy link
Member Author

Choose a reason for hiding this comment

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

yes. but then i have to override == and validate all cases whre code checks identity are correct.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think you need to override ==. Struct does not have to have value equality.

Copy link
Member Author

Choose a reason for hiding this comment

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

currently we have code that checks if the Services instances are the same instance. I wanted to avoid having to go find and audit all of those (esp. as we have no facilty for searching for use of reference equality == on these types. :)

namespace Microsoft.CodeAnalysis.Host
{
// TODO(cyrusn): Make public. Tracked through https://github.com/dotnet/roslyn/issues/62914
internal sealed class HostSolutionServices
Copy link
Member

Choose a reason for hiding this comment

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

sealed

Struct?

@CyrusNajmabadi CyrusNajmabadi merged commit 7b33e50 into dotnet:main Jul 26, 2022
@tmat
Copy link
Member

tmat commented Jul 26, 2022

Seems like in most (all?) cases the caller of SyntaxEditor ctor has a document on hand. Would it make sense to use the extension method in all these call sites?

@ghost ghost added this to the Next milestone Jul 26, 2022
@CyrusNajmabadi CyrusNajmabadi deleted the syntaxEditorWorkspaceService branch July 26, 2022 17:51
@CyrusNajmabadi
Copy link
Member Author

Seems like in most (all?) cases the caller of SyntaxEditor ctor has a document on hand. Would it make sense to use the extension method in all these call sites?

I don't have a problem with that, but it's currently not necessary. I'm trying to make minimal changes to get us off of Solution.Workspace (and hte other associated banned pathways). Extraneous work/cleanup just adds time to that :)

@tmat
Copy link
Member

tmat commented Jul 26, 2022

Yeah, but this leaves us in rather inconsistent state where we have multiple ways of using some APIs. I'd prefer not to increase the amount of inconsistency.

@CyrusNajmabadi
Copy link
Member Author

Yeah, but this leaves us in rather inconsistent state where we have multiple ways of using some APIs. I'd prefer not to increase the amount of inconsistency.

The intent is to remove that extension once hte API review happens as it won't be needed anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants