Skip to content

Add PERF006 rule and fix perf_lint warnings across codebase#2318

Merged
borisbat merged 6 commits into
masterfrom
string-lint-daslib
Mar 22, 2026
Merged

Add PERF006 rule and fix perf_lint warnings across codebase#2318
borisbat merged 6 commits into
masterfrom
string-lint-daslib

Conversation

@borisbat
Copy link
Copy Markdown
Collaborator

@borisbat borisbat commented Mar 22, 2026

Summary

  • Add new perf_lint rule PERF006: detect push/push_clone/emplace on arrays inside for loops without a preceding reserve()
  • Improve existing rules: PERF003 detects character_at(s, 0) and suggests first_character(s), PERF003 now fires inside closures
  • Fix all perf_lint warnings found across daslib/, modules/, examples/, tutorials/

PERF006 — push/emplace in loop without reserve()

Detects push, push_clone, emplace on arrays inside for loops with known-length sources (array, range, fixed_array, string) without a preceding reserve().

  • Expression chain walking: traces through field access, index, deref, cast to find the root variable
  • Field path tracking: reserve(t.a, N) does not suppress warning for t.b
  • Known-length only: skips iterators, generators, and while loops
  • Conditional suppression: push inside if/else not flagged
  • inferStack reporting: warnings on generic instances show instantiation chain

Test plan

  • PERF006 test file: 9 bad patterns + 10 good patterns
  • All existing perf_lint tests: 13 warnings unchanged
  • 546 tests pass (daslib, regex, strings suites)
  • All modified .das files pass formatter check

borisbat and others added 6 commits March 21, 2026 20:55
Detect push, push_clone, emplace on arrays inside loops without a
preceding reserve() call. Uses propagateWrite-style expression chain
walking to trace through field access (self.items), index, deref,
cast to find the root variable and check scope.

Key features:
- Field path tracking: reserve(t.a, N) does not suppress warning for t.b
- Conditional suppression: push inside if/else is not flagged
- Closure suppression: push in lambda/block inside loop is not flagged
- fromGeneric-based function matching for builtin generics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detect push, push_clone, emplace on arrays inside loops without a
preceding reserve() call. Uses propagateWrite-style expression chain
walking (ExprField, ExprAt, ExprCast, etc.) to trace through field
access chains and find the root variable.

Key features:
- Field path tracking: reserve(t.a, N) does not suppress warning for t.b
- Known-length loop detection: only warns in for loops with known-length
  sources (array, range, fixed_array, string), not iterators/generators
- While loops always warn (user controls the bound)
- Conditional suppression: push inside if/else is not flagged
- Closure suppression: push in lambda/block inside loop is not flagged
- inferStack reporting: warnings on generic instances show instantiation chain
- fromGeneric-based function matching for builtin generics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add reserve() before push/emplace loops in 15 daslib files:
aot_standalone, apply, ast_cursor, coverage, dap, das_source_formatter,
daspkg, debug, decs_state, json, match, rst, spoof, templates,
typemacro_boost.

Fix json_boost JV generic: reserve for is_array/is_dim sources.
Fix templates.das: store hashes in array, build_string after loop.
Fix perf_lint.das: use build_string for inferStack message (require strings).

Improve PERF006 accuracy:
- While loops no longer trigger (bound is runtime-dependent, can't reserve)
- Update test file with while loop as good pattern

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tiny fixed-size loop (4 hex chars max), reserve adds overhead for no benefit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-allocate vertex and index arrays in gen_sphere and gen_cylinder
based on computed sizes. Fixes PERF006 warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- linq: reserve in zip_impl/zip3_impl (min of source lengths, require math)
- json_boost: reserve in from_JV array deserialization
- soa: reserve in generated from_array and to_array functions
- geom_gen: reserve vertices/indices in gen_sphere/gen_cylinder
- arcanoid: reserve in gen_rounded_cube geometry
- gameplay/test_gameplay: reserve in build_double_deck, test hand array
- 34_decs tutorial: reserve in entity creation loop

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@borisbat borisbat merged commit ccbf43f into master Mar 22, 2026
26 checks passed
@borisbat borisbat deleted the string-lint-daslib branch March 25, 2026 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant