Skip to content

Commit dca31cf

Browse files
authored
Don't add duplicate suffix in LibraryImport code fix. (#85668)
1 parent 304079a commit dca31cf

File tree

2 files changed

+232
-9
lines changed

2 files changed

+232
-9
lines changed

src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ConvertToLibraryImportFixer.cs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ public sealed class ConvertToLibraryImportFixer : CodeFixProvider
3030
public override FixAllProvider GetFixAllProvider() => CustomFixAllProvider.Instance;
3131

3232
private const string ConvertToLibraryImportKey = "ConvertToLibraryImport,";
33+
private static string AppendSuffix(string entryPoint, char? entryPointSuffix)
34+
=> entryPointSuffix.HasValue && entryPoint.LastOrDefault() == entryPointSuffix.Value
35+
? entryPoint
36+
: entryPoint + entryPointSuffix;
3337
private static string AddSuffixKey(string baseKey, char suffix) => $"{baseKey}{suffix},";
3438
private static string AddUnsafeKey(string baseKey) => baseKey + "AddUnsafe,";
3539
private static string AddMayRequireAdditionalWorkKey(string baseKey) => baseKey + $"{ConvertToLibraryImportAnalyzer.MayRequireAdditionalWork},";
@@ -563,7 +567,7 @@ private static SyntaxNode GetLibraryImportAttribute(
563567

564568
// Update attribute arguments for LibraryImport
565569
bool hasEntryPointAttributeArgument = false;
566-
List<SyntaxNode> argumentsToAdd= new List<SyntaxNode>();
570+
List<SyntaxNode> argumentsToAdd = new List<SyntaxNode>();
567571
List<SyntaxNode> argumentsToRemove = new List<SyntaxNode>();
568572
foreach (SyntaxNode argument in generator.GetAttributeArguments(libraryImportSyntax))
569573
{
@@ -647,17 +651,20 @@ private static SyntaxNode GetLibraryImportAttribute(
647651
argumentsToRemove.Add(attrArg);
648652
argumentsToAdd.Add(attrArg.WithExpression(
649653
SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression,
650-
SyntaxFactory.Literal(entryPoint + entryPointSuffix))));
654+
SyntaxFactory.Literal(AppendSuffix(entryPoint, entryPointSuffix)))));
651655
}
652656
}
653657
else
654658
{
655-
argumentsToRemove.Add(attrArg);
656-
argumentsToAdd.Add(attrArg.WithExpression(
657-
SyntaxFactory.BinaryExpression(SyntaxKind.AddExpression,
658-
attrArg.Expression,
659-
SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression,
660-
SyntaxFactory.Literal(entryPointSuffix.ToString())))));
659+
if (dllImportData.EntryPointName!.LastOrDefault() != entryPointSuffix.Value)
660+
{
661+
argumentsToRemove.Add(attrArg);
662+
argumentsToAdd.Add(attrArg.WithExpression(
663+
SyntaxFactory.BinaryExpression(SyntaxKind.AddExpression,
664+
attrArg.Expression,
665+
SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression,
666+
SyntaxFactory.Literal(entryPointSuffix.ToString())))));
667+
}
661668
}
662669
}
663670
}
@@ -671,7 +678,7 @@ private static SyntaxNode GetLibraryImportAttribute(
671678
if (entryPointSuffix.HasValue && !hasEntryPointAttributeArgument)
672679
{
673680
argumentsToAdd.Add(generator.AttributeArgument("EntryPoint",
674-
generator.LiteralExpression(methodName + entryPointSuffix.Value)));
681+
generator.LiteralExpression(AppendSuffix(methodName, entryPointSuffix.Value))));
675682
}
676683

677684
libraryImportSyntax = generator.RemoveNodes(libraryImportSyntax, argumentsToRemove);

src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,222 @@ partial class Test
490490
await VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A,");
491491
}
492492

493+
[InlineData(CharSet.Ansi, 'A')]
494+
[InlineData(CharSet.Unicode, 'W')]
495+
[Theory]
496+
public async Task SuffixPresent_ExactSpelling_False_NoAutoCharSet_Provides_No_Suffix_And_Suffix_Fix(CharSet charSet, char suffix)
497+
{
498+
string source = $$"""
499+
using System.Runtime.InteropServices;
500+
partial class Test
501+
{
502+
[DllImport("DoesNotExist", EntryPoint = "Entry{{suffix}}", ExactSpelling = false, CharSet = CharSet.{{charSet}})]
503+
public static extern void [|Method|]();
504+
}
505+
""";
506+
string fixedSourceNoSuffix = $$"""
507+
using System.Runtime.InteropServices;
508+
partial class Test
509+
{
510+
[LibraryImport("DoesNotExist", EntryPoint = "Entry{{suffix}}")]
511+
public static partial void {|CS8795:Method|}();
512+
}
513+
""";
514+
await VerifyCodeFixAsync(source, fixedSourceNoSuffix, ConvertToLibraryImportKey);
515+
}
516+
517+
[Fact]
518+
public async Task SuffixWPresent_ExactSpelling_False_AutoCharSet_Provides_No_Suffix_And_Both_Suffix_Fixes()
519+
{
520+
string source = """
521+
522+
using System.Runtime.InteropServices;
523+
partial class Test
524+
{
525+
[DllImport("DoesNotExist", EntryPoint = "EntryW", ExactSpelling = false, CharSet = CharSet.Auto)]
526+
public static extern void [|Method|]();
527+
}
528+
""";
529+
string fixedSourceNoSuffix = """
530+
531+
using System.Runtime.InteropServices;
532+
partial class Test
533+
{
534+
[LibraryImport("DoesNotExist", EntryPoint = "EntryW")]
535+
public static partial void {|CS8795:Method|}();
536+
}
537+
""";
538+
await VerifyCodeFixAsync(source, fixedSourceNoSuffix, ConvertToLibraryImportKey);
539+
string fixedSourceWithASuffix = """
540+
541+
using System.Runtime.InteropServices;
542+
partial class Test
543+
{
544+
[LibraryImport("DoesNotExist", EntryPoint = "EntryWA")]
545+
public static partial void {|CS8795:Method|}();
546+
}
547+
""";
548+
await VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A,");
549+
}
550+
551+
[Fact]
552+
public async Task SuffixAPresent_ExactSpelling_False_AutoCharSet_Provides_No_Suffix_And_Both_Suffix_Fixes()
553+
{
554+
string source = """
555+
556+
using System.Runtime.InteropServices;
557+
partial class Test
558+
{
559+
[DllImport("DoesNotExist", EntryPoint = "EntryA", ExactSpelling = false, CharSet = CharSet.Auto)]
560+
public static extern void [|Method|]();
561+
}
562+
""";
563+
string fixedSourceNoSuffix = """
564+
565+
using System.Runtime.InteropServices;
566+
partial class Test
567+
{
568+
[LibraryImport("DoesNotExist", EntryPoint = "EntryA")]
569+
public static partial void {|CS8795:Method|}();
570+
}
571+
""";
572+
await VerifyCodeFixAsync(source, fixedSourceNoSuffix, ConvertToLibraryImportKey);
573+
string fixedSourceWithASuffix = """
574+
575+
using System.Runtime.InteropServices;
576+
partial class Test
577+
{
578+
[LibraryImport("DoesNotExist", EntryPoint = "EntryAW")]
579+
public static partial void {|CS8795:Method|}();
580+
}
581+
""";
582+
await VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}W,");
583+
}
584+
585+
[Fact]
586+
public async Task SuffixPresent_ExactSpelling_False_ImplicitAnsiCharSet_Provides_No_Suffix_And_Suffix_Fix()
587+
{
588+
string source = """
589+
590+
using System.Runtime.InteropServices;
591+
partial class Test
592+
{
593+
[DllImport("DoesNotExist", EntryPoint = "EntryA", ExactSpelling = false)]
594+
public static extern void [|Method|]();
595+
}
596+
""";
597+
string fixedSourceWithNoAdditionalSuffix = """
598+
599+
using System.Runtime.InteropServices;
600+
partial class Test
601+
{
602+
[LibraryImport("DoesNotExist", EntryPoint = "EntryA")]
603+
public static partial void {|CS8795:Method|}();
604+
}
605+
""";
606+
await VerifyCodeFixAsync(source, fixedSourceWithNoAdditionalSuffix, $"{ConvertToLibraryImportKey}A,");
607+
}
608+
609+
[Fact]
610+
public async Task SuffixPresent_ExactSpelling_False_ConstantNonLiteralEntryPoint()
611+
{
612+
string source = """
613+
614+
using System.Runtime.InteropServices;
615+
partial class Test
616+
{
617+
private const string EntryPoint = "EntryA";
618+
[DllImport("DoesNotExist", EntryPoint = EntryPoint, CharSet = CharSet.Ansi, ExactSpelling = false)]
619+
public static extern void [|Method|]();
620+
}
621+
""";
622+
string fixedSourceWithASuffix = """
623+
624+
using System.Runtime.InteropServices;
625+
partial class Test
626+
{
627+
private const string EntryPoint = "EntryA";
628+
[LibraryImport("DoesNotExist", EntryPoint = EntryPoint)]
629+
public static partial void {|CS8795:Method|}();
630+
}
631+
""";
632+
await VerifyCodeFixAsync(source, fixedSourceWithASuffix, ConvertToLibraryImportKey);
633+
}
634+
635+
[Fact]
636+
public async Task SuffixPresent_Implicit_ExactSpelling_False_Offers_Suffix_Fix()
637+
{
638+
string source = """
639+
640+
using System.Runtime.InteropServices;
641+
partial class Test
642+
{
643+
[DllImport("DoesNotExist", CharSet = CharSet.Ansi)]
644+
public static extern void [|MethodA|]();
645+
}
646+
""";
647+
string fixedSourceWithASuffix = """
648+
649+
using System.Runtime.InteropServices;
650+
partial class Test
651+
{
652+
[LibraryImport("DoesNotExist")]
653+
public static partial void {|CS8795:MethodA|}();
654+
}
655+
""";
656+
await VerifyCodeFixAsync(source, fixedSourceWithASuffix, ConvertToLibraryImportKey);
657+
}
658+
659+
[Fact]
660+
public async Task SuffixPresent_ExactSpelling_False_NameOfEntryPoint()
661+
{
662+
string source = """
663+
664+
using System.Runtime.InteropServices;
665+
partial class Test
666+
{
667+
private const string FooA = "BarA";
668+
[DllImport("DoesNotExist", EntryPoint = nameof(FooA), CharSet = CharSet.Ansi, ExactSpelling = false)]
669+
public static extern void [|Method|]();
670+
}
671+
""";
672+
string fixedSourceWithASuffix = """
673+
674+
using System.Runtime.InteropServices;
675+
partial class Test
676+
{
677+
private const string FooA = "BarA";
678+
[LibraryImport("DoesNotExist", EntryPoint = nameof(FooA))]
679+
public static partial void {|CS8795:Method|}();
680+
}
681+
""";
682+
await VerifyCodeFixAsync(source, fixedSourceWithASuffix, ConvertToLibraryImportKey);
683+
}
684+
685+
[Fact]
686+
public async Task SuffixPresent_ExactSpelling_False_ImplicitEntryPointName()
687+
{
688+
string source = """
689+
690+
using System.Runtime.InteropServices;
691+
partial class Test
692+
{
693+
[DllImport("DoesNotExist", CharSet = CharSet.Ansi, ExactSpelling = false)]
694+
public static extern void [|MethodA|]();
695+
}
696+
""";
697+
string fixedSourceWithASuffix = """
698+
699+
using System.Runtime.InteropServices;
700+
partial class Test
701+
{
702+
[LibraryImport("DoesNotExist")]
703+
public static partial void {|CS8795:MethodA|}();
704+
}
705+
""";
706+
await VerifyCodeFixAsync(source, fixedSourceWithASuffix, ConvertToLibraryImportKey);
707+
}
708+
493709
[Fact]
494710
public async Task PreserveSigFalseSignatureModified()
495711
{

0 commit comments

Comments
 (0)