Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[UP031] When encountering
"%s" % var
offer unsafe fix (#11019)
Resolves #10187 <details> <summary>Old PR description; accurate through commit e86dd7d; probably best to leave this fold closed</summary> ## Description of change In the case of a printf-style format string with only one %-placeholder and a variable at right (e.g. `"%s" % var`): * The new behavior attempts to dereference the variable and then match on the bound expression to distinguish between a 1-tuple (fix), n-tuple (bug 🐛), or a non-tuple (fix). Dereferencing is via `analyze::typing::find_binding_value`. * If the variable cannot be dereferenced, then the type-analysis routine is called to distinguish only tuple (no-fix) or non-tuple (fix). Type analysis is via `analyze::typing::is_tuple`. * If any of the above fails, the rule still fires, but no fix is offered. ## Alternatives * If the reviewers think that singling out the 1-tuple case is too complicated, I will remove that. * The ecosystem results show that no new fixes are detected. So I could probably delete all the variable dereferencing code and code that tries to generate fixes, tbh. ## Changes to existing behavior **All the previous rule-firings and fixes are unchanged except for** the "false negatives" in `crates/ruff_linter/resources/test/fixtures/pyupgrade/UP031_1.py`. Those previous "false negatives" are now true positives and so I moved them to `crates/ruff_linter/resources/test/fixtures/pyupgrade/UP031_0.py`. <details> <summary>Existing false negatives that are now true positives</summary> ``` crates/ruff_linter/resources/test/fixtures/pyupgrade/UP031_0.py:134:1: UP031 Use format specifiers instead of percent format | 133 | # UP031 (no longer false negatives) 134 | 'Hello %s' % bar | ^^^^^^^^^^^^^^^^ UP031 135 | 136 | 'Hello %s' % bar.baz | = help: Replace with format specifiers crates/ruff_linter/resources/test/fixtures/pyupgrade/UP031_0.py:136:1: UP031 Use format specifiers instead of percent format | 134 | 'Hello %s' % bar 135 | 136 | 'Hello %s' % bar.baz | ^^^^^^^^^^^^^^^^^^^^ UP031 137 | 138 | 'Hello %s' % bar['bop'] | = help: Replace with format specifiers crates/ruff_linter/resources/test/fixtures/pyupgrade/UP031_0.py:138:1: UP031 Use format specifiers instead of percent format | 136 | 'Hello %s' % bar.baz 137 | 138 | 'Hello %s' % bar['bop'] | ^^^^^^^^^^^^^^^^^^^^^^^ UP031 | = help: Replace with format specifiers ``` One of them newly offers a fix. ``` # UP031 (no longer false negatives) -'Hello %s' % bar +'Hello {}'.format(bar) ``` This fix occurs because the new code dereferences `bar` to where it was defined earlier in the file as a non-tuple: ```python bar = {"bar": y} ``` --- </details> ## Behavior requiring new tests Additionally, we now handle a few cases that we didn't previously test. These cases are when a string has a single %-placeholder and the righthand operand to the modulo operator is a variable **which can be dereferenced.** One of those was shown in the previous section (the "dereference non-tuple" case). <details> <summary>New cases handled</summary> ``` crates/ruff_linter/resources/test/fixtures/pyupgrade/UP031_0.py:126:1: UP031 [*] Use format specifiers instead of percent format | 125 | t1 = (x,) 126 | "%s" % t1 | ^^^^^^^^^ UP031 127 | # UP031: deref t1 to 1-tuple, offer fix | = help: Replace with format specifiers crates/ruff_linter/resources/test/fixtures/pyupgrade/UP031_0.py:130:1: UP031 Use format specifiers instead of percent format | 129 | t2 = (x,y) 130 | "%s" % t2 | ^^^^^^^^^ UP031 131 | # UP031: deref t2 to n-tuple, this is a bug | = help: Replace with format specifiers ``` One of these offers a fix. ``` t1 = (x,) -"%s" % t1 +"{}".format(t1[0]) # UP031: deref t1 to 1-tuple, offer fix ``` The other doesn't offer a fix because it's a bug. --- </details> --- </details> ## Changes to existing behavior In the case of a string with a single %-placeholder and a single ambiguous righthand argument to the modulo operator, (e.g. `"%s" % var`) the rule now fires and offers a fix. We explain about this in the "fix safety" section of the updated documentation. ## Documentation changes I swapped the order of the "known problems" and the "examples" sections so that the examples which describe the rule are first, before the exceptions to the rule are described. I also tweaked the language to be more explicit, as I had trouble understanding the documentation at first. The "known problems" section is now "fix safety" but the content is largely similar. The diff of the documentation changes looks a little difficult unless you look at the individual commits.
- Loading branch information