qlty_analysis/lang/
python.rs1use crate::code::File;
2use crate::code::{child_source, node_source};
3use crate::lang::Language;
4use tree_sitter::Node;
5
6const CLASS_QUERY: &str = r#"
7(class_definition
8 name: (identifier) @name) @definition.class
9"#;
10
11const FUNCTION_DECLARATION_QUERY: &str = r#"
12(function_definition
13 name: (identifier) @name
14 parameters: (_) @parameters) @definition.function
15"#;
16
17const FIELD_QUERY: &str = r#"
18(
19 (attribute
20 object: (identifier) @receiver
21 attribute: (_) @name) @field
22 (#eq? @receiver "self")
23)
24"#;
25
26pub struct Python {
27 pub class_query: tree_sitter::Query,
28 pub field_query: tree_sitter::Query,
29 pub function_declaration_query: tree_sitter::Query,
30}
31
32impl Python {
33 pub const SELF: &'static str = "self";
34
35 pub const ATTRIBUTE: &'static str = "attribute";
36 pub const BOOLEAN: &'static str = "boolean_operator";
37 pub const BREAK: &'static str = "break_statement";
38 pub const CALL: &'static str = "call";
39 pub const COMMENT: &'static str = "comment";
40 pub const CONDITIONAL_EXPRESSION: &'static str = "conditional_expression"; pub const CONTINUE: &'static str = "continue_statement";
42 pub const ELIF: &'static str = "elif_clause";
43 pub const ELSE: &'static str = "else_clause";
44 pub const EXCEPT: &'static str = "except_clause";
45 pub const FOR: &'static str = "for_statement";
46 pub const FUNCTION: &'static str = "function_definition";
47 pub const IDENTIFIER: &'static str = "identifier";
48 pub const IF: &'static str = "if_statement";
49 pub const LAMBDA: &'static str = "lambda";
50 pub const MATCH: &'static str = "match_statement";
51 pub const MODULE: &'static str = "module";
52 pub const RETURN: &'static str = "return_statement";
53 pub const STRING: &'static str = "string";
54 pub const WHILE: &'static str = "while_statement";
55
56 pub const AND: &'static str = "and";
57 pub const OR: &'static str = "or";
58}
59
60impl Default for Python {
61 fn default() -> Self {
62 let language = tree_sitter_python::language();
63
64 Self {
65 class_query: tree_sitter::Query::new(&language, CLASS_QUERY).unwrap(),
66 function_declaration_query: tree_sitter::Query::new(
67 &language,
68 FUNCTION_DECLARATION_QUERY,
69 )
70 .unwrap(),
71 field_query: tree_sitter::Query::new(&language, FIELD_QUERY).unwrap(),
72 }
73 }
74}
75
76impl Language for Python {
77 fn name(&self) -> &str {
78 "python"
79 }
80
81 fn self_keyword(&self) -> Option<&str> {
82 Some(Self::SELF)
83 }
84
85 fn class_query(&self) -> &tree_sitter::Query {
86 &self.class_query
87 }
88
89 fn function_declaration_query(&self) -> &tree_sitter::Query {
90 &self.function_declaration_query
91 }
92
93 fn field_query(&self) -> &tree_sitter::Query {
94 &self.field_query
95 }
96
97 fn constructor_names(&self) -> Vec<&str> {
98 vec!["__init__"]
99 }
100
101 fn destructor_names(&self) -> Vec<&str> {
102 vec!["__del__"]
103 }
104
105 fn is_instance_method(&self, file: &File, node: &Node) -> bool {
106 let parameters = node.child_by_field_name("parameters").unwrap();
107
108 if let Some(first_parameter) = parameters.named_child(0) {
109 first_parameter.kind() == Self::IDENTIFIER
110 && node_source(&first_parameter, file) == Self::SELF
111 } else {
112 false
113 }
114 }
115
116 fn if_nodes(&self) -> Vec<&str> {
117 vec![Self::IF]
118 }
119
120 fn elsif_nodes(&self) -> Vec<&str> {
121 vec![Self::ELIF]
122 }
123
124 fn else_nodes(&self) -> Vec<&str> {
125 vec![Self::ELSE]
126 }
127
128 fn conditional_assignment_nodes(&self) -> Vec<&str> {
129 vec![]
130 }
131
132 fn invisible_container_nodes(&self) -> Vec<&str> {
133 vec![Self::MODULE]
134 }
135
136 fn switch_nodes(&self) -> Vec<&str> {
137 vec![Self::MATCH]
138 }
139
140 fn case_nodes(&self) -> Vec<&str> {
141 vec!["case_clause"]
142 }
143
144 fn ternary_nodes(&self) -> Vec<&str> {
145 vec![Self::CONDITIONAL_EXPRESSION]
146 }
147
148 fn loop_nodes(&self) -> Vec<&str> {
149 vec![Self::FOR, Self::WHILE]
150 }
151
152 fn except_nodes(&self) -> Vec<&str> {
153 vec![Self::EXCEPT]
154 }
155
156 fn try_expression_nodes(&self) -> Vec<&str> {
157 vec![]
158 }
159
160 fn jump_nodes(&self) -> Vec<&str> {
161 vec![Self::BREAK, Self::CONTINUE]
162 }
163
164 fn return_nodes(&self) -> Vec<&str> {
165 vec![Self::RETURN]
166 }
167
168 fn binary_nodes(&self) -> Vec<&str> {
169 vec![Self::BOOLEAN]
170 }
171
172 fn boolean_operator_nodes(&self) -> Vec<&str> {
173 vec![Self::AND, Self::OR]
174 }
175
176 fn field_nodes(&self) -> Vec<&str> {
177 vec![Self::ATTRIBUTE]
178 }
179
180 fn call_nodes(&self) -> Vec<&str> {
181 vec![Self::CALL]
182 }
183
184 fn function_nodes(&self) -> Vec<&str> {
185 vec![Self::FUNCTION]
186 }
187
188 fn closure_nodes(&self) -> Vec<&str> {
189 vec![Self::LAMBDA]
190 }
191
192 fn comment_nodes(&self) -> Vec<&str> {
193 vec![Self::COMMENT]
194 }
195
196 fn string_nodes(&self) -> Vec<&str> {
197 vec![Self::STRING]
198 }
199
200 fn all_operators(&self) -> Vec<&str> {
201 vec![Self::ATTRIBUTE, Self::IDENTIFIER]
209 }
210
211 fn all_operands(&self) -> Vec<&str> {
212 vec![
213 "false",
214 "float",
215 "identifier",
216 "integer",
217 "none",
218 "string",
219 "true",
220 ]
221 }
222
223 fn is_decorator_function(&self, node: &Node) -> bool {
224 let body = node.child_by_field_name("body").unwrap();
225
226 if body.named_child_count() == 2 {
227 let first_child = body.named_child(0).unwrap();
228 let second_child = body.named_child(1).unwrap();
229
230 first_child.kind() == Self::FUNCTION && second_child.kind() == Self::RETURN
231 } else {
232 false
233 }
234 }
235
236 fn iterator_method_identifiers(&self) -> Vec<&str> {
237 vec!["filter", "map", "any", "all"]
238 }
239
240 fn call_identifiers(&self, source_file: &File, node: &Node) -> (Option<String>, String) {
241 let function_node = node.child_by_field_name("function").unwrap();
242 let function_kind = function_node.kind();
243
244 match function_kind {
245 Self::IDENTIFIER => (
246 Some(Self::SELF.to_string()),
247 node_source(&function_node, source_file),
248 ),
249 Self::ATTRIBUTE => {
250 let (receiver, object) = self.field_identifiers(source_file, &function_node);
251
252 (Some(receiver), object)
253 }
254 _ => (Some("<UNKNOWN>".to_string()), "<UNKNOWN>".to_string()),
255 }
256 }
257
258 fn field_identifiers(&self, source_file: &File, node: &Node) -> (String, String) {
259 (
260 child_source(node, "object", source_file),
261 child_source(node, "attribute", source_file),
262 )
263 }
264
265 fn tree_sitter_language(&self) -> tree_sitter::Language {
266 tree_sitter_python::language()
267 }
268}
269
270#[cfg(test)]
271mod test {
272 use super::*;
273 use std::collections::HashSet;
274 use tree_sitter::Tree;
275
276 #[test]
277 fn mutually_exclusive() {
278 let lang = Python::default();
279 let mut kinds: Vec<&str> = vec![];
280
281 kinds.extend(lang.if_nodes());
282 kinds.extend(lang.else_nodes());
283 kinds.extend(lang.conditional_assignment_nodes());
284 kinds.extend(lang.switch_nodes());
285 kinds.extend(lang.case_nodes());
286 kinds.extend(lang.ternary_nodes());
287 kinds.extend(lang.loop_nodes());
288 kinds.extend(lang.except_nodes());
289 kinds.extend(lang.try_expression_nodes());
290 kinds.extend(lang.jump_nodes());
291 kinds.extend(lang.return_nodes());
292 kinds.extend(lang.binary_nodes());
293 kinds.extend(lang.field_nodes());
294 kinds.extend(lang.call_nodes());
295 kinds.extend(lang.function_nodes());
296 kinds.extend(lang.closure_nodes());
297 kinds.extend(lang.comment_nodes());
298 kinds.extend(lang.string_nodes());
299 kinds.extend(lang.boolean_operator_nodes());
300
301 let unique: HashSet<_> = kinds.iter().cloned().collect();
302 assert_eq!(unique.len(), kinds.len());
303 }
304
305 #[test]
306 fn field_identifier_read() {
307 let source_file = File::from_string("python", "self.foo");
308 let tree = source_file.parse();
309 let root_node = tree.root_node();
310 let expression = root_node.named_child(0).unwrap();
311 let field = expression.named_child(0).unwrap();
312 let language = Python::default();
313
314 assert_eq!(
315 language.field_identifiers(&source_file, &field),
316 ("self".to_string(), "foo".to_string())
317 );
318 }
319
320 #[test]
321 fn field_identifier_write() {
322 let source_file = File::from_string("python", "self.foo = 1");
323 let tree = source_file.parse();
324 let root_node = tree.root_node();
325 let expression = root_node.named_child(0).unwrap();
326 let assignment = expression.named_child(0).unwrap();
327 let field = assignment.named_child(0).unwrap();
328 let language = Python::default();
329
330 assert_eq!(
331 language.field_identifiers(&source_file, &field),
332 ("self".to_string(), "foo".to_string())
333 );
334 }
335
336 #[test]
337 fn field_identifier_collaborator() {
338 let source_file = File::from_string("python", "other.foo");
339 let tree = source_file.parse();
340 let root_node = tree.root_node();
341 let expression = root_node.named_child(0).unwrap();
342 let field = expression.named_child(0).unwrap();
343 let language = Python::default();
344
345 assert_eq!(
346 language.field_identifiers(&source_file, &field),
347 ("other".to_string(), "foo".to_string())
348 );
349 }
350
351 #[test]
352 fn call_identifier() {
353 let source_file = File::from_string("python", "foo()");
354 let tree = source_file.parse();
355 let call = call_node(&tree);
356 let language = Python::default();
357
358 assert_eq!(
359 language.call_identifiers(&source_file, &call),
360 (Some("self".to_string()), "foo".to_string())
361 );
362 }
363
364 #[test]
365 fn call_attribute() {
366 let source_file = File::from_string("python", "foo.bar()");
367 let tree = source_file.parse();
368 let call = call_node(&tree);
369 let language = Python::default();
370
371 assert_eq!(
372 language.call_identifiers(&source_file, &call),
373 (Some("foo".to_string()), "bar".to_string())
374 );
375 }
376
377 #[test]
378 fn decorator() {
379 let source_file = File::from_string(
380 "python",
381 r#"
382 def foo():
383 def bar():
384 pass
385 return bar
386 "#,
387 );
388 let tree = source_file.parse();
389 let root_node = tree.root_node();
390 let function = root_node.named_child(0).unwrap();
391 let language = Python::default();
392
393 assert_eq!(language.is_decorator_function(&function), true);
394 }
395
396 #[test]
397 fn not_decorator() {
398 let source_file = File::from_string(
399 "python",
400 r#"
401 def foo():
402 x = 1 # Invalidates decorator exception
403 def bar():
404 pass
405 return bar
406 "#,
407 );
408 let tree = source_file.parse();
409 let root_node = tree.root_node();
410 let function = root_node.named_child(0).unwrap();
411 let language = Python::default();
412
413 assert_eq!(language.is_decorator_function(&function), false);
414 }
415
416 fn call_node(tree: &Tree) -> Node {
417 let root_node = tree.root_node();
418 let expression = root_node.named_child(0).unwrap();
419 expression.named_child(0).unwrap()
420 }
421}