Skip to content

Commit

Permalink
Fix S112 FN: When using null-coalesce operator (#7911)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-mikula-sonarsource committed Aug 29, 2023
1 parent 31a361a commit 922d983
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 135 deletions.
17 changes: 17 additions & 0 deletions analyzers/its/expected/Automapper/AutoMapper--net461-S112.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"issues": [
{
"id": "S112",
"message": "'System.NullReferenceException' should not be thrown by user code.",
"location": {
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/AutoMapper/src/AutoMapper/Mapper.cs#L151",
"region": {
"startLine": 151,
"startColumn": 117,
"endLine": 151,
"endColumn": 164
}
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"issues": [
{
"id": "S112",
"message": "'System.NullReferenceException' should not be thrown by user code.",
"location": {
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/AutoMapper/src/AutoMapper/Mapper.cs#L151",
"region": {
"startLine": 151,
"startColumn": 117,
"endLine": 151,
"endColumn": 164
}
}
}
]
}
13 changes: 13 additions & 0 deletions analyzers/its/expected/Net5/Net5--net5.0-S112.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@
"endColumn": 34
}
}
},
{
"id": "S112",
"message": "'System.Exception' should not be thrown by user code.",
"location": {
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Net5/Net5/S3877.cs#L15",
"region": {
"startLine": 15,
"startColumn": 44,
"endLine": 15,
"endColumn": 59
}
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"issues": [
{
"id": "S112",
"message": "'System.Exception' should not be thrown by user code.",
"location": {
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/akka.net/src/benchmark/Akka.Benchmarks/IO/TcpOperationsBenchmarks.cs#L149",
"region": {
"startLine": 149,
"startColumn": 55,
"endLine": 149,
"endColumn": 89
}
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,16 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarAnalyzer.Rules.CSharp
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ThrowReservedExceptions : ThrowReservedExceptionsBase
{
private static readonly DiagnosticDescriptor rule =
DescriptorFactory.Create(DiagnosticId, MessageFormat);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(rule);
namespace SonarAnalyzer.Rules.CSharp;

protected override void Initialize(SonarAnalysisContext context)
{
context.RegisterNodeAction(
c => ReportReservedExceptionCreation(c, ((ThrowStatementSyntax)c.Node).Expression),
SyntaxKind.ThrowStatement);
}
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ThrowReservedExceptions : ThrowReservedExceptionsBase<SyntaxKind>
{
protected override ILanguageFacade<SyntaxKind> Language => CSharpFacade.Instance;

protected override bool IsObjectCreation(SyntaxNode throwStatementExpression) =>
throwStatementExpression.IsKind(SyntaxKind.ObjectCreationExpression);
protected override void Initialize(SonarAnalysisContext context)
{
context.RegisterNodeAction(c => Process(c, ((ThrowStatementSyntax)c.Node).Expression), SyntaxKind.ThrowStatement);
context.RegisterNodeAction(c => Process(c, ((ThrowExpressionSyntaxWrapper)c.Node).Expression), SyntaxKindEx.ThrowExpression);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,35 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarAnalyzer.Rules
namespace SonarAnalyzer.Rules;

public abstract class ThrowReservedExceptionsBase<TSyntaxKind> : SonarDiagnosticAnalyzer<TSyntaxKind>
where TSyntaxKind : struct
{
public abstract class ThrowReservedExceptionsBase : SonarDiagnosticAnalyzer
{
internal const string DiagnosticId = "S112";
internal const string MessageFormat = "'{0}' should not be thrown by user code.";
protected const string DiagnosticId = "S112";

internal static readonly ImmutableArray<KnownType> ReservedExceptionTypeNames =
ImmutableArray.Create(
KnownType.System_Exception,
KnownType.System_ApplicationException,
KnownType.System_SystemException,
KnownType.System_ExecutionEngineException,
KnownType.System_IndexOutOfRangeException,
KnownType.System_NullReferenceException,
KnownType.System_OutOfMemoryException
);
private readonly ImmutableArray<KnownType> reservedExceptions = ImmutableArray.Create(
KnownType.System_Exception,
KnownType.System_ApplicationException,
KnownType.System_SystemException,
KnownType.System_ExecutionEngineException,
KnownType.System_IndexOutOfRangeException,
KnownType.System_NullReferenceException,
KnownType.System_OutOfMemoryException);

protected void ReportReservedExceptionCreation(SonarSyntaxNodeReportingContext context,
SyntaxNode throwStatementExpression)
{
if (throwStatementExpression == null ||
!IsObjectCreation(throwStatementExpression))
{
return;
}
protected override string MessageFormat => "'{0}' should not be thrown by user code.";

var expressionType = context.SemanticModel.GetTypeInfo(throwStatementExpression).Type;
if (expressionType.IsAny(ReservedExceptionTypeNames))
protected ThrowReservedExceptionsBase() : base(DiagnosticId) { }

protected void Process(SonarSyntaxNodeReportingContext context, SyntaxNode thrownExpression)
{
if (thrownExpression is not null && Language.Syntax.IsAnyKind(thrownExpression, Language.SyntaxKind.ObjectCreationExpressions))
{
var expressionType = context.SemanticModel.GetTypeInfo(thrownExpression).Type;
if (expressionType.IsAny(reservedExceptions))
{
context.ReportIssue(Diagnostic.Create(SupportedDiagnostics[0], throwStatementExpression.GetLocation(), expressionType.ToDisplayString()));
context.ReportIssue(Diagnostic.Create(Rule, thrownExpression.GetLocation(), expressionType.ToDisplayString()));
}
}

protected abstract bool IsObjectCreation(SyntaxNode throwStatementExpression);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarAnalyzer.Rules.VisualBasic
{
[DiagnosticAnalyzer(LanguageNames.VisualBasic)]
public sealed class ThrowReservedExceptions : ThrowReservedExceptionsBase
{
private static readonly DiagnosticDescriptor rule =
DescriptorFactory.Create(DiagnosticId, MessageFormat);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(rule);
namespace SonarAnalyzer.Rules.VisualBasic;

protected override void Initialize(SonarAnalysisContext context)
{
context.RegisterNodeAction(
c => ReportReservedExceptionCreation(c, ((ThrowStatementSyntax)c.Node).Expression),
SyntaxKind.ThrowStatement);
}
[DiagnosticAnalyzer(LanguageNames.VisualBasic)]
public sealed class ThrowReservedExceptions : ThrowReservedExceptionsBase<SyntaxKind>
{
protected override ILanguageFacade<SyntaxKind> Language => VisualBasicFacade.Instance;

protected override bool IsObjectCreation(SyntaxNode throwStatementExpression) =>
throwStatementExpression.IsKind(SyntaxKind.ObjectCreationExpression);
}
protected override void Initialize(SonarAnalysisContext context) =>
context.RegisterNodeAction(c => Process(c, ((ThrowStatementSyntax)c.Node).Expression), SyntaxKind.ThrowStatement);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,22 @@
using CS = SonarAnalyzer.Rules.CSharp;
using VB = SonarAnalyzer.Rules.VisualBasic;

namespace SonarAnalyzer.UnitTest.Rules
namespace SonarAnalyzer.UnitTest.Rules;

[TestClass]
public class ThrowReservedExceptionsTest
{
[TestClass]
public class ThrowReservedExceptionsTest
{
[TestMethod]
public void ThrowReservedExceptions_CSharp() =>
new VerifierBuilder<CS.ThrowReservedExceptions>().AddPaths("ThrowReservedExceptions.cs").Verify();
private readonly VerifierBuilder builderCS = new VerifierBuilder<CS.ThrowReservedExceptions>();

[TestMethod]
public void ThrowReservedExceptions_CS() =>
builderCS.AddPaths("ThrowReservedExceptions.cs").Verify();

[TestMethod]
public void ThrowReservedExceptions_CSharp8() =>
builderCS.AddPaths("ThrowReservedExceptions.CSharp8.cs").WithOptions(ParseOptionsHelper.FromCSharp8).Verify();

[TestMethod]
public void ThrowReservedExceptions_VisualBasic() =>
new VerifierBuilder<VB.ThrowReservedExceptions>().AddPaths("ThrowReservedExceptions.vb").Verify();
}
[TestMethod]
public void ThrowReservedExceptions_VB() =>
new VerifierBuilder<VB.ThrowReservedExceptions>().AddPaths("ThrowReservedExceptions.vb").Verify();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

public class Sample
{
public void NullCoalesce(object arg)
{
_ = arg ?? throw new Exception(); // Noncompliant
// ^^^^^^^^^^^^^^^

_ = arg switch
{
string s => s,
_ => throw new Exception() // Noncompliant
// ^^^^^^^^^^^^^^^
};
}
}
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
using System;

namespace SonarAnalyzer.UnitTest.TestCases
public class ThrowReservedExceptions
{
public class ThrowReservedExceptions
public void Method1()
{
public void Method1()
{
throw new Exception(); // Noncompliant {{'System.Exception' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^

throw new ApplicationException(); // Noncompliant {{'System.ApplicationException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
throw new Exception(); // Noncompliant {{'System.Exception' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^

throw new SystemException(); // Noncompliant {{'System.SystemException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^
throw new ApplicationException(); // Noncompliant {{'System.ApplicationException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^

throw new ExecutionEngineException(); // Noncompliant {{'System.ExecutionEngineException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
throw new SystemException(); // Noncompliant {{'System.SystemException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^

throw new IndexOutOfRangeException(); // Noncompliant {{'System.IndexOutOfRangeException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
throw new ExecutionEngineException(); // Noncompliant {{'System.ExecutionEngineException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

throw new NullReferenceException(); // Noncompliant {{'System.NullReferenceException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
throw new IndexOutOfRangeException(); // Noncompliant {{'System.IndexOutOfRangeException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

throw new OutOfMemoryException(); // Noncompliant {{'System.OutOfMemoryException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
throw new NullReferenceException(); // Noncompliant {{'System.NullReferenceException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

var e = new OutOfMemoryException(); // Compliant
throw new OutOfMemoryException(); // Noncompliant {{'System.OutOfMemoryException' should not be thrown by user code.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^

throw new ArgumentNullException(); // Compliant
var e = new OutOfMemoryException(); // Compliant
throw new ArgumentNullException(); // Compliant

OutOfMemoryException e1 = (OutOfMemoryException)new ArgumentException(); // Error [CS0030] - cannot cast
OutOfMemoryException e1 = (OutOfMemoryException)new ArgumentException(); // Error [CS0030] - cannot cast

try
{
var a = new int[0];
Console.WriteLine(a[1]); // Throw exception
}
catch (IndexOutOfRangeException)
{
throw; // Compliant
}
try
{
var a = new int[0];
Console.WriteLine(a[1]); // Throw exception
}
catch (IndexOutOfRangeException)
{
throw; // Compliant
}
}

public void Arrow() =>
throw new Exception(); // Noncompliant

public Action Lambda() =>
() => throw new Exception(); // Noncompliant
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
Imports System
Public Class ThrowReservedExceptions
Public Sub Method1()
Throw New Exception() ' Noncompliant {{'System.Exception' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^
Throw New ApplicationException() ' Noncompliant {{'System.ApplicationException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^
Throw New SystemException() ' Noncompliant {{'System.SystemException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^
Throw New ExecutionEngineException() ' Noncompliant {{'System.ExecutionEngineException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Throw New IndexOutOfRangeException() ' Noncompliant {{'System.IndexOutOfRangeException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Throw New NullReferenceException() ' Noncompliant {{'System.NullReferenceException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Throw New OutOfMemoryException() ' Noncompliant {{'System.OutOfMemoryException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^
Dim e = New OutOfMemoryException() ' Compliant
Throw New ArgumentNullException() ' Compliant

Namespace SonarAnalyzer.UnitTest.TestCases
Public Class ThrowReservedExceptions
Public Sub Method1()
Throw New Exception() ' Noncompliant {{'System.Exception' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^
Throw New ApplicationException() ' Noncompliant {{'System.ApplicationException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^
Throw New SystemException() ' Noncompliant {{'System.SystemException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^
Throw New ExecutionEngineException() ' Noncompliant {{'System.ExecutionEngineException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Throw New IndexOutOfRangeException() ' Noncompliant {{'System.IndexOutOfRangeException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Throw New NullReferenceException() ' Noncompliant {{'System.NullReferenceException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Throw New OutOfMemoryException() ' Noncompliant {{'System.OutOfMemoryException' should not be thrown by user code.}}
' ^^^^^^^^^^^^^^^^^^^^^^^^^^
Dim e = New OutOfMemoryException() ' Compliant
Throw New ArgumentNullException() ' Compliant

Try
Dim a = New Integer(-1) {}
' Throw exception
Console.WriteLine(a(1))
Catch generatedExceptionName As IndexOutOfRangeException ' Compliant
Throw
End Try
End Sub
End Class
End Namespace
Try
Dim a = New Integer(-1) {}
' Throw exception
Console.WriteLine(a(1))
Catch generatedExceptionName As IndexOutOfRangeException ' Compliant
Throw
End Try
End Sub
End Class

0 comments on commit 922d983

Please sign in to comment.