Skip to main content

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};