Improve highlight locations of some BizHawk.Analyzer diagnostics

This commit is contained in:
YoshiRulz 2025-03-20 07:05:22 +10:00
parent df11522190
commit ca0ddeffef
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
15 changed files with 192 additions and 68 deletions

View File

@ -39,6 +39,50 @@ public static class DDExtensions
properties: null,
messageArgs: messageArgs));
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
Location location,
DiagnosticSeverity effectiveSeverity,
OAC ctx,
string message)
=> diag.ReportAt(location, effectiveSeverity, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
Location location,
DiagnosticSeverity effectiveSeverity,
SNAC ctx,
string message)
=> diag.ReportAt(location, effectiveSeverity, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
Location location,
bool isErrorSeverity,
OAC ctx,
object?[]? messageArgs = null)
=> diag.ReportAt(
location,
isErrorSeverity ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
ctx,
messageArgs: messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
Location location,
bool isErrorSeverity,
SNAC ctx,
object?[]? messageArgs = null)
=> diag.ReportAt(
location,
isErrorSeverity ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
ctx,
messageArgs: messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, Location location, OAC ctx, object?[]? messageArgs = null)
=> ctx.ReportDiagnostic(Diagnostic.Create(diag, location, messageArgs: messageArgs));
@ -47,6 +91,14 @@ public static class DDExtensions
public static void ReportAt(this DD diag, Location location, SNAC ctx, object?[]? messageArgs = null)
=> ctx.ReportDiagnostic(Diagnostic.Create(diag, location, messageArgs: messageArgs));
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, Location location, OAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, Location location, SNAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
@ -123,6 +175,40 @@ public static class DDExtensions
public static void ReportAt(this DD diag, SyntaxNode location, SNAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
SyntaxToken location,
DiagnosticSeverity effectiveSeverity,
OAC ctx,
string message)
=> diag.ReportAt(location.GetLocation(), effectiveSeverity, ctx, message);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
SyntaxToken location,
DiagnosticSeverity effectiveSeverity,
SNAC ctx,
string message)
=> diag.ReportAt(location.GetLocation(), effectiveSeverity, ctx, message);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxToken location, OAC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(location.GetLocation(), ctx, messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxToken location, SNAC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(location.GetLocation(), ctx, messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxToken location, OAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxToken location, SNAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,

View File

@ -90,6 +90,19 @@ public static class RoslynUtils
public static bool IsEmpty(this SyntaxToken token)
=> token.ToString().Length is 0;
public static Location LocWithoutReceiver(this InvocationExpressionSyntax ies)
{
var location = ies.GetLocation();
if (ies.Expression is MemberAccessExpressionSyntax maes)
{
location = Location.Create(location.SourceTree!, location.SourceSpan.Slice(maes.Expression.Span.Length));
}
return location;
}
public static Location LocWithoutReceiver(this IInvocationOperation operation)
=> ((InvocationExpressionSyntax) operation.Syntax).LocWithoutReceiver();
public static bool Matches(this ISymbol expected, ISymbol? actual)
=> SymbolEqualityComparer.Default.Equals(expected, actual);
@ -118,4 +131,10 @@ public static class RoslynUtils
INamedTypeSymbol targetAttrSym,
SyntaxNodeAnalysisContext snac)
=> list.Matching(targetAttrSym, snac.SemanticModel, snac.CancellationToken);
public static TextSpan Slice(this TextSpan span, int start)
=> TextSpan.FromBounds(start: span.Start + start, end: span.End);
public static TextSpan Slice(this TextSpan span, int start, int length)
=> new(start: span.Start + start, length: length);
}

View File

