Skip to content

Commit

Permalink
New rules to validate method signature when using [UnsafeAccessor] (#657
Browse files Browse the repository at this point in the history
)
  • Loading branch information
meziantou authored Dec 1, 2023
1 parent 3802510 commit bc37dac
Show file tree
Hide file tree
Showing 13 changed files with 806 additions and 132 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ If you are already using other analyzers, you can check [which rules are duplica
|[MA0142](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0142.md)|Usage|Use pattern matching instead of equality operators|ℹ️||✔️|
|[MA0143](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0143.md)|Design|Primary constructor parameters should be readonly|⚠️|||
|[MA0144](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0144.md)|Performance|Use System.OperatingSystem to check the current OS|⚠️|✔️||
|[MA0145](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0145.md)|Usage|Signature for \[UnsafeAccessorAttribute\] method is not valid|⚠️|✔️||
|[MA0146](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0146.md)|Usage|Name must be set explicitly on local functions|⚠️|✔️||

<!-- rules -->

Expand Down
14 changes: 14 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@
|[MA0142](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0142.md)|Usage|Use pattern matching instead of equality operators|<span title='Info'>ℹ️</span>||✔️|
|[MA0143](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0143.md)|Design|Primary constructor parameters should be readonly|<span title='Warning'>⚠️</span>|||
|[MA0144](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0144.md)|Performance|Use System.OperatingSystem to check the current OS|<span title='Warning'>⚠️</span>|✔️||
|[MA0145](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0145.md)|Usage|Signature for \[UnsafeAccessorAttribute\] method is not valid|<span title='Warning'>⚠️</span>|✔️||
|[MA0146](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0146.md)|Usage|Name must be set explicitly on local functions|<span title='Warning'>⚠️</span>|✔️||

|Id|Suppressed rule|Justification|
|--|---------------|-------------|
Expand Down Expand Up @@ -582,6 +584,12 @@ dotnet_diagnostic.MA0143.severity = none
# MA0144: Use System.OperatingSystem to check the current OS
dotnet_diagnostic.MA0144.severity = warning
# MA0145: Signature for [UnsafeAccessorAttribute] method is not valid
dotnet_diagnostic.MA0145.severity = warning
# MA0146: Name must be set explicitly on local functions
dotnet_diagnostic.MA0146.severity = warning
```

# .editorconfig - all rules disabled
Expand Down Expand Up @@ -1015,4 +1023,10 @@ dotnet_diagnostic.MA0143.severity = none
# MA0144: Use System.OperatingSystem to check the current OS
dotnet_diagnostic.MA0144.severity = none
# MA0145: Signature for [UnsafeAccessorAttribute] method is not valid
dotnet_diagnostic.MA0145.severity = none
# MA0146: Name must be set explicitly on local functions
dotnet_diagnostic.MA0146.severity = none
```
13 changes: 13 additions & 0 deletions docs/Rules/MA0145.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# MA0145 - Signature for \[UnsafeAccessorAttribute\] method is not valid

Report some cases where a method decorated with `[UnsafeAccessorAttribute]` is not valid.

Note: Because some references doesn't expose their private members through Roslyn, it's not possible to validate the full signature.

````c#
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "dummy")]
extern static ref int Demo(MyStruct a); // Not compliant as the first parameter is not by ref
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "dummy")]
extern static ref int Demo(ref MyStruct a); // ok
````
19 changes: 19 additions & 0 deletions docs/Rules/MA0146.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# MA0146 - Name must be set explicitly on local functions

Local function names are mangle by the compiler, so the `Name` named constructor parameter is required

````c#
// non compliant
void Sample()
{
[UnsafeAccessor(UnsafeAccessorKind.Field)]
extern static ref int _Major_(System.Version a);
}

// Ok
void Sample()
{
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_Major")]
extern static ref int _Major_(System.Version a);
}
````
155 changes: 25 additions & 130 deletions src/Meziantou.Analyzer/Internals/ContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,177 +2,72 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Meziantou.Analyzer;

