dennisdoomen / dennisdoomen/reflectify
[Feature]: Add trimming and NativeAOT annotations so consumers stay warning-free
- Dominant language
- C#
- Stars
- 80
- Forks
- 6
- Avg merge
- 1d 12m
- Merged PRs (30d)
- 11
Description
### Background and motivation
Reflectify contains no trimming or AOT annotations at all: no `[DynamicallyAccessedMembers]`, no `[RequiresUnreferencedCode]`, no `[UnconditionalSuppressMessage]`.
This matters more for Reflectify than for a normal library, because of how it is delivered. It is a **content-only** package: the source is compiled directly into the consumer's assembly. So when a consumer publishes with `PublishTrimmed` or NativeAOT, the trim analyzer walks Reflectify's code *as if the consumer wrote it*, and reports warnings against the consumer's own project. The consumer cannot suppress them at the package boundary and cannot fix them without editing generated source.
Every hierarchy walk in `Reflector` is a warning site. For example:
```csharp
var allProperties = typeToReflect.GetProperties(flags);
```
and
```csharp
return type
.GetMethods(flags)
.SingleOrDefault(m => m.Name == methodName && HasSameParameters(parameterTypes, m));
```
Both take a `Type` with no annotation, so the analyzer cannot prove the members survive trimming and emits IL2070-class warnings. Same for `GetMethod("Equals", ...)` in `OverridesEquals`, the `"$"` and `"PrintMembers"` lookups in the record detection, and every `GetCustomAttributes` call.
Annotating the API would let consumers who are trim-clean stay trim-clean. Because the parameters are `Type`, the annotations mostly land on the public extension methods and flow inward.
`PolySharp` is already a dependency and can generate `DynamicallyAccessedMembersAttribute`, `RequiresUnreferencedCodeAttribute` and `UnconditionalSuppressMessageAttribute` for the older targets, so the multi-targeting story is already solved.
### Alternative Concerns
- **Annotate everything with `[DynamicallyAccessedMembers]`.** The correct fix, and it preserves trimming for consumers. The cost is that the annotations are viral: a caller passing an unannotated `Type` just moves the warning up one level. That is arguably fine, since it moves the warning to where the type is actually known.
- **Mark the reflection-heavy methods `[RequiresUnreferencedCode]` instead.** Much less work and honest about what the code does, but it makes the whole library unusable warning-free in trimmed apps, which is a worse outcome than annotating.
- **Do nothing and document that Reflectify is not trim-safe.** Cheapest, and defensible for a reflection library, but it effectively rules out NativeAOT consumers, which is a growing constituency.
- **Add a trimming test.** Whatever is chosen, a small `PublishTrimmed` / `PublishAot` smoke project in the build would prevent regressions and prove the annotations actually work. That could be a first step on its own, since it would quantify how many warnings there are today.
Worth noting that some methods, such as the record detection heuristics, genuinely cannot be made trim-safe, since they look up members by string name that the compiler generates. Those are honest `[RequiresUnreferencedCode]` or suppression candidates.
### Are you willing help with a pull-request?
No
Contributor guide
Research direction
Start by reviewing the reflection sites in Reflector and the public extension methods that pass Type values inward. Check how PolySharp supplies the trimming attributes, then separate annotatable hierarchy and attribute lookups from record-detection cases that need RequiresUnreferencedCode or suppression. Add the proposed PublishTrimmed or PublishAot smoke project and verify the resulting consumer warnings.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100