
Sie erinnern sich vielleicht an die kürzliche Ankündigung eines neuen statischen Analysators für Go namens go-kritisch .
Ich habe das Golang / Go- Projekt damit überprüft und einige Patches gesendet, die einige dort gefundene Probleme beheben.
In diesem Artikel werden wir den korrigierten Code analysieren und motiviert sein, noch mehr solcher Änderungen an Go zu senden.
Für die Ungeduldigsten: eine aktualisierte Liste der Trophäen .
Gehen Sie zur Parsing-Liste dupSubExpr
Wir alle machen Fehler und oft durch Unaufmerksamkeit. Go ist eine Sprache, in der Sie manchmal langweiligen Code und Boilerplate-Code schreiben müssen. Manchmal trägt dies zu Tippfehlern und / oder Fehlern beim Kopieren / Einfügen bei.
CL122776 enthält eine Korrektur für einen von dupSubExpr gefundenen Fehler :
func (a partsByVarOffset) Less(i, j int) bool { - return varOffset(a.slots[a.slotIDs[i]]) < varOffset(a.slots[a.slotIDs[i]]) + return varOffset(a.slots[a.slotIDs[i]]) < varOffset(a.slots[a.slotIDs[j]]) // ^__________________________________^ }
Achten Sie auf den Index links und rechts. Vor der Korrektur waren LHS und RHS des Operators <
identisch, und dupSubExpr
arbeitete dupSubExpr
.
Wenn Ihr Projekt von einem Versionskontrollsystem gesponsert wird , sollten Sie den Code nicht deaktivieren, indem Sie ihn in einen Kommentar einschließen, sondern ihn vollständig löschen. Es gibt Ausnahmen, aber häufiger stört, verwirrt und kann ein solcher "toter" Code Fehler verbergen.
commentOutCode konnte ein so interessantes Fragment finden ( CL122896 ):
switch arch.Family {
Es gibt einen etwas höheren Kommentar:
Wenn Sie zum Zweig go1.4
wechseln und diese drei Zeilen aus dem Kommentar entfernen, wird der Code nicht kompiliert. Wenn Sie sie jedoch im Assistenten auskommentieren, funktioniert alles.
In der Regel muss in einem Kommentar ausgeblendeter Code entweder gelöscht oder umgekehrt aktiviert werden.
Es ist von Zeit zu Zeit nützlich, solche Echos der Vergangenheit in Ihrem Code zu besuchen.
Über die Schwierigkeiten bei der ErkennungDies ist einer meiner Lieblingsschecks, aber einer der lautesten.
Viele Fehlalarme für Pakete, die math/big
und sich im Compiler befinden. Im ersten Fall handelt es sich normalerweise um erläuternde Kommentare zu den ausgeführten Operationen und im zweiten um eine Beschreibung des Codes, der das AST-Fragment beschreibt. Es ist nicht trivial, solche Kommentare von echtem "toten" Code zu unterscheiden, ohne falsche Negative einzuführen.
Daraus ergibt sich die Idee: Was ist, wenn wir uns darauf einigen, den Code in den Kommentaren irgendwie zu spezialisieren, was erklärend ist? Dann wird die statische Analyse vereinfacht. Dies kann eine Kleinigkeit sein, die es entweder einfach macht, einen solchen erklärenden Kommentar zu definieren, oder ihn zu einem ungültigen Go-Code macht (wenn Sie beispielsweise ein Nummernzeichen #
am Zeilenanfang hinzufügen).
Eine weitere Kategorie sind Kommentare mit expliziten TODO
. Wenn der Code für einen Kommentar entfernt wird, aber es eine klare Beschreibung gibt, warum dies getan wird und wann geplant ist, diesen Code zu reparieren, ist es besser, keine Warnung zu geben. Dies wurde bereits implementiert, könnte aber zuverlässiger funktionieren.
boolExprSimplify
Manchmal schreiben Leute seltsamen Code. Vielleicht scheint es mir, aber logische ( boolesche ) Ausdrücke sehen manchmal besonders seltsam aus.
Go hat ein exzellentes x86-Assembler-Backend (hier ist ein Riss gefallen), aber ARM hat wirklich falsch gemacht:
if !(o1 != 0) { break }
"Wenn nicht, ist o1 nicht gleich 0" ... Doppelte Negation ist ein Klassiker. Wenn es Ihnen gefallen hat, lade ich Sie ein, sich mit dem CL123377 vertraut zu machen. Dort sehen Sie die korrigierte Version.
Korrigierte Option (für diejenigen, die nicht zur Überprüfung gelockt werden können) - if !(o1 != 0) { + if o1 == 0 {
boolExprSimplify zielt auf Vereinfachungen ab, die die Lesbarkeit verbessern (und das Go-Optimierungsprogramm hätte das Problem der Leistung ohne es bewältigt).
underef
Wenn Sie Go aus früheren Versionen verwenden, können Sie sich an die obligatorischen Semikolons, das Fehlen einer automatischen Dereferenzierung von Zeigern und andere Funktionen erinnern, die heute im neuen Code kaum mehr zu sehen sind.
Im alten Code sehen Sie immer noch Folgendes:
In CL122895 wurden mehrere Trigger des Underef- Analysators behoben .
appendCombine
Möglicherweise wissen Sie, dass das append
mehrere Argumente als Elemente zum Hinzufügen zum Ziel-Slice verwenden kann. In einigen Situationen können Sie so die Lesbarkeit des Codes geringfügig verbessern. Dies kann jedoch interessanter sein und Ihr Programm beschleunigen, da der Compiler keine kompatiblen append
Aufrufe unterdrückt ( cmd / compile: Append-Aufrufe kombinieren ).
In Go hat die appendCombine- Prüfung den folgenden Abschnitt gefunden:
- for i := len(ip) - 1; i >= 0; i-- { - v := ip[i] - buf = append(buf, hexDigit[v&0xF]) - buf = append(buf, '.') - buf = append(buf, hexDigit[v>>4]) - buf = append(buf, '.') - } + for i := len(ip) - 1; i >= 0; i-- { + v := ip[i] + buf = append(buf, hexDigit[v&0xF], + '.', + hexDigit[v>>4], + '.') + }
name old time/op new time/op delta ReverseAddress-8 4.10µs ± 3% 3.94µs ± 1% -3.81% (p=0.000 n=10+9)
Details in CL117615 .
rangeValCopy
Es ist kein Geheimnis, dass die in einer range
iterierten Werte kopiert werden. Bei kleinen Objekten, beispielsweise weniger als 64 Byte, bemerken Sie dies möglicherweise nicht einmal. Wenn ein solcher Zyklus jedoch auf einem „heißen“ Pfad liegt oder, über den Sie iterieren, eine sehr große Anzahl von Elementen enthält, kann der Overhead spürbar sein.
Go hat einen ziemlich langsamen Linker (cmd / link), und ohne wesentliche Änderungen in seiner Architektur kann kein starker Leistungsgewinn erzielt werden. Aber dann können Sie die Ineffizienz mithilfe von Mikrooptimierungen leicht reduzieren. Jedes oder zwei Prozent zählt.
Bei der Überprüfung von rangeValCopy wurden mehrere Zyklen mit gleichzeitigem Kopieren unerwünschter Daten festgestellt. Hier sind die interessantesten von ihnen:
- for _, r := range exports.R { - d.mark(r.Sym, nil) - } + for i := range exports.R { + d.mark(exports.R[i].Sym, nil) + }
Anstatt R[i]
bei jeder Iteration R[i]
zu kopieren, wenden wir uns nur an das einzige für uns interessante Mitglied, Sym
.
name old time/op new time/op delta Linker-4 530ms ± 2% 521ms ± 3% -1.80% (p=0.000 n=17+20)
Die Vollversion des Patches ist verfügbar unter: CL113636 .
namedConst
In Go sind benannte Konstanten, die sogar zu Gruppen zusammengefasst sind, leider nicht miteinander verbunden und bilden keine Aufzählung ( Vorschlag: spec: Unterstützung für typisierte Aufzählungen hinzufügen ).
Ein Problem besteht darin, untypisierte Konstanten in den Typ umzuwandeln , den Sie als Aufzählung verwenden möchten.
Angenommen, Sie definieren einen const ColDefault Color = 0
Wert const ColDefault Color = 0
.
Welches dieser beiden Codefragmente gefällt Ihnen besser?
Wenn Ihnen (B)
angemessener erscheint, können Sie durch Überprüfen von namedConst die Verwendung benannter Konstantenwerte verfolgen und die benannte Konstante selbst umgehen.
So wurde die context.mangle
Methode aus dem html/template
Paket transformiert:
s := templateName + "$htmltemplate_" + c.state.String() - if c.delim != 0 { + if c.delim != delimNone { s += "_" + c.delim.String() } - if c.urlPart != 0 { + if c.urlPart != urlPartNone { s += "_" + c.urlPart.String() } - if c.jsCtx != 0 { + if c.jsCtx != jsCtxRegexp { s += "_" + c.jsCtx.String() } - if c.attr != 0 { + if c.attr != attrNone { s += "_" + c.attr.String() } - if c.element != 0 { + if c.element != elementNone { s += "_" + c.element.String() } return s
Übrigens finden Sie manchmal auf den Links zu den Patches interessante Diskussionen ...
CL123376 ist ein solcher Fall.
unslice
Ein Merkmal des Slice-Ausdrucks ist, dass x[:]
immer mit x
identisch ist, wenn Typ x
ein Slice oder eine Zeichenfolge ist. Im Fall von Slices funktioniert dies für jede Art von []T
Element.
Alles in der Liste unten ist das gleiche ( x
- Slice):
unslice findet ähnliche redundante Slice-Ausdrücke. Diese Ausdrücke sind vor allem mit einer zusätzlichen kognitiven Belastung schädlich. x[:]
hat eine ziemlich bedeutende Semantik, wenn ein Slice aus dem Array genommen wird. Ein Slice-Slice mit Standardbereichen bewirkt nichts anderes als Rauschen.
Ich bitte um einen Patch in CL123375 .
switchTrue
In CL123378 wird " switch true {...}
" durch " switch {...}
" ersetzt.
Beide Formen sind gleichwertig, aber die zweite ist idiomatischer.
Gefunden durch Überprüfen von switchTrue .
Die meisten Stilprüfungen zeigen genau solche Fälle, in denen beide Optionen akzeptabel sind, aber eine davon ist häufiger und mehr Go-Programmierern bekannt. Nächster Check aus derselben Serie.
typeUnparen
Go liebt, wie viele andere Programmiersprachen, Klammern. So sehr, dass ich bereit bin, eine beliebige Anzahl von ihnen zu akzeptieren:
type ( t0 int t1 (int) t2 ((int))
Aber was passiert, wenn Sie gofmt
?
type ( t0 int t1 (int)
Deshalb gibt es typeUnparen . Er findet im Programm alle Typausdrücke, in denen Sie die Anzahl der Klammern reduzieren können. Ich habe versucht, CL123379 zu senden. Mal sehen, wie die Community dies akzeptiert.
Lisp mag keine ZahnspangenIm Gegensatz zu C-ähnlichen Sprachen ist es in Lisp nicht so einfach, an keiner Stelle nutzlose Klammern einzufügen. In Sprachen, deren Syntax auf S-Ausdrücken basiert, ist es schwieriger als in einigen anderen Sprachen, ein Programm zu schreiben, das nur eine große Anzahl von Klammern enthält.
Go-Kritiker unterwegs

Wir haben nur einen kleinen Teil der durchgeführten Kontrollen untersucht. Gleichzeitig werden sich Quantität und Qualität erst im Laufe der Zeit weiterentwickeln, auch dank der Menschen, die sich der Entwicklung angeschlossen haben .
go-kritisch ist für jede Nutzung absolut kostenlos ( MIT-Lizenz ) und auch offen für Ihre Teilnahme an der Entwicklung des Projekts. Senden Sie uns Ideen für Überprüfungen, Sie können sofort mit der Implementierung, gemeldete Fehler und Mängel melden, Ihre Eindrücke teilen. Sie können auch Projekte zur Prüfung vorschlagen oder über Ihre Go-Code-Überprüfung berichten. Diese Erfahrung ist für uns von unschätzbarem Wert.
Gehen Sie zum beitragenden Thema
Haben Sie den Artikel Go Contribution Workshop in Russland gesehen ? Dieser Herbst wird die zweite Runde sein. Und dieses Mal werden wir neben einem erfolgreicheren Format und Sponsoren eine Geheimwaffe haben - einen wunderbaren statischen Analysator. Es wird überhaupt genug Beiträge geben!
Tatsächlich können Sie jetzt beginnen (obwohl es besser ist - etwas später, nach dem Einfrieren des Codes ). Wenn Sie es schaffen, sich vor dem nächsten Workshop wohl zu fühlen, wird es sehr cool, weil wir in Russland sehr wenig Mentoren haben.