1use controlled_option::ControlledOption;
339use lsp_positions::SpanCalculator;
340use once_cell::sync::Lazy;
341use stack_graphs::arena::Handle;
342use stack_graphs::graph::File;
343use stack_graphs::graph::Node;
344use stack_graphs::graph::NodeID;
345use stack_graphs::graph::StackGraph;
346use std::borrow::Cow;
347use std::collections::HashMap;
348use std::collections::HashSet;
349use std::mem::transmute;
350use std::ops::BitOr;
351use std::path::Path;
352use std::path::PathBuf;
353use std::sync::atomic::AtomicBool;
354use std::sync::atomic::Ordering;
355use std::sync::Arc;
356use std::time::Duration;
357use std::time::Instant;
358use thiserror::Error;
359use tree_sitter::Parser;
360use tree_sitter_graph::functions::Functions;
361use tree_sitter_graph::graph::Edge;
362use tree_sitter_graph::graph::Graph;
363use tree_sitter_graph::graph::GraphNode;
364use tree_sitter_graph::graph::GraphNodeRef;
365use tree_sitter_graph::graph::Value;
366use tree_sitter_graph::parse_error::ParseError;
367use tree_sitter_graph::parse_error::TreeWithParseErrorVec;
368use tree_sitter_graph::ExecutionConfig;
369use util::DisplayParseErrorsPretty;
370use util::TreeSitterCancellationFlag;
371
372#[cfg(feature = "cli")]
373pub mod ci;
374#[cfg(feature = "cli")]
375pub mod cli;
376pub mod functions;
377pub mod loader;
378pub mod test;
379mod util;
380
381pub use tree_sitter_graph::VariableError;
382pub use tree_sitter_graph::Variables;
383
384pub(self) const MAX_PARSE_ERRORS: usize = 5;
385
386static DROP_SCOPES_TYPE: &'static str = "drop_scopes";
388static POP_SCOPED_SYMBOL_TYPE: &'static str = "pop_scoped_symbol";
389static POP_SYMBOL_TYPE: &'static str = "pop_symbol";
390static PUSH_SCOPED_SYMBOL_TYPE: &'static str = "push_scoped_symbol";
391static PUSH_SYMBOL_TYPE: &'static str = "push_symbol";
392static SCOPE_TYPE: &'static str = "scope";
393
394static DEBUG_ATTR_PREFIX: &'static str = "debug_";
396static DEFINIENS_NODE_ATTR: &'static str = "definiens_node";
397static EMPTY_SOURCE_SPAN_ATTR: &'static str = "empty_source_span";
398static IS_DEFINITION_ATTR: &'static str = "is_definition";
399static IS_ENDPOINT_ATTR: &'static str = "is_endpoint";
400static IS_EXPORTED_ATTR: &'static str = "is_exported";
401static IS_REFERENCE_ATTR: &'static str = "is_reference";
402static SCOPE_ATTR: &'static str = "scope";
403static SOURCE_NODE_ATTR: &'static str = "source_node";
404static SYMBOL_ATTR: &'static str = "symbol";
405static SYNTAX_TYPE_ATTR: &'static str = "syntax_type";
406static TYPE_ATTR: &'static str = "type";
407
408static POP_SCOPED_SYMBOL_ATTRS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
410 HashSet::from([
411 TYPE_ATTR,
412 SYMBOL_ATTR,
413 IS_DEFINITION_ATTR,
414 DEFINIENS_NODE_ATTR,
415 SYNTAX_TYPE_ATTR,
416 ])
417});
418static POP_SYMBOL_ATTRS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
419 HashSet::from([
420 TYPE_ATTR,
421 SYMBOL_ATTR,
422 IS_DEFINITION_ATTR,
423 DEFINIENS_NODE_ATTR,
424 SYNTAX_TYPE_ATTR,
425 ])
426});
427static PUSH_SCOPED_SYMBOL_ATTRS: Lazy<HashSet<&'static str>> =
428 Lazy::new(|| HashSet::from([TYPE_ATTR, SYMBOL_ATTR, SCOPE_ATTR, IS_REFERENCE_ATTR]));
429static PUSH_SYMBOL_ATTRS: Lazy<HashSet<&'static str>> =
430 Lazy::new(|| HashSet::from([TYPE_ATTR, SYMBOL_ATTR, IS_REFERENCE_ATTR]));
431static SCOPE_ATTRS: Lazy<HashSet<&'static str>> =
432 Lazy::new(|| HashSet::from([TYPE_ATTR, IS_EXPORTED_ATTR, IS_ENDPOINT_ATTR]));
433
434static PRECEDENCE_ATTR: &'static str = "precedence";
436
437pub const ROOT_NODE_VAR: &'static str = "ROOT_NODE";
440pub const JUMP_TO_SCOPE_NODE_VAR: &'static str = "JUMP_TO_SCOPE_NODE";
442pub const FILE_PATH_VAR: &'static str = "FILE_PATH";
445pub const ROOT_PATH_VAR: &'static str = "ROOT_PATH";
448
449pub struct StackGraphLanguage {
451 language: tree_sitter::Language,
452 tsg: tree_sitter_graph::ast::File,
453 tsg_path: PathBuf,
454 tsg_source: std::borrow::Cow<'static, str>,
455 functions: Functions,
456}
457
458impl StackGraphLanguage {
459 pub fn new(
462 language: tree_sitter::Language,
463 tsg: tree_sitter_graph::ast::File,
464 ) -> StackGraphLanguage {
465 debug_assert_eq!(language, tsg.language);
466 StackGraphLanguage {
467 language,
468 tsg,
469 tsg_path: PathBuf::from("<tsg>"),
470 tsg_source: Cow::from(String::new()),
471 functions: Self::default_functions(),
472 }
473 }
474
475 pub fn from_str(
479 language: tree_sitter::Language,
480 tsg_source: &str,
481 ) -> Result<StackGraphLanguage, LanguageError> {
482 let tsg = tree_sitter_graph::ast::File::from_str(language.clone(), tsg_source)?;
483 Ok(StackGraphLanguage {
484 language,
485 tsg,
486 tsg_path: PathBuf::from("<missing tsg path>"),
487 tsg_source: Cow::from(tsg_source.to_string()),
488 functions: Self::default_functions(),
489 })
490 }
491
492 pub fn from_source(
497 language: tree_sitter::Language,
498 tsg_path: PathBuf,
499 tsg_source: &str,
500 ) -> Result<StackGraphLanguage, LanguageError> {
501 let mut sgl = Self::from_str(language, tsg_source)?;
502 sgl.tsg_path = tsg_path;
503 Ok(sgl)
504 }
505
506 pub fn set_tsg_info(&mut self, path: PathBuf, source: Cow<'static, str>) {
507 self.tsg_path = path;
508 self.tsg_source = source;
509 }
510
511 fn default_functions() -> tree_sitter_graph::functions::Functions {
512 let mut functions = tree_sitter_graph::functions::Functions::stdlib();
513 crate::functions::add_path_functions(&mut functions);
514 functions
515 }
516
517 pub fn functions_mut(&mut self) -> &mut tree_sitter_graph::functions::Functions {
518 &mut self.functions
519 }
520
521 pub fn language(&self) -> &tree_sitter::Language {
522 &self.language
523 }
524
525 pub fn tsg_path(&self) -> &Path {
528 &self.tsg_path
529 }
530
531 pub fn tsg_source(&self) -> &Cow<'static, str> {
534 &self.tsg_source
535 }
536}
537
538#[derive(Debug, Error)]
540pub enum LanguageError {
541 #[error(transparent)]
542 ParseError(#[from] tree_sitter_graph::ParseError),
543}
544
545impl LanguageError {
546 pub fn display_pretty<'a>(
547 &'a self,
548 path: &'a Path,
549 source: &'a str,
550 ) -> impl std::fmt::Display + 'a {
551 match self {
552 Self::ParseError(err) => err.display_pretty(path, source),
553 }
554 }
555}
556
557impl StackGraphLanguage {
558 pub fn build_stack_graph_into<'a>(
563 &'a self,
564 stack_graph: &'a mut StackGraph,
565 file: Handle<File>,
566 source: &'a str,
567 globals: &'a Variables<'a>,
568 cancellation_flag: &'a dyn CancellationFlag,
569 ) -> Result<(), BuildError> {
570 self.builder_into_stack_graph(stack_graph, file, source)
571 .build(globals, cancellation_flag)
572 }
573
574 pub fn builder_into_stack_graph<'a>(
579 &'a self,
580 stack_graph: &'a mut StackGraph,
581 file: Handle<File>,
582 source: &'a str,
583 ) -> Builder<'a> {
584 Builder::new(self, stack_graph, file, source)
585 }
586}
587
588pub struct Builder<'a> {
589 sgl: &'a StackGraphLanguage,
590 stack_graph: &'a mut StackGraph,
591 file: Handle<File>,
592 source: &'a str,
593 graph: Graph<'a>,
594 remapped_nodes: HashMap<usize, NodeID>,
595 injected_node_count: usize,
596 span_calculator: SpanCalculator<'a>,
597}
598
599impl<'a> Builder<'a> {
600 fn new(
601 sgl: &'a StackGraphLanguage,
602 stack_graph: &'a mut StackGraph,
603 file: Handle<File>,
604 source: &'a str,
605 ) -> Self {
606 let span_calculator = SpanCalculator::new(source);
607 Builder {
608 sgl,
609 stack_graph,
610 file,
611 source,
612 graph: Graph::new(),
613 remapped_nodes: HashMap::new(),
614 injected_node_count: 0,
615 span_calculator,
616 }
617 }
618
619 pub fn build(
621 mut self,
622 globals: &'a Variables<'a>,
623 cancellation_flag: &dyn CancellationFlag,
624 ) -> Result<(), BuildError> {
625 let tree = {
626 let mut parser = Parser::new();
627 parser.set_language(&self.sgl.language)?;
628 let ts_cancellation_flag = TreeSitterCancellationFlag::from(cancellation_flag);
629 unsafe { parser.set_cancellation_flag(Some(ts_cancellation_flag.as_ref())) };
634 parser
635 .parse(self.source, None)
636 .ok_or(BuildError::ParseError)?
637 };
638 let parse_errors = ParseError::into_all(tree);
639 if parse_errors.errors().len() > 0 {
640 return Err(BuildError::ParseErrors(parse_errors));
641 }
642 let tree = parse_errors.into_tree();
643
644 let mut globals = Variables::nested(globals);
645
646 let root_node = self.inject_node(NodeID::root());
647 globals
648 .add(ROOT_NODE_VAR.into(), root_node.into())
649 .unwrap_or_default();
650
651 let jump_to_scope_node = self.inject_node(NodeID::jump_to());
652 globals
653 .add(JUMP_TO_SCOPE_NODE_VAR.into(), jump_to_scope_node.into())
654 .expect("Failed to set JUMP_TO_SCOPE_NODE");
655
656 if globals.get(&FILE_PATH_VAR.into()).is_none() {
657 let file_name = self.stack_graph[self.file].to_string();
658 globals
659 .add(FILE_PATH_VAR.into(), file_name.into())
660 .expect("Failed to set FILE_PATH");
661 }
662
663 let mut config = ExecutionConfig::new(&self.sgl.functions, &globals)
664 .lazy(true)
665 .debug_attributes(
666 [DEBUG_ATTR_PREFIX, "tsg_location"].concat().as_str().into(),
667 [DEBUG_ATTR_PREFIX, "tsg_variable"].concat().as_str().into(),
668 [DEBUG_ATTR_PREFIX, "tsg_match_node"]
669 .concat()
670 .as_str()
671 .into(),
672 );
673
674 let tree: &'a tree_sitter::Tree = unsafe { transmute(&tree) };
684 self.sgl.tsg.execute_into(
685 &mut self.graph,
686 tree,
687 self.source,
688 &mut config,
689 &(cancellation_flag as &dyn CancellationFlag),
690 )?;
691
692 self.load(cancellation_flag)
693 }
694
695 pub fn inject_node(&mut self, id: NodeID) -> GraphNodeRef {
698 let node = self.graph.add_graph_node();
699 self.remapped_nodes.insert(node.index(), id);
700 self.injected_node_count += 1;
701 node
702 }
703}
704
705pub trait CancellationFlag: Sync {
707 fn check(&self, at: &'static str) -> Result<(), CancellationError>;
708}
709
710#[derive(Clone, Debug, Error)]
711#[error("Cancelled at \"{0}\"")]
712pub struct CancellationError(pub &'static str);
713
714impl stack_graphs::CancellationFlag for &dyn CancellationFlag {
715 fn check(&self, at: &'static str) -> Result<(), stack_graphs::CancellationError> {
716 CancellationFlag::check(*self, at).map_err(|err| stack_graphs::CancellationError(err.0))
717 }
718}
719
720impl tree_sitter_graph::CancellationFlag for &dyn CancellationFlag {
721 fn check(&self, at: &'static str) -> Result<(), tree_sitter_graph::CancellationError> {
722 CancellationFlag::check(*self, at)
723 .map_err(|err| tree_sitter_graph::CancellationError(err.0))
724 }
725}
726
727impl<'a> BitOr for &'a dyn CancellationFlag {
728 type Output = OrCancellationFlag<'a>;
729 fn bitor(self, rhs: Self) -> Self::Output {
730 OrCancellationFlag(self, rhs)
731 }
732}
733
734pub struct OrCancellationFlag<'a>(&'a dyn CancellationFlag, &'a dyn CancellationFlag);
735
736impl CancellationFlag for OrCancellationFlag<'_> {
737 fn check(&self, at: &'static str) -> Result<(), CancellationError> {
738 self.0.check(at)?;
739 self.1.check(at)?;
740 Ok(())
741 }
742}
743
744pub struct NoCancellation;
745
746impl CancellationFlag for NoCancellation {
747 fn check(&self, _at: &'static str) -> Result<(), CancellationError> {
748 Ok(())
749 }
750}
751
752pub struct CancelAfterDuration {
753 start: Instant,
754 limit: Duration,
755}
756
757impl CancelAfterDuration {
758 pub fn new(limit: Duration) -> Self {
759 Self {
760 start: Instant::now(),
761 limit,
762 }
763 }
764
765 pub fn from_option(limit: Option<Duration>) -> Box<dyn CancellationFlag> {
766 match limit {
767 Some(limit) => Box::new(Self::new(limit)),
768 None => Box::new(NoCancellation),
769 }
770 }
771}
772
773impl CancellationFlag for CancelAfterDuration {
774 fn check(&self, at: &'static str) -> Result<(), CancellationError> {
775 if self.start.elapsed().ge(&self.limit) {
776 return Err(CancellationError(at));
777 }
778 Ok(())
779 }
780}
781
782#[derive(Clone)]
783pub struct AtomicCancellationFlag {
784 flag: Arc<AtomicBool>,
785}
786
787impl AtomicCancellationFlag {
788 pub fn new() -> Self {
789 Self {
790 flag: Arc::new(AtomicBool::new(false)),
791 }
792 }
793
794 pub fn cancel(&self) {
795 self.flag.store(true, Ordering::Relaxed)
796 }
797}
798
799impl CancellationFlag for AtomicCancellationFlag {
800 fn check(&self, at: &'static str) -> Result<(), CancellationError> {
801 if self.flag.load(Ordering::Relaxed) {
802 return Err(CancellationError(at));
803 }
804 Ok(())
805 }
806}
807
808#[derive(Debug, Error)]
810pub enum BuildError {
811 #[error("{0}")]
812 Cancelled(&'static str),
813 #[error("Missing ‘type’ attribute on graph node")]
814 MissingNodeType(GraphNodeRef),
815 #[error("Missing ‘symbol’ attribute on graph node")]
816 MissingSymbol(GraphNodeRef),
817 #[error("Missing ‘scope’ attribute on graph node")]
818 MissingScope(GraphNodeRef),
819 #[error("Unknown ‘{0}’ flag type {1}")]
820 UnknownFlagType(String, String),
821 #[error("Unknown node type {0}")]
822 UnknownNodeType(String),
823 #[error("Unknown symbol type {0}")]
824 UnknownSymbolType(String),
825 #[error(transparent)]
826 ExecutionError(tree_sitter_graph::ExecutionError),
827 #[error("Error parsing source")]
828 ParseError,
829 #[error("Error parsing source")]
830 ParseErrors(TreeWithParseErrorVec),
831 #[error("Error converting shorthand ‘{0}’ on {1} with value {2}")]
832 ConversionError(String, String, String),
833 #[error(transparent)]
834 LanguageError(#[from] tree_sitter::LanguageError),
835 #[error("Expected exported symbol scope in {0}, got {1}")]
836 SymbolScopeError(String, String),
837}
838
839impl From<stack_graphs::CancellationError> for BuildError {
840 fn from(value: stack_graphs::CancellationError) -> Self {
841 Self::Cancelled(value.0)
842 }
843}
844
845impl From<tree_sitter_graph::ExecutionError> for BuildError {
846 fn from(value: tree_sitter_graph::ExecutionError) -> Self {
847 match value {
848 tree_sitter_graph::ExecutionError::Cancelled(err) => Self::Cancelled(err.0),
849 err => Self::ExecutionError(err),
850 }
851 }
852}
853
854impl BuildError {
855 pub fn display_pretty<'a>(
856 &'a self,
857 source_path: &'a Path,
858 source: &'a str,
859 tsg_path: &'a Path,
860 tsg: &'a str,
861 ) -> impl std::fmt::Display + 'a {
862 DisplayBuildErrorPretty {
863 error: self,
864 source_path,
865 source,
866 tsg_path,
867 tsg,
868 }
869 }
870}
871
872struct DisplayBuildErrorPretty<'a> {
873 error: &'a BuildError,
874 source_path: &'a Path,
875 source: &'a str,
876 tsg_path: &'a Path,
877 tsg: &'a str,
878}
879
880impl std::fmt::Display for DisplayBuildErrorPretty<'_> {
881 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
882 match self.error {
883 BuildError::ExecutionError(err) => write!(
884 f,
885 "{}",
886 err.display_pretty(self.source_path, self.source, self.tsg_path, self.tsg)
887 ),
888 BuildError::ParseErrors(parse_errors) => write!(
889 f,
890 "{}",
891 DisplayParseErrorsPretty {
892 parse_errors,
893 path: self.source_path,
894 source: self.source,
895 max_errors: crate::MAX_PARSE_ERRORS,
896 }
897 ),
898 err => err.fmt(f),
899 }
900 }
901}
902
903impl<'a> Builder<'a> {
904 fn load(mut self, cancellation_flag: &dyn CancellationFlag) -> Result<(), BuildError> {
905 let cancellation_flag: &dyn stack_graphs::CancellationFlag = &cancellation_flag;
906
907 let mut next_local_id = (self.graph.node_count() - self.injected_node_count) as u32;
911 for node in self.stack_graph.nodes_for_file(self.file) {
912 let local_id = self.stack_graph[node].id().local_id();
913 let index = (local_id as usize) + self.injected_node_count;
914 while self
916 .stack_graph
917 .node_for_id(NodeID::new_in_file(self.file, next_local_id))
918 .is_some()
919 {
920 next_local_id += 1;
921 }
922 self.remapped_nodes
924 .insert(index, NodeID::new_in_file(self.file, next_local_id))
925 .map(|_| panic!("index already remapped"));
926 }
927
928 for node_ref in self.graph.iter_nodes().skip(self.injected_node_count) {
931 cancellation_flag.check("loading graph nodes")?;
932 let node_type = self.get_node_type(node_ref)?;
933 let handle = match node_type {
934 NodeType::DropScopes => self.load_drop_scopes(node_ref),
935 NodeType::PopScopedSymbol => self.load_pop_scoped_symbol(node_ref)?,
936 NodeType::PopSymbol => self.load_pop_symbol(node_ref)?,
937 NodeType::PushScopedSymbol => self.load_push_scoped_symbol(node_ref)?,
938 NodeType::PushSymbol => self.load_push_symbol(node_ref)?,
939 NodeType::Scope => self.load_scope(node_ref)?,
940 };
941 self.load_source_info(node_ref, handle)?;
942 self.load_node_debug_info(node_ref, handle)?;
943 }
944
945 for node in self.stack_graph.nodes_for_file(self.file) {
946 self.verify_node(node)?;
947 }
948
949 for source_ref in self.graph.iter_nodes() {
954 let source = &self.graph[source_ref];
955 let source_node_id = self.node_id_for_graph_node(source_ref);
956 let source_handle = self.stack_graph.node_for_id(source_node_id).unwrap();
957 for (sink_ref, edge) in source.iter_edges() {
958 cancellation_flag.check("loading graph edges")?;
959 let precedence = match edge.attributes.get(PRECEDENCE_ATTR) {
960 Some(precedence) => precedence.as_integer()? as i32,
961 None => 0,
962 };
963 let sink_node_id = self.node_id_for_graph_node(sink_ref);
964 let sink_handle = self.stack_graph.node_for_id(sink_node_id).unwrap();
965 self.stack_graph
966 .add_edge(source_handle, sink_handle, precedence);
967 Self::load_edge_debug_info(
968 &mut self.stack_graph,
969 source_handle,
970 sink_handle,
971 edge,
972 )?;
973 }
974 }
975
976 Ok(())
977 }
978
979 fn get_node_type(&self, node_ref: GraphNodeRef) -> Result<NodeType, BuildError> {
980 let node = &self.graph[node_ref];
981 let node_type = match node.attributes.get(TYPE_ATTR) {
982 Some(node_type) => node_type.as_str()?,
983 None => return Ok(NodeType::Scope),
984 };
985 if node_type == DROP_SCOPES_TYPE {
986 return Ok(NodeType::DropScopes);
987 } else if node_type == POP_SCOPED_SYMBOL_TYPE {
988 return Ok(NodeType::PopScopedSymbol);
989 } else if node_type == POP_SYMBOL_TYPE {
990 return Ok(NodeType::PopSymbol);
991 } else if node_type == PUSH_SCOPED_SYMBOL_TYPE {
992 return Ok(NodeType::PushScopedSymbol);
993 } else if node_type == PUSH_SYMBOL_TYPE {
994 return Ok(NodeType::PushSymbol);
995 } else if node_type == SCOPE_TYPE {
996 return Ok(NodeType::Scope);
997 } else {
998 return Err(BuildError::UnknownNodeType(format!("{}", node_type)));
999 }
1000 }
1001
1002 fn verify_node(&self, node: Handle<Node>) -> Result<(), BuildError> {
1003 if let Node::PushScopedSymbol(node) = &self.stack_graph[node] {
1004 let scope = &self.stack_graph[self.stack_graph.node_for_id(node.scope).unwrap()];
1005 if !scope.is_exported_scope() {
1006 return Err(BuildError::SymbolScopeError(
1007 format!("{}", node.display(self.stack_graph)),
1008 format!("{}", scope.display(self.stack_graph)),
1009 ));
1010 }
1011 }
1012 Ok(())
1013 }
1014}
1015
1016enum NodeType {
1017 DropScopes,
1018 PopSymbol,
1019 PopScopedSymbol,
1020 PushSymbol,
1021 PushScopedSymbol,
1022 Scope,
1023}
1024
1025impl<'a> Builder<'a> {
1026 fn node_id_for_graph_node(&self, node_ref: GraphNodeRef) -> NodeID {
1035 let index = node_ref.index();
1036 self.remapped_nodes.get(&index).map_or_else(
1037 || NodeID::new_in_file(self.file, (index - self.injected_node_count) as u32),
1038 |id| *id,
1039 )
1040 }
1041
1042 fn load_drop_scopes(&mut self, node_ref: GraphNodeRef) -> Handle<Node> {
1043 let id = self.node_id_for_graph_node(node_ref);
1044 self.stack_graph.add_drop_scopes_node(id).unwrap()
1045 }
1046
1047 fn load_pop_scoped_symbol(
1048 &mut self,
1049 node_ref: GraphNodeRef,
1050 ) -> Result<Handle<Node>, BuildError> {
1051 let node = &self.graph[node_ref];
1052 let symbol = match node.attributes.get(SYMBOL_ATTR) {
1053 Some(symbol) => self.load_symbol(symbol)?,
1054 None => return Err(BuildError::MissingSymbol(node_ref)),
1055 };
1056 let symbol = self.stack_graph.add_symbol(&symbol);
1057 let id = self.node_id_for_graph_node(node_ref);
1058 let is_definition = self.load_flag(node, IS_DEFINITION_ATTR)?;
1059 self.verify_attributes(node, POP_SCOPED_SYMBOL_TYPE, &POP_SCOPED_SYMBOL_ATTRS);
1060 let node_handle = self
1061 .stack_graph
1062 .add_pop_scoped_symbol_node(id, symbol, is_definition)
1063 .unwrap();
1064 if is_definition {
1065 self.load_definiens_info(node_ref, node_handle)?;
1066 }
1067 Ok(node_handle)
1068 }
1069
1070 fn load_pop_symbol(&mut self, node_ref: GraphNodeRef) -> Result<Handle<Node>, BuildError> {
1071 let node = &self.graph[node_ref];
1072 let symbol = match node.attributes.get(SYMBOL_ATTR) {
1073 Some(symbol) => self.load_symbol(symbol)?,
1074 None => return Err(BuildError::MissingSymbol(node_ref)),
1075 };
1076 let symbol = self.stack_graph.add_symbol(&symbol);
1077 let id = self.node_id_for_graph_node(node_ref);
1078 let is_definition = self.load_flag(node, IS_DEFINITION_ATTR)?;
1079 self.verify_attributes(node, POP_SYMBOL_TYPE, &POP_SYMBOL_ATTRS);
1080 let node_handle = self
1081 .stack_graph
1082 .add_pop_symbol_node(id, symbol, is_definition)
1083 .unwrap();
1084 if is_definition {
1085 self.load_definiens_info(node_ref, node_handle)?;
1086 }
1087 Ok(node_handle)
1088 }
1089
1090 fn load_push_scoped_symbol(
1091 &mut self,
1092 node_ref: GraphNodeRef,
1093 ) -> Result<Handle<Node>, BuildError> {
1094 let node = &self.graph[node_ref];
1095 let symbol = match node.attributes.get(SYMBOL_ATTR) {
1096 Some(symbol) => self.load_symbol(symbol)?,
1097 None => return Err(BuildError::MissingSymbol(node_ref)),
1098 };
1099 let symbol = self.stack_graph.add_symbol(&symbol);
1100 let id = self.node_id_for_graph_node(node_ref);
1101 let scope = match node.attributes.get(SCOPE_ATTR) {
1102 Some(scope) => self.node_id_for_graph_node(scope.as_graph_node_ref()?),
1103 None => return Err(BuildError::MissingScope(node_ref)),
1104 };
1105 let is_reference = self.load_flag(node, IS_REFERENCE_ATTR)?;
1106 self.verify_attributes(node, PUSH_SCOPED_SYMBOL_TYPE, &PUSH_SCOPED_SYMBOL_ATTRS);
1107 Ok(self
1108 .stack_graph
1109 .add_push_scoped_symbol_node(id, symbol, scope, is_reference)
1110 .unwrap())
1111 }
1112
1113 fn load_push_symbol(&mut self, node_ref: GraphNodeRef) -> Result<Handle<Node>, BuildError> {
1114 let node = &self.graph[node_ref];
1115 let symbol = match node.attributes.get(SYMBOL_ATTR) {
1116 Some(symbol) => self.load_symbol(symbol)?,
1117 None => return Err(BuildError::MissingSymbol(node_ref)),
1118 };
1119 let symbol = self.stack_graph.add_symbol(&symbol);
1120 let id = self.node_id_for_graph_node(node_ref);
1121 let is_reference = self.load_flag(node, IS_REFERENCE_ATTR)?;
1122 self.verify_attributes(node, PUSH_SYMBOL_TYPE, &PUSH_SYMBOL_ATTRS);
1123 Ok(self
1124 .stack_graph
1125 .add_push_symbol_node(id, symbol, is_reference)
1126 .unwrap())
1127 }
1128
1129 fn load_scope(&mut self, node_ref: GraphNodeRef) -> Result<Handle<Node>, BuildError> {
1130 let node = &self.graph[node_ref];
1131 let id = self.node_id_for_graph_node(node_ref);
1132 let is_exported =
1133 self.load_flag(node, IS_EXPORTED_ATTR)? || self.load_flag(node, IS_ENDPOINT_ATTR)?;
1134 self.verify_attributes(node, SCOPE_TYPE, &SCOPE_ATTRS);
1135 Ok(self.stack_graph.add_scope_node(id, is_exported).unwrap())
1136 }
1137
1138 fn load_symbol(&self, value: &Value) -> Result<String, BuildError> {
1139 match value {
1140 Value::Integer(i) => Ok(i.to_string()),
1141 Value::String(s) => Ok(s.clone()),
1142 _ => Err(BuildError::UnknownSymbolType(format!("{}", value))),
1143 }
1144 }
1145
1146 fn load_flag(&self, node: &GraphNode, attribute: &str) -> Result<bool, BuildError> {
1147 match node.attributes.get(attribute) {
1148 Some(value) => value.as_boolean().map_err(|_| {
1149 BuildError::UnknownFlagType(format!("{}", attribute), format!("{}", value))
1150 }),
1151 None => Ok(false),
1152 }
1153 }
1154
1155 fn load_source_info(
1156 &mut self,
1157 node_ref: GraphNodeRef,
1158 node_handle: Handle<Node>,
1159 ) -> Result<(), BuildError> {
1160 let node = &self.graph[node_ref];
1161
1162 if let Some(source_node) = node.attributes.get(SOURCE_NODE_ATTR) {
1163 let source_node = &self.graph[source_node.as_syntax_node_ref()?];
1164 let mut source_span = self.span_calculator.for_node(source_node);
1165 if match node.attributes.get(EMPTY_SOURCE_SPAN_ATTR) {
1166 Some(empty_source_span) => empty_source_span.as_boolean()?,
1167 None => false,
1168 } {
1169 source_span.end = source_span.start.clone();
1170 }
1171 let containing_line = &self.source[source_span.start.containing_line.clone()];
1172 let containing_line = self.stack_graph.add_string(containing_line);
1173 let source_info = self.stack_graph.source_info_mut(node_handle);
1174 source_info.span = source_span;
1175 source_info.containing_line = ControlledOption::some(containing_line);
1176 }
1177
1178 if let Some(syntax_type) = node.attributes.get(SYNTAX_TYPE_ATTR) {
1179 let syntax_type = syntax_type.as_str()?;
1180 let syntax_type = self.stack_graph.add_string(syntax_type);
1181 let source_info = self.stack_graph.source_info_mut(node_handle);
1182 source_info.syntax_type = syntax_type.into();
1183 }
1184
1185 Ok(())
1186 }
1187
1188 fn load_definiens_info(
1189 &mut self,
1190 node_ref: GraphNodeRef,
1191 node_handle: Handle<Node>,
1192 ) -> Result<(), BuildError> {
1193 let node = &self.graph[node_ref];
1194 let definiens_node = match node.attributes.get(DEFINIENS_NODE_ATTR) {
1195 Some(Value::Null) => return Ok(()),
1196 Some(definiens_node) => &self.graph[definiens_node.as_syntax_node_ref()?],
1197 None => return Ok(()),
1198 };
1199 let definiens_span = self.span_calculator.for_node(definiens_node);
1200 let source_info = self.stack_graph.source_info_mut(node_handle);
1201 source_info.definiens_span = definiens_span;
1202 Ok(())
1203 }
1204
1205 fn load_node_debug_info(
1206 &mut self,
1207 node_ref: GraphNodeRef,
1208 node_handle: Handle<Node>,
1209 ) -> Result<(), BuildError> {
1210 let node = &self.graph[node_ref];
1211 for (name, value) in node.attributes.iter() {
1212 let name = name.to_string();
1213 if name.starts_with(DEBUG_ATTR_PREFIX) {
1214 let value = match value {
1215 Value::String(value) => value.clone(),
1216 value => value.to_string(),
1217 };
1218 let key = self
1219 .stack_graph
1220 .add_string(&name[DEBUG_ATTR_PREFIX.len()..]);
1221 let value = self.stack_graph.add_string(&value);
1222 self.stack_graph
1223 .node_debug_info_mut(node_handle)
1224 .add(key, value);
1225 }
1226 }
1227 Ok(())
1228 }
1229
1230 fn load_edge_debug_info(
1231 stack_graph: &mut StackGraph,
1232 source_handle: Handle<Node>,
1233 sink_handle: Handle<Node>,
1234 edge: &Edge,
1235 ) -> Result<(), BuildError> {
1236 for (name, value) in edge.attributes.iter() {
1237 let name = name.to_string();
1238 if name.starts_with(DEBUG_ATTR_PREFIX) {
1239 let value = match value {
1240 Value::String(value) => value.clone(),
1241 value => value.to_string(),
1242 };
1243 let key = stack_graph.add_string(&name[DEBUG_ATTR_PREFIX.len()..]);
1244 let value = stack_graph.add_string(&value);
1245 stack_graph
1246 .edge_debug_info_mut(source_handle, sink_handle)
1247 .add(key, value);
1248 }
1249 }
1250 Ok(())
1251 }
1252
1253 fn verify_attributes(
1254 &self,
1255 node: &GraphNode,
1256 node_type: &str,
1257 allowed_attributes: &HashSet<&'static str>,
1258 ) {
1259 for (id, _) in node.attributes.iter() {
1260 let id = id.as_str();
1261 if !allowed_attributes.contains(id)
1262 && id != SOURCE_NODE_ATTR
1263 && id != EMPTY_SOURCE_SPAN_ATTR
1264 && !id.starts_with(DEBUG_ATTR_PREFIX)
1265 {
1266 eprintln!("Unexpected attribute {} on node of type {}", id, node_type);
1267 }
1268 }
1269 }
1270}
1271
1272pub trait FileAnalyzer {
1273 fn build_stack_graph_into<'a>(
1277 &self,
1278 stack_graph: &mut StackGraph,
1279 file: Handle<File>,
1280 path: &Path,
1281 source: &str,
1282 all_paths: &mut dyn Iterator<Item = &'a Path>,
1283 globals: &HashMap<String, String>,
1284 cancellation_flag: &dyn CancellationFlag,
1285 ) -> Result<(), BuildError>;
1286}