Repository: MartinThoma/flake8-simplify (2022-02-27 ~ 2022-03-28)

problem_statement: [Adjust Rule] SIM102: 'if __name__ == "__main__"' does not count
## Desired change

* **Rule(s)**: SIM102
* **Adjustment**: Ignore `if __name__ == "__main__"`

## Explanation

the check `if __name__ == "__main__"` is special. People recognize it as a special part of a script. Although one can combine things, I think it's better not to do it.

## Example

The following should not trigger SIM102

```python
if __name__ == "__main__":
    if foo(): ...
```

date: 2022-02-27
patch: diff --git a/flake8_simplify/rules/ast_if.py b/flake8_simplify/rules/ast_if.py
index 1756458..4136a26 100644
--- a/flake8_simplify/rules/ast_if.py
+++ b/flake8_simplify/rules/ast_if.py
@@ -9,7 +9,7 @@ from flake8_simplify.utils import get_if_body_pairs, is_body_same, to_source
 
 def get_sim102(node: ast.If) -> List[Tuple[int, int, str]]:
     """Get a list of all nested if-statements without else-blocks."""
-    SIM102 = "SIM102 Use a single if-statement instead of nested if-statements"
+    RULE = "SIM102 Use a single if-statement instead of nested if-statements"
     errors: List[Tuple[int, int, str]] = []
 
     # ## Pattern 1
@@ -31,7 +31,17 @@ def get_sim102(node: ast.If) -> List[Tuple[int, int, str]]:
 
     if not is_pattern_1:
         return errors
-    errors.append((node.lineno, node.col_offset, SIM102))
+    is_main_check = (
+        isinstance(node.test, ast.Compare)
+        and isinstance(node.test.left, ast.Name)
+        and node.test.left.id == "__name__"
+        and len(node.test.comparators) == 1
+        and isinstance(node.test.comparators[0], ast.Str)
+        and node.test.comparators[0].s == "__main__"
+    )
+    if is_main_check:
+        return errors
+    errors.append((node.lineno, node.col_offset, RULE))
     return errors
 
 

test_patch: diff --git a/tests/test_100_rules.py b/tests/test_100_rules.py
index 44ed391..17f4cf7 100644
--- a/tests/test_100_rules.py
+++ b/tests/test_100_rules.py
@@ -54,13 +54,20 @@ def test_sim102_not_active1():
     assert ret == set()
 
 
-def test_sim102_not_active2():
-    ret = _results(
+@pytest.mark.parametrize(
+    "s",
+    (
+        """if __name__ == "__main__":
+    if foo(): ...""",
         """if a:
     d
     if b:
-        c"""
-    )
+        c""",
+    ),
+    ids=("__main__", "intermediate-statement"),
+)
+def test_sim102_should_not_trigger(s):
+    ret = _results(s)
     assert ret == set()
 
 


----

problem_statement: [Adjust Rule] SIM111 - Detect if another statement comes after the loop
## Desired change

* **Rule(s)**: SIM111
* **Adjustment**: Detect if another statement comes after the loop

## Explanation

The below code is flagged with SIM111, but if I would would use the suggested code, the `call_method` call would be lost.

## Example

```python
for a in my_list:
  if a == 2:
    return False
call_method()
return True
```

date: 2022-03-28
patch: diff --git a/flake8_simplify/rules/ast_for.py b/flake8_simplify/rules/ast_for.py
index fcbac36..8d4d984 100644
--- a/flake8_simplify/rules/ast_for.py
+++ b/flake8_simplify/rules/ast_for.py
@@ -97,7 +97,7 @@ def get_sim110_sim111(node: ast.For) -> List[Tuple[int, int, str]]:
         return errors
     if not hasattr(node.body[0].body[0].value, "value"):
         return errors
-    if isinstance(node.next_sibling, ast.Raise):  # type: ignore
+    if not isinstance(node.next_sibling, ast.Return):  # type: ignore
         return errors
     check = to_source(node.body[0].test)
     target = to_source(node.target)

test_patch: diff --git a/tests/test_100_rules.py b/tests/test_100_rules.py
index 2164e31..fba08fe 100644
--- a/tests/test_100_rules.py
+++ b/tests/test_100_rules.py
@@ -498,3 +498,15 @@ def test_sim120():
     assert results == {
         "1:0 SIM120 Use 'class FooBar:' instead of 'class FooBar(object):'"
     }
+
+
+def test_sim111_false_positive():
+    results = _results(
+        """for a in my_list:
+  if a == 2:
+    return False
+call_method()
+return True"""
+    )
+    for el in results:
+        assert "SIM111" not in el


----

problem_statement: [Adjust Rule] Remove SIM116 due to inefficiency/code evaluation problem
## Desired change

* **Rule(s)**: SIM116
* **Adjustment**: Remove the enforcement of the rule #31 

## Explanation

This style guideline may result in better looking code, but it is inefficient and can even create errors. Each dict value is interpreted whenever the dict is evaluated.

## Example

```python
some_array = [0,1] if x else [0,1,2]
mapping = {
  "foo": "bar",
  "foo2": long_expensive_function()
  "foo3": three_arg_func(some_array[0], some_array[1], some_array[2])
}
return mapping.get("foo",42) # This will still evaluate "foo2", and if x is True, completely crash when evaluating "foo3"
```


date: 2022-03-28
patch: diff --git a/flake8_simplify/rules/ast_if.py b/flake8_simplify/rules/ast_if.py
index 6f2b74e..eacf557 100644
--- a/flake8_simplify/rules/ast_if.py
+++ b/flake8_simplify/rules/ast_if.py
@@ -275,6 +275,11 @@ def get_sim116(node: ast.If) -> List[Tuple[int, int, str]]:
             and len(child.orelse) <= 1
         ):
             return errors
+        return_call = child.body[0]
+        assert isinstance(return_call, ast.Return), "hint for mypy"
+        if isinstance(return_call.value, ast.Call):
+            # See https://github.com/MartinThoma/flake8-simplify/issues/113
+            return errors
         key: Any
         if isinstance(child.test.comparators[0], ast.Str):
             key = child.test.comparators[0].s

test_patch: diff --git a/tests/test_100_rules.py b/tests/test_100_rules.py
index 49d9c69..715263e 100644
--- a/tests/test_100_rules.py
+++ b/tests/test_100_rules.py
@@ -430,6 +430,21 @@ else:
     }
 
 
+def test_sim116_false_positive():
+    ret = _results(
+        """if a == "foo":
+    return "bar"
+elif a == "bar":
+    return baz()
+elif a == "boo":
+    return "ooh"
+else:
+    return 42"""
+    )
+    for el in ret:
+        assert "SIM116" not in el
+
+
 def test_sim117():
     ret = _results(
         """with A() as a: