Skip to content

Commit

Permalink
Feature: warn on use of tuples (#257)
Browse files Browse the repository at this point in the history
  • Loading branch information
dpvreony authored Oct 25, 2024
1 parent 11c21e8 commit 0177ee6
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
// This file is licensed to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.Collections.Immutable;
using Dhgms.GripeWithRoslyn.Analyzer.CodeCracker.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Dhgms.GripeWithRoslyn.Analyzer.Analyzers.Language
{
/// <summary>
/// Analyzer to ensure Tuples are not used.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class DoNotUseTuplesAnalyzer : DiagnosticAnalyzer
{
internal const string Title = "Do not use Tuples.";

private const string MessageFormat = Title;

private const string Category = SupportedCategories.Design;

private readonly DiagnosticDescriptor _rule;

/// <summary>
/// Initializes a new instance of the <see cref="DoNotUseTuplesAnalyzer"/> class.
/// </summary>
public DoNotUseTuplesAnalyzer()
{
_rule = new DiagnosticDescriptor(
DiagnosticIdsHelper.DoNotUseTuples,
Title,
MessageFormat,
Category,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: DiagnosticResultDescriptionFactory.DoNotUseTuples());
}

/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(_rule);

/// <inheritdoc />
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.RegisterSyntaxNodeAction(AnalyzeParameter, SyntaxKind.TupleElement, SyntaxKind.TupleExpression, SyntaxKind.TupleType);
}

private void AnalyzeParameter(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext)
{
switch (syntaxNodeAnalysisContext.Node)
{
case TupleElementSyntax tupleElementSyntax:
syntaxNodeAnalysisContext.ReportDiagnostic(Diagnostic.Create(_rule, tupleElementSyntax.GetLocation()));
break;
case TupleExpressionSyntax tupleExpressionSyntax:
syntaxNodeAnalysisContext.ReportDiagnostic(Diagnostic.Create(_rule, tupleExpressionSyntax.GetLocation()));
break;
case TupleTypeSyntax tupleTypeSyntax:
syntaxNodeAnalysisContext.ReportDiagnostic(Diagnostic.Create(_rule, tupleTypeSyntax.GetLocation()));
break;
}
}
}
}
2 changes: 2 additions & 0 deletions src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticIdsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,7 @@ internal static class DiagnosticIdsHelper
internal static string DoNotUseEntityFrameworkCoreDbSetUpdate => "GR0047";

internal static string DoNotUseEntityFrameworkCoreDbSetUpdateRange => "GR0048";

internal static string DoNotUseTuples => "GR0049";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,7 @@ internal static class DiagnosticResultDescriptionFactory
internal static string ConstructorShouldAcceptSchedulerArgument() => $"ReactiveUI ViewModel Constructors should accept a parameter of \nSystem.Reactive.Concurrency.Scheduler.\n\nThis is to aid with Unit Testing time sensitive logic.";

internal static string ProjectShouldEnableNullableReferenceTypes() => $"Project should enable Nullable Reference Types. This is to aid with code correctness and to avoid null reference exceptions.";

internal static string DoNotUseTuples() => $"Do not use Tuples. Consider the use of Record types or Classes. This allows for easier to read and more sustainable code.";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

Expand All @@ -12,7 +11,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Dhgms.GripeWithRoslyn.Analyzer\Dhgms.GripeWithRoslyn.Analyzer.csproj" >
<ProjectReference Include="..\Dhgms.GripeWithRoslyn.Analyzer\Dhgms.GripeWithRoslyn.Analyzer.csproj">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>compile</IncludeAssets>
</ProjectReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This file is licensed to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System.Linq;
using Dhgms.GripeWithRoslyn.Analyzer.Analyzers.EfCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
Expand Down
24 changes: 24 additions & 0 deletions src/Dhgms.GripeWithRoslyn.Testing/Language/TupleProof.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
// This file is licensed to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using Dhgms.GripeWithRoslyn.Analyzer.Analyzers.Language;

