Extract helpers for `Diagnostic.Create` in Analyzers

will rebuild in later commit
This commit is contained in:
YoshiRulz 2025-02-22 15:01:08 +10:00
parent 6eef3668a7
commit 9a388c6693
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
14 changed files with 214 additions and 86 deletions

View File

@ -0,0 +1,163 @@
namespace BizHawk.Analyzers;
using static System.Runtime.CompilerServices.MethodImplOptions;
using DD = Microsoft.CodeAnalysis.DiagnosticDescriptor;
using MI = System.Runtime.CompilerServices.MethodImplAttribute;
using OAC = Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext;
using SNAC = Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext;
public static class DDExtensions
{
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
Location location,
DiagnosticSeverity effectiveSeverity,
OAC ctx,
object?[]? messageArgs = null)
=> ctx.ReportDiagnostic(Diagnostic.Create(
diag,
location,
effectiveSeverity,
additionalLocations: null,
properties: null,
messageArgs: messageArgs));
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
Location location,
DiagnosticSeverity effectiveSeverity,
SNAC ctx,
object?[]? messageArgs = null)
=> ctx.ReportDiagnostic(Diagnostic.Create(
diag,
location,
effectiveSeverity,
additionalLocations: null,
properties: null,
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));
[MI(AggressiveInlining)]
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,
SyntaxNode location,
DiagnosticSeverity effectiveSeverity,
OAC ctx,
object?[]? messageArgs = null)
=> diag.ReportAt(location.GetLocation(), effectiveSeverity, ctx, messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
SyntaxNode location,
DiagnosticSeverity effectiveSeverity,
SNAC ctx,
object?[]? messageArgs = null)
=> diag.ReportAt(location.GetLocation(), effectiveSeverity, ctx, messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
SyntaxNode location,
DiagnosticSeverity effectiveSeverity,
OAC ctx,
string message)
=> diag.ReportAt(location, effectiveSeverity, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
SyntaxNode location,
DiagnosticSeverity effectiveSeverity,
SNAC ctx,
string message)
=> diag.ReportAt(location, effectiveSeverity, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, bool isErrorSeverity, OAC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(
location,
isErrorSeverity ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
ctx,
messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, bool isErrorSeverity, SNAC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(
location,
isErrorSeverity ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
ctx,
messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, bool isErrorSeverity, OAC ctx, string message)
=> diag.ReportAt(location, isErrorSeverity, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, bool isErrorSeverity, SNAC ctx, string message)
=> diag.ReportAt(location, isErrorSeverity, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, OAC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(location.GetLocation(), ctx, messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, SNAC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(location.GetLocation(), ctx, messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, SyntaxNode location, OAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);
[MI(AggressiveInlining)]
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,
IOperation location,
DiagnosticSeverity effectiveSeverity,
OAC ctx,
object?[]? messageArgs = null)
=> diag.ReportAt(location.Syntax, effectiveSeverity, ctx, messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(
this DD diag,
IOperation location,
DiagnosticSeverity effectiveSeverity,
OAC ctx,
string message)
=> diag.ReportAt(location, effectiveSeverity, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, IOperation location, bool isErrorSeverity, OAC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(
location,
isErrorSeverity ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
ctx,
messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, IOperation location, bool isErrorSeverity, OAC ctx, string message)
=> diag.ReportAt(location, isErrorSeverity, ctx, [ message ]);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, IOperation location, OAC ctx, object?[]? messageArgs = null)
=> diag.ReportAt(location.Syntax, ctx, messageArgs);
[MI(AggressiveInlining)]
public static void ReportAt(this DD diag, IOperation location, OAC ctx, string message)
=> diag.ReportAt(location, ctx, [ message ]);
}

View File

@ -47,14 +47,12 @@ public sealed class AmbiguousMoneyToFloatConversionAnalyzer : DiagnosticAnalyzer
}
var conversionSyn = conversionOp.Syntax;
//TODO check the suggested methods are accessible (i.e. BizHawk.Common is referenced)
oac.ReportDiagnostic(Diagnostic.Create(
DiagAmbiguousMoneyToFloatConversion,
(conversionSyn.Parent?.Kind() is SyntaxKind.CheckedExpression or SyntaxKind.UncheckedExpression
DiagAmbiguousMoneyToFloatConversion.ReportAt(
conversionSyn.Parent?.Kind() is SyntaxKind.CheckedExpression or SyntaxKind.UncheckedExpression
? conversionSyn.Parent
: conversionSyn).GetLocation(),
conversionOp.IsChecked ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
additionalLocations: null,
properties: null,
: conversionSyn,
isErrorSeverity: conversionOp.IsChecked,
oac,
messageArgs: isToDecimal
? [
$"new decimal({(isDoublePrecision ? "double" : "float")})", // "checked"
@ -63,7 +61,7 @@ public sealed class AmbiguousMoneyToFloatConversionAnalyzer : DiagnosticAnalyzer
: [
$"decimal.{(isDoublePrecision ? "ConvertToF64" : "ConvertToF32")} ext. (from NumberExtensions)", // "checked"
$"static Decimal.{(isDoublePrecision ? "ToDouble" : "ToSingle")}", // "unchecked"
]));
]);
},
OperationKind.Conversion);
}

