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

Add file watchers for analyzer references #74745

Merged
merged 39 commits into from
Aug 19, 2024

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Aug 13, 2024

We want to know when analyzers change on disk so that we can reload and rerun them. The way this will happen is as follows:

  1. Analyzer changes on disk.
  2. File watchers (added in this pr) will notice and mutate the host workspace to have new AnalyzerFileReference instances for these analyzers.
  3. The next checksum computation will notice these have changed (presuming that the AnalyzerFileReference MVID changed). That was added in Make analyzer mvid part of our checksum #74721.
  4. This will cause the change to be sent to the OOP server, which will make new AnalyzerFileReference instances there and will update the remote solution.
  5. OOP will then attempt to get analyzers from this new AnalyzerFileReference.

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Aug 13, 2024
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.ProjectSystem;

internal sealed class FileWatchedPortableExecutableReferenceFactory
internal sealed class FileWatchedReferenceFactory<TReference>
where TReference : class
Copy link
Member Author

Choose a reason for hiding this comment

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

made generic, so we can share the majority of logic across PortableExecutableReference and AnalyzerReference

referenceDirectories.Add(Path.Combine(dotnetRoot, "packs"));
}

if (PlatformInformation.IsWindows)
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 moved to logic only run for the PortableExecutionReference case, not the AnalyzerFileReference case. I'm not 100% sure if that's what we want, or if we should always be watching tehse special folders for both cases. @jasonmalinowski @dibarbet ?

Copy link
Member

Choose a reason for hiding this comment

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

where does an AnalyzerFileReference come from? (e.g. what is the user scenario)?

Copy link
Member

Choose a reason for hiding this comment

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

I assume we shouldn't be creating multiple file watchers for the same directories and file kinds though (I don't think we should have multiple file watchers and then file change events for the same directory and types)?

Copy link
Member Author

Choose a reason for hiding this comment

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

it generally comes from a user having an <AnalyzerReference> in their project file. :)

Copy link
Member

Choose a reason for hiding this comment

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

hm - I would think then this probably only applies to PortableExecutableReference and probably isn't needed for AnalyzerFileReference. Not 100% sure on that though

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah, i'll talk to @jasonmalinowski to make sure.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks!

Copy link
Member

Choose a reason for hiding this comment

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

I would expect we do have this same logic for both, although it may have less of a benefit in this case. The reason for these general directory watches is we expect a lot of references to come from them, and since they don't really change often it saves us a lot of overhead watching each file separately. If the SDK ends up with a dozen or so analyzers (if it doesn't already?) multiply that across a bunch of projects and you have a nice win.

Hopefully it's easy enough to refactor?


if (PlatformInformation.IsWindows)
{
referenceDirectories.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Reference Assemblies", "Microsoft", "Framework"));
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 moved into code specific to watching for PEReferences. Not sure if we need the same code for AnalyzerFileReferences. @jasonmalinowski i need your thoughts here :)

Func<ProjectSystemProjectFactory, TReference, TReference> createNewReference,
Func<ProjectUpdateState, TReference, TReference, ProjectUpdateState> updateState,
Func<Solution, ProjectId, TReference, TReference, Solution> updateSolution)
where TReference : class
Copy link
Member Author

Choose a reason for hiding this comment

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

extracted a common generic helper so we can have the same logic for PERefs and AnalyzerRefs above.

static project => project.MetadataReferences.OfType<PortableExecutableReference>(),
static reference => reference.FilePath!,
static (@this, reference) => CreateMetadataReference_NoLock(reference.FilePath!, reference.Properties, @this.SolutionServices),
static (projectUpdateState, oldReference, newReference) => projectUpdateState
Copy link
Member

Choose a reason for hiding this comment

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

nit: I see all these lambdas together and wonder if named arguments would improve the readability.

Copy link
Member Author

Choose a reason for hiding this comment

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

@JoeRobich sure. I can add that :)

@arunchndr
Copy link
Member

Not a blocker - would you add the user scenario/feedback that prompted this change/design reevaluation?

@CyrusNajmabadi
Copy link
Member Author

Not a blocker - would you add the user scenario/feedback that prompted this change/design reevaluation?

@jaredpar do you have a good link you'd like to use as the primary here? Basically, this is about the feedback we keep getting from people developing generators, and how they hate having to close/open VS after making every change.

@DoctorKrolic
Copy link
Contributor

would you add the user scenario/feedback that prompted this change/design reevaluation?

@CyrusNajmabadi You can just leave a link to roslyn chat in С# discord 😅

@CyrusNajmabadi
Copy link
Member Author

@jasonmalinowski ptal.


static ImmutableArray<WatchedDirectory> GetAdditionalWatchedDirectories()
{

Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change

@jaredpar
Copy link
Member

@jaredpar do you have a good link you'd like to use as the primary here? Basically, this is about the feedback we keep getting from people developing generators, and how they hate having to close/open VS after making every change.

This is the primary issue that gets feedback. Can see the discussion there as well as the linked issues to this one.

#48083

@CyrusNajmabadi
Copy link
Member Author

@jasonmalinowski ptal

@CyrusNajmabadi
Copy link
Member Author

@jasonmalinowski ptal.

});

static ImmutableArray<WatchedDirectory> GetAdditionalWatchedDirectories()
{
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: trailing newline

FileWatchedReferenceFactory.StartWatchingReference(reference, reference.FilePath!);
FileWatchedPortableExecutableReferenceFactory.StartWatchingReference(reference, reference.FilePath!);

// Remove file watchers for any references we're no longer watching.
Copy link
Contributor

Choose a reason for hiding this comment

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

references

nit: consider "metadata references" and "analyzer references" in the comments to distinguish

Copy link
Member Author

Choose a reason for hiding this comment

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

can add in followup.

Copy link
Contributor

@ToddGrun ToddGrun left a comment

Choose a reason for hiding this comment

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

:shipit:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead VSCode
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants