Add Analyzer to disallow discarding locals e.g. `_ = result;`

fixes 70bd081a9, a2185d9ef
This commit is contained in:
YoshiRulz 2023-04-29 01:52:43 +10:00
parent a2185d9ef0
commit 8a5921182e
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
7 changed files with 28 additions and 7 deletions

View File

@ -16,6 +16,9 @@
<!-- Default branch of switch expression should throw InvalidOperationException/SwitchExpressionException or not throw -->
<Rule Id="BHI1005" Action="Error" />
<!-- Do not discard local variables -->
<Rule Id="BHI1006" Action="Error" />
<!-- Don't call this.GetType() in sealed type, use typeof operator -->
<Rule Id="BHI1100" Action="Error" />

View File

@ -38,6 +38,14 @@ public class HawkSourceAnalyzer : DiagnosticAnalyzer
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
private static readonly DiagnosticDescriptor DiagNoDiscardingLocals = new(
id: "BHI1006",
title: "Do not discard local variables",
messageFormat: "RHS is a local, so this discard has no effect, and is at best unhelpful for humans",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
private static readonly DiagnosticDescriptor DiagNoQueryExpression = new(
id: "BHI1003",
title: "Do not use query expression syntax",
@ -58,11 +66,14 @@ public class HawkSourceAnalyzer : DiagnosticAnalyzer
DiagInterpStringIsDollarAt,
DiagNoAnonClasses,
DiagNoAnonDelegates,
DiagNoDiscardingLocals,
DiagNoQueryExpression,
DiagSwitchShouldThrowIOE);
public override void Initialize(AnalysisContext context)
{
static bool IsDiscard(AssignmentExpressionSyntax aes)
=> aes.OperatorToken.RawKind is (int) SyntaxKind.EqualsToken && aes.Left is IdentifierNameSyntax { Identifier.Text: "_" };
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
INamedTypeSymbol? invalidOperationExceptionSym = null;
@ -83,6 +94,9 @@ public class HawkSourceAnalyzer : DiagnosticAnalyzer
case AnonymousObjectCreationExpressionSyntax:
snac.ReportDiagnostic(Diagnostic.Create(DiagNoAnonClasses, snac.Node.GetLocation()));
break;
case AssignmentExpressionSyntax aes when IsDiscard(aes) && snac.SemanticModel.GetSymbolInfo(aes.Right).Symbol?.Kind is SymbolKind.Local:
snac.ReportDiagnostic(Diagnostic.Create(DiagNoDiscardingLocals, snac.Node.GetLocation()));
break;
case InterpolatedStringExpressionSyntax ises:
if (ises.StringStartToken.Text[0] is '@') snac.ReportDiagnostic(Diagnostic.Create(DiagInterpStringIsDollarAt, ises.GetLocation()));
break;
@ -113,6 +127,7 @@ public class HawkSourceAnalyzer : DiagnosticAnalyzer
SyntaxKind.AnonymousMethodExpression,
SyntaxKind.InterpolatedStringExpression,
SyntaxKind.QueryExpression,
SyntaxKind.SimpleAssignmentExpression,
SyntaxKind.SwitchExpressionArm);
}
}

View File

@ -0,0 +1,7 @@
#!/bin/sh
set -e
cd "$(dirname "$0")"
./build_debug.sh
cd ../../ExternalToolProjects/HelloWorld
rm -fr bin obj
./build_debug.sh

Binary file not shown.

View File

@ -796,7 +796,7 @@ namespace BizHawk.Client.Common
return false;
}
_ = game!; // shouldn't be null if `nextEmulator` isn't? just in case
if (game is null) throw new Exception("RomLoader returned core but no GameInfo"); // shouldn't be null if `nextEmulator` isn't? just in case
}
catch (Exception ex)
{

View File

@ -63,9 +63,7 @@ namespace BizHawk.Client.EmuHawk
tuple =>
{
var instance = tuple.Ctor.Invoke(tuple.CtorTypes.Select(t => avail[t]).ToArray());
var success = ServiceInjector.UpdateServices(serviceProvider, instance, mayCache: true);
Debug.Assert(success);
_ = success;
if (!ServiceInjector.UpdateServices(serviceProvider, instance, mayCache: true)) throw new Exception("ApiHawk impl. has required service(s) that can't be fulfilled");
return (IExternalApi) instance;
}));
}

View File

@ -66,9 +66,7 @@ namespace BizHawk.Client.EmuHawk
|| lib.GetCustomAttribute<LuaLibraryAttribute>(inherit: false)?.Released is not false)
{
var instance = (LuaLibraryBase)Activator.CreateInstance(lib, this, _apiContainer, (Action<string>)LogToLuaConsole);
var updateSuccess = ServiceInjector.UpdateServices(serviceProvider, instance, mayCache: true);
Debug.Assert(updateSuccess);
_ = updateSuccess;
if (!ServiceInjector.UpdateServices(serviceProvider, instance, mayCache: true)) throw new Exception("Lua lib has required service(s) that can't be fulfilled");
// TODO: make EmuHawk libraries have a base class with common properties such as this
// and inject them here