Add Analyzer rule to prohibit `typeof(T).ToString()`

This commit is contained in:
YoshiRulz 2022-07-19 07:15:53 +10:00
parent f3f90a4cd5
commit a2fef59fe1
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
18 changed files with 116 additions and 81 deletions

View File

@ -22,6 +22,12 @@
<!-- Don't call this.GetType(), use typeof operator (or replace subtype check with better encapsulation) -->
<Rule Id="BHI1101" Action="Error" />
<!-- Don't call typeof(T).Name, use nameof operator -->
<Rule Id="BHI1102" Action="Error" />
<!-- Don't call typeof(T).ToString(), use nameof operator or typeof(T).FullName -->
<Rule Id="BHI1103" Action="Error" />
<!-- Throw NotImplementedException from methods/props marked [FeatureNotImplemented] -->
<Rule Id="BHI3300" Action="Error" />
</Rules>

View File

@ -0,0 +1,69 @@
namespace BizHawk.Analyzers;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class UseNameofOperatorAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor DiagNoToStringOnType = new(
id: "BHI1103",
title: "Don't call typeof(T).ToString(), use nameof operator or typeof(T).FullName",
messageFormat: "Replace typeof({0}){1} with either nameof({0}) or typeof({0}).FullName",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
private static readonly DiagnosticDescriptor DiagUseNameof = new(
id: "BHI1102",
title: "Don't call typeof(T).Name, use nameof operator",
messageFormat: "Replace typeof({0}).Name with nameof({0})",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(DiagNoToStringOnType, DiagUseNameof);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
ISymbol? memberInfoDotNameSym = null;
ISymbol? typeDotToStringSym = null;
context.RegisterSyntaxNodeAction(
snac =>
{
memberInfoDotNameSym ??= snac.Compilation.GetTypeByMetadataName("System.Reflection.MemberInfo")!.GetMembers("Name")[0];
typeDotToStringSym ??= snac.Compilation.GetTypeByMetadataName("System.Type")!.GetMembers("ToString")[0];
var toes = (TypeOfExpressionSyntax) snac.Node;
switch (toes.Parent)
{
case BinaryExpressionSyntax bes:
if ((ReferenceEquals(toes, bes.Left) ? bes.Right : bes.Left) is LiteralExpressionSyntax { Token.RawKind: (int) SyntaxKind.StringLiteralToken })
{
snac.ReportDiagnostic(Diagnostic.Create(DiagNoToStringOnType, toes.GetLocation(), toes.Type.GetText(), " in string concatenation"));
}
break;
case InterpolationSyntax:
snac.ReportDiagnostic(Diagnostic.Create(DiagNoToStringOnType, toes.GetLocation(), toes.Type.GetText(), " in string interpolation"));
break;
case MemberAccessExpressionSyntax maes1:
var accessed = snac.SemanticModel.GetSymbolInfo(maes1.Name).Symbol;
if (memberInfoDotNameSym.Matches(accessed))
{
snac.ReportDiagnostic(Diagnostic.Create(DiagUseNameof, maes1.GetLocation(), toes.Type.GetText()));
}
else if (typeDotToStringSym.Matches(accessed))
{
snac.ReportDiagnostic(Diagnostic.Create(DiagNoToStringOnType, maes1.GetLocation(), toes.Type.GetText(), ".ToString()"));
}
break;
}
},
SyntaxKind.TypeOfExpression);
}
}

Binary file not shown.

View File

@ -131,7 +131,7 @@ namespace BizHawk.Client.EmuHawk
if (newTool is Form form) form.Owner = _owner;
ServiceInjector.UpdateServices(_emulator.ServiceProvider, newTool);
SetBaseProperties(newTool);
var toolTypeName = typeof(T).ToString();
var toolTypeName = typeof(T).FullName!;
// auto settings
if (newTool is IToolFormAutoConfig autoConfigTool)
{

View File

@ -347,9 +347,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
catch (Exception ex)
{
// exception during operation
var e = ex;
throw new Exception(typeof(DatacorderDevice).ToString() +
"\n\nTape image file has a valid CDT header, but threw an exception whilst data was being parsed.\n\n" + e.ToString());
throw new Exception($"{nameof(DatacorderDevice)}\n\nTape image file has a valid CDT header, but threw an exception whilst data was being parsed.\n\n{ex}", ex);
}
}
}

View File

@ -775,8 +775,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
if (!found)
{
throw new Exception(typeof(DriveState).ToString() +
"\n\nDisk image file could not be parsed. Potentially an unknown format.");
throw new Exception($"{nameof(DriveState)}\n\nDisk image file could not be parsed. Potentially an unknown format.");
}
}

View File

