提交 7cc2855d 编写于 作者: M Manish Vasani

Respect RunAnalyzers and RunCodeAnalysisDuringLiveAnalysis project properties...

Respect RunAnalyzers and RunCodeAnalysisDuringLiveAnalysis project properties for VSIX analyzer execution

We recently added project properties for configuring analyzer execution during build and live analysis, see documentation [here](https://docs.microsoft.com/visualstudio/code-quality/disable-code-analysis). Currently, these properties are only respected for NuGet based analyzers. This PR adds support to respect these properties even for VSIX based analyzers, including the built-in IDE code style analyzers. https://github.com/dotnet/project-system/pull/5598 passes the required properties down to the Roslyn langauge service, and this PR consumes these properties and adds this support.
上级 49b6ee1a
......@@ -361,7 +361,8 @@ public ProjectInfo ToProjectInfo()
this.AnalyzerConfigDocuments.Select(d => d.ToDocumentInfo()),
this.IsSubmission,
this.HostObjectType,
hasAllInformation: true)
hasAllInformation: true,
runAnalyzers: true)
.WithDefaultNamespace(this.DefaultNamespace);
}
......
......@@ -165,6 +165,14 @@ public bool IsAnalyzerSuppressed(DiagnosticAnalyzer analyzer, Project project)
return false;
}
// If user has disabled analyzer execution for this project, we only want to execute required analyzers
// that report diagnostics with category "Compiler".
if (!project.State.RunAnalyzers &&
GetDiagnosticDescriptors(analyzer).All(d => d.Category != DiagnosticCategory.Compiler))
{
return true;
}
// don't capture project
var projectId = project.Id;
......
......@@ -23,9 +23,10 @@ internal abstract class AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer
// The NotConfigurable custom tag ensures that user can't turn this diagnostic into a warning / error via
// ruleset editor or solution explorer. Setting messageFormat to empty string ensures that we won't display
// this diagnostic in the preview pane header.
// Setting category to "Compiler" ensures we always run this analyzer even when user has turned off analyzer execution for the project.
private static readonly DiagnosticDescriptor s_fixableIdDescriptor =
new DiagnosticDescriptor(DiagnosticFixableId,
title: "", messageFormat: "", category: "",
title: "", messageFormat: "", category: DiagnosticCategory.Compiler,
defaultSeverity: DiagnosticSeverity.Hidden,
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.NotConfigurable);
......
......@@ -4,9 +4,11 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editor.UnitTests;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.LanguageServices.CSharp.Utilities;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Test.Utilities;
using Xunit;
......@@ -66,5 +68,67 @@ public void SetProperty_MaxSupportedLangVersion_CPS(LanguageVersion maxSupported
Assert.Equal(attemptedVersion <= maxSupportedLangVersion, canApply);
}
}
[WpfTheory]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
// RunAnalyzers: Not set, RunAnalyzersDuringLiveAnalysis: Not set, ExpectedRunAnalyzers = true
[InlineData("", "", true)]
// RunAnalyzers: true, RunAnalyzersDuringLiveAnalysis: Not set, ExpectedRunAnalyzers = true
[InlineData("true", "", true)]
// RunAnalyzers: false, RunAnalyzersDuringLiveAnalysis: Not set, ExpectedRunAnalyzers = false
[InlineData("false", "", false)]
// RunAnalyzers: Not set, RunAnalyzersDuringLiveAnalysis: true, ExpectedRunAnalyzers = true
[InlineData("", "true", true)]
// RunAnalyzers: Not set, RunAnalyzersDuringLiveAnalysis: false, ExpectedRunAnalyzers = false
[InlineData("", "false", false)]
// RunAnalyzers: true, RunAnalyzersDuringLiveAnalysis: true, ExpectedRunAnalyzers = true
[InlineData("true", "true", true)]
// RunAnalyzers: true, RunAnalyzersDuringLiveAnalysis: false, ExpectedRunAnalyzers = true
[InlineData("true", "false", true)]
// RunAnalyzers: false, RunAnalyzersDuringLiveAnalysis: true, ExpectedRunAnalyzers = false
[InlineData("false", "true", false)]
// RunAnalyzers: false, RunAnalyzersDuringLiveAnalysis: false, ExpectedRunAnalyzers = false
[InlineData("false", "false", false)]
// Case insensitive
[InlineData("FALSE", "", false)]
// Invalid values ignored
[InlineData("Invalid", "INVALID", true)]
public void SetProperty_RunAnalyzersAndRunAnalyzersDuringLiveAnalysis(string runAnalyzers, string runAnalyzersDuringLiveAnalysis, bool expectedRunAnalyzers)
{
TestCPSProject();
TestLegacyProject();
return;
void TestCPSProject()
{
using var environment = new TestEnvironment();
using var cpsProject = CSharpHelpers.CreateCSharpCPSProject(environment, "Test");
cpsProject.SetProperty(AdditionalPropertyNames.RunAnalyzers, runAnalyzers);
cpsProject.SetProperty(AdditionalPropertyNames.RunAnalyzersDuringLiveAnalysis, runAnalyzersDuringLiveAnalysis);
Assert.Equal(expectedRunAnalyzers, environment.Workspace.CurrentSolution.Projects.Single().State.RunAnalyzers);
}
void TestLegacyProject()
{
using var environment = new TestEnvironment();
var hierarchy = environment.CreateHierarchy("CSharpProject", "Bin", projectRefPath: null, projectCapabilities: "CSharp");
var storage = Assert.IsAssignableFrom<IVsBuildPropertyStorage>(hierarchy);
Assert.True(ErrorHandler.Succeeded(
storage.SetPropertyValue(
AdditionalPropertyNames.RunAnalyzers, null, (uint)_PersistStorageType.PST_PROJECT_FILE, runAnalyzers)));
Assert.True(ErrorHandler.Succeeded(
storage.SetPropertyValue(
AdditionalPropertyNames.RunAnalyzersDuringLiveAnalysis, null, (uint)_PersistStorageType.PST_PROJECT_FILE, runAnalyzersDuringLiveAnalysis)));
_ = CSharpHelpers.CreateCSharpProject(environment, "Test", hierarchy);
Assert.Equal(expectedRunAnalyzers, environment.Workspace.CurrentSolution.Projects.Single().State.RunAnalyzers);
}
}
}
}
......@@ -12,5 +12,7 @@ internal static class AdditionalPropertyNames
public const string RootNamespace = nameof(RootNamespace);
public const string MaxSupportedLangVersion = nameof(MaxSupportedLangVersion);
public const string RunAnalyzers = nameof(RunAnalyzers);
public const string RunAnalyzersDuringLiveAnalysis = nameof(RunAnalyzersDuringLiveAnalysis);
}
}
......@@ -12,6 +12,7 @@
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
using Microsoft.VisualStudio.LanguageServices.Implementation.EditAndContinue;
using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
......@@ -102,6 +103,16 @@ internal abstract partial class AbstractLegacyProject : ForegroundThreadAffiniti
// (e.g. through a <defaultnamespace> msbuild property)
VisualStudioProject.DefaultNamespace = GetRootNamespacePropertyValue(hierarchy);
if (TryGetBoolPropertyValue(hierarchy, AdditionalPropertyNames.RunAnalyzers, out var runAnayzers))
{
VisualStudioProject.RunAnalyzers = runAnayzers;
}
if (TryGetBoolPropertyValue(hierarchy, AdditionalPropertyNames.RunAnalyzersDuringLiveAnalysis, out var runAnayzersDuringLiveAnalysis))
{
VisualStudioProject.RunAnalyzersDuringLiveAnalysis = runAnayzersDuringLiveAnalysis;
}
Hierarchy = hierarchy;
ConnectHierarchyEvents();
RefreshBinOutputPath();
......@@ -392,5 +403,28 @@ private static string GetRootNamespacePropertyValue(IVsHierarchy hierarchy)
return null;
}
private static bool TryGetPropertyValue(IVsHierarchy hierarchy, string propertyName, out string propertyValue)
{
if (!(hierarchy is IVsBuildPropertyStorage storage))
{
propertyValue = null;
return false;
}
return ErrorHandler.Succeeded(storage.GetPropertyValue(propertyName, null, (uint)_PersistStorageType.PST_PROJECT_FILE, out propertyValue));
}
private static bool TryGetBoolPropertyValue(IVsHierarchy hierarchy, string propertyName, out bool? propertyValue)
{
if (!TryGetPropertyValue(hierarchy, propertyName, out var stringPropertyValue))
{
propertyValue = null;
return false;
}
propertyValue = bool.TryParse(stringPropertyValue, out var parsedBoolValue) ? parsedBoolValue : (bool?)null;
return true;
}
}
}
......@@ -61,6 +61,13 @@ internal sealed class VisualStudioProject
private string _outputRefFilePath;
private string _defaultNamespace;
// Actual property values for 'RunAnalyzers' and 'RunAnalyzersDuringLiveAnalysis' properties from the project file.
// Both these properties can be used to configure running analyzers, with RunAnalyzers overriding RunAnalyzersDuringLiveAnalysis.
private bool? _runAnalyzersPropertyValue, _runAnalyzersDuringLiveAnalysisPropertyValue;
// Effective boolean value to determine if analyzers should be executed based on _runAnalyzersPropertyValue and _runAnalyzersDuringLiveAnalysisPropertyValue.
private bool _runAnalyzers = true;
private readonly Dictionary<string, ImmutableArray<MetadataReferenceProperties>> _allMetadataReferences = new Dictionary<string, ImmutableArray<MetadataReferenceProperties>>();
/// <summary>
......@@ -292,6 +299,36 @@ internal bool HasAllInformation
w => w.OnHasAllInformationChanged(Id, value));
}
internal bool? RunAnalyzers
{
get => _runAnalyzersPropertyValue;
set
{
_runAnalyzersPropertyValue = value;
UpdateRunAnalyzers();
}
}
internal bool? RunAnalyzersDuringLiveAnalysis
{
get => _runAnalyzersDuringLiveAnalysisPropertyValue;
set
{
_runAnalyzersDuringLiveAnalysisPropertyValue = value;
UpdateRunAnalyzers();
}
}
void UpdateRunAnalyzers()
{
// Property RunAnalyzers overrides RunAnalyzersDuringLiveAnalysis, and default when both properties are not set is 'true'.
var runAnalyzers = _runAnalyzersPropertyValue ?? _runAnalyzersDuringLiveAnalysisPropertyValue ?? true;
ChangeProjectProperty(ref _runAnalyzers,
runAnalyzers,
s => s.WithRunAnalyzers(Id, runAnalyzers),
w => w.OnRunAnalyzersChanged(Id, runAnalyzers));
}
/// <summary>
/// The default namespace of the project.
/// </summary>
......
......@@ -167,6 +167,16 @@ public void SetProperty(string name, string? value)
{
_visualStudioProject.MaxLangVersion = value;
}
else if (name == AdditionalPropertyNames.RunAnalyzers)
{
bool? boolValue = bool.TryParse(value, out var parsedBoolValue) ? parsedBoolValue : (bool?)null;
_visualStudioProject.RunAnalyzers = boolValue;
}
else if (name == AdditionalPropertyNames.RunAnalyzersDuringLiveAnalysis)
{
bool? boolValue = bool.TryParse(value, out var parsedBoolValue) ? parsedBoolValue : (bool?)null;
_visualStudioProject.RunAnalyzersDuringLiveAnalysis = boolValue;
}
}
public void AddMetadataReference(string referencePath, MetadataReferenceProperties properties)
......
......@@ -7,6 +7,7 @@ Imports Microsoft.VisualStudio.Shell
Imports Roslyn.Utilities
Imports System.IO
Imports Moq
Imports Microsoft.VisualStudio.LanguageServices.ProjectSystem
Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework
Public NotInheritable Class MockHierarchy
......@@ -19,6 +20,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr
Private _projectName As String
Private _projectBinPath As String
Private _runAnalyzers As String
Private _runAnalyzersDuringLiveAnalysis As String
Private ReadOnly _projectRefPath As String
Private ReadOnly _projectCapabilities As String
Private ReadOnly _projectMock As Mock(Of EnvDTE.Project) = New Mock(Of EnvDTE.Project)(MockBehavior.Strict)
......@@ -330,6 +333,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr
ElseIf pszPropName = "TargetRefPath" Then
pbstrPropValue = _projectRefPath
Return VSConstants.S_OK
ElseIf pszPropName = AdditionalPropertyNames.RunAnalyzers Then
pbstrPropValue = _runAnalyzers
Return VSConstants.S_OK
ElseIf pszPropName = AdditionalPropertyNames.RunAnalyzersDuringLiveAnalysis Then
pbstrPropValue = _runAnalyzersDuringLiveAnalysis
Return VSConstants.S_OK
End If
Throw New NotSupportedException($"{NameOf(MockHierarchy)}.{NameOf(GetPropertyValue)} does not support reading {pszPropName}.")
......@@ -342,6 +351,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr
ElseIf pszPropName = "TargetFileName" Then
_projectName = PathUtilities.GetFileName(pszPropValue, includeExtension:=False)
Return VSConstants.S_OK
ElseIf pszPropName = AdditionalPropertyNames.RunAnalyzers Then
_runAnalyzers = pszPropValue
Return VSConstants.S_OK
ElseIf pszPropName = AdditionalPropertyNames.RunAnalyzersDuringLiveAnalysis Then
_runAnalyzersDuringLiveAnalysis = pszPropValue
Return VSConstants.S_OK
End If
Throw New NotImplementedException()
......
......@@ -73,6 +73,7 @@ public static async Task<ProjectInfo> CreateProjectInfoAsync(IAssetProvider asse
documentInfos, p2p, metadata, analyzers, additionalDocumentInfos, projectInfo.IsSubmission)
.WithOutputRefFilePath(projectInfo.OutputRefFilePath)
.WithHasAllInformation(projectInfo.HasAllInformation)
.WithRunAnalyzers(projectInfo.RunAnalyzers)
.WithDefaultNamespace(projectInfo.DefaultNamespace)
.WithAnalyzerConfigDocuments(analyzerConfigDocumentInfos);
}
......
......@@ -85,6 +85,11 @@ public sealed class ProjectInfo
/// </summary>
internal bool HasAllInformation => Attributes.HasAllInformation;
/// <summary>
/// True if we should run analyzers for this project.
/// </summary>
internal bool RunAnalyzers => Attributes.RunAnalyzers;
/// <summary>
/// The initial compilation options for the project, or null if the default options should be used.
/// </summary>
......@@ -177,7 +182,8 @@ public sealed class ProjectInfo
IEnumerable<DocumentInfo>? analyzerConfigDocuments,
bool isSubmission,
Type? hostObjectType,
bool hasAllInformation)
bool hasAllInformation,
bool runAnalyzers)
{
return new ProjectInfo(
new ProjectAttributes(
......@@ -191,7 +197,8 @@ public sealed class ProjectInfo
outputRefFilePath,
defaultNamespace,
isSubmission,
hasAllInformation),
hasAllInformation,
runAnalyzers),
compilationOptions,
parseOptions,
documents,
......@@ -229,7 +236,7 @@ public sealed class ProjectInfo
id, version, name, assemblyName, language,
filePath, outputFilePath, outputRefFilePath: null, defaultNamespace: null, compilationOptions, parseOptions,
documents, projectReferences, metadataReferences, analyzerReferences, additionalDocuments, analyzerConfigDocuments: null,
isSubmission, hostObjectType, hasAllInformation: true);
isSubmission, hostObjectType, hasAllInformation: true, runAnalyzers: true);
}
/// <summary>
......@@ -258,7 +265,7 @@ public sealed class ProjectInfo
id, version, name, assemblyName, language,
filePath, outputFilePath, outputRefFilePath, defaultNamespace: null, compilationOptions, parseOptions,
documents, projectReferences, metadataReferences, analyzerReferences, additionalDocuments, analyzerConfigDocuments: null,
isSubmission, hostObjectType, hasAllInformation: true);
isSubmission, hostObjectType, hasAllInformation: true, runAnalyzers: true);
}
private ProjectInfo With(
......@@ -393,6 +400,11 @@ internal ProjectInfo WithHasAllInformation(bool hasAllInformation)
return With(attributes: Attributes.With(hasAllInformation: hasAllInformation));
}
internal ProjectInfo WithRunAnalyzers(bool runAnalyzers)
{
return With(attributes: Attributes.With(runAnalyzers: runAnalyzers));
}
internal string GetDebuggerDisplay()
{
return nameof(ProjectInfo) + " " + Name + (!string.IsNullOrWhiteSpace(FilePath) ? " " + FilePath : "");
......@@ -461,6 +473,11 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
/// </summary>
public bool HasAllInformation { get; }
/// <summary>
/// True if we should run analyzers for this project.
/// </summary>
public bool RunAnalyzers { get; }
public ProjectAttributes(
ProjectId id,
VersionStamp version,
......@@ -472,7 +489,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
string? outputRefFilePath,
string? defaultNamespace,
bool isSubmission,
bool hasAllInformation)
bool hasAllInformation,
bool runAnalyzers)
{
Id = id ?? throw new ArgumentNullException(nameof(id));
Name = name ?? throw new ArgumentNullException(nameof(name));
......@@ -486,6 +504,7 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
DefaultNamespace = defaultNamespace;
IsSubmission = isSubmission;
HasAllInformation = hasAllInformation;
RunAnalyzers = runAnalyzers;
}
public ProjectAttributes With(
......@@ -498,7 +517,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
Optional<string?> outputRefPath = default,
Optional<string?> defaultNamespace = default,
Optional<bool> isSubmission = default,
Optional<bool> hasAllInformation = default)
Optional<bool> hasAllInformation = default,
Optional<bool> runAnalyzers = default)
{
var newVersion = version.HasValue ? version.Value : Version;
var newName = name ?? Name;
......@@ -510,6 +530,7 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
var newDefaultNamespace = defaultNamespace.HasValue ? defaultNamespace.Value : DefaultNamespace;
var newIsSubmission = isSubmission.HasValue ? isSubmission.Value : IsSubmission;
var newHasAllInformation = hasAllInformation.HasValue ? hasAllInformation.Value : HasAllInformation;
var newRunAnalyzers = runAnalyzers.HasValue ? runAnalyzers.Value : RunAnalyzers;
if (newVersion == Version &&
newName == Name &&
......@@ -520,7 +541,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
newOutputRefPath == OutputRefFilePath &&
newDefaultNamespace == DefaultNamespace &&
newIsSubmission == IsSubmission &&
newHasAllInformation == HasAllInformation)
newHasAllInformation == HasAllInformation &&
newRunAnalyzers == RunAnalyzers)
{
return this;
}
......@@ -536,7 +558,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
newOutputRefPath,
newDefaultNamespace,
newIsSubmission,
newHasAllInformation);
newHasAllInformation,
newRunAnalyzers);
}
bool IObjectWritable.ShouldReuseInSerialization => true;
......@@ -557,6 +580,7 @@ public void WriteTo(ObjectWriter writer)
writer.WriteString(DefaultNamespace);
writer.WriteBoolean(IsSubmission);
writer.WriteBoolean(HasAllInformation);
writer.WriteBoolean(RunAnalyzers);
// TODO: once CompilationOptions, ParseOptions, ProjectReference, MetadataReference, AnalyzerReference supports
// serialization, we should include those here as well.
......@@ -576,8 +600,9 @@ public static ProjectAttributes ReadFrom(ObjectReader reader)
var defaultNamespace = reader.ReadString();
var isSubmission = reader.ReadBoolean();
var hasAllInformation = reader.ReadBoolean();
var runAnalyzers = reader.ReadBoolean();
return new ProjectAttributes(projectId, VersionStamp.Create(), name, assemblyName, language, filePath, outputFilePath, outputRefFilePath, defaultNamespace, isSubmission, hasAllInformation);
return new ProjectAttributes(projectId, VersionStamp.Create(), name, assemblyName, language, filePath, outputFilePath, outputRefFilePath, defaultNamespace, isSubmission, hasAllInformation, runAnalyzers);
}
private Checksum? _lazyChecksum;
......
......@@ -425,6 +425,9 @@ public async Task<VersionStamp> GetSemanticVersionAsync(CancellationToken cancel
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public bool HasAllInformation => this.ProjectInfo.HasAllInformation;
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public bool RunAnalyzers => this.ProjectInfo.RunAnalyzers;
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public bool HasDocuments => _documentIds.Count > 0;
......@@ -610,6 +613,16 @@ public ProjectState UpdateHasAllInformation(bool hasAllInformation)
return this.With(projectInfo: this.ProjectInfo.WithHasAllInformation(hasAllInformation).WithVersion(this.Version.GetNewerVersion()));
}
public ProjectState UpdateRunAnalyzers(bool runAnalyzers)
{
if (runAnalyzers == this.RunAnalyzers)
{
return this;
}
return this.With(projectInfo: this.ProjectInfo.WithRunAnalyzers(runAnalyzers).WithVersion(this.Version.GetNewerVersion()));
}
public static bool IsSameLanguage(ProjectState project1, ProjectState project2)
{
return project1.LanguageServices == project2.LanguageServices;
......
......@@ -435,6 +435,21 @@ internal Solution WithHasAllInformation(ProjectId projectId, bool hasAllInformat
return new Solution(newState);
}
/// <summary>
/// Create a new solution instance with the project specified updated to have
/// the specified runAnalyzers.
/// </summary>
internal Solution WithRunAnalyzers(ProjectId projectId, bool runAnalyzers)
{
var newState = _state.WithRunAnalyzers(projectId, runAnalyzers);
if (newState == _state)
{
return this;
}
return new Solution(newState);
}
/// <summary>
/// Create a new solution instance with the project specified updated to include
/// the specified project reference.
......
......@@ -854,6 +854,31 @@ public SolutionState WithHasAllInformation(ProjectId projectId, bool hasAllInfor
return this.ForkProject(newProject);
}
/// <summary>
/// Create a new solution instance with the project specified updated to have
/// the specified runAnalyzers.
/// </summary>
public SolutionState WithRunAnalyzers(ProjectId projectId, bool runAnalyzers)
{
if (projectId == null)
{
throw new ArgumentNullException(nameof(projectId));
}
Debug.Assert(this.ContainsProject(projectId));
var oldProject = this.GetProjectState(projectId)!;
var newProject = oldProject.UpdateRunAnalyzers(runAnalyzers);
if (oldProject == newProject)
{
return this;
}
// fork without any change on compilation.
return this.ForkProject(newProject);
}
/// <summary>
/// Create a new solution instance with the project specified updated to include
/// the specified project references.
......
......@@ -607,6 +607,14 @@ internal void OnHasAllInformationChanged(ProjectId projectId, bool hasAllInforma
this.HandleProjectChange(projectId, oldSolution => oldSolution.WithHasAllInformation(projectId, hasAllInformation));
}
/// <summary>
/// Call this method when a project's RunAnalyzers property is changed in the host environment.
/// </summary>
internal void OnRunAnalyzersChanged(ProjectId projectId, bool runAnalyzers)
{
this.HandleProjectChange(projectId, oldSolution => oldSolution.WithRunAnalyzers(projectId, runAnalyzers));
}
/// <summary>
/// Call this method when a document is added to a project in the host environment.
/// </summary>
......
......@@ -281,6 +281,11 @@ private async Task<Project> UpdateProjectInfoAsync(Project project, Checksum inf
project = project.Solution.WithHasAllInformation(project.Id, newProjectInfo.HasAllInformation).GetProject(project.Id);
}
if (project.State.ProjectInfo.Attributes.RunAnalyzers != newProjectInfo.RunAnalyzers)
{
project = project.Solution.WithRunAnalyzers(project.Id, newProjectInfo.RunAnalyzers).GetProject(project.Id);
}
return project;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册