|
| 1 | +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. |
| 2 | + |
| 3 | +usingSystem; |
| 4 | +usingSystem.Collections.Immutable; |
| 5 | +usingSystem.Composition; |
| 6 | +usingSystem.Globalization; |
| 7 | +usingSystem.Linq; |
| 8 | +usingSystem.Threading; |
| 9 | +usingSystem.Threading.Tasks; |
| 10 | +usingAnalyzer.Utilities; |
| 11 | +usingAnalyzer.Utilities.Extensions; |
| 12 | +usingMicrosoft.CodeAnalysis; |
| 13 | +usingMicrosoft.CodeAnalysis.CodeActions; |
| 14 | +usingMicrosoft.CodeAnalysis.CodeFixes; |
| 15 | +usingMicrosoft.CodeAnalysis.Editing; |
| 16 | +usingMicrosoft.CodeAnalysis.Operations; |
| 17 | +usingMicrosoft.CodeAnalysis.Text; |
| 18 | + |
| 19 | +namespaceMicrosoft.NetCore.Analyzers.Performance |
| 20 | +{ |
| 21 | +usingstaticMicrosoftNetCoreAnalyzersResources; |
| 22 | + |
| 23 | +/// <summary> |
| 24 | +/// CA1872: <inheritdoc cref="PreferConvertToHexStringOverBitConverterTitle"/> |
| 25 | +/// </summary> |
| 26 | +[ExportCodeFixProvider(LanguageNames.CSharp,LanguageNames.VisualBasic),Shared] |
| 27 | +publicsealedclassPreferConvertToHexStringOverBitConverterFixer:CodeFixProvider |
| 28 | +{ |
| 29 | +privatestaticreadonlySyntaxAnnotations_asSpanSymbolAnnotation=new("SymbolId",WellKnownTypeNames.SystemMemoryExtensions); |
| 30 | + |
| 31 | +publicsealedoverrideImmutableArray<string>FixableDiagnosticIds{get;}= |
| 32 | +ImmutableArray.Create(PreferConvertToHexStringOverBitConverterAnalyzer.RuleId); |
| 33 | + |
| 34 | +publicsealedoverrideFixAllProviderGetFixAllProvider() |
| 35 | +{ |
| 36 | +returnWellKnownFixAllProviders.BatchFixer; |
| 37 | +} |
| 38 | + |
| 39 | +publicsealedoverrideasyncTaskRegisterCodeFixesAsync(CodeFixContextcontext) |
| 40 | +{ |
| 41 | +vardiagnostic=context.Diagnostics.FirstOrDefault(); |
| 42 | + |
| 43 | +if(diagnosticis not{AdditionalLocations.Count:>0,Properties.Count:1}|| |
| 44 | +!diagnostic.Properties.TryGetValue(PreferConvertToHexStringOverBitConverterAnalyzer.ReplacementPropertiesKey,outvarconvertToHexStringName)|| |
| 45 | +convertToHexStringNameisnull) |
| 46 | +{ |
| 47 | +return; |
| 48 | +} |
| 49 | + |
| 50 | +varroot=awaitcontext.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); |
| 51 | +varsemanticModel=awaitcontext.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); |
| 52 | + |
| 53 | +varbitConverterInvocation=GetInvocationFromTextSpan(diagnostic.AdditionalLocations[0].SourceSpan); |
| 54 | +varouterInvocation=GetInvocationFromTextSpan(context.Span); |
| 55 | + |
| 56 | +if(bitConverterInvocationisnull||outerInvocationisnull) |
| 57 | +{ |
| 58 | +return; |
| 59 | +} |
| 60 | + |
| 61 | +vartoLowerInvocation=diagnostic.AdditionalLocations.Count==2 |
| 62 | +?GetInvocationFromTextSpan(diagnostic.AdditionalLocations[1].SourceSpan) |
| 63 | +:null; |
| 64 | + |
| 65 | +varcodeAction=CodeAction.Create( |
| 66 | +string.Format(CultureInfo.CurrentCulture,PreferConvertToHexStringOverBitConverterCodeFixTitle,convertToHexStringName), |
| 67 | +ReplaceWithConvertToHexStringCall, |
| 68 | +nameof(PreferConvertToHexStringOverBitConverterCodeFixTitle)); |
| 69 | + |
| 70 | +context.RegisterCodeFix(codeAction,context.Diagnostics); |
| 71 | + |
| 72 | +IInvocationOperation?GetInvocationFromTextSpan(TextSpanspan) |
| 73 | +{ |
| 74 | +varnode=root.FindNode(span,getInnermostNodeForTie:true); |
| 75 | + |
| 76 | +if(nodeisnull) |
| 77 | +{ |
| 78 | +returnnull; |
| 79 | +} |
| 80 | + |
| 81 | +returnsemanticModel.GetOperation(node,context.CancellationToken)asIInvocationOperation; |
| 82 | +} |
| 83 | + |
| 84 | +asyncTask<Document>ReplaceWithConvertToHexStringCall(CancellationTokencancellationToken) |
| 85 | +{ |
| 86 | +vareditor=awaitDocumentEditor.CreateAsync(context.Document,cancellationToken).ConfigureAwait(false); |
| 87 | +vargenerator=editor.Generator; |
| 88 | +varbitConverterArgumentsInParameterOrder=bitConverterInvocation.Arguments.GetArgumentsInParameterOrder(); |
| 89 | + |
| 90 | +vartypeExpression=generator.DottedName(WellKnownTypeNames.SystemConvert); |
| 91 | +varmethodExpression=generator.MemberAccessExpression(typeExpression,convertToHexStringName); |
| 92 | +varmethodInvocation=bitConverterArgumentsInParameterOrder.Lengthswitch |
| 93 | +{ |
| 94 | +// BitConverter.ToString(data).Replace("-", "") => Convert.ToHexString(data) |
| 95 | +1=>generator.InvocationExpression(methodExpression,bitConverterArgumentsInParameterOrder[0].Value.Syntax), |
| 96 | +// BitConverter.ToString(data, start).Replace("-", "") => Convert.ToHexString(data.AsSpan().Slice(start)) |
| 97 | +2=>generator.InvocationExpression( |
| 98 | +methodExpression, |
| 99 | +generator.InvocationExpression(generator.MemberAccessExpression( |
| 100 | +generator.InvocationExpression(generator.MemberAccessExpression( |
| 101 | +bitConverterArgumentsInParameterOrder[0].Value.Syntax, |
| 102 | +nameof(MemoryExtensions.AsSpan))), |
| 103 | +WellKnownMemberNames.SliceMethodName), |
| 104 | +bitConverterArgumentsInParameterOrder[1].Value.Syntax)) |
| 105 | +.WithAddImportsAnnotation() |
| 106 | +.WithAdditionalAnnotations(s_asSpanSymbolAnnotation), |
| 107 | +// BitConverter.ToString(data, start, length).Replace("-", "") => Convert.ToHexString(data, start, length) |
| 108 | +3=>generator.InvocationExpression(methodExpression,bitConverterArgumentsInParameterOrder.Select(a=>a.Value.Syntax).ToArray()), |
| 109 | + _=>thrownewNotImplementedException() |
| 110 | +}; |
| 111 | + |
| 112 | +// This branch is hit when string.ToLower* is used and Convert.ToHexStringLower is not available. |
| 113 | +if(toLowerInvocationis notnull) |
| 114 | +{ |
| 115 | +methodInvocation=generator.InvocationExpression( |
| 116 | +generator.MemberAccessExpression(methodInvocation,toLowerInvocation.TargetMethod.Name), |
| 117 | +toLowerInvocation.Arguments.Select(a=>a.Value.Syntax).ToArray()); |
| 118 | +} |
| 119 | + |
| 120 | +editor.ReplaceNode(outerInvocation.Syntax,methodInvocation.WithTriviaFrom(outerInvocation.Syntax)); |
| 121 | + |
| 122 | +returncontext.Document.WithSyntaxRoot(editor.GetChangedRoot()); |
| 123 | +} |
| 124 | +} |
| 125 | +} |
| 126 | +} |