|
21 | 21 | from pylint import checkers
|
22 | 22 | from pylint.checkers import utils
|
23 | 23 | from pylint.checkers.utils import node_frame_class
|
24 |
| -from pylint.interfaces import HIGH, INFERENCE |
| 24 | +from pylint.interfaces import HIGH, INFERENCE, Confidence |
25 | 25 |
|
26 | 26 | if TYPE_CHECKING:
|
27 | 27 | from pylint.lint import PyLinter
|
@@ -2110,6 +2110,12 @@ def _check_unnecessary_list_index_lookup(
|
2110 | 2110 | # destructured, so we can't necessarily use it.
|
2111 | 2111 | return
|
2112 | 2112 |
|
| 2113 | + has_start_arg, confidence = self._enumerate_with_start(node) |
| 2114 | + if has_start_arg: |
| 2115 | + # enumerate is being called with start arg/kwarg so resulting index lookup |
| 2116 | + # is not redundant, hence we should not report an error. |
| 2117 | + return |
| 2118 | + |
2113 | 2119 | iterating_object_name = node.iter.args[0].name
|
2114 | 2120 | value_variable = node.target.elts[1]
|
2115 | 2121 |
|
@@ -2191,13 +2197,61 @@ def _check_unnecessary_list_index_lookup(
|
2191 | 2197 | "unnecessary-list-index-lookup",
|
2192 | 2198 | node=subscript,
|
2193 | 2199 | args=(node.target.elts[1].name,),
|
2194 |
| - confidence=HIGH, |
| 2200 | + confidence=confidence, |
2195 | 2201 | )
|
2196 | 2202 |
|
2197 | 2203 | for subscript in bad_nodes:
|
2198 | 2204 | self.add_message(
|
2199 | 2205 | "unnecessary-list-index-lookup",
|
2200 | 2206 | node=subscript,
|
2201 | 2207 | args=(node.target.elts[1].name,),
|
2202 |
| - confidence=HIGH, |
| 2208 | + confidence=confidence, |
2203 | 2209 | )
|
| 2210 | + |
| 2211 | + def _enumerate_with_start( |
| 2212 | + self, node: nodes.For | nodes.Comprehension |
| 2213 | + ) -> tuple[bool, Confidence]: |
| 2214 | + """Check presence of `start` kwarg or second argument to enumerate. |
| 2215 | +
|
| 2216 | + For example: |
| 2217 | +
|
| 2218 | + `enumerate([1,2,3], start=1)` |
| 2219 | + `enumerate([1,2,3], 1)` |
| 2220 | +
|
| 2221 | + If `start` is assigned to `0`, the default value, this is equivalent to |
| 2222 | + not calling `enumerate` with start. |
| 2223 | + """ |
| 2224 | + confidence = HIGH |
| 2225 | + |
| 2226 | + if len(node.iter.args) > 1: |
| 2227 | + # We assume the second argument to `enumerate` is the `start` int arg. |
| 2228 | + # It's a reasonable assumption for now as it's the only possible argument: |
| 2229 | + # https://docs.python.org/3/library/functions.html#enumerate |
| 2230 | + start_arg = node.iter.args[1] |
| 2231 | + start_val, confidence = self._get_start_value(start_arg) |
| 2232 | + if start_val is None: |
| 2233 | + return False, confidence |
| 2234 | + return not start_val == 0, confidence |
| 2235 | + |
| 2236 | + for keyword in node.iter.keywords: |
| 2237 | + if keyword.arg == "start": |
| 2238 | + start_val, confidence = self._get_start_value(keyword.value) |
| 2239 | + if start_val is None: |
| 2240 | + return False, confidence |
| 2241 | + return not start_val == 0, confidence |
| 2242 | + |
| 2243 | + return False, confidence |
| 2244 | + |
| 2245 | + def _get_start_value(self, node: nodes.NodeNG) -> tuple[int | None, Confidence]: |
| 2246 | + confidence = HIGH |
| 2247 | + |
| 2248 | + if isinstance(node, (nodes.Name, nodes.Call)): |
| 2249 | + inferred = utils.safe_infer(node) |
| 2250 | + start_val = inferred.value if inferred else None |
| 2251 | + confidence = INFERENCE |
| 2252 | + elif isinstance(node, nodes.UnaryOp): |
| 2253 | + start_val = node.operand.value |
| 2254 | + else: |
| 2255 | + start_val = node.value |
| 2256 | + |
| 2257 | + return start_val, confidence |
0 commit comments