Skip to main content

big_code_analysis/
lib.rs

1// Per-language metric and AST modules deliberately consume the macro-
2// generated tree-sitter token enums via `use crate::*` and `use Foo::*`
3// inside match expressions — explicit imports would list dozens of
4// variants per arm and obscure the per-language token sets that are the
5// point of these files. Allowed at the module level rather than per
6// function so the per-language impl blocks stay readable.
7#![allow(clippy::doc_markdown, clippy::enum_glob_use, clippy::wildcard_imports)]
8// Per-language Cargo features (commit 7e96b466) let a downstream build
9// only a subset of grammars. In such a build the code for the disabled
10// languages — their macro-generated `*Code` / `*Parser` tags plus the
11// getter / checker / metric helpers and shared plumbing only those
12// languages reach — is compiled but never constructed, so `-D dead-code`
13// fires on ~180 items that are all live in the default `all-languages`
14// build. Relax dead-code to a warning only when the full language set is
15// NOT enabled; the default build and `--all-features` (and thus the
16// primary CI gate and `make pre-commit`) still hard-deny it, so genuine
17// dead code is caught there. Fixes the long-red `features
18// (no-default-features / minimal-langs (lib))` CI legs.
19#![cfg_attr(not(feature = "all-languages"), allow(dead_code))]
20
21//! big-code-analysis is a library to analyze and extract information
22//! from source codes written in many different programming languages.
23//!
24//! You can find the source code of this software on
25//! <a href="https://github.com/dekobon/big-code-analysis/" target="_blank">GitHub</a>,
26//! while issues and feature requests can be posted on the respective
27//! <a href="https://github.com/dekobon/big-code-analysis/issues/" target="_blank">GitHub Issue Tracker</a>.
28//!
29//! ## Quick start
30//!
31//! Most callers want the recommended entry points exposed in
32//! [`prelude`]:
33//!
34//! ```no_run
35//! use big_code_analysis::prelude::*;
36//!
37//! let source = b"fn main() {}";
38//! let space = analyze(
39//!     Source::new(LANG::Rust, source),
40//!     MetricsOptions::default(),
41//! ).expect("Rust source parses");
42//! println!("cognitive sum: {}", space.metrics.cognitive.cognitive_sum());
43//! ```
44//!
45//! ## Supported Languages
46//!
47//! Each grammar is gated behind a per-language Cargo feature; the
48//! default `all-languages` feature enables every grammar so the
49//! historical "every language compiled in" behaviour is preserved.
50//! Library consumers that only need a subset can opt out of the
51//! defaults — see [Per-language Cargo features][feat] in the book.
52//!
53//! - Bash (`bash`)
54//! - C (`c`, upstream `tree-sitter-c`; owns `.c`)
55//! - C/C++ (`cpp`, upstream `tree-sitter-cpp`; the default for `.cpp` /
56//!   `.cc` / `.h` and also exposes the internal `ccomment` / `preproc`
57//!   C-family helpers)
58//! - C++, Firefox-internal "Mozcpp" (`mozcpp`, opt-in; owns no file
59//!   extensions — select it by name)
60//! - C# (`csharp`)
61//! - Objective-C (`objc`, upstream `tree-sitter-objc`; owns `.m`; `.mm`
62//!   Objective-C++ stays on C/C++)
63//! - Elixir (`elixir`)
64//! - Go (`go`)
65//! - Groovy (`groovy`)
66//! - F5 iRules (`irules`)
67//! - Java (`java`)
68//! - JavaScript (`javascript`)
69//! - JavaScript, Firefox-internal "MozJS" (`mozjs`)
70//! - Kotlin (`kotlin`)
71//! - Lua (`lua`)
72//! - Perl (`perl`)
73//! - PHP (`php`)
74//! - Python (`python`)
75//! - Ruby (`ruby`)
76//! - Rust (`rust`)
77//! - Tcl (`tcl`)
78//! - TSX (`tsx`)
79//! - TypeScript (`typescript`)
80//!
81//! [feat]: https://dekobon.github.io/big-code-analysis/library/cargo-features.html
82//!
83//! ## Supported Metrics
84//!
85//! - ABC: it measures the size of a source code based on
86//!   assignments, branches, and conditions.
87//! - CC: it calculates the code complexity examining the control flow of a
88//!   program.  Both standard and modified flavours are exposed: the
89//!   modified variant collapses all case/match arms inside a single
90//!   switch/match/when/select into one decision point.
91//! - Cognitive Complexity: it measures how difficult it is
92//!   to understand a unit of code.
93//! - SLOC: it counts the number of lines in a source file.
94//! - PLOC: it counts the number of physical lines (instructions)
95//!   contained in a source file.
96//! - LLOC: it counts the number of logical lines (statements)
97//!   contained in a source file.
98//! - CLOC: it counts the number of comments in a source file.
99//! - BLANK: it counts the number of blank lines in a source file.
100//! - HALSTEAD: it is a suite that provides a series of information,
101//!   such as the effort required to maintain the analyzed code,
102//!   the size in bits to store the program, the difficulty to understand
103//!   the code, an estimate of the number of bugs present in the codebase,
104//!   and an estimate of the time needed to implement the software.
105//! - MI: it is a suite that allows to evaluate the maintainability
106//!   of a software.
107//! - NOM: it counts the number of functions and closures
108//!   in a file/trait/class.
109//! - NEXITS: it counts the number of possible exit points
110//!   from a method/function.
111//! - NARGS: it counts the number of arguments of a function/method.
112//! - NPA: it counts the number of public attributes of a class.
113//! - NPM: it counts the number of public methods of a class.
114//! - WMC: it is the sum of the complexities of all methods
115//!   in a class.
116
117#![allow(clippy::upper_case_acronyms)]
118
119// Internal-only modules. Nothing is re-exported from these.
120mod c_langs_macros;
121mod c_macro;
122mod cfg_predicate;
123mod checker;
124mod getter;
125mod languages;
126mod macros;
127
128// `langs` hosts the `mk_langs!` macro expansion. `LANG` is the only
129// public name; the per-language `<Lang>Code` tags and `<Lang>Parser`
130// aliases are `pub(crate)` parser machinery reached only through the
131// [`Ast`] seam.
132mod langs;
133pub use crate::langs::{LANG, get_from_emacs_mode, get_from_ext};
134// `<Lang>Code` tags are reached crate-internally through `use crate::*`
135// in the per-language `Checker` / `Getter` / `Alterator` / metric impls.
136pub(crate) use crate::langs::{
137    BashCode, CCode, CcommentCode, CppCode, CsharpCode, ElixirCode, GoCode, GroovyCode, IrulesCode,
138    JavaCode, JavascriptCode, KotlinCode, LuaCode, MozcppCode, MozjsCode, ObjcCode, PerlCode,
139    PhpCode, PreprocCode, PythonCode, RubyCode, RustCode, TclCode, TsxCode, TypescriptCode,
140};
141// The `<Lang>Parser` aliases are the concrete `Parser<<Lang>Code>` types
142// driven by the `AstInner` dispatch in `crate::langs`; at the crate root
143// they are reached only from `#[cfg(test)]` modules, so the re-export is
144// `unused` in a non-test build.
145#[allow(unused_imports)]
146pub(crate) use crate::langs::{
147    BashParser, CParser, CcommentParser, CppParser, CsharpParser, ElixirParser, GoParser,
148    GroovyParser, IrulesParser, JavaParser, JavascriptParser, KotlinParser, LuaParser,
149    MozcppParser, MozjsParser, ObjcParser, PerlParser, PhpParser, PreprocParser, PythonParser,
150    RubyParser, RustParser, TclParser, TsxParser, TypescriptParser,
151};
152// `ParseLangError` is the `FromStr` error for `LANG`; it is defined in
153// the `mk_lang!` macro layer (`crate::macros`) rather than `crate::langs`.
154pub use crate::macros::ParseLangError;
155
156// Internal crate-root re-exports. Hand-written per-language modules
157// (`src/getter.rs`, `src/checker.rs`, `src/alterator.rs`, the
158// per-language metric impls) use `use crate::*` to bring the
159// macro-generated `<Lang>Code` token enums and per-language helper
160// types into scope; the per-language token enums in
161// `src/languages/language_*.rs` are also reached through the crate
162// root. Re-exporting these as `pub(crate)` keeps internal compilation
163// working without widening the published surface.
164pub(crate) use crate::checker::*;
165pub(crate) use crate::languages::*;
166
167// Hand-written modules (`src/spaces.rs`, `src/output/dump_metrics.rs`,
168// the metric macros) refer to per-metric submodules by their short
169// crate-root path (`crate::abc`, `crate::cognitive`, ...). Re-export
170// them under those names without widening the public surface.
171pub(crate) use crate::metrics::{
172    abc, cognitive, cyclomatic, halstead, loc, mi, nargs, nexits, nom, npa, npm, tokens, wmc,
173};
174
175// Module declarations. Each `pub use` line below names exactly the
176// items intended to be part of the public API surface; anything not
177// listed stays out of the crate root. Per issue #255, glob re-exports
178// (`pub use module::*`) are no longer used here because every newly
179// `pub`-marked helper in any sub-module would silently leak into the
180// published API.
181
182// --- Core analysis entry points and result types (spaces.rs) ---
183mod spaces;
184pub use crate::spaces::{Ast, CodeMetrics, FuncSpace, MetricsOptions, Source, SpaceKind, analyze};
185// `metrics_inner` is the per-`ParserTrait` metric walk core consumed by
186// feature-gated arms in `mk_action!` (`AstInner::run_metrics`). With
187// `--no-default-features` and no language feature, every arm compiles
188// out and the re-export becomes nominally unused; the language-features
189// that ship in the default set keep the symbol live in any normal build.
190#[allow(unused_imports)]
191pub(crate) use crate::spaces::metrics_inner;
192#[cfg(test)]
193pub(crate) use crate::tools::check_func_space;
194
195/// Per-metric implementations.
196///
197/// Each sub-module owns one metric — its `Stats` accumulator, the
198/// per-language trait implementations, and any small helpers used
199/// only by tests. Most callers will not need these directly; reach
200/// through [`CodeMetrics`] on a [`FuncSpace`] instead.
201pub mod metrics;
202
203/// Plain, `Deserialize`-capable data-transfer structs mirroring the
204/// serialized metric wire shape. The compute types' `Serialize` impls
205/// delegate here, making these the single definition of the JSON / YAML /
206/// TOML / CBOR output format and the canonical way to read `bca` output
207/// back (`serde_json::from_str::<wire::FuncSpace>(…)`).
208pub mod wire;
209
210// --- Change-history (VCS) metrics ---
211//
212// The project's first language-agnostic, non-AST metric family
213// (issue #328). Gated behind the `vcs-git` backend feature (the
214// `vcs` umbrella turns it on); the generic surface is backend-neutral
215// so future backends (#335) reuse it unchanged.
216/// Change-history (VCS) metrics derived from version-control history:
217/// churn, commit frequency, author count / ownership dilution, bug- and
218/// security-fix history, and an ordinal composite risk score. See
219/// [`vcs::build_history_index`].
220#[cfg(feature = "vcs-git")]
221pub mod vcs;
222
223// --- Errors ---
224mod error;
225pub use crate::error::{FromPathError, MetricsError};
226
227// --- Metric selection ---
228mod metric_set;
229pub use crate::metric_set::{Metric, MetricSet, ParseMetricError};
230
231// --- Suppression markers ---
232mod suppression;
233pub use crate::suppression::{
234    SuppressionDialect, SuppressionMarker, SuppressionPolicy, SuppressionScope, SuppressionTarget,
235    threshold_metric_for_name,
236};
237
238/// Canonical metric catalog: offender sub-metric ids with their
239/// long-form sentences and [`metric_catalog::Direction`], plus the
240/// family view rendered by `bca list-metrics`. Single source of truth
241/// shared by the library's offender formatters and the CLI's threshold
242/// engine, which pins its extractor ids to [`metric_catalog::METRICS`]
243/// via a parity test.
244pub mod metric_catalog;
245
246/// Output formatters: CSV, SARIF, Checkstyle, clang/MSVC warning
247/// lines, and AST/metric pretty-dumps used by `bca` and the offender
248/// reporters.
249///
250/// The most commonly used writers (`write_csv`, `write_sarif`,
251/// `write_checkstyle`, `write_clang_warning`, `write_code_climate`,
252/// `write_msvc_warning`) and shared types (`OffenderRecord`,
253/// `Severity`, `TOOL_ID`, `CSV_HEADER`, `CSV_EXTENSION`) are also
254/// re-exported at the crate root.
255pub mod output;
256pub use crate::output::{
257    CSV_EXTENSION, CSV_HEADER, ColorMode, OffenderRecord, Severity, TOOL_ID, defang_formula,
258    dump_node, dump_node_with_color, dump_ops, dump_ops_with_color, dump_root,
259    dump_root_with_color, write_checkstyle, write_clang_warning, write_code_climate, write_csv,
260    write_csv_aggregate, write_msvc_warning, write_sarif, write_sarif_with_suppressed,
261};
262
263// --- AST plumbing (Node) ---
264mod node;
265pub use crate::node::Node;
266
267// --- Language detection / I/O helpers ---
268mod tools;
269pub use crate::tools::{
270    get_language_for_file, guess_language, is_generated, normalize_eol, read_file,
271    read_file_with_eol, write_file,
272};
273
274// --- Source walker ---
275mod concurrent_files;
276pub use crate::concurrent_files::{
277    ConcurrentErrors, ConcurrentRunner, FilesData, NumJobs, ParseNumJobsError,
278};
279
280// --- Comment removal ---
281//
282// `rm_comments` is the internal walk core reached only through the
283// [`Ast::strip_comments`] seam (`AstInner::run_strip_comments`).
284mod comment_rm;
285
286// --- Per-file node counting / finding (reached via the `Ast` seam) ---
287mod count;
288pub use crate::count::{Count, CountCollector};
289
290mod find;
291
292mod function;
293pub use crate::function::{FunctionSpan, dump_function_spans, dump_function_spans_with_color};
294
295// --- AST dump ---
296mod ast;
297pub use crate::ast::{AstCfg, AstNode, AstPayload, AstResponse, Span};
298
299// --- Halstead operator/operand result type ---
300mod ops;
301pub use crate::ops::Ops;
302// `ops_inner` is the explicit-name walk core consumed by feature-gated
303// `mk_action!` arms (`AstInner::run_ops`); mirrors the `metrics_inner`
304// re-export above and is nominally unused under `--no-default-features`.
305#[allow(unused_imports)]
306pub(crate) use crate::ops::ops_inner;
307
308// --- Preprocessor handling (C/C++) ---
309mod preproc;
310pub use crate::preproc::{
311    PreprocDiagnostic, PreprocFile, PreprocResults, fix_includes, get_macros, preprocess,
312};
313
314// --- Alterator trait (per-language AST simplification) ---
315//
316// Crate-internal: an extension trait over the `pub(crate)` `Checker`
317// machinery, used only by the per-language `Parser<T>` impls behind the
318// [`Ast`] seam.
319mod alterator;
320pub(crate) use crate::alterator::Alterator;
321
322// --- Generic parser plumbing (crate-internal) ---
323//
324// `Parser`, `ParserTrait`, `Filter`, and `LanguageInfo` are the
325// internal parser machinery driving every metric walk. They are
326// `pub(crate)` only: the single public analysis seam is [`Ast`],
327// which wraps the language-dispatched `AstInner` carrier. See
328// STABILITY.md.
329mod parser;
330pub(crate) use crate::parser::Parser;
331
332mod traits;
333pub(crate) use crate::traits::{LanguageInfo, ParserTrait, Search};
334
335/// Re-export of the underlying `tree-sitter` crate.
336///
337/// Lets callers build a [`tree_sitter::Tree`] (via
338/// [`tree_sitter::Parser`]) against the exact grammar version this
339/// library is pinned to, and feed it back through
340/// [`Ast::from_tree_sitter`] without taking a separate `tree-sitter`
341/// dependency that may drift out of pin.
342///
343/// This is part of the value-not-stable surface: the underlying
344/// pin may bump in any minor release (see `STABILITY.md`). The inner
345/// node of a [`Node`] is reached the same way, through
346/// [`Node::as_tree_sitter`], and carries the same value-not-stable
347/// caveat.
348pub use ::tree_sitter;
349
350/// The version of this `big-code-analysis` library crate.
351///
352/// Sourced from the crate's own `CARGO_PKG_VERSION` at compile time.
353/// Exposed so downstream surfaces (the REST `/v1/version` endpoint, the
354/// Python `__version__` attribute, …) can report the exact library
355/// version they were built against without re-deriving it from Cargo
356/// metadata.
357pub const VERSION: &str = env!("CARGO_PKG_VERSION");
358
359/// Recommended entry points for the 90% case.
360///
361/// Star-import this module to get the curated set of types and
362/// functions most callers need:
363///
364/// ```no_run
365/// use big_code_analysis::prelude::*;
366///
367/// let source = b"fn main() {}";
368/// let space = analyze(
369///     Source::new(LANG::Rust, source),
370///     MetricsOptions::default(),
371/// ).expect("Rust source parses");
372/// # let _ = space;
373/// ```
374///
375/// Anything not exposed here can still be imported with its
376/// fully-qualified name from the crate root (`use
377/// big_code_analysis::Something;`). Items deliberately omitted from
378/// the prelude are either deprecated, doc-hidden, or unlikely to
379/// appear in typical caller code.
380pub mod prelude {
381    pub use crate::{
382        // Parse-once handle
383        Ast,
384        // Result types
385        CodeMetrics,
386        // Errors and options
387        FromPathError,
388        FuncSpace,
389        // Language enum
390        LANG,
391        // Metric selection
392        Metric,
393        MetricsError,
394        MetricsOptions,
395        Source,
396        SpaceKind,
397        // Core entry points
398        analyze,
399    };
400}