Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

invariant_booleans: False positives and negatives with repeated conditions. #57643

Closed
rkirsling opened this issue Oct 21, 2017 · 15 comments
Closed
Assignees
Labels
analyzer-linter Issues with the analyzer's support for the linter package area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. linter-false-positive P4 type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@rkirsling
Copy link

rkirsling commented Oct 21, 2017

In 2.0.0-dev.4.0, invariant_booleans behaves erratically when the same condition occurs multiple times in a function.

First, checking the value of a field before and after updating it can cause a false positive:

class Counter {
  int count = 0;
  void increment() { count++; }
}

void test1() {
  final counter = new Counter();
  if (counter.count > 0) return;

  counter.increment();
  if (counter.count > 0) return; // false positive here

  print('hello');
}

The choices of > 0 and ++ for comparison and assignment don't matter, and int can be replaced with a different numerical type. Even String with += 's' will repro.

Now, if we modify our test function to use a ternary, the problem goes away...

int test2() {
  final counter = new Counter();
  if (counter.count > 0) return 1;

  counter.increment();
  return counter.count > 0 ? 1 : 0; // true negative here
}

...but don't rejoice just yet, because if we keep going we hit a false negative!

class FixedCounter {
  final int count = 0;
}

int test3() {
  final counter = new FixedCounter();
  if (counter.count > 0) return 1;
  return counter.count > 0 ? 1 : 0; // false negative here
}

And even without the ternary, if we use a boolean field, invariant_booleans ironically doesn't care how many times we check it. 😆

class A {
  final bool foo = false;
}

void test4() {
  final a = new A();
  if (a.foo) return;
  if (a.foo) return; // false negative here

  print('hello');
}

Follow-up: It didn't occur to me earlier, but the false positive seems to hinge on the first conditional being an early return.

@pq
Copy link
Member

pq commented Oct 23, 2017

cc @alexeieleusis

@alexeieleusis alexeieleusis self-assigned this Oct 23, 2017
@lexaknyazev
Copy link
Contributor

Same issue here (also 2.0.0-dev.4.0):

void main() {
  final bar = 0;
  final foo = 10;

  for (var i = 0; i < foo; ++i) {}

  for (var i = 0; i < foo; ++i) {} // invariant_booleans: i < foo 

  if (bar == 10) {}
}

@kevmoo
Copy link
Member

kevmoo commented Feb 15, 2018

Hitting this as well. @alexeieleusis ? @pq ?

@pq pq added the type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) label Feb 16, 2018
@pq
Copy link
Member

pq commented Feb 16, 2018

Sorry, no update just yet. Hoping to set aside some time to look in the coming week.

cc @bwilkerson

@alexeieleusis
Copy link
Contributor

I have a fix for the bug itself, some other tests broke though. I don't know yet if the problem are the tests themselves, will upload the progress I have if I can't finish it before Tuesday (2/20).

@pq
Copy link
Member

pq commented Feb 20, 2018

Awesome. Thanks @alexeieleusis !

@rkirsling
Copy link
Author

rkirsling commented Feb 20, 2018

While @alexeieleusis' PR targets the case @lexaknyazev demonstrated above, it doesn't address a single one of the cases in the issue description (none of which include a for loop).

Please reopen this issue (as I evidently don't have the permissions to).

@bwilkerson bwilkerson reopened this Feb 20, 2018
@alexeieleusis
Copy link
Contributor

We decided when the rule was implemented that false negatives like the one produced by invoking counter.increment() in the first examples would exists and the cost of detecting those was not worth the effort of implementing them (if possible at all, sounds dangerously close to the halting problem) neither the cost of computing it.

The rule detects early returns and does not use those conditions, as @rkirsling points out.

As for ternary operators, sounds like a missing implementation.

I am unsure about adding it, the reason being that I haven't found a single true positive in code in the real world. The idea behind this rule was to catch debugging leftovers, which in my case, are always caught by literal_only_boolean_expressions.

Thoughts?

@pq
Copy link
Member

pq commented Feb 20, 2018

cc @bwilkerson

@rkirsling
Copy link
Author

That makes sense—I mentioned the false negatives so as to give the most complete description of the problem that I could, but it's really just that the false positive is plaguing me in actual code. 😄

@alexeieleusis
Copy link
Contributor

Looks like you are writing an interpreter, I wonder how useful this lint is for you. I have the impression that performance constraints require changes undetectable for the lint to be common place.

@rkirsling
Copy link
Author

As a policy, I've been keeping as many lint rules enabled as possible. If I disagree with a rule, I'll turn it off, but I don't disagree with invariant_booleans, I've just noticed that it's reporting a problem where there isn't one. It's okay that it can't detect every real problem. 😅

@pingbird
Copy link

Reproduced:

void main() {
  Foo()
    ..a() // prints 'true'
    ..a()
    ..a(); // prints 'false'
}

class Foo {
  int x = 1;

  void a() {
    if (x + 2 > 2) {
      b();
      // Produces an invariant_booleans warning
      if (x + 2 <= 2) {
        print('true');
        return;
      } else {
        print('false');
      }
    }
    x += 2;
  }

  void b() {
    x--;
  }
}

@alexeieleusis
Copy link
Contributor

I think the lint to flag a case like the one above is too ambitious, the most common use case I have (in any language) is forcing a condition to be true or false for debugging purposes, in such cases I use constants (with a FIXME comment) to avoid committing that change. Catching a case like the one in the example above can become too expensive rendering the lint unusable (think of the code changing a property inside an object and the code is performed in a different library).

@srawlins
Copy link
Member

We've deprecated invariant_booleans and will not be fixing correctness bugs going forward.

@devoncarew devoncarew added analyzer-linter Issues with the analyzer's support for the linter package area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. labels Nov 18, 2024
@devoncarew devoncarew transferred this issue from dart-lang/linter Nov 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
analyzer-linter Issues with the analyzer's support for the linter package area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. linter-false-positive P4 type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

9 participants