View File

@ -36,8 +36,6 @@ public sealed class ExprBodiedMemberFlowAnalyzer : DiagnosticAnalyzer
HawkSourceAnalyzer.ReportWTF(aecs, snac, message: $"[{nameof(ExprBodiedMemberFlowAnalyzer)}] Syntax node for expression-bodied member was orphaned?");
return;
}
void Flag(string message)
=> snac.ReportDiagnostic(Diagnostic.Create(DiagExprBodiedMemberFlow, parent.GetLocation(), message));
switch (parent)
{
case MethodDeclarationSyntax:
@ -103,7 +101,7 @@ public sealed class ExprBodiedMemberFlowAnalyzer : DiagnosticAnalyzer
static string EscapeChar(char c)
=> c is '\n' ? "\\n" : c.ToString();
void Fail()
=> Flag($"Whitespace around {kind} arrow syntax should be `{EscapeChar(expectedWhitespace.Before)}=>{EscapeChar(expectedWhitespace.After)}`");
=> DiagExprBodiedMemberFlow.ReportAt(parent, snac, $"Whitespace around {kind} arrow syntax should be `{EscapeChar(expectedWhitespace.Before)}=>{EscapeChar(expectedWhitespace.After)}`");
if ((aecs.ArrowToken.HasLeadingTrivia ? '\n' : ' ') != expectedWhitespace.Before)
{
Fail();

View File

@ -37,10 +37,10 @@ public sealed class FeatureNotImplementedAnalyzer : DiagnosticAnalyzer
initContext.RegisterSyntaxNodeAction(
snac =>
{
void MaybeReportFor(ITypeSymbol? thrownExceptionType, Location location)
void MaybeReportFor(ITypeSymbol? thrownExceptionType, SyntaxNode location)
{
if (thrownExceptionType is null) snac.ReportDiagnostic(Diagnostic.Create(DiagShouldThrowNIE, location, ERR_MSG_METHOD_THROWS_UNKNOWN));
else if (!notImplementedExceptionSym.Matches(thrownExceptionType)) snac.ReportDiagnostic(Diagnostic.Create(DiagShouldThrowNIE, location, ERR_MSG_THROWS_WRONG_TYPE));
if (thrownExceptionType is null) DiagShouldThrowNIE.ReportAt(location, snac, ERR_MSG_METHOD_THROWS_UNKNOWN);
else if (!notImplementedExceptionSym.Matches(thrownExceptionType)) DiagShouldThrowNIE.ReportAt(location, snac, ERR_MSG_THROWS_WRONG_TYPE);
// else correct usage, do not flag
}
bool IncludesFNIAttribute(SyntaxList<AttributeListSyntax> mds)
@ -48,13 +48,13 @@ public sealed class FeatureNotImplementedAnalyzer : DiagnosticAnalyzer
.Any(aSyn => featureNotImplementedAttrSym.Matches(snac.SemanticModel.GetTypeInfo(aSyn, snac.CancellationToken).Type));
void CheckBlockBody(BlockSyntax bs, Location location)
{
if (bs.Statements is [ ThrowStatementSyntax tss ]) MaybeReportFor(snac.SemanticModel.GetThrownExceptionType(tss), tss.GetLocation());
else snac.ReportDiagnostic(Diagnostic.Create(DiagShouldThrowNIE, location, ERR_MSG_DOES_NOT_THROW));
if (bs.Statements is [ ThrowStatementSyntax tss ]) MaybeReportFor(snac.SemanticModel.GetThrownExceptionType(tss), tss);
else DiagShouldThrowNIE.ReportAt(location, snac, [ ERR_MSG_DOES_NOT_THROW ]);
}
void CheckExprBody(ArrowExpressionClauseSyntax aecs, Location location)
{
if (aecs.Expression is ThrowExpressionSyntax tes) MaybeReportFor(snac.SemanticModel.GetThrownExceptionType(tes), tes.GetLocation());
else snac.ReportDiagnostic(Diagnostic.Create(DiagShouldThrowNIE, location, ERR_MSG_DOES_NOT_THROW));
if (aecs.Expression is ThrowExpressionSyntax tes) MaybeReportFor(snac.SemanticModel.GetThrownExceptionType(tes), tes);
else DiagShouldThrowNIE.ReportAt(location, 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) oac.ReportDiagnostic(Diagnostic.Create(DiagUseFirstOrNull, operation.Syntax.GetLocation()));
if (collectionElemType.IsValueType) DiagUseFirstOrNull.ReportAt(operation, oac);
},
OperationKind.Invocation);
});

View File

@ -88,21 +88,27 @@ public class HawkSourceAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true);
#if true
public static OperationCanceledException ReportWTF(IOperation location, OperationAnalysisContext ctx, string message)
{
DiagWTF.ReportAt(location, ctx, message);
return new(ctx.CancellationToken);
}
public static OperationCanceledException ReportWTF(SyntaxNode location, OperationAnalysisContext ctx, string message)
{
ctx.ReportDiagnostic(Diagnostic.Create(DiagWTF, location.GetLocation(), message));
DiagWTF.ReportAt(location, ctx, message);
return new(ctx.CancellationToken);
}
public static OperationCanceledException ReportWTF(SyntaxNode location, SyntaxNodeAnalysisContext ctx, string message)
{
ctx.ReportDiagnostic(Diagnostic.Create(DiagWTF, location.GetLocation(), message));
DiagWTF.ReportAt(location, ctx, message);
return new(ctx.CancellationToken);
}
#else // maybe move to something like this?
public static OperationCanceledException ReportWTF(SyntaxNode alien, string analyzerName, string disambig, SyntaxNodeAnalysisContext ctx)
{
ctx.ReportDiagnostic(Diagnostic.Create(DiagWTF, alien.GetLocation(), $"[{analyzerName}{disambig}] AST/model contained {alien.GetType().FullName} unexpectedly; Analyzer needs updating"));
DiagWTF.ReportAt(alien, ctx, $"[{analyzerName}{disambig}] AST/model contained {alien.GetType().FullName} unexpectedly; Analyzer needs updating");
return new(ctx.CancellationToken);
}
#endif
@ -152,48 +158,42 @@ public class HawkSourceAnalyzer : DiagnosticAnalyzer
switch (snac.Node)
{
case AnonymousMethodExpressionSyntax:
snac.ReportDiagnostic(Diagnostic.Create(DiagNoAnonDelegates, snac.Node.GetLocation()));
DiagNoAnonDelegates.ReportAt(snac.Node, snac);
break;
case AnonymousObjectCreationExpressionSyntax:
snac.ReportDiagnostic(Diagnostic.Create(DiagNoAnonClasses, snac.Node.GetLocation()));
DiagNoAnonClasses.ReportAt(snac.Node, snac);
break;
case AssignmentExpressionSyntax aes:
if (!IsDiscard(aes)) break;
if (snac.SemanticModel.GetSymbolInfo(aes.Right, snac.CancellationToken).Symbol?.Kind is not SymbolKind.Local) break;
snac.ReportDiagnostic(Diagnostic.Create(DiagNoDiscardingLocals, snac.Node.GetLocation()));
DiagNoDiscardingLocals.ReportAt(snac.Node, snac);
break;
case CollectionExpressionSyntax ces:
var cesError = CheckSpacingInList(ces.Elements, ces.OpenBracketToken, ces.ToString);
if (cesError is not null) snac.ReportDiagnostic(Diagnostic.Create(DiagListExprSpacing, ces.GetLocation(), cesError));
if (cesError is not null) DiagListExprSpacing.ReportAt(ces, snac, cesError);
break;
case InterpolatedStringExpressionSyntax ises:
if (ises.StringStartToken.Text[0] is '@') snac.ReportDiagnostic(Diagnostic.Create(DiagInterpStringIsDollarAt, ises.GetLocation()));
if (ises.StringStartToken.Text[0] is '@') DiagInterpStringIsDollarAt.ReportAt(ises, snac);
break;
case ListPatternSyntax lps:
var lpsError = CheckSpacingInList(lps.Patterns, lps.OpenBracketToken, lps.ToString);
if (lpsError is not null) snac.ReportDiagnostic(Diagnostic.Create(DiagListExprSpacing, lps.GetLocation(), lpsError));
if (lpsError is not null) DiagListExprSpacing.ReportAt(lps, snac, lpsError);
break;
case QueryExpressionSyntax:
snac.ReportDiagnostic(Diagnostic.Create(DiagNoQueryExpression, snac.Node.GetLocation()));
DiagNoQueryExpression.ReportAt(snac.Node, snac);
break;
case RecordDeclarationSyntax rds when rds.ClassOrStructKeyword.ToString() is not "class": // `record struct`s don't use this kind
snac.ReportDiagnostic(Diagnostic.Create(DiagRecordImplicitlyRefType, rds.GetLocation()));
DiagRecordImplicitlyRefType.ReportAt(rds, snac);
break;
case SwitchExpressionArmSyntax { WhenClause: null, Pattern: DiscardPatternSyntax, Expression: ThrowExpressionSyntax tes }:
var thrownExceptionType = snac.SemanticModel.GetThrownExceptionType(tes);
if (thrownExceptionType is null)
{
snac.ReportDiagnostic(Diagnostic.Create(
DiagSwitchShouldThrowIOE,
tes.GetLocation(),
DiagnosticSeverity.Warning,
additionalLocations: null,
properties: null,
ERR_MSG_SWITCH_THROWS_UNKNOWN));
DiagSwitchShouldThrowIOE.ReportAt(tes, DiagnosticSeverity.Warning, snac, ERR_MSG_SWITCH_THROWS_UNKNOWN);
}
else if (!invalidOperationExceptionSym.Matches(thrownExceptionType) && switchExpressionExceptionSym?.Matches(thrownExceptionType) != true)
{
snac.ReportDiagnostic(Diagnostic.Create(DiagSwitchShouldThrowIOE, tes.GetLocation(), ERR_MSG_SWITCH_THROWS_WRONG_TYPE));
DiagSwitchShouldThrowIOE.ReportAt(tes, snac, ERR_MSG_SWITCH_THROWS_WRONG_TYPE);
}
// else correct usage, do not flag
break;

View File

@ -119,13 +119,7 @@ public sealed class LINQOnStringsAnalyzer : DiagnosticAnalyzer
: "Use `str.Contains(c.ToString())`";
break;
}
oac.ReportDiagnostic(Diagnostic.Create(
DiagLINQOnStrings,
operation.Syntax.GetLocation(),
level,
additionalLocations: null,
properties: null,
messageArgs: string.Format(msgFmtStr, receiverExpr.Syntax)));
DiagLINQOnStrings.ReportAt(operation, level, oac, string.Format(msgFmtStr, receiverExpr.Syntax));
},
OperationKind.Invocation);
});

View File

@ -5,12 +5,10 @@ using System.Collections.Immutable;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class NoTargetTypedThrowAnalyzer : DiagnosticAnalyzer
{
private const string ERR_MSG_IMPLICIT = "Specify `Exception` (or a more precise type) explicitly";
private static readonly DiagnosticDescriptor DiagNoTargetTypedThrow = new(
id: "BHI1007",
title: "Don't use target-typed new for throw expressions",
messageFormat: "{0}",
messageFormat: "Specify `Exception` (or a more precise type) explicitly",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
@ -27,8 +25,6 @@ public sealed class NoTargetTypedThrowAnalyzer : DiagnosticAnalyzer
{
var exceptionOp = ((IThrowOperation) oac.Operation).Exception;
if (exceptionOp is null) return; // re-`throw;`
void Fail(string message)
=> oac.ReportDiagnostic(Diagnostic.Create(DiagNoTargetTypedThrow, exceptionOp.Syntax.GetLocation(), message));
switch (exceptionOp.Kind)
{
case OperationKind.ObjectCreation:
@ -44,10 +40,10 @@ public sealed class NoTargetTypedThrowAnalyzer : DiagnosticAnalyzer
}
return;
default:
HawkSourceAnalyzer.ReportWTF(exceptionOp.Syntax, oac, message: $"[{nameof(NoTargetTypedThrowAnalyzer)}] Argument to throw expression was of an unexpected kind: {exceptionOp.GetType().FullName}");
HawkSourceAnalyzer.ReportWTF(exceptionOp, oac, message: $"[{nameof(NoTargetTypedThrowAnalyzer)}] Argument to throw expression was of an unexpected kind: {exceptionOp.GetType().FullName}");
return;
}
Fail(ERR_MSG_IMPLICIT);
DiagNoTargetTypedThrow.ReportAt(exceptionOp, oac);
},
OperationKind.Throw);
}

