1. 程式人生 > 其它 >Clang除錯診斷資訊Expressive Diagnostics

Clang除錯診斷資訊Expressive Diagnostics

Clang除錯診斷資訊Expressive Diagnostics

除了快速和實用,目標是使 Clang使用者友好。命令列編譯器,使編譯器生成的診斷(錯誤和警告訊息)儘可能有用。有幾種方法可以做到這一點。依據命令列編譯器經驗,將 Clang 輸出與 GCC 4.9 的輸出進行對比。

Column Numbers and Caret Diagnostics

首先,clang 生成的所有診斷,都包含完整的列號資訊。clang 命令列編譯器驅動程式使用此資訊列印“點診斷”。(IDE 可以使用這些資訊,顯示內嵌錯誤標記)。可以很容易地準確理解特定程式碼段中的錯誤。

即使在字串內部,點(綠色的“^”字元)準確地顯示了問題。使得跳轉到問題變得非常容易,當同一字元的多個例項,出現在一行上時,有所幫助。

$ clang -fsyntax-only format-strings.c

format-strings.c:91:13: warning: '.*' specified field precision is missing a matching 'int' argument

printf("%.*d");

GCC 遵循 Clang 的做法,現在能夠提供診斷列,在結果中包含源文字片段。但是,Clang 的列號要準確得多,指向有問題的格式說明符,不是解析器在檢測到問題時,到達的)字元。預設情況下,Clang 的診斷是彩色的,更容易與附近的文字區分開來。

Range Highlighting for Related Text

Clang 捕獲準確跟蹤程式中表達式、語句和其它結構的範圍資訊,這些資訊使診斷突出顯示相關資訊。下面有點荒謬的示例中,不需要檢視原始原始碼,可瞭解基於 Clang 錯誤的錯誤。clang 列印了一個點,確切地知道說明哪個加號。範圍資訊突出顯示加號的左側和右側,編譯器正在執行的內容一目瞭然。範圍資訊對於涉及優先順序問題和許多其它情況非常有用。

$ gcc-4.9 -fsyntax-only t.c

t.c: In function 'int f(int, int)':

t.c:7:39: error: invalid operands to binary + (have 'int' and 'struct A')

return y + func(y ? ((SomeA.X + 40) + SomeA) / 42 + SomeA.X : SomeA.X);

^

$ clang -fsyntax-only t.c

t.c:7:39: error: invalid operands to binary expression ('int' and 'struct A')

return y + func(y ? ((SomeA.X + 40) + SomeA) / 42 + SomeA.X : SomeA.X);

~~~~~~~~~~~~~~ ^ ~~~~~

Precision in Wording

一個細節,盡力使clang 的診斷,準確包含有關錯誤和原因的相關資訊。左邊和右邊的推斷型別是什麼,不會重複顯而易見的內容(例如,一個“二進位制+”)。

下面示例中,不僅會告訴有問題,還會確切地說明原因,告訴型別是什麼(如果是一個複雜的子表示式,如呼叫過載函式)。這種對細節的關注,使得更容易理解和快速解決問題。

$ gcc-4.9 -fsyntax-only t.c

t.c:5:11: error: invalid type argument of unary '*' (have 'int')

return *SomeA.X;

^

$ clang -fsyntax-only t.c

t.c:5:11: error: indirection requires pointer operand ('int' invalid)

int y = *SomeA.X;

^~~~~~~~

Typedef Preservation and Selective Unwrapping

許多程式設計師使用高階使用者定義型別、typedef 和其它語法,引用程式中的型別。這可以縮寫非常長的型別,在診斷中保留型別名很有用。然而,有時非常簡單的 typedef,可以包裝瑣碎的型別,剝離 typedef,瞭解正在發生的事情。Clang 旨在很好地處理這兩種情況。

以下示例顯示在 C 中,保留 typedef 的重要性。

$ clang -fsyntax-only t.c

t.c:15:11: error: can't convert between vector values of different size ('__m128' and 'int const *')

myvec[1]/P;

~~~~~~~~^~

下例顯示了編譯器公開 typedef 的底層詳細資訊。如果使用者對系統“pid_t”typedef 的定義方式感到困惑,Clang 會用“aka”幫助顯示。

$ clang -fsyntax-only t.c

t.c:13:9: error: member reference base type 'pid_t' (aka 'int') is not a structure or union

myvar = myvar.x;

~~~~~ ^

在 C++ 中,型別保留包括保留寫入型別名稱的任何限定。如果採用一小段程式碼,例如:

namespace services {

struct WebService { };

}

namespace myapp {

namespace servers {

struct Server { };

}

}

using namespace myapp;

void addHTTPService(servers::Server const &server, ::services::WebService const *http) {

server += http;

}

and then compile it, we see that Clang is both providing accurate information and is retaining the types as written by the user (e.g., "servers::Server", "::services::WebService"):

編譯後,看到 Clang 既提供了準確的資訊,保留了使用者編寫的型別(例如,“servers::Server”、“::services::WebService”):

$ clang -fsyntax-only t.cpp

t.cpp:9:10: error: invalid operands to binary expression ('servers::Server const' and '::services::WebService const *')

server += http;

~~~~~~ ^ ~~~~

自然地,型別保留擴充套件到模板的使用,在原始碼中,Clang 保留有關特定模板特化(如std::vector<Real>),如何拼寫的資訊。例如:

$ clang -fsyntax-only t.cpp

t.cpp:12:7: error: incompatible type assigning 'vector<Real>', expected 'std::string' (aka 'class std::basic_string<char>')

str = vec;

^ ~~~

