From 3a20d202516baae6328d98e0ddb543e2d22c9a6e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20C=2E=20Silva?=
<12188364+andrecsilva@users.noreply.github.com>
Date: Tue, 23 Jul 2024 14:59:20 -0300
Subject: [PATCH] Added line only matching option to XMLTransformer (#733)
---
src/codemodder/codemods/xml_transformer.py | 25 ++++++--
tests/test_xml_transformer.py | 69 +++++++++++++++++++++-
2 files changed, 87 insertions(+), 7 deletions(-)
diff --git a/src/codemodder/codemods/xml_transformer.py b/src/codemodder/codemods/xml_transformer.py
index b7715cd4..4ddec0e1 100644
--- a/src/codemodder/codemods/xml_transformer.py
+++ b/src/codemodder/codemods/xml_transformer.py
@@ -30,11 +30,13 @@ def __init__(
encoding: str = "utf-8",
short_empty_elements: bool = False,
results: list[Result] | None = None,
+ line_only_matching=False,
) -> None:
self.file_context = file_context
self.results = results
self.changes: list[Change] = []
self._my_locator = Locator()
+ self.line_only_matching = line_only_matching
super().__init__(out, encoding, short_empty_elements)
def startElement(self, name, attrs):
@@ -81,10 +83,15 @@ def match_result(self, line, column) -> bool:
return True
for result in self.results or []:
for location in result.locations:
- # No two elements can have the same start but different ends.
- # It suffices to only match the start.
- if location.start.line == line and location.start.column - 1 == column:
- return True
+ if self.line_only_matching:
+ return location.start.line == line
+ else:
+ # No two elements can have the same start but different ends.
+ # It suffices to only match the start.
+ return (
+ location.start.line == line
+ and location.start.column - 1 == column
+ )
return False
def add_change(self, line):
@@ -110,9 +117,17 @@ def __init__(
encoding: str = "utf-8",
short_empty_elements: bool = False,
results: list[Result] | None = None,
+ line_only_matching=False,
) -> None:
self.name_attributes_map = name_attributes_map
- super().__init__(out, file_context, encoding, short_empty_elements, results)
+ super().__init__(
+ out,
+ file_context,
+ encoding,
+ short_empty_elements,
+ results,
+ line_only_matching,
+ )
def startElement(self, name, attrs):
new_attrs: AttributesImpl = attrs
diff --git a/tests/test_xml_transformer.py b/tests/test_xml_transformer.py
index 8329863c..bd1c3192 100644
--- a/tests/test_xml_transformer.py
+++ b/tests/test_xml_transformer.py
@@ -13,6 +13,7 @@
NewElementXMLTransformer,
XMLTransformer,
)
+from codemodder.semgrep import SemgrepResult
class TestXMLTransformer:
@@ -56,10 +57,21 @@ def test_parse_cdata(self):
class TestElementAttributeXMLTransformer:
- def run_and_assert(self, name_attr_map, input_code, expected_output):
+ def run_and_assert(
+ self,
+ name_attr_map,
+ input_code,
+ expected_output,
+ results=None,
+ line_only_match=False,
+ ):
with StringIO() as result, StringIO(dedent(input_code)) as input_stream:
transformer = ElementAttributeXMLTransformer(
- result, mock.MagicMock(), name_attributes_map=name_attr_map
+ result,
+ mock.MagicMock(),
+ name_attributes_map=name_attr_map,
+ results=results,
+ line_only_matching=line_only_match,
)
parser = make_parser()
parser.setContentHandler(transformer)
@@ -97,6 +109,59 @@ def test_change_multiple_attr_and_preserve_existing(self):
name_attr_map = {"element": {"first": "one", "second": "two"}}
self.run_and_assert(name_attr_map, input_code, expected_output)
+ def test_change_only_line_matching_result(self):
+ input_code = """\
+
+
+
+
+
+
+ """
+ expected_output = """\
+
+
+
+
+
+
+ """
+ name_attr_map = {"element": {"first": "one"}}
+ data = {
+ "runs": [
+ {
+ "results": [
+ {
+ "fingerprints": {"matchBasedId/v1": "123"},
+ "locations": [
+ {
+ "ruleId": "rule",
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "code.py",
+ "uriBaseId": "%SRCROOT%",
+ },
+ "region": {
+ "snippet": {"text": "snip"},
+ "endColumn": 1,
+ "endLine": 5,
+ "startColumn": 1,
+ "startLine": 5,
+ },
+ },
+ }
+ ],
+ "ruleId": "rule",
+ }
+ ]
+ }
+ ]
+ }
+ sarif_run = data["runs"]
+ sarif_results = sarif_run[0]["results"]
+ results = [SemgrepResult.from_sarif(sarif_results[0], sarif_run)]
+ self.run_and_assert(name_attr_map, input_code, expected_output, results, True)
+
class TestNewElementXMLTransformer: