chidori_static_analysis/language/
mod.rs1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::hash::{Hash, Hasher};
4use petgraph::graph::DiGraph;
5use petgraph::graphmap::DiGraphMap;
6use petgraph::visit::EdgeRef;
7use thiserror::Error;
8
9pub mod typechecker;
10pub mod javascript;
11pub mod python;
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)]
14pub struct TextRange {
15 pub start: usize,
16 pub end: usize,
17}
18
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct ReportItem {
24 }
26
27#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
28pub struct ReportTriggerableFunctions {
29 pub arguments: Vec<String>,
30 pub emit_event: Vec<String>,
34 pub trigger_on: Vec<String>,
35}
36
37#[derive(Debug, Default, Clone)]
38pub struct InternalCallGraph {
39 graph: DiGraph<String, ()>
40}
41
42impl Serialize for InternalCallGraph {
43 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
44 where
45 S: serde::Serializer,
46 {
47 let mut adjacency_list = HashMap::new();
48 for node in self.graph.node_indices() {
49 let edges = self.graph.edges(node)
50 .map(|edge| edge.target().index())
51 .collect::<Vec<_>>();
52 adjacency_list.insert(self.graph[node].clone(), edges);
53 }
54 adjacency_list.serialize(serializer)
55 }
56}
57
58impl<'de> Deserialize<'de> for InternalCallGraph {
59 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60 where
61 D: serde::Deserializer<'de>,
62 {
63 let adjacency_list: HashMap<String, Vec<usize>> = HashMap::deserialize(deserializer)?;
64 let mut graph = DiGraph::new();
65 let mut node_indices = HashMap::new();
66
67 for node in adjacency_list.keys() {
69 let index = graph.add_node(node.clone());
70 node_indices.insert(node.clone(), index);
71 }
72
73 for (node, edges) in adjacency_list {
75 let source_index = node_indices[&node];
76 for target_index in edges {
77 if let Some(&target_node_index) = node_indices.values().find(|&&idx| idx.index() == target_index) {
78 graph.add_edge(source_index, target_node_index, ());
79 }
80 }
81 }
82
83 Ok(InternalCallGraph { graph })
84 }
85}
86
87impl PartialEq for InternalCallGraph {
88 fn eq(&self, other: &Self) -> bool {
89 self.graph.node_indices().all(|n| {
91 self.graph[n] == other.graph[n]
92 }) && self.graph.edge_indices().all(|e| {
93 self.graph.edge_endpoints(e) == other.graph.edge_endpoints(e)
94 })
95 }
96}
97
98impl Eq for InternalCallGraph {}
99
100impl Hash for InternalCallGraph {
101 fn hash<H: Hasher>(&self, state: &mut H) {
102 for node in self.graph.node_indices() {
104 self.graph[node].hash(state);
105 }
106 for edge in self.graph.edge_indices() {
107 self.graph.edge_endpoints(edge).hash(state);
108 }
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
113pub struct Report {
114 pub internal_call_graph: InternalCallGraph,
115 pub cell_exposed_values: HashMap<String, ReportItem>,
116 pub cell_depended_values: HashMap<String, ReportItem>,
117 pub triggerable_functions: HashMap<String, ReportTriggerableFunctions>,
118}
119
120
121#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
122pub enum ContextPath {
123 Initialized,
124 InFunction(String, TextRange),
125 InAnonFunction,
126 FunctionArguments,
127 FunctionArgument(String),
128 InClass(String),
129 InFunctionDecorator(usize),
130 InCallExpression,
131 ChName,
132 AssignmentToStatement,
133 AssignmentFromStatement,
134 IdentifierReferredTo {
136 name: String,
137 in_scope: bool,
138 exposed: bool
139 },
140 Attribute(String),
141 Constant(String),
142}
143
144
145#[derive(Error, Debug, Serialize, Deserialize)]
146pub enum ChidoriStaticAnalysisError {
147 #[error("Unknown chidori analysis error")]
148 Unknown,
149 #[error("Parse error at offset {offset} in {source_path}: {msg}. Source: {source_code}")]
150 ParseError {
151 msg: String,
152 offset: u32,
153 source_path: String,
154 source_code: String
155 },
156}
157
158
159