Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add: add neo smart contract syntax analyzer #839

Merged
merged 55 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
dded435
add neo smart contract syntax analyzer
Jim8y Dec 17, 2023
036bd20
update ID
Jim8y Dec 17, 2023
fd7fcd3
formar
Jim8y Dec 17, 2023
b96edee
fix errors of id
Jim8y Dec 17, 2023
bba53a7
fix unused
Jim8y Dec 17, 2023
74046b3
update readme
Jim8y Dec 17, 2023
1ef59e3
fix path, namespace, and float type check
Jim8y Dec 17, 2023
ec416e6
update diable warning
Jim8y Dec 17, 2023
cf339e2
diable warning for code fix
Jim8y Dec 17, 2023
a16aa8c
remove nowarn as it also diables the error highlight
Jim8y Dec 17, 2023
f50aa26
don't run on github CI
Jim8y Dec 17, 2023
5b57b34
do not compile analyzer
Jim8y Dec 17, 2023
a1adea0
fix build
Jim8y Dec 17, 2023
d368e47
add more analyzer and also add code fix for multiple analyzers
Jim8y Dec 17, 2023
cdd1ab4
add out keyword code fix
Jim8y Dec 18, 2023
86de9c5
fix float double and out keyword
Jim8y Dec 18, 2023
e98c4d6
verify name conflict, showing confilct
Jim8y Dec 19, 2023
5b20ed9
check notify name
Jim8y Dec 23, 2023
4097846
Update src/Neo.SmartContract.Analyzer/CollectionTypesUsageAnalyzer.cs
Jim8y Dec 23, 2023
88e0d41
Update src/Neo.SmartContract.Analyzer/CharMethodsUsageAnalyzer.cs
Jim8y Dec 23, 2023
4f85298
Merge branch 'master' into syntax-analyzer
Jim8y Dec 23, 2023
b876ff4
Merge branch 'master' into syntax-analyzer
shargon Dec 27, 2023
f67f858
Optimization and clean code
shargon Dec 28, 2023
a643983
remove sample from solution build
Jim8y Dec 28, 2023
5edc142
also remove from release
Jim8y Dec 28, 2023
31778b1
Merge branch 'master' into syntax-analyzer
shargon Jan 4, 2024
1078b43
add analyzer to `_deploy` and `_initial`
Jim8y Jan 12, 2024
ccfa750
Merge branch 'master' into syntax-analyzer
Jim8y Jan 12, 2024
66c4e41
fix double detection
Jim8y Jan 15, 2024
d6e95d2
also ban decimal usage.
Jim8y Jan 15, 2024
cfea8b8
Merge branch 'master' into syntax-analyzer
Jim8y Jan 17, 2024
b407482
Merge branch 'master' into syntax-analyzer
Jim8y Feb 4, 2024
4e84843
add NEPStandard analyzer
Jim8y Feb 6, 2024
2fdecd7
Merge branch 'master' into syntax-analyzer
Jim8y Feb 7, 2024
1c0155e
use framework NEP
Jim8y Feb 7, 2024
f7f95a8
Merge branch 'master' into syntax-analyzer
shargon Feb 12, 2024
b24f41c
Fix errors in Examples.cs
shargon Feb 12, 2024
6ff37cb
Merge branch 'master' into syntax-analyzer
Jim8y Feb 26, 2024
818a247
Merge branch 'master' into syntax-analyzer
Jim8y Feb 26, 2024
6e2102d
Merge branch 'master' into syntax-analyzer
Jim8y Mar 2, 2024
b32b4b2
Merge branch 'master' into syntax-analyzer
shargon Mar 2, 2024
c56a5b5
Merge branch 'master' into syntax-analyzer
Jim8y Mar 3, 2024
d0a24c1
Merge branch 'master' into syntax-analyzer
Jim8y Mar 12, 2024
cfb0773
fix analyzer and add unit tests
Jim8y Mar 12, 2024
ef23ae8
delete examples as we have UT now
Jim8y Mar 12, 2024
2af2be3
fix standards and add ut
Jim8y Mar 13, 2024
966b31a
fix analyzer and add UT
Jim8y Mar 13, 2024
1201200
remove keywords fix and update UT
Jim8y Mar 13, 2024
b99083f
biginteger using analyzer and tests
Jim8y Mar 13, 2024
c29a360
initialize analyzer and unit test
Jim8y Mar 13, 2024
20e4db8
initialize analyzer and unit test
Jim8y Mar 13, 2024
6e2c226
Merge branch 'master' into syntax-analyzer
Jim8y Mar 19, 2024
cca61d8
Merge branch 'master' into syntax-analyzer
shargon Mar 21, 2024
4f49f54
use analyzer in examples
Jim8y Mar 22, 2024
01a16d6
Merge branch 'master' into syntax-analyzer
Jim8y Mar 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions neo-devpack-dotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{79389FC0-C62
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D5266066-0AFD-44D5-A83E-2F73668A63C8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.SmartContract.Analyzer", "src\Neo.SmartContract.Analyzer\Neo.SmartContract.Analyzer.csproj", "{F5862E0E-C8ED-4B99-B353-E1F35D86D4EF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Compiler.CSharp.TestContracts", "tests\Neo.Compiler.CSharp.TestContracts\Neo.Compiler.CSharp.TestContracts.csproj", "{8D67DD5A-D683-481F-915E-98683EA38791}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.SmartContract.Framework.TestContracts", "tests\Neo.SmartContract.Framework.TestContracts\Neo.SmartContract.Framework.TestContracts.csproj", "{A372F1D6-51FF-472C-9508-FDAF7E6FEB13}"
Expand Down Expand Up @@ -68,6 +70,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example.SmartContract.Trans
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example.SmartContract.ZKP", "examples\Example.SmartContract.ZKP\Example.SmartContract.ZKP.csproj", "{141AD5BA-1735-4583-93B6-145CF72721E5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.SmartContract.Analyzer.UnitTests", "tests\Neo.SmartContract.Analyzer.UnitTests\Neo.SmartContract.Analyzer.UnitTests.csproj", "{F30E2375-012A-4A38-985B-31CB7DBA4D28}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -94,6 +98,10 @@ Global
{93BEC5CC-BAFF-4389-89E7-84AAFF5D495D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{93BEC5CC-BAFF-4389-89E7-84AAFF5D495D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{93BEC5CC-BAFF-4389-89E7-84AAFF5D495D}.Release|Any CPU.Build.0 = Release|Any CPU
{F5862E0E-C8ED-4B99-B353-E1F35D86D4EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5862E0E-C8ED-4B99-B353-E1F35D86D4EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5862E0E-C8ED-4B99-B353-E1F35D86D4EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5862E0E-C8ED-4B99-B353-E1F35D86D4EF}.Release|Any CPU.Build.0 = Release|Any CPU
{8D67DD5A-D683-481F-915E-98683EA38791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D67DD5A-D683-481F-915E-98683EA38791}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D67DD5A-D683-481F-915E-98683EA38791}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -190,6 +198,10 @@ Global
{141AD5BA-1735-4583-93B6-145CF72721E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{141AD5BA-1735-4583-93B6-145CF72721E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{141AD5BA-1735-4583-93B6-145CF72721E5}.Release|Any CPU.Build.0 = Release|Any CPU
{F30E2375-012A-4A38-985B-31CB7DBA4D28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F30E2375-012A-4A38-985B-31CB7DBA4D28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F30E2375-012A-4A38-985B-31CB7DBA4D28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F30E2375-012A-4A38-985B-31CB7DBA4D28}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -200,13 +212,10 @@ Global
{FC0CAF5A-30A7-4857-B1A4-486FAAA39E5A} = {79389FC0-C621-4CEA-AD2B-6074C32E7BCA}
{4C6B120B-99B5-4888-B8D5-45031458DD07} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
{93BEC5CC-BAFF-4389-89E7-84AAFF5D495D} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
{F5862E0E-C8ED-4B99-B353-E1F35D86D4EF} = {79389FC0-C621-4CEA-AD2B-6074C32E7BCA}
{8D67DD5A-D683-481F-915E-98683EA38791} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
{A372F1D6-51FF-472C-9508-FDAF7E6FEB13} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
{D0153204-6AEF-4D94-B0E1-8124C38C91D4} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
{73223FBD-C562-4FA0-9722-C7F1C382A9DE} = {49D5873D-7B38-48A5-B853-85146F032091}
{D541BCE9-65BC-475B-94E5-19B6BFFF2B8E} = {49D5873D-7B38-48A5-B853-85146F032091}
{35A34EBD-F2BF-4D83-A096-D5F007B12732} = {49D5873D-7B38-48A5-B853-85146F032091}
{D6D53889-5A10-46A4-BA66-E78B56EC1881} = {49D5873D-7B38-48A5-B853-85146F032091}
{648DCE6F-A0BA-4032-951B-20CF5BBFD998} = {79389FC0-C621-4CEA-AD2B-6074C32E7BCA}
{B772B8A9-9362-4C6F-A6D3-2A4138439B2C} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
{17F45E0B-AB1C-4796-8C99-E5212A5592F8} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
Expand All @@ -224,6 +233,11 @@ Global
{18A5C205-2102-4F4B-BB76-37660F74709C} = {C10F9957-077F-42C8-B350-8607D4899B7E}
{5319BE3E-A5E9-4AFF-94D6-A669DA214F24} = {C10F9957-077F-42C8-B350-8607D4899B7E}
{141AD5BA-1735-4583-93B6-145CF72721E5} = {C10F9957-077F-42C8-B350-8607D4899B7E}
{F30E2375-012A-4A38-985B-31CB7DBA4D28} = {D5266066-0AFD-44D5-A83E-2F73668A63C8}
{35A34EBD-F2BF-4D83-A096-D5F007B12732} = {49D5873D-7B38-48A5-B853-85146F032091}
{73223FBD-C562-4FA0-9722-C7F1C382A9DE} = {49D5873D-7B38-48A5-B853-85146F032091}
{D6D53889-5A10-46A4-BA66-E78B56EC1881} = {49D5873D-7B38-48A5-B853-85146F032091}
{D541BCE9-65BC-475B-94E5-19B6BFFF2B8E} = {49D5873D-7B38-48A5-B853-85146F032091}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6DA935E1-C674-4364-B087-F1B511B79215}
Expand Down
6 changes: 6 additions & 0 deletions src/Neo.SmartContract.Analyzer/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## Release 1.0
shargon marked this conversation as resolved.
Show resolved Hide resolved

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|------------------------------------------------
26 changes: 26 additions & 0 deletions src/Neo.SmartContract.Analyzer/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
NC4002 | Type | Error | FloatUsageAnalyzer
NC4003 | Type | Error | DecimalUsageAnalyzer
NC4004 | Type | Error | DoubleUsageAnalyzer
NC4005 | Method | Error | SystemMathUsageAnalyzer
NC4006 | Method | Error | BigIntegerUsageAnalyzer
NC4007 | Method | Error | StringMethodUsageAnalyzer
NC4008 | Usage | Error | BigIntegerCreationAnalyzer
NC4009 | Usage | Error | OutKeywordUsageAnalyzer
NC4010 | Usage | Warning | RefKeywordUsageAnalyzer
NC4011 | Usage | Error | LinqUsageAnalyzer
NC4012 | Method | Error | CharMethodsUsageAnalyzer
NC4013 | Type | Error | CollectionTypesUsageAnalyzer
NC4014 | Usage | Warning | VolatileKeywordUsageAnalyzer
NC4015 | Usage | Error | KeywordUsageAnalyzer
NC4016 | Usage | Error | AnonymousFunctionUsageAnalyzer
NC4017 | Usage | Error | BanCastMethodAnalyzer
NC4018 | Naming | Error | SmartContractMethodNamingAnalyzer
NC4019 | Usage | Error | NotifyEventNameAnalyzer
NC4020 | Naming | Warning | SmartContractMethodNamingAnalyzerUnderline
NC4021 | Usage | Warning | SupportedStandardsAnalyzer
NC4022 | Usage | Warning | BigIntegerUsingUsageAnalyzer
NC4023 | Usage | Error | StaticFieldInitializationAnalyzer
113 changes: 113 additions & 0 deletions src/Neo.SmartContract.Analyzer/AnonymousFunctionUsageAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Neo.SmartContract.Analyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AnonymousFunctionUsageAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "NC4016";
private static readonly string Title = "Anonymous Function usage";
private static readonly string MessageFormat = "Use of anonymous function is not allowed";
private static readonly string Description = "Anonymous functions are restricted in this project.";
private const string Category = "Usage";

private static readonly DiagnosticDescriptor Rule = new(
DiagnosticId,
Title,
MessageFormat,
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: Description);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.AnonymousMethodExpression, SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression);
}

private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
if (context.Node is not AnonymousMethodExpressionSyntax && context.Node is not LambdaExpressionSyntax) return;
var diagnostic = Diagnostic.Create(Rule, context.Node.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AnonymousFunctionToPrivateMethodCodeFixProvider))]
public class AnonymousFunctionToPrivateMethodCodeFixProvider : CodeFixProvider
{
// Generate a unique method name
private readonly string methodName = "GeneratedMethod";

public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AnonymousFunctionUsageAnalyzer.DiagnosticId);

public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;

var anonymousFunction = root?.FindToken(diagnosticSpan.Start).Parent?.AncestorsAndSelf().OfType<AnonymousFunctionExpressionSyntax>().First();
if (anonymousFunction is null) return;

context.RegisterCodeFix(
CodeAction.Create(
title: "Convert to private method",
createChangedDocument: c => ConvertToPrivateMethodAsync(context.Document, anonymousFunction, c),
equivalenceKey: "Convert to private method"),
diagnostic);
}

