Skip to main content

zincite_query/
semantic.rs

1//! Read-only navigation over the existing public model facts.
2use std::collections::HashSet;
3use std::ops::Range;
4use std::rc::Rc;
5
6use serde_json::{Value, json};
7use zincite_lint::{
8    BindingFacts, BindingResolution, CallOutcome, CallableFacts, DeclarationId, FileId,
9    Instantiation, InstantiationFacts, ModelContext, ModelFacts, ReferenceKind, SourceDiagnostic,
10    SourceKind, SourceLocation, TypeInst, TypeKind,
11};
12use zincite_syntax::{FileMode, SyntaxNode};
13
14use crate::inspection::{check_collection, kind_name, type_error};
15use crate::language::{Predicate, PredicateKind, Stage, StageKind};
16use crate::{ErrorLocation, Input, Limits, Query, QueryError, QueryResult, Selection, Work};
17
18/// Identities belong to `SemanticResult::bindings`, independently of CST identity.
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum SemanticEntity {
21    Declaration(DeclarationId),
22    /// Index into the retained binding facts' written references.
23    Reference(usize),
24}
25
26/// A type or instantiation belongs to this exact declaration, occurrence or node.
27#[derive(Clone, Debug)]
28pub enum SemanticSubject<'a> {
29    Entity(SemanticEntity),
30    Node {
31        file: FileId,
32        node: &'a SyntaxNode,
33        location: SourceLocation,
34    },
35}
36
37#[derive(Clone, Debug)]
38pub struct SemanticType<'a> {
39    pub subject: SemanticSubject<'a>,
40    /// None means the producer has no fact for this exact subject.
41    pub ty: Option<TypeInst>,
42}
43
44#[derive(Clone, Debug)]
45pub struct SemanticInstantiation<'a> {
46    pub subject: SemanticSubject<'a>,
47    pub instantiation: Instantiation,
48    pub reason: Option<String>,
49}
50
51#[derive(Debug)]
52pub enum SemanticStream<'a> {
53    /// An ordinary projection, count or JSON emitter; limitations remain outside it.
54    Inspection(Box<QueryResult<'a>>),
55    Entities(Vec<SemanticEntity>),
56    Types(Vec<SemanticType<'a>>),
57    Instantiations(Vec<SemanticInstantiation<'a>>),
58}
59
60/// Native inspection with immutable source owners and only demanded fact families.
61/// The input and model must outlive this result. Locations include each file's BOM;
62/// declaration owning CST ranges remain in the fact API's BOM-stripped coordinates.
63#[derive(Debug)]
64pub struct SemanticResult<'a> {
65    model: &'a ModelContext,
66    pub(crate) facts: Rc<ModelFacts<'a>>,
67    bindings_requested: bool,
68    callables_requested: bool,
69    instantiations_requested: bool,
70    body_callables: Option<CallableFacts>,
71    reference_order: Option<Vec<usize>>,
72    pub(crate) stream: SemanticStream<'a>,
73    limitations: Vec<SourceDiagnostic>,
74    json: bool,
75    pub(crate) incomplete_observation: Option<std::sync::Arc<QueryError>>,
76}
77
78enum ReferenceEndpoint {
79    Declaration(DeclarationId),
80    Intrinsic,
81    Uncertain(SourceLocation, String),
82}
83
84impl<'a> SemanticResult<'a> {
85    pub fn model(&self) -> &'a ModelContext {
86        self.model
87    }
88    pub fn bindings(&self) -> Option<&BindingFacts> {
89        self.bindings_requested.then(|| self.facts.bindings())
90    }
91    pub fn callables(&self) -> Option<&CallableFacts> {
92        self.callables_requested.then(|| {
93            self.body_callables
94                .as_ref()
95                .unwrap_or_else(|| self.facts.source_callables())
96        })
97    }
98    pub fn instantiation_facts(&self) -> Option<&InstantiationFacts> {
99        self.instantiations_requested
100            .then(|| self.facts.instantiations())
101    }
102    pub fn stream(&self) -> &SemanticStream<'a> {
103        &self.stream
104    }
105    pub fn limitations(&self) -> &[SourceDiagnostic] {
106        &self.limitations
107    }
108    pub fn is_complete(&self) -> bool {
109        self.limitations.is_empty()
110    }
111
112    /// Locate a semantic identity using its actual retained file owner.
113    pub fn location(&self, entity: SemanticEntity) -> &SourceLocation {
114        match entity {
115            SemanticEntity::Declaration(id) => {
116                &self.bindings().unwrap().declarations[id.0].location
117            }
118            SemanticEntity::Reference(id) => &self.bindings().unwrap().references[id].location,
119        }
120    }
121    pub fn file(&self, entity: SemanticEntity) -> FileId {
122        match entity {
123            SemanticEntity::Declaration(id) => self.bindings().unwrap().declarations[id.0].file,
124            SemanticEntity::Reference(id) => self.bindings().unwrap().references[id].file,
125        }
126    }
127    /// Source of a declaration's owning node, or an exact written reference token.
128    pub fn source_bytes(&self, entity: SemanticEntity) -> &[u8] {
129        let file = &self.model.files[self.file(entity)];
130        let range = match entity {
131            SemanticEntity::Declaration(id) => self.bindings().unwrap().declarations[id.0]
132                .syntax_range
133                .clone(),
134            SemanticEntity::Reference(_) => {
135                let range = &self.location(entity).range;
136                range.start - file.byte_offset..range.end - file.byte_offset
137            }
138        };
139        &file.source_bytes()[range]
140    }
141    fn name(&self, entity: SemanticEntity) -> &str {
142        match entity {
143            SemanticEntity::Declaration(id) => &self.bindings().unwrap().declarations[id.0].name,
144            SemanticEntity::Reference(id) => &self.bindings().unwrap().references[id].name,
145        }
146    }
147    fn subject_location(&self, subject: &SemanticSubject<'_>) -> SourceLocation {
148        match subject {
149            SemanticSubject::Entity(entity) => self.location(*entity).clone(),
150            SemanticSubject::Node { location, .. } => location.clone(),
151        }
152    }
153    fn subject_file(&self, subject: &SemanticSubject<'_>) -> FileId {
154        match subject {
155            SemanticSubject::Entity(entity) => self.file(*entity),
156            SemanticSubject::Node { file, .. } => *file,
157        }
158    }
159    pub(crate) fn subject_bytes(&self, subject: &SemanticSubject<'_>) -> &[u8] {
160        match subject {
161            SemanticSubject::Entity(entity) => self.source_bytes(*entity),
162            SemanticSubject::Node { file, node, .. } => {
163                &self.model.files[*file].source_bytes()[node.range()]
164            }
165        }
166    }
167    fn limit(
168        &mut self,
169        location: SourceLocation,
170        message: impl Into<String>,
171        work: &mut Work,
172        stage: &Stage,
173        limits: Limits,
174    ) -> Result<(), QueryError> {
175        let message = message.into();
176        for existing in &self.limitations {
177            work.visit(&stage.range)?;
178            if existing.location == location && existing.message == message {
179                return Ok(());
180            }
181        }
182        check_collection(self.limitations.len(), limits.collection, &stage.range)?;
183        work.produce(1, &stage.range)?;
184        work.spend(message.len().max(1), &stage.range)?;
185        self.limitations
186            .push(SourceDiagnostic { location, message });
187        Ok(())
188    }
189
190    pub(crate) fn render(&self) -> Vec<u8> {
191        if self.json {
192            let value = json!({
193                "complete": self.is_complete(),
194                "limitations": self.limitations.iter().map(|d| json!({"file": d.location.path.to_string_lossy(), "range": range_json(&d.location.range), "line": d.location.line, "column": d.location.column, "message": d.message})).collect::<Vec<_>>(),
195                "result": self.json_value().expect("semantic JSON was validated"),
196            });
197            let mut bytes = serde_json::to_vec(&value).expect("inspection values serialize");
198            bytes.push(b'\n');
199            return bytes;
200        }
201        self.render_stream(&self.stream)
202    }
203
204    pub(crate) fn source_output_bytes(&self, stream: &SemanticStream<'a>) -> usize {
205        match stream {
206            SemanticStream::Entities(rows) => {
207                rows.iter().map(|&row| self.source_bytes(row).len()).sum()
208            }
209            SemanticStream::Types(rows) => rows
210                .iter()
211                .map(|row| {
212                    row.ty
213                        .as_ref()
214                        .map_or("unknown", |ty| type_kind(&ty.kind))
215                        .len()
216                        + 1
217                })
218                .sum(),
219            SemanticStream::Instantiations(rows) => rows
220                .iter()
221                .map(|row| instantiation_name(row.instantiation).len() + 1)
222                .sum(),
223            SemanticStream::Inspection(_) => {
224                unreachable!("inspection renderer accounts its own leaves")
225            }
226        }
227    }
228
229    pub(crate) fn render_stream(&self, stream: &SemanticStream<'a>) -> Vec<u8> {
230        match stream {
231            SemanticStream::Inspection(result) => result.render(),
232            SemanticStream::Entities(entities) => entities
233                .iter()
234                .flat_map(|e| self.source_bytes(*e))
235                .copied()
236                .collect(),
237            SemanticStream::Types(rows) => rows
238                .iter()
239                .flat_map(|row| {
240                    format!(
241                        "{}\n",
242                        row.ty.as_ref().map_or("unknown", |ty| type_kind(&ty.kind))
243                    )
244                    .into_bytes()
245                })
246                .collect(),
247            SemanticStream::Instantiations(rows) => rows
248                .iter()
249                .flat_map(|row| format!("{}\n", instantiation_name(row.instantiation)).into_bytes())
250                .collect(),
251        }
252    }
253
254    pub(crate) fn admit_program_json(
255        &self,
256        stream: &SemanticStream<'a>,
257        work: &mut Work,
258        range: &Range<usize>,
259    ) -> Result<(), QueryError> {
260        match stream {
261            SemanticStream::Inspection(value) => crate::inspection::admit_json(value, work, range)?,
262            SemanticStream::Entities(rows) => {
263                work.produce(1, range)?;
264                for &row in rows {
265                    self.admit_subject_program_json(&SemanticSubject::Entity(row), work, range)?;
266                }
267            }
268            SemanticStream::Types(rows) => {
269                work.produce(1, range)?;
270                for row in rows {
271                    let (units, bytes) = row.ty.as_ref().map_or((1, 0), type_allocation);
272                    work.produce(units * 16 + 2, range)?;
273                    work.spend(bytes + units * 64, range)?;
274                    self.admit_subject_program_json(&row.subject, work, range)?;
275                }
276            }
277            SemanticStream::Instantiations(rows) => {
278                work.produce(1, range)?;
279                for row in rows {
280                    self.admit_subject_program_json(&row.subject, work, range)?;
281                    work.produce(4, range)?;
282                    work.spend(row.reason.as_ref().map_or(0, String::len), range)?;
283                }
284            }
285        }
286        Ok(())
287    }
288
289    fn admit_subject_program_json(
290        &self,
291        subject: &SemanticSubject<'_>,
292        work: &mut Work,
293        range: &Range<usize>,
294    ) -> Result<(), QueryError> {
295        work.produce(36, range)?;
296        work.spend(
297            256 + self.model.files[self.subject_file(subject)]
298                .path
299                .as_os_str()
300                .len()
301                + self.subject_bytes(subject).len(),
302            range,
303        )?;
304        if let SemanticSubject::Entity(entity) = subject {
305            work.spend(self.name(*entity).len(), range)?;
306            if let SemanticEntity::Reference(id) = entity {
307                let reference = &self.bindings().unwrap().references[*id];
308                if let BindingResolution::Overloads(ids) | BindingResolution::Ambiguous(ids) =
309                    &reference.resolution
310                {
311                    work.produce(ids.len(), range)?;
312                }
313                if reference.kind == ReferenceKind::Callable
314                    && let Some(callables) = self.callables()
315                {
316                    for call in &callables.calls {
317                        work.visit(range)?;
318                        if call.file == reference.file
319                            && call.location.range.start == reference.location.range.start
320                        {
321                            match &call.outcome {
322                                CallOutcome::Ambiguous { candidates } => {
323                                    work.produce(candidates.len(), range)?
324                                }
325                                CallOutcome::Unsupported { candidates, reason } => {
326                                    work.produce(candidates.len(), range)?;
327                                    work.spend(reason.len(), range)?;
328                                }
329                                CallOutcome::NoMatch { reason }
330                                | CallOutcome::Unresolved { reason } => {
331                                    work.spend(reason.len(), range)?
332                                }
333                                CallOutcome::Intrinsic { name, .. } => {
334                                    work.spend(name.len(), range)?
335                                }
336                                _ => {}
337                            }
338                            break;
339                        }
340                    }
341                }
342            }
343        }
344        Ok(())
345    }
346    fn subject_json(&self, subject: &SemanticSubject<'_>) -> Result<Value, QueryError> {
347        let file_id = self.subject_file(subject);
348        let file = &self.model.files[file_id];
349        let location = self.subject_location(subject);
350        let text = std::str::from_utf8(self.subject_bytes(subject)).map_err(|_| {
351            model_error(
352                location.clone(),
353                "text and JSON inspection require UTF-8 source text",
354            )
355        })?;
356        let mut value = json!({"file": file.path.to_string_lossy(), "file_id": file_id, "range": range_json(&location.range), "source_kind": source_kind(file.kind), "text": text});
357        match subject {
358            SemanticSubject::Node { node, .. } => {
359                value["kind"] = json!(kind_name(node.kind()));
360            }
361            SemanticSubject::Entity(entity) => {
362                value["name"] = json!(self.name(*entity));
363                match entity {
364                    SemanticEntity::Declaration(id) => {
365                        let declaration = &self.bindings().unwrap().declarations[id.0];
366                        value["kind"] = json!("declaration");
367                        value["declaration_id"] = json!(id.0);
368                        value["role"] =
369                            json!(format!("{:?}", declaration.role).to_ascii_lowercase());
370                        value["syntax_range"] = range_json(
371                            &(declaration.syntax_range.start + file.byte_offset
372                                ..declaration.syntax_range.end + file.byte_offset),
373                        );
374                    }
375                    SemanticEntity::Reference(id) => {
376                        let reference = &self.bindings().unwrap().references[*id];
377                        value["kind"] = json!("reference");
378                        value["reference_id"] = json!(id);
379                        value["reference_kind"] = json!(reference_kind(reference.kind));
380                        value["binding"] = binding_json(&reference.resolution);
381                        if let Some(callables) = self.callables()
382                            && reference.kind == ReferenceKind::Callable
383                        {
384                            value["call"] = callables
385                                .calls
386                                .iter()
387                                .find(|call| {
388                                    call.file == reference.file
389                                        && call.location.range.start
390                                            == reference.location.range.start
391                                })
392                                .map_or(Value::Null, |call| call_json(&call.outcome));
393                        }
394                    }
395                }
396            }
397        }
398        Ok(value)
399    }
400    fn validate_json(&self, work: &mut Work, range: &Range<usize>) -> Result<(), QueryError> {
401        let validate = |subject: &SemanticSubject<'_>| {
402            std::str::from_utf8(self.subject_bytes(subject))
403                .map(|_| ())
404                .map_err(|_| {
405                    model_error(
406                        self.subject_location(subject),
407                        "text and JSON inspection require UTF-8 source text",
408                    )
409                })
410        };
411        match &self.stream {
412            SemanticStream::Inspection(result) => {
413                crate::inspection::validate_json(result, work, range)?
414            }
415            SemanticStream::Entities(rows) => {
416                for entity in rows {
417                    validate(&SemanticSubject::Entity(*entity))?;
418                }
419            }
420            SemanticStream::Types(rows) => {
421                for row in rows {
422                    validate(&row.subject)?;
423                }
424            }
425            SemanticStream::Instantiations(rows) => {
426                for row in rows {
427                    validate(&row.subject)?;
428                }
429            }
430        }
431        Ok(())
432    }
433
434    fn json_value(&self) -> Result<Value, QueryError> {
435        self.json_stream(&self.stream)
436    }
437
438    pub(crate) fn json_stream(&self, stream: &SemanticStream<'a>) -> Result<Value, QueryError> {
439        match stream {
440            SemanticStream::Inspection(result) => crate::inspection::json_value(result),
441            SemanticStream::Entities(entities) => entities
442                .iter()
443                .map(|e| self.subject_json(&SemanticSubject::Entity(*e)))
444                .collect::<Result<Vec<_>, _>>()
445                .map(Value::Array),
446            SemanticStream::Types(rows) => rows
447                .iter()
448                .map(|row| {
449                    let mut value = self.subject_json(&row.subject)?;
450                    value["type"] = row.ty.as_ref().map_or(Value::Null, type_json);
451                    Ok(value)
452                })
453                .collect::<Result<Vec<_>, _>>()
454                .map(Value::Array),
455            SemanticStream::Instantiations(rows) => rows
456                .iter()
457                .map(|row| {
458                    let mut value = self.subject_json(&row.subject)?;
459                    value["instantiation"] = json!(instantiation_name(row.instantiation));
460                    value["reason"] = json!(row.reason);
461                    Ok(value)
462                })
463                .collect::<Result<Vec<_>, _>>()
464                .map(Value::Array),
465        }
466    }
467}
468
469pub(crate) fn requests_facts(stage: &Stage) -> bool {
470    matches!(
471        stage.kind,
472        StageKind::References
473            | StageKind::Declarations
474            | StageKind::Uses
475            | StageKind::TransitiveReferences(_)
476            | StageKind::ReferencedDeclarations
477            | StageKind::Types
478            | StageKind::Instantiations
479    ) || matches!(&stage.kind, StageKind::Filter(predicate) if predicate_facts(predicate))
480        || matches!(&stage.kind, StageKind::SharedFilter(predicate) if predicate.requires_model_facts())
481}
482pub(crate) fn predicate_facts(predicate: &Predicate) -> bool {
483    matches!(
484        &predicate.kind,
485        PredicateKind::Type(_) | PredicateKind::Instantiation(_) | PredicateKind::SourceKind(_)
486    )
487}
488
489pub(crate) fn prepare<'a>(
490    query: &Query,
491    input: &'a Input,
492    facts: Rc<ModelFacts<'a>>,
493    work: &mut Work,
494    limits: Limits,
495) -> Result<SemanticResult<'a>, QueryError> {
496    let model = facts.model();
497    let requesting = query.stages.iter().find(|s| requests_facts(s)).unwrap();
498    if let Some(error) = model.errors.first() {
499        return Err(model_error(error.location.clone(), &error.message));
500    }
501    if input.mode != FileMode::Model {
502        return Err(QueryError::query(
503            requesting.range.clone(),
504            "semantic inspection requires model-file mode (.mzn)",
505        ));
506    }
507    let root = model
508        .root_file
509        .and_then(|id| model.files.get(id))
510        .ok_or_else(|| {
511            QueryError::query(
512                requesting.range.clone(),
513                "semantic inspection requires a retained root file",
514            )
515        })?;
516    if input.syntax_offset != root.byte_offset
517        || input.bytes[input.syntax_offset..] != *root.source_bytes()
518    {
519        return Err(QueryError::input(
520            0..input.bytes.len().min(3),
521            "semantic input must exactly match the retained model root's original bytes, including its BOM",
522        ));
523    }
524    let selection = Selection {
525        input,
526        items: crate::collect_items(input, limits.collection, work, &query.range, true)?,
527        document: true,
528    };
529    let mut report = SemanticResult {
530        model,
531        facts,
532        bindings_requested: false,
533        callables_requested: false,
534        instantiations_requested: false,
535        body_callables: None,
536        reference_order: None,
537        stream: SemanticStream::Inspection(Box::new(QueryResult::Selection(selection))),
538        limitations: Vec::new(),
539        json: false,
540        incomplete_observation: None,
541    };
542    for limitation in &model.limitations {
543        report.limit(
544            limitation.location.clone(),
545            &limitation.message,
546            work,
547            requesting,
548            limits,
549        )?;
550    }
551    Ok(report)
552}
553
554impl<'a> SemanticResult<'a> {
555    pub(crate) fn evaluate_stage(
556        &mut self,
557        input: &'a Input,
558        stream: SemanticStream<'a>,
559        stage: &Stage,
560        work: &mut Work,
561        limits: Limits,
562        callable_references: &mut Vec<usize>,
563    ) -> Result<SemanticStream<'a>, QueryError> {
564        let model = self.model;
565        Ok(match &stage.kind {
566            StageKind::References => {
567                self.ensure_bindings(work, stage)?;
568                let subjects = subjects(stream, model, stage, limits, work)?;
569                SemanticStream::Entities(self.references(
570                    &subjects,
571                    work,
572                    stage,
573                    limits,
574                    callable_references,
575                )?)
576            }
577            StageKind::Declarations => {
578                self.ensure_bindings(work, stage)?;
579                let subjects = subjects(stream, model, stage, limits, work)?;
580                SemanticStream::Entities(self.declarations(&subjects, work, stage, limits)?)
581            }
582            StageKind::Uses => {
583                self.ensure_bindings(work, stage)?;
584                let subjects = subjects(stream, model, stage, limits, work)?;
585                let declarations = self.declarations(&subjects, work, stage, limits)?;
586                SemanticStream::Entities(self.uses(&declarations, work, stage, limits)?)
587            }
588            StageKind::ReferencedDeclarations => {
589                self.ensure_bindings(work, stage)?;
590                let SemanticStream::Entities(declarations) = stream else {
591                    return Err(type_error(
592                        &stage.range,
593                        "referenced_declarations requires declarations",
594                    ));
595                };
596                SemanticStream::Entities(self.referenced_declarations(
597                    &declarations,
598                    work,
599                    stage,
600                    limits,
601                )?)
602            }
603            StageKind::TransitiveReferences(depth) => {
604                self.ensure_bindings(work, stage)?;
605                let subjects = subjects(stream, model, stage, limits, work)?;
606                let declarations = self.declarations(&subjects, work, stage, limits)?;
607                SemanticStream::Entities(self.transitive(
608                    &declarations,
609                    *depth,
610                    work,
611                    stage,
612                    limits,
613                )?)
614            }
615            StageKind::Types => {
616                let subjects = subjects(stream, model, stage, limits, work)?;
617                let subjects = self.typed_subjects(subjects, work, stage, limits)?;
618                let mut rows = Vec::new();
619                for subject in subjects {
620                    check_collection(rows.len(), limits.collection, &stage.range)?;
621                    work.produce(1, &stage.range)?;
622                    let ty = self.type_of(&subject, work, stage, limits)?;
623                    rows.push(SemanticType { subject, ty });
624                }
625                SemanticStream::Types(rows)
626            }
627            StageKind::Instantiations => {
628                let subjects = subjects(stream, model, stage, limits, work)?;
629                let subjects = self.typed_subjects(subjects, work, stage, limits)?;
630                let mut rows = Vec::new();
631                for subject in subjects {
632                    check_collection(rows.len(), limits.collection, &stage.range)?;
633                    work.produce(1, &stage.range)?;
634                    let (instantiation, reason) =
635                        self.instantiation_of(&subject, work, stage, limits)?;
636                    rows.push(SemanticInstantiation {
637                        subject,
638                        instantiation,
639                        reason,
640                    });
641                }
642                SemanticStream::Instantiations(rows)
643            }
644            StageKind::Filter(_)
645            | StageKind::Head(_)
646            | StageKind::Tail(_)
647            | StageKind::Reverse
648            | StageKind::Check { .. }
649            | StageKind::Count
650            | StageKind::Unique
651            | StageKind::Tally => unreachable!("the core evaluates common stages"),
652            StageKind::Names | StageKind::Text
653                if !matches!(stream, SemanticStream::Inspection(_)) =>
654            {
655                let subjects = subjects(stream, model, stage, limits, work)?;
656                let mut values = Vec::new();
657                for subject in subjects {
658                    let names = if matches!(stage.kind, StageKind::Text) {
659                        let location = self.subject_location(&subject);
660                        vec![
661                            std::str::from_utf8(self.subject_bytes(&subject)).map_err(|_| {
662                                model_error(location, "text inspection requires UTF-8 source text")
663                            })?,
664                        ]
665                    } else {
666                        match &subject {
667                            SemanticSubject::Entity(entity) => vec![self.name(*entity)],
668                            SemanticSubject::Node { file, node, .. } => {
669                                work.spend(node.children().len(), &stage.range)?;
670                                crate::inspection::direct_names(&model.files[*file].parsed, node)
671                            }
672                        }
673                    };
674                    for name in names {
675                        check_collection(values.len(), limits.collection, &stage.range)?;
676                        work.produce(1, &stage.range)?;
677                        work.spend(name.len().max(1), &stage.range)?;
678                        values.push(name.to_owned());
679                    }
680                }
681                SemanticStream::Inspection(Box::new(QueryResult::Strings(values)))
682            }
683            StageKind::Emit if !matches!(stream, SemanticStream::Inspection(_)) => stream,
684            StageKind::Json => {
685                self.json = true;
686                stream
687            }
688            _ => match stream {
689                SemanticStream::Inspection(result) => SemanticStream::Inspection(Box::new(
690                    crate::evaluate_stage(input, Some(model), *result, stage, work, limits)?,
691                )),
692                _ => {
693                    return Err(type_error(
694                        &stage.range,
695                        "stage does not accept this semantic stream",
696                    ));
697                }
698            },
699        })
700    }
701
702    pub(crate) fn finish(
703        &mut self,
704        callable_references: Vec<usize>,
705        work: &mut Work,
706        requesting: &Stage,
707        range: &Range<usize>,
708        limits: Limits,
709    ) -> Result<(), QueryError> {
710        self.finish_reference_limitations(callable_references, work, requesting, limits)?;
711        self.charge_output(work, range, limits)?;
712        if self.json {
713            self.validate_json(work, range)?;
714        }
715        Ok(())
716    }
717}
718
719fn subjects<'a>(
720    stream: SemanticStream<'a>,
721    model: &ModelContext,
722    stage: &Stage,
723    limits: Limits,
724    work: &mut Work,
725) -> Result<Vec<SemanticSubject<'a>>, QueryError> {
726    let mut subjects = Vec::new();
727    match stream {
728        SemanticStream::Entities(rows) => {
729            for entity in rows {
730                check_collection(subjects.len(), limits.collection, &stage.range)?;
731                work.produce(1, &stage.range)?;
732                work.visit(&stage.range)?;
733                subjects.push(SemanticSubject::Entity(entity));
734            }
735        }
736        SemanticStream::Types(rows) => {
737            for row in rows {
738                work.visit(&stage.range)?;
739                subjects.push(row.subject);
740            }
741        }
742        SemanticStream::Instantiations(rows) => {
743            for row in rows {
744                work.visit(&stage.range)?;
745                subjects.push(row.subject);
746            }
747        }
748        SemanticStream::Inspection(result) => {
749            let nodes = crate::inspection::into_nodes(*result, &stage.range, work)?;
750            for selected in nodes.nodes {
751                work.visit(&stage.range)?;
752                check_collection(subjects.len(), limits.collection, &stage.range)?;
753                work.produce(1, &stage.range)?;
754                subjects.push(SemanticSubject::Node {
755                    file: model.root_file.unwrap(),
756                    node: selected.node,
757                    location: model.files[model.root_file.unwrap()].location(selected.node.range()),
758                });
759            }
760        }
761    }
762    Ok(subjects)
763}
764
765/// Admit source tokens and CST nodes before any public fact producer runs.
766pub(crate) fn admit_model_facts(
767    model: &ModelContext,
768    work: &mut Work,
769    range: &Range<usize>,
770) -> Result<(), QueryError> {
771    if let Some(error) = model.errors.first() {
772        return Err(model_error(error.location.clone(), &error.message));
773    }
774    for source in &model.files {
775        work.spend(source.parsed.tokens().len(), range)?;
776        work.visit(range)?;
777        let mut pending = vec![source.parsed.tree()];
778        while let Some(node) = pending.pop() {
779            for child in node.child_nodes() {
780                work.visit(range)?;
781                pending.push(child);
782            }
783        }
784    }
785    Ok(())
786}
787
788impl SemanticResult<'_> {
789    // Admission is charged per request, including a warm shared owner.
790    pub(crate) fn admit_fact_owner(
791        &mut self,
792        work: &mut Work,
793        range: &Range<usize>,
794    ) -> Result<(), QueryError> {
795        if !self.bindings_requested {
796            admit_model_facts(self.model, work, range)?;
797            self.facts.bindings();
798            self.bindings_requested = true;
799        }
800        Ok(())
801    }
802    fn ensure_bindings(&mut self, work: &mut Work, stage: &Stage) -> Result<(), QueryError> {
803        self.admit_fact_owner(work, &stage.range)
804    }
805    fn ensure_callables(&mut self, work: &mut Work, stage: &Stage) -> Result<(), QueryError> {
806        self.ensure_bindings(work, stage)?;
807        self.facts.source_callables();
808        self.callables_requested = true;
809        Ok(())
810    }
811    fn ensure_occurrence_callables(
812        &mut self,
813        subject: &SemanticSubject<'_>,
814        work: &mut Work,
815        stage: &Stage,
816    ) -> Result<(), QueryError> {
817        self.ensure_callables(work, stage)?;
818        let file = self.subject_file(subject);
819        if self.model.files[file].kind != SourceKind::StandardLibrary
820            || self.declared_subject(subject, work, stage)?.is_some()
821        {
822            return Ok(());
823        }
824        let location = self.subject_location(subject);
825        let facts = self.callables().unwrap();
826        for fact in &facts.expressions {
827            work.visit(&stage.range)?;
828            if fact.file == file && fact.location.range == location.range {
829                return Ok(());
830            }
831        }
832        if let SemanticSubject::Entity(SemanticEntity::Reference(id)) = subject {
833            let reference = &self.bindings().unwrap().references[*id];
834            if reference.kind == ReferenceKind::Callable {
835                for call in &facts.calls {
836                    work.visit(&stage.range)?;
837                    if call.file == file && call.location.range.start == location.range.start {
838                        return Ok(());
839                    }
840                }
841            }
842        }
843        let offset = self.model.files[file].byte_offset;
844        let mut owner = None;
845        for declaration in &self.bindings().unwrap().declarations {
846            work.visit(&stage.range)?;
847            if declaration.file == file
848                && matches!(
849                    declaration.role,
850                    zincite_lint::DeclarationRole::Function
851                        | zincite_lint::DeclarationRole::Predicate
852                        | zincite_lint::DeclarationRole::Test
853                        | zincite_lint::DeclarationRole::Annotation
854                )
855                && declaration.syntax_range.start + offset <= location.range.start
856                && location.range.end <= declaration.syntax_range.end + offset
857            {
858                owner = Some(declaration.id);
859                break;
860            }
861        }
862        if let Some(owner) = owner {
863            self.body_callables = Some(self.facts.source_callable_body(facts, owner));
864        }
865        Ok(())
866    }
867    fn ensure_instantiations(&mut self, work: &mut Work, stage: &Stage) -> Result<(), QueryError> {
868        self.ensure_callables(work, stage)?;
869        self.facts.instantiations();
870        self.instantiations_requested = true;
871        Ok(())
872    }
873    fn owning_range(&self, subject: &SemanticSubject<'_>) -> Range<usize> {
874        match subject {
875            SemanticSubject::Entity(SemanticEntity::Declaration(id)) => {
876                let declaration = &self.bindings().unwrap().declarations[id.0];
877                let offset = self.model.files[declaration.file].byte_offset;
878                declaration.syntax_range.start + offset..declaration.syntax_range.end + offset
879            }
880            _ => self.subject_location(subject).range,
881        }
882    }
883    fn references(
884        &mut self,
885        subjects: &[SemanticSubject<'_>],
886        work: &mut Work,
887        stage: &Stage,
888        limits: Limits,
889        callable_references: &mut Vec<usize>,
890    ) -> Result<Vec<SemanticEntity>, QueryError> {
891        let mut rows = Vec::new();
892        for subject in subjects {
893            let file = self.subject_file(subject);
894            let region = self.owning_range(subject);
895            for id in 0..self.bindings().unwrap().references.len() {
896                work.visit(&stage.range)?;
897                let reference = &self.bindings().unwrap().references[id];
898                if reference.file == file && contains(&region, &reference.location.range) {
899                    check_collection(rows.len(), limits.collection, &stage.range)?;
900                    work.produce(1, &stage.range)?;
901                    rows.push(SemanticEntity::Reference(id));
902                    if reference.kind == ReferenceKind::Callable {
903                        // Lexical lookup cannot distinguish an unresolved call
904                        // from a language intrinsic. Use any callable facts the
905                        // pipeline requests before deciding report uncertainty.
906                        work.spend(callable_references.len(), &stage.range)?;
907                        if !callable_references.contains(&id) {
908                            check_collection(
909                                callable_references.len(),
910                                limits.collection,
911                                &stage.range,
912                            )?;
913                            callable_references.push(id);
914                        }
915                    } else if matches!(
916                        reference.resolution,
917                        BindingResolution::Unresolved | BindingResolution::Ambiguous(_)
918                    ) {
919                        self.limit(
920                            reference.location.clone(),
921                            format!(
922                                "reference '{}' has an unresolved or ambiguous binding",
923                                reference.name
924                            ),
925                            work,
926                            stage,
927                            limits,
928                        )?;
929                    }
930                }
931            }
932        }
933        Ok(rows)
934    }
935    fn finish_reference_limitations(
936        &mut self,
937        callable_references: Vec<usize>,
938        work: &mut Work,
939        stage: &Stage,
940        limits: Limits,
941    ) -> Result<(), QueryError> {
942        for id in callable_references {
943            work.visit(&stage.range)?;
944            if self.callables_requested {
945                // target records actual unknown outcomes and treats resolved
946                // declarations and intrinsics as known endpoints.
947                self.target(id, work, stage, limits)?;
948            } else {
949                let reference = &self.bindings().unwrap().references[id];
950                if matches!(
951                    reference.resolution,
952                    BindingResolution::Unresolved | BindingResolution::Ambiguous(_)
953                ) {
954                    self.limit(
955                        reference.location.clone(),
956                        format!(
957                            "reference '{}' has an unresolved or ambiguous binding",
958                            reference.name
959                        ),
960                        work,
961                        stage,
962                        limits,
963                    )?;
964                }
965            }
966        }
967        Ok(())
968    }
969
970    fn declarations(
971        &mut self,
972        subjects: &[SemanticSubject<'_>],
973        work: &mut Work,
974        stage: &Stage,
975        limits: Limits,
976    ) -> Result<Vec<SemanticEntity>, QueryError> {
977        let mut rows = Vec::new();
978        for subject in subjects {
979            match subject {
980                SemanticSubject::Entity(SemanticEntity::Declaration(id)) => {
981                    check_collection(rows.len(), limits.collection, &stage.range)?;
982                    work.produce(1, &stage.range)?;
983                    work.visit(&stage.range)?;
984                    rows.push(SemanticEntity::Declaration(*id));
985                }
986                SemanticSubject::Entity(SemanticEntity::Reference(id)) => {
987                    if let Some(target) = self.target(*id, work, stage, limits)? {
988                        check_collection(rows.len(), limits.collection, &stage.range)?;
989                        work.produce(1, &stage.range)?;
990                        rows.push(SemanticEntity::Declaration(target));
991                    }
992                }
993                SemanticSubject::Node { file, node, .. } => {
994                    for declaration in &self.bindings().unwrap().declarations {
995                        work.visit(&stage.range)?;
996                        if declaration.file == *file && declaration.syntax_range == node.range() {
997                            check_collection(rows.len(), limits.collection, &stage.range)?;
998                            work.produce(1, &stage.range)?;
999                            rows.push(SemanticEntity::Declaration(declaration.id));
1000                        }
1001                    }
1002                }
1003            }
1004        }
1005        Ok(rows)
1006    }
1007    fn endpoint(
1008        &mut self,
1009        id: usize,
1010        work: &mut Work,
1011        stage: &Stage,
1012        _limits: Limits,
1013    ) -> Result<ReferenceEndpoint, QueryError> {
1014        work.visit(&stage.range)?;
1015        if self.bindings().unwrap().references[id].kind == ReferenceKind::Callable {
1016            self.ensure_occurrence_callables(
1017                &SemanticSubject::Entity(SemanticEntity::Reference(id)),
1018                work,
1019                stage,
1020            )?;
1021        }
1022        let reference = &self.bindings().unwrap().references[id];
1023        let location = reference.location.clone();
1024        let mut reason = format!(
1025            "reference '{}' has no proven declaration target",
1026            reference.name
1027        );
1028        if reference.kind == ReferenceKind::Callable {
1029            for call in &self.callables().unwrap().calls {
1030                work.visit(&stage.range)?;
1031                if call.file != reference.file || call.location.range.start != location.range.start
1032                {
1033                    continue;
1034                }
1035                match &call.outcome {
1036                    CallOutcome::Resolved { declaration, .. } => {
1037                        return Ok(ReferenceEndpoint::Declaration(*declaration));
1038                    }
1039                    CallOutcome::Intrinsic { .. } => return Ok(ReferenceEndpoint::Intrinsic),
1040                    CallOutcome::NoMatch { reason: r }
1041                    | CallOutcome::Unresolved { reason: r }
1042                    | CallOutcome::Unsupported { reason: r, .. } => reason = r.clone(),
1043                    CallOutcome::Ambiguous { .. } => {
1044                        reason = format!("call '{}' is ambiguous", reference.name)
1045                    }
1046                }
1047                break;
1048            }
1049        } else if let BindingResolution::Resolved(declaration) = reference.resolution {
1050            return Ok(ReferenceEndpoint::Declaration(declaration));
1051        }
1052        Ok(ReferenceEndpoint::Uncertain(location, reason))
1053    }
1054    fn target(
1055        &mut self,
1056        id: usize,
1057        work: &mut Work,
1058        stage: &Stage,
1059        limits: Limits,
1060    ) -> Result<Option<DeclarationId>, QueryError> {
1061        match self.endpoint(id, work, stage, limits)? {
1062            ReferenceEndpoint::Declaration(id) => Ok(Some(id)),
1063            ReferenceEndpoint::Intrinsic => Ok(None),
1064            ReferenceEndpoint::Uncertain(location, reason) => {
1065                if self.incomplete_observation.is_none() {
1066                    work.spend(
1067                        location.path.as_os_str().len() + reason.len() + 1,
1068                        &stage.range,
1069                    )?;
1070                    work.produce(1, &stage.range)?;
1071                    self.incomplete_observation = Some(std::sync::Arc::new(QueryError {
1072                        range: location.range.clone(),
1073                        location: crate::ErrorLocation::Model(location.clone()),
1074                        message: reason.clone(),
1075                    }));
1076                }
1077                self.limit(location, reason, work, stage, limits)?;
1078                Ok(None)
1079            }
1080        }
1081    }
1082
1083    /// Complete adjacency in the legacy edge order, refusing any uncertain endpoint.
1084    /// The request-local index bounds each lookup to its owning file and region.
1085    pub(crate) fn complete_targets(
1086        &mut self,
1087        id: DeclarationId,
1088        work: &mut Work,
1089        range: &Range<usize>,
1090        limits: Limits,
1091    ) -> Result<Vec<SemanticEntity>, QueryError> {
1092        let stage = Stage {
1093            kind: StageKind::ReferencedDeclarations,
1094            range: range.clone(),
1095        };
1096        self.ensure_bindings(work, &stage)?;
1097        let declaration = self
1098            .bindings()
1099            .unwrap()
1100            .declarations
1101            .get(id.0)
1102            .ok_or_else(|| type_error(range, "unavailable declaration identity"))?;
1103        let file = declaration.file;
1104        let offset = self.model.files[file].byte_offset;
1105        let region = declaration.syntax_range.start + offset..declaration.syntax_range.end + offset;
1106        if self.reference_order.is_none() {
1107            work.produce(self.bindings().unwrap().references.len(), range)?;
1108            self.reference_order = Some(self.ordered_references(work, &stage, limits)?);
1109        }
1110        let references = &self.bindings().unwrap().references;
1111        let order = self.reference_order.as_ref().unwrap();
1112        work.spend(order.len().max(1).ilog2() as usize + 1, range)?;
1113        let first = order.partition_point(|&i| {
1114            (references[i].file, references[i].location.range.start) < (file, region.start)
1115        });
1116        let mut selected = HashSet::new();
1117        let mut rows = Vec::new();
1118        let mut index = first;
1119        while let Some(&reference) = self.reference_order.as_ref().unwrap().get(index) {
1120            index += 1;
1121            work.visit(range)?;
1122            let occurrence = &self.bindings().unwrap().references[reference];
1123            if occurrence.file != file || occurrence.location.range.start >= region.end {
1124                break;
1125            }
1126            if !contains(&region, &occurrence.location.range) {
1127                continue;
1128            }
1129            match self.endpoint(reference, work, &stage, limits)? {
1130                ReferenceEndpoint::Declaration(target) => {
1131                    if selected.contains(&target.0) {
1132                        continue;
1133                    }
1134                    check_collection(rows.len(), limits.collection, range)?;
1135                    work.produce(2, range)?;
1136                    selected.insert(target.0);
1137                    rows.push(SemanticEntity::Declaration(target));
1138                }
1139                ReferenceEndpoint::Intrinsic => {}
1140                ReferenceEndpoint::Uncertain(location, reason) => {
1141                    return Err(model_error(
1142                        location,
1143                        format!("reference_info cannot provide complete adjacency: {reason}"),
1144                    ));
1145                }
1146            }
1147        }
1148        Ok(rows)
1149    }
1150    fn uses(
1151        &mut self,
1152        declarations: &[SemanticEntity],
1153        work: &mut Work,
1154        stage: &Stage,
1155        limits: Limits,
1156    ) -> Result<Vec<SemanticEntity>, QueryError> {
1157        let mut rows = Vec::new();
1158        let mut selected = HashSet::new();
1159        let mut names = HashSet::new();
1160        for declaration in declarations {
1161            let SemanticEntity::Declaration(id) = declaration else {
1162                unreachable!()
1163            };
1164            work.visit(&stage.range)?;
1165            check_collection(selected.len(), limits.collection, &stage.range)?;
1166            work.produce(1, &stage.range)?;
1167            selected.insert(id.0);
1168            let name = &self.bindings().unwrap().declarations[id.0].name;
1169            work.spend(name.len(), &stage.range)?;
1170            check_collection(names.len(), limits.collection, &stage.range)?;
1171            work.produce(1, &stage.range)?;
1172            names.insert(name.clone());
1173        }
1174        // Uncertain occurrences matter even when the requested declaration set is empty.
1175        let mut targets = Vec::new();
1176        for id in 0..self.bindings().unwrap().references.len() {
1177            work.visit(&stage.range)?;
1178            let reference = &self.bindings().unwrap().references[id];
1179            if !selected.is_empty() {
1180                // Binding names are already normalized. Candidate identity also
1181                // rules out shadowed declarations with the same written name.
1182                let relevant = match &reference.resolution {
1183                    BindingResolution::Resolved(id) => selected.contains(&id.0),
1184                    BindingResolution::Overloads(ids) | BindingResolution::Ambiguous(ids) => {
1185                        work.spend(ids.len(), &stage.range)?;
1186                        ids.iter().any(|id| selected.contains(&id.0))
1187                    }
1188                    BindingResolution::Unresolved => {
1189                        work.spend(reference.name.len(), &stage.range)?;
1190                        names.contains(&reference.name)
1191                    }
1192                };
1193                if !relevant {
1194                    continue;
1195                }
1196            }
1197            check_collection(targets.len(), limits.collection, &stage.range)?;
1198            work.produce(1, &stage.range)?;
1199            targets.push((id, self.target(id, work, stage, limits)?));
1200        }
1201        for declaration in declarations {
1202            let SemanticEntity::Declaration(declaration) = declaration else {
1203                unreachable!()
1204            };
1205            for (id, target) in &targets {
1206                work.visit(&stage.range)?;
1207                if *target == Some(*declaration) {
1208                    check_collection(rows.len(), limits.collection, &stage.range)?;
1209                    work.produce(1, &stage.range)?;
1210                    rows.push(SemanticEntity::Reference(*id));
1211                }
1212            }
1213        }
1214        Ok(rows)
1215    }
1216    fn ordered_references(
1217        &self,
1218        work: &mut Work,
1219        stage: &Stage,
1220        limits: Limits,
1221    ) -> Result<Vec<usize>, QueryError> {
1222        let references = &self.bindings().unwrap().references;
1223        work.spend(
1224            references
1225                .len()
1226                .saturating_mul(references.len().max(1).ilog2() as usize + 1),
1227            &stage.range,
1228        )?;
1229        if references.len() > limits.collection {
1230            return Err(type_error(&stage.range, "query collection limit exceeded"));
1231        }
1232        let mut order: Vec<_> = (0..references.len()).collect();
1233        order.sort_by_key(|&id| {
1234            let r = &references[id];
1235            (
1236                r.file,
1237                r.location.range.start,
1238                r.location.range.end,
1239                reference_order(r.kind),
1240            )
1241        });
1242        Ok(order)
1243    }
1244
1245    fn referenced_declarations(
1246        &mut self,
1247        declarations: &[SemanticEntity],
1248        work: &mut Work,
1249        stage: &Stage,
1250        limits: Limits,
1251    ) -> Result<Vec<SemanticEntity>, QueryError> {
1252        let order = self.ordered_references(work, stage, limits)?;
1253        let mut rows = Vec::new();
1254        for declaration in declarations {
1255            let SemanticEntity::Declaration(id) = declaration else {
1256                return Err(type_error(
1257                    &stage.range,
1258                    "referenced_declarations requires declarations",
1259                ));
1260            };
1261            work.visit(&stage.range)?;
1262            let declaration = &self.bindings().unwrap().declarations[id.0];
1263            let file = declaration.file;
1264            let offset = self.model.files[file].byte_offset;
1265            let region =
1266                declaration.syntax_range.start + offset..declaration.syntax_range.end + offset;
1267            let mut selected = Vec::new();
1268            for &reference in &order {
1269                work.visit(&stage.range)?;
1270                let occurrence = &self.bindings().unwrap().references[reference];
1271                if occurrence.file != file || !contains(&region, &occurrence.location.range) {
1272                    continue;
1273                }
1274                if let Some(target) = self.target(reference, work, stage, limits)? {
1275                    let mut repeated = false;
1276                    for previous in &selected {
1277                        work.visit(&stage.range)?;
1278                        if *previous == target {
1279                            repeated = true;
1280                            break;
1281                        }
1282                    }
1283                    if repeated {
1284                        continue;
1285                    }
1286                    check_collection(selected.len(), limits.collection, &stage.range)?;
1287                    check_collection(rows.len(), limits.collection, &stage.range)?;
1288                    work.produce(2, &stage.range)?;
1289                    selected.push(target);
1290                    rows.push(SemanticEntity::Declaration(target));
1291                }
1292            }
1293        }
1294        Ok(rows)
1295    }
1296
1297    fn transitive(
1298        &mut self,
1299        declarations: &[SemanticEntity],
1300        depth: usize,
1301        work: &mut Work,
1302        stage: &Stage,
1303        limits: Limits,
1304    ) -> Result<Vec<SemanticEntity>, QueryError> {
1305        if depth == 0 {
1306            return Ok(Vec::new());
1307        }
1308        let mut frontier = Vec::new();
1309        let mut visited = HashSet::new();
1310        for entity in declarations {
1311            let SemanticEntity::Declaration(id) = entity else {
1312                unreachable!()
1313            };
1314            work.visit(&stage.range)?;
1315            if !visited.contains(&id.0) {
1316                check_collection(visited.len(), limits.collection, &stage.range)?;
1317                visited.insert(id.0);
1318                frontier.push(*id);
1319            }
1320        }
1321        let order = self.ordered_references(work, stage, limits)?;
1322        let mut emitted = HashSet::new();
1323        let mut rows = Vec::new();
1324        for _ in 0..depth {
1325            if frontier.is_empty() {
1326                break;
1327            }
1328            let mut next = Vec::new();
1329            for declaration in frontier {
1330                work.visit(&stage.range)?;
1331                let declaration = &self.bindings().unwrap().declarations[declaration.0];
1332                let file = declaration.file;
1333                let offset = self.model.files[file].byte_offset;
1334                let region =
1335                    declaration.syntax_range.start + offset..declaration.syntax_range.end + offset;
1336                for &id in &order {
1337                    work.visit(&stage.range)?;
1338                    let reference = &self.bindings().unwrap().references[id];
1339                    if reference.file != file || !contains(&region, &reference.location.range) {
1340                        continue;
1341                    }
1342                    let identity = (
1343                        file,
1344                        reference.location.range.start,
1345                        reference.location.range.end,
1346                        reference_order(reference.kind),
1347                    );
1348                    if !emitted.contains(&identity) {
1349                        check_collection(rows.len(), limits.collection, &stage.range)?;
1350                        work.produce(1, &stage.range)?;
1351                        emitted.insert(identity);
1352                        rows.push(SemanticEntity::Reference(id));
1353                    }
1354                    if let Some(target) = self.target(id, work, stage, limits)?
1355                        && !visited.contains(&target.0)
1356                    {
1357                        check_collection(visited.len(), limits.collection, &stage.range)?;
1358                        visited.insert(target.0);
1359                        next.push(target);
1360                    }
1361                }
1362            }
1363            frontier = next;
1364        }
1365        Ok(rows)
1366    }
1367
1368    fn typed_subjects<'a>(
1369        &mut self,
1370        subjects: Vec<SemanticSubject<'a>>,
1371        work: &mut Work,
1372        stage: &Stage,
1373        limits: Limits,
1374    ) -> Result<Vec<SemanticSubject<'a>>, QueryError> {
1375        self.ensure_bindings(work, stage)?;
1376        let mut rows = Vec::new();
1377        for subject in subjects {
1378            let before = rows.len();
1379            if let SemanticSubject::Node { file, node, .. } = &subject {
1380                for declaration in &self.bindings().unwrap().declarations {
1381                    work.visit(&stage.range)?;
1382                    if declaration.file == *file && declaration.syntax_range == node.range() {
1383                        check_collection(rows.len(), limits.collection, &stage.range)?;
1384                        work.produce(1, &stage.range)?;
1385                        rows.push(SemanticSubject::Entity(SemanticEntity::Declaration(
1386                            declaration.id,
1387                        )));
1388                    }
1389                }
1390            }
1391            if rows.len() == before {
1392                check_collection(rows.len(), limits.collection, &stage.range)?;
1393                work.produce(1, &stage.range)?;
1394                rows.push(subject);
1395            }
1396        }
1397        Ok(rows)
1398    }
1399
1400    fn type_of(
1401        &mut self,
1402        subject: &SemanticSubject<'_>,
1403        work: &mut Work,
1404        stage: &Stage,
1405        limits: Limits,
1406    ) -> Result<Option<TypeInst>, QueryError> {
1407        self.ensure_occurrence_callables(subject, work, stage)?;
1408        let declaration = self.declared_subject(subject, work, stage)?;
1409        let facts = self.callables().unwrap();
1410        let mut ty = None;
1411        if let Some(id) = declaration {
1412            for fact in &facts.declarations {
1413                work.visit(&stage.range)?;
1414                if fact.declaration == id {
1415                    ty = Some(&fact.ty);
1416                    break;
1417                }
1418            }
1419        } else {
1420            let file = self.subject_file(subject);
1421            let location = self.subject_location(subject);
1422            // A different expression never inherits its target declaration's type.
1423            for fact in &facts.expressions {
1424                work.visit(&stage.range)?;
1425                if fact.file == file && fact.location.range == location.range {
1426                    ty = Some(&fact.ty);
1427                    break;
1428                }
1429            }
1430            if ty.is_none()
1431                && let SemanticSubject::Entity(SemanticEntity::Reference(id)) = subject
1432                && self.bindings().unwrap().references[*id].kind == ReferenceKind::Callable
1433            {
1434                for fact in &facts.calls {
1435                    work.visit(&stage.range)?;
1436                    if fact.file == file && fact.location.range.start == location.range.start {
1437                        ty = match &fact.outcome {
1438                            CallOutcome::Resolved { return_type, .. }
1439                            | CallOutcome::Intrinsic { return_type, .. } => Some(return_type),
1440                            _ => None,
1441                        };
1442                        break;
1443                    }
1444                }
1445            }
1446        }
1447        let reason = if let Some(ty) = ty {
1448            type_unknown(ty, work, &stage.range, limits)?
1449        } else {
1450            Some("type fact unavailable for this exact occurrence".to_owned())
1451        };
1452        if work.budget.remaining_results().is_some()
1453            && let Some(ty) = ty
1454        {
1455            let (units, bytes) = type_allocation(ty);
1456            work.produce(units, &stage.range)?;
1457            work.spend(bytes, &stage.range)?;
1458        }
1459        let ty = ty.cloned();
1460        if let Some(reason) = reason {
1461            self.limit(self.subject_location(subject), reason, work, stage, limits)?;
1462        }
1463        Ok(ty)
1464    }
1465
1466    fn declared_subject(
1467        &self,
1468        subject: &SemanticSubject<'_>,
1469        work: &mut Work,
1470        stage: &Stage,
1471    ) -> Result<Option<DeclarationId>, QueryError> {
1472        match subject {
1473            SemanticSubject::Entity(SemanticEntity::Declaration(id)) => Ok(Some(*id)),
1474            SemanticSubject::Node { file, node, .. } => {
1475                // This is the declaration's own node, never an expression with
1476                // a coincident name. Shared nodes are expanded by typed stages.
1477                for declaration in &self.bindings().unwrap().declarations {
1478                    work.visit(&stage.range)?;
1479                    if declaration.file == *file && declaration.syntax_range == node.range() {
1480                        return Ok(Some(declaration.id));
1481                    }
1482                }
1483                Ok(None)
1484            }
1485            _ => Ok(None),
1486        }
1487    }
1488
1489    fn instantiation_of(
1490        &mut self,
1491        subject: &SemanticSubject<'_>,
1492        work: &mut Work,
1493        stage: &Stage,
1494        limits: Limits,
1495    ) -> Result<(Instantiation, Option<String>), QueryError> {
1496        let mut outcome = (
1497            Instantiation::Unknown,
1498            Some("instantiation fact unavailable for this exact occurrence".to_owned()),
1499        );
1500        self.ensure_bindings(work, stage)?;
1501        if let Some(id) = self.declared_subject(subject, work, stage)? {
1502            work.visit(&stage.range)?;
1503            let instantiation = self.bindings().unwrap().declarations[id.0].instantiation;
1504            outcome = (
1505                instantiation,
1506                (instantiation == Instantiation::Unknown)
1507                    .then(|| "declared instantiation is unknown".to_owned()),
1508            );
1509        } else {
1510            self.ensure_occurrence_callables(subject, work, stage)?;
1511            self.ensure_instantiations(work, stage)?;
1512            let location = self.subject_location(subject);
1513            let file = self.subject_file(subject);
1514            for fact in &self.instantiation_facts().unwrap().expressions {
1515                work.visit(&stage.range)?;
1516                if fact.file == file && fact.location.range == location.range {
1517                    outcome = (fact.instantiation, fact.reason.clone());
1518                    break;
1519                }
1520            }
1521            if outcome.0 == Instantiation::Unknown
1522                && self.model.files[file].kind == SourceKind::StandardLibrary
1523            {
1524                // The ordinary instantiation producer walks user sources. An
1525                // explicitly navigated library occurrence uses its source type.
1526                for fact in &self.callables().unwrap().expressions {
1527                    work.visit(&stage.range)?;
1528                    if fact.file == file
1529                        && fact.location.range == location.range
1530                        && fact.ty.instantiation != Instantiation::Unknown
1531                    {
1532                        outcome = (fact.ty.instantiation, None);
1533                        break;
1534                    }
1535                }
1536            }
1537            if outcome.0 == Instantiation::Unknown
1538                && let SemanticSubject::Entity(SemanticEntity::Reference(id)) = subject
1539                && self.bindings().unwrap().references[*id].kind == ReferenceKind::Callable
1540            {
1541                for fact in &self.callables().unwrap().calls {
1542                    work.visit(&stage.range)?;
1543                    if fact.file == file && fact.location.range.start == location.range.start {
1544                        match &fact.outcome {
1545                            CallOutcome::Resolved { return_type, .. }
1546                            | CallOutcome::Intrinsic { return_type, .. } => {
1547                                outcome = (
1548                                    return_type.instantiation,
1549                                    (return_type.instantiation == Instantiation::Unknown)
1550                                        .then(|| "call return instantiation is unknown".to_owned()),
1551                                );
1552                            }
1553                            _ => {}
1554                        }
1555                        break;
1556                    }
1557                }
1558            }
1559        }
1560        if let Some(reason) = &outcome.1 {
1561            self.limit(self.subject_location(subject), reason, work, stage, limits)?;
1562        }
1563        Ok(outcome)
1564    }
1565
1566    pub(crate) fn guard_leaf(
1567        &mut self,
1568        predicate: &Predicate,
1569        subject: &SemanticSubject<'_>,
1570        work: &mut Work,
1571        stage: &Stage,
1572        limits: Limits,
1573    ) -> Result<(Truth, Option<SourceDiagnostic>), QueryError> {
1574        let truth = self.predicate_leaf(predicate, subject, work, stage, limits)?;
1575        let diagnostic = if truth == Truth::Unknown {
1576            let location = match subject {
1577                SemanticSubject::Entity(entity) => self.location(*entity),
1578                SemanticSubject::Node { location, .. } => location,
1579            };
1580            let mut existing = None;
1581            for diagnostic in self.limitations.iter().rev() {
1582                work.visit(&predicate.range)?;
1583                if diagnostic.location == *location {
1584                    existing = Some(diagnostic);
1585                    break;
1586                }
1587            }
1588            let message = existing.map_or("semantic guard fact is unknown", |d| d.message.as_str());
1589            work.spend(
1590                location.path.as_os_str().len() + message.len() + 1,
1591                &predicate.range,
1592            )?;
1593            work.produce(1, &predicate.range)?;
1594            Some(SourceDiagnostic {
1595                location: location.clone(),
1596                message: message.to_owned(),
1597            })
1598        } else {
1599            None
1600        };
1601        Ok((truth, diagnostic))
1602    }
1603    pub(crate) fn predicate_leaf(
1604        &mut self,
1605        predicate: &Predicate,
1606        subject: &SemanticSubject<'_>,
1607        work: &mut Work,
1608        stage: &Stage,
1609        limits: Limits,
1610    ) -> Result<Truth, QueryError> {
1611        Ok(match &predicate.kind {
1612            PredicateKind::Type(expected) => match self.type_of(subject, work, stage, limits)? {
1613                Some(ty) if type_unknown(&ty, work, &predicate.range, limits)?.is_none() => {
1614                    Truth::from(type_kind(&ty.kind) == expected)
1615                }
1616                _ => Truth::Unknown,
1617            },
1618            PredicateKind::Instantiation(expected) => {
1619                let (instantiation, _) = self.instantiation_of(subject, work, stage, limits)?;
1620                if instantiation == Instantiation::Unknown {
1621                    Truth::Unknown
1622                } else {
1623                    Truth::from(instantiation_name(instantiation) == expected)
1624                }
1625            }
1626            PredicateKind::SourceKind(expected) => Truth::from(
1627                source_kind(self.model.files[self.subject_file(subject)].kind) == expected,
1628            ),
1629            PredicateKind::Name(expected) => match subject {
1630                SemanticSubject::Entity(entity) => {
1631                    Truth::from(self.name(*entity) == crate::identifier_identity(expected))
1632                }
1633                SemanticSubject::Node { file, node, .. } => {
1634                    work.spend(node.children().len(), &predicate.range)?;
1635                    Truth::from(
1636                        crate::inspection::direct_names(&self.model.files[*file].parsed, node)
1637                            .first()
1638                            .copied()
1639                            == Some(crate::identifier_identity(expected)),
1640                    )
1641                }
1642            },
1643            PredicateKind::Kind(expected) => match subject {
1644                SemanticSubject::Entity(SemanticEntity::Reference(_)) => {
1645                    Truth::from(expected == "reference")
1646                }
1647                SemanticSubject::Entity(SemanticEntity::Declaration(_)) => {
1648                    Truth::from(expected == "declaration")
1649                }
1650                SemanticSubject::Node { node, .. } => Truth::from(
1651                    kind_name(node.kind()) == *expected
1652                        || crate::ItemKind::parse(expected)
1653                            .is_some_and(|kind| kind.as_str() == item_kind_name(node.kind())),
1654                ),
1655            },
1656        })
1657    }
1658    fn charge_subject_json(
1659        &self,
1660        subject: &SemanticSubject<'_>,
1661        work: &mut Work,
1662        range: &Range<usize>,
1663    ) -> Result<(), QueryError> {
1664        if !self.json {
1665            return Ok(());
1666        }
1667        let file = &self.model.files[self.subject_file(subject)];
1668        work.spend(file.path.as_os_str().len(), range)?;
1669        if let SemanticSubject::Entity(entity) = subject {
1670            work.spend(self.name(*entity).len(), range)?;
1671            if let SemanticEntity::Reference(id) = entity {
1672                let reference = &self.bindings().unwrap().references[*id];
1673                if let BindingResolution::Overloads(ids) | BindingResolution::Ambiguous(ids) =
1674                    &reference.resolution
1675                {
1676                    work.spend(ids.len(), range)?;
1677                }
1678                if reference.kind == ReferenceKind::Callable
1679                    && let Some(callables) = self.callables()
1680                {
1681                    for call in &callables.calls {
1682                        work.visit(range)?;
1683                        if call.file == reference.file
1684                            && call.location.range.start == reference.location.range.start
1685                        {
1686                            match &call.outcome {
1687                                CallOutcome::Ambiguous { candidates }
1688                                | CallOutcome::Unsupported { candidates, .. } => {
1689                                    work.spend(candidates.len(), range)?
1690                                }
1691                                _ => {}
1692                            }
1693                            break;
1694                        }
1695                    }
1696                }
1697            }
1698        }
1699        Ok(())
1700    }
1701
1702    fn charge_output(
1703        &self,
1704        work: &mut Work,
1705        range: &Range<usize>,
1706        limits: Limits,
1707    ) -> Result<(), QueryError> {
1708        if self.json {
1709            for limitation in &self.limitations {
1710                work.visit(range)?;
1711                work.spend(
1712                    limitation.location.path.as_os_str().len() + limitation.message.len(),
1713                    range,
1714                )?;
1715            }
1716        }
1717        match &self.stream {
1718            SemanticStream::Inspection(result) => match result.as_ref() {
1719                QueryResult::Selection(selection) => {
1720                    for item in &selection.items {
1721                        work.spend(item.range.len().max(1), range)?;
1722                        if self.json {
1723                            work.spend(selection.input.file().len(), range)?;
1724                        }
1725                    }
1726                }
1727                QueryResult::Nodes(selection) => {
1728                    for node in &selection.nodes {
1729                        work.spend(node.range.len().max(1), range)?;
1730                        if self.json {
1731                            work.spend(selection.input.file().len(), range)?;
1732                        }
1733                    }
1734                }
1735                QueryResult::Strings(values) => {
1736                    for value in values {
1737                        work.spend(value.len().max(1), range)?;
1738                    }
1739                }
1740                QueryResult::Tally(values) => {
1741                    for (value, _) in values {
1742                        work.spend(value.len().max(1), range)?;
1743                    }
1744                }
1745                QueryResult::Count(_) => {}
1746                QueryResult::Literals(selection) => {
1747                    for value in selection.values() {
1748                        work.spend(value.range.len(), range)?;
1749                    }
1750                }
1751                _ => {
1752                    return Err(type_error(
1753                        range,
1754                        "semantic inspection cannot produce an edit candidate",
1755                    ));
1756                }
1757            },
1758            SemanticStream::Entities(rows) => {
1759                for entity in rows {
1760                    work.spend(self.source_bytes(*entity).len().max(1), range)?;
1761                    self.charge_subject_json(&SemanticSubject::Entity(*entity), work, range)?;
1762                }
1763            }
1764            SemanticStream::Types(rows) => {
1765                for row in rows {
1766                    work.spend(self.subject_bytes(&row.subject).len().max(1), range)?;
1767                    self.charge_subject_json(&row.subject, work, range)?;
1768                    if let Some(ty) = &row.ty {
1769                        type_unknown(ty, work, range, limits)?;
1770                    }
1771                }
1772            }
1773            SemanticStream::Instantiations(rows) => {
1774                for row in rows {
1775                    work.spend(self.subject_bytes(&row.subject).len().max(1), range)?;
1776                    self.charge_subject_json(&row.subject, work, range)?;
1777                }
1778            }
1779        }
1780        Ok(())
1781    }
1782}
1783
1784#[derive(Clone, Copy, PartialEq, Eq)]
1785pub(crate) enum Truth {
1786    True,
1787    False,
1788    Unknown,
1789}
1790impl Truth {
1791    fn from(value: bool) -> Self {
1792        if value { Self::True } else { Self::False }
1793    }
1794}
1795fn item_kind_name(kind: zincite_syntax::NodeKind) -> &'static str {
1796    use zincite_syntax::NodeKind::*;
1797    match kind {
1798        Assignment => "assignment",
1799        Declaration => "declaration",
1800        EnumDeclaration => "enum",
1801        TypeAlias => "type_alias",
1802        FunctionDeclaration => "function",
1803        PredicateDeclaration => "predicate",
1804        TestDeclaration => "test",
1805        AnnotationDeclaration => "annotation",
1806        Constraint => "constraint",
1807        Include => "include",
1808        Output => "output",
1809        Solve | SolveMinimize | SolveMaximize => "solve",
1810        _ => "",
1811    }
1812}
1813fn model_error(location: SourceLocation, message: impl Into<String>) -> QueryError {
1814    QueryError {
1815        range: location.range.clone(),
1816        location: ErrorLocation::Model(location),
1817        message: message.into(),
1818    }
1819}
1820fn contains(region: &Range<usize>, range: &Range<usize>) -> bool {
1821    region.start <= range.start && range.end <= region.end
1822}
1823fn reference_order(kind: ReferenceKind) -> u8 {
1824    match kind {
1825        ReferenceKind::Value => 0,
1826        ReferenceKind::Callable => 1,
1827        ReferenceKind::Type => 2,
1828    }
1829}
1830fn reference_kind(kind: ReferenceKind) -> &'static str {
1831    match kind {
1832        ReferenceKind::Value => "value",
1833        ReferenceKind::Callable => "callable",
1834        ReferenceKind::Type => "type",
1835    }
1836}
1837fn source_kind(kind: SourceKind) -> &'static str {
1838    match kind {
1839        SourceKind::User => "user",
1840        SourceKind::StandardLibrary => "standard_library",
1841    }
1842}
1843fn instantiation_name(instantiation: Instantiation) -> &'static str {
1844    match instantiation {
1845        Instantiation::Parameter => "par",
1846        Instantiation::Decision => "var",
1847        Instantiation::Unknown => "unknown",
1848    }
1849}
1850pub(crate) fn known_type_kind(name: &str) -> bool {
1851    matches!(
1852        name,
1853        "bool"
1854            | "int"
1855            | "float"
1856            | "string"
1857            | "annotation"
1858            | "enum"
1859            | "set"
1860            | "array"
1861            | "tuple"
1862            | "record"
1863            | "bottom"
1864    )
1865}
1866fn type_kind(kind: &TypeKind) -> &'static str {
1867    match kind {
1868        TypeKind::Bool => "bool",
1869        TypeKind::Int => "int",
1870        TypeKind::Float => "float",
1871        TypeKind::String => "string",
1872        TypeKind::Annotation => "annotation",
1873        TypeKind::Enum(_) => "enum",
1874        TypeKind::Set(_) => "set",
1875        TypeKind::Array { .. } => "array",
1876        TypeKind::Tuple(_) => "tuple",
1877        TypeKind::Record(_) => "record",
1878        TypeKind::Bottom => "bottom",
1879        TypeKind::Variable { .. } => "variable",
1880        TypeKind::Unknown(_) => "unknown",
1881    }
1882}
1883fn type_unknown(
1884    ty: &TypeInst,
1885    work: &mut Work,
1886    range: &Range<usize>,
1887    limits: Limits,
1888) -> Result<Option<String>, QueryError> {
1889    check_collection(0, limits.collection, range)?;
1890    let mut pending = vec![(ty, 0)];
1891    let mut reason = None;
1892    while let Some((ty, depth)) = pending.pop() {
1893        work.visit(range)?;
1894        if depth > limits.nesting {
1895            return Err(type_error(range, "query type nesting limit exceeded"));
1896        }
1897        let unknown = match &ty.kind {
1898            TypeKind::Unknown(message) => {
1899                work.spend(message.len(), range)?;
1900                Some(message.clone())
1901            }
1902            TypeKind::Variable { name, .. } => {
1903                work.spend(name.len(), range)?;
1904                Some(format!("type variable '{name}' is unresolved"))
1905            }
1906            _ if ty.instantiation == Instantiation::Unknown => {
1907                Some("type instantiation is unknown".to_owned())
1908            }
1909            _ => None,
1910        };
1911        if reason.is_none() {
1912            reason = unknown;
1913        }
1914        let mut push = |component| -> Result<(), QueryError> {
1915            check_collection(pending.len(), limits.collection, range)?;
1916            pending.push((component, depth + 1));
1917            Ok(())
1918        };
1919        match &ty.kind {
1920            TypeKind::Set(element) => push(element.as_ref())?,
1921            TypeKind::Array { indices, element } => {
1922                push(element.as_ref())?;
1923                for index in indices.iter().rev() {
1924                    push(index)?;
1925                }
1926            }
1927            TypeKind::Tuple(fields) => {
1928                for field in fields.iter().rev() {
1929                    push(field)?;
1930                }
1931            }
1932            TypeKind::Record(fields) => {
1933                for (name, field) in fields.iter().rev() {
1934                    work.spend(name.len(), range)?;
1935                    push(field)?;
1936                }
1937            }
1938            _ => {}
1939        }
1940    }
1941    Ok(reason)
1942}
1943
1944fn range_json(range: &Range<usize>) -> Value {
1945    json!({"start": range.start, "end": range.end})
1946}
1947fn binding_json(binding: &BindingResolution) -> Value {
1948    match binding {
1949        BindingResolution::Resolved(id) => json!({"outcome": "resolved", "declaration_id": id.0}),
1950        BindingResolution::Overloads(ids) => {
1951            json!({"outcome": "overloads", "candidates": ids.iter().map(|id| id.0).collect::<Vec<_>>()})
1952        }
1953        BindingResolution::Ambiguous(ids) => {
1954            json!({"outcome": "ambiguous", "candidates": ids.iter().map(|id| id.0).collect::<Vec<_>>()})
1955        }
1956        BindingResolution::Unresolved => json!({"outcome": "unresolved"}),
1957    }
1958}
1959fn call_json(call: &CallOutcome) -> Value {
1960    match call {
1961        CallOutcome::Resolved { declaration, .. } => {
1962            json!({"outcome": "resolved", "declaration_id": declaration.0})
1963        }
1964        CallOutcome::Intrinsic { name, .. } => json!({"outcome": "intrinsic", "name": name}),
1965        CallOutcome::NoMatch { reason } => json!({"outcome": "no_match", "reason": reason}),
1966        CallOutcome::Unresolved { reason } => json!({"outcome": "unresolved", "reason": reason}),
1967        CallOutcome::Ambiguous { candidates } => {
1968            json!({"outcome": "ambiguous", "candidates": candidates.iter().map(|id| id.0).collect::<Vec<_>>()})
1969        }
1970        CallOutcome::Unsupported { reason, candidates } => {
1971            json!({"outcome": "unsupported", "reason": reason, "candidates": candidates.iter().map(|id| id.0).collect::<Vec<_>>()})
1972        }
1973    }
1974}
1975fn type_json(ty: &TypeInst) -> Value {
1976    let mut value = json!({"kind": type_kind(&ty.kind), "instantiation": instantiation_name(ty.instantiation), "optional": ty.optional});
1977    match &ty.kind {
1978        TypeKind::Enum(id) => value["declaration_id"] = json!(id.0),
1979        TypeKind::Set(element) => value["element"] = type_json(element),
1980        TypeKind::Array { indices, element } => {
1981            value["indices"] = json!(indices.iter().map(type_json).collect::<Vec<_>>());
1982            value["element"] = type_json(element);
1983        }
1984        TypeKind::Tuple(fields) => {
1985            value["fields"] = json!(fields.iter().map(type_json).collect::<Vec<_>>())
1986        }
1987        TypeKind::Record(fields) => {
1988            value["fields"] = json!(
1989                fields
1990                    .iter()
1991                    .map(|(name, ty)| json!({"name": name, "type": type_json(ty)}))
1992                    .collect::<Vec<_>>()
1993            )
1994        }
1995        TypeKind::Variable {
1996            name,
1997            enum_only,
1998            any,
1999        } => {
2000            value["name"] = json!(name);
2001            value["enum_only"] = json!(enum_only);
2002            value["any"] = json!(any);
2003        }
2004        TypeKind::Unknown(reason) => value["reason"] = json!(reason),
2005        _ => {}
2006    }
2007    value
2008}
2009
2010/// Size of owned native type components, before cloning retained facts.
2011pub(crate) fn type_allocation(ty: &TypeInst) -> (usize, usize) {
2012    let mut total = (1, 0);
2013    let mut add = |ty| {
2014        let (units, bytes) = type_allocation(ty);
2015        total.0 += units;
2016        total.1 += bytes;
2017    };
2018    match &ty.kind {
2019        TypeKind::Set(element) => add(element),
2020        TypeKind::Array { indices, element } => {
2021            for index in indices {
2022                add(index);
2023            }
2024            add(element);
2025        }
2026        TypeKind::Tuple(fields) => {
2027            for field in fields {
2028                add(field);
2029            }
2030        }
2031        TypeKind::Record(fields) => {
2032            for (_, field) in fields {
2033                add(field);
2034            }
2035            total.0 += fields.len();
2036            total.1 += fields.iter().map(|(name, _)| name.len()).sum::<usize>();
2037        }
2038        TypeKind::Variable { name, .. } | TypeKind::Unknown(name) => total.1 += name.len(),
2039        _ => {}
2040    }
2041    total
2042}