日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Flutter代碼靜態(tài)檢查原理與應(yīng)用

一、背景

Flutter雖然火了很久,但是大家對(duì)Flutter代碼靜態(tài)檢查原理與應(yīng)用依然有很多大大小小的問(wèn)題,在Flutter開(kāi)發(fā)中就存在一些大家都會(huì)遇到普適性的問(wèn)題:

創(chuàng)新互聯(lián)公司服務(wù)項(xiàng)目包括夷陵網(wǎng)站建設(shè)、夷陵網(wǎng)站制作、夷陵網(wǎng)頁(yè)制作以及夷陵網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃等。多年來(lái),我們專(zhuān)注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,夷陵網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶(hù)以成都為中心已經(jīng)輻射到夷陵省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶(hù)的支持與信任!

  • 團(tuán)隊(duì)沉淀了很多flutter編碼規(guī)范。目前團(tuán)隊(duì)完全靠人工CR,人工CR存在效率低,容易遺漏。
  • 另外一方面,我們?cè)跇I(yè)務(wù)迭代中也總結(jié)了大量代碼質(zhì)量、代碼穩(wěn)定性、代碼性能方面的最佳實(shí)踐。同樣這些最佳實(shí)踐也是通過(guò)人工CR來(lái)保證的。

上述兩個(gè)點(diǎn),均指向了人工CR的缺陷與不足,因此我們急需一些自動(dòng)化手段來(lái)解決人工CR的效率低、容易遺漏這一問(wèn)題。

所以想通過(guò)本文來(lái)為大家介紹下,代碼靜態(tài)分析可以在編碼時(shí)讓IDE實(shí)時(shí)提示程序員其代碼存在缺陷甚至根據(jù)最佳實(shí)踐的內(nèi)容提示更好的代碼實(shí)現(xiàn)。

二、代碼靜態(tài)分析

IDE與代碼分析服務(wù)器

IDE如何提示代碼存在問(wèn)題

1. 當(dāng)打開(kāi)android studio編輯器時(shí),首先會(huì)初始化AnalyzerServer服務(wù)。

2. AnalyzerServer通過(guò)創(chuàng)建Isolate啟動(dòng)加載AnalyzerPlugin插件main方法。

3. AnalyzerPlugin會(huì)一直處在循環(huán)當(dāng)中,等待調(diào)用。

4. 當(dāng)修改了代碼,IDE觸發(fā)文件改動(dòng)通知到AnalyzerServer,AnalyzerServer通知文件變動(dòng)到AnalyzerPlugin。觸發(fā)analyzer代碼靜態(tài)分析方法。

analyzer_server作用是什么?扮演著什么角色?

代碼分析服務(wù)器

analyzer_server主要提供Dart代碼的分析和檢查功能。同時(shí),也是Dart語(yǔ)言服務(wù)器協(xié)議(LSP)的實(shí)現(xiàn),可以通過(guò)LSP協(xié)議與IDE進(jìn)行通信,并提供相關(guān)的API和功能。

IDE與analyzer_server的關(guān)系:

1. 打開(kāi)Dart文件:IDE可以通過(guò)LSP協(xié)議發(fā)送打開(kāi)Dart文件的請(qǐng)求,analyzer_server會(huì)加載Dart文件,并進(jìn)行代碼分析和檢查。

2. 獲取Dart文件的分析結(jié)果:IDE可以通過(guò)LSP協(xié)議發(fā)送獲取Dart文件分析結(jié)果的請(qǐng)求,analyzer_server會(huì)返回分析結(jié)果,例如Dart文件中的變量、函數(shù)、類(lèi)等信息。

3. 執(zhí)行Dart代碼:IDE可以通過(guò)LSP協(xié)議發(fā)送執(zhí)行Dart代碼的請(qǐng)求,analyzer_server會(huì)加載并執(zhí)行Dart代碼,并返回執(zhí)行結(jié)果。

4. 擴(kuò)展Dart分析器功能:IDE可以通過(guò)LSP協(xié)議調(diào)用analyzer_plugin插件提供的API,擴(kuò)展Dart分析器的功能。例如,IDE可以通過(guò)analyzer_plugin插件來(lái)實(shí)現(xiàn)自定義的代碼檢查、代碼重構(gòu)等功能。