private async Task<Document> ConvertToPrivateMethodAsync(Document document, AnonymousFunctionExpressionSyntax anonymousFunction, CancellationToken cancellationToken)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken);

// Determine the return type and parameters of the lambda expression
var symbolInfo = semanticModel.GetSymbolInfo(anonymousFunction, cancellationToken);
if (symbolInfo.Symbol is not IMethodSymbol delegateType) return document;

var returnType = delegateType.ReturnType;
var parameters = delegateType.Parameters.Select(p =>
SyntaxFactory.Parameter(SyntaxFactory.Identifier(p.Name)).WithType(SyntaxFactory.ParseTypeName(p.Type.ToDisplayString())));

// Create a new method declaration
var newMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(returnType.ToDisplayString()), methodName)
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)))
.WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters)))
.WithBody(anonymousFunction.Body as BlockSyntax ?? SyntaxFactory.Block());

// Insert the new method at the end of the class
var classDeclaration = anonymousFunction.FirstAncestorOrSelf<ClassDeclarationSyntax>();
if (classDeclaration is null) return document;

var editor = new SyntaxEditor(await document.GetSyntaxRootAsync(cancellationToken), document.Project.Solution.Workspace);
editor.InsertAfter(classDeclaration.Members.Last(), newMethod);

// Replace the anonymous function with a call to the new method
var invocationExpression = SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(methodName))
.WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(
delegateType.Parameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name))))));
editor.ReplaceNode(anonymousFunction, invocationExpression);

