docs(messages): document interpolation anti-patterns and :L format spec (#2441)
## Summary Captures the durable learnings from the message-interpolation work (PRs #2434, #2436, #2437, #2438, #2440) as reference documentation. **Doc-only PR — no code changes.** The original Phase 2 audit (PR #2435) was development scaffolding and was closed unmerged once Phase 3 consumed it. This PR replaces it with proper reference docs that future authors can consult. ## What's added ### `dev-docs/string-handling.md` - Promote `RawInterpolatedStringHandler` from a one-line note to a proper section listing all APIs that accept it (messages, OPL, gumps, packets). - Document the `:L` lowercase format specifier. - New comprehensive **"Interpolation Anti-Patterns"** section covering 8 patterns with before/after examples — applies to any handler-aware API: 1. Ternary with interpolated branches 2. Switch expression with interpolated arms 3. Pre-built local typed as `string` 4. `.ToString()` (or any string-returning method) inside a hole 5. String concatenation inside a hole 6. `string.Format` feeding a handler-aware API 7. LINQ-built strings inside a hole 8. Pre-built concat var ### `dev-docs/networking-packets.md` - Add **"Player-Facing Message APIs"** section listing `Mobile` / `Item` / `NetState` message methods with their handler overloads. - Note the `IBroadcastFilter` pattern for new spatial-broadcast helpers. ### `dev-docs/property-lists.md`, `dev-docs/gump-system.md` - Cross-reference the new anti-patterns section. - Add explicit `.ToString()` inside holes warning to property-lists (it had no such guidance before). ### `dev-docs/claude-skills/` - Mirror the same content (condensed) in `modernuo-string-handling.md`, `modernuo-networking.md`, `modernuo-property-lists.md`, `modernuo-gump-system.md`. - Add audit rule #17 to `modernuo-code-audit.md` covering all 8 anti-patterns with severity WARNING, plus the `:L` format spec. ### `CLAUDE.md` - Add audit rule #18 summarizing the interpolation anti-patterns + `:L`, pointing to `dev-docs/string-handling.md` for details. ## Why this matters Before this PR there was no documentation explaining when an interpolated string call site silently allocates a string despite the receiving API providing a handler overload. The Phase 3 cleanup (PRs #2436/#2437/#2438) discovered ~28 such sites in the codebase; without these docs the same patterns would re-emerge. The new audit rule + CLAUDE.md entry will catch them at write time.
This commit is contained in:
parent
5f9fa88220
commit
9ea1b54758
10 changed files with 431 additions and 6 deletions
|
|
@ -95,6 +95,22 @@ The compiler generates different calls for each:
|
|||
|
||||
**Rule of thumb**: The only text that should appear as bare literals in the interpolated string is `\t` (the argument separator). Everything else — including string constants — must be inside `{}` holes.
|
||||
|
||||
### No `.ToString()` Inside Holes
|
||||
|
||||
`IPropertyList`'s interpolated string handler formats values directly into a pooled buffer via `ISpanFormattable.TryFormat` — no intermediate `string` allocation per hole. An explicit `.ToString()` defeats this:
|
||||
|
||||
```csharp
|
||||
// BAD — .ToString() allocates a string, then the handler copies its chars
|
||||
list.Add(1060658, $"{"Charges"}\t{_charges.ToString()}");
|
||||
|
||||
// GOOD — the handler formats _charges directly with no intermediate string
|
||||
list.Add(1060658, $"{"Charges"}\t{_charges}");
|
||||
```
|
||||
|
||||
Same applies to `.String()` on `TextDefinition`, `.GetValue()`, and any method that returns a freshly allocated `string` — drop the call and let the handler format the underlying value directly.
|
||||
|
||||
The full list of interpolation anti-patterns (ternaries, switch expressions, pre-built locals, `string.Format`, concat, LINQ in holes) applies equally to `IPropertyList.Add($"...")`. See [`dev-docs/string-handling.md`](string-handling.md#interpolation-anti-patterns) for the full reference.
|
||||
|
||||
### Cliloc as Argument (Use `:#` Format Specifier)
|
||||
|
||||
When a cliloc argument is itself another cliloc number (i.e., the argument should resolve to localized text), use the `:#` format specifier on the integer — **not** a `"#number"` string:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue