DEV Community

Rodion Gorkovenko
Rodion Gorkovenko

Posted on

Listen to Compiler!

Many programmers, especially juniors (or while yet studying) tend to ignore compiler warnings :)

I'd like to share two examples - first is "real-life" from some of my past jobs. Another is recent, asked at the forum on my hobby-website.

Alt Text

#1 Losing daily income reports :)

Some years ago I was working on project which used POS-terminals to collect payments bus rides from contactless cards. One of my colleagues was adapting new version of terminal to project and, as it seemd, did this successfully. Then he went to other project (banking).

Suddenly, when this new terminal was tried in real conditions, horrible problem was found. It was sending money report in the end of the day (over built-in cell-phone like modem) - but often this report was never seen at the receiving server! And terminal, after sending it, was deleting report from internal storage.

Our client was losing money reports! In worst case some of client's employees could end in prison for fraud despite being really innocent!

I spent about a day to find what was wrong. It was comically simple:

// in terminal API all modem functions
// return negative value on error, like this

/** returns number of bytes sent or negative error code */
int MODEM_SendData(unsigned char* buffer, int count);

// in the code my colleague did the following

unsigned int result = MODEM_SendData(fileData, fileLength);
if (result < 0) {
    reportError("Sending report failed");

So he was storing operation result in variable which could only have non-negative values - and of course negative error values were lost.

I found he deliberately turned off compiler warnings, shouting that result < 0 will never be true - when I asked him, he told "Ha-ha, I didn't understand what it is about, thought it is some silly useless message, breaking compilation". (we used "treat warning as error" policy)

So this was about careless types treating, regretfully it is easy in C++ and languages like Java or Go try maniacally prevent such errors.

#2 Java Generics and Raw types

Second example is in Java and it came from this forum thread (find somewhere below, post by "Radovan Markus"). Colleague complains that:

I recieved this kind of error when I clicked compile with "java"

"Note: uses unchecked or unsafe operations.Note: Recompile with -Xlint:unchecked for details."

Code contains things like this:

import java.util.ArrayList;
import javafx.util.Pair;

// ...

ArrayList<Pair<Integer,Integer>> primes = new ArrayList<>();

// ...

inputNumbers.add(new Pair(2, 1));

Here situation is more subtle. Though details (and line numbers) could be seen if compiler is executed with -Xlint:unchecked flag as the message suggests.

The matter is that new Pair(2, 1) creates Pair object of "raw type", with types of components erased. This is allowed for compatibility with older code when java had no type parameters (before 1.5). To correct this we should indicate we want Pair with specific types:

new Pair<Integer, Integer>(2, 1);

// or, since java 1.7, indicate it is typed, but implicitly:
new Pair<>(2,1);

In some of later lines such correction will lead to error:

long number;
int index;
// ...
inputNumbers.add(new Pair<>(number, index));

it is because typed Pair<Integer, Integer> don't want to hold long, unless it is explicitly converted:

inputNumbers.add(new Pair<>((int)number, index));

This may seem capricious because code works anyway. But it works only by luck (as operations used won't break on finding Long instead of Integer inside the pair).

However with types erased we could put completely different type inside, e.g. String:

inputNumbers.add(new Pair("2", 1));

This will compile nicely (just with warning like above), but will break in runtime if value from this pair is used in some calculation (but won't break if it is used just for printing).

In enterprise projects such errors may remain unnoticed for long time, if the code with flaw is not covered with tests or error condition is not met usually.

That is why strictly typed languages are sometimes preferred for enterprise projects - for sake of improving reliability - compiler can find out and prevent some kinds of mistakes, which are inevitable when many people work on the same large code through months and years.

Sorry for that many letters and thanks for reading that far :)

Top comments (0)