Skip to main content

brokk_bifrost_cpp/
template_heads.rs

1//! Structured reconciliation of C++ template heads and class-template owners
2//! (#6902 item 7).
3//!
4//! A declaration and its definition do not have to spell their template
5//! heads alike. An in-class `void get(T);` in `template <class T> struct Box`
6//! and its out-of-line body `template <typename U> void Box<U>::get(U) {}`
7//! declare one member ([temp.mem.func], [temp.over.link]): the heads agree in
8//! arity and in the kind of each parameter, the written owner `Box<U>` names
9//! the primary template (its arguments are exactly the head's parameters, in
10//! order), and the parameter types agree once `U` is read as `T`.
11//!
12//! This module answers that question from syntax and nothing else. It turns
13//! one declaration into a [`CppTemplateContext`] -- the template levels that
14//! apply to it, outermost first: each enclosing class with the head that
15//! introduces its parameters and the arguments that select it, then the
16//! declaration's own head -- and [`CppTemplateContext::reconcile`] decides
17//! whether two contexts declare one entity. Equivalence carries a
18//! [`CppTemplateBinding`] from the right context's parameter names to the
19//! left's, which the caller applies when it compares the parameter types.
20//!
21//! The comparison is deliberately positional and structural. Parameter names
22//! never decide anything; kinds, pack-ness, non-type parameter types,
23//! requires-clauses and owner arguments do. A written owner whose arguments are
24//! not the head's parameters (`Box<T*>`, `Box<int>`) selects a specialization,
25//! which is a different owner from the primary; the arguments are kept as
26//! [`CppTemplateTerm`]s so a specialization member can still be matched with
27//! its own in-class declaration, and so later owner-selection work (a
28//! specialization alias keeping its owner, #6902 item 9) can compare a
29//! qualifier's arguments against a specialization's with the same terms.
30//!
31//! What the module cannot model -- a qualifier spelled through an alias, more
32//! heads than the qualifier can consume, a local class, parser recovery --
33//! answers [`CppTemplateReconciliation::Unsupported`] rather than either
34//! verdict, so callers keep those pairs apart without claiming they differ.
35
36use crate::declarations::{
37    cpp_template_parameter_kind, cpp_template_parameter_name, cpp_template_term, node_text,
38    normalize_cpp_whitespace,
39};
40use crate::graph::resolver::{
41    cpp_name_component_nodes, cpp_substitute_template_term, cpp_template_terms_equal,
42};
43use brokk_bifrost_core::analyzer::model::{CppTemplateParameterKind, CppTemplateTerm};
44use brokk_bifrost_core::analyzer::tree_walk::ParentIndex;
45use brokk_bifrost_core::hash::{HashMap, HashSet};
46use tree_sitter::Node;
47
48/// What a template parameter is, apart from its name.
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum CppTemplateParameterShape {
51    Type,
52    /// A non-type parameter: its declared type and, when the declarator adds
53    /// pointer or reference structure, that declarator. Both terms read the
54    /// parameters already in scope, the parameter itself included, as
55    /// [`CppTemplateTerm::Parameter`].
56    Value {
57        type_term: CppTemplateTerm,
58        declarator: Option<CppTemplateTerm>,
59    },
60    /// A template template parameter: the kind and pack-ness of each of its
61    /// own parameters. Their names are unobservable, and a nested template
62    /// template parameter is compared by kind only.
63    Template(Vec<(CppTemplateParameterKind, bool)>),
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct CppTemplateParameter {
68    /// The written name, or an impossible identifier for an unnamed one.
69    pub name: String,
70    pub variadic: bool,
71    pub shape: CppTemplateParameterShape,
72}
73
74impl CppTemplateParameter {
75    /// The argument that names this parameter in its own template: `T`, or
76    /// `Ts...` for a pack -- the injected template arguments of a primary
77    /// class template ([temp.local]).
78    fn injected_argument(&self) -> CppTemplateTerm {
79        let parameter = CppTemplateTerm::Parameter(self.name.clone());
80        if self.variadic {
81            CppTemplateTerm::Node {
82                kind: "parameter_pack_expansion".to_owned(),
83                children: vec![
84                    parameter,
85                    CppTemplateTerm::Atom {
86                        kind: "...".to_owned(),
87                        text: "...".to_owned(),
88                    },
89                ],
90            }
91        } else {
92            parameter
93        }
94    }
95}
96
97/// One `template <...>` head with a non-empty parameter list.
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct CppTemplateHead {
100    pub parameters: Vec<CppTemplateParameter>,
101    /// The head's requires-clause, over the parameters in scope.
102    pub requires: Option<CppTemplateTerm>,
103}
104
105/// One level of template ownership: an enclosing class, or the declaration
106/// itself (always the last level; its `name` is empty for a callable).
107#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct CppTemplateLevel {
109    /// The class name as written at this level; empty for a callable.
110    pub name: String,
111    /// The head that introduces this level's parameters. `None` for a
112    /// non-template level and for an explicit specialization, whose
113    /// `template <>` introduces nothing.
114    pub head: Option<CppTemplateHead>,
115    /// The written template arguments that select this level's class: the
116    /// specialization arguments of an in-class declaration's class, or the
117    /// qualifier's arguments of an out-of-line one, or a function template
118    /// explicit specialization's own arguments. `None` means the primary
119    /// template's injected arguments when `head` is present, and a
120    /// non-template class otherwise.
121    pub arguments: Option<Vec<CppTemplateTerm>>,
122}
123
124impl CppTemplateLevel {
125    fn is_bare(&self) -> bool {
126        self.head.is_none() && self.arguments.is_none()
127    }
128
129    /// Whether the written arguments select a specialization rather than the
130    /// primary template: they are not the injected arguments of the head that
131    /// introduces this level's parameters. `Box<T*>` under `template <class
132    /// T>` and `Box<int>` with no head of its own both do; `Box<U>` under
133    /// `template <class U>` names the primary.
134    pub fn selects_specialization(&self) -> bool {
135        let Some(arguments) = &self.arguments else {
136            return false;
137        };
138        let Some(head) = &self.head else {
139            return true;
140        };
141        arguments.len() != head.parameters.len()
142            || !arguments
143                .iter()
144                .zip(&head.parameters)
145                .all(|(argument, parameter)| {
146                    cpp_template_terms_equal(argument, &parameter.injected_argument())
147                })
148    }
149
150    fn effective_arguments(&self) -> Option<Vec<CppTemplateTerm>> {
151        self.arguments.clone().or_else(|| {
152            self.head.as_ref().map(|head| {
153                head.parameters
154                    .iter()
155                    .map(CppTemplateParameter::injected_argument)
156                    .collect()
157            })
158        })
159    }
160}
161
162/// The template levels that apply to one declaration, outermost first; the
163/// last level is the declaration's own.
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub struct CppTemplateContext {
166    pub levels: Vec<CppTemplateLevel>,
167}
168
169/// The right context's template parameters renamed to the left's, position by
170/// position. Empty for two non-template contexts.
171#[derive(Debug, Clone, Default, PartialEq, Eq)]
172pub struct CppTemplateBinding {
173    right_to_left: HashMap<String, CppTemplateTerm>,
174    left: HashSet<String>,
175}
176
177impl CppTemplateBinding {
178    pub fn is_empty(&self) -> bool {
179        self.left.is_empty()
180    }
181
182    pub fn is_left_parameter(&self, name: &str) -> bool {
183        self.left.contains(name)
184    }
185
186    pub fn is_right_parameter(&self, name: &str) -> bool {
187        self.right_to_left.contains_key(name)
188    }
189
190    /// Whether `right` is the parameter the binding pairs with `left`.
191    pub fn binds(&self, right: &str, left: &str) -> bool {
192        matches!(
193            self.right_to_left.get(right),
194            Some(CppTemplateTerm::Parameter(bound)) if bound == left
195        )
196    }
197
198    /// `term`, written over the right context's parameters, rewritten over
199    /// the left's. `None` when it names a parameter the binding does not know.
200    pub fn rename(&self, term: &CppTemplateTerm) -> Option<CppTemplateTerm> {
201        cpp_substitute_template_term(term, &self.right_to_left)
202    }
203
204    /// Whether a right-context term means what a left-context term means.
205    pub fn terms_agree(&self, left: &CppTemplateTerm, right: &CppTemplateTerm) -> bool {
206        self.rename(right)
207            .is_some_and(|renamed| cpp_template_terms_equal(left, &renamed))
208    }
209
210    fn bind(&mut self, right: &str, left: &str) {
211        self.left.insert(left.to_owned());
212        self.right_to_left.insert(
213            right.to_owned(),
214            CppTemplateTerm::Parameter(left.to_owned()),
215        );
216    }
217}
218
219/// Whether two template contexts declare one entity.
220#[derive(Debug, Clone, PartialEq, Eq)]
221pub enum CppTemplateReconciliation {
222    /// The heads and owner arguments agree level by level.
223    Equivalent(CppTemplateBinding),
224    /// A head, a parameter, a requires-clause or an owner argument list
225    /// differs: different arity, kind or pack-ness, a non-type parameter of
226    /// another type, a specialization against the primary.
227    Distinct,
228    /// The levels cannot be aligned structurally, for example because a
229    /// qualifier is spelled through an alias.
230    Unsupported,
231}
232
233impl CppTemplateContext {
234    /// Whether any level carries a head or written arguments.
235    pub fn is_templated(&self) -> bool {
236        self.levels.iter().any(|level| !level.is_bare())
237    }
238
239    /// Decide whether `self` and `other` declare one entity.
240    ///
241    /// Levels are aligned from the declaration outwards. Leading levels only
242    /// one side spells -- the namespaces an out-of-line qualifier names, or a
243    /// non-template outer class one side omits -- must be bare, because a
244    /// level that introduces or selects template arguments has to be matched.
245    pub fn reconcile(&self, other: &CppTemplateContext) -> CppTemplateReconciliation {
246        let shared = self.levels.len().min(other.levels.len());
247        let (left_extra, left) = self.levels.split_at(self.levels.len() - shared);
248        let (right_extra, right) = other.levels.split_at(other.levels.len() - shared);
249        if !left_extra
250            .iter()
251            .chain(right_extra)
252            .all(CppTemplateLevel::is_bare)
253        {
254            return CppTemplateReconciliation::Unsupported;
255        }
256        let mut binding = CppTemplateBinding::default();
257        for (left_level, right_level) in left.iter().zip(right) {
258            if left_level.name != right_level.name {
259                return CppTemplateReconciliation::Unsupported;
260            }
261            match (&left_level.head, &right_level.head) {
262                (None, None) => {}
263                (Some(left_head), Some(right_head)) => {
264                    if !heads_agree(left_head, right_head, &mut binding) {
265                        return CppTemplateReconciliation::Distinct;
266                    }
267                }
268                _ => return CppTemplateReconciliation::Distinct,
269            }
270            let agree = match (
271                left_level.effective_arguments(),
272                right_level.effective_arguments(),
273            ) {
274                (None, None) => true,
275                (Some(left_arguments), Some(right_arguments)) => {
276                    left_arguments.len() == right_arguments.len()
277                        && left_arguments
278                            .iter()
279                            .zip(&right_arguments)
280                            .all(|(left, right)| binding.terms_agree(left, right))
281                }
282                _ => false,
283            };
284            if !agree {
285                return CppTemplateReconciliation::Distinct;
286            }
287        }
288        CppTemplateReconciliation::Equivalent(binding)
289    }
290}
291
292/// Compare two heads position by position, extending `binding` with each
293/// parameter before its own shape is compared: a later parameter's type may
294/// name an earlier one (`template <class T, T value>`), and a non-type
295/// parameter's declarator names the parameter itself.
296fn heads_agree(
297    left: &CppTemplateHead,
298    right: &CppTemplateHead,
299    binding: &mut CppTemplateBinding,
300) -> bool {
301    if left.parameters.len() != right.parameters.len() {
302        return false;
303    }
304    for (left_parameter, right_parameter) in left.parameters.iter().zip(&right.parameters) {
305        if left_parameter.variadic != right_parameter.variadic {
306            return false;
307        }
308        binding.bind(&right_parameter.name, &left_parameter.name);
309        let agree = match (&left_parameter.shape, &right_parameter.shape) {
310            (CppTemplateParameterShape::Type, CppTemplateParameterShape::Type) => true,
311            (
312                CppTemplateParameterShape::Value {
313                    type_term: left_type,
314                    declarator: left_declarator,
315                },
316                CppTemplateParameterShape::Value {
317                    type_term: right_type,
318                    declarator: right_declarator,
319                },
320            ) => {
321                binding.terms_agree(left_type, right_type)
322                    && match (left_declarator, right_declarator) {
323                        (None, None) => true,
324                        (Some(left), Some(right)) => binding.terms_agree(left, right),
325                        _ => false,
326                    }
327            }
328            (
329                CppTemplateParameterShape::Template(left_kinds),
330                CppTemplateParameterShape::Template(right_kinds),
331            ) => left_kinds == right_kinds,
332            _ => false,
333        };
334        if !agree {
335            return false;
336        }
337    }
338    match (&left.requires, &right.requires) {
339        (None, None) => true,
340        (Some(left), Some(right)) => binding.terms_agree(left, right),
341        _ => false,
342    }
343}
344
345/// The template context of the declaration `node` -- a function definition,
346/// declaration, field declaration or function declarator, or a class
347/// specifier -- read from its syntax.
348///
349/// `None` when the shape is outside what this module models: parser recovery
350/// on the path, a local class, a qualified name inside a class body, or more
351/// heads than the written owner can consume.
352pub fn cpp_declaration_template_context(
353    node: Node<'_>,
354    source: &str,
355) -> Option<CppTemplateContext> {
356    let statement = declaration_statement(node)?;
357    let (own_heads, top) = direct_template_heads(statement);
358    let qualified = declaration_qualifier(statement);
359    let mut enclosing = Vec::new();
360    let mut current = top;
361    while let Some(parent) = current.parent() {
362        match parent.kind() {
363            "field_declaration_list" => {
364                let class = parent.parent()?;
365                if !is_class_specifier(class) {
366                    return None;
367                }
368                enclosing.push(class);
369                current = class;
370            }
371            "template_declaration"
372            | "declaration"
373            | "field_declaration"
374            | "declaration_list"
375            | "namespace_definition"
376            | "linkage_specification"
377            | "preproc_if"
378            | "preproc_ifdef"
379            | "preproc_else"
380            | "preproc_elif"
381            | "preproc_elifdef" => current = parent,
382            "translation_unit" => break,
383            _ => return None,
384        }
385    }
386    let mut builder = ContextBuilder {
387        source,
388        ancestry: ParentIndex::unindexed(),
389        names: Vec::new(),
390        levels: Vec::new(),
391    };
392    for class in enclosing.into_iter().rev() {
393        let (heads, _) = direct_template_heads(class);
394        match heads.as_slice() {
395            [] => builder.push_class(class, None)?,
396            [head] => builder.push_class(class, Some(*head))?,
397            _ => return None,
398        }
399    }
400    if is_class_specifier(statement) {
401        match own_heads.as_slice() {
402            [] => builder.push_class(statement, None)?,
403            [head] => builder.push_class(statement, Some(*head))?,
404            _ => return None,
405        }
406        return Some(builder.finish());
407    }
408    match qualified {
409        // An out-of-line definition lives at namespace scope; a qualified
410        // name inside a class body is a friend or a recovery artifact.
411        Some(_) if !builder.levels.is_empty() => return None,
412        Some(qualifier) => builder.push_out_of_line(qualifier, &own_heads)?,
413        None => {
414            let head = match own_heads.as_slice() {
415                [] => None,
416                [head] => builder.head(*head)?,
417                _ => return None,
418            };
419            builder.levels.push(CppTemplateLevel {
420                name: String::new(),
421                head,
422                arguments: None,
423            });
424        }
425    }
426    // An explicit specialization of a function template, `f<int>`, names its
427    // arguments on the declaration itself; they distinguish it from both the
428    // primary template and an ordinary function of the same signature.
429    if let Some(arguments) = declaration_terminal(statement)
430        .filter(|terminal| terminal.kind() == "template_function")
431        .and_then(|terminal| terminal.child_by_field_name("arguments"))
432    {
433        let arguments = builder.arguments(arguments);
434        builder.levels.last_mut()?.arguments = Some(arguments);
435    }
436    Some(builder.finish())
437}
438
439/// The owner each qualifier component of the out-of-line declaration `node`
440/// names, outermost first, terminal member excluded.
441///
442/// A component whose written arguments select a specialization
443/// ([`CppTemplateLevel::selects_specialization`]) names that specialization by
444/// its written template-id, `Box<T*>` or `Box<int>`, which is how the class
445/// body publishes the specialization's own members. Every other component,
446/// the primary template's `Box<T>` included, names its bare class or
447/// namespace. `None` when the template levels cannot be read.
448pub fn cpp_out_of_line_owner_names(node: Node<'_>, source: &str) -> Option<Vec<String>> {
449    let context = cpp_declaration_template_context(node, source)?;
450    let mut components =
451        cpp_name_component_nodes(declaration_qualifier(declaration_statement(node)?)?)?;
452    components.pop()?;
453    // One level per qualifier component plus the member's own.
454    debug_assert_eq!(context.levels.len(), components.len() + 1);
455    let names = components
456        .into_iter()
457        .zip(&context.levels)
458        .map(|(component, level)| {
459            debug_assert_eq!(level.name, node_text(component, source));
460            match component.parent() {
461                Some(template)
462                    if level.selects_specialization()
463                        && template.kind() == "template_type"
464                        && template.child_by_field_name("name") == Some(component) =>
465                {
466                    normalize_cpp_whitespace(node_text(template, source))
467                }
468                _ => node_text(component, source).to_owned(),
469            }
470        })
471        .collect();
472    Some(names)
473}
474
475struct ContextBuilder<'a, 'tree> {
476    source: &'a str,
477    ancestry: ParentIndex<'tree>,
478    /// Every parameter name in scope at the level being built.
479    names: Vec<String>,
480    levels: Vec<CppTemplateLevel>,
481}
482
483impl<'tree> ContextBuilder<'_, 'tree> {
484    fn finish(self) -> CppTemplateContext {
485        CppTemplateContext {
486            levels: self.levels,
487        }
488    }
489
490    fn push_class(&mut self, class: Node<'tree>, head: Option<Node<'tree>>) -> Option<()> {
491        let head = match head {
492            Some(head) => self.head(head)?,
493            None => None,
494        };
495        let (name, arguments) = match class.child_by_field_name("name") {
496            None => (String::new(), None),
497            Some(name) if name.kind() == "template_type" => (
498                node_text(name.child_by_field_name("name")?, self.source).to_owned(),
499                Some(self.arguments(name.child_by_field_name("arguments")?)),
500            ),
501            Some(name) if name.kind() == "type_identifier" => {
502                (node_text(name, self.source).to_owned(), None)
503            }
504            // A qualified class name is an out-of-line nested class
505            // definition, whose heads this module does not assign.
506            Some(_) => return None,
507        };
508        self.levels.push(CppTemplateLevel {
509            name,
510            head,
511            arguments,
512        });
513        Some(())
514    }
515
516    /// Assign an out-of-line definition's heads to its qualifier.
517    ///
518    /// Each qualifier component with template arguments consumes the next
519    /// head when that head is `template <>` (an explicit specialization) or
520    /// when the component's arguments name one of its parameters; otherwise
521    /// the component is a member of an explicit specialization, which takes
522    /// no head of its own. One head left over is the member's own; more than
523    /// one cannot be assigned.
524    fn push_out_of_line(&mut self, qualifier: Node<'tree>, heads: &[Node<'tree>]) -> Option<()> {
525        let mut components = cpp_name_component_nodes(qualifier)?;
526        components.pop()?;
527        let mut heads = heads.iter().copied().peekable();
528        for component in components {
529            let arguments = component
530                .parent()
531                .filter(|parent| {
532                    matches!(parent.kind(), "template_type" | "template_function")
533                        && parent.child_by_field_name("name") == Some(component)
534                })
535                .map(|template| template.child_by_field_name("arguments"));
536            let name = node_text(component, self.source).to_owned();
537            let Some(arguments) = arguments else {
538                self.levels.push(CppTemplateLevel {
539                    name,
540                    head: None,
541                    arguments: None,
542                });
543                continue;
544            };
545            let arguments = arguments?;
546            let scope = self.names.len();
547            let head = match heads.peek().copied() {
548                Some(head) => {
549                    let parsed = self.head(head)?;
550                    let consumed = match &parsed {
551                        None => true,
552                        Some(parsed) => {
553                            let introduced = parsed
554                                .parameters
555                                .iter()
556                                .map(|parameter| parameter.name.as_str())
557                                .collect::<HashSet<_>>();
558                            self.arguments(arguments)
559                                .iter()
560                                .any(|term| term_names_any(term, &introduced))
561                        }
562                    };
563                    if consumed {
564                        heads.next();
565                        parsed
566                    } else {
567                        self.names.truncate(scope);
568                        None
569                    }
570                }
571                None => None,
572            };
573            let arguments = Some(self.arguments(arguments));
574            self.levels.push(CppTemplateLevel {
575                name,
576                head,
577                arguments,
578            });
579        }
580        let own = match heads.next() {
581            Some(head) => self.head(head)?,
582            None => None,
583        };
584        if heads.next().is_some() {
585            return None;
586        }
587        self.levels.push(CppTemplateLevel {
588            name: String::new(),
589            head: own,
590            arguments: None,
591        });
592        Some(())
593    }
594
595    /// Parse one head and bring its parameters into scope. `Some(None)` is
596    /// `template <>`.
597    fn head(&mut self, template: Node<'tree>) -> Option<Option<CppTemplateHead>> {
598        let list = template.child_by_field_name("parameters")?;
599        let mut parameters = Vec::new();
600        let mut cursor = list.walk();
601        for (index, parameter) in list
602            .named_children(&mut cursor)
603            .filter(|child| !child.is_extra())
604            .enumerate()
605        {
606            if !matches!(
607                parameter.kind(),
608                "type_parameter_declaration"
609                    | "optional_type_parameter_declaration"
610                    | "variadic_type_parameter_declaration"
611                    | "template_template_parameter_declaration"
612                    | "parameter_declaration"
613                    | "optional_parameter_declaration"
614                    | "variadic_parameter_declaration"
615            ) {
616                return None;
617            }
618            let name = cpp_template_parameter_name(parameter, self.source)
619                .unwrap_or_else(|| format!("<anonymous:{}:{index}>", self.names.len()));
620            self.names.push(name.clone());
621            let variadic = matches!(
622                parameter.kind(),
623                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
624            );
625            let shape = match cpp_template_parameter_kind(parameter) {
626                CppTemplateParameterKind::Type => CppTemplateParameterShape::Type,
627                CppTemplateParameterKind::Template => {
628                    let nested = parameter.child_by_field_name("parameters")?;
629                    let mut cursor = nested.walk();
630                    CppTemplateParameterShape::Template(
631                        nested
632                            .named_children(&mut cursor)
633                            .filter(|child| !child.is_extra())
634                            .map(|child| {
635                                (
636                                    cpp_template_parameter_kind(child),
637                                    matches!(
638                                        child.kind(),
639                                        "variadic_type_parameter_declaration"
640                                            | "variadic_parameter_declaration"
641                                    ),
642                                )
643                            })
644                            .collect(),
645                    )
646                }
647                CppTemplateParameterKind::Value => CppTemplateParameterShape::Value {
648                    type_term: self.term(parameter.child_by_field_name("type")?),
649                    declarator: parameter
650                        .child_by_field_name("declarator")
651                        .filter(|declarator| {
652                            !matches!(declarator.kind(), "identifier" | "variadic_declarator")
653                        })
654                        .map(|declarator| self.term(declarator)),
655                },
656            };
657            parameters.push(CppTemplateParameter {
658                name,
659                variadic,
660                shape,
661            });
662        }
663        let mut cursor = template.walk();
664        let requires = template
665            .named_children(&mut cursor)
666            .find(|child| child.kind() == "requires_clause")
667            .map(|clause| self.term(clause));
668        Some((!parameters.is_empty()).then_some(CppTemplateHead {
669            parameters,
670            requires,
671        }))
672    }
673
674    fn arguments(&self, list: Node<'tree>) -> Vec<CppTemplateTerm> {
675        let mut cursor = list.walk();
676        list.named_children(&mut cursor)
677            .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
678            .map(|argument| self.term(argument))
679            .collect()
680    }
681
682    fn term(&self, node: Node<'tree>) -> CppTemplateTerm {
683        cpp_template_term(node, self.source, &self.names, &self.ancestry)
684    }
685}
686
687fn term_names_any(term: &CppTemplateTerm, names: &HashSet<&str>) -> bool {
688    let mut work = vec![term];
689    while let Some(term) = work.pop() {
690        match term {
691            CppTemplateTerm::Parameter(name) if names.contains(name.as_str()) => return true,
692            CppTemplateTerm::Node { children, .. } => work.extend(children),
693            CppTemplateTerm::Parameter(_) | CppTemplateTerm::Atom { .. } => {}
694        }
695    }
696    false
697}
698
699fn is_class_specifier(node: Node<'_>) -> bool {
700    matches!(
701        node.kind(),
702        "class_specifier" | "struct_specifier" | "union_specifier"
703    )
704}
705
706/// The statement a declaration's template heads wrap: the declaration node
707/// itself, or for a function declarator the declaration that owns it.
708fn declaration_statement(node: Node<'_>) -> Option<Node<'_>> {
709    let mut current = node;
710    loop {
711        match current.kind() {
712            "function_definition" | "declaration" | "field_declaration" => return Some(current),
713            _ if is_class_specifier(current) => return Some(current),
714            "function_declarator"
715            | "pointer_declarator"
716            | "reference_declarator"
717            | "parenthesized_declarator"
718            | "attributed_declarator"
719            | "init_declarator" => current = current.parent()?,
720            _ => return None,
721        }
722    }
723}
724
725/// The statement whose template wrapper belongs to a class specifier: the
726/// specifier, or the declaration it is the type of.
727fn class_statement(class: Node<'_>) -> Node<'_> {
728    class
729        .parent()
730        .filter(|parent| {
731            matches!(parent.kind(), "declaration" | "field_declaration")
732                && parent.child_by_field_name("type") == Some(class)
733        })
734        .unwrap_or(class)
735}
736
737/// The `template <...>` heads that directly wrap `statement`, outermost
738/// first, and the outermost node of that wrapper chain.
739fn direct_template_heads(statement: Node<'_>) -> (Vec<Node<'_>>, Node<'_>) {
740    let statement = if is_class_specifier(statement) {
741        class_statement(statement)
742    } else {
743        statement
744    };
745    let mut heads = Vec::new();
746    let mut current = statement;
747    while let Some(parent) = current.parent() {
748        if parent.kind() != "template_declaration"
749            || parent.child_by_field_name("parameters") == Some(current)
750        {
751            break;
752        }
753        heads.push(parent);
754        current = parent;
755    }
756    heads.reverse();
757    (heads, current)
758}
759
760/// The qualified name an out-of-line callable declares, when it has one.
761fn declaration_qualifier(statement: Node<'_>) -> Option<Node<'_>> {
762    if is_class_specifier(statement) {
763        return None;
764    }
765    let mut current = statement.child_by_field_name("declarator")?;
766    loop {
767        match current.kind() {
768            "qualified_identifier" => return Some(current),
769            "identifier" | "field_identifier" | "operator_name" | "destructor_name"
770            | "template_function" => return None,
771            _ => {
772                current = current
773                    .child_by_field_name("declarator")
774                    .or_else(|| current.named_child(0))?;
775            }
776        }
777    }
778}
779
780/// The last name component a callable declarator declares, through any
781/// qualifier: `f`, or the `template_function` `f<int>`.
782fn declaration_terminal(statement: Node<'_>) -> Option<Node<'_>> {
783    if is_class_specifier(statement) {
784        return None;
785    }
786    let mut current = statement.child_by_field_name("declarator")?;
787    loop {
788        match current.kind() {
789            "qualified_identifier" => current = current.child_by_field_name("name")?,
790            "identifier" | "field_identifier" | "operator_name" | "destructor_name"
791            | "template_function" => return Some(current),
792            _ => {
793                current = current
794                    .child_by_field_name("declarator")
795                    .or_else(|| current.named_child(0))?;
796            }
797        }
798    }
799}
800
801#[cfg(test)]
802mod tests {
803    use super::*;
804
805    const DECLARATIONS: &str = r#"namespace n {
806template<class T> struct Box {
807    void put(T);
808    template<class U> void take(U);
809};
810template<class T> struct Box<T*> {
811    void put(T*);
812};
813template<> struct Box<int> {
814    void exact();
815};
816template<class T, class U> struct Pair {
817    void pair();
818};
819template<int N> struct Fixed {
820    void reset();
821};
822template<class... Ts> struct Pack {
823    void pack();
824};
825template<class T> requires Ready<T> struct Gate {
826    void open();
827};
828void make();
829}
830"#;
831
832    const DEFINITIONS: &str = r#"namespace n {
833template<typename U> void Box<U>::put(U) {}
834template<class T> template<class W> void Box<T>::take(W) {}
835template<class T> void Box<T*>::put(T*) {}
836void Box<int>::exact() {}
837template<class T> void Pair<T>::pair() {}
838template<long N> void Fixed<N>::reset() {}
839template<int M> void Fixed<M>::reset() {}
840template<class... Us> void Pack<Us...>::pack() {}
841template<class T> void Pack<T>::pack() {}
842template<class U> requires Ready<U> void Gate<U>::open() {}
843template<class U> void Gate<U>::open() {}
844template<> void make<int>() {}
845using Exact = Box<int>;
846void Exact::exact() {}
847}
848"#;
849
850    fn context_of(source: &str, needle: &str) -> CppTemplateContext {
851        let tree = crate::diagnostics::parse_cpp_tree(source);
852        assert!(!tree.root_node().has_error(), "fixture must parse cleanly");
853        let start = source.find(needle).expect("needle");
854        let mut node = tree
855            .root_node()
856            .descendant_for_byte_range(start, start + needle.len())
857            .expect("needle node");
858        while !matches!(
859            node.kind(),
860            "function_definition" | "field_declaration" | "declaration" | "template_declaration"
861        ) {
862            node = node.parent().expect("declaration ancestor");
863        }
864        while node.kind() == "template_declaration" {
865            node = node
866                .named_child(node.named_child_count() - 1)
867                .expect("templated declaration");
868        }
869        cpp_declaration_template_context(node, source)
870            .unwrap_or_else(|| panic!("no template context for {needle}"))
871    }
872
873    /// Each in-class declaration against the out-of-line definitions that only
874    /// look like it: renamed heads and members of a partial or explicit
875    /// specialization agree, while a different arity, parameter kind or
876    /// non-type parameter type, a pack against a single parameter, a missing
877    /// requires-clause, the primary against a specialization, and a function
878    /// template specialization against an ordinary function do not. A
879    /// qualifier spelled through an alias cannot be aligned at all.
880    #[test]
881    fn reconcile_matches_heads_and_owner_arguments_structurally() {
882        use CppTemplateReconciliation::{Distinct, Unsupported};
883        let cases = [
884            ("put(T);", "Box<U>::put(U)", Some(true)),
885            ("take(U)", "Box<T>::take(W)", Some(true)),
886            ("put(T*)", "Box<T*>::put(T*)", Some(true)),
887            ("put(T);", "Box<T*>::put(T*)", Some(false)),
888            ("exact();", "Box<int>::exact()", Some(true)),
889            ("put(T);", "Box<int>::exact()", Some(false)),
890            ("pair();", "Pair<T>::pair()", Some(false)),
891            ("reset();", "Fixed<N>::reset()", Some(false)),
892            ("reset();", "Fixed<M>::reset()", Some(true)),
893            ("pack();", "Pack<Us...>::pack()", Some(true)),
894            ("pack();", "Pack<T>::pack()", Some(false)),
895            ("open();", "Ready<U> void Gate<U>::open()", Some(true)),
896            ("open();", "<class U> void Gate<U>::open()", Some(false)),
897            ("make();", "make<int>()", Some(false)),
898            ("exact();", "Exact::exact()", None),
899        ];
900        let mut mismatches = Vec::new();
901        for (declaration, definition, expected) in cases {
902            let left = context_of(DECLARATIONS, declaration);
903            let right = context_of(DEFINITIONS, definition);
904            let forward = left.reconcile(&right);
905            let inverse = right.reconcile(&left);
906            let verdict = |reconciliation: &CppTemplateReconciliation| match reconciliation {
907                CppTemplateReconciliation::Equivalent(_) => Some(true),
908                Distinct => Some(false),
909                Unsupported => None,
910            };
911            if verdict(&forward) != expected || verdict(&inverse) != expected {
912                mismatches.push((declaration, definition, forward, inverse));
913            }
914        }
915        assert!(mismatches.is_empty(), "{mismatches:#?}");
916    }
917}