analyzer_server 和 analyzer_plugin 的關(guān)系:

  • analyzer_server 可以加載和運(yùn)行 analyzer_plugin 來(lái)提供額外的分析功能。
  • analyzer_plugin 是一個(gè)用于擴(kuò)展 analyzer_server 功能的插件,它可以實(shí)現(xiàn)自定義的 lint 規(guī)則、代碼生成、代碼補(bǔ)全等功能。
  • analyzer_server 負(fù)責(zé)啟動(dòng)、停止和管理 analyzer_plugin 的生命周期。

從上面可以看出,analyzer_server負(fù)責(zé)與IDE進(jìn)行通信,同時(shí)也會(huì)加載analyzer_plugin插件,實(shí)現(xiàn)開(kāi)發(fā)者可以自定義規(guī)則。

自定義代碼分析插件工程搭建及原理

插件入口環(huán)境配置

1. 新建flutter插件 dw_pink_lint_rules。

2. 在插件目錄下新建/tools/analyzer_plugin/pubspec.yaml文件,依賴(lài)dw_pink_lint_rules。

圖片

3. 再新建/tools/analyzer_plugin/bin/plugin.dart,main()是插件啟動(dòng)的入口,IDE啟動(dòng)或者點(diǎn)擊重啟按鈕時(shí),analyzer_server會(huì)調(diào)用到入口啟動(dòng)插件。

圖片

start()方法啟動(dòng)一個(gè)繼承自ServerPlugin的自定義類(lèi)DwServerPlugin,所有自定義的工作實(shí)現(xiàn)在這里完成。

4. 目錄結(jié)構(gòu)如下:

圖片

圖片

主工程中使用自定義插件

開(kāi)發(fā)者可以通過(guò)插件機(jī)制,來(lái)擴(kuò)展其自定義的代碼分析、代碼補(bǔ)全等功能。那如何自定義一個(gè)代碼分析插件?

1. 在主工程pubspec.yaml中引入dw_pink_lint_rules依賴(lài)。

2. 同時(shí)在analysis_options.yaml配置插件入口( analyzer_server 會(huì)讀取解析這個(gè)yaml配置文件,找到自定義的插件,也就是dw_pink_lint_rules)。

圖片

插件啟動(dòng)到自定義代碼入口

圖片

1. 在/tools/bin/plugin.dart中main()是插件入口,這里的入口就是通過(guò)analyzer_server調(diào)用啟動(dòng)。

圖片

2. 調(diào)用lib/starter.dart中的start(),這里初始化ServerPluginStarter()對(duì)象及DwServerPlugin()對(duì)象

圖片

DwServerPlugin是自定義的實(shí)現(xiàn)類(lèi),繼承自ServerPlugin。這個(gè)類(lèi)主要是用于創(chuàng)建分析驅(qū)動(dòng)器、執(zhí)行代碼靜態(tài)分析、發(fā)送分析結(jié)果給analyzer_server,并處理analyzer_server發(fā)送的分析請(qǐng)求。同時(shí)實(shí)現(xiàn)了一些自定義的方法來(lái)實(shí)現(xiàn)特定的功能。

3. ServerPluginStarter調(diào)用的是Driver()初始化并調(diào)用start()

圖片

ServerPluginStarter實(shí)際構(gòu)造對(duì)象是Driver,這里新建了一個(gè)PluginIsolateChannel(),用于與analyzer_server進(jìn)行通訊。

每個(gè)插件運(yùn)行在一個(gè)獨(dú)立的Isolate中,這使得它們可以在不阻塞主線(xiàn)程的情況下執(zhí)行耗時(shí)任務(wù)。為了使不同的Isolate之間可以進(jìn)行通信,F(xiàn)lutter提供了IsolateChannel的API。插件使用IsolateChannel來(lái)與主Isolate中的analysis_server進(jìn)行通信,以序列化和傳遞數(shù)據(jù)。analysis_server在接收到請(qǐng)求后會(huì)在自己的Isolate中執(zhí)行相應(yīng)的任務(wù),并將結(jié)果通過(guò)IsolateChannel返回給插件所在的Isolate。這種通訊方式使得插件可以在不同的Isolate之間傳輸數(shù)據(jù),而不會(huì)阻塞主線(xiàn)程。

