tool/pputil: robust LoadIncludes parsing - #885
fennoai[bot] wants to merge 2 commits into
Conversation
Rewrite LoadIncludes to parse header files in a single pass: - allow spaces/tabs between '#' and 'include' - require '#' to be the first non-blank char on the line - skip line comments, block comments, and string/char literals so '#include' inside them is not treated as a directive Add pputil_test.go covering the new behavior. Fixes #884
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #885 +/- ##
==========================================
+ Coverage 90.39% 90.69% +0.29%
==========================================
Files 19 19
Lines 1791 1827 +36
==========================================
+ Hits 1619 1657 +38
+ Misses 172 170 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Review: single-pass #include scanner
The rewrite of the #include scanner into a single-pass byte tokenizer (scanIncludes / skipLiteral / parseInclude) is a solid improvement over the old repeated-bytes.Index approach. It is O(n), idiomatic, well-documented, and the new pputil_test.go covers a wide range of cases (whitespace variants, comments, string/char literals, keyword substrings like #includes/#include_next, early-stop, EOF-without-newline, unterminated header name). Tests pass and gofmt/go vet are clean.
I verified the security concern around manual byte indexing: all accesses are correctly bounds-checked. Unterminated block comments and a trailing backslash in a literal produce an i that overshoots len(b), but that value is only ever consumed by the outer i < len(b) loop guard, so there is no out-of-bounds risk.
A couple of minor points below. None are blocking.
Other notes (no reliable inline location):
- Unterminated block comment robustness (
scanIncludes, ~pputil.go:137-143): after an unterminated/* ... EOF, the loop exits withi == len(b)and theni += 2setsi = len(b)+2. This is safe today (only the outer loop guard reads it), but making it explicit — advancing past*/only when the closing delimiter was actually found, elsei = len(b)— would be more robust and self-documenting. Optional, and a test for the unterminated-comment case would be nice. - Line-continuation splicing not performed: an
#includebroken across lines with a trailing backslash (backslash-newline splicing, which the C preprocessor performs before tokenizing) is not handled. This is virtually never seen in real headers, so leaving it unsupported is reasonable — a one-line doc note inscanIncludeswould set expectations.
|
@fennoai fix review comments |
Review comments addressed ✅Pushed [P2] Same-line block comment suppresses next [P2] Inaccurate doc claim [P3] Tests Verification
|
- Treat same-line block comments as whitespace so an #include following a block comment on the same line (e.g. /* c */ #include <x.h>) is no longer silently missed. - Fix the scanIncludes doc: comments count as whitespace, so drop the inaccurate "as required by the C/C++ preprocessor" claim. - Rename local var 'close' to 'closer' to avoid shadowing the predeclared builtin. - Add tests for same-line block comment before #include.
Summary
Enhances
LoadIncludesintool/pputilper #884. The previous implementation searched for the literal#includesubstring anywhere in the file, which mishandled several cases. This rewrites it as a single-pass scanner.Changes
#:# include,#\tinclude, etc. are now recognized.#must be the first non-blank character on a line (matching C/C++ preprocessor rules), so#includeappearing mid-expression is no longer matched.#includeinside// ...line comments and/* ... */block comments (including multi-line) is skipped.#includeinside"..."and'...'literals is skipped, honoring\escapes.includebyte slice; parsing no longer mutates shared state.The scan is a single O(n) pass over the file bytes with no extra allocations beyond the yielded filenames.
Tests
Added
tool/pputil/pputil_test.gocovering spaced directives, leading whitespace, line/block comments, string/char literals, non-line-start#, missing separators, substrings like#includes/#include_next, unterminated names, EOF without newline, early iterator stop, andLoadIncludesfile I/O (success and error).All tests pass;
go vet,gofmt, andgo buildare clean.Fixes #884