View File

@ -57,7 +57,7 @@ public class OrderBySelfAnalyzer : DiagnosticAnalyzer
var calledSym = operation.TargetMethod.ConstructedFrom;
if (!(orderByAscSym!.Matches(calledSym) || orderByDescSym!.Matches(calledSym))) return;
if (((ArgumentSyntax) operation.Arguments[1].Syntax).Expression is not AnonymousFunctionExpressionSyntax afes) return;
if (IsSelfReturnLambda(afes)) oac.ReportDiagnostic(Diagnostic.Create(DiagUseOrderBySelfExt, afes.GetLocation(), orderByDescSym.Matches(calledSym) ? "Descending" : string.Empty));
if (IsSelfReturnLambda(afes)) DiagUseOrderBySelfExt.ReportAt(afes, oac, orderByDescSym.Matches(calledSym) ? "Descending" : string.Empty);
},
OperationKind.Invocation);
});

View File

@ -67,13 +67,7 @@ public sealed class TernaryInferredTypeMismatchAnalyzer : DiagnosticAnalyzer
else if (typeTernary.Matches(typeRHS)) flaggedOp = lhs;
message = $"ternary branches are converted to {typeTernary} before serialisation, possibly unintended";
}
oac.ReportDiagnostic(Diagnostic.Create(
DiagTernaryInferredTypeMismatch,
flaggedOp.Syntax.GetLocation(),
fatal ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
additionalLocations: null,
properties: null,
messageArgs: message));
DiagTernaryInferredTypeMismatch.ReportAt(flaggedOp, fatal ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning, oac, message);
},
OperationKind.Conditional);
}