internal static class ContextExtensions
internal static partial class ContextExtensions
{
private static Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, Location location, ImmutableDictionary<string, string?>? properties, params string?[] messageArgs)
private static Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, Location location, ImmutableDictionary<string, string?>? properties, string?[]? messageArgs)
{
return Diagnostic.Create(descriptor, location, properties, messageArgs);
}

public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxToken syntaxToken, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, syntaxToken, messageArgs);
}

public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, SyntaxToken syntaxToken, params string?[] messageArgs)
{
context.ReportDiagnostic(CreateDiagnostic(descriptor, syntaxToken.GetLocation(), properties, messageArgs));
}
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, SyntaxReference syntaxReference, string?[]? messageArgs = null)
=> ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, syntaxReference, messageArgs);

public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxNode syntaxNode, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, syntaxNode, messageArgs);
}

public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, SyntaxNode syntaxNode, params string?[] messageArgs)
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, SyntaxReference syntaxReference, string?[]? messageArgs = null)
{
var syntaxNode = syntaxReference.GetSyntax(context.CancellationToken);
context.ReportDiagnostic(CreateDiagnostic(descriptor, syntaxNode.GetLocation(), properties, messageArgs));
}

public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, ISymbol symbol, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, symbol, messageArgs);
}
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, Location location, string?[]? messageArgs = null) => context.ReportDiagnostic(CreateDiagnostic(descriptor, location, ImmutableDictionary<string, string?>.Empty, messageArgs));
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, Location location, string?[]? messageArgs = null) => context.ReportDiagnostic(CreateDiagnostic(descriptor, location, properties, messageArgs));

public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, ISymbol symbol, params string?[] messageArgs)
{
foreach (var location in symbol.Locations)
{
context.ReportDiagnostic(CreateDiagnostic(descriptor, location, properties, messageArgs));
}
}
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, SyntaxNode syntax, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, syntax.GetLocation(), messageArgs);
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, SyntaxNode syntax, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, properties, syntax.GetLocation(), messageArgs);

public static void ReportDiagnostic(this SymbolAnalysisContext context, DiagnosticDescriptor descriptor, ISymbol symbol, params string?[] messageArgs)
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, SyntaxToken token, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, token.GetLocation(), messageArgs);
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, SyntaxToken syntaxToken, params string?[]? messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, symbol, messageArgs);
context.ReportDiagnostic(CreateDiagnostic(descriptor, syntaxToken.GetLocation(), properties, messageArgs));
}

public static void ReportDiagnostic(this SymbolAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, ISymbol symbol, params string?[] messageArgs)
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ISymbol symbol, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, symbol, messageArgs);
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, ISymbol symbol, string?[]? messageArgs = null)
{
foreach (var location in symbol.Locations)
{
ReportDiagnostic(context, descriptor, properties, location, messageArgs);
}
}

public static void ReportDiagnostic(this SymbolAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxReference syntaxReference, params string?[] messageArgs)
{
var syntaxNode = syntaxReference.GetSyntax(context.CancellationToken);
context.ReportDiagnostic(Diagnostic.Create(descriptor, syntaxNode.GetLocation(), ImmutableDictionary<string, string?>.Empty, messageArgs));
}

public static void ReportDiagnostic(this SymbolAnalysisContext context, DiagnosticDescriptor descriptor, Location location, params string?[] messageArgs)
{
context.ReportDiagnostic(Diagnostic.Create(descriptor, location, ImmutableDictionary<string, string?>.Empty, messageArgs));
}

public static void ReportDiagnostic(this SymbolAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, Location location, params string?[] messageArgs)
{
context.ReportDiagnostic(CreateDiagnostic(descriptor, location, properties, messageArgs));
}

public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxToken token, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, token, messageArgs);
}

public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, SyntaxToken token, params string?[] messageArgs)
{
context.ReportDiagnostic(CreateDiagnostic(descriptor, token.GetLocation(), properties, messageArgs));
}

public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxNode node, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, node, messageArgs);
}

public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, SyntaxNode node, params string?[] messageArgs)
{
context.ReportDiagnostic(CreateDiagnostic(descriptor, node.GetLocation(), properties, messageArgs));
}

public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, IOperation operation, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, operation, messageArgs);
}

public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, IOperation operation, params string?[] messageArgs)
{
context.ReportDiagnostic(CreateDiagnostic(descriptor, operation.Syntax.GetLocation(), properties, messageArgs));
}
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, IOperation operation, string?[]? messageArgs = null)
=> ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, operation, messageArgs);
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, IOperation operation, string?[]? messageArgs = null)
=> context.ReportDiagnostic(CreateDiagnostic(descriptor, operation.Syntax.GetLocation(), properties, messageArgs));

public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, IInvocationOperation operation, DiagnosticReportOptions options, params string?[] messageArgs)
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, ILocalFunctionOperation operation, DiagnosticReportOptions options, string?[]? messageArgs = null)
{
if (options.HasFlag(DiagnosticReportOptions.ReportOnMethodName) &&
operation.Syntax.ChildNodes().FirstOrDefault() is MemberAccessExpressionSyntax memberAccessExpression)
operation.Syntax is LocalFunctionStatementSyntax memberAccessExpression)
{
context.ReportDiagnostic(Diagnostic.Create(descriptor, memberAccessExpression.Name.GetLocation(), properties, messageArgs));
context.ReportDiagnostic(Diagnostic.Create(descriptor, memberAccessExpression.Identifier.GetLocation(), properties, messageArgs));
return;
}

context.ReportDiagnostic(descriptor, properties, operation, messageArgs);
}

public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, ILocalFunctionOperation operation, DiagnosticReportOptions options, params string?[] messageArgs)
public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, IInvocationOperation operation, DiagnosticReportOptions options, params string?[]? messageArgs)
{
if (options.HasFlag(DiagnosticReportOptions.ReportOnMethodName) &&
operation.Syntax is LocalFunctionStatementSyntax memberAccessExpression)
operation.Syntax.ChildNodes().FirstOrDefault() is MemberAccessExpressionSyntax memberAccessExpression)
{
context.ReportDiagnostic(Diagnostic.Create(descriptor, memberAccessExpression.Identifier.GetLocation(), properties, messageArgs));
context.ReportDiagnostic(Diagnostic.Create(descriptor, memberAccessExpression.Name.GetLocation(), properties, messageArgs));
return;
}

context.ReportDiagnostic(descriptor, properties, operation, messageArgs);
}

public static void ReportDiagnostic(this CompilationAnalysisContext context, DiagnosticDescriptor descriptor, ISymbol symbol, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, symbol, messageArgs);
}

public static void ReportDiagnostic(this CompilationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, ISymbol symbol, params string?[] messageArgs)
{
foreach (var location in symbol.Locations)
{
context.ReportDiagnostic(CreateDiagnostic(descriptor, location, properties, messageArgs));
}
}

public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ISymbol symbol, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, symbol, messageArgs);
}

public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, ISymbol symbol, params string?[] messageArgs)
{
foreach (var location in symbol.Locations)
{
ReportDiagnostic(context, descriptor, properties, location, messageArgs);
}
}

public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxNode syntax, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, syntax.GetLocation(), messageArgs);
}

public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxToken token, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, ImmutableDictionary<string, string?>.Empty, token.GetLocation(), messageArgs);
}

public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, SyntaxNode syntax, params string?[] messageArgs)
{
ReportDiagnostic(context, descriptor, properties, syntax.GetLocation(), messageArgs);
}

public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary<string, string?>? properties, Location location, params string?[] messageArgs)
{
context.ReportDiagnostic(CreateDiagnostic(descriptor, location, properties, messageArgs));
}
}
Loading

0 comments on commit bc37dac

Please sign in to comment.