Skip to content

Commit

Permalink
Enable full analyzers on the production projects
Browse files Browse the repository at this point in the history
  • Loading branch information
bradwilson committed Dec 9, 2023
1 parent 14788b8 commit cf8a4b7
Show file tree
Hide file tree
Showing 120 changed files with 627 additions and 503 deletions.
10 changes: 8 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -222,5 +222,11 @@ dotnet_naming_style.begins_with_i.capitalization = pascal_case

#### Roslyn diagnostics ####

dotnet_diagnostic.CA1851.severity = warning
dotnet_diagnostic.IDE1006.severity = none
dotnet_diagnostic.CA1000.severity = none # Do not declare static members on generic types
dotnet_diagnostic.CA1002.severity = none # Do not expose generic lists
dotnet_diagnostic.CA1034.severity = none # Do not nest types
dotnet_diagnostic.CA1707.severity = none # Remove the underscores from type name
dotnet_diagnostic.CA1720.severity = none # Identifier contains type name
dotnet_diagnostic.CA1724.severity = none # Type names should not match namespaces
dotnet_diagnostic.CA1859.severity = none # Use concrete types when possible for improved performance
dotnet_diagnostic.IDE1006.severity = none # Naming rule violation
7 changes: 7 additions & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)common\*.cs" LinkBase="Utility\Common" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="[4.2.0]" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" />
Expand All @@ -37,7 +41,10 @@
<Choose>
<When Condition=" !$(MSBuildProjectName.EndsWith('.tests')) ">
<PropertyGroup>
<AnalysisLevel>latest-All</AnalysisLevel>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)signing.snk</AssemblyOriginatorKeyFile>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// Imported from xUnit.net v3, must be removed when this test project is upgraded

#if NETFRAMEWORK
#if !NETCOREAPP

namespace System.Runtime.CompilerServices;

Expand Down
86 changes: 86 additions & 0 deletions src/common/Guard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace Xunit;

/// <summary>
/// Helper class for guarding value arguments and valid state.
/// </summary>
static class Guard
{
/// <summary>
/// Ensures that a nullable reference type argument is not null.
/// </summary>
/// <typeparam name="T">The argument type</typeparam>
/// <param name="argValue">The value of the argument</param>
/// <param name="argName">The name of the argument</param>
/// <returns>The argument value as a non-null value</returns>
/// <exception cref="ArgumentNullException">Thrown when the argument is null</exception>
public static T ArgumentNotNull<T>(
[NotNull] T? argValue,
[CallerArgumentExpression("argValue")] string? argName = null)
where T : class
{
if (argValue is null)
throw new ArgumentNullException(argName?.TrimStart('@'));

return argValue;
}

/// <summary>
/// Ensures that a nullable enumerable type argument is not null or empty.
/// </summary>
/// <typeparam name="T">The argument type</typeparam>
/// <param name="argValue">The value of the argument</param>
/// <param name="argName">The name of the argument</param>
/// <returns>The argument value as a non-null, non-empty value</returns>
/// <exception cref="ArgumentException">Thrown when the argument is null or empty</exception>
public static T ArgumentNotNullOrEmpty<T>(
[NotNull] T? argValue,
[CallerArgumentExpression("argValue")] string? argName = null)
where T : class, IEnumerable
{
ArgumentNotNull(argValue, argName);

if (!argValue.GetEnumerator().MoveNext())
throw new ArgumentException("Argument was empty", argName?.TrimStart('@'));

return argValue;
}

/// <summary>
/// Ensures that an argument is valid.
/// </summary>
/// <param name="message">The exception message to use when the argument is not valid</param>
/// <param name="test">The validity test value</param>
/// <param name="argName">The name of the argument</param>
/// <returns>The argument value as a non-null value</returns>
/// <exception cref="ArgumentException">Thrown when the argument is not valid</exception>
public static void ArgumentValid(
string message,
bool test,
string? argName = null)
{
if (!test)
throw new ArgumentException(message, argName);
}

/// <summary>
/// Ensures that an argument is valid.
/// </summary>
/// <param name="messageFunc">The creator for an exception message to use when the argument is not valid</param>
/// <param name="test">The validity test value</param>
/// <param name="argName">The name of the argument</param>
/// <returns>The argument value as a non-null value</returns>
/// <exception cref="ArgumentException">Thrown when the argument is not valid</exception>
public static void ArgumentValid(
Func<string> messageFunc,
bool test,
string? argName = null)
{
if (!test)
throw new ArgumentException(messageFunc?.Invoke(), argName);
}
}
13 changes: 11 additions & 2 deletions src/xunit.analyzers.fixes/Utility/AsyncHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ public static class AsyncHelper
/// <summary>
/// Get a method's modifiers that include the async keyword.
/// </summary>
public static SyntaxTokenList GetModifiersWithAsyncKeywordAdded(MethodDeclarationSyntax method) =>
method.Modifiers.Any(SyntaxKind.AsyncKeyword)
public static SyntaxTokenList GetModifiersWithAsyncKeywordAdded(MethodDeclarationSyntax method)
{
Guard.ArgumentNotNull(method);

return method.Modifiers.Any(SyntaxKind.AsyncKeyword)
? method.Modifiers
: method.Modifiers.Add(Token(SyntaxKind.AsyncKeyword));
}