4. DwServerPlugin會(huì)調(diào)用了 analysisDriverScheduler 的 start 方法,開(kāi)始調(diào)度分析驅(qū)動(dòng)器。

5. AnalysisDriverScheduler 類(lèi)是一個(gè)用于管理多個(gè)分析驅(qū)動(dòng)器的調(diào)度器,它負(fù)責(zé)為分析驅(qū)動(dòng)器提供任務(wù)隊(duì)列、任務(wù)執(zhí)行器和回調(diào)接口,并根據(jù)驅(qū)動(dòng)器的優(yōu)先級(jí)和依賴(lài)關(guān)系,安排驅(qū)動(dòng)器的執(zhí)行順序,從而實(shí)現(xiàn)高效、可靠的代碼分析。

6. channel 主要作用有兩個(gè):

  1. 監(jiān)聽(tīng)服務(wù)端發(fā)送的消息,并進(jìn)行處理。
  2. 可用于插件主動(dòng)發(fā)送消息,如收集到的error消息。

在了解完插件的啟動(dòng)流程后,我們可以看看自定義插件應(yīng)該怎么實(shí)現(xiàn)?

7. 實(shí)現(xiàn)自定義DwServerPlugin類(lèi)

下面代碼實(shí)現(xiàn)了 ServerPlugin 類(lèi)中的 createAnalysisDriver 方法,其主要作用是創(chuàng)建一個(gè) Dart 語(yǔ)言的分析驅(qū)動(dòng)器,并注冊(cè)一個(gè)回調(diào)函數(shù)來(lái)處理分析結(jié)果。

圖片

具體的實(shí)現(xiàn)步驟如下:

1. 指定分析根目錄與過(guò)濾白名單文件夾

2. 創(chuàng)建AnalysisDriver driver ,啟動(dòng)監(jiān)聽(tīng)邏輯

3. 監(jiān)聽(tīng)到變化時(shí),執(zhí)行l(wèi)inter代碼分析邏輯

執(zhí)行校驗(yàn)Linter邏輯

圖片

這段代碼分析邏輯,會(huì)創(chuàng)建一個(gè)DwChecker類(lèi),通過(guò)AST遍歷訪(fǎng)問(wèn)節(jié)點(diǎn),對(duì)代碼做靜態(tài)分析。

通過(guò)訪(fǎng)問(wèn)AST(抽象語(yǔ)法樹(shù))做代碼靜態(tài)分析

要遍歷 Dart 代碼的抽象語(yǔ)法樹(shù),可以使用 ast 包中的訪(fǎng)問(wèn)者模式。accept方法是AST節(jié)點(diǎn)的一個(gè)方法,用于接受訪(fǎng)問(wèn)者(visitor)。在Dart中,AST節(jié)點(diǎn)是由Dart解析器生成的,它們代表了源代碼中的語(yǔ)法元素,例如函數(shù)、類(lèi)、變量等等。訪(fǎng)問(wèn)者(visitor)是一個(gè)實(shí)現(xiàn)了訪(fǎng)問(wèn)AST節(jié)點(diǎn)的接口的類(lèi),它可以對(duì)AST節(jié)點(diǎn)進(jìn)行遍歷,并根據(jù)需要執(zhí)行相應(yīng)的操作。

當(dāng)我們調(diào)用一個(gè)AST節(jié)點(diǎn)的accept方法時(shí),它會(huì)調(diào)用訪(fǎng)問(wèn)者的相應(yīng)方法(例如visitPostfixExpression等等),并將自己作為參數(shù)傳遞給訪(fǎng)問(wèn)者。訪(fǎng)問(wèn)者可以使用這個(gè)AST節(jié)點(diǎn)來(lái)獲取有關(guān)該節(jié)點(diǎn)的信息,并根據(jù)需要執(zhí)行相應(yīng)的操作。

