-
-
Notifications
You must be signed in to change notification settings - Fork 123
Expand file tree
/
Copy pathas_compiler.rs
More file actions
89 lines (80 loc) · 3.17 KB
/
as_compiler.rs
File metadata and controls
89 lines (80 loc) · 3.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use super::{
compiler::NodeCompilationContext, node_compiler::NodeCompiler,
pattern_compiler::PatternCompiler, variable_compiler::VariableCompiler,
};
use crate::{problem::MarzanoQueryContext, split_snippet::split_snippet};
use anyhow::{anyhow, Result};
use grit_pattern_matcher::pattern::{Container, Match, Pattern, Predicate, Where};
use grit_util::{traverse, AnalysisLogBuilder, AstNode, Language, Order};
use marzano_util::{cursor_wrapper::CursorWrapper, node_with_source::NodeWithSource};
use std::path::Path;
pub(crate) struct AsCompiler;
impl NodeCompiler for AsCompiler {
// todo make `as` its own pattern
type TargetPattern = Where<MarzanoQueryContext>;
fn from_node_with_rhs(
node: &NodeWithSource,
context: &mut NodeCompilationContext,
_is_rhs: bool,
) -> Result<Self::TargetPattern> {
let pattern = node
.child_by_field_name("pattern")
.ok_or_else(|| anyhow!("missing pattern of patternWhere"))?;
let variable = node
.child_by_field_name("variable")
.ok_or_else(|| anyhow!("missing variable of patternWhere"))?;
let name = variable.text()?;
let name = name.trim();
// this just searches the subtree for a variables that share the name.
// could possible lead to some false positives, but more precise solutions
// require much greater changes.
if pattern_repeated_variable(&pattern, name, context.compilation.lang)? {
let range = node.range();
let log = AnalysisLogBuilder::default()
.level(441_u16)
.file(context.compilation.file.map(Path::to_owned))
.source(node.source)
.position(range.start)
.range(range)
.message(format!(
"Warning: it is usually incorrect to redefine a variable {name} using as"
))
.build()?;
context.logs.push(log);
}
let pattern = PatternCompiler::from_node(&pattern, context)?;
let variable = VariableCompiler::from_node(&variable, context)?;
Ok(Where::new(
Pattern::Variable(variable),
Predicate::Match(Box::new(Match::new(
Container::Variable(variable),
Some(pattern),
))),
))
}
}
fn pattern_repeated_variable(
pattern: &NodeWithSource,
name: &str,
lang: &impl Language,
) -> Result<bool> {
let cursor = pattern.node.walk();
let cursor = traverse(CursorWrapper::new(cursor, pattern.source), Order::Pre);
Ok(cursor
.filter(|n| n.node.kind() == "variable" || n.node.kind() == "codeSnippet")
.map(|n| {
let s = n.text()?.trim().to_string();
if n.node.kind() == "variable" {
Ok(s == name)
} else {
Ok(is_variables_in_snippet(name, &s, lang))
}
})
.collect::<Result<Vec<bool>>>()?
.into_iter()
.any(|b| b))
}
fn is_variables_in_snippet(name: &str, snippet: &str, lang: &impl Language) -> bool {
let variables = split_snippet(snippet, lang);
variables.iter().any(|v| v.1 == name)
}