實(shí)現(xiàn)自定義OCLint:靜態(tài)代碼檢查-自定義clang的Attribute

前言

如果想要擴(kuò)展clang的特定操作,通過(guò)Attribute(屬性)是最便捷的方式,通過(guò)擴(kuò)展屬性,我們可以實(shí)現(xiàn)自定義的語(yǔ)義診斷,添加定制化語(yǔ)法檢查,實(shí)現(xiàn)自己的OCLint.具體實(shí)現(xiàn)方法往下看吧.

在clang中增加屬性(attribute)

屬性(Attribute)是構(gòu)成程序基本結(jié)構(gòu)的元數(shù)據(jù),開(kāi)發(fā)者可以通過(guò)屬性來(lái)給編譯器傳遞必要的語(yǔ)義信息.例如,屬性可以改變程序的代碼生成結(jié)構(gòu),或者提供額外的靜態(tài)分析的語(yǔ)義信息.下面我們來(lái)討論如何在Clang中增加自定義的屬性.

屬性的定義

Clang中的屬性被分為三個(gè)階段來(lái)處理

  1. 解析為語(yǔ)法屬性
  2. 語(yǔ)法屬性到語(yǔ)義屬性的轉(zhuǎn)換
  3. 屬性的語(yǔ)義分析

語(yǔ)法解析取決于屬性所在的語(yǔ)法形式.一般屬性是語(yǔ)法解析的會(huì)通過(guò)AttributeList對(duì)象來(lái)表示.以列表形式來(lái)體現(xiàn)解析后的屬性,該列表從屬于聲明符或聲明的分隔符.屬性的語(yǔ)法分析是Clang自動(dòng)進(jìn)行的,但是關(guān)鍵字的屬性除外,關(guān)鍵字的解析和AttributeList對(duì)象的生成必須我們手動(dòng)控制.

當(dāng)DeclAttributeList從語(yǔ)法屬性轉(zhuǎn)換為語(yǔ)義屬性時(shí),會(huì)調(diào)用Sema::ProcessDeclAttributeList()方法.語(yǔ)法屬性到語(yǔ)義屬性的轉(zhuǎn)換過(guò)程取決于屬性的定義和屬性的語(yǔ)義需要.最終,語(yǔ)義屬性從屬于Decl對(duì)象,可以通過(guò)Decl::getAttr<T>()來(lái)獲得.

語(yǔ)義屬性的結(jié)構(gòu)也是由Attr.td提供的屬性的定義來(lái)決定的.該定義用于自動(dòng)生成一系列方法,方法是用于實(shí)現(xiàn)對(duì)于的屬性.例如clang::Attr的衍生類(lèi),語(yǔ)法解析需要用的必要信息,某些屬性的自動(dòng)語(yǔ)義檢查等.

屬性的結(jié)構(gòu)

通過(guò)查看屬性文件Attr.td(路徑為include/clang/Basic/Attr.td),通過(guò)一個(gè)集成自InheritableAttr的屬性示例

def Aligned : InheritableAttr {
  let Spellings = [GCC<"aligned">, Declspec<"align">, Keyword<"alignas">,
                   Keyword<"_Alignas">];
//  let Subjects = SubjectList<[NonBitField, NormalVar, Tag]>;
  let Args = [AlignedArgument<"Alignment", 1>];
  let Accessors = [Accessor<"isGNU", [GCC<"aligned">]>,
                   Accessor<"isC11", [Keyword<"_Alignas">]>,
                   Accessor<"isAlignas", [Keyword<"alignas">,
                                          Keyword<"_Alignas">]>,
                   Accessor<"isDeclspec",[Declspec<"align">]>];
  let Documentation = [Undocumented];
}

屬性的基本參數(shù)有:Spellings(拼寫(xiě)),Subjects(主題),Documentation(文檔).

添加屬性步驟

Clang中添加新屬性的步驟分為以下幾部分.

  1. include/clang/Basic/Attr.td中添加屬性的定義.
    tablegen的定義必須要屬于Attr類(lèi)型.大多數(shù)屬性都屬于InheritableAttr類(lèi)型,該類(lèi)型指明了該屬性的可以被它關(guān)聯(lián)的Decl的重定義繼承.
    InheritableParamAttrInheritableAttr的用法相似,主要不同在于前者屬性通過(guò)參數(shù)來(lái),后者通過(guò)聲明.
    當(dāng)一個(gè)屬性需要應(yīng)用于類(lèi)型而不是聲明時(shí),就應(yīng)該屬于TypeAttr,此屬性不會(huì)生成AST表示(我們下面的內(nèi)容不討論類(lèi)型屬性).
    繼承自IgnoredAttr的屬性會(huì)被解析,并且生成一個(gè)忽略屬性的診斷,該設(shè)計(jì)主要用于由第三方而非Clang提供的屬性.

  2. include/clang/Basic/AttrDocs.td中添加文檔定義.

  3. lib/Sema/SemaDeclAttr.cpp中添加語(yǔ)義操作.

添加屬性具體細(xì)節(jié)

以上介紹了添加屬性需要的步驟,現(xiàn)在對(duì)步驟進(jìn)行拆分,詳細(xì)講解每部分需要進(jìn)行的具體操作.

屬性的定義會(huì)指定幾種關(guān)鍵信息,例如屬性的語(yǔ)義名稱(chēng),屬性支持的拼寫(xiě),屬性的參數(shù)等等.大部分的Attr派生類(lèi)不會(huì)要求在聲明時(shí)就定義所有的信息,但是每個(gè)屬性至少要指明的內(nèi)容有:拼寫(xiě)列表,主題列表文檔列表.

拼寫(xiě)列表

所有的屬性都需要指明拼寫(xiě)列表,用于表示屬性可以識(shí)別的拼寫(xiě)方式.例如,單個(gè)語(yǔ)義屬性包含一個(gè)關(guān)鍵字拼寫(xiě).空的拼寫(xiě)列表也是合法的,有時(shí)回用于屬性的隱式創(chuàng)建.

主題列表

屬性可以關(guān)聯(lián)一個(gè)或多個(gè)Decl主題.如果屬性需要關(guān)聯(lián)主題列表中沒(méi)有的主題,會(huì)自動(dòng)觸發(fā)診斷信息.該診斷類(lèi)型屬于警告還是錯(cuò)誤,取決于屬性主題列表的定義,默認(rèn)是警告.
向用戶(hù)展示的診斷信息是通過(guò)列表中的主題自動(dòng)判斷的,主題列表也可以指明自定義的診斷參數(shù).
主題列表生成的診斷類(lèi)型為diag::warn_attribute_wrong_decl_typediag::err_attribute_wrong_decl_type,如果之前在SubjectList中加入過(guò)未使用的Decl節(jié)點(diǎn),對(duì)應(yīng)的參數(shù)枚舉可以在include/clang/Sema/AttributeList.h中找到.自動(dòng)判斷診斷參數(shù)的邏輯可以在utils/TableGen/ClangAttrEmitter.cpp中設(shè)置.

默認(rèn)情況下,主題列表中的元素屬于以下兩個(gè)節(jié)點(diǎn)中的一種:DeclNodes.td中的Decl節(jié)點(diǎn),StmtNodes.td中的statement節(jié)點(diǎn).然而,復(fù)雜的主題可以通過(guò)SubsetSubject對(duì)象來(lái)創(chuàng)建.每個(gè)SubsetSubject對(duì)象都有一個(gè)基本的主題(一定是DeclStmt節(jié)點(diǎn),而不是SubsetSubject節(jié)點(diǎn)).例如NonBitFieldSubsetSubject關(guān)聯(lián)FieldDecl,用于檢測(cè)給的的FieldDecl是否是bit field.當(dāng)SubsetSubject在主題列表中聲明時(shí),必須提供自定義的診斷參數(shù).

當(dāng)HasCustomParsing被設(shè)置為1時(shí),屬性的主題列表的診斷校驗(yàn)會(huì)自動(dòng)進(jìn)行.

文檔列表

所有的屬性必須有關(guān)聯(lián)的文檔.文檔是由服務(wù)端的公共網(wǎng)頁(yè)服務(wù)器來(lái)生成的.一般屬性文檔的定義是在include/clang/Basic/AttrDocs.td中生成的.

如果屬性不允許公共預(yù)覽或者是一個(gè)隱式生成的沒(méi)有可視拼寫(xiě)的屬性時(shí),文檔列表可以制定Undocumented對(duì)象.否則就需要在AttrDocs.td中添加屬性的文檔.