@ -45,6 +45,7 @@ public sealed class DefineCheckedOpAnalyzer : DiagnosticAnalyzer
obsoleteAttrSym ??= snac.Compilation.GetTypeByMetadataName("System.ObsoleteAttribute")!;
void FindCounterpartAndMaybeReport<T>(
T declSyn,
SyntaxToken operatorTkn,
SyntaxList<AttributeListSyntax> attrLists,
string checkedName,
Func<T, string> getMethodName)
@ -54,11 +55,11 @@ public sealed class DefineCheckedOpAnalyzer : DiagnosticAnalyzer
.Any(syn => checkedName.Equals(getMethodName(syn), StringComparison.Ordinal));
if (!hasCheckedCounterpart)
{
DiagDefineCheckedOp.ReportAt(declSyn, snac, ERR_MSG_MAKE_CHECKED);
DiagDefineCheckedOp.ReportAt(operatorTkn, snac, ERR_MSG_MAKE_CHECKED);
}
else if (!attrLists.Matching(obsoleteAttrSym, snac).Any())
{
DiagDefineCheckedOp.ReportAt(declSyn, DiagnosticSeverity.Warning, snac, ERR_MSG_DEPRECATE);
DiagDefineCheckedOp.ReportAt(operatorTkn, DiagnosticSeverity.Warning, snac, ERR_MSG_DEPRECATE);
}
// else usage is correct
}
@ -69,6 +70,7 @@ public sealed class DefineCheckedOpAnalyzer : DiagnosticAnalyzer
{
FindCounterpartAndMaybeReport(
cods,
cods.OperatorKeyword,
cods.AttributeLists,
checkedName,
RoslynUtils.GetMethodName);
@ -79,6 +81,7 @@ public sealed class DefineCheckedOpAnalyzer : DiagnosticAnalyzer
{
FindCounterpartAndMaybeReport(
ods,
ods.OperatorKeyword,
ods.AttributeLists,
checkedName1,
RoslynUtils.GetMethodName);

View File

@ -48,12 +48,12 @@ public sealed class FeatureNotImplementedAnalyzer : DiagnosticAnalyzer
void CheckBlockBody(BlockSyntax bs)
{
if (bs.Statements is [ ThrowStatementSyntax tss ]) MaybeReportFor(snac.SemanticModel.GetThrownExceptionType(tss), tss);
else DiagShouldThrowNIE.ReportAt(bs.Parent!, snac, ERR_MSG_DOES_NOT_THROW);
else DiagShouldThrowNIE.ReportAt(bs, snac, ERR_MSG_DOES_NOT_THROW);
}
void CheckExprBody(ArrowExpressionClauseSyntax aecs)
{
if (aecs.Expression is ThrowExpressionSyntax tes) MaybeReportFor(snac.SemanticModel.GetThrownExceptionType(tes), tes);
else DiagShouldThrowNIE.ReportAt(aecs.Parent!, snac, ERR_MSG_DOES_NOT_THROW);
else DiagShouldThrowNIE.ReportAt(aecs, snac, ERR_MSG_DOES_NOT_THROW);
}
void CheckAccessor(AccessorDeclarationSyntax ads)
{

View File

@ -48,7 +48,7 @@ public sealed class FirstOrDefaultOnStructAnalyzer : DiagnosticAnalyzer
IArrayTypeSymbol ats => ats.ElementType,
_ => throw HawkSourceAnalyzer.ReportWTF(receiverExpr, oac, message: $"[{nameof(FirstOrDefaultOnStructAnalyzer)}] receiver parameter's effective type was of an unexpected kind (neither class/struct nor array): {receiverExprType.GetType().FullName}")
};
if (collectionElemType.IsValueType) DiagUseFirstOrNull.ReportAt(operation, oac);
if (collectionElemType.IsValueType) DiagUseFirstOrNull.ReportAt(operation.LocWithoutReceiver(), oac);
},
OperationKind.Invocation);
});

View File

