Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"rollForward": false
},
"purview.build": {
"version": "0.9.0",
"version": "0.9.1",
"commands": [
"purview-build"
],
Expand Down
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Purview.BuildSdk 2.0.0 - managed file, do not edit
# Purview.BuildSdk 2.0.1 - managed file, do not edit
#
# This file is written by Purview.BuildSdk and is replaced whenever the SDK version changes. The version
# on the first line is how the SDK recognises a file it owns: a .editorconfig without that marker is
Expand Down
12 changes: 7 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,19 @@ The compiler's contract is not ours to redefine. Every generated union must sati
## Diagnostics rules

- Prefix `UNI`. Every reported rule is in `AnalyzerReleases.Unshipped.md` (RS2008). Suppression
descriptors are not release-tracked. `UNI0000` and `UNI9001` are suppression descriptors, not rules: they
suppress `CA1034` and `CA1515` on a union's public nested `IUnionMembers` provider.
descriptors are not release-tracked. `UNI0000`, `UNI9001` and `UNI9002` are suppression descriptors, not
rules: `UNI0000` suppresses `CA1034` and `UNI9001` suppresses `CA1515` on a union's public nested
`IUnionMembers` provider, and `UNI9002` suppresses `CA1000` on a generic union's provider's static `Create`
members.
- The blocking policy lives beside the descriptors (`DiagnosticLibrary.IsBlocking`); there is one policy
for both hosts.
- A rule is reported once. The analyzer reports per-target findings; the generator applies the same
findings to `GeneratorResult.ShouldProcess` and reports only `UNI9000`.
- Do not suppress warnings with `NoWarn`, `#pragma warning disable` or `SuppressMessage` in product code.
The only suppressions kept repo-wide are `Purview.BuildSdk`'s `PDS0006` for `CA1815` on a union
declaration, this package's `UNI0000` `DiagnosticSuppressor` for `CA1034` and `UNI9001` for `CA1515` on a
union's `IUnionMembers` provider, and the deliberate `RSEXPERIMENTAL006` at the `ITypeSymbol.IsUnion` call
in a test.
declaration, this package's `UNI0000` `DiagnosticSuppressor` for `CA1034`, `UNI9001` for `CA1515` and
`UNI9002` for `CA1000` on a union's `IUnionMembers` provider, and the deliberate `RSEXPERIMENTAL006` at the
`ITypeSymbol.IsUnion` call in a test.

## Packaging rules

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- A `UNI0000` `DiagnosticSuppressor` that suppresses `CA1034` and a `UNI9001` suppressor that suppresses
`CA1515` on the public nested `IUnionMembers` provider the C# union contract requires. Both ship with the
package's analyzer, so a surface-C union needs no suppression attribute.
- A `UNI9002` `DiagnosticSuppressor` that suppresses `CA1000` (do not declare static members on generic
types) on a generic union's `IUnionMembers` provider static `Create` members. The members are the
contract's creation members and the provider is necessarily generic when the union is, so `CA1000` cannot
be satisfied without removing the union semantics. This lets the `purview-dev/results`
`ProvidedOption<T>.IUnionMembers` provider drop its `SuppressMessage` workaround.
- A `UNI0005` code fix that makes the case type and every containing type public.

### Fixed
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@ provider owns its one-line `Create` bodies, because a source generator cannot ad
declared.

The provider must be **public**, so a strict analyzer set reports `CA1034` (nested types should not be
visible) and `CA1515` (public types should be internal). The package's `UNI0000` and `UNI9001` suppression
descriptors handle both for an `IUnionMembers` provider nested in a union; no attribute is needed. The nested
shape is mandated by the language, not a design choice.
visible) and `CA1515` (public types should be internal). A generic union's provider is itself generic, so its
static `Create` members are reported as `CA1000` (do not declare static members on generic types). The
package's `UNI0000`, `UNI9001` and `UNI9002` suppression descriptors handle all three for an `IUnionMembers`
provider nested in a union; no attribute is needed. The nested shape is mandated by the language, not a
design choice.

### D — hand-written `Union<T1, T2>`

Expand Down Expand Up @@ -152,8 +154,9 @@ takes `int?`, and `TryGetValue(out int)` returns false for null — a null value

