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