在實(shí)際使用中,我們通常會(huì)創(chuàng)建一個(gè)訪(fǎng)問(wèn)者類(lèi),繼承自AstVisitor或者RecursiveAstVisitor類(lèi),并實(shí)現(xiàn)其中的方法。然后,我們可以創(chuàng)建一個(gè)AST節(jié)點(diǎn)對(duì)象,并調(diào)用其accept方法,將訪(fǎng)問(wèn)者對(duì)象傳遞給該方法。這樣,就會(huì)觸發(fā)對(duì)AST節(jié)點(diǎn)的遍歷,并調(diào)用訪(fǎng)問(wèn)者的相應(yīng)方法。

具體來(lái)說(shuō),以下是使用訪(fǎng)問(wèn)者模式遍歷 AST 的步驟:

1. 定義一個(gè)繼承自 RecursiveAstVisitor 的訪(fǎng)問(wèn)者類(lèi),并實(shí)現(xiàn)相應(yīng)的 visit 方法。

圖片

2. 創(chuàng)建一個(gè)訪(fǎng)問(wèn)者對(duì)象,并使用 unit 對(duì)象的 accept 方法遍歷 AST。

圖片

通過(guò)AST遍歷的方式可以訪(fǎng)問(wèn)的指定的token。有了這些基礎(chǔ)知識(shí),下面可以開(kāi)始實(shí)現(xiàn)代碼分析的自定義部分邏輯。

自定義代碼分析插件實(shí)現(xiàn)

下面將列舉三個(gè)由易到難自定義規(guī)則,讓讀者更好的了解實(shí)現(xiàn)一個(gè)自定義規(guī)則是如何實(shí)現(xiàn)的,在實(shí)際實(shí)現(xiàn)過(guò)程中會(huì)遇到哪些挑戰(zhàn)?

規(guī)則一:context.read()不能在await之后使用

context.read()在await之后使用,在頁(yè)面退出或其他場(chǎng)景之后會(huì)拋異常,使用代碼靜態(tài)分析能很好的解決此類(lèi)異常問(wèn)題。

實(shí)現(xiàn)

在當(dāng)前節(jié)點(diǎn)context.read()向前查找是否有await,有則報(bào)錯(cuò)。

1. 分析context.read() 屬于方法調(diào)用;從AST遍歷訪(fǎng)問(wèn)可知在visitMethodInvocation()中,方法調(diào)用是read且context是buildContext,則去查找。

2. 能定位到context.read的token,接下來(lái)需要做的是遍歷向前查找是否有await。

圖片

這段代碼做了一件事: 向前去查找是否有await語(yǔ)句。

具體來(lái)說(shuō),在查找的過(guò)程中,會(huì)執(zhí)行以下操作:

1. 如果當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)是一個(gè)Block對(duì)象或者SwitchCase對(duì)象,則調(diào)用checkStatements函數(shù),檢查其中的每個(gè)語(yǔ)句是否有await操作。

2. 如果當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)不屬于上述任何一種類(lèi)型,則將當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)作為新的child節(jié)點(diǎn),并繼續(xù)向上遍歷。

如果找到了使用await異步操作,就會(huì)調(diào)用addError函數(shù),將相應(yīng)的錯(cuò)誤信息添加到visitor對(duì)象中。如果遍歷完整個(gè)AST節(jié)點(diǎn)樹(shù),仍然沒(méi)有找到await,則函數(shù)會(huì)正常返回,不執(zhí)行任何操作。

如何判斷是否有await

具體來(lái)說(shuō),首先創(chuàng)建了一個(gè)_AwaitVisitor對(duì)象visitor。然后,調(diào)用statement的accept方法,將visitor對(duì)象傳入其中。這個(gè)accept方法會(huì)遍歷statement的AST節(jié)點(diǎn),并對(duì)每個(gè)節(jié)點(diǎn)調(diào)用相應(yīng)的visitor方法。在這個(gè)過(guò)程中,如果遇到了await表達(dá)式,就會(huì)調(diào)用_AwaitVisitor對(duì)象的visitAwaitExpression方法,將hasAwait屬性設(shè)置為true。

