Skip to content

Commit

Permalink
Conventional.Roslyn vNext (#73)
Browse files Browse the repository at this point in the history
* Initial work to update Roslyn conventions to dotnet 3.1

Solves the cross-platform issues with MSBuildWorkspace

Extracts Analyzers to a separate assembly to ensure they are decoupled from Microsoft.CodeAnalysis.Document, Microsoft.CodeAnalysis.Project, Microsoft.CodeAnalysis.Solution

* Conventions work in Rider both in IDE, and in tests.

Still TODO: Packaging the analyzers separately, pushing a new version of Best.Conventional.Roslyn, check the tools install / uninstall in VS 2019.

* Ensure Analyzers dependency is not a nuget-type dependency

We don't want to publish the analyzers seperately to the Roslyn project.

* Configures generated code analysis

* Adds category name to analyzers to ensure they display correctly in IDEs
  • Loading branch information
andrewabest authored Oct 5, 2020
1 parent 1516844 commit dfcc9aa
Show file tree
Hide file tree
Showing 2,355 changed files with 265 additions and 2,687,993 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,4 @@ UpgradeLog*.htm
FakesAssemblies/

/development.json
**/.idea/*
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Conventional [![Build status](https://ci.appveyor.com/api/projects/status/b34y026n60v9oe16?svg=true)](https://ci.appveyor.com/project/andrewabest/conventional)
Conventional [![Build status](https://ci.appveyor.com/api/projects/status/b34y026n60v9oe16?svg=true)](https://ci.appveyor.com/project/andrewabest/conventional)
[![NuGet](https://img.shields.io/nuget/v/Best.Conventional.svg)](https://www.nuget.org/packages/Best.Conventional/)
============

Expand All @@ -12,10 +12,6 @@ Conventional targets .NET Standard 2.0, and as of Conventional 7.x, Conventional

Install-Package Best.Conventional

Looking for Roslyn-based conventions? Check out [the documentation](https://github.com/andrewabest/Conventional/wiki/Roslyn-Conventions) for more information.

Conventional.Roslyn does not target .NET Standard 2.0 due to there being [no official .NET Core support](https://github.com/dotnet/roslyn/issues/17974) for `MSBuildWorkspace`, a core building block of Roslyn.

## Documentation

To get started with Conventional, please check out [the wiki](https://github.com/andrewabest/Conventional/wiki) for a comprehensive list of included conventions, sample usages, and configuration information.
Expand All @@ -24,6 +20,14 @@ To get started with Conventional, please check out [the wiki](https://github.com

Not sure how to get started with Conventional? Check out [the sample repository](https://github.com/andrewabest/Conventional.Samples) which contains a bunch of real-world usage examples

## Roslyn-based conventions

As of [This PR](https://github.com/andrewabest/Conventional/pull/73) Roslyn conventions are now supported on Dotnet Core.

Install-Package Best.Conventional.Roslyn

Rolsyn-based conventions target `netcoreapp3.1`. Check out [the documentation](https://github.com/andrewabest/Conventional/wiki/Roslyn-Conventions) for more information.

## Contributing

Conventional's test suite requires a default named `.\SQLEXPRESS` instance. If you have another instance you would like to use for development, create a copy of `development.settings.example` in the solution root and rename to `development.settings`, and supply your own connection string.
Expand Down
4 changes: 4 additions & 0 deletions src/Roslyn/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
root = true

[*.cs]
dotnet_diagnostic.RS1027.severity = none
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.6.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.6.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Conventional.Roslyn.Analyzers
{
/// <summary>
/// Register an action to be executed at completion of semantic analysis of a SyntaxNode with an appropriate Kind. A syntax node action can report Diagnostics about SyntaxNodes, and can also collect state information to be used by other syntax node actions or code block end actions
/// </summary>
public abstract class ConventionalSyntaxNodeAnalyzer : DiagnosticAnalyzer
{
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.IfStatement, SyntaxKind.ElseClause);
context.EnableConcurrentExecution();
// Generate diagnostic reports, but do not allow analyzer actions
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics);
}

void Analyze(SyntaxNodeAnalysisContext context)
{
var result = CheckNode(context.Node);

if (result.Success == false)
{
var loc = context.Node.GetLocation();
var diagnostic = Diagnostic.Create(Rule, loc, $"{result.Message} must have braces");
context.ReportDiagnostic(diagnostic);
}
}

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

protected abstract DiagnosticDescriptor Rule { get; }

public abstract DiagnosticResult CheckNode(SyntaxNode node);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Conventional.Roslyn.Analyzers
{
public static class DiagnosticDescriptorIdentifiers
{
public const string IfAndElseMustHaveBracesAnalyzer = "BC1000";
public const string UsingsStatementsMustNotBeNestedAnalyzer = "BC1001";
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
namespace Conventional.Roslyn.Conventions
using Microsoft.CodeAnalysis;

namespace Conventional.Roslyn.Analyzers
{
public class DiagnosticResult
{
public string Message { get; private set; }
public int LineNumber { get; private set; }
public SyntaxNode[] FailedNodes { get; private set; }
public bool Success { get; private set; }

public static DiagnosticResult Succeeded()
{
return new DiagnosticResult() { Success = true };
}

public static DiagnosticResult Failed(string failureMessage, int lineNumber)
public static DiagnosticResult Failed(string failureMessage, params SyntaxNode[] failedNodes)
{
return new DiagnosticResult() { Message = failureMessage, FailedNodes = failedNodes };
}

public void UpdateFailureMessage(string failureMessage)
{
return new DiagnosticResult() { Message = failureMessage, LineNumber = lineNumber };
Message = failureMessage;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Conventional.Roslyn.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class IfAndElseMustHaveBracesAnalyzer : ConventionalSyntaxNodeAnalyzer
{
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticDescriptorIdentifiers.IfAndElseMustHaveBracesAnalyzer,
"IfAndElseMustHaveBracesAnalyzer",
"{0}",
"Conventional Analyzers",
DiagnosticSeverity.Warning,
true);

public override DiagnosticResult CheckNode(SyntaxNode node)
{
switch (node)
{
case IfStatementSyntax ifStatement when (ifStatement.Statement.IsKind(SyntaxKind.Block) == false):
return DiagnosticResult.Failed("If");
case ElseClauseSyntax elseSyntax when (elseSyntax.Statement.IsKind(SyntaxKind.IfStatement) == false) &&
(elseSyntax.Statement.IsKind(SyntaxKind.Block) == false):
return DiagnosticResult.Failed("Else");
default:
return DiagnosticResult.Succeeded();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Conventional.Roslyn.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class UsingsStatementsMustNotBeNestedAnalyzer : ConventionalSyntaxNodeAnalyzer
{
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticDescriptorIdentifiers.UsingsStatementsMustNotBeNestedAnalyzer,
"UsingsStatementsMustNotBeNestedAnalyzer",
"{0}",
"Conventional Analyzers",
DiagnosticSeverity.Warning,
true);

public override DiagnosticResult CheckNode(SyntaxNode node)
{
if (node is NamespaceDeclarationSyntax)
{
var usings = node.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
if (usings.Any())
{
return DiagnosticResult.Failed("Using", usings.First());
}
}

return DiagnosticResult.Succeeded();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Best.Conventional" Version="9.0.0" />
<PackageReference Include="FluentAssertions" Version="4.19.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Conventional.Roslyn\Conventional.Roslyn.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System.Linq;
using Conventional.Roslyn;
using NUnit.Framework;
using FluentAssertions;
using NUnit.Framework;

namespace Conventional.Tests.Roslyn.Conventions
namespace Conventional.Roslyn.Tests.Conventions
{
public class SolutionDiagnosticAnalyzerConventionSpecificationTests
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using System;
using System.Linq;
using Conventional.Extensions;
using Conventional.Roslyn;
using Conventional.Roslyn.Conventions;
using NUnit.Framework;

namespace Conventional.Tests.Roslyn
namespace Conventional.Roslyn.Tests
{
public class DogFoodConventions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;

namespace Conventional.Tests.Roslyn
namespace Conventional.Roslyn.Tests
{
public class TestSolution : IDisposable
{
Expand All @@ -10,7 +10,7 @@ public TestSolution(string extendedPath)
{
_previousSolutionRoot = KnownPaths.SolutionRoot;

KnownPaths.SolutionRoot = KnownPaths.SolutionRoot + extendedPath;
KnownPaths.SolutionRoot += extendedPath;
}

public void Dispose()
Expand Down
59 changes: 15 additions & 44 deletions src/Roslyn/Conventional.Roslyn.sln
Original file line number Diff line number Diff line change
@@ -1,57 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Conventional.Roslyn", "Conventional.Roslyn\Conventional.Roslyn.csproj", "{7D0DAF28-CA63-49F8-A9C2-25729C731948}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Conventional.Roslyn", "Conventional.Roslyn\Conventional.Roslyn.csproj", "{DCDC83C2-C01D-4A13-99D7-B312665D238D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Conventional.Tests.Roslyn", "Conventional.Tests.Roslyn\Conventional.Tests.Roslyn.csproj", "{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Conventional.Roslyn.Tests", "Conventional.Roslyn.Tests\Conventional.Roslyn.Tests.csproj", "{1F5EFA38-0FB1-41CB-B856-DE4AA55D07AC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Getting Started", "Getting Started", "{C38DA37B-5388-4443-8C6C-617A677D86AD}"
ProjectSection(SolutionItems) = preProject
development.json.example = development.json.example
README.md = README.md
EndProjectSection
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Conventional.Roslyn.Analyzers", "Conventional.Roslyn.Analyzers\Conventional.Roslyn.Analyzers.csproj", "{3546B173-D100-47AE-BF78-15DDBA903B20}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Debug|x64.ActiveCfg = Debug|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Debug|x64.Build.0 = Debug|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Debug|x86.ActiveCfg = Debug|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Debug|x86.Build.0 = Debug|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Release|Any CPU.Build.0 = Release|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Release|x64.ActiveCfg = Release|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Release|x64.Build.0 = Release|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Release|x86.ActiveCfg = Release|Any CPU
{7D0DAF28-CA63-49F8-A9C2-25729C731948}.Release|x86.Build.0 = Release|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Debug|x64.ActiveCfg = Debug|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Debug|x64.Build.0 = Debug|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Debug|x86.ActiveCfg = Debug|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Debug|x86.Build.0 = Debug|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Release|Any CPU.Build.0 = Release|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Release|x64.ActiveCfg = Release|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Release|x64.Build.0 = Release|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Release|x86.ActiveCfg = Release|Any CPU
{10DC5795-E692-4B6E-854B-E1E1AA9F1B36}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3358011E-DD23-4143-BAD1-A5755A6154A7}
{DCDC83C2-C01D-4A13-99D7-B312665D238D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DCDC83C2-C01D-4A13-99D7-B312665D238D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCDC83C2-C01D-4A13-99D7-B312665D238D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DCDC83C2-C01D-4A13-99D7-B312665D238D}.Release|Any CPU.Build.0 = Release|Any CPU
{1F5EFA38-0FB1-41CB-B856-DE4AA55D07AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F5EFA38-0FB1-41CB-B856-DE4AA55D07AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F5EFA38-0FB1-41CB-B856-DE4AA55D07AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F5EFA38-0FB1-41CB-B856-DE4AA55D07AC}.Release|Any CPU.Build.0 = Release|Any CPU
{3546B173-D100-47AE-BF78-15DDBA903B20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3546B173-D100-47AE-BF78-15DDBA903B20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3546B173-D100-47AE-BF78-15DDBA903B20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3546B173-D100-47AE-BF78-15DDBA903B20}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
Loading

0 comments on commit dfcc9aa

Please sign in to comment.