1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//! daml-lint as a library.
//!
//! The binary (`src/main.rs`) is a thin CLI over these modules. The parser
//! pipeline — `lexer` → `layout` → `parse` over the AST types — lives in the
//! separate [`daml_parser`] crate. This crate lowers that AST into a
//! rule-facing IR ([`ir`], via [`parser`]) and runs [`detectors`] over it.
//! Start at [`parser::parse_daml_with_diagnostics`].
//! The IR is public so custom rules and library callers can inspect it; parser
//! lowering is the supported construction path.
//!
//! ## API posture
//!
//! This crate is pre-1.0. Its public IR is also intentionally data-oriented;
//! adding/removing fields, changing enum variants, or altering shapes is
//! SemVer-relevant. Use constructors in the same crate for creation and treat
//! these as versioned contracts with downstream rules/detectors.
//! Matching contract for rule-facing IR:
//! `TypeNode`, `LiteralKind`, `Expr`, `Consuming`, `Statement`, and
//! `ImportStyle` are intentionally `#[non_exhaustive]`.
//! Read-mostly IR/report DTO structs (`DamlModule`, `Template`, `Finding`, and
//! related nodes) are also `#[non_exhaustive]` so fields can evolve in 0.x
//! without breaking downstream field reads.
//! Downstream code should include wildcard arms when matching any of these
//! enums; adding variants is a compatible evolution for new Daml syntax and
//! recovery paths. Construct IR through [`parser::parse_daml_with_diagnostics`]
//! or documented constructors such as [`detector::Finding::new`].
//!
//! Parse diagnostics use [`parser::ParseDiagnosticCategory`] (not the parser
//! crate's internal category enum) and [`parser::ParseResult`] (`module` +
//! `diagnostics`) as the supported lowering entry point. Rust-facing locations
//! and spans use [`daml_syntax::LineNumber`], [`daml_syntax::CharColumn`],
//! [`daml_syntax::Utf16Offset`], and [`daml_syntax::ByteOffset`] so coordinate
//! spaces cannot be mixed accidentally; JSON, SARIF, and custom-rule JavaScript
//! output still serialize those coordinates as numbers. For severity thresholds
//! and report ordering, use [`detector::Severity::rank`] and
//! [`detector::Severity::meets_or_exceeds`]; `Severity` does not implement
//! `Ord` because declaration order does not match risk rank.
//!
//! # Example
//!
//! ```
//! use std::path::Path;
//!
//! let source = "\
//! module M where
//!
//! template T
//! with
//! owner : Party
//! where
//! signatory owner
//! ";
//!
//! let parse_result =
//! daml_lint::parser::parse_daml_with_diagnostics(source, Path::new("M.daml"));
//! let module = parse_result.module;
//! let diagnostics = parse_result.diagnostics;
//!
//! assert!(diagnostics.is_empty());
//! assert_eq!(module.name, "M");
//! assert_eq!(module.templates.len(), 1);
//! ```
//!
//! Loss-tolerant parsing always returns a module; reject scans when diagnostics
//! are present. [`parser::ParseDiagnosticCategory`] parses stable category tags:
//!
//! ```
//! use std::path::Path;
//! use std::str::FromStr;
//!
//! use daml_lint::parser::{ParseDiagnosticCategory, parse_daml_with_diagnostics};
//!
//! let broken = parse_daml_with_diagnostics("module M where\n%%% junk\n", Path::new("M.daml"));
//! assert!(!broken.diagnostics.is_empty());
//! assert_eq!(broken.module.name, "M");
//!
//! assert_eq!(
//! ParseDiagnosticCategory::from_str("malformed").unwrap(),
//! ParseDiagnosticCategory::Malformed,
//! );
//! assert!(ParseDiagnosticCategory::from_str("not-a-category").is_err());
//! ```
/// Detector traits, finding DTOs, severity values, and configured-detector wrappers.
/// Built-in and JavaScript-backed detector implementations.
/// Lowering: `daml-parser`'s typed AST → rule-facing IR ([`ir`]).
/// Report formatting and scan exit-code helpers.