views:

404

answers:

5

I don't think this is currently possible or if it's even a good idea, but it's something I was thinking about just now. I use MSTest for unit testing my C# project. In one of my tests, I do the following:

MyClass instance;

try
{
    instance = getValue();
}
catch (MyException ex)
{
    Assert.Fail("Caught MyException");
}

instance.doStuff(); // Use of unassigned local variable 'instance'

To make this code compile, I have to assign a value to instance either at its declaration or in the catch block. I could alternatively return after the Assert.Fail but that's still a workaround instead of the compiler just knowing that execution cannot continue after this point. Assert.Fail will never, to the best of my knowledge, allow execution to proceed past it, hence instance will never be used without a value. Why is it then that I must assign a value to it? If I change the Assert.Fail to something like throw ex, the code compiles fine, I assume because it knows that exception will disallow execution to proceed to a point where instance would be used uninitialized.

Contrariwise, what if I didn't want the test to fail, but rather be marked as inconclusive? I could do an Assert.Inconclusive instead of Fail, and it would be nice if the compiler knew execution would not continue after that.

So is it a case of runtime versus compile-time knowledge about where execution will be allowed to proceed? Would it ever be reasonable for C# to have some way of saying that a member, in this case Assert.Fail, will never allow execution after it returns? Maybe that could be in the form of a method attribute. Would this be useful or an unnecessary complexity for the compiler?

Outside Unit Tests

Since people are [validly] pointing out that this is a silly way to write a unit test, consider my question outside the realm of unit testing:

MyClass instance;

if (badThings)
{
    someMethodThatWillNeverReturn();
}
else
{
    instance = new MyClass();
}

instance.doStuff();

Here potentially I could replace the call to someMethodThatWillNeverReturn with throwing an exception, and perhaps if I had stuff to do, I could do it in the constructor for the exception.

Resharper Knows

If I add a return after Assert.Fail or Assert.Inconclusive, Resharper colors return gray and has a tooltip saying "Code is heuristically unreachable."

A: 

return after Assert.Fail()

might be annoying to write if the function is supposed to returned some object that doesn't have a default constructor.
redtuna
This is just another workaround like assigning `null` to `instance`. What I'm asking about is if the compiler could know that `Assert.Fail` would not continue execution after it's complete, so no `return` would be necessary.
Sarah Vessels
Kind of defeats the purpose, too, if the idea is to express the condition declaratively instead of imperatively.
harpo
I doubt you will find a way to let the compiler know that but, if you do, I'll be interested in hearing what it is.I suspect your best bet would be to encapsulate construction of an exception and throw that exception. So `throw MyFailer.GetFailure("The reason it failed")`.
+3  A: 

Yes, it would be reasonable to have something which indicated that a member would never complete normally - i.e. asserting that the point after the member was unreachable. (This could either be due to an exception or due to looping forever.)

You'd want there to be something (whether in the CLR or the compiler) to come up with a backup plan for if you're wrong: what would happen if someone changed Assert.Fail to return normally? You'd potentially want part of code verification to be something that checked it would never return normally.

I believe there's a blog post about this idea from someone in Microsoft... I'll see if I can find it.

In terms of syntax for representing it, while an attribute is an obvious idea, I quite like the idea of a return type of "never". Obviously that would potentially clash with existing "never" types, but hey...

In terms of its usefulness: the obvious workaround is to throw an exception immediately after the statement, but it's certainly annoying to have to do that. (It's generally better than a return as it means if the method you're writing this in has a return type, you don't have to specify a pointless return value - and you also don't need to make sure that all out parameters are assigned values.) So it's not required - but I think it would be nice. Whether it's the most important thing the C# team can do with their limited budget is a different matter - just to pre-empt Eric ;)