@ -24,34 +24,25 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary>
public virtual bool IsWriter => false;
protected abstract Type SelfType { get; }
protected abstract string SelfTypeName { get; }
/// <summary>
/// Serialization method
/// </summary>
public virtual void Read(byte[] data)
{
throw new NotImplementedException(SelfType +
"Read operation is not implemented for this converter");
}
=> throw new NotImplementedException($"Read operation is not implemented for {SelfTypeName}");
/// <summary>
/// DeSerialization method
/// </summary>
public virtual void Write(byte[] data)
{
throw new NotImplementedException(SelfType +
"Write operation is not implemented for this converter");
}
=> throw new NotImplementedException($"Write operation is not implemented for {SelfTypeName}");
/// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary>
public virtual bool CheckType(byte[] data)
{
throw new NotImplementedException(SelfType.ToString() +
"Check type operation is not implemented for this converter");
}
=> throw new NotImplementedException($"Check type operation is not implemented for {SelfTypeName}");
/// <summary>
/// Converts an int32 value into a byte array

View File

@ -26,8 +26,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary>
public override bool IsWriter => false;
protected override Type SelfType
=> typeof(CdtConverter);
protected override string SelfTypeName
=> nameof(CdtConverter);
/// <summary>
/// Working list of generated tape data blocks
@ -167,8 +167,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
if (ident != "ZXTape!" || eotm != 0x1A)
{
// this is not a valid TZX format file
throw new Exception(typeof(CdtConverter) +
"This is not a valid TZX format file");
throw new Exception($"{nameof(CdtConverter)}: This is not a valid TZX format file");
}
// iterate through each block

View File

@ -311,9 +311,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex)
{
// exception during operation
var e = ex;
throw new Exception(typeof(DatacorderDevice).ToString() +
"\n\nTape image file has a valid TZX header, but threw an exception whilst data was being parsed.\n\n" + e.ToString());
throw new Exception($"{nameof(DatacorderDevice)}\n\nTape image file has a valid TZX header, but threw an exception whilst data was being parsed.\n\n{ex}", ex);
}
}
@ -331,9 +329,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex)
{
// exception during operation
var e = ex;
throw new Exception(typeof(DatacorderDevice).ToString() +
"\n\nTape image file has a valid PZX header, but threw an exception whilst data was being parsed.\n\n" + e.ToString());
throw new Exception($"{nameof(DatacorderDevice)}\n\nTape image file has a valid PZX header, but threw an exception whilst data was being parsed.\n\n{ex}", ex);
}
}
@ -351,9 +347,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex)
{
// exception during operation
var e = ex;
throw new Exception(typeof(DatacorderDevice).ToString() +
"\n\nTape image file has a valid CSW header, but threw an exception whilst data was being parsed.\n\n" + e.ToString());
throw new Exception($"{nameof(DatacorderDevice)}\n\nTape image file has a valid CSW header, but threw an exception whilst data was being parsed.\n\n{ex}", ex);
}
}
@ -371,9 +365,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex)
{
// exception during operation
var e = ex;
throw new Exception(typeof(DatacorderDevice).ToString() +
"\n\nTape image file has a valid WAV header, but threw an exception whilst data was being parsed.\n\n" + e.ToString());
throw new Exception($"{nameof(DatacorderDevice)}\n\nTape image file has a valid WAV header, but threw an exception whilst data was being parsed.\n\n{ex}", ex);
}
}
@ -390,9 +382,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex)
{
// exception during operation
var e = ex;
throw new Exception(typeof(DatacorderDevice).ToString() +
"\n\nAn exception was thrown whilst data from this tape image was being parsed as TAP.\n\n" + e.ToString());
throw new Exception($"{nameof(DatacorderDevice)}\n\nAn exception was thrown whilst data from this tape image was being parsed as TAP.\n\n{ex}", ex);
}
}
}

View File

@ -776,8 +776,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (!found)
{
throw new Exception(typeof(DriveState).ToString() +
"\n\nDisk image file could not be parsed. Potentially an unknown format.");
throw new Exception($"{nameof(DriveState)}\n\nDisk image file could not be parsed. Potentially an unknown format.");
}
}

View File

@ -26,34 +26,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public virtual bool IsWriter => false;
protected abstract Type SelfType { get; }
protected abstract string SelfTypeName { get; }
/// <summary>
/// Serialization method
/// </summary>
public virtual void Read(byte[] data)
{
throw new NotImplementedException(SelfType.ToString() +
"Read operation is not implemented for this converter");
}
=> throw new NotImplementedException($"Read operation is not implemented for {SelfTypeName}");
/// <summary>
/// DeSerialization method
/// </summary>
public virtual void Write(byte[] data)
{
throw new NotImplementedException(SelfType.ToString() +
"Write operation is not implemented for this converter");
}
=> throw new NotImplementedException($"Write operation is not implemented for {SelfTypeName}");
/// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary>
public virtual bool CheckType(byte[] data)
{
throw new NotImplementedException(SelfType.ToString() +
"Check type operation is not implemented for this converter");
}
=> throw new NotImplementedException($"Check type operation is not implemented for {SelfTypeName}");
/// <summary>
/// Converts an int32 value into a byte array

