Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…yzers into GH-22-unify-resource-files
  • Loading branch information
tpodolak committed Jul 18, 2018
2 parents a602c38 + a43e3db commit 7215921
Show file tree
Hide file tree
Showing 86 changed files with 14,270 additions and 65 deletions.
4 changes: 4 additions & 0 deletions Acknowledgements.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ Cake (C# Make) is used for NSubstitute.Analyzers's build. Cake is distributed un

## Microsoft .NET Framework [http://www.microsoft.com/net/]
NSubstitute.Analyzers is coded in C# and compiled using Microsoft .NET.

## TinyJson [https://github.com/gering/Tiny-JSON]
TinyJson is used for serialization of settings file. Due to its small size it was incorporated directly into NSubstitute.Analyzers which solved our issues with NuGet references to Newtonsoft.Json.
TinyJson is distributed under MIT license [https://github.com/gering/Tiny-JSON/blob/master/LICENSE]
14 changes: 14 additions & 0 deletions NSubstitute.Analyzers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSubstitute.Analyzers.Visua
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSubstitute.Analyzers.Tests.VisualBasic", "tests\NSubstitute.Analyzers.Tests.VisualBasic\NSubstitute.Analyzers.Tests.VisualBasic.csproj", "{00A000B8-B887-4216-92B2-E810616CE7A8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSubstitute.Analyzers.CSharp.Vsix", "src\NSubstitute.Analyzers.CSharp.Vsix\NSubstitute.Analyzers.CSharp.Vsix.csproj", "{4DF03310-98B3-450E-90FE-2EF423A6CC59}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSubstitute.Analyzers.VisualBasic.Vsix", "src\NSubstitute.Analyzers.VisualBasic.Vsix\NSubstitute.Analyzers.VisualBasic.Vsix.csproj", "{0B098301-16AA-401B-ACAC-3D81189C18C6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -46,6 +50,14 @@ Global
{00A000B8-B887-4216-92B2-E810616CE7A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00A000B8-B887-4216-92B2-E810616CE7A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00A000B8-B887-4216-92B2-E810616CE7A8}.Release|Any CPU.Build.0 = Release|Any CPU
{4DF03310-98B3-450E-90FE-2EF423A6CC59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DF03310-98B3-450E-90FE-2EF423A6CC59}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DF03310-98B3-450E-90FE-2EF423A6CC59}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DF03310-98B3-450E-90FE-2EF423A6CC59}.Release|Any CPU.Build.0 = Release|Any CPU
{0B098301-16AA-401B-ACAC-3D81189C18C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B098301-16AA-401B-ACAC-3D81189C18C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B098301-16AA-401B-ACAC-3D81189C18C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B098301-16AA-401B-ACAC-3D81189C18C6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{2B7BF4D2-4964-4674-9268-70AE5A3C98DB} = {FB46CD9F-6E27-4767-AD1E-8C21CD08F993}
Expand All @@ -54,5 +66,7 @@ Global
{4FA76F86-FB64-4DD9-8F62-3508FB4544C8} = {0ED2F2A2-CF6E-4EED-8166-F22B5D7631F4}
{94139977-A98F-4B19-B3AE-E1001F1EDF00} = {FB46CD9F-6E27-4767-AD1E-8C21CD08F993}
{00A000B8-B887-4216-92B2-E810616CE7A8} = {0ED2F2A2-CF6E-4EED-8166-F22B5D7631F4}
{4DF03310-98B3-450E-90FE-2EF423A6CC59} = {FB46CD9F-6E27-4767-AD1E-8C21CD08F993}
{0B098301-16AA-401B-ACAC-3D81189C18C6} = {FB46CD9F-6E27-4767-AD1E-8C21CD08F993}
EndGlobalSection
EndGlobal
3 changes: 2 additions & 1 deletion build/paths.cake
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ public class BuildPaths
var artifacts = rootDir.Combine(".artifacts");
var testResults = artifacts.Combine("Test-Results");

var sharedTestsDir = rootDir.Combine("tests").Combine("NSubstitute.Analyzers.Tests.Shared");
var csharpAnalyzerTestDir = rootDir.Combine("tests").Combine("NSubstitute.Analyzers.Tests.CSharp");

var visualBasicAnalyzerTestDir = rootDir.Combine("tests").Combine("NSubstitute.Analyzers.Tests.VisualBasic");

var testDirs = new []{
sharedTestsDir,
csharpAnalyzerTestDir,
visualBasicAnalyzerTestDir
};
Expand Down
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>
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"
}
}
}
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>
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
{
}
}
2 changes: 1 addition & 1 deletion src/NSubstitute.Analyzers.CSharp/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:element name="root" msdata:IsSettingsFileDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
context.RegisterCodeFix(codeAction, diagnostic);
}

return Task.FromResult(1);
return Task.CompletedTask;
}

protected abstract TInvocationExpressionSyntax GetInvocationExpressionSyntaxWithEmptyArgumentList(TInvocationExpressionSyntax invocationExpressionSyntax);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
context.RegisterCodeFix(codeAction, diagnostic);
}

return Task.FromResult(1);
return Task.CompletedTask;
}

protected abstract TGenericNameSyntax GetGenericNameSyntax(TInvocationExpression methodInvocationNode);
Expand Down
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);
}
}
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));
}
}
}
Loading

0 comments on commit 7215921

Please sign in to comment.