Remove ErrorCode runtime registry; make ErrorContextKey registration idempotent#39
Merged
Merged
Conversation
ErrorCode.Create maintained a process-wide HashSet and threw InvalidOperationException the second time a code was created. That runtime guard cost more than it was worth: a duplicate surfaced as an opaque TypeInitializationException from a consumer's static initializer, it forced an internal ResetForTests plus non-parallel test collections, and it broke collectible AssemblyLoadContext / plugin reload scenarios. Meanwhile ErrorCode already compares by value, so duplicate codes are harmless, and FCE001 catches literal duplicates at build time with far better ergonomics. Remove the registry, its lock and ResetForTests; Create now only rejects null/blank codes and returns a value. Update the ErrorCode tests (creating the same code twice is now allowed and yields equal instances), drop the now-needless ErrorCode resets from the other suites, and refresh the FCE001 analyzer description and documentation (EN/FR) that described the removed runtime throw. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E6eKjRdyosK1CfnTg1XV4Z
…e and type ErrorContextKey.Create threw on any same-name re-registration, including the re-execution of an identical declaration (reloaded plugin, re-run test fixture) where nothing conflicts. Yet the registry does guard something real: keys compare by name inside error contexts, so two keys sharing a name with different value types would silently collide in the context dictionary and make the stored value unreadable through the typed API. Split the two cases instead of rejecting both. Create is now get-or-create: re-declaring a key with the same name and value type returns the already-registered instance (the first registration is canonical, so its description or provider wins by construction), while a same-named key with a different value type still throws — with a message that now names both types. GetRegisteredKeys and the documentation pipeline are unchanged. Update the ErrorContextKey tests accordingly: idempotent re-declaration returns the same instance, the first description wins, the type conflict keeps throwing, and concurrent same-key registrations all succeed and share one registration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E6eKjRdyosK1CfnTg1XV4Z
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.
Summary
Removes the process-wide runtime registry behind
ErrorCode.Create(duplicate codes are harmless at run time thanks to value equality, and FCE001 keeps flagging literal duplicates at build time) and makesErrorContextKeyregistration idempotent: re-declaring a key with the same name and value type returns the already-registered instance, while a same-named key with a different value type is still rejected. This eliminates the opaqueTypeInitializationExceptionfailures raised from consumer static initializers and repairs plugin-reload / re-run-fixture scenarios, without giving up the name↔type guarantee that protects typed context reads.Type of change
Changes
Registeredset, its lock, and the internalResetForTests().Create()no longer throwsInvalidOperationExceptionfor a code created twice; it only rejects null/blank codes. XML docs describe the value semantics and point to FCE001.Create<T>is now get-or-create. Same name + same value type returns the registered instance (the first registration is canonical, so its description or provider wins); same name + different value type still throws, with a message that now names both types.GetRegisteredKeys()andResetForTests()are unchanged. XML docs updated.ErrorCode.Createdoes not enforce uniqueness at run time and that cross-assembly duplicates surface as a documentation-pipeline warning.IDisposable/reset plumbing; the duplicate-rejection test became "creating the same code twice is allowed and produces equal instances"; the concurrency test now asserts that all parallel creations succeed and are equal.ErrorCode.ResetForTests()calls (theirErrorContextKeyresets remain).Testing
dotnet build FirstClassErrors.sln— not run: no .NET SDK was available in the authoring environment (network policy blocks installing one)dotnet test FirstClassErrors.sln— not run:FirstClassErrors.UnitTests— including the rewrittenErrorCodeTests/ErrorContextKeyTests— has not been built or executed anywhere yet; please run this before mergingFirstClassErrors.Analyzers.UnitTests) — run by theanalyzersCI workflow on the head commit (green); that workflow also buildsFirstClassErrors.Usagewith the analyzers active, which compiles the main libraryDocumentation
ErrorCode.Createand bothErrorContextKey.Createoverloads describe the new contractdoc/updated —doc/analyzers/FCE001.en.mdanddoc/analyzers/FCE001.fr.mddoc/README.fr.md) updated if user-facing behavior changed — not needed: neither README describes the runtime registry behavior; the French analyzer page was updated in sync with the English oneRelated issues
None
🤖 Generated with Claude Code
https://claude.ai/code/session_01E6eKjRdyosK1CfnTg1XV4Z