Clears the two production-code SonarQube rules called out next on the cleanup list:
S1168 (1 instance) — GetQuery<T>() no longer returns IQueryable<T>? / null; now returns a non-nullable IQueryable<T> that is Enumerable.Empty<T>().AsQueryable() when reconstruction isn't possible. Three tests in LinqQueryBreakdownTests.cs that asserted Is.Null are renamed and now assert Is.Empty.
CA1822 (5 instances) — five member methods that don't access instance state are now static. Three are public methods on the Markdown.SqlServer generators (binary-breaking for NuGet consumers); two are private helpers (non-breaking). The auto-fixer also caught one additional private helper not in the original list (LinqExpressionVisitor.ExtractSelectExpression).
Three commits, each verified with dotnet build -c Release and the full test suite (1180/1180 passing throughout):
Commit
Rule
Breaking?
What changed
2c23ba4
S1168
yes
GetQuery<T> signature + empty returns + test contract updates
Markdown.SqlServer.QueryBreakdownGenerator.GenerateMermaidDiagram(QueryBreakdown, string?), Markdown.SqlServer.SqlStatementGenerator.GenerateSequenceDiagram(ISqlBreakdown, string?) + GenerateEntityRelationshipDiagram(IEnumerable<string>, string?). Plus a bonus ExtractSelectExpression the fixer caught.
Notes
The S1168 site was previously tracked as a "won't-fix / deliberate design" decision (memory note from 2026-05-22). The user explicitly opted to code-fix it this round; the memory has been updated to record the reversal.
The CA1822 public-method change leaves the Snowflake / LinqToSql / PostgreSql wrapper generators with an unused _baseGenerator field. That S4487 / IDE0052 cleanup is intentionally a follow-up — keeps the breaking-change blast radius limited to the three methods explicitly named in the BREAKING CHANGE: footer of 5202d93.
Test plan
dotnet build Strata.SqlTools.QueryBreakdown.sln -c Release — 0 errors after each commit
Gitea Actions sonarqube.yml runs the SonarScanner upload on this PR
Re-query /api/issues/search?componentKeys=sql-utilities&resolved=false&facets=rules after the scan completes; S1168 and CA1822 should both drop to 0
## Summary
Clears the two production-code SonarQube rules called out next on the cleanup list:
- **S1168** (1 instance) — `GetQuery<T>()` no longer returns `IQueryable<T>?` / `null`; now returns a non-nullable `IQueryable<T>` that is `Enumerable.Empty<T>().AsQueryable()` when reconstruction isn't possible. Three tests in `LinqQueryBreakdownTests.cs` that asserted `Is.Null` are renamed and now assert `Is.Empty`.
- **CA1822** (5 instances) — five member methods that don't access instance state are now `static`. Three are public methods on the `Markdown.SqlServer` generators (binary-breaking for NuGet consumers); two are private helpers (non-breaking). The auto-fixer also caught one additional private helper not in the original list (`LinqExpressionVisitor.ExtractSelectExpression`).
Three commits, each verified with `dotnet build -c Release` and the full test suite (1180/1180 passing throughout):
| Commit | Rule | Breaking? | What changed |
|---|---|---|---|
| `2c23ba4` | S1168 | yes | `GetQuery<T>` signature + empty returns + test contract updates |
| `3c602a4` | CA1822 (private) | no | `LinqExpressionVisitor.ExtractMemberName`, `Markdown.Expressions.ExpressionGenerator.GenerateMermaidDiagram(Expression)` |
| `5202d93` | CA1822 (public) | yes | `Markdown.SqlServer.QueryBreakdownGenerator.GenerateMermaidDiagram(QueryBreakdown, string?)`, `Markdown.SqlServer.SqlStatementGenerator.GenerateSequenceDiagram(ISqlBreakdown, string?)` + `GenerateEntityRelationshipDiagram(IEnumerable<string>, string?)`. Plus a bonus `ExtractSelectExpression` the fixer caught. |
## Notes
- The S1168 site was previously tracked as a "won't-fix / deliberate design" decision (memory note from 2026-05-22). The user explicitly opted to code-fix it this round; the memory has been updated to record the reversal.
- The CA1822 public-method change leaves the Snowflake / LinqToSql / PostgreSql wrapper generators with an unused `_baseGenerator` field. That `S4487 / IDE0052` cleanup is intentionally a follow-up — keeps the breaking-change blast radius limited to the three methods explicitly named in the `BREAKING CHANGE:` footer of `5202d93`.
## Test plan
- [x] `dotnet build Strata.SqlTools.QueryBreakdown.sln -c Release` — 0 errors after each commit
- [x] `dotnet test ... -c Release --no-build` — 1180 passed, 0 failed
- [ ] Gitea Actions `sonarqube.yml` runs the SonarScanner upload on this PR
- [ ] Re-query `/api/issues/search?componentKeys=sql-utilities&resolved=false&facets=rules` after the scan completes; S1168 and CA1822 should both drop to 0
Replaces `null` returns from `SqlBreakdownBase.GetQuery<T>()` and its four
overrides (LinqQueryBreakdown, SqlServer/PostgreSql/Snowflake QueryBreakdown)
with `Enumerable.Empty<T>().AsQueryable()`, and tightens the signature from
`IQueryable<T>?` to `IQueryable<T>`. The "we can't reconstruct" semantic
now lives in "the query yields zero rows" rather than a nullable return,
which is what callers in LINQ pipelines actually want.
Three LinqQueryBreakdownTests tests asserting `Is.Null` are renamed and
updated to assert `Is.Empty`.
BREAKING CHANGE: SqlBreakdownBase.GetQuery<T> and the SqlServer / PostgreSql /
Snowflake / LinqToSql QueryBreakdown.GetQuery<T> overrides no longer return
`IQueryable<T>?`; they now return a non-nullable `IQueryable<T>` that is
empty when reconstruction isn't possible. External NuGet consumers null-
checking the result must switch to `.Any()` / `Is.Empty` checks instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two private methods that don't touch instance state get the `static`
keyword:
- `LinqExpressionVisitor.ExtractMemberName`
- `Markdown.Expressions.ExpressionGenerator.GenerateMermaidDiagram(Expression)`
Both have only intra-class callers, so this is a non-breaking change —
the call sites continue to work unchanged under C# method-resolution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three public methods on the SqlServer-namespaced Markdown generators no
longer touch instance state and now carry the `static` keyword:
- `Markdown.SqlServer.QueryBreakdownGenerator.GenerateMermaidDiagram(QueryBreakdown, string?)`
- `Markdown.SqlServer.SqlStatementGenerator.GenerateSequenceDiagram(ISqlBreakdown, string?)`
- `Markdown.SqlServer.SqlStatementGenerator.GenerateEntityRelationshipDiagram(IEnumerable<string>, string?)`
Plus one private bonus the analyzer caught on the same pass:
- `LinqExpressionVisitor.ExtractSelectExpression` → static (non-breaking).
Internal callers in the Snowflake/LinqToSql/PostgreSql wrapper classes
and in the test fixtures are updated to the type-name form
(`SqlServer.SqlStatementGenerator.GenerateSequenceDiagram(...)`).
The wrappers retain their `_baseGenerator` field for now even though it
is no longer used — that S4487 / unused-field cleanup is its own commit.
BREAKING CHANGE: External NuGet consumers calling
`generatorInstance.GenerateMermaidDiagram(...)`,
`generatorInstance.GenerateSequenceDiagram(...)`, or
`generatorInstance.GenerateEntityRelationshipDiagram(...)` on the
SqlServer-namespaced generators must switch to type-name form, e.g.
`Markdown.SqlServer.SqlStatementGenerator.GenerateSequenceDiagram(...)`.
Calls through the Snowflake / LinqToSql / PostgreSql wrapper classes are
unaffected at the call site.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DRYs the four `Enumerable.Empty<T>().AsQueryable()` returns added in
`2c23ba4` (S1168 fix) into a single `public static IQueryable<T>
GetEmptyQueryable<T>()` helper on `LinqQueryBreakdown`. No behavior
change — all 1180 tests stay green.
Co-Authored-By: Thom Lamb <thomlamb@gmail.com>
Commit 5202d93 made the SqlServer-namespaced Markdown generator methods
static, which left the LinqToSql / PostgreSql / Snowflake wrapper
classes' instance methods delegating to nothing but a static call.
SonarQube re-flagged those 10 wrapper methods as CA1822 on the next
scan.
This sweep:
- Makes all 10 wrapper instance methods `static` (`dotnet format` driven).
- Makes `Markdown.LinqToSql.QueryBreakdownGenerator.GenerateCombinedDiagram`
static preemptively — it composes two static helpers and would otherwise
be the next-iteration cascade flag.
- Removes the now-dead `_baseGenerator` field and its initializing
constructor from all six dialect wrappers (LinqToSql / PostgreSql /
Snowflake × QueryBreakdownGenerator + SqlStatementGenerator). The
classes keep their implicit parameterless constructor so `new
Snowflake.QueryBreakdownGenerator()` still compiles.
- Updates the one test call site (`GenerateCombinedDiagram`) the fixer
didn't catch to use type-name form.
BREAKING CHANGE: External NuGet consumers calling
`instance.Generate*Diagram(...)` on `Markdown.LinqToSql.*`,
`Markdown.PostgreSql.*`, or `Markdown.Snowflake.*` generators must
switch to type-name form, e.g. `Markdown.Snowflake.SqlStatementGenerator.GenerateSequenceDiagram(...)`.
The class types and parameterless constructors remain — only the call
syntax for these methods changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Clears the two production-code SonarQube rules called out next on the cleanup list:
GetQuery<T>()no longer returnsIQueryable<T>?/null; now returns a non-nullableIQueryable<T>that isEnumerable.Empty<T>().AsQueryable()when reconstruction isn't possible. Three tests inLinqQueryBreakdownTests.csthat assertedIs.Nullare renamed and now assertIs.Empty.static. Three are public methods on theMarkdown.SqlServergenerators (binary-breaking for NuGet consumers); two are private helpers (non-breaking). The auto-fixer also caught one additional private helper not in the original list (LinqExpressionVisitor.ExtractSelectExpression).Three commits, each verified with
dotnet build -c Releaseand the full test suite (1180/1180 passing throughout):2c23ba4GetQuery<T>signature + empty returns + test contract updates3c602a4LinqExpressionVisitor.ExtractMemberName,Markdown.Expressions.ExpressionGenerator.GenerateMermaidDiagram(Expression)5202d93Markdown.SqlServer.QueryBreakdownGenerator.GenerateMermaidDiagram(QueryBreakdown, string?),Markdown.SqlServer.SqlStatementGenerator.GenerateSequenceDiagram(ISqlBreakdown, string?)+GenerateEntityRelationshipDiagram(IEnumerable<string>, string?). Plus a bonusExtractSelectExpressionthe fixer caught.Notes
_baseGeneratorfield. ThatS4487 / IDE0052cleanup is intentionally a follow-up — keeps the breaking-change blast radius limited to the three methods explicitly named in theBREAKING CHANGE:footer of5202d93.Test plan
dotnet build Strata.SqlTools.QueryBreakdown.sln -c Release— 0 errors after each commitdotnet test ... -c Release --no-build— 1180 passed, 0 failedsonarqube.ymlruns the SonarScanner upload on this PR/api/issues/search?componentKeys=sql-utilities&resolved=false&facets=rulesafter the scan completes; S1168 and CA1822 should both drop to 0