#pragma once #include #include #include #include "locator.h" #include "util.h" // katom_list class Machine; // Static checking of klammer applications. // // Klammertext can do something TeX structurally cannot: know the shape of a // klammer body before that body is expanded. Katom structure is fixed when a // file is read -- there are no catcodes, so no later assignment can change how // text already read is divided into katoms -- which means every klammer // application that appears literally in a document or in a klammer body can be // located, named, and checked against the registry without running anything. // // This matters most where dynamic checking cannot reach. @cond is a // non-strict special form: the branch it does not select is never applied, so // an undefined klammer or a wrong argument count sitting in that branch is // invisible at run time and stays invisible until the day the predicate flips. // The same holds for a klammer body that is never applied for the target being // built. The checker reports all of them. // // What it deliberately does NOT see: klammers produced by @eval (a generator's // result is text computed at run time), and the contents of @eval and @read // argument spans (code and filenames, not applications). Its guarantee is // therefore about what is written, not about what will run. // // One thing it does not see that it SHOULD: a @cond written at the top level // of a document is resolved when the file is read (process_cond_katoms() runs // inside process_katoms()), so by the time anything can be checked the // unselected branch has already been discarded. Inside a klammer body the // @cond survives until the klammer is applied, so body branches ARE checked -- // which is where most of them are written. Closing the gap means resolving // @cond at application time rather than at read time, which is part of the // pass-ordering question; see notes/Klammertext_improvements.md. struct Diagnostic { Diagnostic(const std::string& severity, const std::string& message, const std::string& context, const Locator& loc) : m_severity(severity) , m_message(message) , m_context(context) , m_loc(loc) {} std::string m_severity {}; // "error" or "warning" std::string m_message {}; std::string m_context {}; // where it was found, e.g. "body of @s1 (tex)" Locator m_loc {}; }; // Check every statically visible klammer application in the document and in // the body of every defined klammer, for the named target. A target of "*" // (Target_registry::general_name) checks every defined target. Diagnostics // accumulate: checking never stops at the first failure, because the point is // to see all of them at once. // `document` is the katom list to check. It is a parameter rather than being // taken from machine.m_katoms because the caller is the one that has it, and // the two are not always the same list: Machine::read() fills m_katoms, but // Machine::process() -- which is how kdiag builds its katoms -- does not. // Reading m_katoms therefore checked an EMPTY document under kdiag and // reported "0 diagnostics, 0 errors" for anything, which is worse than not // checking: it reports success for input it never examined. Passing the list // makes that mistake impossible to write. std::vector check_machine(Machine& machine, const katom_list& document, const std::string& target); // Print diagnostics, grouped in the order found, and return the number of // errors (warnings do not count). Used by "ktext --check". int report_diagnostics(const std::vector& diagnostics, std::ostream& os);