Skip to content

feat: report scan matches whose type is inaccessible (AWT193)#115

Merged
vbreuss merged 3 commits into
mainfrom
fix/scan-inaccessible-type
Jul 17, 2026
Merged

feat: report scan matches whose type is inaccessible (AWT193)#115
vbreuss merged 3 commits into
mainfrom
fix/scan-inaccessible-type

Conversation

@vbreuss

@vbreuss vbreuss commented Jul 17, 2026

Copy link
Copy Markdown
Member

A [Scan] dropped a candidate the generated container cannot name (an implementation internal to another assembly, or a private nested class) without a word: the type vanished from the container and the build stayed green. The contract side of the same feature already carries dropped interfaces through to AWT188, so an inaccessible exposure interface warned while an inaccessible implementation type did not.

The accessibility test moves out of IsScanCandidate's early-out, where it ran before marker assignability and so could only have warned once per internal type in every scanned assembly. Candidates are now carried as a ScanCandidate flagged with the accessibility verdict (mirroring ScanContractSet.Inaccessible), the scan's NamePatterns/NamespacePatterns/Exclude filters run as usual, and only a flagged candidate that survives them is reported as AWT193 and dropped. A markerless scan reports it too, since AWT183 already forces it to narrow its candidates.

IsScanCandidate now also skips a type with no source name at all (CanBeReferencedByName), which the accessibility test used to filter incidentally: <Module> and compiler-generated closures are nobody's scan match and must not reach the diagnostic.

Two consequences worth naming: an InAssembliesOf assembly whose only matches are inaccessible reports AWT193 rather than AWT140, which no longer has to hint at a missing ProjectReference; and Awaiten.Tests sets NoWarn=AWT193, because its cross-assembly scan fixture deliberately keeps an internal plugin it has no way to filter out.

A `[Scan]` dropped a candidate the generated container cannot name (an implementation `internal` to another assembly, or a private nested class) without a word: the type vanished from the container and the build stayed green. The contract side of the same feature already carries dropped interfaces through to AWT188, so an inaccessible exposure interface warned while an inaccessible implementation type did not.

The accessibility test moves out of `IsScanCandidate`'s early-out, where it ran before marker assignability and so could only have warned once per internal type in every scanned assembly. Candidates are now carried as a `ScanCandidate` flagged with the accessibility verdict (mirroring `ScanContractSet.Inaccessible`), the scan's `NamePatterns`/`NamespacePatterns`/`Exclude` filters run as usual, and only a flagged candidate that survives them is reported as AWT193 and dropped. A markerless scan reports it too, since AWT183 already forces it to narrow its candidates.

`IsScanCandidate` now also skips a type with no source name at all (`CanBeReferencedByName`), which the accessibility test used to filter incidentally: `<Module>` and compiler-generated closures are nobody's scan match and must not reach the diagnostic.

Two consequences worth naming: an `InAssembliesOf` assembly whose only matches are inaccessible reports AWT193 rather than AWT140, which no longer has to hint at a missing `ProjectReference`; and `Awaiten.Tests` sets `NoWarn=AWT193`, because its cross-assembly scan fixture deliberately keeps an internal plugin it has no way to filter out.
@vbreuss vbreuss self-assigned this Jul 17, 2026
@vbreuss vbreuss added the enhancement New feature or request label Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

Test Results

   18 files  ± 0     18 suites  ±0   7m 38s ⏱️ -59s
1 136 tests + 8  1 135 ✅ + 8  1 💤 ±0  0 ❌ ±0 
5 525 runs  +24  5 524 ✅ +24  1 💤 ±0  0 ❌ ±0 

Results for commit ffc92a6. ± Comparison against base commit bc305b8.

♻️ This comment has been updated with latest results.

Corrects the NoWarn justification in Awaiten.Tests. The old comment claimed the cross-assembly scans had no way to filter the internal fixture out, which contradicted both the diagnostic's own advice ("exclude it from the scan") and this branch's own DoesNotReportWhenNamePatternsExcludeTheType, which pins that a `!`-prefixed NamePatterns entry silences AWT193 on an inaccessible type without ever naming it. The real reason to suppress rather than filter is that a name filter would become the reason the type is skipped, when those scans exist to prove that the accessibility is.

Pins the AWT184 a markerless scan reports alongside AWT193. It was previously unasserted, so the pair was an accident rather than a decision. Both are true and complementary: AWT184 says nothing registered, AWT193 says why, which is strictly more than AWT184 said on its own before.

Covers the InternalsVisibleTo remedy the message advertises, which had no test. It holds for an internal type with an implicit constructor; an explicitly internal constructor still reports AWT104 until the sibling accessibility fix lands, so the test pins only the half that is stable here.

Rewrites AWT193's XML doc to drop a spaced-hyphen parenthetical, which the rest of Diagnostics.cs expresses with commas and colons instead.
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

