|  | 
| 1 |  | -using System; | 
|  | 1 | +using System; | 
| 2 | 2 | using System.Collections.Immutable; | 
| 3 | 3 | using System.Globalization; | 
| 4 | 4 | using System.Linq.Expressions; | 
| @@ -69,19 +69,27 @@ public override void Initialize(AnalysisContext context) | 
| 69 | 69 | 
 | 
| 70 | 70 |                 if (callerMustUseNamedArgumentType is not null) | 
| 71 | 71 |                 { | 
| 72 |  | -                    var operation = syntaxContext.SemanticModel.GetOperation(argument, syntaxContext.CancellationToken) as IArgumentOperation; | 
| 73 |  | -                    if ((operation?.Parameter) is not null) | 
|  | 72 | +                    if (IsCallerMustUseNamedArgumentAttribute(syntaxContext, argument, callerMustUseNamedArgumentType)) | 
| 74 | 73 |                     { | 
| 75 |  | -                        var attribute = operation.Parameter.GetAttribute(callerMustUseNamedArgumentType); | 
| 76 |  | -                        if (attribute is not null) | 
|  | 74 | +                        syntaxContext.ReportDiagnostic(Diagnostic.Create(Rule, syntaxContext.Node.GetLocation(), effectiveSeverity: DiagnosticSeverity.Warning, additionalLocations: null, properties: null)); | 
|  | 75 | +                        return; | 
|  | 76 | +                    } | 
|  | 77 | + | 
|  | 78 | +                    static bool IsCallerMustUseNamedArgumentAttribute(SyntaxNodeAnalysisContext context, SyntaxNode argument, INamedTypeSymbol callerMustUseNamedArgumentType) | 
|  | 79 | +                    { | 
|  | 80 | +                        var operation = context.SemanticModel.GetOperation(argument, context.CancellationToken) as IArgumentOperation; | 
|  | 81 | +                        if ((operation?.Parameter) is not null) | 
| 77 | 82 |                         { | 
| 78 |  | -                            var requireNamedArgument = attribute.ConstructorArguments.Length == 0 || attribute.ConstructorArguments[0].Value is true; | 
| 79 |  | -                            if (requireNamedArgument) | 
|  | 83 | +                            var attribute = operation.Parameter.GetAttribute(callerMustUseNamedArgumentType); | 
|  | 84 | +                            if (attribute is not null) | 
| 80 | 85 |                             { | 
| 81 |  | -                                syntaxContext.ReportDiagnostic(Diagnostic.Create(Rule, syntaxContext.Node.GetLocation(), effectiveSeverity: DiagnosticSeverity.Warning, additionalLocations: null, properties: null)); | 
| 82 |  | -                                return; | 
|  | 86 | +                                var requireNamedArgument = attribute.ConstructorArguments.Length == 0 || attribute.ConstructorArguments[0].Value is true; | 
|  | 87 | +                                if (requireNamedArgument) | 
|  | 88 | +                                    return true; | 
| 83 | 89 |                             } | 
| 84 | 90 |                         } | 
|  | 91 | + | 
|  | 92 | +                        return false; | 
| 85 | 93 |                     } | 
| 86 | 94 |                 } | 
| 87 | 95 | 
 | 
| @@ -114,6 +122,15 @@ public override void Initialize(AnalysisContext context) | 
| 114 | 122 |                 if (argument.Parent.IsKind(SyntaxKind.TupleExpression)) | 
| 115 | 123 |                     return; // Don't consider tuple | 
| 116 | 124 | 
 | 
|  | 125 | + | 
|  | 126 | +                var operation = syntaxContext.SemanticModel.GetOperation(argument, syntaxContext.CancellationToken) as IArgumentOperation; | 
|  | 127 | +                if (operation?.Parameter is not null) | 
|  | 128 | +                { | 
|  | 129 | +                    var parameterName = operation.Parameter.Name; | 
|  | 130 | +                    if (!IsMeaningfulParameterName(parameterName)) | 
|  | 131 | +                        return; | 
|  | 132 | +                } | 
|  | 133 | + | 
| 117 | 134 |                 // Exclude in some methods such as ConfigureAwait(false) | 
| 118 | 135 |                 var invocationExpression = argument.FirstAncestorOrSelf<ExpressionSyntax>(t => t.IsKind(SyntaxKind.InvocationExpression) || t.IsKind(SyntaxKind.ObjectCreationExpression) || t.IsKind(SyntaxKind.ElementAccessExpression)); | 
| 119 | 136 |                 if (invocationExpression is not null) | 
| @@ -247,7 +264,6 @@ bool IsParams(SyntaxNode node) | 
| 247 | 264 |                         if (invokedMethodSymbol.Name.StartsWith("With", StringComparison.Ordinal) && invokedMethodSymbol.ContainingType.IsOrInheritFrom(syntaxNodeType)) | 
| 248 | 265 |                             return; | 
| 249 | 266 | 
 | 
| 250 |  | -                        var operation = syntaxContext.SemanticModel.GetOperation(argument, syntaxContext.CancellationToken); | 
| 251 | 267 |                         if (operation is not null && operationUtilities.IsInExpressionContext(operation)) | 
| 252 | 268 |                             return; | 
| 253 | 269 | 
 | 
| @@ -323,6 +339,32 @@ private static bool MustCheckExpressionKind(SyntaxNodeAnalysisContext context, S | 
| 323 | 339 |         return (options & kind) == kind; | 
| 324 | 340 |     } | 
| 325 | 341 | 
 | 
|  | 342 | +    private static bool IsMeaningfulParameterName(string parameterName) | 
|  | 343 | +    { | 
|  | 344 | +        if (string.IsNullOrEmpty(parameterName)) | 
|  | 345 | +            return false; | 
|  | 346 | + | 
|  | 347 | +        if (parameterName is "obj") | 
|  | 348 | +            return false; | 
|  | 349 | + | 
|  | 350 | +        // arg, arg1, arg2, etc. are not meaningful | 
|  | 351 | +        if (parameterName.StartsWith("arg", StringComparison.OrdinalIgnoreCase) && IsAllDigit(parameterName.AsSpan(3))) | 
|  | 352 | +            return false; | 
|  | 353 | + | 
|  | 354 | +        return true; | 
|  | 355 | + | 
|  | 356 | +        static bool IsAllDigit(ReadOnlySpan<char> span) | 
|  | 357 | +        { | 
|  | 358 | +            for (var i = 0; i < span.Length; i++) | 
|  | 359 | +            { | 
|  | 360 | +                if (!char.IsDigit(span[i])) | 
|  | 361 | +                    return false; | 
|  | 362 | +            } | 
|  | 363 | + | 
|  | 364 | +            return true; | 
|  | 365 | +        } | 
|  | 366 | +    } | 
|  | 367 | + | 
| 326 | 368 |     [Flags] | 
| 327 | 369 |     private enum ArgumentExpressionKinds | 
| 328 | 370 |     { | 
|  | 
0 commit comments