提交 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() ...@@ -361,7 +361,8 @@ public ProjectInfo ToProjectInfo()
this.AnalyzerConfigDocuments.Select(d => d.ToDocumentInfo()), this.AnalyzerConfigDocuments.Select(d => d.ToDocumentInfo()),
this.IsSubmission, this.IsSubmission,
this.HostObjectType, this.HostObjectType,
hasAllInformation: true) hasAllInformation: true,
runAnalyzers: true)
.WithDefaultNamespace(this.DefaultNamespace); .WithDefaultNamespace(this.DefaultNamespace);
} }
......
...@@ -165,6 +165,14 @@ public bool IsAnalyzerSuppressed(DiagnosticAnalyzer analyzer, Project project) ...@@ -165,6 +165,14 @@ public bool IsAnalyzerSuppressed(DiagnosticAnalyzer analyzer, Project project)
return false; 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 // don't capture project
var projectId = project.Id; var projectId = project.Id;
......
...@@ -23,9 +23,10 @@ internal abstract class AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer ...@@ -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 // 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 // ruleset editor or solution explorer. Setting messageFormat to empty string ensures that we won't display
// this diagnostic in the preview pane header. // 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 = private static readonly DiagnosticDescriptor s_fixableIdDescriptor =
new DiagnosticDescriptor(DiagnosticFixableId, new DiagnosticDescriptor(DiagnosticFixableId,
title: "", messageFormat: "", category: "", title: "", messageFormat: "", category: DiagnosticCategory.Compiler,
defaultSeverity: DiagnosticSeverity.Hidden, defaultSeverity: DiagnosticSeverity.Hidden,
isEnabledByDefault: true, isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.NotConfigurable); customTags: WellKnownDiagnosticTags.NotConfigurable);
......
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests;
using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.LanguageServices.CSharp.Utilities; using Microsoft.VisualStudio.LanguageServices.CSharp.Utilities;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework; using Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Test.Utilities; using Roslyn.Test.Utilities;
using Xunit; using Xunit;
...@@ -66,5 +68,67 @@ public void SetProperty_MaxSupportedLangVersion_CPS(LanguageVersion maxSupported ...@@ -66,5 +68,67 @@ public void SetProperty_MaxSupportedLangVersion_CPS(LanguageVersion maxSupported
Assert.Equal(attemptedVersion <= maxSupportedLangVersion, canApply); 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 ...@@ -12,5 +12,7 @@ internal static class AdditionalPropertyNames
public const string RootNamespace = nameof(RootNamespace); public const string RootNamespace = nameof(RootNamespace);
public const string MaxSupportedLangVersion = nameof(MaxSupportedLangVersion); public const string MaxSupportedLangVersion = nameof(MaxSupportedLangVersion);
public const string RunAnalyzers = nameof(RunAnalyzers);
public const string RunAnalyzersDuringLiveAnalysis = nameof(RunAnalyzersDuringLiveAnalysis);
} }
} }
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
using Microsoft.VisualStudio.LanguageServices.Implementation.EditAndContinue; using Microsoft.VisualStudio.LanguageServices.Implementation.EditAndContinue;
using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -102,6 +103,16 @@ internal abstract partial class AbstractLegacyProject : ForegroundThreadAffiniti ...@@ -102,6 +103,16 @@ internal abstract partial class AbstractLegacyProject : ForegroundThreadAffiniti
// (e.g. through a <defaultnamespace> msbuild property) // (e.g. through a <defaultnamespace> msbuild property)
VisualStudioProject.DefaultNamespace = GetRootNamespacePropertyValue(hierarchy); 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; Hierarchy = hierarchy;
ConnectHierarchyEvents(); ConnectHierarchyEvents();
RefreshBinOutputPath(); RefreshBinOutputPath();
...@@ -392,5 +403,28 @@ private static string GetRootNamespacePropertyValue(IVsHierarchy hierarchy) ...@@ -392,5 +403,28 @@ private static string GetRootNamespacePropertyValue(IVsHierarchy hierarchy)
return null; 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 ...@@ -61,6 +61,13 @@ internal sealed class VisualStudioProject
private string _outputRefFilePath; private string _outputRefFilePath;
private string _defaultNamespace; 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>>(); private readonly Dictionary<string, ImmutableArray<MetadataReferenceProperties>> _allMetadataReferences = new Dictionary<string, ImmutableArray<MetadataReferenceProperties>>();
/// <summary> /// <summary>
...@@ -292,6 +299,36 @@ internal bool HasAllInformation ...@@ -292,6 +299,36 @@ internal bool HasAllInformation
w => w.OnHasAllInformationChanged(Id, value)); 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> /// <summary>
/// The default namespace of the project. /// The default namespace of the project.
/// </summary> /// </summary>
......
...@@ -167,6 +167,16 @@ public void SetProperty(string name, string? value) ...@@ -167,6 +167,16 @@ public void SetProperty(string name, string? value)
{ {
_visualStudioProject.MaxLangVersion = 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) public void AddMetadataReference(string referencePath, MetadataReferenceProperties properties)
......
...@@ -7,6 +7,7 @@ Imports Microsoft.VisualStudio.Shell ...@@ -7,6 +7,7 @@ Imports Microsoft.VisualStudio.Shell
Imports Roslyn.Utilities Imports Roslyn.Utilities
Imports System.IO Imports System.IO
Imports Moq Imports Moq
Imports Microsoft.VisualStudio.LanguageServices.ProjectSystem
Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework
Public NotInheritable Class MockHierarchy Public NotInheritable Class MockHierarchy
...@@ -19,6 +20,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr ...@@ -19,6 +20,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr
Private _projectName As String Private _projectName As String
Private _projectBinPath As String Private _projectBinPath As String
Private _runAnalyzers As String
Private _runAnalyzersDuringLiveAnalysis As String
Private ReadOnly _projectRefPath As String Private ReadOnly _projectRefPath As String
Private ReadOnly _projectCapabilities As String Private ReadOnly _projectCapabilities As String
Private ReadOnly _projectMock As Mock(Of EnvDTE.Project) = New Mock(Of EnvDTE.Project)(MockBehavior.Strict) 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 ...@@ -330,6 +333,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr
ElseIf pszPropName = "TargetRefPath" Then ElseIf pszPropName = "TargetRefPath" Then
pbstrPropValue = _projectRefPath pbstrPropValue = _projectRefPath
Return VSConstants.S_OK 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 End If
Throw New NotSupportedException($"{NameOf(MockHierarchy)}.{NameOf(GetPropertyValue)} does not support reading {pszPropName}.") Throw New NotSupportedException($"{NameOf(MockHierarchy)}.{NameOf(GetPropertyValue)} does not support reading {pszPropName}.")
...@@ -342,6 +351,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr ...@@ -342,6 +351,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr
ElseIf pszPropName = "TargetFileName" Then ElseIf pszPropName = "TargetFileName" Then
_projectName = PathUtilities.GetFileName(pszPropValue, includeExtension:=False) _projectName = PathUtilities.GetFileName(pszPropValue, includeExtension:=False)
Return VSConstants.S_OK 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 End If
Throw New NotImplementedException() Throw New NotImplementedException()
......
...@@ -73,6 +73,7 @@ public static async Task<ProjectInfo> CreateProjectInfoAsync(IAssetProvider asse ...@@ -73,6 +73,7 @@ public static async Task<ProjectInfo> CreateProjectInfoAsync(IAssetProvider asse
documentInfos, p2p, metadata, analyzers, additionalDocumentInfos, projectInfo.IsSubmission) documentInfos, p2p, metadata, analyzers, additionalDocumentInfos, projectInfo.IsSubmission)
.WithOutputRefFilePath(projectInfo.OutputRefFilePath) .WithOutputRefFilePath(projectInfo.OutputRefFilePath)
.WithHasAllInformation(projectInfo.HasAllInformation) .WithHasAllInformation(projectInfo.HasAllInformation)
.WithRunAnalyzers(projectInfo.RunAnalyzers)
.WithDefaultNamespace(projectInfo.DefaultNamespace) .WithDefaultNamespace(projectInfo.DefaultNamespace)
.WithAnalyzerConfigDocuments(analyzerConfigDocumentInfos); .WithAnalyzerConfigDocuments(analyzerConfigDocumentInfos);
} }
......
...@@ -85,6 +85,11 @@ public sealed class ProjectInfo ...@@ -85,6 +85,11 @@ public sealed class ProjectInfo
/// </summary> /// </summary>
internal bool HasAllInformation => Attributes.HasAllInformation; internal bool HasAllInformation => Attributes.HasAllInformation;
/// <summary>
/// True if we should run analyzers for this project.
/// </summary>
internal bool RunAnalyzers => Attributes.RunAnalyzers;
/// <summary> /// <summary>
/// The initial compilation options for the project, or null if the default options should be used. /// The initial compilation options for the project, or null if the default options should be used.
/// </summary> /// </summary>
...@@ -177,7 +182,8 @@ public sealed class ProjectInfo ...@@ -177,7 +182,8 @@ public sealed class ProjectInfo
IEnumerable<DocumentInfo>? analyzerConfigDocuments, IEnumerable<DocumentInfo>? analyzerConfigDocuments,
bool isSubmission, bool isSubmission,
Type? hostObjectType, Type? hostObjectType,
bool hasAllInformation) bool hasAllInformation,
bool runAnalyzers)
{ {
return new ProjectInfo( return new ProjectInfo(
new ProjectAttributes( new ProjectAttributes(
...@@ -191,7 +197,8 @@ public sealed class ProjectInfo ...@@ -191,7 +197,8 @@ public sealed class ProjectInfo
outputRefFilePath, outputRefFilePath,
defaultNamespace, defaultNamespace,
isSubmission, isSubmission,
hasAllInformation), hasAllInformation,
runAnalyzers),
compilationOptions, compilationOptions,
parseOptions, parseOptions,
documents, documents,
...@@ -229,7 +236,7 @@ public sealed class ProjectInfo ...@@ -229,7 +236,7 @@ public sealed class ProjectInfo
id, version, name, assemblyName, language, id, version, name, assemblyName, language,
filePath, outputFilePath, outputRefFilePath: null, defaultNamespace: null, compilationOptions, parseOptions, filePath, outputFilePath, outputRefFilePath: null, defaultNamespace: null, compilationOptions, parseOptions,
documents, projectReferences, metadataReferences, analyzerReferences, additionalDocuments, analyzerConfigDocuments: null, documents, projectReferences, metadataReferences, analyzerReferences, additionalDocuments, analyzerConfigDocuments: null,
isSubmission, hostObjectType, hasAllInformation: true); isSubmission, hostObjectType, hasAllInformation: true, runAnalyzers: true);
} }
/// <summary> /// <summary>
...@@ -258,7 +265,7 @@ public sealed class ProjectInfo ...@@ -258,7 +265,7 @@ public sealed class ProjectInfo
id, version, name, assemblyName, language, id, version, name, assemblyName, language,
filePath, outputFilePath, outputRefFilePath, defaultNamespace: null, compilationOptions, parseOptions, filePath, outputFilePath, outputRefFilePath, defaultNamespace: null, compilationOptions, parseOptions,
documents, projectReferences, metadataReferences, analyzerReferences, additionalDocuments, analyzerConfigDocuments: null, documents, projectReferences, metadataReferences, analyzerReferences, additionalDocuments, analyzerConfigDocuments: null,
isSubmission, hostObjectType, hasAllInformation: true); isSubmission, hostObjectType, hasAllInformation: true, runAnalyzers: true);
} }
private ProjectInfo With( private ProjectInfo With(
...@@ -393,6 +400,11 @@ internal ProjectInfo WithHasAllInformation(bool hasAllInformation) ...@@ -393,6 +400,11 @@ internal ProjectInfo WithHasAllInformation(bool hasAllInformation)
return With(attributes: Attributes.With(hasAllInformation: hasAllInformation)); return With(attributes: Attributes.With(hasAllInformation: hasAllInformation));
} }
internal ProjectInfo WithRunAnalyzers(bool runAnalyzers)
{
return With(attributes: Attributes.With(runAnalyzers: runAnalyzers));
}
internal string GetDebuggerDisplay() internal string GetDebuggerDisplay()
{ {
return nameof(ProjectInfo) + " " + Name + (!string.IsNullOrWhiteSpace(FilePath) ? " " + FilePath : ""); return nameof(ProjectInfo) + " " + Name + (!string.IsNullOrWhiteSpace(FilePath) ? " " + FilePath : "");
...@@ -461,6 +473,11 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable ...@@ -461,6 +473,11 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
/// </summary> /// </summary>
public bool HasAllInformation { get; } public bool HasAllInformation { get; }
/// <summary>
/// True if we should run analyzers for this project.
/// </summary>
public bool RunAnalyzers { get; }
public ProjectAttributes( public ProjectAttributes(
ProjectId id, ProjectId id,
VersionStamp version, VersionStamp version,
...@@ -472,7 +489,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable ...@@ -472,7 +489,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
string? outputRefFilePath, string? outputRefFilePath,
string? defaultNamespace, string? defaultNamespace,
bool isSubmission, bool isSubmission,
bool hasAllInformation) bool hasAllInformation,
bool runAnalyzers)
{ {
Id = id ?? throw new ArgumentNullException(nameof(id)); Id = id ?? throw new ArgumentNullException(nameof(id));
Name = name ?? throw new ArgumentNullException(nameof(name)); Name = name ?? throw new ArgumentNullException(nameof(name));
...@@ -486,6 +504,7 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable ...@@ -486,6 +504,7 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
DefaultNamespace = defaultNamespace; DefaultNamespace = defaultNamespace;
IsSubmission = isSubmission; IsSubmission = isSubmission;
HasAllInformation = hasAllInformation; HasAllInformation = hasAllInformation;
RunAnalyzers = runAnalyzers;
} }
public ProjectAttributes With( public ProjectAttributes With(
...@@ -498,7 +517,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable ...@@ -498,7 +517,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
Optional<string?> outputRefPath = default, Optional<string?> outputRefPath = default,
Optional<string?> defaultNamespace = default, Optional<string?> defaultNamespace = default,
Optional<bool> isSubmission = 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 newVersion = version.HasValue ? version.Value : Version;
var newName = name ?? Name; var newName = name ?? Name;
...@@ -510,6 +530,7 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable ...@@ -510,6 +530,7 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
var newDefaultNamespace = defaultNamespace.HasValue ? defaultNamespace.Value : DefaultNamespace; var newDefaultNamespace = defaultNamespace.HasValue ? defaultNamespace.Value : DefaultNamespace;
var newIsSubmission = isSubmission.HasValue ? isSubmission.Value : IsSubmission; var newIsSubmission = isSubmission.HasValue ? isSubmission.Value : IsSubmission;
var newHasAllInformation = hasAllInformation.HasValue ? hasAllInformation.Value : HasAllInformation; var newHasAllInformation = hasAllInformation.HasValue ? hasAllInformation.Value : HasAllInformation;
var newRunAnalyzers = runAnalyzers.HasValue ? runAnalyzers.Value : RunAnalyzers;
if (newVersion == Version && if (newVersion == Version &&
newName == Name && newName == Name &&
...@@ -520,7 +541,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable ...@@ -520,7 +541,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
newOutputRefPath == OutputRefFilePath && newOutputRefPath == OutputRefFilePath &&
newDefaultNamespace == DefaultNamespace && newDefaultNamespace == DefaultNamespace &&
newIsSubmission == IsSubmission && newIsSubmission == IsSubmission &&
newHasAllInformation == HasAllInformation) newHasAllInformation == HasAllInformation &&
newRunAnalyzers == RunAnalyzers)
{ {
return this; return this;
} }
...@@ -536,7 +558,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable ...@@ -536,7 +558,8 @@ internal class ProjectAttributes : IChecksummedObject, IObjectWritable
newOutputRefPath, newOutputRefPath,
newDefaultNamespace, newDefaultNamespace,
newIsSubmission, newIsSubmission,
newHasAllInformation); newHasAllInformation,
newRunAnalyzers);
} }
bool IObjectWritable.ShouldReuseInSerialization => true; bool IObjectWritable.ShouldReuseInSerialization => true;
...@@ -557,6 +580,7 @@ public void WriteTo(ObjectWriter writer) ...@@ -557,6 +580,7 @@ public void WriteTo(ObjectWriter writer)
writer.WriteString(DefaultNamespace); writer.WriteString(DefaultNamespace);
writer.WriteBoolean(IsSubmission); writer.WriteBoolean(IsSubmission);
writer.WriteBoolean(HasAllInformation); writer.WriteBoolean(HasAllInformation);
writer.WriteBoolean(RunAnalyzers);
// TODO: once CompilationOptions, ParseOptions, ProjectReference, MetadataReference, AnalyzerReference supports // TODO: once CompilationOptions, ParseOptions, ProjectReference, MetadataReference, AnalyzerReference supports
// serialization, we should include those here as well. // serialization, we should include those here as well.
...@@ -576,8 +600,9 @@ public static ProjectAttributes ReadFrom(ObjectReader reader) ...@@ -576,8 +600,9 @@ public static ProjectAttributes ReadFrom(ObjectReader reader)
var defaultNamespace = reader.ReadString(); var defaultNamespace = reader.ReadString();
var isSubmission = reader.ReadBoolean(); var isSubmission = reader.ReadBoolean();
var hasAllInformation = 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; private Checksum? _lazyChecksum;
......
...@@ -425,6 +425,9 @@ public async Task<VersionStamp> GetSemanticVersionAsync(CancellationToken cancel ...@@ -425,6 +425,9 @@ public async Task<VersionStamp> GetSemanticVersionAsync(CancellationToken cancel
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public bool HasAllInformation => this.ProjectInfo.HasAllInformation; public bool HasAllInformation => this.ProjectInfo.HasAllInformation;
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public bool RunAnalyzers => this.ProjectInfo.RunAnalyzers;
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public bool HasDocuments => _documentIds.Count > 0; public bool HasDocuments => _documentIds.Count > 0;
...@@ -610,6 +613,16 @@ public ProjectState UpdateHasAllInformation(bool hasAllInformation) ...@@ -610,6 +613,16 @@ public ProjectState UpdateHasAllInformation(bool hasAllInformation)
return this.With(projectInfo: this.ProjectInfo.WithHasAllInformation(hasAllInformation).WithVersion(this.Version.GetNewerVersion())); 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) public static bool IsSameLanguage(ProjectState project1, ProjectState project2)
{ {
return project1.LanguageServices == project2.LanguageServices; return project1.LanguageServices == project2.LanguageServices;
......
...@@ -435,6 +435,21 @@ internal Solution WithHasAllInformation(ProjectId projectId, bool hasAllInformat ...@@ -435,6 +435,21 @@ internal Solution WithHasAllInformation(ProjectId projectId, bool hasAllInformat
return new Solution(newState); 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> /// <summary>
/// Create a new solution instance with the project specified updated to include /// Create a new solution instance with the project specified updated to include
/// the specified project reference. /// the specified project reference.
......
...@@ -854,6 +854,31 @@ public SolutionState WithHasAllInformation(ProjectId projectId, bool hasAllInfor ...@@ -854,6 +854,31 @@ public SolutionState WithHasAllInformation(ProjectId projectId, bool hasAllInfor
return this.ForkProject(newProject); 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> /// <summary>
/// Create a new solution instance with the project specified updated to include /// Create a new solution instance with the project specified updated to include
/// the specified project references. /// the specified project references.
......
...@@ -607,6 +607,14 @@ internal void OnHasAllInformationChanged(ProjectId projectId, bool hasAllInforma ...@@ -607,6 +607,14 @@ internal void OnHasAllInformationChanged(ProjectId projectId, bool hasAllInforma
this.HandleProjectChange(projectId, oldSolution => oldSolution.WithHasAllInformation(projectId, hasAllInformation)); 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> /// <summary>
/// Call this method when a document is added to a project in the host environment. /// Call this method when a document is added to a project in the host environment.
/// </summary> /// </summary>
......
...@@ -281,6 +281,11 @@ private async Task<Project> UpdateProjectInfoAsync(Project project, Checksum inf ...@@ -281,6 +281,11 @@ private async Task<Project> UpdateProjectInfoAsync(Project project, Checksum inf
project = project.Solution.WithHasAllInformation(project.Id, newProjectInfo.HasAllInformation).GetProject(project.Id); 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; return project;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册