/// <summary>
/// Get the syntax type for an updated return type to support using async.
Expand All @@ -28,6 +32,11 @@ public static SyntaxTokenList GetModifiersWithAsyncKeywordAdded(MethodDeclaratio
DocumentEditor editor,
CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(method);
Guard.ArgumentNotNull(invocation);
Guard.ArgumentNotNull(document);
Guard.ArgumentNotNull(editor);

// Consider the case where a custom awaiter type is awaited
if (invocation.Parent.IsKind(SyntaxKind.AwaitExpression))
return method.ReturnType;
Expand Down
4 changes: 2 additions & 2 deletions src/xunit.analyzers.fixes/Utility/BatchedMemberFixProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Xunit.Analyzers.Fixes;

public abstract class BatchedMemberFixProvider : BatchedCodeFixProvider
{
public BatchedMemberFixProvider(params string[] diagnostics) :
protected BatchedMemberFixProvider(params string[] diagnostics) :
base(diagnostics)
{ }

Expand Down Expand Up @@ -41,7 +41,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
if (member.Locations.FirstOrDefault()?.IsInMetadata ?? true)
return;

await RegisterCodeFixesAsync(context, member);
await RegisterCodeFixesAsync(context, member).ConfigureAwait(false);
}

public abstract Task RegisterCodeFixesAsync(
Expand Down
42 changes: 37 additions & 5 deletions src/xunit.analyzers.fixes/Utility/CodeAnalysisExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@ public static async Task<Document> AddConstructor(
string typeName,
CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(document);
Guard.ArgumentNotNull(declaration);
Guard.ArgumentNotNull(typeDisplayName);
Guard.ArgumentNotNull(typeName);

#pragma warning disable CA1308 // These are display names, not normalizations for comparison

// TODO: Make this respect the user's preferences on identifier name style
var fieldName = "_" + typeName.Substring(0, 1).ToLower() + typeName.Substring(1, typeName.Length - 1);
var constructorArgName = typeName.Substring(0, 1).ToLower() + typeName.Substring(1, typeName.Length - 1);
var fieldName = "_" + typeName.Substring(0, 1).ToLowerInvariant() + typeName.Substring(1, typeName.Length - 1);
var constructorArgName = typeName.Substring(0, 1).ToLowerInvariant() + typeName.Substring(1, typeName.Length - 1);
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);

#pragma warning restore CA1308

var fieldDeclaration =
FieldDeclaration(
VariableDeclaration(ParseTypeName(typeDisplayName))
Expand Down Expand Up @@ -67,6 +76,9 @@ public static async Task<Document> ChangeAccessibility(
Accessibility accessibility,
CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(document);
Guard.ArgumentNotNull(declaration);

var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
editor.SetAccessibility(declaration, accessibility);
return editor.GetChangedDocument();
Expand All @@ -78,6 +90,9 @@ public static async Task<Solution> ChangeMemberAccessibility(
Accessibility accessibility,
CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(solution);
Guard.ArgumentNotNull(memberSymbol);

var editor = SymbolEditor.Create(solution);

await editor.EditAllDeclarationsAsync(
Expand All @@ -95,6 +110,9 @@ public static async Task<Solution> ChangeMemberStaticModifier(
bool isStatic,
CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(solution);
Guard.ArgumentNotNull(memberSymbol);

var editor = SymbolEditor.Create(solution);

await editor.EditAllDeclarationsAsync(
Expand Down Expand Up @@ -122,6 +140,10 @@ public static async Task<Solution> ChangeMemberType(
ITypeSymbol type,
CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(solution);
Guard.ArgumentNotNull(memberSymbol);
Guard.ArgumentNotNull(type);

var editor = SymbolEditor.Create(solution);

await editor.EditAllDeclarationsAsync(
Expand All @@ -138,6 +160,9 @@ public static async Task<Document> RemoveNode(
SyntaxNode node,
CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(document);
Guard.ArgumentNotNull(node);

var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
editor.RemoveNode(node);

Expand All @@ -149,6 +174,9 @@ public static async Task<Document> ExtractNodeFromParent(
SyntaxNode node,
CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(document);
Guard.ArgumentNotNull(node);

var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
var parent = node.Parent;

Expand All @@ -158,8 +186,8 @@ public static async Task<Document> ExtractNodeFromParent(

var formattedNode =
node
.WithLeadingTrivia(SyntaxFactory.ElasticMarker)
.WithTrailingTrivia(SyntaxFactory.ElasticMarker)
.WithLeadingTrivia(ElasticMarker)
.WithTrailingTrivia(ElasticMarker)
.WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);

editor.InsertAfter(parent, formattedNode);
Expand All @@ -174,9 +202,13 @@ public static async Task<Document> SetBaseClass(
string baseType,
CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(document);
Guard.ArgumentNotNull(declaration);
Guard.ArgumentNotNull(baseType);

var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
var generator = editor.Generator;
var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

if (semanticModel is not null)
{
Expand Down
12 changes: 6 additions & 6 deletions src/xunit.analyzers.fixes/Utility/ConvertAttributeCodeAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ public ConvertAttributeCodeAction(
string fromTypeName,
string toTypeName)
{
Title = title;
EquivalenceKey = equivalenceKey;
Title = Guard.ArgumentNotNull(title);
EquivalenceKey = Guard.ArgumentNotNull(equivalenceKey);

this.toTypeName = toTypeName;
this.fromTypeName = fromTypeName;
this.toTypeName = Guard.ArgumentNotNull(toTypeName);
this.fromTypeName = Guard.ArgumentNotNull(fromTypeName);
this.attributeLists = attributeLists;
this.document = document;
this.document = Guard.ArgumentNotNull(document);
}

public override string EquivalenceKey { get; }
Expand All @@ -50,7 +50,7 @@ protected override async Task<Document> GetChangedDocumentAsync(CancellationToke
{
cancellationToken.ThrowIfCancellationRequested();

var currentType = semanticModel.GetTypeInfo(attribute).Type;
var currentType = semanticModel.GetTypeInfo(attribute, cancellationToken).Type;
if (SymbolEqualityComparer.Default.Equals(currentType, fromTypeSymbol))
editor.SetName(attribute, toTypeName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public RemoveAttributesOfTypeCodeAction(
string attributeType,
bool exactMatch = false)
{
Title = title;
EquivalenceKey = equivalenceKey;
Title = Guard.ArgumentNotNull(title);
EquivalenceKey = Guard.ArgumentNotNull(equivalenceKey);

this.attributeLists = attributeLists;
this.attributeType = attributeType;
this.document = document;
this.attributeType = Guard.ArgumentNotNull(attributeType);
this.document = Guard.ArgumentNotNull(document);
this.exactMatch = exactMatch;
}

Expand All @@ -38,7 +38,7 @@ public RemoveAttributesOfTypeCodeAction(
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
{
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

if (semanticModel is not null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ public UseDifferentMethodCodeAction(
InvocationExpressionSyntax invocation,
string replacementMethod)
{
Title = title;
EquivalenceKey = equivalenceKey;
Title = Guard.ArgumentNotNull(title);
EquivalenceKey = Guard.ArgumentNotNull(equivalenceKey);

this.document = document;
this.invocation = invocation;
this.replacementMethod = replacementMethod;
this.document = Guard.ArgumentNotNull(document);
this.invocation = Guard.ArgumentNotNull(invocation);
this.replacementMethod = Guard.ArgumentNotNull(replacementMethod);
}

public override string EquivalenceKey { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
if (root is null)
return;

var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken);
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
if (semanticModel is null)
return;

Expand All @@ -45,7 +45,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
);
}

async Task<Solution> FixClass(
static async Task<Solution> FixClass(
Solution solution,
INamedTypeSymbol typeSymbol,
CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
);
}

async Task<Document> MarkAsTheoryAsync(
static async Task<Document> MarkAsTheoryAsync(
Document document,
MethodDeclarationSyntax methodDeclaration,
CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
);
}

async Task<Document> RemoveParameters(
static async Task<Document> RemoveParameters(
Document document,
ParameterListSyntax parameterListSyntax,
CancellationToken cancellationToken)
Expand Down
Loading

0 comments on commit cf8a4b7

Please sign in to comment.