Bugs revealed by X# in existing code - Part 2

In part 1 of this article series I have shown you 2 problems that we found in existing code (in our own code as well, and we thought that that code was perfect). Today we will discuss 3 other problems we have seen quite often. And yes, I found this in our own code as well.

Missing Assignment

Another common problem in existing code revealed by X# is with expressions that were unintentionally left incomplete, as demonstrated in this cut down sample:

#using System.Collections.Generic

FUNCTION Start() AS VOID
LOCAL
aMyDictionary AS Dictionary<STRING,STRING>
aMyDictionary := Dictionary<STRING,STRING>{}
aMyDictionary["myKey"] // incomplete code
// intended code was:
// aMyDictionary["myKey"] := “myValue”
? aMyDictionary["myKey"]

Vulcan did not report any error or warning about this, leading again to unintended behavior of the code at runtime (a runtime exception). Fortunately X# reports an error though: error XS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement which points us to the missing assignment in the code.

Missing arguments in _Or(), _And(), _XOr()

I was very surprised when I saw this problem in existing code, as both VO and Vulcan apparently allow _Or(), _And() and _XOr() operators to have only one argument! Something like that:

FUNCTION CalculateStyles(lReadOnly AS LOGIC , lMultiline AS LOGIC) AS DWORD
LOCAL
dwStyles := WS_CHILD AS DWORD
IF
lReadOnly
     dwStyles := _Or(dwStyles , ES_READONLY)
END IF
IF
lMultiline
     dwStyles := _Or(dwStyles) // missing argument
     // correct code should be:
     // dwStyles := _Or(dwStyles , ES_MULTILINE)
END IF
RETURN
dwStyles

Of course an _Or() expression with only one argument does not make sense, this is again a typo in the code, causing unintended behavior at runtime. Fortunately, the X# compiler is smart enough to catch this, too, reporting: error XS0839: Argument missing, revealing several such occurrences in code that we tested the compiler with.

Assignment instead of comparison

This is another common typo in code that is very difficult to notice without help from the compiler. Here is some sample code:

FUNCTION CalculateTAX(nPrice AS REAL8 , lReduced AS LOGIC) AS REAL8
     LOCAL nTax := nPrice * 0.25 AS REAL8
     IF lReduced := TRUE // typo, should use == instead
          nTax := nTax / 2.0
     END IF
RETURN
nTax

In the code above, instead of making a comparison to TRUE, the code accidentally always assigns TRUE to the lReduced parameter and the code inside the IF statement always executes, so the function always returns TRUE, no matter the value of the (LOGIC) argument passed to it! VO and Vulcan allowed this code to compile without warnings or errors, while X# pointed me also to that problem in the code to fix it, as it reported: warning XS0665: Assignment in conditional expression is always constant; did you mean to use == instead of := ?

This concludes our article for today. Next week, in part 3 of this article,  I will discuss some problems we found with Win32 api functions being called from managed code.


No comments