branch suggestion: create proof files from template #63

Merged
marfavi merged 19 commits from fix/create-proof-files into feature/create-spec-from-template 2026-10-23 13:47:15 +02:00
Owner

Suggestion branch for feature/create-spec-from-template.

What it does

  • Generates one verif_<file>.v per C file with annotations from the verif/spec/loop/proof templates.
  • Reads annotations from disk instead of the shared annotation store, so it does not depend on the document sync or on VsRocq being ready.
  • Never overwrites existing proof files (see #65).
  • Honours PROOF links: the lemma keeps the linked name, and a proof linked to another document is generated in that document rather than beside the spec.
  • Keeps the templates in src/templates and ships them through a .vscodeignore exception.

Why the command layer was restructured

  • The old flow opened every C file and then read the shared store, which is filled asynchronously and only while VsRocq is available: it could run against an empty store, and closing/deleting files left stale entries behind.
  • The vscode-facing helpers are injected (as in createRocqModel) so the command and the annotation mapping can run in npm run test:unit; before, the unit test failed to load (MODULE_NOT_FOUND: vscode).

Known limitations

  • The generated specs and loop invariants are placeholders; deriving a VST funspec/invariant from C is not implemented, so the generated file does not compile until they are filled in (see #27).
  • A PROOF link pointing at a document that does not exist generates a proof-only document; that file references a spec defined elsewhere and will not compile on its own.

npm run check, npm run test:unit and npm run test:integration pass.

Suggestion branch for `feature/create-spec-from-template`. ## What it does - Generates one `verif_<file>.v` per C file with annotations from the `verif`/`spec`/`loop`/`proof` templates. - Reads annotations from disk instead of the shared annotation store, so it does not depend on the document sync or on VsRocq being ready. - Never overwrites existing proof files (see #65). - Honours `PROOF` links: the lemma keeps the linked name, and a proof linked to another document is generated in that document rather than beside the spec. - Keeps the templates in `src/templates` and ships them through a `.vscodeignore` exception. ## Why the command layer was restructured - The old flow opened every C file and then read the shared store, which is filled asynchronously and only while VsRocq is available: it could run against an empty store, and closing/deleting files left stale entries behind. - The vscode-facing helpers are injected (as in `createRocqModel`) so the command and the annotation mapping can run in `npm run test:unit`; before, the unit test failed to load (`MODULE_NOT_FOUND: vscode`). ## Known limitations - The generated specs and loop invariants are placeholders; deriving a VST funspec/invariant from C is not implemented, so the generated file does not compile until they are filled in (see #27). - A `PROOF` link pointing at a document that does not exist generates a proof-only document; that file references a spec defined elsewhere and will not compile on its own. `npm run check`, `npm run test:unit` and `npm run test:integration` pass.
crocq.checkDependencies, crocq.openDependencyPanel and crocq.createProofFiles
were each declared twice in contributes.commands, a leftover of the merge with
main. Keep one entry per command.
The .eta templates lived under src/templates, which .vscodeignore excludes from
the packaged extension (src/**); esbuild does not copy them either. A packaged
build therefore had no templates to render.

Move them to templates/ at the extension root and point the command at that
directory. spec.eta also notes that its funspec arguments are placeholders.
- fileNameFromUri parses Uri.fsPath with node:path instead of splitting
  uri.toString(), which mishandled percent-encoded names and queries.
- getCFilesInWorkspace uses try/catch (findFiles rejects when no folder is
  open) and logs the failure instead of swallowing it.
- Add fileExists, and make writeFile create missing parent directories.
- Add readFileAnnotations, which parses a C file from disk whether or not it
  is open. The document sync only stores annotations of open documents.
- Add showWarningMessage.
- Inject the vscode-facing services (same convention as createRocqModel) so
  the command and the annotation-to-template mapping are unit testable without
  an extension host. The unit tests previously failed to load because they
  imported vscode.
- Read the annotations of every C file from disk instead of opening all of
  them and hoping the asynchronous onDidOpenTextDocument listener had filled
  the store by then. This removes the empty/stale-store race (and the note
  about proofs for deleted files).
- Never overwrite an existing proof file; skip it and warn instead. The old
  code overwrote Verif_*.v on every run, destroying user proofs.
- Match the optional file argument on path boundaries instead of endsWith, and
  name the "all" argument.
- Report failures instead of rejecting: aggregated, de-duplicated annotation
  errors, per-file read resilience, and an information message when there is
  nothing to generate.
- Drop triggerOnDocumentOpen, which the command no longer uses, and move the
  proof file's uri out of the template arguments (it is not a template input).
- Assert that package.json contributes each command exactly once, guarding the
  duplicate-command merge artifact.
- Assert that the verif/spec/proof templates ship where the command looks for
  them.
- Cover fileNameFromUri, fileExists, writeFile (including parent-directory
  creation) and getTargetUriAndSpec against real vscode URIs.
Describe what it scaffolds, that it never overwrites existing documents, and
that the generated funspec arguments are placeholders until deriving a VST
funspec from the C source is implemented.
chore(examples): end _CoqProject with a newline
All checks were successful
Grammar / Corpus and binding tests (pull_request) Successful in 43s
Extension / Format, types, lint, extension tests (pull_request) Successful in 1m36s
1796da2e71
marfavi force-pushed fix/create-proof-files from 1796da2e71
All checks were successful
Grammar / Corpus and binding tests (pull_request) Successful in 43s
Extension / Format, types, lint, extension tests (pull_request) Successful in 1m36s
to 9d5e44b110
All checks were successful
Grammar / Corpus and binding tests (pull_request) Successful in 44s
Extension / Format, types, lint, extension tests (pull_request) Successful in 1m43s
2026-10-21 14:37:32 +02:00
Compare
Merge branch 'feature/create-spec-from-template' into fix/create-proof-files
All checks were successful
Grammar / Corpus and binding tests (pull_request) Successful in 44s
Extension / Format, types, lint, extension tests (pull_request) Successful in 1m40s
6cbcf2d1c2
- A function whose PROOF clause points at the document that receives its spec
  keeps generating the lemma, now under the lemma name from the clause instead
  of the hard-coded body_<fn>.
- When the PROOF clause points at another document, only the spec is generated
  here: the linked proof already lives there, so generating the lemma again
  would duplicate it.
docs(createProofFiles): spell out placeholder bodies and skip-only policy
All checks were successful
Grammar / Corpus and binding tests (pull_request) Successful in 40s
Extension / Format, types, lint, extension tests (pull_request) Successful in 1m32s
21bc88769d
The generated specs and loop invariants are intentionally placeholders until
deriving a VST funspec/invariant from C lands (#27); note it in loop.eta, as
spec.eta already does, and in the README. Also document that existing proof
files are never overwritten and link the force-option issue (#65).
resolveTarget(null) resolves the default document; it does not return
undefined. The "no proof target" branch was therefore dead: a function with
a SPEC link and no PROOF link had its proof generated in a second
verif_<file>.v document instead of next to its spec, and the proof name fell
back to the spec name.

Only resolve a proof target when the PROOF clause exists; otherwise the proof
stays with the spec target and keeps the body_<fn> name.
test/docs: align with the lowercase verif_ default and proof placement
All checks were successful
Grammar / Corpus and binding tests (pull_request) Successful in 1m6s
Extension / Format, types, lint, extension tests (pull_request) Successful in 3m22s
e999296734
The default document is verif_<file>.v since the template update; update the
integration expectation and the docs/comments that still said Verif_<file>.v.
The README also now says a linked proof is generated in the document its
PROOF clause points at.
michael changed title from WIP: branch suggestion: create spec from template to branch suggestion: create spec from template 2026-10-23 11:34:45 +02:00
michael approved these changes 2026-10-23 12:02:45 +02:00
@ -22,0 +22,4 @@
# The proof templates are read from disk at run time, so they are the one
# thing under `src/` that must survive packaging. Re-include only the `.eta`
# files: the `.ts` template argument interfaces stay out via `**/*.ts`.
!src/templates/*.eta
Owner

I would do !src/templates//*.eta or just !/*.eta, so that the files are also allowed to be nested in deeper folders without having to change this further

I would do !src/templates/**/*.eta or just !**/*.eta, so that the files are also allowed to be nested in deeper folders without having to change this further
michael marked this conversation as resolved
@ -35,3 +23,1 @@
*/
export async function triggerOnDocumentOpen(path: Uri): Promise<void> {
await workspace.openTextDocument(path).then();
try {
Owner

why change this, it does exactly the same thing, and has just been changed for the hell of it?

why change this, it does exactly the same thing, and has just been changed for the hell of it?
michael marked this conversation as resolved
@ -47,3 +41,1 @@
const lastIndex = fileName.lastIndexOf('.');
if (lastIndex === -1) return fileName;
return fileName.substring(0, lastIndex);
const { name } = parse(path.fsPath);
Owner

technically better but harder to understand that Uri destructs to the name of the file

i would do it more explicit with const name = parse(path.fsPath).name if it has to be changed

technically better but harder to understand that Uri destructs to the name of the file i would do it more explicit with `const name = parse(path.fsPath).name` if it has to be changed
michael marked this conversation as resolved
michael changed title from branch suggestion: create spec from template to WIP: branch suggestion: create spec from template 2026-10-23 12:03:27 +02:00
fixed incorrect ignorign of node modules, and made it possible to further nest templates if needed
Some checks are pending
Extension / Format, types, lint, extension tests (pull_request) Waiting to run
Grammar / Corpus and binding tests (pull_request) Waiting to run
369ae76fb8
Small tweak for better readability
All checks were successful
Grammar / Corpus and binding tests (pull_request) Successful in 53s
Extension / Format, types, lint, extension tests (pull_request) Successful in 2m3s
384b5b96f5
michael changed title from WIP: branch suggestion: create spec from template to branch suggestion: create spec from template 2026-10-23 13:00:37 +02:00
marfavi changed title from branch suggestion: create spec from template to branch suggestion: create proof files from template 2026-10-23 13:44:36 +02:00
marfavi merged commit e893dc7731 into feature/create-spec-from-template 2026-10-23 13:47:15 +02:00
marfavi deleted branch fix/create-proof-files 2026-10-23 13:47:15 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code2rocq/crocq!63
No description provided.