Skip to content

Declare EmbeddedAttribute as partial so it can coexist with Reflectify - #153

Merged
dennisdoomen merged 1 commit into
mainfrom
dennisdoomen-make-embeddedattribute-partial
Aug 10, 2026
Merged

Declare EmbeddedAttribute as partial so it can coexist with Reflectify#153
dennisdoomen merged 1 commit into
mainfrom
dennisdoomen-make-embeddedattribute-partial

Conversation

@dennisdoomen

Copy link
Copy Markdown
Owner

The problem

Pathy ships as source and compiles into the consuming assembly. It declares Microsoft.CodeAnalysis.EmbeddedAttribute so its own types can be marked [Embedded]. That attribute tells Roslyn to hide those types from every other assembly, including InternalsVisibleTo friends.

Reflectify is another source-only package that declares the same attribute for the same reason. A project that uses both packages ended up with two declarations of one type in a single compilation:

CS0101: The namespace 'Microsoft.CodeAnalysis' already contains a definition for 'EmbeddedAttribute'

This is not theoretical. FluentAssertions already consumes Reflectify, so any project combining it with Pathy would hit this.

The fix

Declare the attribute as partial:

internal sealed partial class EmbeddedAttribute : Attribute

Two partial declarations of the same type merge into one type instead of colliding. Roslyn matches [Embedded] by name, so it does not care how many parts the type came from. Embedding keeps working exactly as before.

Reflectify made the matching change in dennisdoomen/reflectify#170. Both sides are needed. Neither one works alone.

Why the type-level attributes had to be removed

This part is not optional, and it is easy to undo by accident.

C# does not allow a non-AllowMultiple attribute to be repeated across the parts of a partial type. If both Pathy's part and Reflectify's part carry [AttributeUsage], the build fails:

CS0579: Duplicate 'System.AttributeUsage' attribute

So re-adding [AttributeUsage(AttributeTargets.All)] would silently bring the collision straight back, just with a different error code. Please do not add it back. It is also redundant: AttributeTargets.All is already the default when an attribute carries no [AttributeUsage], so removing it changes nothing at runtime.

[ExcludeFromCodeCoverage] was removed for the same reason.

Both parts must have an identical shape for the merge to work: same accessibility (internal), same sealed, same base type, and no type-level attributes. Reflectify's copy is now internal sealed partial class EmbeddedAttribute : System.Attribute { }. An XML <remarks> block on the declaration records this constraint for the next reader.

Analyzer suppressions

None were needed. Removing [AttributeUsage] normally triggers CA1018, MA0010 and RCS1203, and this repo escalates warnings to errors. I checked by forcing the analyzer package set on (it is currently gated on a net6.0 target framework that no project in this repo builds) and confirmed those three analyzers do fire on a plain test attribute type in this project, but none of them fire on this declaration. No #pragma and no .editorconfig change was required.

Verification

  • dotnet build -c Release on the full solution: 0 warnings, 0 errors.
  • Tests: Pathy.Specs net8.0 109 passed, Pathy.ApiVerificationTests net8.0 8 passed, 0 failures. The net472 run reports "No test is available ... make sure that test discoverer & executors are registered". I confirmed this happens on unmodified main as well, so it is a pre-existing local environment issue and not a regression from this change.
  • A scratch consumer project outside the repo compiled Pathy's sources the way a real consumer does (without PATHY_PUBLIC), next to a second file holding Reflectify's declaration, with InternalsVisibleTo pointing at a friend assembly. Results:
    • the library compiles with both declarations present,
    • the friend assembly can still use the library's own internal types,
    • the friend assembly cannot see Pathy.ChainablePath (CS0234), so embedding still works.
  • Two negative controls confirmed the reasoning rather than assumed it: reverting partial reproduces the collision (CS0260), and putting [AttributeUsage] back on both parts reproduces CS0579.

The scratch project was deleted afterwards.

…s copy

Pathy ships as source and compiles into the consuming assembly. It declares
Microsoft.CodeAnalysis.EmbeddedAttribute so its own types can be marked
[Embedded], which tells Roslyn to hide them from every other assembly,
including InternalsVisibleTo friends.

Reflectify is another source-only package by the same author and declares the
same attribute for the same reason. A project that uses both packages therefore
ended up with two declarations of one type in a single compilation, which failed
to build with CS0101.

Declaring the type as partial fixes this. Two partial declarations of the same
type merge into one type instead of colliding, and Roslyn matches [Embedded] by
name, so it does not care how many parts the type came from. Reflectify made the
same change in dennisdoomen/reflectify#170.

For the parts to merge they must have an identical shape, so the type-level
attributes had to go. C# does not allow a non-AllowMultiple attribute to be
repeated across parts of a partial type, so leaving [AttributeUsage] in place
would fail with CS0579 and defeat the purpose. Dropping it costs nothing because
AttributeTargets.All is already the default when an attribute carries no
[AttributeUsage]. [ExcludeFromCodeCoverage] was removed for the same reason.

Verified with a scratch consumer project that compiles Pathy's sources next to
Reflectify's declaration: the library builds, a friend assembly can still reach
the library's own internal types, and Pathy types remain hidden from it.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

Test Results

117 tests  ±0   117 ✅ ±0   1s ⏱️ ±0s
  3 suites ±0     0 💤 ±0 
  3 files   ±0     0 ❌ ±0 

Results for commit be3dad8. ± Comparison against base commit 6b9ac41.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant