diff --git a/src/Neo.SmartContract.Analyzer/AnalyzerReleases.Unshipped.md b/src/Neo.SmartContract.Analyzer/AnalyzerReleases.Unshipped.md index 185d107bd..98696f2c2 100644 --- a/src/Neo.SmartContract.Analyzer/AnalyzerReleases.Unshipped.md +++ b/src/Neo.SmartContract.Analyzer/AnalyzerReleases.Unshipped.md @@ -23,3 +23,4 @@ NC4020 | Naming | Warning | SmartContractMethodNamingAnalyzerUnderline NC4021 | Usage | Warning | SupportedStandardsAnalyzer NC4022 | Usage | Warning | BigIntegerUsingUsageAnalyzer NC4023 | Usage | Error | StaticFieldInitializationAnalyzer +NC4024 | Usage | Error | MultipleCatchBlockAnalyzer diff --git a/src/Neo.SmartContract.Analyzer/MultipleCatchBlockAnalyzer.cs b/src/Neo.SmartContract.Analyzer/MultipleCatchBlockAnalyzer.cs new file mode 100644 index 000000000..272137e8a --- /dev/null +++ b/src/Neo.SmartContract.Analyzer/MultipleCatchBlockAnalyzer.cs @@ -0,0 +1,48 @@ +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 MultipleCatchBlockAnalyzer : DiagnosticAnalyzer + { + public const string DiagnosticId = "NC4024"; + + private static readonly LocalizableString Title = "Multiple catch blocks are not allowed in Neo smart contracts"; + private static readonly LocalizableString MessageFormat = "Neo smart contracts only support a single catch block: {0}"; + private static readonly LocalizableString Description = "Neo smart contracts are limited to one catch block per try statement."; + private const string Category = "Usage"; + + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + DiagnosticId, + Title, + MessageFormat, + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: Description); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(AnalyzeTryStatement, SyntaxKind.TryStatement); + } + + private static void AnalyzeTryStatement(SyntaxNodeAnalysisContext context) + { + var tryStatement = (TryStatementSyntax)context.Node; + + if (tryStatement.Catches.Count > 1) + { + var diagnostic = Diagnostic.Create(Rule, tryStatement.GetLocation(), tryStatement.Catches.Count); + context.ReportDiagnostic(diagnostic); + } + } + } +} diff --git a/src/Neo.SmartContract.Analyzer/Readme.md b/src/Neo.SmartContract.Analyzer/Readme.md index 5639d3af3..beade172d 100644 --- a/src/Neo.SmartContract.Analyzer/Readme.md +++ b/src/Neo.SmartContract.Analyzer/Readme.md @@ -26,6 +26,7 @@ This repository contains a set of Roslyn analyzers and code fix providers for Ne - [SupportedStandardsAnalyzer.cs](NeoContractAnalyzer/SupportedStandardsAnalyzer.cs): This analyzer checks for correct implementation of supported standards in smart contracts. - [BigIntegerUsingUsageAnalyzer.cs](NeoContractAnalyzer/BigIntegerUsingUsageAnalyzer.cs): This analyzer warns about incorrect usage of BigInteger in using statements. - [StaticFieldInitializationAnalyzer.cs](NeoContractAnalyzer/StaticFieldInitializationAnalyzer.cs): This analyzer checks for proper initialization of static fields in smart contracts. +- [MultipleCatchBlockAnalyzer.cs](NeoContractAnalyzer/MultipleCatchBlockAnalyzer.cs): This analyzer checks for multiple catch blocks in try statements. ## How to Use @@ -41,4 +42,4 @@ Contributions to improve existing analyzers or add new ones are welcome. Please ## License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. \ No newline at end of file +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/tests/Neo.SmartContract.Analyzer.UnitTests/MultipleCatchBlockAnalyzerUnitTest.cs b/tests/Neo.SmartContract.Analyzer.UnitTests/MultipleCatchBlockAnalyzerUnitTest.cs new file mode 100644 index 000000000..e190e39fc --- /dev/null +++ b/tests/Neo.SmartContract.Analyzer.UnitTests/MultipleCatchBlockAnalyzerUnitTest.cs @@ -0,0 +1,43 @@ +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Verifier = Microsoft.CodeAnalysis.CSharp.Testing.XUnit.AnalyzerVerifier; + +namespace Neo.SmartContract.Analyzer.UnitTests +{ + [TestClass] + public class MultipleCatchBlockAnalyzerUnitTest + { + [TestMethod] + public async Task MultipleCatchBlockAnalyzer_DetectMultipleCatchBlocks() + { + const string sourceCode = """ + using System; + + public class TestClass + { + public void TestMethod() + { + try + { + // Some code that might throw an exception + } + catch (FormatException ex) + { + // Handle general exception + } + catch (Exception ex) + { + // Handle specific exception + } + } + } + """; + + var expected = Verifier.Diagnostic(MultipleCatchBlockAnalyzer.DiagnosticId) + .WithSpan(7, 9, 18, 10) + .WithArguments("2"); + + await Verifier.VerifyAnalyzerAsync(sourceCode, expected); + } + } +}