最后,函數(shù)返回visitor對(duì)象的hasAwait屬性,即表示給定的statement中是否含有await關(guān)鍵字。

一條簡(jiǎn)單的自定義規(guī)則就實(shí)現(xiàn)了,需要實(shí)現(xiàn)的有三點(diǎn):

1. 如何定位到context.read的token

2. 通過(guò)循環(huán)的方式向前遍歷,判斷是否有await

3. 通過(guò)AST遍歷方式判斷語(yǔ)句是否是await

規(guī)則二:使用as表達(dá)式前需要使用is判斷(完成對(duì)強(qiáng)制類(lèi)型校驗(yàn))

在 Dart 中,as 表達(dá)式用于將一個(gè)對(duì)象轉(zhuǎn)換為指定的類(lèi)型。如果對(duì)象不是指定類(lèi)型的實(shí)例,則會(huì)拋出一個(gè) TypeError 異常。此條規(guī)則也能很好的減少代碼異常。

as規(guī)則主要是檢查當(dāng)前node節(jié)點(diǎn)前面是否有符合is判斷的條件。

這里查找與context.read不同之處,除了向前查找,同時(shí)還會(huì)向上查找。同時(shí)由于涉及if判斷語(yǔ)句,整條規(guī)則的復(fù)雜度會(huì)上一個(gè)臺(tái)階。

圖片

這段遍歷代碼與之前有兩個(gè)不同點(diǎn):

1. If 語(yǔ)句內(nèi)的遍歷,這是正向遍歷,查找if(變量 is 類(lèi)型)校驗(yàn)類(lèi)型

2. 向前遍歷,這是逆向遍歷,查找if(變量 is! 類(lèi)型) return的校驗(yàn)

這里先看下else if (parent is IfStatement)分支中的isExpressionCheck()方法,這個(gè)方法主要作用是處理正向遍歷邏輯。

圖片

1. 先引入一個(gè)變量positiveCheck,代表正向和反向。下述情況滿(mǎn)足之一,變量都算類(lèi)型校驗(yàn)成功。

  1. 正向是if()語(yǔ)句包裹內(nèi)的,需要向上查找if (a is String) 條件;這里檢查if語(yǔ)句內(nèi),是正向。
  2. 反向是if...return 語(yǔ)句,需要向同級(jí)向前查找 if( is! ) return; 條件;checkStatements 查找的是取反的條件。

2. 變量isBang標(biāo)識(shí)是否有整個(gè)條件取反,例如:if (!(a != null)),為什么非引入這么個(gè)變量呢?

If (a == null)與if (!(a != null))的邏輯是一樣的,但I(xiàn)f (a == null && 其他條件)與if (!(a != null && 其他條件))這種邏輯就完全不同。

positiveCheck與isBang組合起來(lái)有以下4種情況:

If (a is String)
If (a is! String)
If (!(a is String))
If (!(a is! String))

上述代碼正是解決此類(lèi)組合問(wèn)題, If (a is String) {a as String}與 If (a is! String) return; a as String。這兩種方式也屬于判斷了類(lèi)型。

解決了組合問(wèn)題,再看一個(gè)嵌套的問(wèn)題。

If 判斷的邏輯復(fù)雜,情況有多種。例如:

在正向情況下:

If (a is String && b is String) 是有效的
If (a is String || b is String) 是無(wú)效的

在反向情況下:

If (a is! String && b is! String) return 是無(wú)效的
If (a is! String || b is! String) return 是有效的

圖片

這段代碼能處理好if的條件,是因?yàn)樗ㄟ^(guò)遞歸的方式,深度遍歷if語(yǔ)句條件中的所有子表達(dá)式,找到其中是否包含is表達(dá)式。

在if語(yǔ)句中的條件表達(dá)式中,如果包含is表達(dá)式,則判斷條件表達(dá)式是否滿(mǎn)足positiveCheck或isBang參數(shù)的要求。如果滿(mǎn)足要求,則直接返回true,否則需要判斷if語(yǔ)句的then部分是否終止控制流,如果終止,則返回true,否則返回false。