@ -158,7 +158,15 @@ public class HawkSourceAnalyzer : DiagnosticAnalyzer
void MaybeReportListExprSpacing(SyntaxNode listSyn, string? message)
{
if (message is null) return;
DiagListExprSpacing.ReportAt(listSyn, snac, message);
var location = listSyn.GetLocation();
TextSpan? slice = message switch
{
ERR_MSG_LIST_EXPR_END => location.SourceSpan.Slice(start: location.SourceSpan.Length - 1),
ERR_MSG_LIST_EXPR_START => location.SourceSpan.Slice(start: 0, length: 1),
_ => null
};
if (slice is not null) location = Location.Create(location.SourceTree!, slice.Value);
DiagListExprSpacing.ReportAt(location, snac, message);
}
switch (snac.Node)
{
@ -179,7 +187,8 @@ public class HawkSourceAnalyzer : DiagnosticAnalyzer
CheckSpacingInList(ces.Elements, ces.OpenBracketToken, ces.ToString));
break;
case InterpolatedStringExpressionSyntax ises:
if (ises.StringStartToken.Text[0] is '@') DiagInterpStringIsDollarAt.ReportAt(ises, snac);
var interpTkn = ises.StringStartToken;
if (interpTkn.Text[0] is '@') DiagInterpStringIsDollarAt.ReportAt(interpTkn, snac);
break;
case ListPatternSyntax lps:
MaybeReportListExprSpacing(
@ -190,7 +199,7 @@ public class HawkSourceAnalyzer : DiagnosticAnalyzer
DiagNoQueryExpression.ReportAt(snac.Node, snac);
break;
case RecordDeclarationSyntax rds when rds.ClassOrStructKeyword.ToString() is not "class": // `record struct`s don't use this kind
DiagRecordImplicitlyRefType.ReportAt(rds, snac);
DiagRecordImplicitlyRefType.ReportAt(rds.Keyword, snac);
break;
case SwitchExpressionArmSyntax { WhenClause: null, Pattern: DiscardPatternSyntax, Expression: ThrowExpressionSyntax tes }:
var thrownExceptionType = snac.SemanticModel.GetThrownExceptionType(tes);

View File

@ -119,7 +119,11 @@ public sealed class LINQOnStringsAnalyzer : DiagnosticAnalyzer
: "Use `str.Contains(c.ToString())`";
break;
}
DiagLINQOnStrings.ReportAt(operation, level, oac, string.Format(msgFmtStr, receiverExpr.Syntax));
DiagLINQOnStrings.ReportAt(
operation.LocWithoutReceiver(),
level,
oac,
string.Format(msgFmtStr, receiverExpr.Syntax));
},
OperationKind.Invocation);
});

View File

@ -39,7 +39,10 @@ public sealed class TryGetValueImplicitDiscardAnalyzer : DiagnosticAnalyzer
var calledSym = operation.TargetMethod.ConstructedFrom;
if (calledSym.Name is STR_TGV)
{
DiagUncheckedTryGetValue.ReportAt(operation, isErrorSeverity: IsBCLTryGetValue(calledSym), oac);
DiagUncheckedTryGetValue.ReportAt(
operation.LocWithoutReceiver(),
isErrorSeverity: IsBCLTryGetValue(calledSym),
oac);
}
},
OperationKind.Invocation);

View File