文檔是由Document生成的,所有的衍生類(lèi)型必須指定文檔的分類(lèi)和文檔本身,還可以指定屬性的自定義頭.

現(xiàn)在有四種預(yù)定義的文檔分類(lèi):DocCatFunction關(guān)聯(lián)函數(shù)類(lèi)主題,DocCatVariable關(guān)聯(lián)變量類(lèi)主題, DocCatType關(guān)聯(lián)類(lèi)型屬性,DocCatStmt表達(dá)式屬性.自定義文檔分類(lèi)可以用于表達(dá)相同方法的一類(lèi)屬性.自定義分類(lèi)的優(yōu)勢(shì)在于便于提供屬性類(lèi)簇的預(yù)覽信息.例如用于注釋的屬性自定義分類(lèi)DocCatConsumed,用于解釋注釋的級(jí)別.

文檔內(nèi)容通過(guò)reStructuredText(RST)語(yǔ)法來(lái)編寫(xiě).

在屬性文檔書(shū)寫(xiě)完成后,需要在本地測(cè)試來(lái)確保不會(huì)在服務(wù)端生成文檔時(shí)出現(xiàn)問(wèn)題.本地測(cè)試需要通過(guò)clang-tblgen來(lái)構(gòu)建.為了生成屬性文檔,可以執(zhí)行以下代碼

clang-tblgen -gen-attr-docs -I /path/to/clang/include /path/to/clang/include/clang/Basic/Attr.td -o /path/to/clang/docs/AttributeReference.rst

本地測(cè)試完成后,不用在AttributeReference.rst中提交修改.該文件是由服務(wù)端自動(dòng)生成的,在該文件中的任何改動(dòng)之后都會(huì)被覆蓋.

其他的屬性

Attr定義包含其他控制屬性行為的元素.我們只討論幾種.
如果屬性的語(yǔ)法解析較為復(fù)雜或者與語(yǔ)義解析出入較大時(shí),可以通過(guò)設(shè)置HasCustomParsing1,Parser::ParseGNUAttributeArgs()的解析代碼就可以在特定情況下進(jìn)行更新.將該變量設(shè)置為1需要進(jìn)行額外的實(shí)現(xiàn).

如果屬性不想衍生出模板聲明時(shí),將Clone設(shè)置為0,模式所有的屬性都會(huì)復(fù)制模板實(shí)例.

如果屬性不想生成AST節(jié)點(diǎn),應(yīng)該吧ASTNode設(shè)置為0.注意的是,所有繼承TypeAttrIgnoredAttr的節(jié)點(diǎn)都不會(huì)生成AST節(jié)點(diǎn).其他節(jié)點(diǎn)默認(rèn)生成AST節(jié)點(diǎn).AST節(jié)點(diǎn)是屬性的語(yǔ)義表達(dá)形式.

LangOpts指明屬性的語(yǔ)言選項(xiàng).

基于屬性的拼寫(xiě)列表會(huì)自動(dòng)生成屬性的存取器.例如,一個(gè)屬性有兩種不同的拼寫(xiě)方式FooBar.存取器會(huì)生成如下樣式[Accessor<"isFoo", [GNU<"Foo">]>, Accessor<"isBar", [GNU<"Bar">]>].

如果屬性不需要自定義語(yǔ)義操作,可以將SemaHandler設(shè)置為0.IgnoredAttr自動(dòng)不進(jìn)行語(yǔ)義操作,其他屬性默認(rèn)都會(huì)進(jìn)行語(yǔ)義操作.不進(jìn)行語(yǔ)義操作的屬性,不會(huì)給語(yǔ)法分析的屬性提供Kind枚舉.

默認(rèn)情況下,在合并聲明的屬性時(shí),屬性是不可重復(fù)的,如果一個(gè)屬性在合并階段允許重復(fù),需要將DuplicatesAllowedWhileMerging設(shè)置為1.

樣板文件

所有屬性聲明的語(yǔ)義分析都是在lib/Sema/SemaDeclAttr.cpp進(jìn)行的,一般是由ProcessDeclAttribute()方法開(kāi)始.如果屬性是一個(gè)簡(jiǎn)單屬性(不需要自定義的語(yǔ)義分析,只需要遵循默認(rèn)提供的分析),調(diào)用handleSimpleAttribute<YourAttr>(S, D, Attr)即可.否則,就應(yīng)該編寫(xiě)新的handleYourAttr()方法來(lái)增加轉(zhuǎn)換的表達(dá)式.不要在屬性的內(nèi)部直接實(shí)現(xiàn)這些控制邏輯.

除非有特定的屬性定義,常規(guī)的語(yǔ)法檢查是自動(dòng)進(jìn)行的.常規(guī)檢查包括診斷屬性是否符合給定的Decl,確定參數(shù)的輸入數(shù)量.

如果屬性增加了額外的警告,在include/clang/Basic/DiagnosticGroups.td定義了DiagGroup,在拼寫(xiě)后將_替換為-.如果只有一種檢測(cè)時(shí),可以通過(guò)使用DiagnosticSemaKinds.td中的InGroup<DiagGroup<"your-attribute">>來(lái)實(shí)現(xiàn).

屬性生成的所有的語(yǔ)法診斷,包括系統(tǒng)自動(dòng)生成,都需要有對(duì)應(yīng)的測(cè)試用例.

語(yǔ)義操作

大多數(shù)屬性的實(shí)現(xiàn)都會(huì)對(duì)編譯產(chǎn)生作用.例如,修改代碼生成方式,或者增加額外的分析的語(yǔ)義檢查.除了增加屬性的定義和到語(yǔ)義表達(dá)式的轉(zhuǎn)換,還需要實(shí)現(xiàn)自定義的屬性的用法.

屬性是否包含clang::Decl對(duì)象可以通過(guò)hasAttr<T>()方法來(lái)獲得.想要獲得屬性的語(yǔ)義表達(dá)式可以通過(guò)getAttr<T>.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蹬音,一起剝皮案震驚了整個(gè)濱河市靴跛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌砂沛,老刑警劉巖奔缠,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜻底,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拔稳,警方通過(guò)查閱死者的電腦和手機(jī)葛峻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)巴比,“玉大人术奖,你說(shuō)我怎么就攤上這事∏峤剩” “怎么了采记?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)政勃。 經(jīng)常有香客問(wèn)我唧龄,道長(zhǎng),這世上最難降的妖魔是什么稼病? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任选侨,我火速辦了婚禮,結(jié)果婚禮上然走,老公的妹妹穿的比我還像新娘援制。我一直安慰自己,他們只是感情好芍瑞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布晨仑。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洪己。 梳的紋絲不亂的頭發(fā)上妥凳,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音答捕,去河邊找鬼逝钥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拱镐,可吹牛的內(nèi)容都是我干的艘款。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沃琅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哗咆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起益眉,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤晌柬,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后郭脂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體年碘,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年朱庆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盛泡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡娱颊,死狀恐怖傲诵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情箱硕,我是刑警寧澤拴竹,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站剧罩,受9級(jí)特大地震影響栓拜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惠昔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一幕与、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镇防,春花似錦啦鸣、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)香拉。三九已至,卻和暖如春中狂,著一層夾襖步出監(jiān)牢的瞬間凫碌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工胃榕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盛险,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓勋又,卻偏偏與公主長(zhǎng)得像枉层,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赐写,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 2000年,伊利諾伊大學(xué)厄巴納-香檳分校(University of Illinois at Urbana-...
    星光社的戴銘閱讀 15,896評(píng)論 8 180
  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說(shuō)閱讀 10,970評(píng)論 6 13
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理膜赃,服務(wù)發(fā)現(xiàn)挺邀,斷路器,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139
  • 阿里是一個(gè)很成功的人,他是大企業(yè)的董事長(zhǎng)疲眷,而且與一般的董事長(zhǎng)不一樣禾蚕,他是幾乎不必操心任何事情的董事長(zhǎng),他只要待在家...
    楊大輝閱讀 798評(píng)論 0 6
  • 1 剛才專(zhuān)門(mén)數(shù)了一下,我一共有96個(gè)微信群誊役。除了個(gè)別群之外穷娱,這些群主要涉及兩個(gè)圈子,俄語(yǔ)圈子蛋哭、文學(xué)圈子县习。 我是個(gè)俄...
    熊藝楸閱讀 753評(píng)論 3 2