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.