namespace Dhgms.GripeWithRoslyn.Testing.Language
{
/// <summary>
/// Analyzer Proofs for <see cref="Tuple"/>.
/// </summary>
public static class TupleProof
{
/// <summary>
/// Proof of <see cref="Tuple"/> method invocation to trigger <see cref="DoNotUseTuplesAnalyzer"/>.
/// </summary>
/// <returns>A <see cref="Tuple"/>.</returns>
public static (int Number, string Name) GetTuple()
{
return (1, "Hello");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ protected override ExpectedDiagnosticModel[] GetExpectedDiagnosticLines()
new ExpectedDiagnosticModel(
"EfCore\\DbSetUpdateProof.cs",
DiagnosticSeverity.Error,
29,
30,
12)
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ protected override ExpectedDiagnosticModel[] GetExpectedDiagnosticLines()
new ExpectedDiagnosticModel(
"EfCore\\DbSetUpdateProof.cs",
DiagnosticSeverity.Error,
46,
47,
12)
];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
// This file is licensed to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using Dhgms.GripeWithRoslyn.Analyzer.Analyzers.EfCore;
using Dhgms.GripeWithRoslyn.Analyzer.Analyzers.Language;
using Dhgms.GripeWithRoslyn.UnitTests.Analyzer.Analyzers.EfCore;
using Microsoft.CodeAnalysis;

namespace Dhgms.GripeWithRoslyn.UnitTests.Analyzer.Analyzers.Language
{
/// <summary>
/// Unit Tests for <see cref="DoNotUseTuplesAnalyzer"/>.
/// </summary>
public sealed class DoNotUseTuplesAnalyzerTests : AbstractAnalyzerTest<DoNotUseTuplesAnalyzer>
{
/// <inheritdoc/>
protected override ExpectedDiagnosticModel[] GetExpectedDiagnosticLines()
{
const string TupleProofFilePath = "Language\\TupleProof.cs";

return
[
new ExpectedDiagnosticModel(
TupleProofFilePath,
DiagnosticSeverity.Error,
18,
22),

new ExpectedDiagnosticModel(
TupleProofFilePath,
DiagnosticSeverity.Error,
18,
23),

new ExpectedDiagnosticModel(
TupleProofFilePath,
DiagnosticSeverity.Error,
18,
35),

new ExpectedDiagnosticModel(
TupleProofFilePath,
DiagnosticSeverity.Error,
20,
19),
];
}
}
}
16 changes: 1 addition & 15 deletions src/Dhgms.GripeWithRoslyn.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dhgms.GripeWithRoslyn.UnitT
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dhgms.GripeWithRoslyn.DotNetTool", "Dhgms.GripeWithRoslyn.DotNetTool\Dhgms.GripeWithRoslyn.DotNetTool.csproj", "{343C46ED-B318-4E49-A219-D142DC309461}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dhgms.GripeWithRoslyn.Vsix", "Dhgms.GripeWithRoslyn.Vsix\Dhgms.GripeWithRoslyn.Vsix.csproj", "{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dhgms.GripeWithRoslyn.Testing", "Dhgms.GripeWithRoslyn.Testing\Dhgms.GripeWithRoslyn.Testing.csproj", "{08C16441-2A0A-4DFF-87B3-F839FA59E986}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dhgms.GripeWithRoslyn.Testing", "Dhgms.GripeWithRoslyn.Testing\Dhgms.GripeWithRoslyn.Testing.csproj", "{08C16441-2A0A-4DFF-87B3-F839FA59E986}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -59,18 +57,6 @@ Global
{343C46ED-B318-4E49-A219-D142DC309461}.Release|arm64.Build.0 = Release|Any CPU
{343C46ED-B318-4E49-A219-D142DC309461}.Release|x86.ActiveCfg = Release|Any CPU
{343C46ED-B318-4E49-A219-D142DC309461}.Release|x86.Build.0 = Release|Any CPU
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Debug|arm64.ActiveCfg = Debug|arm64
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Debug|arm64.Build.0 = Debug|arm64
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Debug|x86.ActiveCfg = Debug|x86
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Debug|x86.Build.0 = Debug|x86
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Release|Any CPU.Build.0 = Release|Any CPU
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Release|arm64.ActiveCfg = Release|arm64
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Release|arm64.Build.0 = Release|arm64
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Release|x86.ActiveCfg = Release|x86
{E558BA1B-3DDE-48C4-9E58-1450D65FE9B5}.Release|x86.Build.0 = Release|x86
{08C16441-2A0A-4DFF-87B3-F839FA59E986}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{08C16441-2A0A-4DFF-87B3-F839FA59E986}.Debug|Any CPU.Build.0 = Debug|Any CPU
{08C16441-2A0A-4DFF-87B3-F839FA59E986}.Debug|arm64.ActiveCfg = Debug|Any CPU
Expand Down

0 comments on commit 0177ee6

Please sign in to comment.