Fix-it Hints

“Fix-it”提示為Fix-it原始碼中的小、本地化問題提供建議。當 Clang 生成可以解決的特定問題的診斷時(例如,非標準或冗餘語法、缺少關鍵字、常見錯誤等),可能以程式碼轉換的形式提供特定指導,糾正錯誤問題。以下示例中,Clang 警告使用自 1993 年以來已被視為過時的 GCC 擴充套件。應刪除帶下劃線的程式碼,然後替換為點線下方的程式碼(".x =" or ".y =", respectively)。

$ clang t.c

t.c:5:28: warning: use of GNU old-style field designator extension

struct point origin = { x: 0.0, y: 0.0 };

~~ ^

.x =

t.c:5:36: warning: use of GNU old-style field designator extension

struct point origin = { x: 0.0, y: 0.0 };

~~ ^

.y =

“Fix-it”提示,對於解決常見的使用者錯誤和誤解最有用。例如,C++ 使用者通常會忘記顯式特化類模板的語法,如下例中的錯誤所示。同樣,在描述問題後,Clang 提供了修復--新增 --template<>作為診斷的一部分。

$ clang t.cpp

t.cpp:9:3: error: template specialization requires 'template<>'

struct iterator_traits<file_iterator> {

^

template<>

Template Type Diffing

模板型別可能很長且難以閱讀。當錯誤訊息的一部分時更是如此。Clang 不只是打印出型別名稱,而是有足夠的資訊來刪除公共元素並突出顯示差異。為了更清楚地顯示模板結構,模板型別也可以列印為縮排的文字樹。

預設值:帶有型別省略的模板差異

Default: template diff with type elision

t.cc:4:5: note: candidate function not viable: no known conversion from 'vector<map<[...], float>>' to 'vector<map<[...], double>>' for 1st argument;

-fno-elide-type: template diff without elision

t.cc:4:5: note: candidate function not viable: no known conversion from 'vector<map<int, float>>' to 'vector<map<int, double>>' for 1st argument;

-fdiagnostics-show-template-tree: template tree printing with elision

t.cc:4:5: note: candidate function not viable: no known conversion for 1st argument;

vector<

map<

[...],

[float != double]>>

-fdiagnostics-show-template-tree -fno-elide-type: template tree printing with no elision

t.cc:4:5: note: candidate function not viable: no known conversion for 1st argument;

vector<

map<

int,

[float != double]>>

Automatic Macro Expansion

許多錯誤,有時發生在深度巢狀的巨集中。對於傳統的編譯器,需要深入研究巨集的定義,瞭解是如何陷入困境的。下面簡單示例,展示了 Clang 如何通過自動列印診斷資訊和巢狀範圍資訊,通過巨集例項化的,展示了其它一些部分,如何在更大的示例中工作。

$ clang -fsyntax-only t.c

t.c:80:3: error: invalid operands to binary expression ('typeof(P)' (aka 'struct mystruct') and 'typeof(F)' (aka 'float'))

X = MYMAX(P, F);

^~~~~~~~~~~

t.c:76:94: note: expanded from:

#define MYMAX(A,B) __extension__ ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; })

~~~ ^ ~~~

Here's another real world warning that occurs in the "window" Unix package (which implements the "wwopen" class of APIs):

這是在“window” Unix 包(實現了“wwopen”類 API)中出現的,另一個真實世界警告:

$ clang -fsyntax-only t.c

t.c:22:2: warning: type specifier missing, defaults to 'int'

ILPAD();

^

t.c:17:17: note: expanded from:

#define ILPAD() PAD((NROW - tt.tt_row) * 10) /* 1 ms per char */

^

t.c:14:2: note: expanded from:

register i; \

^

在實踐中,發現 Clang 對巨集的處理,在多巢狀巨集中比在簡單巨集中更有用。

Quality of Implementation and Attention to Detail

最後,投入了大量工作打磨小事,隨著時間的推移累積起來,有助於提供出色的使用者體驗。

以下示例顯示,從forgetting a 的簡單情況中恢復;經過一個結構體定義,比GCC好得多。

$ cat t.cc

template<class T>

class a {};

struct b {}

a<int> c;

$ gcc-4.9 t.cc

t.cc:4:8: error: invalid declarator before 'c'

a<int> c;

^

$ clang t.cc

t.cc:3:12: error: expected ';' after struct

struct b {}

^

;

下面的例子表明,在 GCC 無法應對的複雜情況下,能很好地診斷和恢復缺少的typename關鍵字。

$ cat t.cc

template<class T> void f(T::type) { }

struct A { };

void g()

{

A a;

f<A>(a);

}

$ gcc-4.9 t.cc

t.cc:1:33: error: variable or field 'f' declared void

template<class T> void f(T::type) { }

^

t.cc: In function 'void g()':

t.cc:6:5: error: 'f' was not declared in this scope

f<A>(a);

^

t.cc:6:8: error: expected primary-expression before '>' token

f<A>(a);

^

$ clang t.cc

t.cc:1:26: error: missing 'typename' prior to dependent type name 'T::type'

template<class T> void f(T::type) { }

^~~~~~~

typename

t.cc:6:5: error: no matching function for call to 'f'

f<A>(a);

^~~~

t.cc:1:24: note: candidate template ignored: substitution failure [with T = A]: no type named 'type' in 'A'

template<class T> void f(T::type) { }

^ ~~~~

這些細節中的每一個都是次要的,加起來可以提供更加精緻的體驗。

參考連結:

https://clang.llvm.org/diagnostics.html

人工智慧晶片與自動駕駛