Jon Skeet
It seems like if there were some attribute like `NoReturn`, verification would be possible at compile-time. That's already the case, isn't it, since the compiler knows execution won't continue after a `throw` for example.
Sarah Vessels
@Sarah: But my point is that the compiler would be trusting that the method *really would* never return. What if it's a method in a third-party library? How could the compiler verify that that was never going to return?
Jon Skeet
The trouble that I think Jon is pointing to, is that unlike C or C++ (where if you change the header file after compiling a source file, all bets are off, so programmers learnt not to do that), compiled code units in .NET are not subject to the same rigour: if `Assert.Fail` is `noreturn` by virtue of, say, calling another a function that's `noreturn`, and you later recompile that function to no longer be `noreturn`, the system has a harder time of verifying that `Assert.Fail`'s `noreturn`-ness still holds (short of recompilation).
Chris Jester-Young
@Jon: oh, gotcha. Ha, what if such an attribute/`never` return type were only obeyed in local code? Let's just add another layer of complexity here, I'm sure the compiler writers are bored...
Sarah Vessels
@Jon: wouldn't it create some confusion? I mean, if it is does not have an obvious name, like `Assert.Fail()` but say `MyHelpers.DoSomething()` and the `noreturn` is "hidden" somewhere else I would be unable to tell by just looking at the above code if it works, if it compiles or what the control flow would be?
andras
@andras: Any function can throw an exception. What would the control flow be? :-P
Chris Jester-Young
@Chris: you are so d*mn right. :) Exceptions being "exceptional", I still don't expect them. :)
andras
@Chris: every call in my program can potentially be a source of an exception - now I would have to prepare for methods that just simply kill my program? :) (I'm just arguing, of course. :)
andras
I think it would actually be simpler for the compiler (or possibly runtime) to automatically insert code to throw an exception if the method ever actually *did* return.
Jon Skeet
The Netscape guys proposed a "never" return type for ECMAScript that had the desired semantics; such a method could be assumed to always either run forever or throw an exception. We have no plans to add such a type to C# or the CLR type system; it would be nice to have, but it doesn't actually buy you much.
Eric Lippert
Re: detecting this: sure, it's easy. We already have to implement code that detects if a non-void method has a reachable end point. A "never" method would simply be a void method that is not allowed to have a reachable end point or any return statements. That solves the problem at compile time. At runtime, it's the verifier's responsibility to ensure that methods actually implement their return type semantics correctly; the verifier could similarly determine that there are no return instructions and that the end point is not reachable.
Eric Lippert
@Chris: my main point is that if it is an attribute/something placed somewhere else and you look at the above code, you cannot even decide if you are looking at a compilable snippet. If it is ever implemented, perhaps it should be indicated at the call site as well. Just as you have to indicate that in case of ref and out params. It is just my preference, though. Sure, the compiler can figure out that instance will not end up uninitialized - but I am sorry, I can't, without some help.
andras
@andras: If you followed a non-returnable call with anything other than the end of a block, that would cause a compilation error just as it does with a return statement. I suspect it wouldn't *actually* cause confusion - but I agree with Eric that it wouldn't add very much.
Jon Skeet
@Jon: now I see the potential benefits of this for SO: a whole new steady stream of "why does it compile/why does it not compile" questions. :)
andras
+1  A: 

throw ex after Assert.Fail? Or, better, just remove the try/catch and Assert.Fail entirely in this case, and let the uncaught exception fail the unit test for you.

Cory Petosky
When you want to re-throw an exception, just use `throw` within the catch block, this will preserve the call stack. Also see: http://www.codinghorror.com/blog/2004/07/rethrowing-exceptions.html
Ronald
A: 

Seeing that you are asserting Fail on a thrown exception, why don't you either move the entire test into the try, or get rid of the try and let the thrown exception automatically Assert fail.

try
{
    MyClass instance;
    instance = MyClass.getValue();
    instance.doStuff();
}
catch (Exception ex)
{
    Assert.Fail("Caught MyException");
}

... or ...

MyClass instance;
instance = MyClass.getValue();
instance.doStuff();
Matthew Whited
Both of these solutions have different semantics than the original post.
NickLarsen
Yes, but the semantics of the original post are wrong, in terms of writing a good unit test.
Cory Petosky
@Nick: I agree they are different, but both examples show a way of getting a similar result without making fundamental changes to the language and runtime. Setting the value to null (or some other default) at the point of declaration would work as well.
Matthew Whited
+1  A: 

One thing I'm wondering about: How would the runtime treat this code if you'd manually compile and execute it? I believe it checks for errors in the IL code, maybe it catches this "error" as well... maybe not :)

mafutrct