Bug 176283 - [compiler][null] Null pointer problems are reported where no problem actually exists
Summary: [compiler][null] Null pointer problems are reported where no problem actually...
Status: RESOLVED WONTFIX
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.3   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.3 M6   Edit
Assignee: Maxime Daniel CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-03-03 08:56 EST by Ed Merks CLA
Modified: 2007-03-22 16:56 EDT (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ed Merks CLA 2007-03-03 08:56:29 EST
The new problem reporting support for null pointers is extremely handy and has found a number of problems that need attention.  But it is also over aggressive about reporting problems that aren't actual problems.  This simple example illustrates simple versions of such over aggressive problem reporting:

public class Example
{
  boolean condition()
  {
    return true;
  }

  void assignmentAndAccessGuardedBySameGuard()
  {
    Object x = null;
    if (condition())
    {
      x = new Object();
    }
    if (condition())
    {
      System.out.println(x.hashCode());
    }
  }

  void equivalentGuard(List<?> list)
  {
    Object x = null;
    boolean y = false;
    for (Object z : list)
    {
      if (z != null)
      {
        x = z;
        y = true;
      }
    }
    if (y)
    {
      System.out.println(x.hashCode());
    }
  }
  
  void anotherEquivalentGuard(String s)
  {
    int length = s == null ? 0 : s.length();
    if (length != 0)
    {
      System.out.println(s.charAt(length - 0));
    }
  }
}

If problems are reported for code that's logically correct, the support for the problem reporting becomes less useful...
Comment 1 Philipe Mulet CLA 2007-03-03 09:34:12 EST
Ed - what exactly are you seeing which you do not like ?

The idea is that with the new pref separation, definite threats are separated from potential threats. If you are complaining about false definite threats, then this is clearly an issue we need to consider. If only, a false potential threats, then we have a few scenarii which we are aware of in this space, and there is no easy fix without introducing ways of tagging the code (likely using new annotations, ala JSR305).
Comment 2 Ed Merks CLA 2007-03-03 11:46:25 EST
Philippe,

I'm not sure what you mean by "new pref separation".  Is there some preference control beyond M5eh that I don't see with M5eh?  Suppose I wanted to treat definite threats as an error, while I wanted to ignore potential threats or perhaps treat them only as a warning.  Is that possible?
Comment 3 Philipe Mulet CLA 2007-03-03 22:46:28 EST
It just got added since M5, you should see it in last integration build I believe.
Comment 4 Maxime Daniel CLA 2007-03-05 03:49:02 EST
Ed, the newest null related options were released for 3.3 M6 indeed (bug 170704 for JDT Core, bug 175496 for JDT UI), hence they are available starting with I20070228-0930. They allow for separate error levels to be set on 'Null reference', 'Potential null reference' and 'Redundant null check'. The options names in the UI may still change (see bug 175830) but the semantics should remain the same.

Having said that, copying your example with comments:
public class Example
{
  boolean condition()
  {
    return true;
  }

  void assignmentAndAccessGuardedBySameGuard()
  {
    Object x = null;
    if (condition())
    {
      x = new Object();
    }
    if (condition())
    {
      System.out.println(x.hashCode()); // a) x may be null
    }
  }

  void equivalentGuard(List<?> list)
  {
    Object x = null;
    boolean y = false;
    for (Object z : list)
    {
      if (z != null)
      {
        x = z;
        y = true;
      }
    }
    if (y)
    {
      System.out.println(x.hashCode()); // b) x may be null
    }
  }

  void anotherEquivalentGuard(String s)
  {
    int length = s == null ? 0 : s.length();
    if (length != 0)
    {
      System.out.println(s.charAt(length - 0)); // c) s may be null
    }
  }
}

and considering each case separately:
- c) is a case of variables correlation in which length controls s; while the 
  most general cases of variables correlation is out of scope for performance    
  reasons (the compiler cannot afford the CPU and memory that more aggressive 
  static analysis tools devote to such problems), we have listed some patterns  
  that we may address at some point in time (not in 3.3 though); this peculiar
  case is tracked by bug 125319;
- b) is a variables correlation case again; it is more complex than any of the 
  patterns we consider, because the relationship between y and x being null  
  (or not) is typically conditioned by their presence in the same block and 
  involves scenario playing of some sorts (or other techniques similarly 
  complex); I do not foresee such class of analysis making its way in JDT plans;
- a) is even more agressive; while the code of condition is readily available to
  the reader if the code is complete, a class may extend Example and get 
  condition() return different values over time; even if we made condition 
  final, we would need to determine that it always returns true and prune the
  flow of control graph accordingly; again, I fear that this is out of JDT scope
  for the foreseable future.

As Philippe suggested above, the 'maybe null' series is far less affirmative about the runtime behavior that the 'can only be null' (resp. 'cannot be null')  series of warnings. In the former context, we emit a warning whenever the code shades doubts upon the considered variable. In the latter context, we try hard to only complain on situations where we *know* the null status of a given variable, but under the constraint that we do not perform general variables correlation, scenario exploration, or inter-procedural analysis.
Please do not hesitate to fill in new bugs if you find situations in which we do not fulfill those objectives.
Comment 5 Ed Merks CLA 2007-03-22 14:43:26 EDT
Guys,

I was surprised to get a warning about this one:

  public AdapterFactory getFactoryForTypes(Collection<?> types)
  {
    FactoryLoop: 
    for (AdapterFactory factory : adapterFactories)
    {
      if (factory instanceof ComposedAdapterFactory)
      {
        factory = ((ComposedAdapterFactory)factory).getFactoryForTypes(types);
        if (factory != null)
        {
          return factory;
        }
      }
      else 
      {
        for (Object type : types)
        {
          if (!factory.isFactoryForType(type))
          {
            continue FactoryLoop;
          }
        }
        return factory;
      }
    }
    
    if (adapterFactoryDescriptorRegistry != null)
    {
      Descriptor descriptor = adapterFactoryDescriptorRegistry.getDescriptor(types);
      if (descriptor != null)
      {
        AdapterFactory result = descriptor.createAdapterFactory();
        addAdapterFactory(result);
        return result;
      }
    }   
    
    return delegatedGetFactoryForTypes(types);
  }

The null check *after* the assignment in the "then" branch should really not have any impact on the "else" branch.

Is this something that could be fixed?  I'll change my code to avoid this, so it's not a really important thing for me.  I'm very happy with the "for sure" problem and the "potential" problem split we have now...
Comment 6 Maxime Daniel CLA 2007-03-22 16:30:38 EDT
Ed, seems we miss the fact that the for changes factory at each iteration. Would you please open a specific bug for that case? I would then close this one again, since it discusses the issue in a wider perspective.

Simplified test case:
  void foo(List<Object> l, boolean b) {
	for (Object o: l) {
	  if (b) {
		if (o != null) {
		  return;
		}
	  } else {
		System.out.println(o.toString()); // warning here
	  }
	}
  }

Contrast with:
  void bar(List<Object> l, boolean b) {
	Iterator<Object> i = l.iterator();
	Object o;
	while (i.hasNext()) {
	  o = i.next();
	  if (b) {
		if (o != null) {
		  return;
		}
	  } else {
		System.out.println(o.toString()); // silent
	  }
	}
  }
Comment 7 Ed Merks CLA 2007-03-22 16:56:09 EDT
Bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=178895 was opened.  The general problem can't be fixed.