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) --> <!-- Don't call this.GetType(), use typeof operator (or replace subtype check with better encapsulation) -->
<Rule Id="BHI1101" Action="Error" /> <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] --> <!-- Throw NotImplementedException from methods/props marked [FeatureNotImplemented] -->
<Rule Id="BHI3300" Action="Error" /> <Rule Id="BHI3300" Action="Error" />
</Rules> </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; if (newTool is Form form) form.Owner = _owner;
ServiceInjector.UpdateServices(_emulator.ServiceProvider, newTool); ServiceInjector.UpdateServices(_emulator.ServiceProvider, newTool);
SetBaseProperties(newTool); SetBaseProperties(newTool);
var toolTypeName = typeof(T).ToString(); var toolTypeName = typeof(T).FullName!;
// auto settings // auto settings
if (newTool is IToolFormAutoConfig autoConfigTool) if (newTool is IToolFormAutoConfig autoConfigTool)
{ {

View File

@ -347,9 +347,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
catch (Exception ex) catch (Exception ex)
{ {
// exception during operation // exception during operation
var e = ex; 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);
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());
} }
} }
} }

View File

@ -775,8 +775,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
if (!found) if (!found)
{ {
throw new Exception(typeof(DriveState).ToString() + throw new Exception($"{nameof(DriveState)}\n\nDisk image file could not be parsed. Potentially an unknown format.");
"\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> /// </summary>
public virtual bool IsWriter => false; public virtual bool IsWriter => false;
protected abstract Type SelfType { get; } protected abstract string SelfTypeName { get; }
/// <summary> /// <summary>
/// Serialization method /// Serialization method
/// </summary> /// </summary>
public virtual void Read(byte[] data) public virtual void Read(byte[] data)
{ => throw new NotImplementedException($"Read operation is not implemented for {SelfTypeName}");
throw new NotImplementedException(SelfType +
"Read operation is not implemented for this converter");
}
/// <summary> /// <summary>
/// DeSerialization method /// DeSerialization method
/// </summary> /// </summary>
public virtual void Write(byte[] data) public virtual void Write(byte[] data)
{ => throw new NotImplementedException($"Write operation is not implemented for {SelfTypeName}");
throw new NotImplementedException(SelfType +
"Write operation is not implemented for this converter");
}
/// <summary> /// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type /// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary> /// </summary>
public virtual bool CheckType(byte[] data) public virtual bool CheckType(byte[] data)
{ => throw new NotImplementedException($"Check type operation is not implemented for {SelfTypeName}");
throw new NotImplementedException(SelfType.ToString() +
"Check type operation is not implemented for this converter");
}
/// <summary> /// <summary>
/// Converts an int32 value into a byte array /// Converts an int32 value into a byte array

View File

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

View File

@ -311,9 +311,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex) catch (Exception ex)
{ {
// exception during operation // exception during operation
var e = ex; 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);
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());
} }
} }
@ -331,9 +329,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex) catch (Exception ex)
{ {
// exception during operation // exception during operation
var e = ex; 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);
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());
} }
} }
@ -351,9 +347,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex) catch (Exception ex)
{ {
// exception during operation // exception during operation
var e = ex; 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);
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());
} }
} }
@ -371,9 +365,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex) catch (Exception ex)
{ {
// exception during operation // exception during operation
var e = ex; 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);
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());
} }
} }
@ -390,9 +382,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
catch (Exception ex) catch (Exception ex)
{ {
// exception during operation // exception during operation
var e = ex; 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);
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());
} }
} }
} }

View File

@ -776,8 +776,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (!found) if (!found)
{ {
throw new Exception(typeof(DriveState).ToString() + throw new Exception($"{nameof(DriveState)}\n\nDisk image file could not be parsed. Potentially an unknown format.");
"\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> /// </summary>
public virtual bool IsWriter => false; public virtual bool IsWriter => false;
protected abstract Type SelfType { get; } protected abstract string SelfTypeName { get; }
/// <summary> /// <summary>
/// Serialization method /// Serialization method
/// </summary> /// </summary>
public virtual void Read(byte[] data) public virtual void Read(byte[] data)
{ => throw new NotImplementedException($"Read operation is not implemented for {SelfTypeName}");
throw new NotImplementedException(SelfType.ToString() +
"Read operation is not implemented for this converter");
}
/// <summary> /// <summary>
/// DeSerialization method /// DeSerialization method
/// </summary> /// </summary>
public virtual void Write(byte[] data) public virtual void Write(byte[] data)
{ => throw new NotImplementedException($"Write operation is not implemented for {SelfTypeName}");
throw new NotImplementedException(SelfType.ToString() +
"Write operation is not implemented for this converter");
}
/// <summary> /// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type /// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary> /// </summary>
public virtual bool CheckType(byte[] data) public virtual bool CheckType(byte[] data)
{ => throw new NotImplementedException($"Check type operation is not implemented for {SelfTypeName}");
throw new NotImplementedException(SelfType.ToString() +
"Check type operation is not implemented for this converter");
}
/// <summary> /// <summary>
/// Converts an int32 value into a byte array /// Converts an int32 value into a byte array

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -293,7 +293,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
private void CheckDisposed() private void CheckDisposed()
{ {
if (Context == IntPtr.Zero) 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. // 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) 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]; data[statenum] ??= new byte[length];
@ -60,7 +60,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public unsafe void Cmp(int statex, int statey) 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>>(); List<Tuple<int, int>> bads = new List<Tuple<int, int>>();
byte[] x = data[statex]; byte[] x = data[statex];
@ -134,7 +134,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public List<Symbol> Find(IntPtr addr, int length) 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 min = new Symbol { addr = addr };
Symbol max = new Symbol { addr = addr + length }; Symbol max = new Symbol { addr = addr + length };