-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev' of https://github.com/nsubstitute/NSubstitute.Anal…
- Loading branch information
Showing
86 changed files
with
14,270 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
src/NSubstitute.Analyzers.CSharp.Vsix/NSubstitute.Analyzers.CSharp.Vsix.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project> | ||
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" /> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net461</TargetFramework> | ||
<RootNamespace>NSubstitute.Analyzers.CSharp</RootNamespace> | ||
<AssemblyName>NSubstitute.Analyzers.CSharp.Vsix</AssemblyName> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup> | ||
<GeneratePkgDefFile>false</GeneratePkgDefFile> | ||
<IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> | ||
<IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> | ||
<IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> | ||
<CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> | ||
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> | ||
<VSSDKTargetPlatformRegRootSuffix>Roslyn</VSSDKTargetPlatformRegRootSuffix> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(BuildingInsideVisualStudio)' != 'true'"> | ||
<!-- This property disables extension deployment for command line builds; required for AppVeyor and the build script --> | ||
<DeployExtension>False</DeployExtension> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<DotNetCliToolReference Include="Microsoft.VSSDK.BuildTools" Version="15.1.192" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\NSubstitute.Analyzers.CSharp\NSubstitute.Analyzers.CSharp.csproj" /> | ||
<ProjectReference Include="..\NSubstitute.Analyzers.Shared\NSubstitute.Analyzers.Shared.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Include="source.extension.vsixmanifest" /> | ||
</ItemGroup> | ||
|
||
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" /> | ||
|
||
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="Exists('$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets')" /> | ||
|
||
<ItemGroup> | ||
<!-- https://github.com/dotnet/sdk/issues/433 --> | ||
<ProjectReference Update="@(ProjectReference)" AdditionalProperties="TargetFramework=netstandard1.1" /> | ||
|
||
<!-- https://github.com/Microsoft/extendvs/issues/57 --> | ||
<ProjectReference Update="@(ProjectReference)" Name="%(Filename)" /> | ||
</ItemGroup> | ||
|
||
</Project> |
8 changes: 8 additions & 0 deletions
8
src/NSubstitute.Analyzers.CSharp.Vsix/Properties/launchSettings.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"profiles": { | ||
"Visual Studio Extension": { | ||
"executablePath": "$(DevEnvDir)devenv.exe", | ||
"commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log" | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/NSubstitute.Analyzers.CSharp.Vsix/source.extension.vsixmanifest
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011"> | ||
<Metadata> | ||
<Identity Id="NSubstitute.Analyzers.CSharp.d429c94d-b39b-4294-99cd-205607d7d485" Version="1.0" Language="en-US" Publisher="Tomek"/> | ||
<DisplayName>NSubstitute.Analyzers.CSharp</DisplayName> | ||
<Description xml:space="preserve">This is a sample diagnostic extension for the .NET Compiler Platform ("Roslyn").</Description> | ||
</Metadata> | ||
<Installation> | ||
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[15.0,)" /> | ||
</Installation> | ||
<Dependencies> | ||
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" /> | ||
</Dependencies> | ||
<Assets> | ||
<Asset Type="Microsoft.VisualStudio.MefComponent" d:Source="Project" d:ProjectName="NSubstitute.Analyzers.CSharp" Path="|NSubstitute.Analyzers.CSharp|"/> | ||
<Asset Type="Microsoft.VisualStudio.Analyzer" d:Source="Project" d:ProjectName="NSubstitute.Analyzers.CSharp" Path="|NSubstitute.Analyzers.CSharp|"/> | ||
<Asset Type="Microsoft.VisualStudio.Analyzer" d:Source="Project" d:ProjectName="NSubstitute.Analyzers.Shared" Path="|NSubstitute.Analyzers.Shared|"/> | ||
</Assets> | ||
<Prerequisites> | ||
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[15.0,16.0)" DisplayName="Visual Studio core editor" /> | ||
<Prerequisite Id="Microsoft.VisualStudio.Component.Roslyn.LanguageServices" Version="[15.0,16.0)" DisplayName="Roslyn Language Services" /> | ||
</Prerequisites> | ||
</PackageManifest> |
11 changes: 11 additions & 0 deletions
11
...te.Analyzers.CSharp/CodeFixProviders/NonVirtualSetupSuppressDiagnosticsCodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using NSubstitute.Analyzers.Shared.CodeFixProviders; | ||
|
||
namespace NSubstitute.Analyzers.CSharp.CodeFixProviders | ||
{ | ||
[ExportCodeFixProvider(LanguageNames.CSharp)] | ||
internal class NonVirtualSetupSuppressDiagnosticsCodeFixProvider : AbstractNonVirtualSetupSuppressDiagnosticsCodeFixProvider | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
...zers.Shared/CodeFixProviders/AbstractNonVirtualSetupSuppressDiagnosticsCodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System.Collections.Immutable; | ||
|
||
namespace NSubstitute.Analyzers.Shared.CodeFixProviders | ||
{ | ||
internal class AbstractNonVirtualSetupSuppressDiagnosticsCodeFixProvider : AbstractSuppressDiagnosticsCodeFixProvider | ||
{ | ||
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIdentifiers.NonVirtualSetupSpecification); | ||
} | ||
} |
172 changes: 172 additions & 0 deletions
172
...ubstitute.Analyzers.Shared/CodeFixProviders/AbstractSuppressDiagnosticsCodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using NSubstitute.Analyzers.Shared.Extensions; | ||
using NSubstitute.Analyzers.Shared.Settings; | ||
using NSubstitute.Analyzers.Shared.TinyJson; | ||
|
||
namespace NSubstitute.Analyzers.Shared.CodeFixProviders | ||
{ | ||
internal abstract class AbstractSuppressDiagnosticsCodeFixProvider : CodeFixProvider | ||
{ | ||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
var project = context.Document.Project; | ||
var workspace = project.Solution.Workspace; | ||
|
||
// check if we are allowed to add it | ||
if (!workspace.CanApplyChange(ApplyChangesKind.AddAdditionalDocument)) | ||
{ | ||
return; | ||
} | ||
|
||
var settingsFile = GetSettingsFile(project); | ||
|
||
// creating additional document from Roslyn is broken (https://github.com/dotnet/roslyn/issues/4655) the nsubstitute.json file have to be created by users manually | ||
// if there is no settings file do not provide refactorings | ||
if (settingsFile == null) | ||
{ | ||
return; | ||
} | ||
|
||
var root = await context.Document.GetSyntaxRootAsync(); | ||
var model = await context.Document.GetSemanticModelAsync(); | ||
foreach (var diagnostic in context.Diagnostics.Where(diagnostic => | ||
FixableDiagnosticIds.Contains(diagnostic.Id))) | ||
{ | ||
var syntaxNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); | ||
var symbolInfo = model.GetSymbolInfo(syntaxNode); | ||
|
||
foreach (var innerSymbol in GetSuppressibleSymbol(symbolInfo.Symbol)) | ||
{ | ||
context.RegisterCodeFix( | ||
CodeAction.Create( | ||
CreateCodeFixTitle(diagnostic, innerSymbol), | ||
cancellationToken => GetTransformedSolutionAsync(context, diagnostic, settingsFile, innerSymbol)), | ||
diagnostic); | ||
} | ||
} | ||
} | ||
|
||
protected virtual IEnumerable<ISymbol> GetSuppressibleSymbol(ISymbol symbol) | ||
{ | ||
if (symbol == null) | ||
{ | ||
yield break; | ||
} | ||
|
||
yield return symbol; | ||
|
||
if (!(symbol is ITypeSymbol)) | ||
{ | ||
yield return symbol.ContainingType; | ||
yield return symbol.ContainingType.ContainingNamespace; | ||
} | ||
|
||
if (symbol is ITypeSymbol) | ||
{ | ||
yield return symbol.ContainingNamespace; | ||
} | ||
} | ||
|
||
private static string CreateCodeFixTitle(Diagnostic diagnostic, ISymbol innerSymbol) | ||
{ | ||
var prefix = GetSymbolTitlePrefix(innerSymbol); | ||
return $"Suppress {diagnostic.Id} for {prefix} {innerSymbol.Name} in {AnalyzersSettings.AnalyzerFileName}"; | ||
} | ||
|
||
private static string GetSymbolTitlePrefix(ISymbol innerSymbol) | ||
{ | ||
switch (innerSymbol) | ||
{ | ||
case IMethodSymbol _: | ||
return "method"; | ||
case IPropertySymbol propertySymbol when propertySymbol.IsIndexer: | ||
return "indexer"; | ||
case IPropertySymbol _: | ||
return "property"; | ||
case ITypeSymbol _: | ||
return "class"; | ||
case INamespaceSymbol _: | ||
return "namespace"; | ||
default: | ||
return string.Empty; | ||
} | ||
} | ||
|
||
private Task<Solution> GetTransformedSolutionAsync(CodeFixContext context, Diagnostic diagnostic, TextDocument settingsFile, ISymbol symbol) | ||
{ | ||
var project = context.Document.Project; | ||
var settingsFileId = settingsFile?.Id; | ||
if (settingsFileId != null) | ||
{ | ||
project = project.RemoveAdditionalDocument(settingsFileId); | ||
} | ||
else | ||
{ | ||
settingsFileId = DocumentId.CreateNewId(project.Id); | ||
} | ||
|
||
var options = GetUpdatedAnalyzersOptions(context, diagnostic, symbol); | ||
|
||
var solution = project.Solution; | ||
|
||
solution = solution.AddAdditionalDocument( | ||
settingsFileId, | ||
AnalyzersSettings.AnalyzerFileName, | ||
Json.Encode(options, pretty: true)); | ||
|
||
return Task.FromResult(solution); | ||
} | ||
|
||
private static AnalyzersSettings GetUpdatedAnalyzersOptions(CodeFixContext context, Diagnostic diagnostic, ISymbol symbol) | ||
{ | ||
var options = context.Document.Project.AnalyzerOptions.GetSettings(default(CancellationToken)); | ||
var target = CreateSuppressionTarget(symbol); | ||
options.Suppressions = options.Suppressions ?? new List<Suppression>(); | ||
|
||
var existingSuppression = options.Suppressions.FirstOrDefault(suppression => suppression.Target == target); | ||
|
||
if (existingSuppression != null) | ||
{ | ||
existingSuppression.Rules = existingSuppression.Rules ?? new List<string>(); | ||
existingSuppression.Rules.Add(diagnostic.Id); | ||
} | ||
else | ||
{ | ||
options.Suppressions.Add(new Suppression | ||
{ | ||
Target = target, | ||
Rules = new List<string> | ||
{ | ||
diagnostic.Id | ||
} | ||
}); | ||
} | ||
|
||
return options; | ||
} | ||
|
||
private static string CreateSuppressionTarget(ISymbol symbol) | ||
{ | ||
ISymbol actualSymbol = symbol; | ||
if (actualSymbol is IMethodSymbol methodSymbol && methodSymbol.ReducedFrom != null) | ||
{ | ||
actualSymbol = methodSymbol.ReducedFrom; | ||
} | ||
|
||
return DocumentationCommentId.CreateDeclarationId(actualSymbol); | ||
} | ||
|
||
private static TextDocument GetSettingsFile(Project project) | ||
{ | ||
return project.AdditionalDocuments.SingleOrDefault(document => | ||
document.Name.Equals(AnalyzersSettings.AnalyzerFileName, StringComparison.CurrentCultureIgnoreCase)); | ||
} | ||
} | ||
} |
Oops, something went wrong.