Skip to content

ast, parser, fio: fix six PVS-Studio findings#3111

Merged
borisbat merged 1 commit into
masterfrom
bbatkin/pvs-studio-fixes
Jun 12, 2026
Merged

ast, parser, fio: fix six PVS-Studio findings#3111
borisbat merged 1 commit into
masterfrom
bbatkin/pvs-studio-fixes

Conversation

@borisbat

Copy link
Copy Markdown
Collaborator

Triage of a PVS-Studio sweep over the vendored daScript copy: ~80 warnings, 6 real findings, all fixed here. Everything else classified as false positives (invariant-guarded derefs the parser guarantees, gc_node-tracked "leaks", deliberate hopeless-clear memsets, union aliasing PVS can't model, flex-generated boilerplate).

Fixes

  1. isFullySealed copy-paste (ast_typedecl.cpp) — null-checked secondType but re-tested firstType, so a sealed key type masked an unsealed value type (e.g. a table with sealed key and still-auto value), letting typeinfo offsetof fold prematurely. Pinned by a new doctest (tests-cpp/small/test_fully_sealed.cpp).
  2. Bitfield-constant null deref (ast_infer_type.cpp) — the "bitfield constant ... is not initialized" error branch fell through to found->init->clone(). Added the missing return; only reachable via macro-side flag manipulation (the grammar always sets the initializer).
  3. Silent ambiguous-variable failure (ast_infer_type.cpp) — else { if (verbose) error(...) } made the terse "too many matching variables" diagnostic dead code, so non-verbose compiles reported nothing for an ambiguous global. Also folded the mirror always-true if (verbose) on the "can't locate variable" path.
  4. Dangling updateAliasMapCallback (ast_infer_type_function.cpp, 2 sites) — early returns left program->updateAliasMapCallback holding a lambda capturing dead stack frames by reference; a later das-side update_alias_map call (typeinfo macro) would write through them. The third site already cleared the callback before its early return — copied that pattern.
  5. Lexer line-directive OOB write (ds_lexer.lpp + ds2_lexer.lpp) — the file name in #row,col,"file"# was parsed with sscanf %s, which stops at whitespace: a file name containing a space made strlen(lFile)-2 underflow into a wild out-of-bounds write. Both lexers now take the quote-bounded span directly (also un-breaks paths with spaces and paths longer than 255 chars). Generated lexer sources regenerated.
  6. context guard asymmetry (module_builtin_fio.cpp) — register_dynamic_module deref'd context unguarded on its first error path while both sibling error paths guard with if (context); harmonized (a null context is passed internally today, Quiet-only).

Validation

  • preflight --full: 16 passed, 0 failed (lint skipped — no .das changes): cpp-syntax full sweep, das2rst + sphinx latex/html + pdflatex, tests-cpp, interp + JIT + AOT suites, sequence smoke
  • full das suite: 11036 tests, 0 failed
  • ctest: 64/64 incl. the new isFullySealed regression test
  • lexer probed by hand: #5,1,"probe b.das"# (space in name) now lexes, resolves, and runs; plain directives unchanged

🤖 Generated with Claude Code

- isFullySealed re-tested firstType where it null-checked secondType
  (copy-paste), so a sealed key type masked an unsealed value type —
  prematurely folding typeinfo offsetof; doctest pins it
- bitfield-constant 'not initialized' error branch fell through to
  found->init->clone() — null deref (macro-reachable shape)
- non-verbose 'too many matching variables' error was dead code
  (if (verbose) inside the else of if (verbose)) — ambiguous globals
  compiled with no diagnostic; folded the mirror always-true check too
- two early returns left program->updateAliasMapCallback holding a
  lambda with by-ref captures of dead stack frames; a later das-side
  update_alias_map call would write through them
- lexer line directives parsed the file name with sscanf %s, which
  stops at whitespace: #1,2,"a b"# made strlen(lFile)-2 underflow into
  a wild OOB write; both lexers now take the quote-bounded span (also
  un-breaks paths with spaces and >255-char paths)
- register_dynamic_module deref'd context unguarded on its first error
  path while both sibling paths guard; harmonized

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 12, 2026 06:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a handful of confirmed PVS-Studio findings in daScript’s core compiler/runtime components (AST/type inference, dynamic module loading, and both lexers), and adds a C++ regression test to lock in the isFullySealed behavior.

Changes:

  • Fix TypeDecl::isFullySealed to correctly validate secondType (plus new doctest regression coverage).
  • Fix two inference/diagnostic issues in ast_infer_type.cpp (missing return in a bitfield-constant error path; make ambiguous-variable diagnostics non-silent).
  • Harden lexer line-directive filename extraction to handle whitespace safely (and regenerate lexer outputs), plus guard a Context* deref in register_dynamic_module.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests-cpp/small/test_fully_sealed.cpp Adds regression coverage ensuring isFullySealed checks secondType independently.
src/ast/ast_typedecl.cpp Fixes copy-paste bug: secondType now calls secondType->isFullySealed.
src/ast/ast_infer_type.cpp Adds missing early return on bitfield-constant init error; ensures ambiguous/missing variable errors are emitted consistently.
src/ast/ast_infer_type_function.cpp Clears program->updateAliasMapCallback on early-return paths to avoid dangling callbacks.
src/builtin/module_builtin_fio.cpp Guards context before emitting/throwing on dynamic module load failure.
src/parser/ds_lexer.lpp Fixes line-directive filename parsing to use quote-bounded span (supports spaces; avoids OOB write).
src/parser/ds_lexer.cpp Regenerated lexer output reflecting the line-directive parsing fix.
src/parser/ds2_lexer.lpp Same line-directive parsing fix for ds2 lexer.
src/parser/ds2_lexer.cpp Regenerated ds2 lexer output reflecting the line-directive parsing fix.
src/parser/lex.yy.h Regenerated header line markers (line directives shifted due to lexer regeneration).
src/parser/lex2.yy.h Regenerated header line markers (line directives shifted due to lexer regeneration).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@borisbat borisbat merged commit d2bb4d1 into master Jun 12, 2026
35 checks passed
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.

2 participants