Skip to content

Commit 29bd87c

Browse files
Add a basic fixer to suggest replacing intrinsics with their cross-platform counterpart (#7721)
* Add a basic fixer to suggest replacing intrinsics with their cross-platform counterpart * Remove an unnecessary using * Ensure we use a new analyzer code, not an existing one * Make csharp_indent_case_contents_when_block silent until the rest of the repo can be updated * Don't set csharp_indent_case_contents_when_block in this PR * Respond to PR feedback * Fix nullability of the func parameter * Fix nullability warning on arg0 parameter * Prefer properties over custom tags * Fix nullability * Fix formatting * Run msbuild /t:pack again * Check for the existence of Vector64/128/256/512 * Split the tests apart into multiple files * Mark test class as partial * Address various PR feedback * Fix formatting * Additional cleanup and handle more PR feedback * Ensure the additional tests compile * Ensure the generator is passed through * Ensure the SyntaxGeneratorExtensions are part of the shared project * Missing semicolon * Formatting * Fix tests
1 parent dfeef6d commit 29bd87c

File tree

41 files changed

+5355
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+5355
-1
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
2+
3+
using Microsoft.CodeAnalysis;
4+
using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
using Microsoft.CodeAnalysis.Diagnostics;
6+
using Microsoft.CodeAnalysis.Operations;
7+
using Microsoft.CodeQuality.Analyzers.Maintainability;
8+
9+
namespace Microsoft.CodeQuality.CSharp.Analyzers.Maintainability
10+
{
11+
/// <summary>
12+
/// CA1516: Use cross-platform intrinsics
13+
/// </summary>
14+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
15+
public sealed class CSharpUseCrossPlatformIntrinsicsAnalyzer : UseCrossPlatformIntrinsicsAnalyzer
16+
{
17+
protected override bool IsSupported(IInvocationOperation invocation, RuleKind ruleKind)
18+
{
19+
if (invocation.Syntax is not InvocationExpressionSyntax)
20+
{
21+
return false;
22+
}
23+
24+
return base.IsSupported(invocation, ruleKind);
25+
}
26+
}
27+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
2+
3+
using System;
4+
using System.Composition;
5+
using System.Diagnostics;
6+
using Analyzer.Utilities.Extensions;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CodeFixes;
9+
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
using Microsoft.CodeAnalysis.Editing;
11+
using Microsoft.CodeQuality.Analyzers.Maintainability;
12+
using static Microsoft.CodeQuality.Analyzers.Maintainability.UseCrossPlatformIntrinsicsAnalyzer;
13+
14+
namespace Microsoft.CodeQuality.CSharp.Analyzers.Maintainability
15+
{
16+
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
17+
public sealed class CSharpUseCrossPlatformIntrinsicsFixer : UseCrossPlatformIntrinsicsFixer
18+
{
19+
protected override SyntaxNode ReplaceNode(SyntaxNode currentNode, SyntaxGenerator generator, RuleKind ruleKind)
20+
{
21+
return ruleKind switch
22+
{
23+
RuleKind.op_ExclusiveOr => ReplaceWithBinaryOperator(currentNode, generator, isCommutative: true, generator.ExclusiveOrExpression),
24+
RuleKind.op_LeftShift => ReplaceWithBinaryOperator(currentNode, generator, isCommutative: false, generator.LeftShiftExpression),
25+
RuleKind.op_RightShift => ReplaceWithBinaryOperator(currentNode, generator, isCommutative: false, generator.RightShiftExpression),
26+
RuleKind.op_UnsignedRightShift => ReplaceWithBinaryOperator(currentNode, generator, isCommutative: false, generator.UnsignedRightShiftExpression),
27+
_ => base.ReplaceNode(currentNode, generator, ruleKind),
28+
};
29+
}
30+
31+
protected override SyntaxNode ReplaceWithBinaryOperator(SyntaxNode currentNode, SyntaxGenerator generator, bool isCommutative, Func<SyntaxNode, SyntaxNode, SyntaxNode?> binaryOpFunc)
32+
{
33+
if (currentNode is not InvocationExpressionSyntax invocationExpression)
34+
{
35+
Debug.Fail($"Found unexpected node kind: {currentNode.RawKind}");
36+
return currentNode;
37+
}
38+
39+
SeparatedSyntaxList<ArgumentSyntax> arguments = invocationExpression.ArgumentList.Arguments;
40+
41+
if (arguments.Count != 2)
42+
{
43+
Debug.Fail($"Found unexpected number of arguments for binary operator replacement: {arguments.Count}");
44+
return currentNode;
45+
}
46+
47+
if (binaryOpFunc(arguments[0].Expression, arguments[1].Expression) is not ExpressionSyntax replacementExpression)
48+
{
49+
return currentNode;
50+
}
51+
52+
return generator.Parenthesize(replacementExpression);
53+
}
54+
55+
protected override SyntaxNode ReplaceWithUnaryOperator(SyntaxNode currentNode, SyntaxGenerator generator, Func<SyntaxNode, SyntaxNode?> unaryOpFunc)
56+
{
57+
if (currentNode is not InvocationExpressionSyntax invocationExpression)
58+
{
59+
Debug.Fail($"Found unexpected node kind: {currentNode.RawKind}");
60+
return currentNode;
61+
}
62+
63+
SeparatedSyntaxList<ArgumentSyntax> arguments = invocationExpression.ArgumentList.Arguments;
64+
65+
if (arguments.Count != 1)
66+
{
67+
Debug.Fail($"Found unexpected number of arguments for unary operator replacement: {arguments.Count}");
68+
return currentNode;
69+
}
70+
71+
if (unaryOpFunc(arguments[0].Expression) is not ExpressionSyntax replacementExpression)
72+
{
73+
return currentNode;
74+
}
75+
76+
return generator.Parenthesize(replacementExpression);
77+
}
78+
}
79+
}

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,18 @@ Unlike a class library, an application's API isn't typically referenced publicly
936936
|CodeFix|True|
937937
---
938938

939+
## [CA1516](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1516): Use cross-platform intrinsics
940+
941+
This rule detects usage of platform-specific intrinsics that can be replaced with an equivalent cross-platform intrinsic instead.
942+
943+
|Item|Value|
944+
|-|-|
945+
|Category|Maintainability|
946+
|Enabled|True|
947+
|Severity|Info|
948+
|CodeFix|True|
949+
---
950+
939951
## [CA1700](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1700): Do not name enum values 'Reserved'
940952

941953
This rule assumes that an enumeration member that has a name that contains "reserved" is not currently used but is a placeholder to be renamed or removed in a future version. Renaming or removing a member is a breaking change.

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers.sarif

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,25 @@
145145
]
146146
}
147147
},
148+
"CA1516": {
149+
"id": "CA1516",
150+
"shortDescription": "Use cross-platform intrinsics",
151+
"fullDescription": "This rule detects usage of platform-specific intrinsics that can be replaced with an equivalent cross-platform intrinsic instead.",
152+
"defaultLevel": "note",
153+
"helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1516",
154+
"properties": {
155+
"category": "Maintainability",
156+
"isEnabledByDefault": true,
157+
"typeName": "CSharpUseCrossPlatformIntrinsicsAnalyzer",
158+
"languages": [
159+
"C#"
160+
],
161+
"tags": [
162+
"Telemetry",
163+
"EnabledRuleInAggressiveMode"
164+
]
165+
}
166+
},
148167
"CA1802": {
149168
"id": "CA1802",
150169
"shortDescription": "Use literals where appropriate",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
2+
3+
namespace Microsoft.CodeQuality.Analyzers.Maintainability
4+
{
5+
public partial class UseCrossPlatformIntrinsicsAnalyzer
6+
{
7+
public enum RuleKind
8+
{
9+
// These names match the underlying IL names for the cross-platform API that will be used in the fixer.
10+
11+
op_Addition,
12+
op_BitwiseAnd,
13+
op_BitwiseOr,
14+
op_Division,
15+
op_ExclusiveOr,
16+
op_LeftShift,
17+
op_Multiply,
18+
op_OnesComplement,
19+
op_RightShift,
20+
op_Subtraction,
21+
op_UnaryNegation,
22+
op_UnsignedRightShift,
23+
24+
Count,
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)