big_code_analysis/macros/mod.rs
1// `get_language!` is invoked only from feature-gated arms in `mk_lang!`
2// (one arm per `LANG::*` variant whose per-language Cargo feature is
3// enabled). A build with `--no-default-features` and no language
4// feature has no remaining call sites; suppress the lint for that
5// pathological-but-valid configuration.
6#[allow(unused_macros)]
7macro_rules! get_language {
8 (tree_sitter_typescript) => {
9 tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()
10 };
11 (tree_sitter_tsx) => {
12 tree_sitter_typescript::LANGUAGE_TSX.into()
13 };
14 (tree_sitter_php) => {
15 tree_sitter_php::LANGUAGE_PHP.into()
16 };
17 ($name:ident) => {
18 $name::LANGUAGE.into()
19 };
20}
21
22// `implement_metric_trait!` emits no-op `compute` bodies for every
23// metric / language pair listed. Every named-trait arm below
24// (`Abc`, `Cognitive`, `Halstead`, `Exit`, `Cyclomatic`, `Npa`,
25// `Npm`, `Loc`, `Wmc`) is silent: the metric will report 0 on every
26// input. The bracketed-trait arm (`[Trait]`) is different — it
27// emits an empty `impl Trait for X {}` and relies on the trait's
28// own default method body, which is correct for `Mi`, `Tokens`,
29// `Nom`, and `NArgs`.
30//
31// Audit: #188 walked every `(language, metric)` cell and classified
32// each as either a real default (the language has no construct the
33// metric measures) or a placeholder (the language HAS the construct
34// but no impl exists yet). Each invocation site carries a comment
35// recording the rationale and any follow-up issue number — keep
36// those comments in sync when you add a new language or land a real
37// impl.
38macro_rules! implement_metric_trait {
39 (Abc, $($code:ident),+) => (
40 implement_metric_trait!(@code_taking Abc, $($code),+);
41 );
42 (Cognitive, $($code:ident),+) => (
43 $(
44 impl Cognitive for $code {
45 fn compute<'a>(
46 _node: &Node<'a>,
47 _code: &'a [u8],
48 _stats: &mut Stats,
49 _nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
50 ) {}
51 }
52 )+
53 );
54 (Halstead, $($code:ident),+) => (
55 $(
56 impl Halstead for $code {
57 fn compute<'a>(_node: &Node<'a>, _code: &'a [u8], _halstead_maps: &mut HalsteadMaps<'a>) {}
58 }
59 )+
60 );
61 // Internal helper: shared no-op body for traits whose `compute`
62 // signature is `<'a>(&Node<'a>, &'a [u8], &mut Stats)` (Exit,
63 // Cyclomatic). Public arms below delegate here so the body is
64 // written once.
65 (@code_taking $trait:ident, $($code:ident),+) => (
66 $(
67 impl $trait for $code {
68 fn compute<'a>(_node: &Node<'a>, _code: &'a [u8], _stats: &mut Stats) {}
69 }
70 )+
71 );
72 (Exit, $($code:ident),+) => (
73 implement_metric_trait!(@code_taking Exit, $($code),+);
74 );
75 (Cyclomatic, $($code:ident),+) => (
76 implement_metric_trait!(@code_taking Cyclomatic, $($code),+);
77 );
78 (Npa, $($code:ident),+) => (
79 implement_metric_trait!(@code_taking Npa, $($code),+);
80 );
81 (Npm, $($code:ident),+) => (
82 implement_metric_trait!(@code_taking Npm, $($code),+);
83 );
84 (Loc, $($code:ident),+) => (
85 $(
86 impl Loc for $code {
87 fn compute(_node: &Node, _stats: &mut Stats, _is_func_space: bool, _is_unit: bool) {}
88 }
89 )+
90 );
91 (Wmc, $($code:ident),+) => (
92 $(
93 impl Wmc for $code {
94 fn compute(_space_kind: SpaceKind, _cyclomatic: &cyclomatic::Stats, _stats: &mut Stats) {}
95 }
96 )+
97 );
98 ([$trait:ident], $($code:ident),+) => (
99 $(
100 impl $trait for $code {}
101 )+
102 );
103 ($trait:ident, $($code:ident),+) => (
104 $(
105 impl $trait for $code {
106 fn compute(_node: &Node, _stats: &mut Stats) {}
107 }
108 )+
109 )
110}
111
112macro_rules! mk_lang {
113 ( $( ($feature:literal, $camel:ident, $name:ident, $display: expr, $description:expr, $version:literal) ),* ) => {
114 /// The list of supported languages.
115 ///
116 /// Every variant is always defined regardless of the Cargo
117 /// feature set: per-language features only gate the grammar
118 /// crate references, never the enum surface itself. Disabled
119 /// variants surface at runtime as
120 /// [`crate::MetricsError::LanguageDisabled`] from every entry
121 /// point that returns a `Result`.
122 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
123 pub enum LANG {
124 $(
125 #[doc = $description]
126 $camel,
127 )*
128 }
129 impl LANG {
130 /// Return an iterator over the supported languages.
131 ///
132 /// # Examples
133 ///
134 /// ```
135 /// use big_code_analysis::LANG;
136 ///
137 /// for lang in LANG::into_enum_iter() {
138 /// println!("{:?}", lang);
139 /// }
140 /// ```
141 pub fn into_enum_iter() -> impl Iterator<Item=LANG> {
142 use LANG::*;
143 [$( $camel, )*].into_iter()
144 }
145
146 /// Returns the name of a language as a `&str`.
147 ///
148 /// # Examples
149 ///
150 /// ```
151 /// use big_code_analysis::LANG;
152 ///
153 /// println!("{}", LANG::Rust.name());
154 /// ```
155 pub fn name(&self) -> &'static str {
156 match self {
157 $(
158 LANG::$camel => $display,
159 )*
160 }
161 }
162
163 /// Returns the pinned tree-sitter grammar crate version that
164 /// backs this variant (e.g. `"0.25.1"` for [`LANG::Bash`]).
165 ///
166 /// The value mirrors the `=X.Y.Z` pin in the workspace
167 /// `Cargo.toml` and is independent of the per-language Cargo
168 /// feature: it is returned even for a variant whose feature is
169 /// disabled in the current build (a build-time constant, no
170 /// grammar crate reference). A drift test in `src/langs.rs`
171 /// asserts every value here matches the manifest pin.
172 ///
173 /// # Grammars vs. forks
174 ///
175 /// For languages backed by an upstream crates.io grammar
176 /// (`bash`, `rust`, `python`, `typescript`, …) this is the
177 /// exact upstream grammar version, so a consumer migrating
178 /// matchers off py-tree-sitter can line node-kind vocabularies
179 /// up against the same pin. For the vendored big-code-analysis
180 /// forks (`mozcpp`, `mozjs`, `tcl`, `ccomment`, `preproc`,
181 /// `kotlin`) the value is the **fork crate's** version
182 /// (published as `bca-tree-sitter-*` / `tree-sitter-kotlin-ng`),
183 /// not an upstream tree-sitter grammar semver — there is no
184 /// upstream release to compare against.
185 ///
186 /// This is part of the value-not-stable surface: the returned
187 /// version changes whenever the grammar pin is bumped.
188 #[must_use]
189 pub fn grammar_version(&self) -> &'static str {
190 match self {
191 $(
192 LANG::$camel => $version,
193 )*
194 }
195 }
196
197 /// Reports whether this variant's grammar crate is
198 /// compiled into the current build.
199 ///
200 /// Returns `false` for variants whose per-language Cargo
201 /// feature is disabled; calling
202 /// [`Self::tree_sitter_language`], [`crate::analyze`],
203 /// or any other dispatcher with such a variant will
204 /// return [`crate::MetricsError::LanguageDisabled`].
205 #[must_use]
206 pub fn is_enabled(&self) -> bool {
207 match self {
208 $(
209 #[cfg(feature = $feature)]
210 LANG::$camel => true,
211 #[cfg(not(feature = $feature))]
212 LANG::$camel => false,
213 )*
214 }
215 }
216
217 // Returns a tree-sitter language paired with this variant,
218 // or `Err(LanguageDisabled)` when the matching Cargo
219 // feature is off. This is the internal entry point used
220 // by `Tree::new` to construct a parser; the public
221 // counterpart is `tree_sitter_language`.
222 pub(crate) fn get_ts_language(&self) -> Result<Language, crate::MetricsError> {
223 match self {
224 $(
225 #[cfg(feature = $feature)]
226 LANG::$camel => Ok(get_language!($name)),
227 #[cfg(not(feature = $feature))]
228 LANG::$camel => Err(crate::MetricsError::LanguageDisabled(*self)),
229 )*
230 }
231 }
232
233 /// Returns the [`tree_sitter::Language`] grammar used by
234 /// this variant.
235 ///
236 /// Useful when feeding a caller-built
237 /// [`tree_sitter::Parser`] into the
238 /// [`crate::Ast::from_tree_sitter`] entry point — the
239 /// language returned here is the one the metric walker
240 /// expects for `kind_id` matching, so the trees agree
241 /// structurally.
242 ///
243 /// This method is part of the value-not-stable surface:
244 /// the underlying `tree-sitter-*` grammar pin may bump
245 /// in any minor release, which can change `Language`
246 /// equality on the caller side.
247 ///
248 /// # Errors
249 ///
250 /// Returns [`crate::MetricsError::LanguageDisabled`] when
251 /// the variant's per-language Cargo feature is not
252 /// enabled in the current build (see the `[features]`
253 /// table in the root `Cargo.toml`).
254 ///
255 /// # Examples
256 ///
257 /// ```
258 /// use big_code_analysis::LANG;
259 ///
260 /// let _lang = LANG::Rust.tree_sitter_language().expect("rust feature enabled");
261 /// ```
262 pub fn tree_sitter_language(&self) -> Result<::tree_sitter::Language, crate::MetricsError> {
263 self.get_ts_language()
264 }
265 }
266
267 /// Renders the language's canonical lowercase slug, identical to
268 /// [`LANG::name`].
269 ///
270 /// Every variant has a distinct slug, so `Display` is injective
271 /// and a `Display` → [`FromStr`](std::str::FromStr) round-trip
272 /// returns the original variant (see the round-trip test in
273 /// `src/langs.rs`). The slug is the single canonical identifier
274 /// used across every surface (CLI JSON, web `/metrics`, the
275 /// Python bindings): it contains no punctuation and is always a
276 /// valid `FromStr` lookup token.
277 impl ::std::fmt::Display for LANG {
278 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
279 f.write_str(self.name())
280 }
281 }
282
283 /// Parses a [`LANG`] from its [`Display`](std::fmt::Display)
284 /// spelling (the canonical lowercase [`LANG::name`] slug, e.g.
285 /// `"rust"`, `"cpp"`, `"csharp"`, `"tsx"`).
286 ///
287 /// Matching is case-sensitive and exact, mirroring
288 /// [`Metric`](crate::Metric)'s `FromStr`: only the canonical
289 /// lowercase slug is accepted. File extensions and emacs modes
290 /// are deliberately *not* accepted here — use
291 /// [`get_from_ext`](crate::get_from_ext) /
292 /// [`get_from_emacs_mode`](crate::get_from_emacs_mode) for those.
293 ///
294 /// Every variant has a distinct slug, so this is the exact
295 /// inverse of [`Display`](std::fmt::Display): the round-trip
296 /// `LANG::from_str(&lang.to_string())` returns the original
297 /// variant for every `LANG`.
298 impl ::std::str::FromStr for LANG {
299 type Err = $crate::macros::ParseLangError;
300
301 fn from_str(s: &str) -> Result<Self, Self::Err> {
302 LANG::into_enum_iter()
303 .find(|lang| lang.name() == s)
304 .ok_or_else(|| $crate::macros::ParseLangError::new(s))
305 }
306 }
307 };
308}
309
310/// Error returned by [`LANG`](crate::LANG)'s
311/// [`FromStr`](std::str::FromStr) impl when the input is not a
312/// recognised language name.
313///
314/// Holds the offending input verbatim so wrapper layers can format
315/// their own user-facing message; mirrors
316/// [`ParseMetricError`](crate::ParseMetricError).
317#[derive(Debug, Clone, PartialEq, Eq)]
318pub struct ParseLangError(String);
319
320impl ParseLangError {
321 // Constructor kept `pub(crate)` so the macro-generated `FromStr`
322 // impl in `src/langs.rs` can build the error without exposing the
323 // private field across module boundaries.
324 pub(crate) fn new(input: &str) -> Self {
325 Self(input.to_owned())
326 }
327
328 /// The rejected input that failed to parse as a language name.
329 ///
330 /// Lets callers recover the offending string programmatically
331 /// rather than scraping it out of the [`Display`](std::fmt::Display)
332 /// output. Mirrors
333 /// [`ParseMetricError::input`](crate::ParseMetricError::input).
334 #[must_use]
335 pub fn input(&self) -> &str {
336 &self.0
337 }
338}
339
340impl ::std::fmt::Display for ParseLangError {
341 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
342 write!(f, "unknown language: {}", self.0)
343 }
344}
345
346impl ::std::error::Error for ParseLangError {}
347
348macro_rules! mk_action {
349 ( $( ($feature:literal, $camel:ident, $parser:ident) ),* ) => {
350 /// Language-dispatched bundle of a parsed tree plus its
351 /// source bytes, one variant per Cargo-feature-enabled
352 /// language. The public seam is [`crate::Ast`]; this enum is
353 /// the macro-generated internal carrier it wraps.
354 ///
355 /// With every per-language feature disabled this enum is a
356 /// 0-variant uninhabited type. Each method below therefore
357 /// terminates its `match self` with a
358 /// `#[cfg(not(any(feature = …)))] _ => match *self {}` arm:
359 /// stable Rust treats `&UninhabitedType` as inhabited (E0004),
360 /// so the outer match needs a wildcard, and `match *self {}`
361 /// is exhaustive over the uninhabited dereferenced value —
362 /// divergent, no panic, no `unsafe`, statically unreachable in
363 /// safe code because the public seam `crate::Ast` has only
364 /// fallible constructors that return `Err(LanguageDisabled)`
365 /// for every `LANG` variant under that build.
366 ///
367 /// When a method takes by-value parameters (see
368 /// [`Self::run_metrics`]), prefix the divergent arm with
369 /// `let _ = (param1, param2, …);` to silence
370 /// `unused_variables` under `RUSTFLAGS=-D warnings` — the
371 /// `match *self {}` body is `!`, so the consumed values are
372 /// never actually dropped at runtime.
373 pub(crate) enum AstInner {
374 $(
375 #[cfg(feature = $feature)]
376 $camel($parser),
377 )*
378 }
379
380 impl AstInner {
381 /// Run the metric walker against the held parse. The
382 /// caller passes `name` and `options` per call so a
383 /// single `AstInner` can be reused with different metric
384 /// subsets.
385 pub(crate) fn run_metrics(
386 &self,
387 name: Option<String>,
388 options: MetricsOptions,
389 ) -> Result<FuncSpace, MetricsError> {
390 match self {
391 $(
392 #[cfg(feature = $feature)]
393 AstInner::$camel(parser) => metrics_inner(parser, name, options),
394 )*
395 #[cfg(not(any( $( feature = $feature ),* )))]
396 _ => {
397 let _ = (name, options);
398 match *self {}
399 },
400 }
401 }
402
403 /// Run the operator/operand walk against the held parse,
404 /// carrying an explicit `name` end-to-end. Backs
405 /// [`crate::Ast::ops`]; the ops analogue of [`Self::run_metrics`].
406 pub(crate) fn run_ops(
407 &self,
408 name: Option<String>,
409 ) -> Result<Ops, MetricsError> {
410 match self {
411 $(
412 #[cfg(feature = $feature)]
413 AstInner::$camel(parser) => ops_inner(parser, name),
414 )*
415 #[cfg(not(any( $( feature = $feature ),* )))]
416 _ => {
417 let _ = name;
418 match *self {}
419 },
420 }
421 }
422
423 /// Strip comments from the held parse. Backs
424 /// [`crate::Ast::strip_comments`]; the comment-removal analogue
425 /// of [`Self::run_ops`].
426 pub(crate) fn run_strip_comments(&self) -> Option<Vec<u8>> {
427 match self {
428 $(
429 #[cfg(feature = $feature)]
430 AstInner::$camel(parser) => crate::comment_rm::rm_comments(parser),
431 )*
432 #[cfg(not(any( $( feature = $feature ),* )))]
433 _ => match *self {},
434 }
435 }
436
437 /// Detect the span of every function in the held parse. Backs
438 /// [`crate::Ast::functions`].
439 pub(crate) fn run_functions(&self) -> Vec<crate::FunctionSpan> {
440 match self {
441 $(
442 #[cfg(feature = $feature)]
443 AstInner::$camel(parser) => crate::function::function(parser),
444 )*
445 #[cfg(not(any( $( feature = $feature ),* )))]
446 _ => match *self {},
447 }
448 }
449
450 /// Build the AST dump for the held parse under `cfg`. Backs
451 /// [`crate::Ast::dump`].
452 pub(crate) fn run_dump(&self, cfg: crate::AstCfg) -> crate::AstResponse {
453 match self {
454 $(
455 #[cfg(feature = $feature)]
456 AstInner::$camel(parser) => crate::ast::dump_inner(parser, cfg),
457 )*
458 #[cfg(not(any( $( feature = $feature ),* )))]
459 _ => {
460 let _ = cfg;
461 match *self {}
462 },
463 }
464 }
465
466 /// Count `(matching, total)` nodes for `filters` in the held
467 /// parse. Backs [`crate::Ast::count`].
468 pub(crate) fn run_count(&self, filters: &[String]) -> (usize, usize) {
469 match self {
470 $(
471 #[cfg(feature = $feature)]
472 AstInner::$camel(parser) => crate::count::count(parser, filters),
473 )*
474 #[cfg(not(any( $( feature = $feature ),* )))]
475 _ => {
476 let _ = filters;
477 match *self {}
478 },
479 }
480 }
481
482 /// Find every node matching `filters` in the held parse. Backs
483 /// [`crate::Ast::find`]; the returned nodes borrow the held tree.
484 pub(crate) fn run_find(
485 &self,
486 filters: &[String],
487 ) -> Result<Vec<crate::Node<'_>>, MetricsError> {
488 match self {
489 $(
490 #[cfg(feature = $feature)]
491 AstInner::$camel(parser) => crate::find::find(parser, filters),
492 )*
493 #[cfg(not(any( $( feature = $feature ),* )))]
494 _ => {
495 let _ = filters;
496 match *self {}
497 },
498 }
499 }
500
501 /// Collect every in-source suppression marker in the held parse.
502 /// Backs [`crate::Ast::suppressions`].
503 pub(crate) fn run_suppressions(&self) -> Vec<crate::SuppressionMarker> {
504 match self {
505 $(
506 #[cfg(feature = $feature)]
507 AstInner::$camel(parser) => crate::suppression::suppression_markers(parser),
508 )*
509 #[cfg(not(any( $( feature = $feature ),* )))]
510 _ => match *self {},
511 }
512 }
513
514 /// Borrow the root [`crate::Node`] of the held parse. Backs
515 /// [`crate::Ast::root_node`].
516 pub(crate) fn root_node(&self) -> crate::Node<'_> {
517 match self {
518 $(
519 #[cfg(feature = $feature)]
520 AstInner::$camel(parser) => parser.root(),
521 )*
522 #[cfg(not(any( $( feature = $feature ),* )))]
523 _ => match *self {},
524 }
525 }
526
527 pub(crate) fn language(&self) -> LANG {
528 match self {
529 $(
530 #[cfg(feature = $feature)]
531 AstInner::$camel(_) => LANG::$camel,
532 )*
533 #[cfg(not(any( $( feature = $feature ),* )))]
534 _ => match *self {},
535 }
536 }
537
538 pub(crate) fn code_bytes(&self) -> &[u8] {
539 match self {
540 $(
541 #[cfg(feature = $feature)]
542 AstInner::$camel(parser) => parser.code(),
543 )*
544 #[cfg(not(any( $( feature = $feature ),* )))]
545 _ => match *self {},
546 }
547 }
548
549 pub(crate) fn ts_tree(&self) -> &::tree_sitter::Tree {
550 match self {
551 $(
552 #[cfg(feature = $feature)]
553 AstInner::$camel(parser) => parser.ts_tree(),
554 )*
555 #[cfg(not(any( $( feature = $feature ),* )))]
556 _ => match *self {},
557 }
558 }
559 }
560
561 /// Internal parse-dispatch shim that backs [`crate::Ast::parse`].
562 /// Lives in the `mk_action!` macro so each new language only
563 /// has to declare its parser tag once.
564 pub(crate) fn ast_parse_dispatch(
565 lang: LANG,
566 source: Vec<u8>,
567 preproc_path: Option<&Path>,
568 preproc: Option<Arc<PreprocResults>>,
569 ) -> Result<AstInner, MetricsError> {
570 // `Parser::new` keys the C++ macro-expansion lookup off the
571 // caller-supplied path; for callers analysing in-memory
572 // snippets with no preprocessor path, fall back to an
573 // empty `Path` ("") which the lookup ignores. The empty
574 // path is *not* leaked into `FuncSpace::name` — that
575 // is carried separately on `Ast`. `source` is taken by value
576 // so an owned `Source` (`Source::from_bytes`) moves its
577 // buffer straight into the parser instead of copying it.
578 let preproc_path = preproc_path.unwrap_or(Path::new(""));
579 match lang {
580 $(
581 #[cfg(feature = $feature)]
582 LANG::$camel => Ok(AstInner::$camel($parser::new(source, preproc_path, preproc))),
583 #[cfg(not(feature = $feature))]
584 LANG::$camel => {
585 let _ = (source, preproc_path, preproc);
586 Err(MetricsError::LanguageDisabled(lang))
587 },
588 )*
589 }
590 }
591
592 /// Internal tree-adoption dispatch that backs
593 /// [`crate::Ast::from_tree_sitter`].
594 pub(crate) fn ast_from_tree_dispatch(
595 lang: LANG,
596 tree: ::tree_sitter::Tree,
597 source: Vec<u8>,
598 ) -> Result<AstInner, MetricsError> {
599 match lang {
600 $(
601 #[cfg(feature = $feature)]
602 LANG::$camel => Ok(AstInner::$camel($parser::from_tree(tree, source))),
603 #[cfg(not(feature = $feature))]
604 LANG::$camel => {
605 let _ = (tree, source);
606 Err(MetricsError::LanguageDisabled(lang))
607 },
608 )*
609 }
610 }
611
612 };
613}
614
615macro_rules! mk_extensions {
616 ( $( ($camel:ident, [ $( $ext:ident ),* ]) ),* ) => {
617 /// Detects the language associated to the input file extension.
618 ///
619 /// # Examples
620 ///
621 /// ```
622 /// use big_code_analysis::get_from_ext;
623 ///
624 /// let ext = "rs";
625 ///
626 /// get_from_ext(ext).unwrap();
627 /// ```
628 pub fn get_from_ext(ext: &str) -> Option<LANG>{
629 match ext {
630 $(
631 $(
632 stringify!($ext) => Some(LANG::$camel),
633 )*
634 )*
635 _ => None,
636 }
637 }
638
639 impl LANG {
640 /// Returns the file extensions recognised for this language.
641 ///
642 /// The returned list is the same one consulted by
643 /// [`get_from_ext`] and [`crate::get_language_for_file`].
644 /// Helper variants without user-facing files (`Ccomment`,
645 /// `Preproc`) return an empty slice.
646 ///
647 /// # Examples
648 ///
649 /// ```
650 /// use big_code_analysis::LANG;
651 ///
652 /// assert!(LANG::Rust.extensions().contains(&"rs"));
653 /// ```
654 #[must_use]
655 pub fn extensions(&self) -> &'static [&'static str] {
656 match self {
657 $(
658 LANG::$camel => &[ $( stringify!($ext), )* ],
659 )*
660 }
661 }
662 }
663 };
664}
665
666macro_rules! mk_emacs_mode {
667 ( $( ($camel:ident, [ $( $emacs_mode:expr ),* ]) ),* ) => {
668 /// Detects the language associated to the input `Emacs` mode.
669 ///
670 /// An `Emacs` mode is used to detect a language according to
671 /// particular text-information contained in a file.
672 ///
673 /// # Examples
674 ///
675 /// ```
676 /// use big_code_analysis::get_from_emacs_mode;
677 ///
678 /// let emacs_mode = "rust";
679 ///
680 /// get_from_emacs_mode(emacs_mode).unwrap();
681 /// ```
682 pub fn get_from_emacs_mode(mode: &str) -> Option<LANG>{
683 match mode {
684 $(
685 $(
686 $emacs_mode => Some(LANG::$camel),
687 )*
688 )*
689 _ => None,
690 }
691 }
692 };
693}
694
695macro_rules! mk_code {
696 ( $( ($camel:ident, $code:ident, $parser:ident, $name:ident, $docname:expr) ),* ) => {
697 $(
698 #[doc = concat!("Per-language code type tag for ", $docname, "; carries no data.")]
699 pub(crate) struct $code { _guard: (), }
700
701 impl LanguageInfo for $code {
702 type BaseLang = $camel;
703
704 fn lang() -> LANG {
705 LANG::$camel
706 }
707 }
708
709 #[doc = "The `"]
710 #[doc = $docname]
711 #[doc = "` language parser."]
712 pub(crate) type $parser = Parser<$code>;
713 )*
714 };
715}
716
717macro_rules! mk_langs {
718 ( $( ($feature:literal, $camel:ident, $description: expr, $display: expr, $code:ident, $parser:ident, $name:ident, [ $( $ext:ident ),* ], [ $( $emacs_mode:expr ),* ], $version:literal) ),* ) => {
719 mk_lang!($( ($feature, $camel, $name, $display, $description, $version) ),*);
720 mk_action!($( ($feature, $camel, $parser) ),*);
721 mk_extensions!($( ($camel, [ $( $ext ),* ]) ),*);
722 mk_emacs_mode!($( ($camel, [ $( $emacs_mode ),* ]) ),*);
723 mk_code!($( ($camel, $code, $parser, $name, stringify!($camel)) ),*);
724 };
725}
726
727mod kind_sets;
728
729pub(crate) use implement_metric_trait;
730pub(crate) use kind_sets::{
731 cpp_bool_terminal_kinds, csharp_bool_terminal_kinds, csharp_invocation_expr_kinds,
732 csharp_paren_expr_kinds, csharp_prefix_unary_expr_kinds, csharp_var_decl_kinds,
733 csharp_var_declarator_kinds, elixir_bool_terminal_kinds, go_bool_terminal_kinds,
734 groovy_bool_terminal_kinds, irules_bool_terminal_kinds, java_bool_terminal_kinds,
735 javascript_bool_terminal_kinds, kotlin_bool_terminal_kinds, lua_bool_terminal_kinds,
736 mozjs_bool_terminal_kinds, perl_bool_terminal_kinds, php_bool_terminal_kinds,
737 python_bool_terminal_kinds, ruby_bool_terminal_kinds, rust_bool_terminal_kinds,
738 tcl_bool_terminal_kinds, tsx_bool_terminal_kinds, typescript_bool_terminal_kinds,
739};
740pub(crate) use {
741 get_language, mk_action, mk_code, mk_emacs_mode, mk_extensions, mk_lang, mk_langs,
742};