Skip to content

Commit

Permalink
Add SP1003 analyzer that validates correct return statements formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
sergey-rybalkin committed Jun 12, 2024
1 parent f997c3f commit 805c324
Show file tree
Hide file tree
Showing 20 changed files with 341 additions and 210 deletions.
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,5 @@ dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = priva
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case

# RS2008: Enable analyzer release tracking
dotnet_diagnostic.RS2008.severity = none
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ refactorings from NuGet packages so you need to install VSIX extension in order

## Analyzers

- SP1001 (Invalid Exception Message) - validates exception message to match best practices. Inspired by [Microsoft guidelines](https://docs.microsoft.com/en-us/dotnet/api/system.exception.message?view=netcore-3.1#remarks) and [StackOverflow discussion](https://stackoverflow.com/questions/1136829/do-you-end-your-exception-messages-with-a-period/34136055).
- SP1002 (Cancellation token name) - validates that parameters of type `CancellationToken` are named `ct`.
- SP1003 (Separate return with empty line) - validates that return statement in block is separated with empty line.
- SP1131 (Unsafe Condition Analyzer) - validates that constant pattern matching is used instead of `==` operator to avoid typos like `if (flag = true)`, also suggests using negated not pattern instead of `!=` operator.
- SP2100 (Line Too Long Analyzer) - validates that code lines do not exceed 110 symbols.
- SP2101 (Method Too Long Analyzer) - validates that methods length do not exceed 50 lines.
- SP2102 (Property Too Long Analyzer) - validates that property accessors do not exceed 40 lines.
- SP2103 (File Too Long Analyzer) - validates that files length do not exceed 400 lines.
- SP1001 (Invalid Exception Message) - validates exception message to match best practices. Inspired by [Microsoft guidelines](https://docs.microsoft.com/en-us/dotnet/api/system.exception.message?view=netcore-3.1#remarks) and [StackOverflow discussion](https://stackoverflow.com/questions/1136829/do-you-end-your-exception-messages-with-a-period/34136055).
- SP1002 (Cancellation token name) - validates that parameters of type `CancellationToken` are named `ct`.

## Configuration
Analyzer line limits can be configured through [StyleCop configuration file](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/Configuration.md). Add the following snippet with configured values
Expand Down
70 changes: 70 additions & 0 deletions src/StyleCopPlus.Test/Analyzers/SP1003AnalyzerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StyleCopPlus.Analyzers;
using StyleCopPlus.Test.Helpers;
using TestHelper;

namespace StyleCopPlus.Test.Analyzers
{
[TestClass]
public class SP1003AnalyzerTests : DiagnosticVerifier
{
[TestMethod]
public void DoesNotReport_ValidReturnStatements()
{
string test = DataHelper.GetEmbeddedResource(
DataHelper.SP1003FormattedReturnStatements,
out _,
out _);

VerifyCSharpDiagnostic(test);
}

[TestMethod]
public void Reports_UnformattedBlockReturnStatement()
{
string test = DataHelper.GetEmbeddedResource(
DataHelper.SP1003UnformattedBlockReturnStatement,
out int line,
out int column);

DiagnosticResult expected = CreateResult(line, column);

VerifyCSharpDiagnostic(test, expected);
}

[TestMethod]
public void Reports_UnformattedMethodReturnStatement()
{
string test = DataHelper.GetEmbeddedResource(
DataHelper.SP1003UnformattedMethodReturnStatement,
out int line,
out int column);

DiagnosticResult expected = CreateResult(line, column);

VerifyCSharpDiagnostic(test, expected);
}

protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
{
return new SP1003SeparatedReturnStatementAnalyzer();
}

private DiagnosticResult CreateResult(int lineNumber, int column)
{
return new DiagnosticResult
{
Id = SP1003SeparatedReturnStatementAnalyzer.DiagnosticId,
Message = SP1003SeparatedReturnStatementAnalyzer.MessageFormat,
Severity = DiagnosticSeverity.Warning,
Locations =
[
new DiagnosticResultLocation("Test0.cs", lineNumber, column)
]
};
}

}
}
3 changes: 3 additions & 0 deletions src/StyleCopPlus.Test/Helpers/DataHelper.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public static partial class DataHelper
public const string SP1001ValidThrowStatement = "SP1001.ValidThrowStatement.cs";
public const string SP1002InvalidParameterName = "SP1002.InvalidParameterName.cs";
public const string SP1002ValidParameterName = "SP1002.ValidParameterName.cs";
public const string SP1003FormattedReturnStatements = "SP1003.FormattedReturnStatements.cs";
public const string SP1003UnformattedBlockReturnStatement = "SP1003.UnformattedBlockReturnStatement.cs";
public const string SP1003UnformattedMethodReturnStatement = "SP1003.UnformattedMethodReturnStatement.cs";
public const string SP1131CorrectOperandsWithStatic = "SP1131.CorrectOperandsWithStatic.cs";
public const string SP1131IncorrectOperands = "SP1131.IncorrectOperands.cs";
public const string SP1131IncorrectOperandsFixed = "SP1131.IncorrectOperandsFixed.cs";
Expand Down
2 changes: 1 addition & 1 deletion src/StyleCopPlus.Test/Helpers/DataHelper.tt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace StyleCopPlus.Test.Helpers
foreach (string file in files)
{
string fileName = Path.GetFileName(file);
if (fileName.StartsWith("Temp"))
if (fileName.StartsWith("Temp") || file.Contains("\\bin\\") || file.Contains("\\obj\\"))
continue;

string folderName = Path.GetFileName(Path.GetDirectoryName(file));
Expand Down
112 changes: 12 additions & 100 deletions src/StyleCopPlus.Test/Playground/AnalyzerPlayground.csproj
Original file line number Diff line number Diff line change
@@ -1,105 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E1FA01A7-84FB-4BAA-8A2F-2B70E223965E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>AnalyzerPlayground</RootNamespace>
<AssemblyName>AnalyzerPlayground</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CheckParameters\GenericParameter.cs" />
<Compile Include="CheckParameters\OutParameter.cs" />
<Compile Include="CheckParameters\ReferenceParameter.cs" />
<Compile Include="CheckParameters\RefParameter.cs" />
<Compile Include="CheckParameters\ValueTypeParameter.cs" />
<Compile Include="CheckParameters\ConstructorParameter.cs" />
<Compile Include="CreateVariable\ConstructorCall.cs" />
<Compile Include="CreateVariable\FluentApiCalls.cs" />
<Compile Include="CreateVariable\PropertyCall.cs" />
<Compile Include="CreateVariable\StaticCall.cs" />
<Compile Include="CreateVariable\VoidCall.cs" />
<Compile Include="IntroduceField\ReadonlyField.cs" />
<Compile Include="SP1001\ThrowExpression.cs" />
<Compile Include="SP1001\ThrowStatement.cs" />
<Compile Include="SP1001\ThrowWithoutMessage.cs" />
<Compile Include="SP1001\ValidThrowExpression.cs" />
<Compile Include="SP1001\ValidThrowStatement.cs" />
<Compile Include="SP1002\InvalidParameterName.cs" />
<Compile Include="SP1002\ValidParameterName.cs" />
<Compile Include="SP1131\OperandWithinLambda.cs" />
<Compile Include="SP2100\All.cs" />
<Compile Include="SP2100\ConstructorDefinition.cs" />
<Compile Include="SP2100\ConstructorInvocation.cs" />
<Compile Include="SP2100\MethodDefinition.cs" />
<Compile Include="SP2100\MethodInvocation.cs" />
<Compile Include="SP2100\MethodInvocationWithAssignment.cs" />
<Compile Include="SP2100\FluentApi.cs" />
<Compile Include="SP2101\LongConstructor.cs" />
<Compile Include="SP2101\LongFinalizer.cs" />
<Compile Include="SP2101\LongMethod.cs" />
<Compile Include="SP2101\LongStaticConstructor.cs" />
<Compile Include="SP2101\ValidClass.cs" />
<Compile Include="SP2102\LongGetter.cs" />
<Compile Include="SP2102\LongSetter.cs" />
<Compile Include="SP2102\ValidClass.cs" />
<Compile Include="SP2103\LongClass.cs" />
<Compile Include="SP2103\ValidClass.cs" />
<Compile Include="SP1131\CorrectOperandsWithStatic.cs" />
<Compile Include="SP1131\IncorrectOperands.cs" />
<Compile Include="SP1131\IncorrectOperandsFixed.cs" />
<Compile Include="SP1131\IncorrectOperandsWithConst.cs" />
<Compile Include="SP1131\IncorrectOperandsWithConstFixed.cs" />
<Compile Include="SP1131\InvertedOperands.cs" />
<Compile Include="SP1131\InvertedOperandsFixed.cs" />
<Compile Include="SP1131\NotEqualsOperator.cs" />
<Compile Include="SP1131\NotEqualsOperatorFixed.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>

<ItemGroup>
<AdditionalFiles Include="stylecop.json" Link="stylecop.json" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

</Project>



Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;

namespace AnalyzerPlayground.SP1003
{
internal class FormattedReturnStatements
{
public int M()
{
Console.WriteLine("test");

if (DateTime.Now.Second % 2 == 0)
{
return 1;
}
else if (DateTime.Now.Second % 2 == 1)
{
Console.Write("test");

return 2;
}
else if (DateTime.Now.Second % 3 == 1)
return 5;

Console.WriteLine("test");

return 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace AnalyzerPlayground.SP1003
{
internal class UnformattedBlockReturnStatement
{
public int M()
{
if (DateTime.Now.Second % 2 == 0)
{
Console.WriteLine("test");
/*C*/return 1;
}

return 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace AnalyzerPlayground.SP1003
{
internal class UnformattedMethodReturnStatement
{
public int M()
{
Console.WriteLine("test");
/*C*/return 0;
}
}
}
9 changes: 6 additions & 3 deletions src/StyleCopPlus.Test/StyleCopPlus.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
<EmbeddedResource Include="Playground\SP1001\ValidThrowStatement.cs" />
<EmbeddedResource Include="Playground\SP1002\InvalidParameterName.cs" />
<EmbeddedResource Include="Playground\SP1002\ValidParameterName.cs" />
<EmbeddedResource Include="Playground\SP1003\FormattedReturnStatements.cs" />
<EmbeddedResource Include="Playground\SP1003\UnformattedBlockReturnStatement.cs" />
<EmbeddedResource Include="Playground\SP1003\UnformattedMethodReturnStatement.cs" />
<EmbeddedResource Include="Playground\SP1131\CorrectOperandsWithStatic.cs" />
<EmbeddedResource Include="Playground\SP1131\IncorrectOperands.cs" />
<EmbeddedResource Include="Playground\SP1131\IncorrectOperandsFixed.cs" />
Expand Down Expand Up @@ -79,10 +82,10 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2" />
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
<PackageReference Include="MSTest.TestAdapter" Version="3.4.3" />
<PackageReference Include="MSTest.TestFramework" Version="3.4.3" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion src/StyleCopPlus.Vsix/StyleCopPlus.Vsix.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.9.3184" PrivateAssets="all" />
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.10.2179" PrivateAssets="all" />
</ItemGroup>

<PropertyGroup>
Expand Down
6 changes: 3 additions & 3 deletions src/StyleCopPlus.Vsix/source.extension.vsixmanifest
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?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">
<?xml Version="3.3" encoding="utf-8"?>
<PackageManifest Version="3.3.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="StyleCopPlus..f472894e-d3a6-4fe0-a4fc-5d32dfd0e204" Version="3.2.0" Language="en-US" Publisher="Sergey Rybalkin"/>
<Identity Id="StyleCopPlus..f472894e-d3a6-4fe0-a4fc-5d32dfd0e204" Version="3.3.0" Language="en-US" Publisher="Sergey Rybalkin"/>
<DisplayName>StyleCopPlus</DisplayName>
<Description xml:space="preserve">A set of analyzers and refactorings based on StyleCopPlus project implemented with Roslyn engine.</Description>
<MoreInfo>https://github.com/sergey-rybalkin/StyleCopPlus.Analyzers</MoreInfo>
Expand Down
Loading

0 comments on commit 805c324

Please sign in to comment.