Skip to content

Commit

Permalink
python-infer: respect ignore pragma w/ strings (pantsbuild#20477)
Browse files Browse the repository at this point in the history
Ignores any strings that have the ignore pragma comment.

This used to work with the python-based parser, but apparently there
weren't tests so it was not carried into the rust-based parser.

Related: pantsbuild#20324, pantsbuild#20472
  • Loading branch information
cognifloyd authored Feb 2, 2024
1 parent cc9c523 commit a4fbfb8
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,14 @@ def test_imports_from_strings(rule_runner: RuleRunner, min_dots: int) -> None:
'a.2b.c.D',
'a.b.c_狗.',
# Explicitly ignored strings
'w.x', # pants: no-infer-dep
'w.x.Foo', # pants: no-infer-dep
'w.x.y.z', # pants: no-infer-dep
'w.x.y.z.Foo', # pants: no-infer-dep
'w.x.y.z.FooBar', # pants: no-infer-dep
'u.v.w.x.y.z.Baz', # pants: no-infer-dep
# Definitely invalid strings
'I/have/a/slash',
'I\\\\have\\\\backslashes',
Expand Down
47 changes: 39 additions & 8 deletions src/rust/engine/dep_inference/src/python/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,47 @@ impl ImportCollector<'_> {
.trim_matches(|c| "'\"".contains(c))
}

fn is_pragma_ignored_at_row(&self, node: tree_sitter::Node, end_row: usize) -> bool {
let node_range = node.range();
if node.kind_id() == KindID::COMMENT
&& end_row == node_range.start_point.row
&& self.code_at(node_range).contains("# pants: no-infer-dep")
{
return true;
}
false
}

fn is_pragma_ignored(&self, node: tree_sitter::Node) -> bool {
if let Some(sibling) = node.next_named_sibling() {
let next_node_range = sibling.range();
if sibling.kind_id() == KindID::COMMENT
&& node.range().end_point.row == next_node_range.start_point.row
&& self
.code_at(next_node_range)
.contains("# pants: no-infer-dep")
{
return self.is_pragma_ignored_at_row(sibling, node.range().end_point.row);
}
false
}

fn is_pragma_ignored_recursive(&self, node: tree_sitter::Node) -> bool {
let node_end_point = node.range().end_point;
if let Some(sibling) = node.next_named_sibling() {
if self.is_pragma_ignored_at_row(sibling, node_end_point.row) {
return true;
}
}

let mut current = node;
loop {
if let Some(parent) = current.parent() {
if let Some(sibling) = parent.next_named_sibling() {
if self.is_pragma_ignored_at_row(sibling, node_end_point.row) {
return true;
}
}
current = parent;
continue;
}
// At the root / no more parents.
break;
}

false
}

Expand Down Expand Up @@ -322,7 +351,9 @@ impl Visitor for ImportCollector<'_> {
fn visit_string(&mut self, node: tree_sitter::Node) -> ChildBehavior {
let range = node.range();
let text: &str = self.string_at(range);
if !text.contains(|c: char| c.is_ascii_whitespace() || c == '\\') {
if !text.contains(|c: char| c.is_ascii_whitespace() || c == '\\')
&& !self.is_pragma_ignored_recursive(node)
{
self.string_candidates
.insert(text.to_string(), (range.start_point.row + 1) as u64);
}
Expand Down
26 changes: 26 additions & 0 deletions src/rust/engine/dep_inference/src/python/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,32 @@ fn string_candidates() {
// Technically the value of the string doesn't contain whitespace, but the parser isn't that
// sophisticated yet.
assert_strings("'''\\\na'''", &[]);

// pragma ignored strings
assert_strings("'a' # pants: no-infer-dep", &[]);
assert_strings("'''a''' # pants: no-infer-dep", &[]);
assert_strings("'a.b' # pants: no-infer-dep", &[]);
assert_strings("'a.b.c_狗' # pants: no-infer-dep", &[]);
assert_strings("'..a.b.c.d' # pants: no-infer-dep", &[]);
assert_strings("['a.b'] # pants: no-infer-dep", &[]);
assert_strings("[{'a.b': 1}] # pants: no-infer-dep", &[]);
assert_strings("[{('a.b',): 1}] # pants: no-infer-dep", &[]);
assert_strings("[{2: 'a.b'}] # pants: no-infer-dep", &[]);
assert_strings("[{2: ('a.b',)}] # pants: no-infer-dep", &[]);
assert_strings("print('a.b') # pants: no-infer-dep", &[]);
assert_strings("print('a.b' if x else 3) # pants: no-infer-dep", &[]);
assert_strings("print(3 if x else 'a.b') # pants: no-infer-dep", &[]);
assert_strings(
"print([a for a in b if a not in ['a.b', 'c.d']]) # pants: no-infer-dep",
&[],
);
assert_strings(
r"
for a, b in foo['a.b'].items(): # pants: no-infer-dep
pass
",
&[],
);
}

#[test]
Expand Down

0 comments on commit a4fbfb8

Please sign in to comment.