views:

146

answers:

4

I've just started experimenting with CodeContracts in .NET 4 on an existing medium-sized project, and I'm surprised that the static checker is giving me compile-time warnings about the following piece of code:

public class Foo
{
   private readonly List<string> strs = new List<string>();

   public void DoSomething()
   {
       // Compiler warning from the static checker:
       // "requires unproven: source != null"
       strs.Add("hello");
   }
}

Why is the CodeContracts static checker complaining about the strs.Add(...) line? There's no possible way for strs to be null, right? Am I doing something wrong?

A: 

I'm not knowledgeable enough with the intricacies of .NET's object initialization semantics to answer your direct question. However, here's two tips:

  1. Microsoft Research's Pex can automatically generate unit tests that demonstrate exactly under what conditions a contract violation might occur. It uses the same theorem prover and static analyzer as CC does, so it's a fair bet, the two will agree.
  2. Proving contracts is equivalent to solving the Halting Problem, so just because CC can't prove that it cannot ever be null, doesn't mean it isn't true. IOW: CC might just be wrong and you need to help it along with a Contract.Assume (or, if you're feeling confident, Contract.Assert).

Interestingly, if you explicitly add the Object Invariant Assertion that strs can never be null, CC is able to prove that and, consequently, can also prove that strs.Add() will never be a null reference:

[ContractInvariantMethod]
private void StrsIsNotNull()
{
    Contract.Invariant(strs != null);
}

So, I guess my hunch #2 is correct: CC is just simply wrong in this case. (Or more precisely: the encoding of the semantics of C# into the theorem prover is incomplete.)

Jörg W Mittag
"Proving contracts is equivalent to solving the Halting Problem" - only very generally. Meanwhile there are a ton of commonly encountered specific cases that can be proven. As Skeet says, if it can't figure out the above, that would be weird.
Daniel Earwicker
+2  A: 

The following is valid code, which I would expect to generate the warning.

public class Foo 
{ 
   private readonly List<string> strs = new List<string>(); 

   public Foo()
   {
       // strs is readonly, so we can assign it here but nowhere else
       strs = ResultOfSomeFunction();
   }

   public void DoSomething() 
   { 
       // Compiler warning from the static checker: 
       // "requires unproven: source != null" 
       strs.Add("hello"); 
   } 
}

It's quite possible that their analyzer doesn't go so far as to make sure that you have nothing in a constructor that changes the value of strs. Or maybe you are somehow changing strs in a constructor and you don't realize it.

Gabe
Did you have "Implicit Non-Null Obligations" checked?
adrift
+6  A: 

The field may be marked readonly, but unfortunately the static checker isn't smart enough for this. Therefore, as the static checker isn't able to infer on its own that strs is never null, you must explicitly tell it through an invariant:

[ContractInvariantMethod]
private void ObjectInvariant() {
    Contract.Invariant(strs != null);
}
Rich
The thing with Code Contracts is to always be explicit. It doesn't "know" anything you haven't told it.
Porges
+1  A: 

Little correction: Pex uses Z3, an SMT solver while Clousot (the static checker code name) uses abstract interpretation and abstract domains.

Peli