View File

@ -37,13 +37,10 @@ public sealed class TryGetValueImplicitDiscardAnalyzer : DiagnosticAnalyzer
var operation = (IInvocationOperation) oac.Operation;
if (operation.Parent?.Kind is not OperationKind.ExpressionStatement) return;
var calledSym = operation.TargetMethod.ConstructedFrom;
if (calledSym.Name is STR_TGV) oac.ReportDiagnostic(Diagnostic.Create(
DiagUncheckedTryGetValue,
operation.Syntax.GetLocation(),
IsBCLTryGetValue(calledSym) ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
additionalLocations: null,
properties: null,
messageArgs: null));
if (calledSym.Name is STR_TGV)
{
DiagUncheckedTryGetValue.ReportAt(operation, isErrorSeverity: IsBCLTryGetValue(calledSym), oac);
}
},
OperationKind.Invocation);
});

View File

@ -42,22 +42,16 @@ public sealed class UseNameofOperatorAnalyzer : DiagnosticAnalyzer
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"));
DiagNoToStringOnType.ReportAt(toes, snac, [ toes.Type.GetText(), " in string concatenation" ]);
}
break;
case InterpolationSyntax:
snac.ReportDiagnostic(Diagnostic.Create(DiagNoToStringOnType, toes.GetLocation(), toes.Type.GetText(), " in string interpolation"));
DiagNoToStringOnType.ReportAt(toes, snac, [ toes.Type.GetText(), " in string interpolation" ]);
break;
case MemberAccessExpressionSyntax maes1:
var accessed = snac.SemanticModel.GetSymbolInfo(maes1.Name, snac.CancellationToken).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()"));
}
if (memberInfoDotNameSym.Matches(accessed)) DiagUseNameof.ReportAt(maes1, snac, [ toes.Type.GetText() ]);
else if (typeDotToStringSym.Matches(accessed)) DiagNoToStringOnType.ReportAt(maes1, snac, [ toes.Type.GetText(), ".ToString()" ]);
break;
}
},

View File

@ -58,16 +58,10 @@ public sealed class UseSimplerBoolFlipAnalyzer : DiagnosticAnalyzer
lhsIsSimpleExpr = false;
break;
default:
HawkSourceAnalyzer.ReportWTF(operation.Syntax, oac, message: $"[{nameof(UseSimplerBoolFlipAnalyzer)}] Left-hand side of XOR-assign was of an unexpected kind: {lhsOp.GetType().FullName}");
HawkSourceAnalyzer.ReportWTF(operation, oac, message: $"[{nameof(UseSimplerBoolFlipAnalyzer)}] Left-hand side of XOR-assign was of an unexpected kind: {lhsOp.GetType().FullName}");
return;
}
oac.ReportDiagnostic(Diagnostic.Create(
DiagUseSimplerBoolFlip,
operation.Syntax.GetLocation(),
lhsIsSimpleExpr ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
additionalLocations: null,
properties: null,
lhsIsSimpleExpr ? ERR_MSG_SIMPLE : ERR_MSG_COMPLEX));
DiagUseSimplerBoolFlip.ReportAt(operation, isErrorSeverity: lhsIsSimpleExpr, oac, lhsIsSimpleExpr ? ERR_MSG_SIMPLE : ERR_MSG_COMPLEX);
},
OperationKind.CompoundAssignment);
}

View File

@ -39,7 +39,7 @@ public sealed class UseTypeofOperatorAnalyzer : DiagnosticAnalyzer
var enclosingType = operation.SemanticModel!.GetDeclaredSymbol(
((CSharpSyntaxNode) operation.Syntax).EnclosingTypeDeclarationSyntax()!,
oac.CancellationToken)!;
oac.ReportDiagnostic(Diagnostic.Create(enclosingType.IsSealed ? DiagNoGetTypeOnThisSealed : DiagNoGetTypeOnThis, operation.Syntax.GetLocation(), enclosingType.Name));
(enclosingType.IsSealed ? DiagNoGetTypeOnThisSealed : DiagNoGetTypeOnThis).ReportAt(operation, oac, enclosingType.Name);
},
OperationKind.Invocation);
}