View File

@ -32,8 +32,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public override bool IsWriter => false;
protected override Type SelfType
=> typeof(CswConverter);
protected override string SelfTypeName
=> nameof(CswConverter);
private readonly DatacorderDevice _datacorder;
@ -83,15 +83,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (ident.ToUpper() != "COMPRESSED SQUARE WAVE")
{
// this is not a valid CSW format file
throw new Exception(typeof(CswConverter).ToString() +
"This is not a valid CSW format file");
throw new Exception($"{nameof(CswConverter)}: This is not a valid CSW format file");
}
if (data[0x16] != 0x1a)
{
// invalid terminator code
throw new Exception(typeof(CswConverter).ToString() +
"This image reports as a CSW but has an invalid terminator code");
throw new Exception($"{nameof(CswConverter)}: This image reports as a CSW but has an invalid terminator code");
}
_position = 0;
@ -185,13 +183,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (compressionType == 1)
Array.Copy(data, _position, cswDataUncompressed, 0, cswDataUncompressed.Length);
else
throw new Exception(typeof(CswConverter).ToString() +
"CSW Format unknown compression type");
throw new Exception($"{nameof(CswConverter)}: CSW Format unknown compression type");
}
else
{
throw new Exception(typeof(CswConverter).ToString() +
"CSW Format Version " + majorVer + "." + minorVer + " is not currently supported");
throw new Exception($"{nameof(CswConverter)}: CSW Format Version {majorVer}.{minorVer} is not currently supported");
}
// create the single tape block

View File

@ -27,8 +27,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public override bool IsWriter => false;
protected override Type SelfType
=> typeof(PzxConverter);
protected override string SelfTypeName
=> nameof(PzxConverter);
/// <summary>
/// Working list of generated tape data blocks
@ -99,8 +99,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (ident.ToUpper() != "PZXT")
{
// this is not a valid TZX format file
throw new Exception(typeof(PzxConverter).ToString() +
"This is not a valid PZX format file");
throw new Exception($"{nameof(PzxConverter)}: This is not a valid PZX format file");
}
_position = 0;

View File

@ -27,8 +27,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public override bool IsWriter => false;
protected override Type SelfType
=> typeof(TapConverter);
protected override string SelfTypeName
=> nameof(TapConverter);
private readonly DatacorderDevice _datacorder;

View File

@ -26,8 +26,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public override bool IsWriter => false;
protected override Type SelfType
=> typeof(TzxConverter);
protected override string SelfTypeName
=> nameof(TzxConverter);
/// <summary>
/// Working list of generated tape data blocks
@ -205,8 +205,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (ident != "ZXTape!" || eotm != 0x1A)
{
// this is not a valid TZX format file
throw new Exception(typeof(TzxConverter) +
"This is not a valid TZX format file");
throw new Exception($"{nameof(TzxConverter)}: This is not a valid TZX format file");
}
// iterate through each block

View File

@ -28,8 +28,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public override bool IsWriter => false;
protected override Type SelfType
=> typeof(WavConverter);
protected override string SelfTypeName
=> nameof(WavConverter);
/// <summary>
/// Position counter
@ -77,8 +77,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (ident.ToUpper() != "WAVE")
{
// this is not a valid TZX format file
throw new Exception(typeof(WavConverter).ToString() +
"This is not a valid WAV format file");
throw new Exception($"{nameof(WavConverter)}: This is not a valid WAV format file");
}
//_position = 0;

View File

@ -293,7 +293,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
private void CheckDisposed()
{
if (Context == IntPtr.Zero)
throw new ObjectDisposedException(typeof(QuickNES).Name);
throw new ObjectDisposedException(nameof(QuickNES));
}
// Fix some incorrect ines header entries that QuickNES uses to load games.

View File

@ -49,7 +49,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public void SaveState(int statenum)
{
if (disposed) throw new ObjectDisposedException(typeof(GenDbgHlp).ToString());
if (disposed) throw new ObjectDisposedException(nameof(GenDbgHlp));
data[statenum] ??= new byte[length];
@ -60,7 +60,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public unsafe void Cmp(int statex, int statey)
{
if (disposed) throw new ObjectDisposedException(typeof(GenDbgHlp).ToString());
if (disposed) throw new ObjectDisposedException(nameof(GenDbgHlp));
List<Tuple<int, int>> bads = new List<Tuple<int, int>>();
byte[] x = data[statex];
@ -134,7 +134,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public List<Symbol> Find(IntPtr addr, int length)
{
if (disposed) throw new ObjectDisposedException(typeof(GenDbgHlp).ToString());
if (disposed) throw new ObjectDisposedException(nameof(GenDbgHlp));
Symbol min = new Symbol { addr = addr };
Symbol max = new Symbol { addr = addr + length };