@ -19,23 +19,23 @@ public sealed class DefineCheckedOpAnalyzerTests
public struct GF7 {
private static readonly int[] Inverses = [ -1, 1, 4, 5, 2, 3, 6 ];
public static GF7 operator checked -(GF7 x) => new(-x._value); // the most useless but here for completeness
{|BHI1300:public static GF7 operator /*unchecked*/ -(GF7 x) => new(-x._value % 7);|}
public static GF7 {|BHI1300:operator|} /*unchecked*/ -(GF7 x) => new(-x._value % 7);
public static GF7 operator checked ++(GF7 x) => new(x._value + 1);
{|BHI1300:public static GF7 operator /*unchecked*/ ++(GF7 x) => new((x._value + 1) % 7);|}
public static GF7 {|BHI1300:operator|} /*unchecked*/ ++(GF7 x) => new((x._value + 1) % 7);
public static GF7 operator checked --(GF7 x) => new(x._value - 1);
{|BHI1300:public static GF7 operator /*unchecked*/ --(GF7 x) => new((x._value - 1) % 7);|}
public static GF7 {|BHI1300:operator|} /*unchecked*/ --(GF7 x) => new((x._value - 1) % 7);
public static GF7 operator checked +(GF7 x, GF7 y) => new(x._value + y._value);
{|BHI1300:public static GF7 operator /*unchecked*/ +(GF7 x, GF7 y) => new((x._value + y._value) % 7);|}
public static GF7 {|BHI1300:operator|} /*unchecked*/ +(GF7 x, GF7 y) => new((x._value + y._value) % 7);
public static GF7 operator checked +(GF7 x, int y) => new(x._value + y);
{|BHI1300:public static GF7 operator /*unchecked*/ +(GF7 x, int y) => new((x._value + y) % 7);|}
public static GF7 {|BHI1300:operator|} /*unchecked*/ +(GF7 x, int y) => new((x._value + y) % 7);
public static GF7 operator checked -(GF7 minuend, int subtrahend) => new(minuend._value - subtrahend);
{|BHI1300:public static GF7 operator /*unchecked*/ -(GF7 minuend, int subtrahend) => new((minuend._value - subtrahend) % 7);|}
public static GF7 {|BHI1300:operator|} /*unchecked*/ -(GF7 minuend, int subtrahend) => new((minuend._value - subtrahend) % 7);
public static GF7 operator checked *(GF7 x, int y) => new(x._value * y);
{|BHI1300:public static GF7 operator /*unchecked*/ *(GF7 x, int y) => new((x._value * y) % 7);|}
public static GF7 {|BHI1300:operator|} /*unchecked*/ *(GF7 x, int y) => new((x._value * y) % 7);
public static GF7 operator checked /(GF7 dividend, GF7 divisor) => new((dividend._value * Inverses[divisor._value]) % 7);
{|BHI1300:public static GF7 operator /*unchecked*/ /(GF7 dividend, GF7 divisor) => divisor._value is 0 ? default : new((dividend._value * Inverses[divisor._value]) % 7);|}
public static GF7 {|BHI1300:operator|} /*unchecked*/ /(GF7 dividend, GF7 divisor) => divisor._value is 0 ? default : new((dividend._value * Inverses[divisor._value]) % 7);
public static explicit operator checked GF7(int n) => new(n);
{|BHI1300:public static explicit operator /*unchecked*/ GF7(int n) => new(n % 7);|}
public static explicit {|BHI1300:operator|} /*unchecked*/ GF7(int n) => new(n % 7);
private byte _value;
public byte Value {
get => _value;
@ -55,15 +55,15 @@ public sealed class DefineCheckedOpAnalyzerTests
using System;
public struct GF7 {
private static readonly int[] Inverses = [ -1, 1, 4, 5, 2, 3, 6 ];
{|BHI1300:public static GF7 operator -(GF7 x) => new(-x._value % 7);|}
{|BHI1300:public static GF7 operator ++(GF7 x) => new((x._value + 1) % 7);|}
{|BHI1300:public static GF7 operator --(GF7 x) => new((x._value - 1) % 7);|}
{|BHI1300:public static GF7 operator +(GF7 x, GF7 y) => new((x._value + y._value) % 7);|}
{|BHI1300:public static GF7 operator +(GF7 x, int y) => new((x._value + y) % 7);|}
{|BHI1300:public static GF7 operator -(GF7 minuend, int subtrahend) => new((minuend._value - subtrahend) % 7);|}
{|BHI1300:public static GF7 operator *(GF7 x, int y) => new((x._value * y) % 7);|}
{|BHI1300:public static GF7 operator /(GF7 dividend, GF7 divisor) => divisor._value is 0 ? default : new((dividend._value * Inverses[divisor._value]) % 7);|}
{|BHI1300:public static explicit operator GF7(int n) => new(n % 7);|}
public static GF7 {|BHI1300:operator|} -(GF7 x) => new(-x._value % 7);
public static GF7 {|BHI1300:operator|} ++(GF7 x) => new((x._value + 1) % 7);
public static GF7 {|BHI1300:operator|} --(GF7 x) => new((x._value - 1) % 7);
public static GF7 {|BHI1300:operator|} +(GF7 x, GF7 y) => new((x._value + y._value) % 7);
public static GF7 {|BHI1300:operator|} +(GF7 x, int y) => new((x._value + y) % 7);
public static GF7 {|BHI1300:operator|} -(GF7 minuend, int subtrahend) => new((minuend._value - subtrahend) % 7);
public static GF7 {|BHI1300:operator|} *(GF7 x, int y) => new((x._value * y) % 7);
public static GF7 {|BHI1300:operator|} /(GF7 dividend, GF7 divisor) => divisor._value is 0 ? default : new((dividend._value * Inverses[divisor._value]) % 7);
public static explicit {|BHI1300:operator|} GF7(int n) => new(n % 7);
private byte _value;
public byte Value {
get => _value;

View File

@ -20,13 +20,13 @@ public sealed class FeatureNotImplementedAnalyzerTests
}
[FeatureNotImplemented] private static int Z()
=> throw new NotImplementedException();
{|BHI3300:[FeatureNotImplemented] private static int A => default;|}
[FeatureNotImplemented] private static int A {|BHI3300:=> default|};
private static int B {
{|BHI3300:[FeatureNotImplemented] get => default;|}
{|BHI3300:[FeatureNotImplemented] set => _ = value;|}
[FeatureNotImplemented] get {|BHI3300:=> default|};
[FeatureNotImplemented] set {|BHI3300:=> _ = value|};
}
{|BHI3300:[FeatureNotImplemented] private static int C()
=> default;|}
[FeatureNotImplemented] private static int C()
{|BHI3300:=> default|};
// wrong exception type, same code but different message:
[FeatureNotImplemented] private static int D => {|BHI3300:throw new InvalidOperationException()|};
private static int E {

View File

@ -18,13 +18,13 @@ public sealed class FirstOrDefaultOnStructAnalyzerTests
private static string? Z()
=> new List<int> { 0x80, 0x20, 0x40 }.Select(static n => n.ToString()).FirstOrDefault();
private static int A()
=> {|BHI3100:new[] { 0x80, 0x20, 0x40 }.FirstOrDefault()|};
=> new[] { 0x80, 0x20, 0x40 }{|BHI3100:.FirstOrDefault()|};
private static int B()
=> {|BHI3100:new List<int> { 0x80, 0x20, 0x40 }.FirstOrDefault()|};
=> new List<int> { 0x80, 0x20, 0x40 }{|BHI3100:.FirstOrDefault()|};
private static int C()
=> {|BHI3100:new[] { 0x80, 0x20, 0x40 }.FirstOrDefault(static n => n.ToString().Length > 2)|};
=> new[] { 0x80, 0x20, 0x40 }{|BHI3100:.FirstOrDefault(static n => n.ToString().Length > 2)|};
private static int D()
=> {|BHI3100:new List<int> { 0x80, 0x20, 0x40 }.FirstOrDefault(static n => n.ToString().Length > 2)|};
=> new List<int> { 0x80, 0x20, 0x40 }{|BHI3100:.FirstOrDefault(static n => n.ToString().Length > 2)|};
}
namespace BizHawk.Common.CollectionExtensions {
public static class CollectionExtensions {} // Analyzer short-circuits if this doesn't exist, since that's where the extension lives

View File

@ -106,7 +106,7 @@ public sealed class HawkSourceAnalyzerTests
=> Verify.VerifyAnalyzerAsync("""
public static class Cases {
private static readonly int Z = $@"{0x100}".Length;
private static readonly int A = {|BHI1004:@$"{0x100}"|}.Length;
private static readonly int A = {|BHI1004:@$"|}{0x100}".Length;
}
""");
@ -119,9 +119,9 @@ public sealed class HawkSourceAnalyzerTests
private static readonly int[] X = W ? [ ] : V;
private static readonly int[] Y = [ 0x80, 0x20, 0x40 ];
private static readonly bool Z = Y is [ _, > 20, .. ];
private static readonly int[] A = {|BHI1110:[0x80, 0x20, 0x40 ]|};
private static readonly bool B = A is {|BHI1110:[ _, > 20, ..]|};
private static readonly bool C = A is {|BHI1110:[_, > 20, ..]|};
private static readonly int[] A = {|BHI1110:[|}0x80, 0x20, 0x40 ];
private static readonly bool B = A is [ _, > 20, ..{|BHI1110:]|};
private static readonly bool C = A is [_, > 20, ..{|BHI1110:]|}; // the way this is written, it will flag end and then start
private static readonly int[] D = {|BHI1110:[]|};
private static readonly bool E = D is {|BHI1110:[]|};
private static readonly int[] F = E ? {|BHI1110:[]|} : D;
@ -133,7 +133,7 @@ public sealed class HawkSourceAnalyzerTests
=> Verify.VerifyAnalyzerAsync("""
internal record struct Y {}
internal record class Z {}
{|BHI1130:internal record A {}|}
internal {|BHI1130:record|} A {}
""");
[TestMethod]

View File

@ -36,49 +36,49 @@ public sealed class LINQOnStringsAnalyzerTests
private static bool ZZ(string str)
=> str.All(DummyPredicate);
private static bool AA(string str)
=> {|BHI3102:str.Any()|};
=> str{|BHI3102:.Any()|};
private static IEnumerable<char> AB(string str)
=> {|BHI3102:str.Append('.')|};
=> str{|BHI3102:.Append('.')|};
private static IEnumerable<char> AC(string str)
=> {|BHI3102:str.Concat("-_-")|};
=> str{|BHI3102:.Concat("-_-")|};
private static IEnumerable<char> AD(string str)
=> {|BHI3102:str.Concat(new[] { '-', '_', '-' })|};
=> str{|BHI3102:.Concat(new[] { '-', '_', '-' })|};
private static bool AE(string str)
=> {|BHI3102:Enumerable.Contains(str, '.')|};
=> Enumerable{|BHI3102:.Contains(str, '.')|};
private static int AF(string str)
=> {|BHI3102:str.Count()|};
=> str{|BHI3102:.Count()|};
private static IEnumerable<char> AG(string str)
=> {|BHI3102:str.DefaultIfEmpty()|};
=> str{|BHI3102:.DefaultIfEmpty()|};
private static IEnumerable<char> AH(string str)
=> {|BHI3102:str.DefaultIfEmpty('.')|};
=> str{|BHI3102:.DefaultIfEmpty('.')|};
private static char AI(string str)
=> {|BHI3102:str.ElementAt(2)|};
=> str{|BHI3102:.ElementAt(2)|};
private static char AJ(string str)
=> {|BHI3102:str.ElementAtOrDefault(2)|};
=> str{|BHI3102:.ElementAtOrDefault(2)|};
private static char AK(string str)
=> {|BHI3102:str.First()|};
=> str{|BHI3102:.First()|};
private static char AL(string str)
=> {|BHI3102:str.FirstOrDefault()|};
=> str{|BHI3102:.FirstOrDefault()|};
private static char AM(string str)
=> {|BHI3102:str.Last()|};
=> str{|BHI3102:.Last()|};
private static char AN(string str)
=> {|BHI3102:str.LastOrDefault()|};
=> str{|BHI3102:.LastOrDefault()|};
private static long AO(string str)
=> {|BHI3102:str.LongCount()|};
=> str{|BHI3102:.LongCount()|};
private static IEnumerable<char> AP(string str)
=> {|BHI3102:str.Prepend('.')|};
=> str{|BHI3102:.Prepend('.')|};
private static IEnumerable<char> AQ(string str)
=> {|BHI3102:str.Reverse()|};
=> str{|BHI3102:.Reverse()|};
private static char AR(string str)
=> {|BHI3102:str.Single()|};
=> str{|BHI3102:.Single()|};
private static char AS(string str)
=> {|BHI3102:str.SingleOrDefault()|};
=> str{|BHI3102:.SingleOrDefault()|};
private static IEnumerable<char> AT(string str)
=> {|BHI3102:str.Skip(2)|};
=> str{|BHI3102:.Skip(2)|};
private static IEnumerable<char> AU(string str)
=> {|BHI3102:str.Take(2)|};
=> str{|BHI3102:.Take(2)|};
private static char[] AV(string str)
=> {|BHI3102:str.ToArray()|};
=> str{|BHI3102:.ToArray()|};
}
namespace BizHawk.Common.StringExtensions {
public static class StringExtensions {} // Analyzer does more checks if this exists

View File

@ -23,13 +23,13 @@ public sealed class TryGetValueImplicitDiscardAnalyzerTests
private static void Z()
=> _ = MakeDict().TryGetValue("z", out _);
private static void A()
=> {|BHI1200:MakeDict().TryGetValue("a", out _)|};
=> MakeDict(){|BHI1200:.TryGetValue("a", out _)|};
private static void B()
=> {|BHI1200:((IDictionary<string, int>) MakeDict()).TryGetValue("b", out _)|};
=> ((IDictionary<string, int>) MakeDict()){|BHI1200:.TryGetValue("b", out _)|};
private static void C()
=> {|BHI1200:((IReadOnlyDictionary<string, int>) MakeDict()).TryGetValue("c", out _)|};
=> ((IReadOnlyDictionary<string, int>) MakeDict()){|BHI1200:.TryGetValue("c", out _)|};
private static void D()
=> {|BHI1200:new CustomDict<string, int>().TryGetValue("d", out _)|};
=> new CustomDict<string, int>(){|BHI1200:.TryGetValue("d", out _)|};
}
""");
}

Binary file not shown.