var newRoot = editor.GetChangedRoot();
return document.WithSyntaxRoot(newRoot);
}
}
}
47 changes: 47 additions & 0 deletions src/Neo.SmartContract.Analyzer/BanCastMethodAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;

namespace Neo.SmartContract.Analyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class BanCastMethodAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "NC4017";
private const string Title = "Do not use .Cast<T>() method";
private const string MessageFormat = "Use of .Cast<T>() method is not allowed";
private const string Description = "Replace .Cast<T>() with direct type casting.";
private const string Category = "Usage";

private static readonly DiagnosticDescriptor Rule = new(
DiagnosticId,
Title,
MessageFormat,
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: Description);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression);
}

private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var invocationExpr = (InvocationExpressionSyntax)context.Node;
if (invocationExpr.Expression is MemberAccessExpressionSyntax memberAccessExpr &&
memberAccessExpr.Name.Identifier.ValueText == "Cast" &&
memberAccessExpr.Expression != null)
{
context.ReportDiagnostic(Diagnostic.Create(Rule, memberAccessExpr.Name.GetLocation()));
}
}
}
}
95 changes: 95 additions & 0 deletions src/Neo.SmartContract.Analyzer/BigIntegerCreationAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Neo.SmartContract.Analyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class BigIntegerCreationAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "NC4008";