因此,這段代碼能夠處理好if語(yǔ)句中的條件,以及其他語(yǔ)句中的表達(dá)式,判斷其中是否包含is表達(dá)式,并根據(jù)positiveCheck和isBang參數(shù)進(jìn)行判斷,最終返回判斷結(jié)果。

當(dāng)前結(jié)點(diǎn)與if條件結(jié)點(diǎn)比較:

圖片

函數(shù)首先調(diào)用addPropertyAccessTarget()函數(shù),將sourceExp和targetExp按照token分解成List類(lèi)型的sourceExpTarget和targetExpTarget。

然后,函數(shù)調(diào)用isCompareList()函數(shù)比較sourceExpTarget和targetExpTarget是否相等。如果相等,則判斷positiveCheck和isBang參數(shù)去判斷!,如果滿(mǎn)足要求,則將isRes設(shè)置為true。

如果checksIsExpression比較成功:

1. positiveCheck為true,表示正向比較成功。

2. positiveCheck為false,則去判斷thenStatement的最后一條語(yǔ)句是否為return,bread,continue等關(guān)鍵字,如果是則為true,否則為false。

圖片

至此,一個(gè)正向的、逆向的is類(lèi)型判斷基本完成。但實(shí)際代碼還有一些特殊情況,例如:

解決了正向、反向;組合的問(wèn)題。在實(shí)際開(kāi)發(fā)中還遇到一些特殊情況,例如都是一個(gè)if條件、二元表達(dá)式、數(shù)據(jù)中的二元表達(dá)式等。這些解決思路與上述類(lèi)似。

3. 同一個(gè)if判斷,is在條件前面已經(jīng)判斷,可查看else if (parent is BinaryExpression)分支。

4. 二元表達(dá)式  ?:,可查看isConditionalExpressionCheck方法:

json['list'] is List ? json['list'] as List : []

5. 數(shù)組中的判斷,[]中的token是IfElement,可查看else if (parent is ConditionalExpression || parent is IfElement)分支代碼:

[json['list'] is List ? json['list'] as List : []];

這條自定義規(guī)則要復(fù)雜很多,難點(diǎn)在于:

  • if判斷組合情況比較復(fù)雜,如何處理好組合情況是個(gè)難點(diǎn)。
  • if語(yǔ)句會(huì)有邏輯運(yùn)算,怎么處理好這種情況值得思考。
  • 還需要考慮一些特殊情況:例如二元表達(dá)式等。

規(guī)則三:使用強(qiáng)制解包!前需要if判空

在 Dart 語(yǔ)言中,使用 ! 符號(hào)進(jìn)行強(qiáng)制解包時(shí),如果對(duì)象為 null 會(huì)拋出 NoSuchMethodError 異常。因此,在使用 ! 操作符時(shí),我們需要確保變量或表達(dá)式不為空。這又是一個(gè)使用自定義規(guī)則很好解決的場(chǎng)景。

If 判空邏輯處理

If 語(yǔ)句的判空邏輯還是比較復(fù)雜,其主要難點(diǎn)在:

If該如何判空,a == null 是判空,a.isEmpty也是判空,a?.isEmpty也是判空,is String判斷也是判空。其復(fù)雜度會(huì)更高。

這里抽象了一個(gè)思想:不是去處理 a != null 或者 a?.isNotEmpty == true,還有isEmpty,靠方法去判空代碼就復(fù)雜了。而是按以下邏輯:

  • rightOperand 是 null字面量且operator操作符是 !=
  • 又或者rightOperand 是 非null字面量 操作符是 ==

圖片

讀者可以思考以下場(chǎng)景代碼能否校驗(yàn)成功:

if的變量對(duì)比邏輯也略有不同,例如:

If (a?.b != null) {} 這個(gè)時(shí)候變量a變量屬于判空。所以括號(hào)內(nèi)的變量是條件判空的子集。

if判空邏輯一些特殊情況

