diff --git a/ChangeLog.md b/ChangeLog.md index 943f53254a..f787f4218c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix analyzer [RCS1264](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1264) ([PR](https://github.com/dotnet/roslynator/pull/1666)) +- Fix analyzer [RCS1229](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1229) ([PR](https://github.com/dotnet/roslynator/pull/1667)) ## [4.13.1] - 2025-02-23 diff --git a/src/Analyzers/CSharp/Analysis/UnnecessaryUnsafeContextAnalyzer.cs b/src/Analyzers/CSharp/Analysis/UnnecessaryUnsafeContextAnalyzer.cs index 7a7a178aad..2677141b23 100644 --- a/src/Analyzers/CSharp/Analysis/UnnecessaryUnsafeContextAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/UnnecessaryUnsafeContextAnalyzer.cs @@ -59,7 +59,7 @@ private static void AnalyzeUnsafeStatement(SyntaxNodeAnalysisContext context) if (!unsafeStatement.Block.Statements.Any()) return; - if (!AncestorContainsUnsafeModifier(unsafeStatement.Parent)) + if (!CSharpUtility.IsInUnsafeContext(unsafeStatement.Parent)) return; DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UnnecessaryUnsafeContext, unsafeStatement.UnsafeKeyword); @@ -166,39 +166,9 @@ private static void AnalyzeMemberDeclaration( if (index == -1) return; - if (!AncestorContainsUnsafeModifier(node.Parent)) + if (!CSharpUtility.IsInUnsafeContext(node.Parent)) return; DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UnnecessaryUnsafeContext, modifiers[index]); } - - private static bool AncestorContainsUnsafeModifier(SyntaxNode node) - { - while (node is not null) - { - switch (node) - { - case UnsafeStatementSyntax: - return true; - case MemberDeclarationSyntax memberDeclarationSyntax: - { - if (memberDeclarationSyntax.Modifiers.Contains(SyntaxKind.UnsafeKeyword)) - return true; - - break; - } - case LocalFunctionStatementSyntax localFunctionStatement: - { - if (localFunctionStatement.Modifiers.Contains(SyntaxKind.UnsafeKeyword)) - return true; - - break; - } - } - - node = node.Parent; - } - - return false; - } } diff --git a/src/Analyzers/CSharp/Analysis/UseAsyncAwaitAnalyzer.cs b/src/Analyzers/CSharp/Analysis/UseAsyncAwaitAnalyzer.cs index 2edaac64d6..14cf301009 100644 --- a/src/Analyzers/CSharp/Analysis/UseAsyncAwaitAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/UseAsyncAwaitAnalyzer.cs @@ -162,7 +162,8 @@ private static bool IsFixable(BlockSyntax body, SyntaxNodeAnalysisContext contex walker.VisitBlock(body); - return walker.ReturnStatement is not null; + return walker.ReturnStatement is not null + && !CSharpUtility.IsInUnsafeContext(body); } finally { diff --git a/src/CSharp/CSharp/CSharpUtility.cs b/src/CSharp/CSharp/CSharpUtility.cs index 6954116817..d6627c6381 100644 --- a/src/CSharp/CSharp/CSharpUtility.cs +++ b/src/CSharp/CSharp/CSharpUtility.cs @@ -10,55 +10,35 @@ namespace Roslynator.CSharp; internal static class CSharpUtility { - //public static bool CanConvertToCollectionExpression(ImplicitObjectCreationExpressionSyntax implicitObjectCreation, SemanticModel semanticModel, CancellationToken cancellationToken) - //{ - // return !implicitObjectCreation.WalkUpParentheses().IsParentKind(SyntaxKind.Argument) - // && implicitObjectCreation.ArgumentList?.Arguments.Any() != true - // && SyntaxUtility.CanConvertToCollectionExpression(implicitObjectCreation, semanticModel, cancellationToken); - //} - - //public static bool CanConvertToCollectionExpression(ObjectCreationExpressionSyntax objectCreation, SemanticModel semanticModel, CancellationToken cancellationToken) - //{ - // return !objectCreation.WalkUpParentheses().IsParentKind(SyntaxKind.Argument) - // && objectCreation.ArgumentList?.Arguments.Any() != true - // && SyntaxUtility.CanConvertToCollectionExpression(objectCreation, semanticModel, cancellationToken); - //} - - //public static bool CanConvertToCollectionExpression(ArrayCreationExpressionSyntax arrayCreation, SemanticModel semanticModel, CancellationToken cancellationToken) - //{ - // return CanConvertToCollectionExpression(arrayCreation) - // && SyntaxUtility.CanConvertToCollectionExpression(arrayCreation, semanticModel, cancellationToken); - //} - - //public static bool CanConvertToCollectionExpression(ImplicitArrayCreationExpressionSyntax implicitArrayCreation, SemanticModel semanticModel, CancellationToken cancellationToken) - //{ - // CSharpTypeAnalysis.IsTypeObvious(implicitArrayCreation, semanticModel, cancellationToken); - // return CanConvertToCollectionExpression(implicitArrayCreation) - // && SyntaxUtility.CanConvertToCollectionExpression(implicitArrayCreation, semanticModel, cancellationToken); - //} - - //private static bool CanConvertToCollectionExpression(ExpressionSyntax expression) - //{ - // expression = expression.WalkUpParentheses(); - - // if (expression.IsParentKind( - // SyntaxKind.Argument, - // SyntaxKind.ForEachStatement, - // SyntaxKind.ForEachVariableStatement)) - // { - // return false; - // } - - // if (expression.Parent.IsKind(SyntaxKind.EqualsValueClause) - // && expression.Parent.Parent.IsKind(SyntaxKind.VariableDeclarator) - // && expression.Parent.Parent.Parent is VariableDeclarationSyntax variableDeclaration - // && variableDeclaration.Type.IsVar) - // { - // return false; - // } - - // return true; - //} + public static bool IsInUnsafeContext(SyntaxNode? node) + { + while (node is not null) + { + switch (node) + { + case UnsafeStatementSyntax: + return true; + case MemberDeclarationSyntax memberDeclarationSyntax: + { + if (memberDeclarationSyntax.Modifiers.Contains(SyntaxKind.UnsafeKeyword)) + return true; + + break; + } + case LocalFunctionStatementSyntax localFunctionStatement: + { + if (localFunctionStatement.Modifiers.Contains(SyntaxKind.UnsafeKeyword)) + return true; + + break; + } + } + + node = node.Parent; + } + + return false; + } public static bool IsNullableReferenceType( TypeSyntax type, diff --git a/src/Tests/Analyzers.Tests/RCS1229UseAsyncAwaitTests.cs b/src/Tests/Analyzers.Tests/RCS1229UseAsyncAwaitTests.cs index 3e433094e1..2fc21c22a5 100644 --- a/src/Tests/Analyzers.Tests/RCS1229UseAsyncAwaitTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1229UseAsyncAwaitTests.cs @@ -831,4 +831,32 @@ ValueTask M() } "); } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseAsyncAwait)] + public async Task TestNoDiagnostic_Unsafe() + { + await VerifyNoDiagnosticAsync(@" +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +internal abstract unsafe class UnsafeStream() : Stream +{ + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + try + { + var tcs = new TaskCompletionSource(); + + return new ValueTask(tcs.Task); + } + catch + { + throw; + } + } +} +", options: Options.WithAllowUnsafe(true)); + } }