Add structured Publicize item metadata - #234
Merged
Merged
Conversation
Additive: the colon-string Include form is unchanged and still the public contract. Both forms parse into the same AssemblyPlan. Naming a type structurally sweeps its members, while the colon form publicizes only the type itself. The divergence is deliberate — it is what makes "all members of this type" expressible without a regex — and both readings are supported permanently. Sweep settings move from assembly-global to per-scope so a namespace or type can carry its own filters. With no structured items present this resolves to the old two rungs, which is why the characterization suite is untouched. Generic arity is spelled with braces only; a backtick is rejected so that Parameters never has to reconcile two spellings later.
Commas inside a nested argument list were counted as arity, so
Holder{Dictionary{K,V}} lowered to Holder`2 and matched nothing.
IncludeSubNamespaces and IncludeTypeContents are settled in the design but not built. Ignoring them would publicize more than the author asked for, which is the one failure mode this parser is strict about.
Readability pass over the review comments: the type-name lowering loop split into a segment scan plus a per-segment rewrite, error accumulation made explicit instead of leaning on Fail's return value, and the scope tie-break and prefix-coverage checks spelled out rather than packed into compound conditions. The design rationale that was duplicated across doc comments now lives only in docs/publicization-semantics.md.
Resolution ranked a scope above the assembly, so adding a structured Publicize item could publicize a namespace inside an assembly that a DoNotPublicize item had denied. The frozen forms exist to prevent that widening, so the assembly deny keeps the precedence its type-level counterpart already has. Also drops the namespace copy a type scope carried: the reflection name already holds it, and nothing read the second copy.
Reverts the veto from eb90abe. Its rationale was that a scope overriding an assembly deny would let the structured form widen what gets publicized -- but rung 3 has always allowed exactly that for colon-form targets, so the veto did not uphold the principle, it applied it to one of the two forms. Two targets of identical specificity resolved differently based only on which syntax spelled them. The assembly rule is now just the loosest scope, which is where Resolve already fell through to, so the drop was all that had to go. That leaves one precedence mechanism instead of a lattice plus an out-of-band drop -- the second mechanism being the kind of thing the rewrite exists to retire. The real hazard was always composition (a shared .props denies, a consumer reopens), which a veto on scopes could not have addressed anyway since the colon form leaks the same way. Reported as a warning instead.
krafs
force-pushed
the
structured-scope-syntax
branch
from
August 3, 2026 18:47
e863f7b to
2f262c1
Compare
A deny scope naming a type is the same statement as the colon form, so it gets the same absolute treatment. Namespace scopes deliberately do not stop the walk: they name no type, and stopping would leave an explicitly publicized nested type public but unreachable.
krafs
force-pushed
the
structured-scope-syntax
branch
from
August 3, 2026 19:03
58d71b8 to
0ca8465
Compare
The doc was chartered as a characterization baseline for a future rewrite. The structured form is that rewrite, and it is being specified in the same file, so the header now describes both jobs and where design rationale does and does not belong.
Only the count is read today, so 'Pair{,}' worked. The names are
reserved for 'Parameters', and tightening this once they mean
something would break items that build now.
Whether a scope nested in another inherits that scope's filters or the assembly's is undecided. The two readings differ only in which members end up public, so picking one now and changing it later would silently rewrite working builds. Rejecting the ambiguous item defers the choice for the price of one attribute.
The regex matches dnlib's reflection name, the '+' and backtick spelling 'Type' refuses in the same item, so honoring it on a scope would freeze that spelling in a second place. The assembly-level pattern is already slated to be re-anchored or dropped; extending it to scopes only widens what that has to migrate. Assembly-level behavior is unchanged and still applies inside scopes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a structured item form where
NamespaceandTypeare their own metadata instead of being packed into theIncludestring. Additive — the colon-string form is unchanged.Splitting
Namespaceout is what removes the namespace-vs-nested-type ambiguity, so dots insideTypealways mean nesting.docs/publicization-semantics.mdhas the full behavior, including why the reserved qualifiers are rejected rather than ignored, and why several things that would otherwise be accepted are refused instead.Include="Asm:N.T"still publicizes only the type. Deliberate divergence, and what retires the wart that "all members of one type" was regex-only.DoNotPublicizeon a type outranks every scope, because that form's behavior is frozen. An assembly-wideDoNotPublicizedoes not: it is the loosest scope there is, so a scope naming part of the assembly is more specific and carves an exception out of it. That matches the carve-out a colon-form member target has always had over an assembly deny, so the answer no longer depends on which syntax spelled an equally specific target, and "deny the assembly except namespace N" stays expressible. The composition hazard that argues for a veto — a shared.propsdenies, a consumer reopens — is not specific to scopes and is not fixable by one, since the colon form leaks the same way; it is reported as a warning instead.Hasherdid not cover the new scopes, so two differentTypetargets would have shared a cache entry. The colon-form sets have a second collision of the same shape, tracked separately — fixing that one invalidates every existing cache entry, so it does not belong here.SweepSettingsandPublicizeScopeare init-only, since oneSweepSettingsis resolved per scope and then shared by every type that scope covers. That needs anIsExternalInitpolyfill on netstandard2.0; thenet10.0test projects bind their own throughInternalsVisibleTowithout ambiguity.Three things are refused rather than resolved, on the same principle: anything this form accepts now, it accepts forever, and any semantics it leaves observable-but-undecided gets depended on.
MemberPatternon a scope. The regex matches dnlib's reflection name —+for nesting,`2for arity — which is the exact spellingTyperefuses in the same item, so honoring it on a scope would freeze that spelling in a second place and recreate the two-spellings fork one level down. The assembly-level pattern is unchanged and still applies inside every scope, and it is already slated for re-anchoring or removal; extending it here would only widen what that has to migrate.Pair{,}. Only the count is read today, so it would work, but the names are reserved forParametersand tightening this once they mean something would break items that build now.No README yet. The syntax is only half-landed until member-level metadata arrives, and documenting it now would mean shipping a contract that has to explain what it cannot do. Note this makes the release, not the README, the point where these semantics freeze — worth deciding deliberately before this ships in one.