private static readonly DiagnosticDescriptor Rule = new(
DiagnosticId,
"Use of BigInteger constructor",
"Use of new BigInteger(int) is not allowed, please use BigInteger x = 0;",
"Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Rule);

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeSyntaxNode, SyntaxKind.ObjectCreationExpression);
}

private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context)
{
var objectCreationExpression = (ObjectCreationExpressionSyntax)context.Node;

// Check if it is a BigInteger creation
if (context.SemanticModel.GetTypeInfo(objectCreationExpression).Type?.ToString() == "System.Numerics.BigInteger" &&
objectCreationExpression.ArgumentList?.Arguments.Count == 1 &&
context.SemanticModel.GetTypeInfo(objectCreationExpression.ArgumentList.Arguments[0].Expression).Type?.SpecialType == SpecialType.System_Int32)
{
var diagnostic = Diagnostic.Create(Rule, objectCreationExpression.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(BigIntegerCreationCodeFixProvider)), Shared]
public class BigIntegerCreationCodeFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(BigIntegerCreationAnalyzer.DiagnosticId);

public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;

var declaration = root?.FindToken(diagnosticSpan.Start).Parent?.AncestorsAndSelf().OfType<ObjectCreationExpressionSyntax>().First();
if (declaration is null) return;

context.RegisterCodeFix(
CodeAction.Create(
title: "Replace with direct assignment",
createChangedDocument: c => ReplaceWithDirectAssignment(context.Document, declaration, c),
equivalenceKey: "Replace with direct assignment"),
diagnostic);
}

private static async Task<Document> ReplaceWithDirectAssignment(Document document, ObjectCreationExpressionSyntax objectCreation, CancellationToken cancellationToken)
{
var argument = objectCreation.ArgumentList?.Arguments.First().Expression;
if (argument is null) return document;

var newExpression = SyntaxFactory.ParseExpression(argument.ToString())
.WithLeadingTrivia(objectCreation.GetLeadingTrivia())
.WithTrailingTrivia(objectCreation.GetTrailingTrivia());

var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
editor.ReplaceNode(objectCreation, newExpression);

var newRoot = editor.GetChangedRoot();
return document.WithSyntaxRoot(newRoot);
}
}
}
Loading
Loading