Fix LibraryImportDiagnosticsAnalyzer and DownlevelLibraryImportDiagnosticsAnalyzer missing diagnostics when StringMarshalling is set#126691
Conversation
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
|
It seems Copilot could not make a fix before timing out. |
Add tests verifying that SYSLIB1051 (ParameterTypeNotSupported) is correctly reported for StringBuilder parameters when StringMarshalling is set to Utf16 or Utf8, both as standalone parameters and alongside string parameters with [Out] attribute. Regression test for #126687 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@danmoseley I had copilot crank locally for quite a while and it couldn't figure it out but it did add some regression tests that should cover your scenario and pass without any product changes. Can you get me a complog of your build that didn't report the diagnostic? Is it possible that you have |
There was a problem hiding this comment.
Pull request overview
This PR targets a regression in LibraryImportDiagnosticsAnalyzer where SYSLIB1051 is not reported for StringBuilder parameters when LibraryImportAttribute.StringMarshalling is specified, leading to silently incorrect forwarder stub generation.
Changes:
- Added analyzer unit tests asserting SYSLIB1051 (
ParameterTypeNotSupported) is reported forStringBuilderparameters withStringMarshallingset (Utf16/Utf8) and without it. - Added a reproduction-style test case for
string+[Out] StringBuilderparameter combinations.
This comment has been minimized.
This comment has been minimized.
@jkoritzinsky I updated the repro (top post #126687) to include a global.json -- can you still not repro with that? |
|
@jkoritzinsky also added the install command for preview 3. Using preview 2, it doesn't repro. I verified this by hand again, and it does repro for me with global.json pointing to 11.0.100-preview.3.26170.106 C:\temp\repro>type global.json | find /i "ver"
"version": "11.0.100-preview.3.26170.106",
C:\temp\repro>type program.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
Console.WriteLine("If you see this, SYSLIB1051 was NOT reported.");
static partial class NativeMethods
{
[LibraryImport("kernel32.dll", StringMarshalling = StringMarshalling.Utf16)]
internal static partial int GetVolumeNameForVolumeMountPointW(
string volumeMountPoint,
[Out] StringBuilder volumeName,
int bufferLength);
}
C:\temp\repro>dotnet build
Restore complete (0.3s)
info NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
doit net11.0 succeeded (0.1s) → bin\Debug\net11.0\doit.dll
Build succeeded in 0.9s
and |
If I understand right then there are two bugs -- the error no longer firing, and the generated code not working (as I discovered in my original PR) the issue with the generated code pasted above (I think) is that it doesn't have |
…16 is set When LibraryImportGenerator creates a non-forwarder stub with an inner local DllImport function, it did not forward StringMarshalling.Utf16 as CharSet=Unicode. This caused any types forwarded to the runtime marshaller (e.g. StringBuilder) to default to Ansi encoding, producing incorrect results for Unicode APIs. Add CharSet=CharSet.Unicode to the inner DllImport attribute when StringMarshalling.Utf16 is specified on the LibraryImport, matching the existing behavior in CreateForwarderDllImport. Fix for #126687 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@copilot please update the diagnostics analyzer to skip the SYSLIB1050 diagnostic when a LibraryImport method has an implementation marked with our GeneratedCodeAttribute pointing to our generator. |
…plementation from our generator Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/408e36a5-d92b-4944-8b37-13c9d31754fd Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Head branch was pushed to by a user without write access
Done in commit b2bacee. Both |
|
@copilot I want you to only skip the |
…enerated implementation; always run CalculateDiagnostics Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/301361db-1d3f-4105-871a-90438d6a01df Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Done in commit d4687fc. Both analyzers now pass |
|
@copilot the analyzer is now returning two diagnostics, one in the generated code and one in the non-generated code. Please update the analyzer to only report diagnostics with the location in the non-generated source. |
…tial implementation part and filter by syntax tree Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/9fbadd4b-a632-4127-9b12-2bad0f80873f Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Fixed in 351839b. The root cause was that with |
Description
LibraryImportDiagnosticsAnalyzerandDownlevelLibraryImportDiagnosticsAnalyzerfail to report diagnostics (e.g. SYSLIB1051) for[LibraryImport]methods whenStringMarshallingis set. This is a regression introduced in preview.3 when diagnostic reporting was moved from the generators to separate analyzer classes. Additionally, the generated inner[DllImport]stub omitsCharSet = CharSet.UnicodewhenStringMarshalling.Utf16is set, causing incorrect runtime marshalling of forwarded types (e.g.StringBuilder).Root cause: The source generator marks its generated stub output with
[GeneratedCode]. Roslyn's generated-code heuristics then also classify the user'spartialmethod declaration as generated code. WithConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None), both analyzers silently skipped all[LibraryImport]methods. Additionally, withAnalyze | ReportDiagnosticsenabled,RegisterSymbolActionfires for both the partial definition and partial implementation as separateIMethodSymbolinstances, which could produce duplicate diagnostics — one in user source and one in generated source. The generated implementation syntax (which has a body) also caused a spurious SYSLIB1050 (InvalidAttributedMethodSignature) becauseGetDiagnosticIfInvalidMethodForGenerationrejects methods with a body.Reproduction:
Changes
LibraryImportDiagnosticsAnalyzer: ChangedGeneratedCodeAnalysisFlags.None→Analyze | ReportDiagnosticsso the analyzer runs on and reports diagnostics for methods whose partial declaration is classified as generated code. AddedPartialDefinitionPartguard to skip the partial implementation part (avoiding duplicate diagnostics fromRegisterSymbolActionfiring for both parts). AddedIsGeneratedByOurGeneratorhelper. When iteratingDeclaringSyntaxReferences, the generated implementation syntax (body present + our[GeneratedCode]) is skipped to find the user's partial declaration.GetDiagnosticIfInvalidMethodForGenerationis skipped (viaskipInvalidMethodCheckflag) when our generator has already produced an implementation — since the method must have been valid for the generator to run — butCalculateDiagnosticsalways runs to catch other issues. Diagnostics are filtered bySyntaxTreeto only report those located in the user's (non-generated) source.DownlevelLibraryImportDiagnosticsAnalyzer: Applied the sameGeneratedCodeAnalysisFlags.Analyze | ReportDiagnosticsfix, the samePartialDefinitionPartguard, the same selective SYSLIB1050 guard, and the sameSyntaxTreediagnostic location filter.LibraryImportGenerator.CreateTargetDllImportAsLocalStatement: Now forwardsCharSet = CharSet.Unicodeto the inner[DllImport]whenStringMarshalling.Utf16is set, using the sharedCreateEnumExpressionSyntaxhelper (extracted to class-level) to ensure consistency with the forwarder stub.DownlevelLibraryImportGenerator.CreateTargetDllImportAsLocalStatement: Applied the sameCharSet = CharSet.Unicodeforwarding fix using the sharedCreateEnumExpressionSyntaxhelper (also extracted to class-level in the downlevel generator).DownlevelLibraryImportGenerator.CreateForwarderDllImport: Updated to use the shared class-levelCreateEnumExpressionSyntaxhelper.Diagnostics.cs): AddedStringBuilderNotSupported_ReportsDiagnosticandStringBuilderNotSupported_WithStringParam_ReportsDiagnosticregression tests coveringStringBuilderwith and withoutStringMarshallingvariants.Compiles.cs): AddedForwardedTypesWithStringMarshalling_InnerDllImportHasCharSettest verifying that the inner[DllImport]hasCharSet.UnicodewhenStringMarshalling.Utf16is set and noCharSetargument when other values are used. Fixed test assertion to useEndsWithto handle theglobal::qualified name emitted by the generator. Fixed theDllImportattribute predicate to useEndsWith("DllImportAttribute")instead ofContains("DllImport")to avoid false matches onDefaultDllImportSearchPathsAttribute.Testing
LibraryImportGenerator,DownlevelLibraryImportGenerator) build successfully with zero errors and warnings.AddDisableRuntimeMarshallingAttributeFixerTestswhich validated the duplicate diagnostic fix.