1. 判斷條件不再是單純的is判斷。下面是算法核心:

  1. 例如正向只有兩種情況, != null和== (!null),這種包括了 a != null、a?.isNotEmpty == true。逆向場(chǎng)景類(lèi)似。
/*
*判斷條件
*正向:
*1. != null
*2. == (!null)
*反向:
*1. == null
*2. != (!null)
*
*加!
*正向:
*1. !(== null)
*2. !(!= (!null))
*反向:
*1. !(!= null)
*2. !(== (!null))
* */

2. 支持StringUtils工具類(lèi)判空,思路與上面類(lèi)型,可查看else if (check is MethodInvocation) 分支。

圖片

3. 支持is類(lèi)型判空,思路也是調(diào)用as的規(guī)則。

圖片

4. 支持contains判空,思路不贅述。

圖片

5. 支持條件提取為變量。

圖片

6. 支持前面使用了 = 或者 ??= 默認(rèn)為非空。

圖片

強(qiáng)制解包!的if判斷比as的更復(fù)雜:

1. 除了a == null、a != null等簡(jiǎn)單判空, a?.isNotEmpty == true,a?.isNotEmpty ?? true都是判空;相對(duì)于之前判空會(huì)更復(fù)雜。

2. 同時(shí)還需要支持StringUtils工具類(lèi)的判空;也囊括了 Is String的判空情況,特殊情況也會(huì)多。

3. 同時(shí)變量token與判空條件的token是子集的關(guān)系,這點(diǎn)與is稍有差異。

忽略注釋

這是一個(gè)非常好的應(yīng)用,理想情況下是所有代碼均可修改,但實(shí)際情況時(shí),有些代碼修改起來(lái)非常麻煩,又或者改動(dòng)之后影響不可評(píng)估,這個(gè)時(shí)候最好的辦法就是不修改,而忽略注釋正好解決這個(gè)問(wèn)題。

使用

當(dāng)有些不需要修改或者風(fēng)險(xiǎn)較大,可以使用//ignore:的方式來(lái)忽略報(bào)錯(cuò):

//ignore: avoid_use_as
//ignore: use_postfix_pre_need_if_empty

1. 添加在類(lèi)的前一行:

圖片

2. 添加在方法的前一行:

圖片

3. 添加在報(bào)錯(cuò)節(jié)點(diǎn)的前一行或者當(dāng)前行:

圖片

實(shí)現(xiàn)思路

1. 遍歷給定的Dart編譯單元中的所有token;把單行注釋添加到_commentTokens中。

2. 在addError之前,判斷該報(bào)錯(cuò)node是否有ignore:忽略策略。

  1. 遍歷注釋節(jié)點(diǎn)行號(hào)
  2. 與當(dāng)前報(bào)錯(cuò)的node行號(hào)比較,如果差值等于0或者1,則查找成功,否則查找失敗
  3. node當(dāng)前所在函數(shù)的行號(hào)、所在類(lèi)的行號(hào)比較,差值等于1則查找成功,否則查找失敗

圖片

三、總結(jié)

本文主要介紹了自定義代碼分析插件工程的搭建及由易到難實(shí)現(xiàn)了3個(gè)自定義代碼分析插件的規(guī)則,解決了人工CR的效率低、容易遺漏這一問(wèn)題。

代碼開(kāi)發(fā)過(guò)程中遭遇很多挑戰(zhàn),網(wǎng)上關(guān)于自定義代碼分析文章幾乎為0,能搜索到只是一些對(duì)linter的簡(jiǎn)單配置。也希望本文給讀者啟發(fā),少走彎路。

后續(xù)會(huì)實(shí)現(xiàn)更多的規(guī)則,來(lái)規(guī)范團(tuán)隊(duì)內(nèi)的代碼,減少人工CR的工作量。同時(shí)分享自定義規(guī)則的實(shí)現(xiàn),使得每個(gè)成員都能進(jìn)行自定義規(guī)則的實(shí)現(xiàn)。


文章名稱(chēng):Flutter代碼靜態(tài)檢查原理與應(yīng)用
當(dāng)前鏈接:http://www.dlmjj.cn/article/cojdgis.html