-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add analyzer and code fix to recommend against IHeaderDictionary.Add #44463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
1373fbe
aabfef9
eb91cd8
16332ba
2bf902d
88625b2
dd36127
e6190b7
049bde0
1577df7
a2331a8
d9aff0a
a7d5124
ed6ddcf
faee1d0
81f6715
c2d8519
8a0bc4b
362d172
e3caef0
debb846
40bd514
e2f0a12
a216f87
1bad1a3
289260a
bc3ef4d
620275d
117ab9e
a57af9b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -151,4 +151,13 @@ internal static class DiagnosticDescriptors | |
| DiagnosticSeverity.Info, | ||
| isEnabledByDefault: true, | ||
| helpLinkUri: "https://aka.ms/aspnet/analyzers"); | ||
|
|
||
| internal static readonly DiagnosticDescriptor DoNotUseIHeaderDictionaryAdd = new( | ||
| "ASP0019", | ||
| new LocalizableResourceString(nameof(Resources.Analyzer_HeaderDictionaryAdd_Title), Resources.ResourceManager, typeof(Resources)), | ||
| new LocalizableResourceString(nameof(Resources.Analyzer_HeaderDictionaryAdd_Message), Resources.ResourceManager, typeof(Resources)), | ||
| "Usage", | ||
| DiagnosticSeverity.Warning, | ||
| isEnabledByDefault: true, | ||
| helpLinkUri: "https://aka.ms/aspnet/analyzers"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there an issue tracking documentation for analyzers? I see ASP0015, ASP0016, ASP0017, and ASP0018 with the same help link are not yet documented.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @david-acker You can file a docs issue in the docs repo here https://github.com/dotnet/AspNetCore.Docs/issues. If you're so inclined, you can also submit the doc for this by updating this page. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Collections.Immutable; | ||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.Diagnostics; | ||
| using Microsoft.CodeAnalysis.Operations; | ||
|
|
||
| namespace Microsoft.AspNetCore.Analyzers.Http; | ||
|
|
||
| [DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
| public partial class HeaderDictionaryAddAnalyzer : DiagnosticAnalyzer | ||
david-acker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseIHeaderDictionaryAdd); | ||
|
|
||
| public override void Initialize(AnalysisContext context) | ||
| { | ||
| context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); | ||
| context.EnableConcurrentExecution(); | ||
| context.RegisterOperationAction(context => | ||
| { | ||
| var invocation = (IInvocationOperation)context.Operation; | ||
|
|
||
| if (invocation.Instance?.Type is INamedTypeSymbol type && | ||
| IsIHeadersDictionaryType(type)) | ||
| { | ||
| if (invocation.TargetMethod.Parameters.Length == 2 && | ||
| IsAddMethod(invocation.TargetMethod)) | ||
| { | ||
| AddDiagnosticWarning(context, invocation.Syntax.GetLocation()); | ||
| } | ||
| } | ||
| }, OperationKind.Invocation); | ||
| } | ||
|
|
||
| private static bool IsIHeadersDictionaryType(INamedTypeSymbol type) | ||
| { | ||
| // Only IHeaderDictionary is valid. Types like HeaderDictionary, which implement IHeaderDictionary, | ||
| // can't access header properties unless cast as IHeaderDictionary. | ||
| return type is | ||
| { | ||
| Name: "IHeaderDictionary", | ||
| ContainingNamespace: | ||
| { | ||
| Name: "Http", | ||
| ContainingNamespace: | ||
| { | ||
| Name: "AspNetCore", | ||
| ContainingNamespace: | ||
| { | ||
| Name: "Microsoft", | ||
| ContainingNamespace: | ||
| { | ||
| IsGlobalNamespace: true | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| } | ||
david-acker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| private static bool IsAddMethod(IMethodSymbol method) | ||
| { | ||
| return method is | ||
| { | ||
| Name: "Add", | ||
| ContainingType: | ||
| { | ||
| Name: "IDictionary", | ||
| ContainingNamespace: | ||
| { | ||
| Name: "Generic", | ||
| ContainingNamespace: | ||
| { | ||
| Name: "Collections", | ||
| ContainingNamespace: | ||
| { | ||
| Name: "System", | ||
| ContainingNamespace: | ||
| { | ||
| IsGlobalNamespace: true | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| private static void AddDiagnosticWarning(OperationAnalysisContext context, Location location) | ||
| { | ||
| context.ReportDiagnostic(Diagnostic.Create( | ||
| DiagnosticDescriptors.DoNotUseIHeaderDictionaryAdd, | ||
| location)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -201,4 +201,10 @@ | |
| <data name="Analyzer_UnusedParameter_Title" xml:space="preserve"> | ||
| <value>Unused route parameter</value> | ||
| </data> | ||
| <data name="Analyzer_HeaderDictionaryAdd_Message" xml:space="preserve"> | ||
| <value>Suggest using IHeaderDictionary.Append or the indexer instead of Add</value> | ||
|
||
| </data> | ||
| <data name="Analyzer_HeaderDictionaryAdd_Title" xml:space="preserve"> | ||
| <value>Suggest using IHeaderDictionary.Append or the indexer</value> | ||
| </data> | ||
| </root> | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,150 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Licensed to the .NET Foundation under one or more agreements. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The .NET Foundation licenses this file to you under the MIT license. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Collections.Immutable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Composition; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Threading; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Threading.Tasks; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Microsoft.CodeAnalysis; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Microsoft.CodeAnalysis.CodeActions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Microsoft.CodeAnalysis.CodeFixes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Microsoft.CodeAnalysis.CSharp; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace Microsoft.AspNetCore.Analyzers.Http.Fixers; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ExportCodeFixProvider(LanguageNames.CSharp), Shared] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class HeaderDictionaryAddFixer : CodeFixProvider | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
david-acker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticDescriptors.DoNotUseIHeaderDictionaryAdd.Id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var diagnostic in context.Diagnostics) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var appendTitle = "Use IHeaderDictionary.Append"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
david-acker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| context.RegisterCodeFix( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CodeAction.Create(appendTitle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cancellationToken => ReplaceAddWithAppendAsync(diagnostic, context.Document, cancellationToken), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| equivalenceKey: appendTitle), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| diagnostic); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var indexerTitle = "Use the indexer"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
david-acker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| context.RegisterCodeFix( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CodeAction.Create(indexerTitle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cancellationToken => ReplaceAddWithIndexerAsync(diagnostic, context.Document, cancellationToken), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| equivalenceKey: indexerTitle), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| diagnostic); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Task.CompletedTask; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static async Task<Document> ReplaceAddWithAppendAsync(Diagnostic diagnostic, Document document, CancellationToken cancellationToken) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (root is not CompilationUnitSyntax compilationUnitSyntax) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return document; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var invocation = compilationUnitSyntax.FindNode(diagnostic.Location.SourceSpan); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (invocation is InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax { Name.Identifier: { } identifierToken } }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
david-acker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| compilationUnitSyntax = compilationUnitSyntax.ReplaceToken(identifierToken, SyntaxFactory.Identifier("Append")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // IHeaderDictionary.Append is defined as an extension method on Microsoft.AspNetCore.Http.HeaderDictionaryExtensions. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // We'll need to add the required using directive, when not already present. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| compilationUnitSyntax = AddRequiredUsingDirectiveForAppend(compilationUnitSyntax); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return document.WithSyntaxRoot(compilationUnitSyntax); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return document; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static CompilationUnitSyntax AddRequiredUsingDirectiveForAppend(CompilationUnitSyntax compilationUnitSyntax) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
david-acker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var usingDirectives = compilationUnitSyntax.Usings; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var includesRequiredUsingDirective = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var insertionIndex = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (var i = 0; i < usingDirectives.Count; i++) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var namespaceName = usingDirectives[i].Name.ToString(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Always insert the new using directive after any 'System' using directives. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (namespaceName.StartsWith("System", StringComparison.Ordinal)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| insertionIndex = i + 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var result = string.Compare("Microsoft.AspNetCore.Http", namespaceName, StringComparison.Ordinal); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (result == 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| includesRequiredUsingDirective = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (result < 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| insertionIndex = i; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (includesRequiredUsingDirective) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return compilationUnitSyntax; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var requiredUsingDirective = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SyntaxFactory.UsingDirective( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SyntaxFactory.QualifiedName( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SyntaxFactory.QualifiedName( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SyntaxFactory.IdentifierName("Microsoft"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SyntaxFactory.IdentifierName("AspNetCore")), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SyntaxFactory.IdentifierName("Http"))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return compilationUnitSyntax.WithUsings( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usingDirectives.Insert(insertionIndex, requiredUsingDirective)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static CompilationUnitSyntax AddRequiredUsingDirectiveForAppend(CompilationUnitSyntax compilationUnitSyntax) | |
| { | |
| var usingDirectives = compilationUnitSyntax.Usings; | |
| var includesRequiredUsingDirective = false; | |
| var insertionIndex = 0; | |
| for (var i = 0; i < usingDirectives.Count; i++) | |
| { | |
| var namespaceName = usingDirectives[i].Name.ToString(); | |
| // Always insert the new using directive after any 'System' using directives. | |
| if (namespaceName.StartsWith("System", StringComparison.Ordinal)) | |
| { | |
| insertionIndex = i + 1; | |
| continue; | |
| } | |
| var result = string.Compare("Microsoft.AspNetCore.Http", namespaceName, StringComparison.Ordinal); | |
| if (result == 0) | |
| { | |
| includesRequiredUsingDirective = true; | |
| break; | |
| } | |
| if (result < 0) | |
| { | |
| insertionIndex = i; | |
| break; | |
| } | |
| } | |
| if (includesRequiredUsingDirective) | |
| { | |
| return compilationUnitSyntax; | |
| } | |
| var requiredUsingDirective = | |
| SyntaxFactory.UsingDirective( | |
| SyntaxFactory.QualifiedName( | |
| SyntaxFactory.QualifiedName( | |
| SyntaxFactory.IdentifierName("Microsoft"), | |
| SyntaxFactory.IdentifierName("AspNetCore")), | |
| SyntaxFactory.IdentifierName("Http"))); | |
| return compilationUnitSyntax.WithUsings( | |
| usingDirectives.Insert(insertionIndex, requiredUsingDirective)); | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.CodeAnalysis.Testing; | ||
| using VerifyCS = Microsoft.AspNetCore.Analyzers.Verifiers.CSharpAnalyzerVerifier< | ||
| Microsoft.AspNetCore.Analyzers.Http.HeaderDictionaryAddAnalyzer>; | ||
|
|
||
| namespace Microsoft.AspNetCore.Analyzers.Http; | ||
|
|
||
| public class HeaderDictionaryAddAnalyzerTests | ||
| { | ||
| [Fact] | ||
| public async Task IHeaderDictionary_WithAdd_ReportsDiagnostics() | ||
david-acker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| // Arrange & Act & Assert | ||
| await VerifyCS.VerifyAnalyzerAsync(@" | ||
| using Microsoft.AspNetCore.Http; | ||
| namespace HeaderDictionaryAddAnalyzerTests; | ||
| public class Program | ||
| { | ||
| public static void Main() | ||
| { | ||
| var context = new DefaultHttpContext(); | ||
| {|#0:context.Request.Headers.Add(""Accept"", ""text/html"")|}; | ||
david-acker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| }", | ||
| new DiagnosticResult(DiagnosticDescriptors.DoNotUseIHeaderDictionaryAdd) | ||
| .WithLocation(0) | ||
| .WithMessage(Resources.Analyzer_HeaderDictionaryAdd_Message)); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task IHeaderDictionary_WithAppend_NoDiagnostics() | ||
| { | ||
| // Arrange & Act & Assert | ||
| await VerifyCS.VerifyAnalyzerAsync(@" | ||
| using Microsoft.AspNetCore.Http; | ||
| namespace HeaderDictionaryAddAnalyzerTests; | ||
| public class Program | ||
| { | ||
| public static void Main() | ||
| { | ||
| var context = new DefaultHttpContext(); | ||
| context.Request.Headers.Append(""Accept"", ""text/html""); | ||
| } | ||
| }"); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task IHeaderDictionary_WithIndexer_NoDiagnostics() | ||
| { | ||
| // Arrange & Act & Assert | ||
| await VerifyCS.VerifyAnalyzerAsync(@" | ||
| using Microsoft.AspNetCore.Http; | ||
| namespace HeaderDictionaryAddAnalyzerTests; | ||
| public class Program | ||
| { | ||
| public static void Main() | ||
| { | ||
| var context = new DefaultHttpContext(); | ||
| context.Request.Headers[""Accept""] = ""text/html""; | ||
| } | ||
| }"); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.