Merge 8449c8e2ab
into 7124d679fd
This commit is contained in:
commit
004fe1b7c8
49 changed files with 1201 additions and 16 deletions
3
.github/workflows/checks.yml
vendored
3
.github/workflows/checks.yml
vendored
|
@ -36,6 +36,9 @@ jobs:
|
||||||
|
|
||||||
- run: dotnet restore
|
- run: dotnet restore
|
||||||
|
|
||||||
|
- name: Build Ryujinx.Analyzers
|
||||||
|
run: dotnet build src/Ryujinx.Analyzers
|
||||||
|
|
||||||
- name: Print dotnet format version
|
- name: Print dotnet format version
|
||||||
run: dotnet format --version
|
run: dotnet format --version
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
||||||
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1" />
|
||||||
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.1" />
|
||||||
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.4.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.4.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||||
|
@ -48,5 +51,7 @@
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
|
<PackageVersion Include="xunit" Version="2.4.2" />
|
||||||
|
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
12
Ryujinx.sln
12
Ryujinx.sln
|
@ -87,6 +87,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Analyzers", "src\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj", "{C9523766-7101-442D-89F8-98A43B00267D}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Tests.Analyzers", "src\Ryujinx.Tests.Analyzers\Ryujinx.Tests.Analyzers.csproj", "{F9D4CBAA-C63D-4062-94A8-F06299DC486B}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -249,6 +253,14 @@ Global
|
||||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C9523766-7101-442D-89F8-98A43B00267D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C9523766-7101-442D-89F8-98A43B00267D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C9523766-7101-442D-89F8-98A43B00267D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C9523766-7101-442D-89F8-98A43B00267D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F9D4CBAA-C63D-4062-94A8-F06299DC486B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F9D4CBAA-C63D-4062-94A8-F06299DC486B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F9D4CBAA-C63D-4062-94A8-F06299DC486B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F9D4CBAA-C63D-4062-94A8-F06299DC486B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
7
src/Ryujinx.Analyzers/AnalyzerReleases.Shipped.md
Normal file
7
src/Ryujinx.Analyzers/AnalyzerReleases.Shipped.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
## Release 1.0
|
||||||
|
|
||||||
|
### New Rules
|
||||||
|
|
||||||
|
| Rule ID | Category | Severity | Notes |
|
||||||
|
|---------|-----------------|----------|-------------------------------------|
|
||||||
|
| RYU0001 | Maintainability | Warning | Caught exceptions should be logged. |
|
193
src/Ryujinx.Analyzers/CatchClauseAnalyzer.cs
Normal file
193
src/Ryujinx.Analyzers/CatchClauseAnalyzer.cs
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ryujinx.Analyzers
|
||||||
|
{
|
||||||
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
|
public class CatchClauseAnalyzer : DiagnosticAnalyzer
|
||||||
|
{
|
||||||
|
private const string LoggerIdentifier = "Logger";
|
||||||
|
private const string LogClassIdentifier = "LogClass";
|
||||||
|
|
||||||
|
public const string DiagnosticId = "RYU0001";
|
||||||
|
|
||||||
|
private static readonly LocalizableString _title = new LocalizableResourceString(nameof(Resources.RYU0001Title),
|
||||||
|
Resources.ResourceManager, typeof(Resources));
|
||||||
|
|
||||||
|
private static readonly LocalizableString _messageFormat =
|
||||||
|
new LocalizableResourceString(nameof(Resources.RYU0001MessageFormat), Resources.ResourceManager,
|
||||||
|
typeof(Resources));
|
||||||
|
|
||||||
|
private static readonly LocalizableString _description =
|
||||||
|
new LocalizableResourceString(nameof(Resources.RYU0001Description), Resources.ResourceManager,
|
||||||
|
typeof(Resources));
|
||||||
|
|
||||||
|
private const string Category = "Maintainability";
|
||||||
|
|
||||||
|
private static readonly DiagnosticDescriptor _rule = new(DiagnosticId, _title, _messageFormat, Category,
|
||||||
|
DiagnosticSeverity.Warning, isEnabledByDefault: true, description: _description);
|
||||||
|
|
||||||
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
|
||||||
|
ImmutableArray.Create(_rule);
|
||||||
|
|
||||||
|
public override void Initialize(AnalysisContext context)
|
||||||
|
{
|
||||||
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics);
|
||||||
|
context.EnableConcurrentExecution();
|
||||||
|
|
||||||
|
context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.CatchClause);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool EndsWithExpressionText(ExpressionSyntax expression, string text)
|
||||||
|
{
|
||||||
|
if (expression is MemberAccessExpressionSyntax memberAccessExpression)
|
||||||
|
{
|
||||||
|
if (memberAccessExpression.Expression.ToString().EndsWith(text))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var childNode in expression.ChildNodes())
|
||||||
|
{
|
||||||
|
if (childNode is not ExpressionSyntax childExpression)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EndsWithExpressionText(childExpression, text))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsInvocationArgText(ExpressionSyntax expression, string text)
|
||||||
|
{
|
||||||
|
if (expression is InvocationExpressionSyntax invocationExpression)
|
||||||
|
{
|
||||||
|
if (invocationExpression.ArgumentList.Arguments.Count > 0)
|
||||||
|
{
|
||||||
|
var invocationArg = invocationExpression.ArgumentList.Arguments.First();
|
||||||
|
|
||||||
|
return invocationArg.ToString().StartsWith($"{text}.") ||
|
||||||
|
invocationArg.ToString().Contains($".{text}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var childNode in expression.ChildNodes())
|
||||||
|
{
|
||||||
|
if (childNode is not ExpressionSyntax childExpression)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ContainsInvocationArgText(childExpression, text))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsIdentifier(ExpressionSyntax expression, string identifierText)
|
||||||
|
{
|
||||||
|
return expression.DescendantNodes().Any(
|
||||||
|
x => x is IdentifierNameSyntax identifierName
|
||||||
|
&& identifierName.ToString() == identifierText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executed for each Syntax Node with 'SyntaxKind.CatchClause'.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Operation context.</param>
|
||||||
|
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
|
||||||
|
{
|
||||||
|
if (context.Node is not CatchClauseSyntax catchClauseSyntax)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var catchDeclaration = catchClauseSyntax.Declaration;
|
||||||
|
|
||||||
|
// Ignore if catch block contains a throw statement.
|
||||||
|
if (catchClauseSyntax.Block.DescendantNodes().Any(x => x is ThrowStatementSyntax))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find catch clauses without declaration.
|
||||||
|
if (catchDeclaration == null)
|
||||||
|
{
|
||||||
|
var diagnostic = Diagnostic.Create(_rule,
|
||||||
|
// The highlighted area in the analyzed source code. Keep it as specific as possible.
|
||||||
|
catchClauseSyntax.GetLocation(),
|
||||||
|
// The value is passed to 'MessageFormat' argument of your 'Rule'.
|
||||||
|
"Exception");
|
||||||
|
|
||||||
|
context.ReportDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
// Find catch declarations without an identifier
|
||||||
|
else if (string.IsNullOrWhiteSpace(catchDeclaration.Identifier.Text))
|
||||||
|
{
|
||||||
|
var diagnostic = Diagnostic.Create(_rule,
|
||||||
|
// The highlighted area in the analyzed source code. Keep it as specific as possible.
|
||||||
|
catchClauseSyntax.GetLocation(),
|
||||||
|
// The value is passed to 'MessageFormat' argument of your 'Rule'.
|
||||||
|
catchDeclaration.Type.ToString());
|
||||||
|
|
||||||
|
context.ReportDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
// Check logging statements for a reference to the identifier of the catch declaration
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var catchDeclarationIdentifier = catchDeclaration.Identifier;
|
||||||
|
bool exceptionLogged = false;
|
||||||
|
|
||||||
|
// Ignore if identifier is equal to "_"
|
||||||
|
if (catchDeclarationIdentifier.Text == "_")
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through all expression statements
|
||||||
|
foreach (var blockNode in catchClauseSyntax.Block.DescendantNodes())
|
||||||
|
{
|
||||||
|
if (blockNode is not ExpressionStatementSyntax expressionStatement)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find Logger invocation
|
||||||
|
if (EndsWithExpressionText(expressionStatement.Expression, LoggerIdentifier) && ContainsInvocationArgText(expressionStatement.Expression, LogClassIdentifier))
|
||||||
|
{
|
||||||
|
// Find catchDeclarationIdentifier in Logger invocation
|
||||||
|
if (ContainsIdentifier(expressionStatement.Expression, catchDeclarationIdentifier.Text))
|
||||||
|
{
|
||||||
|
exceptionLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a diagnostic report if the exception was not logged
|
||||||
|
if (!exceptionLogged)
|
||||||
|
{
|
||||||
|
var diagnostic = Diagnostic.Create(_rule,
|
||||||
|
// The highlighted area in the analyzed source code. Keep it as specific as possible.
|
||||||
|
catchClauseSyntax.GetLocation(),
|
||||||
|
// The value is passed to 'MessageFormat' argument of your 'Rule'.
|
||||||
|
catchDeclaration.Type.ToString());
|
||||||
|
|
||||||
|
context.ReportDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
src/Ryujinx.Analyzers/CatchClauseCodeFixProvider.cs
Normal file
150
src/Ryujinx.Analyzers/CatchClauseCodeFixProvider.cs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CodeActions;
|
||||||
|
using Microsoft.CodeAnalysis.CodeFixes;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.CodeAnalysis.Formatting;
|
||||||
|
using Microsoft.CodeAnalysis.Simplification;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Composition;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||||
|
using SyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind;
|
||||||
|
|
||||||
|
namespace Ryujinx.Analyzers
|
||||||
|
{
|
||||||
|
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CatchClauseCodeFixProvider)), Shared]
|
||||||
|
public class CatchClauseCodeFixProvider : CodeFixProvider
|
||||||
|
{
|
||||||
|
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } =
|
||||||
|
ImmutableArray.Create(CatchClauseAnalyzer.DiagnosticId);
|
||||||
|
|
||||||
|
public override FixAllProvider? GetFixAllProvider() => null;
|
||||||
|
|
||||||
|
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||||
|
{
|
||||||
|
var diagnostic = context.Diagnostics.Single();
|
||||||
|
var diagnosticSpan = diagnostic.Location.SourceSpan;
|
||||||
|
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Find SyntaxNode corresponding to the diagnostic.
|
||||||
|
var diagnosticNode = root?.FindNode(diagnosticSpan);
|
||||||
|
|
||||||
|
// To get the required metadata, we should match the Node to the specific type: 'CatchClauseSyntax'.
|
||||||
|
if (diagnosticNode is not CatchClauseSyntax catchClause)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Register a code action that will invoke the fix.
|
||||||
|
context.RegisterCodeFix(
|
||||||
|
CodeAction.Create(
|
||||||
|
title: string.Format(Resources.RYU0001CodeFixTitle, GetExceptionType(catchClause)),
|
||||||
|
createChangedDocument: c => LogException(context.Document, catchClause, c),
|
||||||
|
equivalenceKey: nameof(Resources.RYU0001CodeFixTitle)),
|
||||||
|
diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetExceptionType(CatchClauseSyntax catchClause)
|
||||||
|
{
|
||||||
|
return catchClause.Declaration != null ? catchClause.Declaration.Type.ToString() : "Exception";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MemberAccessExpressionSyntax GetLoggingClass(string className)
|
||||||
|
{
|
||||||
|
return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||||
|
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||||
|
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||||
|
SyntaxFactory.IdentifierName("Ryujinx"),
|
||||||
|
SyntaxFactory.IdentifierName("Common")),
|
||||||
|
SyntaxFactory.IdentifierName("Logging")),
|
||||||
|
SyntaxFactory.IdentifierName(className))
|
||||||
|
.WithAdditionalAnnotations(Simplifier.AddImportsAnnotation, Simplifier.Annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executed on the quick fix action raised by the user.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="document">Affected source file.</param>
|
||||||
|
/// <param name="catchClauseSyntax">Highlighted catch clause syntax node.</param>
|
||||||
|
/// <param name="cancellationToken">Any fix is cancellable by the user, so we should support the cancellation token.</param>
|
||||||
|
/// <returns>Clone of the document with the modified catch clause.</returns>
|
||||||
|
private async Task<Document> LogException(Document document,
|
||||||
|
CatchClauseSyntax catchClauseSyntax, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
CatchDeclarationSyntax catchDeclaration;
|
||||||
|
string catchDeclarationIdentifier = "exception";
|
||||||
|
|
||||||
|
// Add a catch declaration if it doesn't exist.
|
||||||
|
if (catchClauseSyntax.Declaration == null)
|
||||||
|
{
|
||||||
|
// System.Exception exception
|
||||||
|
catchDeclaration =
|
||||||
|
SyntaxFactory.CatchDeclaration(
|
||||||
|
SyntaxFactory.QualifiedName(
|
||||||
|
SyntaxFactory.IdentifierName("System"), SyntaxFactory.IdentifierName("Exception")),
|
||||||
|
SyntaxFactory.Identifier("exception")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(catchClauseSyntax.Declaration.Identifier.Text))
|
||||||
|
{
|
||||||
|
catchDeclaration = catchClauseSyntax.Declaration;
|
||||||
|
catchDeclarationIdentifier = catchDeclaration.Identifier.Text;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
catchDeclaration = catchClauseSyntax.Declaration.WithIdentifier(
|
||||||
|
SyntaxFactory.Identifier(catchDeclarationIdentifier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create logging statement.
|
||||||
|
// Ryujinx.Common.Logging.Logger.Error?.Print(LogClass.Application, $"Exception caught: {exception}");
|
||||||
|
var newStatements = catchClauseSyntax.Block.Statements.Insert(0, SyntaxFactory.ExpressionStatement(SyntaxFactory.ConditionalAccessExpression(
|
||||||
|
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||||
|
GetLoggingClass("Logger"),
|
||||||
|
SyntaxFactory.IdentifierName("Error")),
|
||||||
|
SyntaxFactory.InvocationExpression(
|
||||||
|
SyntaxFactory.MemberBindingExpression(SyntaxFactory.IdentifierName("Print")))
|
||||||
|
.AddArgumentListArguments(
|
||||||
|
SyntaxFactory.Argument(SyntaxFactory.MemberAccessExpression(
|
||||||
|
SyntaxKind.SimpleMemberAccessExpression,
|
||||||
|
GetLoggingClass("LogClass"),
|
||||||
|
SyntaxFactory.IdentifierName("Application"))),
|
||||||
|
SyntaxFactory.Argument(SyntaxFactory.InterpolatedStringExpression(
|
||||||
|
SyntaxFactory.Token(SyntaxKind.InterpolatedStringStartToken)).AddContents(
|
||||||
|
SyntaxFactory.InterpolatedStringText().WithTextToken(
|
||||||
|
SyntaxFactory.Token(
|
||||||
|
SyntaxTriviaList.Empty,
|
||||||
|
SyntaxKind.InterpolatedStringTextToken,
|
||||||
|
"Exception caught: ",
|
||||||
|
"Exception caught: ",
|
||||||
|
SyntaxTriviaList.Empty)
|
||||||
|
),
|
||||||
|
SyntaxFactory.Interpolation(
|
||||||
|
SyntaxFactory.IdentifierName(
|
||||||
|
catchDeclarationIdentifier).WithAdditionalAnnotations(
|
||||||
|
RenameAnnotation.Create())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)));
|
||||||
|
|
||||||
|
// Produce the new catch clause.
|
||||||
|
var newCatchClause = catchClauseSyntax
|
||||||
|
.WithCatchKeyword(catchClauseSyntax.CatchKeyword.WithTrailingTrivia(SyntaxFactory.Space))
|
||||||
|
.WithDeclaration(catchDeclaration)
|
||||||
|
.WithBlock(catchClauseSyntax.Block.WithStatements(newStatements))
|
||||||
|
.WithAdditionalAnnotations(Formatter.Annotation);
|
||||||
|
|
||||||
|
// Replace the old local declaration with the new local declaration.
|
||||||
|
SyntaxNode oldRoot = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!;
|
||||||
|
SyntaxNode newRoot = oldRoot.ReplaceNode(catchClauseSyntax, newCatchClause);
|
||||||
|
|
||||||
|
// Return document with the transformed tree.
|
||||||
|
return document.WithSyntaxRoot(newRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
src/Ryujinx.Analyzers/Resources.Designer.cs
generated
Normal file
72
src/Ryujinx.Analyzers/Resources.Designer.cs
generated
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Ryujinx.Analyzers {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources {
|
||||||
|
|
||||||
|
private static System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.Equals(null, resourceMan)) {
|
||||||
|
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Ryujinx.Analyzers.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string RYU0001Title {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RYU0001Title", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string RYU0001Description {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RYU0001Description", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string RYU0001MessageFormat {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RYU0001MessageFormat", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string RYU0001CodeFixTitle {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RYU0001CodeFixTitle", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
src/Ryujinx.Analyzers/Resources.resx
Normal file
33
src/Ryujinx.Analyzers/Resources.resx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<root>
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>1.3</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="RYU0001Title" xml:space="preserve">
|
||||||
|
<value>Caught exception is not logged</value>
|
||||||
|
</data>
|
||||||
|
<data name="RYU0001Description" xml:space="preserve">
|
||||||
|
<value>Caught exceptions should be logged.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RYU0001MessageFormat" xml:space="preserve">
|
||||||
|
<value>Caught exception '{0}' is not logged</value>
|
||||||
|
</data>
|
||||||
|
<data name="RYU0001CodeFixTitle" xml:space="preserve">
|
||||||
|
<value>Log the caught exception '{0}'</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
44
src/Ryujinx.Analyzers/Ryujinx.Analyzers.csproj
Normal file
44
src/Ryujinx.Analyzers/Ryujinx.Analyzers.csproj
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
|
||||||
|
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||||
|
<IsRoslynComponent>true</IsRoslynComponent>
|
||||||
|
|
||||||
|
<RootNamespace>Ryujinx.Analyzers</RootNamespace>
|
||||||
|
<AssemblyName>Ryujinx.Analyzers</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"/>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Update="Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Resources.Designer.cs">
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<AdditionalFiles Remove="AnalyzerReleases.Unshipped.md" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
|
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -12,4 +12,9 @@
|
||||||
<PackageReference Include="System.Management" />
|
<PackageReference Include="System.Management" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -2,19 +2,14 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Nvdec.Vp9\Ryujinx.Graphics.Nvdec.Vp9.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Nvdec.Vp9\Ryujinx.Graphics.Nvdec.Vp9.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Spv.Generator\Spv.Generator.csproj" />
|
<ProjectReference Include="..\Spv.Generator\Spv.Generator.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -2,13 +2,6 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -60,6 +53,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -13,4 +13,9 @@
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -13,4 +13,8 @@
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,8 +8,11 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
<ProjectReference Include="..\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
327
src/Ryujinx.Tests.Analyzers/CatchClauseAnalyzerTests.cs
Normal file
327
src/Ryujinx.Tests.Analyzers/CatchClauseAnalyzerTests.cs
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
using Verifier =
|
||||||
|
Microsoft.CodeAnalysis.CSharp.Testing.XUnit.AnalyzerVerifier<
|
||||||
|
Ryujinx.Analyzers.CatchClauseAnalyzer>;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.Analyzers
|
||||||
|
{
|
||||||
|
public class CatchClauseAnalyzerTests
|
||||||
|
{
|
||||||
|
private static readonly string _loggerTextPath = Path.Combine(
|
||||||
|
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
|
||||||
|
"Fixtures", "CatchClauseLogger.cs");
|
||||||
|
|
||||||
|
private static readonly string _loggerText = File.ReadAllText(_loggerTextPath);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CatchWithoutDeclaration_WarningDiagnostic()
|
||||||
|
{
|
||||||
|
const string Text = @"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod1()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
var expected = Verifier.Diagnostic()
|
||||||
|
.WithSpan(12, 9, 15, 10)
|
||||||
|
.WithArguments("Exception");
|
||||||
|
await Verifier.VerifyAnalyzerAsync(Text, expected).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CatchWithoutIdentifier_WarningDiagnostic()
|
||||||
|
{
|
||||||
|
const string Text = @"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod2()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
// testme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
var expected = Verifier.Diagnostic()
|
||||||
|
.WithSpan(12, 9, 15, 10)
|
||||||
|
.WithArguments("NullReferenceException");
|
||||||
|
await Verifier.VerifyAnalyzerAsync(Text, expected).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CatchWithEmptyThrowStatement_NoDiagnostic()
|
||||||
|
{
|
||||||
|
string text = @"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod3()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CatchWithIdentifierAndThrowStatement_NoDiagnostic()
|
||||||
|
{
|
||||||
|
string text = @"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod4()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (NullReferenceException exception)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(""invalid"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CatchWithIgnoredIdentifier_NoDiagnostic()
|
||||||
|
{
|
||||||
|
string text = @"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod5()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (NullReferenceException _)
|
||||||
|
{
|
||||||
|
// Skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LogWithoutCatchIdentifier_WarningDiagnostic()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod6()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (ArgumentException exception)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, ""test exception"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
var expected = Verifier.Diagnostic()
|
||||||
|
.WithSpan(89, 9, 92, 10)
|
||||||
|
.WithArguments("ArgumentException");
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text, expected).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LogWithIdentifierInString_NoDiagnostic()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod7()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (Exception abc)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $""test: {abc}"");
|
||||||
|
Console.WriteLine(""Test"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LogWithIdentifierAsArg_NoDiagnostic()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod8()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (System.Exception exception)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $""test"", exception);
|
||||||
|
Console.WriteLine(""Test"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LogWithIdentifierAndMethodCall_NoDiagnostic()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod9()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException abc)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $""test: {abc.ToString()}"");
|
||||||
|
Console.WriteLine(""Test"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LogWithMethodCallOnIdentifier_NoDiagnostic()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod10()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (IndexOutOfRangeException mistake1)
|
||||||
|
{
|
||||||
|
string test = ""another test"";
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $""test: {string.Concat(mistake1, test)}"");
|
||||||
|
Console.WriteLine(""Test"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LogWithPropertyOfException_NoDiagnostic()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod11()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (ArgumentOutOfRangeException oob)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $""test: {oob.Message}"");
|
||||||
|
Console.WriteLine(""Test"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LogWithIdentifierInSubBlock_NoDiagnostic()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod12()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
string testString = ""first time?"";
|
||||||
|
|
||||||
|
if (1 == 1)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $""test: {testString} Error: {ex.Message}"");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine(""Test"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
await Verifier.VerifyAnalyzerAsync(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
151
src/Ryujinx.Tests.Analyzers/CatchClauseCodeFixProviderTests.cs
Normal file
151
src/Ryujinx.Tests.Analyzers/CatchClauseCodeFixProviderTests.cs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
using Verifier =
|
||||||
|
Microsoft.CodeAnalysis.CSharp.Testing.XUnit.CodeFixVerifier<Ryujinx.Analyzers.CatchClauseAnalyzer,
|
||||||
|
Ryujinx.Analyzers.CatchClauseCodeFixProvider>;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.Analyzers
|
||||||
|
{
|
||||||
|
public class CatchClauseCodeFixProviderTests
|
||||||
|
{
|
||||||
|
private static readonly string _loggerTextPath = Path.Combine(
|
||||||
|
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
|
||||||
|
"Fixtures", "CatchClauseLogger.cs");
|
||||||
|
|
||||||
|
private static readonly string _loggerText = File.ReadAllText(_loggerTextPath);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CatchWithoutDeclaration_LogException()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod1()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
string newText = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod1()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (System.Exception exception)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Error?.Print(Ryujinx.Common.Logging.LogClass.Application, $""Exception caught: {exception}"");
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
var expected = Verifier.Diagnostic()
|
||||||
|
.WithSpan(89, 9, 92, 10)
|
||||||
|
.WithArguments("Exception");
|
||||||
|
await Verifier.VerifyCodeFixAsync(text, expected, newText).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CatchWithoutIdentifier_LogException()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod2()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
string newText = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod2()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (NullReferenceException exception)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Error?.Print(Ryujinx.Common.Logging.LogClass.Application, $""Exception caught: {exception}"");
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
var expected = Verifier.Diagnostic()
|
||||||
|
.WithSpan(89, 9, 92, 10)
|
||||||
|
.WithArguments("NullReferenceException");
|
||||||
|
await Verifier.VerifyCodeFixAsync(text, expected, newText).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LogWithoutCatchIdentifier_LogException()
|
||||||
|
{
|
||||||
|
string text = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod3()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (ArgumentException ex)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, ""test"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
string newText = _loggerText + @"
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public void MyMethod3()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(""test"");
|
||||||
|
}
|
||||||
|
catch (ArgumentException ex)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Error?.Print(Ryujinx.Common.Logging.LogClass.Application, $""Exception caught: {ex}"");
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, ""test"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
var expected = Verifier.Diagnostic()
|
||||||
|
.WithSpan(89, 9, 92, 10)
|
||||||
|
.WithArguments("ArgumentException");
|
||||||
|
await Verifier.VerifyCodeFixAsync(text, expected, newText).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
src/Ryujinx.Tests.Analyzers/Fixtures/CatchClauseLogger.cs
Normal file
79
src/Ryujinx.Tests.Analyzers/Fixtures/CatchClauseLogger.cs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
// ReSharper disable All
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
public enum LogClass
|
||||||
|
{
|
||||||
|
Application,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LogEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public readonly int Level;
|
||||||
|
public readonly TimeSpan Time;
|
||||||
|
public readonly string ThreadName;
|
||||||
|
|
||||||
|
public readonly string Message;
|
||||||
|
public readonly object Data;
|
||||||
|
|
||||||
|
public LogEventArgs(int level, TimeSpan time, string threadName, string message, object data = null)
|
||||||
|
{
|
||||||
|
Level = level;
|
||||||
|
Time = time;
|
||||||
|
ThreadName = threadName;
|
||||||
|
Message = message;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Logger
|
||||||
|
{
|
||||||
|
private static readonly Stopwatch _time;
|
||||||
|
private static readonly bool[] _enabledClasses;
|
||||||
|
public static event EventHandler<LogEventArgs> Updated;
|
||||||
|
public readonly struct Log
|
||||||
|
{
|
||||||
|
internal readonly int Level;
|
||||||
|
|
||||||
|
internal Log(int level)
|
||||||
|
{
|
||||||
|
Level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Print(LogClass logClass, string message, [CallerMemberName] string caller = "")
|
||||||
|
{
|
||||||
|
if (_enabledClasses[(int)logClass])
|
||||||
|
{
|
||||||
|
Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Print(LogClass logClass, string message, object data, [CallerMemberName] string caller = "")
|
||||||
|
{
|
||||||
|
if (_enabledClasses[(int)logClass])
|
||||||
|
{
|
||||||
|
Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static string FormatMessage(LogClass logClass, string caller, string message) => $"{logClass} {caller}: {message}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Log? Debug { get; private set; }
|
||||||
|
public static Log? Info { get; private set; }
|
||||||
|
public static Log? Warning { get; private set; }
|
||||||
|
public static Log? Error { get; private set; }
|
||||||
|
public static Log? Guest { get; private set; }
|
||||||
|
public static Log? AccessLog { get; private set; }
|
||||||
|
public static Log? Stub { get; private set; }
|
||||||
|
public static Log? Trace { get; private set; }
|
||||||
|
public static Log Notice { get; } // Always enabled
|
||||||
|
}
|
||||||
|
}
|
35
src/Ryujinx.Tests.Analyzers/Ryujinx.Tests.Analyzers.csproj
Normal file
35
src/Ryujinx.Tests.Analyzers/Ryujinx.Tests.Analyzers.csproj
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Fixtures\**" />
|
||||||
|
<None
|
||||||
|
Include="Fixtures\**"
|
||||||
|
CopyToOutputDirectory="PreserveNewest"
|
||||||
|
LinkBase="Fixtures\"
|
||||||
|
/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit"/>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit"/>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
|
||||||
|
<PackageReference Include="xunit"/>
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -63,6 +63,8 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -15,4 +15,9 @@
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -70,7 +70,10 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
<ProjectReference Include="..\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -4,4 +4,9 @@
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ryujinx.Analyzers\Ryujinx.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue