1mod analysis;
4mod edit;
5mod module_resolution;
6mod project;
7mod snapshot;
8
9pub use project::{
10 ProgramExpansion, ProgramProjectContext, ProgramProjectModule, ProgramProjectReference,
11 ProgramProjectSource,
12};
13pub use snapshot::{PROGRAM_SNAPSHOT_SCHEMA_VERSION, construct_program_from_fragments};
14
15use std::collections::{BTreeMap, BTreeSet};
16use std::error::Error;
17use std::fmt;
18
19use crate::decorators::{DecoratorLevel, DecoratorSet, decorator_record, record_field};
20use crate::{LinkNetwork, LinkType, ParseConfiguration, language_support};
21use analysis::{
22 SemanticToken, effect_markers, extension_markers, module_markers, proof_markers,
23 resolve_bindings, scope_by_id, semantic_tokens, syntax_facts, unique_facts,
24 validate_identifier,
25};
26use module_resolution::{module_requests, project_has_module};
27
28pub const PROGRAM_REPRESENTATION_SCHEMA_VERSION: u32 = 1;
30
31pub const SEMANTIC_CONSTRUCTS: [&str; 10] = [
33 "modules-and-imports",
34 "scopes-and-bindings",
35 "recursive-definitions",
36 "types-and-universes",
37 "effects",
38 "attributes",
39 "macros-and-notation",
40 "proof-terms-and-tactics",
41 "surface-expansion-elaboration-traces",
42 "project-context-and-dependencies",
43];
44
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
47pub struct ProgramRange {
48 start: usize,
49 end: usize,
50}
51
52impl ProgramRange {
53 #[must_use]
55 pub const fn new(start: usize, end: usize) -> Self {
56 Self { start, end }
57 }
58
59 #[must_use]
61 pub const fn start(self) -> usize {
62 self.start
63 }
64
65 #[must_use]
67 pub const fn end(self) -> usize {
68 self.end
69 }
70}
71
72#[derive(Clone, Debug, PartialEq, Eq)]
74pub struct ProgramScope {
75 id: String,
76 parent: Option<String>,
77 range: ProgramRange,
78 depth: usize,
79}
80
81impl ProgramScope {
82 #[must_use]
84 pub fn id(&self) -> &str {
85 &self.id
86 }
87
88 #[must_use]
90 pub fn parent(&self) -> Option<&str> {
91 self.parent.as_deref()
92 }
93
94 #[must_use]
96 pub const fn range(&self) -> ProgramRange {
97 self.range
98 }
99
100 #[must_use]
102 pub const fn depth(&self) -> usize {
103 self.depth
104 }
105}
106
107#[derive(Clone, Debug, PartialEq, Eq)]
109pub struct ProgramBinding {
110 id: String,
111 name: String,
112 kind: String,
113 scope: String,
114 declaration: ProgramRange,
115 references: Vec<ProgramRange>,
116}
117
118impl ProgramBinding {
119 #[must_use]
121 pub fn id(&self) -> &str {
122 &self.id
123 }
124
125 #[must_use]
127 pub fn name(&self) -> &str {
128 &self.name
129 }
130
131 #[must_use]
133 pub fn kind(&self) -> &str {
134 &self.kind
135 }
136
137 #[must_use]
139 pub fn scope(&self) -> &str {
140 &self.scope
141 }
142
143 #[must_use]
145 pub const fn declaration(&self) -> ProgramRange {
146 self.declaration
147 }
148
149 #[must_use]
151 pub fn references(&self) -> &[ProgramRange] {
152 &self.references
153 }
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
158pub struct ProgramFact {
159 kind: String,
160 name: String,
161 range: ProgramRange,
162 phase: Option<String>,
163 file: Option<String>,
164}
165
166impl ProgramFact {
167 fn new(kind: impl Into<String>, name: impl Into<String>, range: ProgramRange) -> Self {
168 Self {
169 kind: kind.into(),
170 name: name.into(),
171 range,
172 phase: None,
173 file: None,
174 }
175 }
176
177 fn with_phase(mut self, phase: &str) -> Self {
178 self.phase = Some(phase.to_string());
179 self
180 }
181
182 #[must_use]
184 pub fn kind(&self) -> &str {
185 &self.kind
186 }
187
188 #[must_use]
190 pub fn name(&self) -> &str {
191 &self.name
192 }
193
194 #[must_use]
196 pub const fn range(&self) -> ProgramRange {
197 self.range
198 }
199
200 #[must_use]
202 pub fn phase(&self) -> Option<&str> {
203 self.phase.as_deref()
204 }
205
206 #[must_use]
209 pub fn file(&self) -> Option<&str> {
210 self.file.as_deref()
211 }
212}
213
214#[derive(Clone, Debug, PartialEq, Eq)]
216pub struct ProgramSourceMapping {
217 link_id: u64,
218 term: String,
219 range: ProgramRange,
220}
221
222impl ProgramSourceMapping {
223 #[must_use]
225 pub const fn link_id(&self) -> u64 {
226 self.link_id
227 }
228
229 #[must_use]
231 pub fn term(&self) -> &str {
232 &self.term
233 }
234
235 #[must_use]
237 pub const fn range(&self) -> ProgramRange {
238 self.range
239 }
240}
241
242#[derive(Clone, Debug, PartialEq, Eq)]
244pub struct ProgramDiagnostic {
245 kind: &'static str,
246 term: String,
247 range: ProgramRange,
248}
249
250impl ProgramDiagnostic {
251 #[must_use]
253 pub const fn kind(&self) -> &'static str {
254 self.kind
255 }
256
257 #[must_use]
259 pub fn term(&self) -> &str {
260 &self.term
261 }
262
263 #[must_use]
265 pub const fn range(&self) -> ProgramRange {
266 self.range
267 }
268}
269
270#[derive(Clone, Copy, Debug, PartialEq, Eq)]
272pub enum ProgramConstructStatus {
273 Represented,
275 NotPresent,
277 NotApplicable,
279 Unavailable,
281}
282
283#[derive(Clone, Debug, PartialEq, Eq)]
285pub struct ProgramConstruct {
286 kind: &'static str,
287 status: ProgramConstructStatus,
288 evidence: Vec<ProgramFact>,
289 rationale: Option<String>,
290}
291
292impl ProgramConstruct {
293 #[must_use]
295 pub const fn kind(&self) -> &'static str {
296 self.kind
297 }
298
299 #[must_use]
301 pub const fn status(&self) -> ProgramConstructStatus {
302 self.status
303 }
304
305 #[must_use]
307 pub fn evidence(&self) -> &[ProgramFact] {
308 &self.evidence
309 }
310
311 #[must_use]
313 pub fn rationale(&self) -> Option<&str> {
314 self.rationale.as_deref()
315 }
316}
317
318#[derive(Clone, Debug, PartialEq, Eq)]
320pub enum ProgramRepresentationError {
321 UnsupportedLanguage(String),
323 UnknownBinding(String),
325 InvalidIdentifier {
327 language: String,
328 identifier: String,
329 },
330 CaptureConflict { identifier: String, offset: usize },
332 InvalidRange { start: usize, end: usize },
334 DestinationInsideRange,
336 InvalidEdit,
338 InvalidSnapshot(String),
340}
341
342impl fmt::Display for ProgramRepresentationError {
343 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
344 match self {
345 Self::UnsupportedLanguage(language) => {
346 write!(
347 formatter,
348 "program semantics are not registered for {language}"
349 )
350 }
351 Self::UnknownBinding(binding) => write!(formatter, "unknown binding {binding}"),
352 Self::InvalidIdentifier {
353 language,
354 identifier,
355 } => write!(
356 formatter,
357 "{identifier:?} is not a valid {language} identifier"
358 ),
359 Self::CaptureConflict { identifier, offset } => write!(
360 formatter,
361 "rename would capture {identifier} at {offset} (binding conflict)"
362 ),
363 Self::InvalidRange { start, end } => {
364 write!(formatter, "invalid UTF-8 source range {start}..{end}")
365 }
366 Self::DestinationInsideRange => {
367 formatter.write_str("move destination is inside the moved range")
368 }
369 Self::InvalidEdit => formatter.write_str("structured edit does not reparse cleanly"),
370 Self::InvalidSnapshot(reason) => {
371 write!(formatter, "invalid program snapshot: {reason}")
372 }
373 }
374 }
375}
376
377impl Error for ProgramRepresentationError {}
378
379#[derive(Clone, Debug)]
381pub struct ProgramRepresentation {
382 schema_version: u32,
383 language: &'static str,
384 source: String,
385 project: ProgramProjectContext,
386 network: LinkNetwork,
387 scopes: Vec<ProgramScope>,
388 bindings: Vec<ProgramBinding>,
389 unresolved_references: Vec<ProgramFact>,
390 source_mappings: Vec<ProgramSourceMapping>,
391 modules: Vec<ProgramFact>,
392 types: Vec<ProgramFact>,
393 extensions: Vec<ProgramFact>,
394 proofs: Vec<ProgramFact>,
395 diagnostics: Vec<ProgramDiagnostic>,
396 project_modules: Vec<ProgramProjectModule>,
397 project_facts: Vec<ProgramFact>,
398 project_references: Vec<ProgramProjectReference>,
399 expansions: Vec<ProgramExpansion>,
400 constructs: Vec<ProgramConstruct>,
401}
402
403impl ProgramRepresentation {
404 fn analyze(
405 source: &str,
406 language: &str,
407 project: ProgramProjectContext,
408 ) -> Result<Self, ProgramRepresentationError> {
409 Self::analyze_decorated(source, language, project, &DecoratorSet::empty())
410 }
411
412 fn analyze_decorated(
413 source: &str,
414 language: &str,
415 project: ProgramProjectContext,
416 decorators: &DecoratorSet,
417 ) -> Result<Self, ProgramRepresentationError> {
418 let support = language_support(language)
419 .ok_or_else(|| ProgramRepresentationError::UnsupportedLanguage(language.to_string()))?;
420 let network = LinkNetwork::parse(source, support.name, ParseConfiguration::default());
421 let source_mappings = decorate_syntax_facts(syntax_facts(&network), source, decorators);
422 let tokens = semantic_tokens(source, support.name, &source_mappings);
423 let (scopes, bindings, unresolved_references) =
424 resolve_bindings(&tokens, &source_mappings, source.len(), support.name);
425 let context = project::project_semantics(
426 support.name,
427 source,
428 &source_mappings,
429 &bindings,
430 &unresolved_references,
431 &project,
432 );
433 let modules = module_facts(
434 &tokens,
435 &source_mappings,
436 source,
437 support.name,
438 &project,
439 &context.requests,
440 );
441 let types = type_facts(&tokens, &source_mappings, support.name);
442 let extensions = extension_facts(&tokens, &source_mappings, source, support.name);
443 let proofs = proof_facts(&tokens, &source_mappings, support.name);
444 let mut diagnostics = diagnostic_facts(&network);
445 diagnostics.extend(project_diagnostics(&modules, &context.resolved_requests));
446 diagnostics.extend(context.diagnostics.iter().cloned());
447 let mut program = Self {
448 schema_version: PROGRAM_REPRESENTATION_SCHEMA_VERSION,
449 language: support.name,
450 source: source.to_string(),
451 project,
452 network,
453 scopes,
454 bindings,
455 unresolved_references,
456 source_mappings,
457 modules,
458 types,
459 extensions,
460 proofs,
461 diagnostics,
462 project_modules: context.modules.clone(),
463 project_facts: context.facts.clone(),
464 project_references: context.references.clone(),
465 expansions: context.expansions.clone(),
466 constructs: Vec::new(),
467 };
468 program.constructs = construct_facts(&program, &context);
469 Ok(program)
470 }
471
472 #[must_use]
474 pub const fn schema_version(&self) -> u32 {
475 self.schema_version
476 }
477
478 #[must_use]
480 pub const fn language(&self) -> &'static str {
481 self.language
482 }
483
484 #[must_use]
486 pub fn source(&self) -> &str {
487 &self.source
488 }
489
490 #[must_use]
492 pub const fn project(&self) -> &ProgramProjectContext {
493 &self.project
494 }
495
496 #[must_use]
498 pub const fn network(&self) -> &LinkNetwork {
499 &self.network
500 }
501
502 #[must_use]
504 pub fn scopes(&self) -> &[ProgramScope] {
505 &self.scopes
506 }
507
508 #[must_use]
510 pub fn bindings(&self) -> &[ProgramBinding] {
511 &self.bindings
512 }
513
514 #[must_use]
516 pub fn unresolved_references(&self) -> &[ProgramFact] {
517 &self.unresolved_references
518 }
519
520 #[must_use]
522 pub fn source_mappings(&self) -> &[ProgramSourceMapping] {
523 &self.source_mappings
524 }
525
526 #[must_use]
528 pub fn modules(&self) -> &[ProgramFact] {
529 &self.modules
530 }
531
532 #[must_use]
534 pub fn types(&self) -> &[ProgramFact] {
535 &self.types
536 }
537
538 #[must_use]
540 pub fn extensions(&self) -> &[ProgramFact] {
541 &self.extensions
542 }
543
544 #[must_use]
546 pub fn proofs(&self) -> &[ProgramFact] {
547 &self.proofs
548 }
549
550 #[must_use]
552 pub fn diagnostics(&self) -> &[ProgramDiagnostic] {
553 &self.diagnostics
554 }
555
556 #[must_use]
558 pub fn project_modules(&self) -> &[ProgramProjectModule] {
559 &self.project_modules
560 }
561
562 #[must_use]
564 pub fn project_facts(&self) -> &[ProgramFact] {
565 &self.project_facts
566 }
567
568 #[must_use]
570 pub fn project_references(&self) -> &[ProgramProjectReference] {
571 &self.project_references
572 }
573
574 #[must_use]
576 pub fn expansions(&self) -> &[ProgramExpansion] {
577 &self.expansions
578 }
579
580 #[must_use]
582 pub fn constructs(&self) -> &[ProgramConstruct] {
583 &self.constructs
584 }
585
586 #[must_use]
588 pub fn emit(&self) -> String {
589 self.network.reconstruct_text()
590 }
591}
592
593pub fn analyze_program(
595 source: &str,
596 language: &str,
597 project: ProgramProjectContext,
598) -> Result<ProgramRepresentation, ProgramRepresentationError> {
599 ProgramRepresentation::analyze(source, language, project)
600}
601
602pub fn analyze_program_decorated(
608 source: &str,
609 language: &str,
610 project: ProgramProjectContext,
611 decorators: &DecoratorSet,
612) -> Result<ProgramRepresentation, ProgramRepresentationError> {
613 ProgramRepresentation::analyze_decorated(source, language, project, decorators)
614}
615
616fn decorate_syntax_facts(
618 facts: Vec<ProgramSourceMapping>,
619 source: &str,
620 decorators: &DecoratorSet,
621) -> Vec<ProgramSourceMapping> {
622 if !decorators.has(DecoratorLevel::CstToAst) {
623 return facts;
624 }
625 facts
626 .into_iter()
627 .filter_map(|mut fact| {
628 let text = source
629 .get(fact.range.start..fact.range.end)
630 .unwrap_or_default();
631 let record = decorator_record([("term", fact.term.as_str()), ("text", text)]);
632 let decorated = decorators.decorate(DecoratorLevel::CstToAst, &record)?;
633 record_field(&decorated, "term").clone_into(&mut fact.term);
634 Some(fact)
635 })
636 .collect()
637}
638
639pub fn construct_program(
641 source: &str,
642 language: &str,
643 project: ProgramProjectContext,
644) -> Result<ProgramRepresentation, ProgramRepresentationError> {
645 let program = ProgramRepresentation::analyze(source, language, project)?;
646 if !program.network.verify_full_match(None).is_clean() {
647 return Err(ProgramRepresentationError::InvalidEdit);
648 }
649 Ok(program)
650}
651
652#[derive(Clone, Copy, Debug, PartialEq, Eq)]
653enum TokenKind {
654 Identifier,
655 Keyword,
656 Punctuation,
657}
658
659fn module_facts(
660 tokens: &[SemanticToken],
661 syntax: &[ProgramSourceMapping],
662 source: &str,
663 language: &str,
664 project: &ProgramProjectContext,
665 project_requests: &[ProgramFact],
666) -> Vec<ProgramFact> {
667 let markers = module_markers(language);
668 let mut facts = Vec::new();
669 for (index, token) in tokens.iter().enumerate() {
670 if !markers.contains(&token.text.as_str()) {
671 continue;
672 }
673 let names = tokens[index + 1..]
674 .iter()
675 .take_while(|candidate| {
676 candidate.text != ";" && !markers.contains(&candidate.text.as_str())
677 })
678 .filter(|candidate| candidate.kind == TokenKind::Identifier)
679 .take(3)
680 .map(|candidate| candidate.text.as_str())
681 .collect::<Vec<_>>()
682 .join(".");
683 facts.push(ProgramFact::new(&token.text, names, token.range));
684 }
685 facts.extend(project.dependencies().iter().map(|dependency| {
686 ProgramFact::new(
687 "declared-project-dependency",
688 dependency,
689 ProgramRange::default(),
690 )
691 }));
692 let requests = module_requests(syntax, source, language);
693 for request in requests.into_iter().chain(project_requests.iter().cloned()) {
694 if project_has_module(project, &request.name, language) {
695 facts.push(ProgramFact::new(
696 "recognized-toolchain-module",
697 &request.name,
698 request.range,
699 ));
700 }
701 facts.push(request);
702 }
703 unique_facts(facts)
704}
705
706fn type_facts(
707 tokens: &[SemanticToken],
708 syntax: &[ProgramSourceMapping],
709 language: &str,
710) -> Vec<ProgramFact> {
711 let mut facts = syntax
712 .iter()
713 .filter(|mapping| {
714 let term = mapping.term.to_ascii_lowercase();
715 term.contains("type") || term.contains("universe")
716 })
717 .map(|mapping| {
718 ProgramFact::new("syntax-type", &mapping.term, mapping.range).with_phase("surface")
719 })
720 .collect::<Vec<_>>();
721 for (index, token) in tokens.iter().enumerate() {
722 if token.text == ":"
723 && let Some(value) = tokens.get(index + 1)
724 {
725 facts.push(
726 ProgramFact::new("annotation", &value.text, value.range).with_phase("surface"),
727 );
728 }
729 if matches!(token.text.as_str(), "universe" | "Universe" | "Type") {
730 facts.push(
731 ProgramFact::new(
732 "universe",
733 tokens
734 .get(index + 1)
735 .map_or(&token.text, |value| &value.text),
736 token.range,
737 )
738 .with_phase("surface"),
739 );
740 }
741 }
742 if language == "JavaScript" {
743 facts.push(
744 ProgramFact::new("dynamic-type", "ECMAScript value", ProgramRange::default())
745 .with_phase("runtime"),
746 );
747 }
748 unique_facts(facts)
749}
750
751fn extension_facts(
752 tokens: &[SemanticToken],
753 syntax: &[ProgramSourceMapping],
754 source: &str,
755 language: &str,
756) -> Vec<ProgramFact> {
757 let markers = extension_markers(language);
758 let mut facts = tokens
759 .iter()
760 .filter(|token| markers.contains(&token.text.as_str()))
761 .map(|token| ProgramFact::new(&token.text, &token.text, token.range))
762 .collect::<Vec<_>>();
763 facts.extend(syntax.iter().filter_map(|mapping| {
764 let term = mapping.term.to_ascii_lowercase();
765 [
766 "macro",
767 "attribute",
768 "decorator",
769 "quotation",
770 "template",
771 "notation",
772 ]
773 .iter()
774 .any(|needle| term.contains(needle))
775 .then(|| ProgramFact::new(&mapping.term, &mapping.term, mapping.range))
776 }));
777 if language == "JavaScript"
778 && let Some(start) = source.find("\"use strict\"")
779 {
780 facts.push(ProgramFact::new(
781 "directive",
782 "use strict",
783 ProgramRange::new(start, start + 12),
784 ));
785 }
786 unique_facts(facts)
787}
788
789fn proof_facts(
790 tokens: &[SemanticToken],
791 syntax: &[ProgramSourceMapping],
792 language: &str,
793) -> Vec<ProgramFact> {
794 if matches!(language, "JavaScript" | "Rust") {
795 return Vec::new();
796 }
797 let markers = proof_markers(language);
798 let facts = tokens
799 .iter()
800 .filter(|token| markers.contains(&token.text.as_str()))
801 .map(|token| ProgramFact::new(&token.text, &token.text, token.range))
802 .chain(syntax.iter().filter_map(|mapping| {
803 let term = mapping.term.to_ascii_lowercase();
804 ["theorem", "proof", "tactic"]
805 .iter()
806 .any(|needle| term.contains(needle))
807 .then(|| ProgramFact::new(&mapping.term, &mapping.term, mapping.range))
808 }))
809 .collect();
810 unique_facts(facts)
811}
812
813fn diagnostic_facts(network: &LinkNetwork) -> Vec<ProgramDiagnostic> {
814 network
815 .verify_full_match(None)
816 .issues()
817 .iter()
818 .filter_map(|issue| {
819 let link = network.link(issue.link_id())?;
820 let span = link.metadata().span();
821 Some(ProgramDiagnostic {
822 kind: if link.metadata().flags().is_missing() {
823 "missing"
824 } else {
825 "parse-error"
826 },
827 term: link.metadata().term().unwrap_or_default().to_string(),
828 range: span.map_or_else(ProgramRange::default, |span| {
829 ProgramRange::new(span.byte_range().start(), span.byte_range().end())
830 }),
831 })
832 })
833 .collect()
834}
835
836fn project_diagnostics(
837 modules: &[ProgramFact],
838 resolved: &[(String, usize)],
839) -> Vec<ProgramDiagnostic> {
840 let recognized = modules
841 .iter()
842 .filter(|fact| fact.kind == "recognized-toolchain-module")
843 .map(|fact| fact.name.as_str())
844 .collect::<std::collections::HashSet<_>>();
845 modules
846 .iter()
847 .filter(|fact| {
848 fact.kind == "module-import"
849 && !recognized.contains(fact.name.as_str())
850 && !resolved
851 .iter()
852 .any(|(name, start)| *name == fact.name && *start == fact.range.start)
853 })
854 .map(|fact| ProgramDiagnostic {
855 kind: "missing-project-context",
856 term: fact.name.clone(),
857 range: fact.range,
858 })
859 .collect()
860}
861
862fn construct_facts(
863 program: &ProgramRepresentation,
864 context: &project::ProjectResult,
865) -> Vec<ProgramConstruct> {
866 SEMANTIC_CONSTRUCTS
867 .iter()
868 .map(|kind| {
869 let mut evidence = construct_evidence(program, kind);
870 evidence.extend(project::project_evidence(&program.project, context, kind));
871 if evidence.is_empty() && *kind == "surface-expansion-elaboration-traces" {
872 return ProgramConstruct {
873 kind,
874 status: ProgramConstructStatus::Unavailable,
875 evidence,
876 rationale: Some(
877 "no macro expansion or elaboration trace has been produced".to_string(),
878 ),
879 };
880 }
881 if evidence.is_empty()
882 && *kind == "proof-terms-and-tactics"
883 && matches!(program.language, "JavaScript" | "Rust")
884 {
885 return ProgramConstruct {
886 kind,
887 status: ProgramConstructStatus::NotApplicable,
888 evidence,
889 rationale: Some(format!(
890 "{} defines no proof/tactic sublanguage",
891 program.language
892 )),
893 };
894 }
895 let represented = !evidence.is_empty();
896 ProgramConstruct {
897 kind,
898 status: if represented {
899 ProgramConstructStatus::Represented
900 } else {
901 ProgramConstructStatus::NotPresent
902 },
903 evidence,
904 rationale: (!represented).then(|| {
905 "the analyzed source contains no instance of this construct".to_string()
906 }),
907 }
908 })
909 .collect()
910}
911
912fn construct_evidence(program: &ProgramRepresentation, kind: &str) -> Vec<ProgramFact> {
913 match kind {
914 "modules-and-imports" => program.modules.clone(),
915 "scopes-and-bindings" => program
916 .bindings
917 .iter()
918 .map(|binding| ProgramFact::new(&binding.kind, &binding.name, binding.declaration))
919 .collect(),
920 "recursive-definitions" => program
921 .bindings
922 .iter()
923 .filter(|binding| {
924 matches!(
925 binding.kind.as_str(),
926 "function" | "Fixpoint" | "CoFixpoint" | "def"
927 ) && !binding.references.is_empty()
928 })
929 .map(|binding| ProgramFact::new("recursive", &binding.name, binding.declaration))
930 .collect(),
931 "types-and-universes" => program.types.clone(),
932 "effects" => effect_facts(program),
933 "attributes" => program
934 .extensions
935 .iter()
936 .filter(|fact| {
937 let kind = fact.kind.to_ascii_lowercase();
938 ["attribute", "directive", "allow", "local", "simp", "#"]
939 .iter()
940 .any(|needle| kind.contains(needle))
941 })
942 .cloned()
943 .collect(),
944 "macros-and-notation" => program
945 .extensions
946 .iter()
947 .filter(|fact| {
948 let kind = fact.kind.to_ascii_lowercase();
949 [
950 "macro", "notation", "template", "tagged", "prefix", "postfix", "infix",
951 "syntax",
952 ]
953 .iter()
954 .any(|needle| kind.contains(needle))
955 })
956 .cloned()
957 .collect(),
958 "proof-terms-and-tactics" => program.proofs.clone(),
959 "project-context-and-dependencies" => program
960 .project
961 .files()
962 .iter()
963 .chain(program.project.dependencies())
964 .map(|name| ProgramFact::new("project", name, ProgramRange::default()))
965 .collect(),
966 _ => Vec::new(),
967 }
968}
969
970fn effect_facts(program: &ProgramRepresentation) -> Vec<ProgramFact> {
971 let mut facts = Vec::new();
972 for marker in effect_markers(program.language) {
973 facts.extend(program.source.match_indices(marker).map(|(start, value)| {
974 ProgramFact::new(
975 "effect",
976 value,
977 ProgramRange::new(start, start + value.len()),
978 )
979 }));
980 }
981 unique_facts(facts)
982}