Unreachable code in C# for loop – intelligenter compiler
Thursday, March 20th, 2008Hallo,
kürzlich hat mich Microsofts C# compiler mit seiner intelligenten Analyse verblüfft. Ich hatte sinngemäß folgenden code geschrieben:
for (int jj = 0; jj < parts.Length - 1; ++jj)
{
part = parts[jj];
int count = result.getRefCount(refAttr);
for (int ii = 0; ii < count; ++ii)
{
EOInstance packagedElement = result.getRefObject(refAttr, ii);
if (packagedElement.getValue("Name") == part)
{
result = packagedElement;
continue;
}
}
string msg = "Cannot resolve path '" + parts.ToString() +
"' for attribute '" + refAttr + ". Unknown part is '" +
parts + "'.";
throw (new Exceptions.UnknownPathException(msg));
}
Der C# compiler meldete:
Fehler CS0162: Warnung als Fehler: Unerreichbarer Code wurde entdeckt.
und die IDE markierte die Inkrement-Anweisung ‘++jj in der äußeren for-Schleife.
Nach konzentrierter Durchsicht des codes ist mit der Fehler aufgefallen: die throw Anweisung sollte in die innere Schleife verschoben werden. So wie der code geschrieben war, wurde die äußere Schleife tatsächlich nur einmal durchlaufen und die Inkrement-Anweisung nie ausgeführt da immer am Ende des ersten Durchlaufs eine exception geworfen wurde.
Der C++ compiler von Visual Studio findet dieses Problem nicht, auch nicht im release build (wo der compiler weitergehende semantische Prüfungen machen kann unter Ausnutzung der Analyse-Ergebnisse des optimizers).
UnreachableCode - 0 Fehler, 0 Warnung(en) ==== Erstellen: 1 erfolgreich, Fehler bei 0, 0 aktuell, 0 übersprungen ====
Bleibt noch die Frage, was PC-Lint (das ich seit mehr als 10 Jahren für alle C++-Entwicklungen verwende) zu der Sache meint.
Enttäuschenderweise meldet es diesen Fehler auch nicht, nicht mal mit zwei Durchläufen (-passes(2)):
PC-lint for C/C++ (NT) Vers. 8.00w, Copyright Gimpel Software 1985-2007 --- Module: UnreachableCode.cpp (C++) /// Start of Pass 2 /// --- Module: UnreachableCode.cpp (C++) --- Global Wrap-up error 900: (Note -- Successful completion, 0 messages produced)
Für diese Test habe ich folgenden mutmaßlich äquivalenten code verwendet:
for (int jj = 0; jj < 10; ++jj)
{
for (int ii = 0; ii < 10; ++ii)
{
if (ii == 5)
{
continue;
}
}
throw(std::exception("Huh"));
}
Also wirklich eine beeindruckende Leistung des C# compilers.
Der code enthält übrigens noch einen weiteren logischen Fehler, auf den ich erst durch die Meldung des C# compilers aufmerksam wurde, was aber für die Intention dieses postings nicht relevant ist.
Viele Grüße,
Andreas
Technorati Tags: C#, compiler, FlexeLint, PC-Lint
Nachtrag vom 04.05.2008: ein kluger Kollege hat mich darauf hingewiesen, dass Microsoft Visual C++ bei Verwendung von warning level 4 den gewünschten Hinweis tatsächlich meldet. Für meine Produktionssoftware verwende ich /W4, für das kleine Beispielprogramm habe ich es aber bei den defaults belassen.