The generated union is `CA1815`-clean: `Purview.BuildSdk`'s `PDS0006` suppresses the warning for a union
declaration, because a union is matched by its active case rather than compared by value. Surface C is
`CA1034`- and `CA1515`-clean: the package's `UNI0000` and `UNI9001` suppression descriptors cover the
language-mandated public nested `IUnionMembers` provider. No `#pragma` is needed.
`CA1034`-, `CA1515`- and `CA1000`-clean: the package's `UNI0000`, `UNI9001` and `UNI9002` suppression
descriptors cover the language-mandated public nested `IUnionMembers` provider and its static `Create`
members. No `#pragma` is needed.

## Diagnostics

Expand Down
10 changes: 6 additions & 4 deletions docs/wiki/Diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ whether it can emit and reports only `UNI9000`. A rule is reported once.
Every error blocks its union: the generated constructors, storage and access members are one coherent shape,
so there is no partial output. `UNI0015` is the only warning and does not block.

`UNI0000` and `UNI9001` are **suppression descriptors**, not reported rules. `UNI0000` suppresses `CA1034`
(nested types should not be visible) and `UNI9001` suppresses `CA1515` (public types should be internal) on a
union's public nested `IUnionMembers` provider, which the C# union contract requires. Add either id to
`NoWarn` to turn that suppression off.
`UNI0000`, `UNI9001` and `UNI9002` are **suppression descriptors**, not reported rules. `UNI0000` suppresses
`CA1034` (nested types should not be visible) and `UNI9001` suppresses `CA1515` (public types should be
internal) on a union's public nested `IUnionMembers` provider, which the C# union contract requires.
`UNI9002` suppresses `CA1000` (do not declare static members on generic types) on that provider's static
`Create` members, which are the contract's creation members and are necessarily generic when the union is.
Add any of these ids to `NoWarn` to turn that suppression off; a consumer should not need to.

## Details

Expand Down
4 changes: 2 additions & 2 deletions docs/wiki/Getting-Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ Option<int> some = new Some<int>(42);
```

A nested `IUnionMembers` provider is the alternative when another generator must see the case list; the
provider is public, so the package's `UNI0000` and `UNI9001` suppressors handle the resulting `CA1034` and
`CA1515`. See [Union Design](Union-Design.md).
provider is public, so the package's `UNI0000`, `UNI9001` and `UNI9002` suppressors handle the resulting
`CA1034`, `CA1515` and `CA1000`. See [Union Design](Union-Design.md).

## No generator

Expand Down
11 changes: 11 additions & 0 deletions docs/wiki/Interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,21 @@ follow-up in `purview-dev/results`.
Do not hand-write the constructors to satisfy Results: the union generator would then treat the union as
hand-written and the declared and existing lists would disagree (`UNI0012`/`UNI0014`).

A hand-written `[Union]` union whose cases live on a nested `IUnionMembers` provider — the shape the Results
tests use — needs no `SuppressMessage` attribute for `CA1000`. The package's `UNI9002` suppression descriptor
answers the rule on the provider's static `Create` members, which are generic whenever the union is. See
[Diagnostics](Diagnostics.md).

### Follow-up

> `purview-dev/results`: add the two-generator integration test once `Purview.Unions` is on a CI-safe package
> feed.
>
> `purview-dev/results`: once a `Purview.Unions` version carrying `UNI9002` is on the feed, bump
> `PurviewUnionsVersion` in `Directory.Packages.props` and delete the `CA1000` `SuppressMessage` from
> `ProvidedOption<T>.IUnionMembers` in `src/tests/SourceGenerator.UnitTests/GeneratedTestUnions.cs`. Verify by
> raising `CA1000` to `warning` in a temporary project-local `.editorconfig` under that test project: the build
> must report no `CA1000` for the provider's static `Create` members.

## Purview.Telemetry

Expand Down
8 changes: 4 additions & 4 deletions docs/wiki/Union-Design.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ Design points:
- **No equality, `ToString`, `Deconstruct`, `Match` or `Switch`** for generated unions in v1. A union is
matched by its active case; `CA1815` is expected and `PDS0006` answers it. A `record` union gets the
compiler's record equality over the tag and fields.
- **Surface C is `CA1034`- and `CA1515`-clean.** The provider must be a public nested interface, so the
package's `UNI0000` and `UNI9001` suppression descriptors suppress the nesting and visibility warnings for
an `IUnionMembers` provider inside a union; a differently named nested type, or one outside a union, keeps
them.
- **Surface C is `CA1034`-, `CA1515`- and `CA1000`-clean.** The provider must be a public nested interface,
so the package's `UNI0000`, `UNI9001` and `UNI9002` suppression descriptors suppress the nesting, visibility
and static-member-on-generic-type warnings for an `IUnionMembers` provider inside a union (for `CA1000`, on
its static `Create` members); a differently named nested type, or one outside a union, keeps them.
- **Accessibility is never widened.** The partial declaration repeats the union's accessibility, and every
case type must be at least as accessible as the union because a public creation member cannot expose a less
accessible parameter type. An internal union can therefore use internal case types; a public union needs
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "unions",
"version": "1.0.0-prerelease.2",
"version": "1.0.0-prerelease.3",
"license": "MIT",
"homepage": "https://purview.dev/projects/unions/",
"bugs": {
Expand Down
17 changes: 12 additions & 5 deletions skills/purview-unions-diagnostics/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ never reported twice.
Every error blocks its union: the generated constructors, storage and access members are one coherent
shape, so there is no partial output to emit. `UNI0015` is the only warning and does not block.

`UNI0000` and `UNI9001` are **suppression descriptors**, not reported rules. `UNI0000` suppresses `CA1034`
(nested types should not be visible) and `UNI9001` suppresses `CA1515` (public types should be internal) on a
union's public nested `IUnionMembers` provider, which the C# union contract requires. Add either id to
`NoWarn` to turn that suppression off.
`UNI0000`, `UNI9001` and `UNI9002` are **suppression descriptors**, not reported rules. `UNI0000` suppresses
`CA1034` (nested types should not be visible) and `UNI9001` suppresses `CA1515` (public types should be
internal) on a union's public nested `IUnionMembers` provider, which the C# union contract requires.
`UNI9002` suppresses `CA1000` (do not declare static members on generic types) on the provider's static
`Create` members: they are the contract's creation members, and a generic union's provider is necessarily
generic. Add any of these ids to `NoWarn` to turn that suppression off; you should not need to.

## `UNI0001` — not partial

Expand Down Expand Up @@ -104,7 +106,12 @@ The generator catches its own failures so one malformed union cannot discard eve

There is deliberately no `NoWarn`/`#pragma` in this stack. The suppressions are `DiagnosticSuppressors`, not
reported rules: `Purview.BuildSdk`'s `PDS0006` for `CA1815` on a union declaration, and this package's
`UNI0000` for `CA1034` and `UNI9001` for `CA1515` on a union's nested `IUnionMembers` provider.
`UNI0000` for `CA1034`, `UNI9001` for `CA1515` and `UNI9002` for `CA1000` on a union's nested
`IUnionMembers` provider.

