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
9 changes: 5 additions & 4 deletions docs/ANcpLua.Analyzers.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Roslyn analyzers + code fixes covering modern C# correctness pitfalls, ASP.NET C

## Package family

- **[ANcpLua.Analyzers](https://github.com/ANcpLua/ANcpLua.Analyzers)** — this package; the `AL00xx`–`AL18xx` Roslyn diagnostics.
- **[ANcpLua.Analyzers](https://github.com/ANcpLua/ANcpLua.Analyzers)** — this package; the `AL10xx`–`AL18xx` Roslyn diagnostics.
- **[ANcpLua.NET.Sdk](https://github.com/ANcpLua/ANcpLua.NET.Sdk)** — MSBuild SDK that auto-injects this analyzer + the bundled `editorconfig` severity profile.
- **[ANcpLua.Roslyn.Utilities](https://github.com/ANcpLua/ANcpLua.Roslyn.Utilities)** — shared Roslyn helpers + the `Guard.*` API the `AL12xx` band promotes.

Expand Down Expand Up @@ -108,7 +108,7 @@ Each ID links to a per-rule page under [`docs/rules/`](rules/) with severity, ca

## Consumer-side severity profile (`AlAnalysisMode`)

Set `<AlAnalysisMode>` in your csproj to switch the whole `AL00xx`–`AL18xx` band in one line instead of dropping editorconfig files:
Set `<AlAnalysisMode>` in your csproj to switch the whole `AL10xx`–`AL18xx` band in one line instead of dropping editorconfig files:

```xml
<PropertyGroup>
Expand All @@ -127,9 +127,10 @@ The property is exposed via the analyzer NuGet's `buildTransitive/ANcpLua.Analyz

## See also

- [Per-rule pages](rules/) — one markdown file per `AL00xx`–`AL18xx` rule with severity, category, code-fix status, and description.
- [Per-rule pages](rules/) — one markdown file per `AL10xx`–`AL18xx` rule with severity, category, code-fix status, and description.
- [Editorconfig profiles](editorconfig/) — three drop-in severity profiles: `Default`, `AllRulesAsErrors`, `AllRulesDisabled`. Same content ships inside the NuGet under `buildTransitive/editorconfig/`.
- [SARIF rule manifest](ANcpLua.Analyzers.sarif) — SARIF v2.1.0 catalog of every `AL00xx`–`AL18xx` rule (id, name, severity, category, helpUri). Consume from Sonar bridges, GitHub Advanced Security uploads, or IDE rule-catalog tools.
- [SARIF rule manifest](ANcpLua.Analyzers.sarif) — SARIF v2.1.0 catalog of every `AL10xx`–`AL18xx` rule (id, name, severity, category, helpUri). Consume from Sonar bridges, GitHub Advanced Security uploads, or IDE rule-catalog tools.
- [Migration catalog](migration-catalog.md) — the `AL0xxx` → `AL1xxx` rename map from the 2.0.0 break. Use to resolve stale `dotnet_diagnostic.AL0xxx.severity = …` editorconfig entries.
- [`AnalyzerReleases.Unshipped.md`](../src/ANcpLua.Analyzers/AnalyzerReleases.Unshipped.md) — release-tracking manifest with `ClassName` attribution per Microsoft NetAnalyzers convention.

## Generated File
Expand Down
140 changes: 140 additions & 0 deletions docs/migration-catalog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# AL Analyzer Renumber Catalog

<!-- <auto-generated /> This file is generated by tools/ANcpLua.Analyzers.DocsGenerator. -->

AL0xxx → AL1xxx rename map from the ANcpLua.Analyzers 2.0.0 break. The 2.0.1 analyzer no longer emits any `AL0xxx` ID, so consumers carrying `dotnet_diagnostic.AL0xxx.severity = …` entries from an older version need this table to find the replacement.

## Summary

`89` renames across `9` 100-wide domain bands. The `AL0xxx` range is reserved for sibling packages (`ANcpLua.AotReflection`, `ANcpLua.ExtensibleEnumMirror`, `ANcpLua.DiscriminatedUnion`) and is not reissued by this analyzer.

## Completion audit

`AlIdMigrationCatalog.Validate()` enforces these invariants on every generator run + on every CI build (via `AnalyzerConventionTests.AlIdMigrationCatalog_StructuralInvariants_Hold`):

- No duplicate `OldId` across the catalog.
- No duplicate `NewId` across the catalog.
- Every `NewId` matches `^AL1[0-8]\d{2}$` — sits inside the post-renumber band `AL1000..AL1899`.
- Every `OldId` matches `^AL\d{4}$` — well-formed legacy diagnostic ID.

No hardcoded `ExpectedCount` constant — count is a consequence of the invariants, not a property worth asserting on its own.

## Renames by band

| Band | Count |
| -- | --: |
| AL1000..AL1099 Correctness | 13 |
| AL1100..AL1199 ASP.NET Core | 10 |
| AL1200..AL1299 Roslyn Utilities | 21 |
| AL1300..AL1399 Async / reliability | 15 |
| AL1400..AL1499 AOT / trim | 10 |
| AL1500..AL1599 Roslyn-author hygiene | 6 |
| AL1600..AL1699 Package / version | 7 |
| AL1700..AL1799 Style | 4 |
| AL1800..AL1899 Agent governance | 3 |

## Old → New mapping

Sorted by `NewId` — scan to find your current ID's predecessor.

| Old ID | New ID | Title |
| -- | -- | -- |
| `AL0001` | `AL1000` | Prohibit reassignment of primary constructor parameters |
| `AL0002` | `AL1001` | Don't repeat negated patterns |
| `AL0003` | `AL1002` | Don't divide by constant zero |
| `AL0004` | `AL1003` | Use pattern matching when comparing Span with constants |
| `AL0005` | `AL1004` | Use SequenceEqual when comparing Span with non-constants |
| `AL0006` | `AL1005` | Field name conflicts with primary constructor parameter |
| `AL0007` | `AL1006` | GetSchema should be explicitly implemented |
| `AL0008` | `AL1007` | GetSchema must return null and not be abstract |
| `AL0009` | `AL1008` | Don't call IXmlSerializable.GetSchema |
| `AL0011` | `AL1009` | Avoid lock keyword on non-Lock types |
| `AL0014` | `AL1010` | Prefer pattern matching for null and zero comparisons |
| `AL0015` | `AL1011` | Normalize null-guard style |
| `AL0016` | `AL1012` | Combine declaration with subsequent null-check |
| `AL0020` | `AL1100` | IFormCollection requires explicit attribute |
| `AL0021` | `AL1101` | Multiple structured form sources |
| `AL0022` | `AL1102` | Mixed form collection and DTO |
| `AL0023` | `AL1103` | Unsupported form type |
| `AL0024` | `AL1104` | Form and body conflict |
| `AL0080` | `AL1105` | Missing resilience configuration |
| `AL0081` | `AL1106` | Missing health checks |
| `AL0082` | `AL1107` | Consider using configuration for connection string |
| `AL0084` | `AL1108` | Missing service discovery |
| `AL0106` | `AL1109` | Avoid Task.Run in ASP.NET Core request handlers |
| `AL0028` | `AL1200` | Use IsEqualTo extension |
| `AL0029` | `AL1201` | Use HasAttribute extension |
| `AL0030` | `AL1202` | Use type hierarchy extension |
| `AL0031` | `AL1203` | Use operation extension |
| `AL0032` | `AL1204` | Use OrEmpty extension |
| `AL0033` | `AL1205` | Use ToImmutableArrayOrEmpty extension |
| `AL0034` | `AL1206` | Use WhereNotNull extension |
| `AL0035` | `AL1207` | Use symbol display string extension |
| `AL0036` | `AL1208` | Use null-guard helper |
| `AL0037` | `AL1209` | Use TryParse extension |
| `AL0039` | `AL1210` | Use StringComparison extension |
| `AL0040` | `AL1211` | Use attribute argument extraction extension |
| `AL0045` | `AL1212` | Use null-or-empty guard helper |
| `AL0046` | `AL1213` | Use null-or-whitespace guard helper |
| `AL0047` | `AL1214` | Use zero-guard helper |
| `AL0048` | `AL1215` | Use non-negative guard helper |
| `AL0049` | `AL1216` | Use positive-guard helper |
| `AL0050` | `AL1217` | Use empty-guid guard helper |
| `AL0051` | `AL1218` | Use defined-enum guard helper |
| `AL0125` | `AL1219` | Use *Any* string comparison extension |
| `AL0137` | `AL1220` | Use Guard.* helpers instead of throw helpers |
| `AL0057` | `AL1300` | Avoid async void methods |
| `AL0058` | `AL1301` | Avoid lock on 'this' |
| `AL0059` | `AL1302` | Avoid lock on typeof(T) |
| `AL0060` | `AL1303` | Avoid lock on string |
| `AL0104` | `AL1304` | Prefer 'await using' for IAsyncDisposable |
| `AL0105` | `AL1305` | Avoid blocking calls in async methods |
| `AL0111` | `AL1306` | Avoid SQL string interpolation in CommandText |
| `AL0112` | `AL1307` | Avoid fire-and-forget task discard |
| `AL0114` | `AL1308` | Prefer TryParse over Parse |
| `AL0115` | `AL1309` | Empty catch block swallows exceptions |
| `AL0116` | `AL1310` | Exception details leaked in HTTP response |
| `AL0117` | `AL1311` | Unnecessary LINQ materialization |
| `AL0118` | `AL1312` | Read-modify-write without transaction |
| `AL0126` | `AL1313` | Forward CancellationToken to invocations that support it |
| `AL0138` | `AL1314` | Use Math.Round/MathF.Round overload with explicit MidpointRounding |
| `AL0041` | `AL1400` | Method with [AotTest] or [TrimTest] must return int |
| `AL0042` | `AL1401` | [AotTest]/[TrimTest] method should return 100 on success |
| `AL0043` | `AL1402` | [TrimSafe] code must not call methods with [RequiresUnreferencedCode] |
| `AL0044` | `AL1403` | [AotSafe] code must not call methods with [RequiresDynamicCode] |
| `AL0052` | `AL1404` | [AotSafe] code must not call [AotUnsafe] code |
| `AL0053` | `AL1405` | Unnecessary [AotUnsafe] attribute |
| `AL0094` | `AL1406` | Avoid 'dynamic' keyword in AOT-published code |
| `AL0095` | `AL1407` | Avoid Expression.Compile() in AOT context |
| `AL0101` | `AL1408` | Activator.CreateInstance is not AOT-safe |
| `AL0102` | `AL1409` | Type.GetType with dynamic name is not AOT-safe |
| `AL0103` | `AL1500` | Closed hierarchy match is not exhaustive |
| `AL0119` | `AL1501` | Avoid storing ISymbol in source generator models |
| `AL0120` | `AL1502` | Use IIncrementalGenerator instead of ISourceGenerator |
| `AL0121` | `AL1503` | Avoid NormalizeWhitespace in source generators |
| `AL0122` | `AL1504` | [DuckDbTable] type must be partial |
| `AL0123` | `AL1505` | Conflicting [DuckDbColumn] ordinal values |
| `AL0017` | `AL1600` | Hardcoded package version detected |
| `AL0018` | `AL1601` | Version.props not imported |
| `AL0019` | `AL1602` | Undefined version variable |
| `AL0054` | `AL1603` | Diagnostic missing from documentation |
| `AL0055` | `AL1604` | Diagnostic missing from release notes |
| `AL0056` | `AL1605` | Diagnostic documentation mismatch |
| `AL0127` | `AL1606` | Outdated MAF ecosystem package version |
| `AL0025` | `AL1700` | Anonymous function can be made static |
| `AL0026` | `AL1701` | Avoid DateTime/DateTimeOffset time accessors |
| `AL0027` | `AL1702` | Avoid legacy JSON library |
| `AL0139` | `AL1703` | Use implicit type when type is apparent |
| `AL0128` | `AL1800` | Destructive Loom tool must require approval |
| `AL0129` | `AL1801` | Loom tool should declare its side effect |
| `AL0130` | `AL1802` | Loom tool should declare required capabilities |

## Generated file

Regenerate with:

```bash
dotnet run --project tools/ANcpLua.Analyzers.DocsGenerator # rewrite docs + editorconfig profiles + migration-catalog
dotnet run --project tools/ANcpLua.Analyzers.DocsGenerator -- --check # CI guard; fails if anything is stale
dotnet run --project tools/ANcpLua.Analyzers.DocsGenerator -- --audit # print catalog statistics
```
4 changes: 4 additions & 0 deletions tests/ANcpLua.Analyzers.Tests/ANcpLua.Analyzers.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,9 @@
<ItemGroup>
<ProjectReference Include="..\..\src\ANcpLua.Analyzers\ANcpLua.Analyzers.csproj"/>
<ProjectReference Include="..\..\src\ANcpLua.Analyzers.CodeFixes\ANcpLua.Analyzers.CodeFixes.csproj"/>
<!-- Exposes the AlIdMigrationCatalog so AnalyzerConventionTests can run
Validate() at CI time — the actual safety net for hand-transcription
drift in the 89-row Entries array. -->
<ProjectReference Include="..\..\tools\ANcpLua.Analyzers.DocsGenerator\ANcpLua.Analyzers.DocsGenerator.csproj"/>
</ItemGroup>
</Project>
12 changes: 12 additions & 0 deletions tests/ANcpLua.Analyzers.Tests/AnalyzerConventionTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reflection;
using ANcpLua.Analyzers.DocsGenerator;
using Microsoft.CodeAnalysis.Diagnostics;

namespace ANcpLua.Analyzers.Tests;
Expand Down Expand Up @@ -47,6 +48,17 @@ public void AllDiagnosticIdsMatchExpectedFormat() {
}
}

/// <summary>
/// Highest-leverage safety net for the hand-transcribed AL0xxx → AL1xxx rename map.
/// <c>--check</c> on a dev machine catches OUTPUT drift but only after someone runs
/// <c>./build.sh CheckDocs</c>; this test catches SOURCE drift (typo, duplicate,
/// leak into a sibling-package band) at CI on every PR. Mandatory.
/// </summary>
[Fact]
public void AlIdMigrationCatalog_StructuralInvariants_Hold() {
AlIdMigrationCatalog.Validate();
}

[Fact]
public void AllAnalyzersDeclareDiagnosticIdConsts() {
const BindingFlags AnyStatic = BindingFlags.Public | BindingFlags.NonPublic
Expand Down
Loading
Loading