I am using Visual Studio 2010 SP1 Ultimate on a c# class library project (.net 4) and I am curious about something...
Given this method:
public void DoSomethingBrilliant(int input)
{
if (input == int.MaxValue)
throw new ArgumentOutOfRangeException("input");
input++;
Console.WriteLine(input);
}
I get this warning from code analysis:
CA2233 : Microsoft.Usage : Correct the potential overflow in the operation 'input+1' in 'Test.DoSomethingBrilliant(int)'.
I thought to myself, that is a bit odd since I am checking that the input++
operation won't overflow by throwing that snazzy exception at the beginning but I changed it to this:
public void DoSomethingBrilliant(int input)
{
if (input >= int.MaxValue)
throw new ArgumentOutOfRangeException("input");
input++;
Console.WriteLine(input);
}
and sure enough the warning went away.
Now my little brain is all confused because given I am getting an int as an argument why would checking to see if it is greater than the maximum value allowed for an integer ever provide any value?
Then I went back to the original bit of code and switched to debug and it built without the warning! Curiouser and curioser...
I checked the differences between debug and release and found that if I tick the Optimize code option the warning from code analysis pops right back up.
So the optimization results in something that means I need to check for greater than int.MaxValue. Huh? Why? Am I being super dense? What has the optimization done that means I might get an int bigger than int.MaxValue passed into a method accepting an int?
Or, is this just a bug in the code analysis feature?
Update
Here is the IL for the "unoptimized" version (where the code analysis gets it right):
.method public hidebysig instance void DoSomethingBrilliant(int32 input) cil managed
{
// Code size 40 (0x28)
.maxstack 2
.locals init ([0] bool CS$4$0000)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldc.i4 0x7fffffff
IL_0007: ceq
IL_0009: ldc.i4.0
IL_000a: ceq
IL_000c: stloc.0
IL_000d: ldloc.0
IL_000e: brtrue.s IL_001b
IL_0010: ldstr "input"
IL_0015: newobj instance void [mscorlib]System.ArgumentOutOfRangeException::.ctor(string)
IL_001a: throw
IL_001b: ldarg.1
IL_001c: ldc.i4.1
IL_001d: add
IL_001e: starg.s input
IL_0020: ldarg.1
IL_0021: call void [mscorlib]System.Console::WriteLine(int32)
IL_0026: nop
IL_0027: ret
} // end of method Test::DoSomethingBrilliant
and here it is for the optimized version (where it gets it wrong):
.method public hidebysig instance void DoSomethingBrilliant(int32 input) cil managed
{
// Code size 31 (0x1f)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldc.i4 0x7fffffff
IL_0006: bne.un.s IL_0013
IL_0008: ldstr "input"
IL_000d: newobj instance void [mscorlib]System.ArgumentOutOfRangeException::.ctor(string)
IL_0012: throw
IL_0013: ldarg.1
IL_0014: ldc.i4.1
IL_0015: add
IL_0016: starg.s input
IL_0018: ldarg.1
IL_0019: call void [mscorlib]System.Console::WriteLine(int32)
IL_001e: ret
} // end of method Test::DoSomethingBrilliant
I see a bunch of extra calls before the throw operation but I am going to be honest - I have no idea what they do!
The MinValue property or Field of Int32 Struct is used to represent the minimum possible value of Int32. The value of this field is constant means that a user cannot change the value of this field. The value of this field is -2,147,483,648. Its hexadecimal value is 0x80000000.
The value of this constant is 2,147,483,647; that is, hexadecimal 0x7FFFFFFF.
Int16 is 16 bit, int32 is 32 bit and int64 is 64 bit.
Represents the largest possible value of an Int32. This field is constant. public: int MaxValue = 2147483647; C# Copy.
Or, is this just a bug in the code analysis feature?
Looks like it. Not terribly surprising, to be honest - getting this sort of code analysis to be perfect is mightily tricky. Given that any particular int
cannot be greater than int.MaxValue
, >=
and ==
are definitely equivalent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With