`UNI9002` covers a generic union's provider static `Create` members, which `CA1000` reports on. A
hand-written `[Union]` generic union's provider needs no `SuppressMessage` attribute for them; the
suppression descriptor already answers the finding.

## Checklist

Expand Down
8 changes: 5 additions & 3 deletions skills/purview-unions-modelling/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ one-line `Create` bodies and delegates to the generated `CreateCaseN` methods. U
want the case list visible to other generators.

Because `IUnionMembers` must be **public**, a strict analyzer set reports `CA1034` and `CA1515` (nested types
should not be visible; public types should be internal). The package's `UNI0000`/`UNI9001` suppression
descriptors handle both for an `IUnionMembers` provider nested in a union; no attribute is needed. The nested
shape is mandated by the language, not a design choice.
should not be visible; public types should be internal). A generic union's provider is itself generic, so its
static `Create` members are reported as `CA1000` (do not declare static members on generic types). The
package's `UNI0000`/`UNI9001`/`UNI9002` suppression descriptors handle all three for an `IUnionMembers`
provider nested in a union; no attribute is needed. The nested shape is mandated by the language, not a
design choice.

### D — hand-written `Purview.Unions.Union<T1, T2>`

Expand Down
2 changes: 1 addition & 1 deletion skills/purview-unions-quickstart/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ for inspecting the declared source in isolation; it is not a way to make a faili
| --- | --- | --- |
| A | `[GenerateUnion(typeof(int), typeof(string))]` | Every case can be named in an attribute argument |
| B | partial static factory signatures | A case names a type parameter |
| C | a nested `IUnionMembers` provider | A generic union, or another generator must see the cases (the provider is public, but `UNI0000`/`UNI9001` suppress the resulting `CA1034`/`CA1515`) |
| C | a nested `IUnionMembers` provider | A generic union, or another generator must see the cases (the provider is public, but `UNI0000`/`UNI9001`/`UNI9002` suppress the resulting `CA1034`/`CA1515`/`CA1000`) |
| D | `Union<T1, T2>` | Exactly two cases, or no generator |

## Checklist
Expand Down
Loading
Loading