diff --git a/ExternalProjects/AnalyzersCommon/DiagnosticDescriptorExtensions.cs b/ExternalProjects/AnalyzersCommon/DiagnosticDescriptorExtensions.cs new file mode 100644 index 0000000000..409f4de966 --- /dev/null +++ b/ExternalProjects/AnalyzersCommon/DiagnosticDescriptorExtensions.cs @@ -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 ]); +} diff --git a/ExternalProjects/BizHawk.Analyzer/AmbiguousMoneyToFloatConversionAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/AmbiguousMoneyToFloatConversionAnalyzer.cs index 60fd92cbcb..91c45ce309 100644 --- a/ExternalProjects/BizHawk.Analyzer/AmbiguousMoneyToFloatConversionAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/AmbiguousMoneyToFloatConversionAnalyzer.cs @@ -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); } diff --git a/ExternalProjects/BizHawk.Analyzer/ExprBodiedMemberFlowAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/ExprBodiedMemberFlowAnalyzer.cs index 5e4f00b7f8..484ca50919 100644 --- a/ExternalProjects/BizHawk.Analyzer/ExprBodiedMemberFlowAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/ExprBodiedMemberFlowAnalyzer.cs @@ -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(); diff --git a/ExternalProjects/BizHawk.Analyzer/FeatureNotImplementedAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/FeatureNotImplementedAnalyzer.cs index 03762761e3..2d64ff198c 100644 --- a/ExternalProjects/BizHawk.Analyzer/FeatureNotImplementedAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/FeatureNotImplementedAnalyzer.cs @@ -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 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) { diff --git a/ExternalProjects/BizHawk.Analyzer/FirstOrDefaultOnStructAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/FirstOrDefaultOnStructAnalyzer.cs index 9b5a1e769b..400ae45ea5 100644 --- a/ExternalProjects/BizHawk.Analyzer/FirstOrDefaultOnStructAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/FirstOrDefaultOnStructAnalyzer.cs @@ -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); }); diff --git a/ExternalProjects/BizHawk.Analyzer/HawkSourceAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/HawkSourceAnalyzer.cs index ac02f0fa9c..51f6a16c51 100644 --- a/ExternalProjects/BizHawk.Analyzer/HawkSourceAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/HawkSourceAnalyzer.cs @@ -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; diff --git a/ExternalProjects/BizHawk.Analyzer/LINQOnStringsAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/LINQOnStringsAnalyzer.cs index 5ddee615be..96d95aec9d 100644 --- a/ExternalProjects/BizHawk.Analyzer/LINQOnStringsAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/LINQOnStringsAnalyzer.cs @@ -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); }); diff --git a/ExternalProjects/BizHawk.Analyzer/NoTargetTypedThrowAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/NoTargetTypedThrowAnalyzer.cs index 4645ff4419..342c45ec88 100644 --- a/ExternalProjects/BizHawk.Analyzer/NoTargetTypedThrowAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/NoTargetTypedThrowAnalyzer.cs @@ -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); } diff --git a/ExternalProjects/BizHawk.Analyzer/OrderBySelfAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/OrderBySelfAnalyzer.cs index e97bde5e66..6b2e1180ab 100644 --- a/ExternalProjects/BizHawk.Analyzer/OrderBySelfAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/OrderBySelfAnalyzer.cs @@ -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); }); diff --git a/ExternalProjects/BizHawk.Analyzer/TernaryInferredTypeMismatchAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/TernaryInferredTypeMismatchAnalyzer.cs index 6f82cc696f..dbd67845ff 100644 --- a/ExternalProjects/BizHawk.Analyzer/TernaryInferredTypeMismatchAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/TernaryInferredTypeMismatchAnalyzer.cs @@ -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); } diff --git a/ExternalProjects/BizHawk.Analyzer/TryGetValueImplicitDiscardAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/TryGetValueImplicitDiscardAnalyzer.cs index d7d38b10cd..d3c3a91689 100644 --- a/ExternalProjects/BizHawk.Analyzer/TryGetValueImplicitDiscardAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/TryGetValueImplicitDiscardAnalyzer.cs @@ -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); }); diff --git a/ExternalProjects/BizHawk.Analyzer/UseNameofOperatorAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/UseNameofOperatorAnalyzer.cs index dd1dd582af..0862f03ef4 100644 --- a/ExternalProjects/BizHawk.Analyzer/UseNameofOperatorAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/UseNameofOperatorAnalyzer.cs @@ -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; } }, diff --git a/ExternalProjects/BizHawk.Analyzer/UseSimplerBoolFlipAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/UseSimplerBoolFlipAnalyzer.cs index e6c242fd5f..8114afd590 100644 --- a/ExternalProjects/BizHawk.Analyzer/UseSimplerBoolFlipAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/UseSimplerBoolFlipAnalyzer.cs @@ -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); } diff --git a/ExternalProjects/BizHawk.Analyzer/UseTypeofOperatorAnalyzer.cs b/ExternalProjects/BizHawk.Analyzer/UseTypeofOperatorAnalyzer.cs index 30784fae34..9594e8caf4 100644 --- a/ExternalProjects/BizHawk.Analyzer/UseTypeofOperatorAnalyzer.cs +++ b/ExternalProjects/BizHawk.Analyzer/UseTypeofOperatorAnalyzer.cs @@ -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); }