CodeFixer: move unsafe from method modifier to method body#128304
CodeFixer: move unsafe from method modifier to method body#128304EgorBo wants to merge 9 commits into
Conversation
|
Tagging subscribers to this area: @dotnet/area-system-text-json |
There was a problem hiding this comment.
Pull request overview
This PR adds a new ILLink Roslyn analyzer + code fix (IL5005) intended to move the unsafe member modifier into a method-/accessor-scoped unsafe { ... } block, and wires an MSBuild property to enable the analyzer. It also applies the fixer output broadly across System.Text.Json and some shared library code.
Changes:
- Add IL5005 (
UnsafeModifierOnMethod) analyzer and code fix provider, plus associated resource strings and MSBuild property plumbed through ILLink analyzer infrastructure. - Update build logic (
eng/liveILLink.targets) and System.Text.Json project settings to enable the analyzer. - Mechanical rewrites across many BCL files replacing
unsafemodifiers with method-bodyunsafe { ... }blocks and inserting// SAFETY-TODOcomments.
Reviewed changes
Copilot reviewed 58 out of 58 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tools/illink/src/ILLink.Shared/SharedStrings.resx | Adds IL5005 title/message strings. |
| src/tools/illink/src/ILLink.Shared/DiagnosticId.cs | Introduces DiagnosticId.UnsafeModifierOnMethod = 5005 (DEBUG-only). |
| src/tools/illink/src/ILLink.RoslynAnalyzer/UnsafeModifierOnMethodAnalyzer.cs | New analyzer that reports IL5005 on members using the unsafe modifier (when enabled). |
| src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.cs | Adds MSBuild property name constant to enable the new analyzer (DEBUG-only). |
| src/tools/illink/src/ILLink.RoslynAnalyzer/build/Microsoft.NET.ILLink.Analyzers.props | Makes the new MSBuild property compiler-visible. |
| src/tools/illink/src/ILLink.CodeFix/UnsafeModifierOnMethodCodeFixProvider.cs | New code fix to move unsafe into the body and insert audit comments. |
| src/tools/illink/src/ILLink.CodeFix/Resources.resx | Adds the code fix title resource. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.UnsignedNumber.cs | Applies fixer output: wraps stackalloc usage in unsafe {} and adds SAFETY-TODO. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.StringSegment.cs | Applies fixer output to multiple writer helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs | Applies fixer output to string escaping helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.SignedNumber.cs | Applies fixer output for numeric formatting helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Raw.cs | Applies fixer output around transcoding + pooled buffer logic. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Float.cs | Applies fixer output for float formatting paths. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Double.cs | Applies fixer output for double formatting paths. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Decimal.cs | Applies fixer output for decimal formatting paths. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs | Applies fixer output in property-name numeric writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs | Applies fixer output in property-name numeric writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs | Applies fixer output in literal property-name writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs | Applies fixer output in Guid property-name writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.FormattedNumber.cs | Applies fixer output in formatted-number property writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs | Applies fixer output in float property writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs | Applies fixer output in double property writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs | Applies fixer output in decimal property writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs | Applies fixer output in DateTimeOffset property writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs | Applies fixer output in DateTime property writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs | Applies fixer output in base64 property writers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs | Applies fixer output in writer start-property escaping helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.Date.cs | Applies fixer output in Date/DateTimeOffset trim formatting helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.cs | Applies fixer output in string quoting/escaping helper. |
| src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs | Applies fixer output in stackalloc-based Truncate helper. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.String.cs | Applies fixer output in UTF-16->UTF-8 transcoding read helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt128Converter.cs | Applies fixer output in converter read/write helpers using stackalloc / pools. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs | Applies fixer output in TimeSpan converter stackalloc paths. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeOnlyConverter.cs | Applies fixer output in TimeOnly converter stackalloc paths. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int128Converter.cs | Applies fixer output in converter read/write helpers using stackalloc / pools. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/HalfConverter.cs | Applies fixer output in converter read/write helpers and constant parsing. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs | Applies fixer output around ValueStringBuilder(stackalloc) usage. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateOnlyConverter.cs | Applies fixer output in DateOnly converter stackalloc formatting. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/CharConverter.cs | Applies fixer output around stackalloc + CopyString. |
| src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs | Applies fixer output around escape handling / parsing helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs | Applies fixer output in multi-segment literal validation helper. |
| src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs | Applies fixer output in ValueTextEquals transcoding helper. |
| src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.Unescaping.cs | Applies fixer output across unescaping and base64 helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.netstandard.cs | Applies fixer output in netstandard span scanning helper (vectorized path). |
| src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs | Applies fixer output in escaped DateTime/Guid parsing helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.cs | Applies fixer output to GetPath, introducing unsafe {} + SAFETY-TODO. |
| src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs | Applies fixer output in escaping helpers using stackalloc/ArrayPool. |
| src/libraries/System.Text.Json/src/System/Text/Json/JsonEncodedText.cs | Applies fixer output in TranscodeAndEncode stackalloc/ArrayPool path. |
| src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.TryGetProperty.cs | Applies fixer output in property lookup helpers. |
| src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs | Applies fixer output in TextEquals transcoding helper. |
| src/libraries/System.Text.Json/src/System.Text.Json.csproj | Enables the new analyzer via project property. |
| src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs | Applies fixer output to remove unsafe modifier and wrap bodies in unsafe {}. |
| src/libraries/Common/src/System/Text/AsciiPolyfills.cs | Applies fixer output to move unsafe from signature into body. |
| src/libraries/Common/src/Polyfills/StringBuilderPolyfills.cs | Applies fixer output to move unsafe from signature into body. |
| src/libraries/Common/src/Polyfills/SinglePolyfills.cs | Applies fixer output to move unsafe from signature into body. |
| src/libraries/Common/src/Polyfills/EncodingPolyfills.cs | Applies fixer output to move unsafe from signatures into bodies and wrap pointer helpers. |
| eng/liveILLink.targets | Treats the new MSBuild property as requiring live ILLink wiring. |
…r's own output shape
Previously BlockNeedsWrap used body.DescendantNodes() to look for an existing
'unsafe' block anywhere in the body, which incorrectly suppressed wrapping when:
- the outer body contained other top-level unsafe operations alongside a manual
'unsafe { }' block, or
- a nested local function / lambda happened to contain its own 'unsafe' block.
In either case, removing the method-level 'unsafe' modifier without wrapping the
outer body produces code that no longer compiles. Tighten the skip condition so
we only treat the body as already-wrapped when it's literally
'{ unsafe { ... } }' — the shape the fixer itself emits — preserving idempotence
without false positives.
The same descendant-scan was also in ExpressionBodyNeedsWrap, where it could
only ever match nested lambdas (since an unsafe statement can't be the body of
an arrow expression directly); simplified to always wrap when an expression body
is present.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
PTAL @agocke @jkotas @jjonescz @tannergooding @333fred I tested it on STJ and it worked as I expected, it gave up on a few cases (see desc.) but I was able to easily fix those by hand.
We might need unsafe-as-expression for it then. |
Adds a code fixer (analyzer
IL5005+IL5006, bothinfoseverity, gated behind MSBuild propertyEnableUnsafeV2MigrationAnalyzer) that mechanically migrates a BCL assembly to the unsafe-evolution semantics.Run with (example for STJ):
What it does
IL5005 —
unsafeon a type or delegate declarationclass,struct,interface,record,record struct,delegateunsafeunconditionally (per spec: has no meaning under updated rules).IL5006 —
unsafeon a member declarationNext, we move
unsafefrom method modifer to wrap the entire method body: base(...)/: this(...)=> _x/=> _x = value) left aloneevent Action E;)What it gives up on
#if/#elif/#else/#endifdotnet formatrun produces different active branches per TFM, which makesdotnet formatwrite<<<<<<< Unmerged changemarkers into the source.unsafe { ... }blockunsafe, inheriting fromunsafe class Cunsafetoken themselves. Migrating implicit-context members would need data-flow analysis.: base(deref(p)))unsafemodifier on any ctor that has an initializer at all (initializer is an unsafe context under unsafe-v2 per spec).yieldinside)unsafe { yield ... }is CS1629.unsafeinwards by hand.scopedlocals assigned to outer parametersunsafe { ... }puts the local in a narrower scope than the parameter, triggering CS9080. Example:unsafeas a lambda modifier; the analyzer never sees one.unsafeThe
scopedcase:(no warning if
unsafeis moved back to a method modifier; needs manual rework after the bulk pass)Notes
dotnet formatruns don't loop.// SAFETY-TODO:comment wording is provisional — feedback on a better template is welcome.unsafe { ... }wrap.