🚀 Benchmark Results

Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Build Size Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 8 16.37 ns 0.108 ns 0.090 ns 0.98 144 B 1.00
Awaiten 8 16.626 ns 0.2926 ns 0.2443 ns 1.00 144 B 1.00
MsDI 8 1,424.995 ns 11.5958 ns 10.2794 ns 85.72 5688 B 39.50
Autofac 8 33,971.077 ns 193.3897 ns 180.8969 ns 2,043.63 33098 B 229.85
Jab 8 9.417 ns 0.5575 ns 0.5215 ns 0.57 96 B 0.67
PureDI 8 18.007 ns 0.2665 ns 0.2493 ns 1.08 128 B 0.89
DryIoc 8 755.827 ns 15.5558 ns 14.5509 ns 45.47 1528 B 10.61
SimpleInjector 8 11,498.094 ns 63.8556 ns 59.7306 ns 691.70 24760 B 171.94
baseline* 256 86.97 ns 0.352 ns 0.329 ns 1.11 2128 B 1.00
Awaiten 256 78.583 ns 0.2546 ns 0.2257 ns 1.00 2128 B 1.00
MsDI 256 13,607.325 ns 55.0342 ns 51.4790 ns 173.16 61016 B 28.67
Autofac 256 828,016.438 ns 3,410.3470 ns 3,190.0407 ns 10,536.96 740937 B 348.18
Jab 256 64.545 ns 0.0655 ns 0.0512 ns 0.82 2080 B 0.98
PureDI 256 73.535 ns 0.2053 ns 0.1820 ns 0.94 2112 B 0.99
DryIoc 256 45,212.776 ns 367.6702 ns 343.9189 ns 575.36 80666 B 37.91
SimpleInjector 256 339,810.743 ns 1,795.8823 ns 1,592.0029 ns 4,324.28 573060 B 269.30
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Realistic Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 245.6 ns 1.71 ns 1.51 ns 0.94 568 B 1.00
Awaiten 261.5 ns 2.54 ns 2.12 ns 1.00 568 B 1.00
MsDI 608.6 ns 6.69 ns 5.59 ns 2.33 1104 B 1.94
Autofac 6,450.8 ns 90.00 ns 84.18 ns 24.67 10784 B 18.99
Jab 198.3 ns 1.64 ns 1.53 ns 0.76 432 B 0.76
DryIoc 423.5 ns 3.12 ns 2.92 ns 1.62 944 B 1.66
SimpleInjector 703.0 ns 5.29 ns 4.95 ns 2.69 1096 B 1.93
PureDI 209.8 ns 0.76 ns 0.67 ns 0.80 632 B 1.11
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.30GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v4

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Resolve Size Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 8 7.022 ns 0.0083 ns 0.0065 ns 0.87 - NA
Awaiten 8 8.068 ns 0.2935 ns 0.2745 ns 1.00 - NA
MsDI 8 6.551 ns 0.3795 ns 0.3549 ns 0.81 - NA
Autofac 8 109.956 ns 6.0998 ns 5.7057 ns 13.64 656 B NA
Jab 8 3.793 ns 0.1363 ns 0.1275 ns 0.47 - NA
PureDI 8 4.671 ns 0.1011 ns 0.0946 ns 0.58 - NA
DryIoc 8 8.175 ns 0.3643 ns 0.3408 ns 1.01 - NA
SimpleInjector 8 9.636 ns 0.3419 ns 0.3198 ns 1.20 - NA
baseline* 256 8.092 ns 0.0144 ns 0.0121 ns 0.86 - NA
Awaiten 256 9.400 ns 0.3120 ns 0.2918 ns 1.00 - NA
MsDI 256 6.200 ns 0.3492 ns 0.3267 ns 0.66 - NA
Autofac 256 100.645 ns 2.9807 ns 2.7882 ns 10.72 656 B NA
Jab 256 46.056 ns 1.0221 ns 0.9561 ns 4.90 - NA
PureDI 256 8.093 ns 0.3020 ns 0.2825 ns 0.86 - NA
DryIoc 256 8.024 ns 0.1728 ns 0.1617 ns 0.85 - NA
SimpleInjector 256 10.260 ns 0.2417 ns 0.2261 ns 1.09 - NA

baseline* rows show the corresponding Awaiten benchmark from the most recent successful main branch build with results, for regression comparison.

The message and docs advised 'exclude it from the scan', but the Exclude
type list cannot name an inaccessible type; only a NamePatterns/
NamespacePatterns entry can. Say so.

Also record why the Awaiten.Tests suppression is project-wide NoWarn: AWT193
is emitted by the source generator, which neither #pragma warning disable nor
a per-file .editorconfig severity reaches.
@vbreuss
vbreuss enabled auto-merge (squash) July 17, 2026 13:08
@sonarqubecloud

Copy link
Copy Markdown

@vbreuss
vbreuss merged commit 8c555d8 into main Jul 17, 2026
14 checks passed
@vbreuss
vbreuss deleted the fix/scan-inaccessible-type branch July 17, 2026 13:12
github-actions Bot added a commit that referenced this pull request Jul 17, 2026
github-actions Bot added a commit that referenced this pull request Jul 17, 2026
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