views:

52

answers:

2

I have a problem with code contracts and linq. I managed to narrow the issue to the following code sample. And now I am stuck.

public void SomeMethod()
{
    var list = new List<Question>();

    if (list.Take(5) == null) { }
    // resharper hints that condition can never be true

    if (list.ForPerson(12) == null) { }
    // resharper does not hint that condition can never be true
}

public static IQueryable<Question> ForPerson(this IQueryable<Question> source, int personId)
{
    if(source == null) throw new ArgumentNullException();

    return from q in source
           where q.PersonId == personId
           select q;
}

What is wrong with my linq chain? Why doesn't resharper 'complain' when analyzing the ForPerson call?

EDIT: return type for ForPerson method changed from string to IQueryable, which I meant. (my bad)

+3  A: 

Reshaper is correct that the result of a Take or Skip is never null - if there are no items the result is an IEnumerable<Question> which has no elements. I think to do what you want you should check Any.

var query = list.Take(5);
if (!query.Any())
{
    // Code here executes only if there were no items in the list.
}

But how does this warning work? Resharper cannot know that the method never returns null from only looking at the method definition, and I assume that it does not reverse engineer the method body to determine that it never returns null. I assume therefore that it has been specially hard-coded with a rule specifying that the .NET methods Skip and Take do not return null.

When you write your own custom methods Reflector can make assumptions about your method behaviour from the interface, but your interface allows it to return null. Therefore it issues no warnings. If it analyzed the method body then it could see that null is impossible and would be able to issue a warning. But analyzing code to determine its possible behaviour is an incredibly difficult task and I doubt that Red Gate are willing to spend the money on solving this problem when they could add more useful features elsewhere with a much lower development cost.

To determine whether a boolean expression can ever return true is called the Boolean satisfiability problem and is an NP-hard problem.

You want Resharper to determine whether general method bodies can ever return null. This is a generalization of the above mentioned NP-hard problem. It's unlikely any tool will ever be able to do this correctly in 100% of cases.

Mark Byers
Thanks for your swift answer. The return type should have been IQueryable<Question>. I changed this in my question. The problem is that resharpers is NOT complaining. It should because ForPeson will never return null.
Florian
Thanks for this. +1
ewwwyn
@Mark: actually, it's my understanding that ReSharper _has_ reverse-engineered the various methods, and _then_ hard-codes the values determined through reverse-engineering. The result is that it sometimes arrives at false conclusions, like that `XmlReader.Create` can return null. There's one very obscure edge case where it could.
John Saunders
@John Saunders: I just had a look at the code for XmlReader.Create in Reflector and I'm not surprised ReSharper has trouble. The difficulty seems to be the private method it calls: `XmlReader.AddWrapper`. In one special case it can return one of its parameters as the result. To know whether the result can or cannot ever return null you'd have to know if that parameter could ever be null. And this is a really simple case. I bet you could create even more evil examples with mutually recursive functions.
Mark Byers
@Mark: it's actually worse than that. Deciding whether a method returns `null` is equivalent to solving the halting problem.
Porges
A: 
if(source == null) throw new ArgumentNullException(); 

That's not the code contract way, do you instead mean:

Contract.Require(source != null);
David B
No, that is not what I meant. You could substitute 'if (list.ForPerson(12) == null)' by something like 'Contract.Requires(list.ForPerson(12) != null)' and you have the same problem. Which is actualy the problem I experience. Since more people use Resharper than Code Contracts, I figured to ask the question in de Resharper-context...
Florian
I see, question is tagged code contracts and not tagged resharper so I was confused. Anyhoo - this scenario should be supported by code contracts.
David B