本文章是由機器翻譯。

C# 和 Visual Basic

使用 Roslyn 為您的 API 編寫實際代碼分析器

Alex Turner

10 年來,Visual Studio代碼分析提供了您 C# 和Visual Basic的程式集,運行一組特定的 FxCop 規則為 Microsoft.NET Framework 2.0 編寫生成時間分析。這些規則非常善於説明您避免在代碼中,一般缺陷,但他們有針對性的問題開發商早在 2005 年達到。 今天的新的語言功能和 Api 呢?

活的、 基於專案的代碼分析儀Visual Studio2015年預覽中,API 作者可以運送特定于域的代碼分析作為其 NuGet 套裝程式的一部分。當您鍵入甚至之前你已經完成了線因為這些分析儀由.NET 編譯器平臺 (代號為"羅斯林"),他們可以產生在代碼中的警告 — — 不再等待生成代碼來找出你犯了一個錯誤。分析器還可以通過新的Visual Studio燈泡提示讓你立即清理您的代碼的自動代碼修復浮出水面。

許多這些活代碼分析器重量只是 50 到 100 行的代碼,和你不需要一個編譯器運動員來寫下它們。在這篇文章我將演示如何寫目標編碼的一個常見問題,受到那些使用.NET 正則運算式 (RegEx) 分析器:如何確保你寫的正則運算式模式是語法上有效的您在運行您的應用程式之前。我就這樣通過展示如何編寫包含診斷程式以指出不正確正則運算式模式的分析器。我會跟進這篇文章後,第二部分看看如何添加代碼修復,清理你的分析器發現的錯誤。

準備工作

若要開始,請確保您有必要的Visual Studio2015年預覽位:

  • 建立了一個具有這兩種方式之一的Visual Studio2015年預覽框:
    1. 安裝Visual Studio2015年預覽從 aka.ms/vs2015preview
    2. 抓預建的 Azure VM 映射從 aka.ms/vs2015azuregallery
  • 安裝Visual Studio2015年預覽 SDK 從 aka.ms/vs2015preview。你需要做到這一點,即使您使用 Azure VM 映射。
  • 安裝 SDK 範本 VSIX 包從 aka.ms/roslynsdktemplates 以獲取.NET 編譯器平臺專案範本。
  • 安裝語法視覺化檢視 VSIX 包從 aka.ms/roslynsyntaxvisualizer 得到一個語法視覺化檢視工具視窗,以有助於探索你會分析語法樹。
  • 您還需要將語法視覺化檢視安裝到實驗的Visual Studio沙箱,您將使用來調試您的分析器。我會穿越怎麼辦,後來語法視覺化檢視一節中。

探索分析器範本

一旦你了,運行所需的 VSIX 包,與Visual Studio2015年、Visual StudioSDK 查閱建築分析儀的專案範本。

內Visual Studio2015年,轉到檔 |新專案 |C# |可擴充性和選擇的診斷代碼修復 (NuGet + VSIX) 的範本,如中所示圖 1。分析儀,以及可以創建在Visual Basic,但對於這篇文章中,我將使用 C#。因此,請務必在頂部的目標框架設置為.NET 框架 4.5。給您的專案的名稱 RegexAnalyzer,然後選擇確定以創建專案。

創建分析器專案
圖 1 創建分析器專案

您將看到一組範本生成的三個專案:

  • RegexAnalyzer:這是生成 DLL 包含診斷和代碼修復儀的主要專案。建設這一專案也會產生包含分析器專案當地 NuGet 套裝程式 (.nupkg 檔)。
  • RegexAnalyzer.VSIX:這是成Visual Studio捆綁你的分析器 DLL 的專案 — — 廣泛的擴展包 (.vsix 檔)。如果你分析儀不需要添加會影響生成的警告,您可以選擇分發此.vsix 檔,而不是 NuGet 套裝程式。無論哪種方式,.vsix 專案允許您按 f5 鍵並測試您的分析器的Visual Studio(調試物件) 單獨實例中。
  • RegexAnalyzer.Test:這是一個您可以確保您的分析器生產正確診斷和修復程式,但不運行調試物件實例的Visual Studio每次的單元測試專案。

如果你在主專案中打開 DiagnosticAnalyzer.cs 檔,你可以看到產生診斷的範本中的預設代碼。此診斷程式做事有點傻 — — 它"squiggles"任何類型名稱中使用了小寫字母。然而,因為大多數程式將具有這種類型的名稱,這讓你很容易地看到分析儀在行動。

請確保 RegexAnalyzer.VSIX 專案是啟動專案並按 f5 鍵。運行 VSIX 專案載入Visual Studio,讓Visual Studio跟蹤的一套單獨的Visual Studio擴展實驗沙箱副本。這很有用,因為您開發您自己的擴展,並需要調試Visual StudioVisual Studio工作室。因為調試物件Visual Studio實例是一個新的實驗沙箱,您將看到相同的對話方塊,當你第一次跑Visual Studio2015年你得到了。 按一下通過他們像平常一樣。當您下載調試符號Visual Studio本身,也可能會看到一些延誤。後第一次,Visual Studio應為你緩存這些符號。

一旦您調試物件的Visual Studio實例正在運行,請使用它來創建 C# 主控台應用程式。因為您正在調試的分析器是Visual Studio-寬.vsix 副檔名,您應該會看到綠色的波浪線,出現在幾秒鐘內程式類定義。如果您將滑鼠懸停在此曲線,或看看錯誤清單,如中所示,您會看到的消息,"類型名稱 '程式' 包含小寫字母" 圖 2。當你點擊波形曲線上時,您將看到一個燈泡圖示顯示在左側。點擊燈泡圖示將顯示一個清理診斷程式通過改變該類型名稱,可以是大寫的"讓大寫"修復。

從分析器範本代碼修復
圖 2 從分析器範本代碼修復

你能夠調試從這裡,以及分析器。在你的Visual Studio的主要實例中,設置一個中斷點裡面的初始化方法內診斷­Analyzer.cs。 在編輯器中鍵入時,分析儀不斷重新計算診斷。下次您鍵入內 Program.cs 在調試物件Visual Studio實例中,您將看到調試器在該中斷點處停止。

保持主控台應用程式專案打開,現在,你會在下一節中使用它進一步。

檢查使用語法視覺化檢視的相關代碼

現在,你要面向分析器範本中,是時候開始規劃什麼你會尋找在決定什麼時候波形曲線的分析代碼的代碼模式。

你的目標是介紹在您編寫一個不正確正則運算式模式時顯示的錯誤。第一,在主控台應用程式的 Main 方法中,添加以下行調用 Regex.Match 具有不正確正則運算式模式:

Regex.Match("my text", @"\pXXX");

看著這段代碼,在正則運算式和匹配上徘徊,您可以使用當您想要生成的波形曲線的條件:

  • 還有對 Regex.Match 方法的調用。
  • 涉及的正則運算式類型是 System.Text.RegularExpressions 命名空間中。
  • 該方法的第二個參數是一個字串,表示不正確正則運算式模式。在實踐中,運算式可能也是變數或常量的引用 — — 或計算的字串 — — 但對於此初始版本的分析器,我會首先集中于字串字面值。通常,最好的是要分析儀工作端到端為一個簡單的例子之前在您著手支援更多的代碼模式。

所以,你將如何轉化這些簡單的約束為.NET 編譯器平臺代碼?語法視覺化檢視是一個偉大的工具,來説明推斷出來的。

要安裝該視覺化檢視在您一直在使用調試分析儀的實驗沙箱中。您可能已經安裝該視覺化檢視較早前,但安裝程式只安裝套裝軟體,變成你主要的Visual Studio。

當你仍然在您調試物件的Visual Studio實例中時,打開工具 |擴展 & 更新 |線上,並搜索Visual Studio畫廊"語法視覺化檢視"。下載並安裝.NET 編譯器平臺語法視覺化檢視套裝軟體。然後選擇立即重新開機,重新開機Visual Studio。

一旦重新開機Visual Studio,開放相同的主控台應用程式專案並打開語法視覺化檢視,請選擇視圖 |其他視窗 |羅斯林語法視覺化檢視。你現在可以移動插入符號編輯器周圍,看著語法視覺化檢視向您顯示了語法樹的相關部分。圖 3 顯示 Regex.Match 調用運算式的視圖,你有興趣在這裡。

行動目標調用運算式中的語法視覺化檢視
圖 3 行動目標調用運算式中的語法視覺化檢視

語法樹的部分在語法樹中流覽周圍時,你會看到各種元素。

藍樹中的節點是代碼的語法節點,表示您的邏輯樹狀結構,編譯器已解析該檔後。

綠樹中的節點是語法標記,個別的單詞、 數位和符號編譯器找到時,它讀取原始檔案。標記所示他們屬於語法節點下的樹。

樹中的紅色節點是瑣事,代表一切,不是標記:空白,評論等等。一些編譯器拋出這一資訊,但.NET 編譯器平臺抓住它,不放,這樣您的代碼修復可以保持這類瑣事需要您修補程式更改使用者的代碼時。

通過在編輯器中選擇代碼,您可以看到相關的節點在樹中,反之亦然。為了説明視覺化你關心的節點,可以上 InvocationExpression 在視覺化檢視語法樹中按右鍵並選擇視圖定向語法圖。這將生成一個.dgml 圖,直觀顯示樹狀結構下面所選的節點,如中所示圖 4

為目標調用運算式的的語法圖
圖 4 為目標調用運算式的的語法圖

在這種情況下,您可以看到您正在尋找一個調用­是的 Regex.Match,ArgumentList 有第二個參數節點包含 StringLiteralExpression 調用的運算式。如果字串值表示不正確正則運算式模式,如"\pXXX,"你已經找到要波形曲線的範圍。您現在收集到了大部分寫你診斷的分析器所需的資訊。

符號和語義模型:要超越語法樹語法節點、 權杖、 瑣事示語法視覺化檢視代表該檔的完整文本的同時,他們沒告訴你每個­的事情。你還需要知道每個代碼中的識別碼實際上引用。例如,您知道此調用是對正則運算式類型具有兩個參數上的匹配方法的調用,但你不知道正則運算式類型或調用哪個重載匹配的命名空間。發現到底什麼定義引用需要編譯器用來分析他們附近使用的上下文中的識別碼的指令。

回答這類問題要求你提出要給你與一個給定的運算式節點關聯的符號的語義模型。符號表示的代碼定義了,如你的類型和方法的邏輯實體。找出給定的運算式引用的符號的過程被稱為綁定。符號還可以表示你消耗從被引用的庫,如正則運算式類型從基類庫 (BCL) 的實體。

如果您按右鍵調用­的表達,然後選擇下面從被調用的方法的方法符號的資訊填充的屬性網格視圖符號,如中所示圖 5

在語法視覺化檢視中查看 Regex.Match 方法符號
圖 5 在語法視覺化檢視中查看 Regex.Match 方法符號

在這種情況下,你可以看看原始­定義屬性,看看指的調用 System.Text.RegularExpressions.Regex.Match 方法和不上其他一些正則運算式類型的方法。寫作你的診斷是,綁定該調用並檢查象徵你回來在拼圖的最後一塊與 System.Text.RegularExpressions.Regex.Match 的字串匹配。

建立診斷程式

現在,你已經得到你的策略,關閉調試物件Visual Studio實例並返回到您的分析器專案開始建立你的診斷。

SupportedDiagnostics 屬性診斷程式打開­Analyzer.cs 檔,看看四個字串常量頂部附近。這是為您診斷的規則定義中繼資料的地方。即使您的分析器產生任何的花體字之前,規則集編輯器和其他Visual Studio功能將使用此中繼資料知道的診斷您的分析器可能會產生的細節。

更新這些要匹配診斷您計畫產生的正則運算式的字串:

public const string DiagnosticId = "Regex";
internal const string Title = "Regex error parsing string argument";
internal const string MessageFormat = "Regex error {0}";
internal const string Category = "Syntax";

當此診斷產生錯誤清單中的使用者顯示診斷 ID 和填充在消息字串。使用者也可以使用標識在他 #pragma 指令中的原始程式碼中壓制的診斷實例。在後續的文章中,我將展示如何使用此 ID 來將您的代碼修復與此規則相關聯。

在行中聲明規則欄位,您也可以更新的嚴重程度的診斷程式,你就會產生錯誤,而不是警告。如果正則運算式字串不解析,匹配方法肯定將引發運行時異常,你應該阻止生成一個 C# 編譯器錯誤那樣。對 DiagnosticSeverity.Error 更改該規則的嚴重性:

internal static DiagnosticDescriptor Rule =
  new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat,
   Category, DiagnosticSeverity.Error, isEnabledByDefault: true);

這也是你決定從哪裡是否應該在預設情況下啟用該規則的行。你分析儀可以定義預設情況下,處於關閉的規則一大套,使用者可以加入宣告一些或所有的規則。離開,預設情況下啟用此規則。

SupportedDiagnostics 屬性返回該診斷­作為一個不可變數組的單個元素描述項。在這種情況下,您的分析器只會產生一種診斷,所以沒什麼可在此處進行更改。如果你分析儀可以產生多種類型的診斷程式,您可以使多個描述項,並返回所有從 SupportedDiagnostics。

初始化方法為您診斷的分析器的主進入點是初始化方法。在此方法中,您註冊了一整套行動來處理各種事件,編譯器將觸發,它將遍歷您的代碼,如發現語法的各個節點,或遇到的一個新的符號聲明。傻預設分析器可以從範本調用 RegisterSymbolAction 來找出當鍵入符號變化或介紹。在這種情況下,象徵行動讓我們看看每個類型的符號,來看看是否它指示需要歪歪扭扭的臭名型分析儀。

在這種情況下,您需要註冊一個 SyntaxNode 操作,以便找出時 Regex.Match 的新呼籲。 記得從探索你正在尋找特定的節點類型是 InvocationExpression 的語法視覺化檢視,所以,更換註冊調用中的初始化方法與下面的調用:

context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression);

正則運算式分析器只需註冊生產診斷本地的語法節點操作。跨多個方法收集資料的更複雜的分析呢?更多關於這在這篇文章中看到"處理其他登記冊的方法"。

AnalyzeNode 方法刪除範本的 AnalyzeSymbol 方法,您不再需要。在它的地方,您將創建一個 AnalyzeNode 方法。AnalyzeNode 在 RegisterSyntaxNodeAction 調用中按一下,然後按 Ctrl + 點拉起新燈泡功能表。從那裡,選擇生成方法來創建一個 AnalyzeNode 方法具有正確的簽名。在生成的 AnalyzeNode 方法中,更改該形參的名稱從"obj"到"範圍內"。

現在,您已經看到了你的分析儀的核心,它是時間去檢查,看看是否你應該表面診斷的語法節點。

首先,您應該按 Ctrl + F5 啟動調試物件實例的Visual Studio再一次 (這一次沒有調試)。打開主控台應用程式,你只在看著,並確保語法視覺化檢視是可用。幾次你就會看看視覺化檢視來找到相關的詳細資訊,當你建立起的 AnalyzeNode 方法。

獲得目標節點 AnalyzeNode 方法的第一步是採取你正在分析的節點物件,但將它強制轉換為相關類型。若要找到該類型,請使用語法視覺化檢視,你只是開。選擇 Regex.Match 調用,然後在語法樹到 InvocationExpression 節點中導航。你可以看到屬性網格的上方 InvocationExpression 是那種節點,而類型為 InvocationExpressionSyntax。

你可以測試節點類型與類型檢查或用不但方法測試其具體種類。但是,在這裡你可以保證強制轉換會成功沒有任何一種測試,因為你的要求的特定類型的初始化方法中。你的行為分析的節點可從上下文參數的節點屬性為:

var invocationExpr = (InvocationExpressionSyntax)context.Node;

現在,你有調用節點,您需要檢查它是否需要歪歪扭扭的 Regex.Match 電話。

**支票號 1:這是對一種匹配方法的調用嗎?**你需要的第一次檢查是確保此調用是對正確的 Regex.Match 的調用。 因為此分析器會對每次按鍵在編輯器中運行的它是一個好的主意,要首先執行最快的測試和 API 的更昂貴提問,只有那些最初的測試通過。

最便宜的測試是看看是否調用語法是對命名匹配的方法的調用。這可以確定編譯器已完成任何特殊的工作,找出哪些特定的匹配方法,這是之前。

回看語法視覺化檢視,你看到,調用­運算式中有兩個主要的子節點,SimpleMemberAccess­表達及 ArgumentList。在編輯器中選擇匹配識別碼,如中所示的圖 6,你可以看到你要找的節點是內簡單的第二個 IdentifierName­MemberAccessExpression。

在語法樹中找到的匹配識別碼
圖 6 在語法樹中找到的匹配識別碼

分析器生成時,你會經常挖到語法和這樣找到的相關類型和需要在代碼中引用的屬性值的符號。分析儀樓時,方便隨身攜帶與語法視覺化檢視的目標專案。

回來在分析器代碼中,您可以流覽 invocationExpr 在IntelliSense的成員,並找到對應的 InvocationExpression 的子節點,每個屬性之一命名運算式和一個命名的 ArgumentList。在這種情況下,您想要指定運算式的屬性。因為之前的參數清單調用的一部分可以有多種形式 (例如,它可能是委託調用的一個本地變數),則此屬性返回一般的基類型,ExpressionSyntax。從語法的視覺化檢視,您可以看到您所期望的具體類型是 MemberAccessExpressionSyntax,所以強制轉換為該類型:

var memberAccessExpr =
  invocationExpr.Expression as MemberAccessExpressionSyntax;

當你深入上 memberAccessExpr 的屬性,您會看到類似細目。有代表之前點的任意運算式的運算式屬性和右邊的圓點表示的識別碼的名稱屬性。因為你想要檢查,看看是否你打電話匹配方法,請檢查名稱屬性的字串值。對於語法節點,獲取字串值是快速的方法來獲取該節點的源文本。您可以使用最新的 C#"?。"運算子來處理的情況,在那裡你分析的運算式不是實際上的成員訪問,導致前一行"as"子句返回空值:

if (memberAccessExpr?.Name.
    ToString() != "Match") return;

如果被調用的方法不具名引數匹配,您只需保釋出來。你的分析是最小的代價完成並沒有診斷來生成。

**支票號 2:這是對真正的 Regex.Match 方法的調用嗎?**如果該方法命名比賽,做更多地參與的檢查,要求編譯器來確定精確的匹配方法代碼正在調用。確定精確的匹配需要請求上下文的語義模型來綁定此運算式要引用的符號。

在語義模型上調用 GetSymbolInfo 方法,通過它,你想要的符號運算式:

var memberSymbol =
  context.SemanticModel.GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol;

你回來的符號物件是同一個,用滑鼠右鍵按一下 SimpleMemberAccessExpression,然後選擇視圖符號,您可以預覽中語法視覺化檢視。在這種情況下,你選擇投下的通用的 IMethodSymbol 介面的符號。所提及的語法視覺化檢視中該元件內部的 PEMethodSymbol 類型實現此介面。

既然你已經有符號,你可以比較你期望從真正的 Regex.Match 方法的完全限定名稱。為一種象徵,獲取字串值會給你它的完全限定的名。你真的不在乎你打不過,哪個重載,這樣你就可以只是檢查到詞匹配:

if (!memberSymbol?.ToString().
  StartsWith("System.Text.RegularExpressions.Regex.Match") ?? true) return;

當與你以前的測試,檢查,看看如果符號匹配的名稱,你期望,和如果不匹配,或如果不是實際上是方法的符號,保釋出來。雖然它可能會感到有點奇怪,在這裡的字串上操作,字串比較是在編譯器中的常見操作。

其餘檢查現在您的測試開始陷入一種節奏。每一步,你挖遠一點,到了樹上,並檢查語法節點或語義模型來測試如果您仍處於錯誤狀態。每一次,你可以使用語法視覺化檢視看到你期望什麼類型和屬性值,所以你知道在這種情況下返回,在這種情況下繼續。你會遵循這種模式來檢查接下來的幾個條件。

請確保 ArgumentList 具有至少兩個參數:

var argumentList = invocationExpr.ArgumentList as ArgumentListSyntax;
if ((argumentList?.Arguments.Count ?? 0) < 2) return;

然後確保第二個參數是 LiteralExpression,因為你正期待一個字串文字:

var regexLiteral =
  argumentList.Arguments[1].Expression as LiteralExpressionSyntax;
if (regexLiteral == null) return;

最後,一旦你知道它是文本,你可以問給你其常量的編譯時間值,並確保它是具體的字串文本的語義模型:

 

var regexOpt = context.SemanticModel.GetConstantValue(regexLiteral);
if (!regexOpt.HasValue) return;
var regex = regexOpt.Value as string;
if (regex == null) return;

驗證正則運算式模式在這一點上,你有你需要的所有資料。你知道你打電話 Regex.Match,和你有模式運算式的字串值。那麼如何驗證它?

只需調用同一個 Regex.Match 方法,並在該模式字串中傳遞。因為你只在尋找解析錯誤,模式字串中,可以將一個空的輸入的字串作為第一個參數傳遞。打電話的 try-catch 塊中的代碼,你可以看看 Regex.Match,當它看到不正確模式字串會引發 ArgumentException:

try
{
  System.Text.RegularExpressions.Regex.Match("", regex);
}
catch (ArgumentException e)
{
}

如果模式字串解析沒有錯誤,您的 AnalyzeNode 方法將正常退出,還有報告無關。如果分析錯誤,你就會趕上參數異常 — — 你準備報告診斷 !

報告診斷在 catch 塊中,您可以使用規則物件你早些時候填寫要創建一個診斷的物件,表示要生成一個特定波形。每個診斷需求兩個主要的東西特定于該實例:應胡亂塗抹出來的代碼和填補字元串插入到您在前面定義的消息格式的間距:

var diagnostic =
    Diagnostic.Create(Rule,
    regexLiteral.GetLocation(), e.Message);

在這種情況下,您想要波形曲線字面字串,所以你通過在其位置作為診斷的跨度。你還拔出的描述是什麼毛病,模式字串的異常消息,包含在診斷消息。

最後一步是要進行診斷和報告回上下文傳遞給 AnalyzeNode,所以Visual Studio知道向錯誤清單添加行,並在編輯器中添加的波形曲線:

context.ReportDiagnostic(diagnostic);

您在 DiagnosticAnalyzer.cs 的代碼現在應該看起來像圖 7

圖 7 DiagnosticAnalyzer.cs 的完整代碼

using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace RegexAnalyzer
{
  [DiagnosticAnalyzer(LanguageNames.CSharp)]
  public class RegexAnalyzerAnalyzer : DiagnosticAnalyzer
  {
    public const string DiagnosticId = "Regex";
    internal const string Title = "Regex error parsing string argument";
    internal const string MessageFormat = "Regex error {0}";
    internal const string Category = "Syntax";
    internal static DiagnosticDescriptor Rule =
      new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat,
      Category, DiagnosticSeverity.Error, isEnabledByDefault: true);
    public override ImmutableArray<DiagnosticDescriptor>
      SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
    public override void Initialize(AnalysisContext context)
    {
      context.RegisterSyntaxNodeAction(
        AnalyzeNode, SyntaxKind.InvocationExpression);
    }
    private void AnalyzeNode(SyntaxNodeAnalysisContext context)
    {
      var invocationExpr = (InvocationExpressionSyntax)context.Node;
      var memberAccessExpr =
        invocationExpr.Expression as MemberAccessExpressionSyntax;
      if (memberAccessExpr?.Name.ToString() != "Match") return;
      var memberSymbol = context.SemanticModel.
        GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol;
      if (!memberSymbol?.ToString().StartsWith(
        "System.Text.RegularExpressions.Regex.Match") ?? true) return;
      var argumentList = invocationExpr.ArgumentList as ArgumentListSyntax;
      if ((argumentList?.Arguments.Count ?? 0) < 2) return;
      var regexLiteral =
        argumentList.Arguments[1].Expression as LiteralExpressionSyntax;
      if (regexLiteral == null) return;
      var regexOpt = context.SemanticModel.GetConstantValue(regexLiteral);
      if (!regexOpt.HasValue) return;
      var regex = regexOpt.Value as string;
      if (regex == null) return;
      try
      {
        System.Text.RegularExpressions.Regex.Match("", regex);
      }
      catch (ArgumentException e)
      {
        var diagnostic =
          Diagnostic.Create(Rule, regexLiteral.GetLocation(), e.Message);
        context.ReportDiagnostic(diagnostic);
      }
    }
  }
}

嘗試它就是這樣 — — 你的診斷,現已完成 !嘗試它,只需按 F5 (確保 RegexAnalyzer.VSIX 是啟動專案),然後重新打開主控台應用程式中的Visual Studio調試物件實例。你應該很快就看到紅色的波浪線上指出為何未能解析,如中所示的模式運算式圖 8

嘗試你診斷分析儀
圖 8 嘗試你診斷分析儀

如果你看到波形曲線上時,恭喜你 !如果不是這樣,你可以 AnalyzeNode 方法內設置一個中斷點,鍵入裡要觸發重新分析,然後逐句通過分析器代碼,以查看在哪裡分析器早被困的模式字串的字元。您還可以檢查您的代碼對圖 7,其中顯示了完整的代碼為 DiagnosticAnalyzer.cs。

用例診斷分析儀

總括來說,從分析器範本和編寫新,大約 30 行你是代碼的能夠識別並提供一個現實的問題,在您的使用者代碼中的波形曲線。最重要的是,這樣做並不需要你成為一個深的專家,在 C# 編譯器的操作。你就能夠保持專注于你的正則運算式的目標域和使用語法視覺化檢視,引導你的語法節點和符號與你分析有關小的一套。

在哪裡寫診斷分析儀可以快速且有用你日常編碼中有多個域:

  • 作為一個開發人員或團隊領導,你可能會看到別人犯同樣的錯誤,往往當你進行代碼審查。現在,你可以寫一個簡單的分析儀,squiggles 這些反模式並檢查分析儀入原始程式碼管理中,確保鍵入內容介紹了這樣一個錯誤的人會注意到它。
  • 作為一個共用的層定義為您的組織的業務物件的維護者,你可能有正確使用的是硬編碼在類型系統中,特別是如果他們涉及數值,或者如果他們涉及某些操作始終應該去的地方的過程中的步驟來在別人面前這些物件的商務規則。現在您可以實施你的圖層,使用這些軟規則撿的類型系統的留下。
  • 作為開源或商業 API 包擁有者,你可能會厭倦回答同樣的問題一再在論壇中。你甚至可能寫白皮書和文獻,發現您的許多客戶繼續打那些同樣的問題,因為他們沒有讀過你寫的東西。現在你可以捆綁您的 API 和相關代碼分析成一個 NuGet 套裝程式,確保使用您的 API 的每個人看到的相同的指導,從一開始。

我們希望,這篇文章能激發你想要打造提升自己的專案分析儀。與.NET 編譯器平臺,微軟最近做的繁重工作使深的語言理解和豐富的代碼分析 C# 和Visual Basic— — 剩下的就靠你了 !

下一個是什麼?

所以,現在你有Visual Studio顯示錯誤不正確正則運算式模式下的波形曲線。你可以做更多嗎?

如果你的正則運算式域知識,看到什麼不對勁的模式字串不僅如何修復它,你可以建議一個燈泡,修復,正如您看到的範本預設分析器中。

在下一篇文章中,我將展示如何編寫該代碼修復,當你學習如何更改您的語法樹。下次見!

處理其他註冊方法

你可以挖到的初始化方法上下文參數,即可看到完整的註冊方法可以調用集。方法在圖 A 讓你鉤編譯器的管道中的各種事件。

要注意的關鍵點是註冊的註冊紀錄冊的任何方法的頂級行動應該永遠不會藏在分析器類型上的實例欄位的任何國家。Visual Studio將重用要避免重複的分配整個Visual Studio會話的分析器類型的一個實例。你存儲和重用的任何國家很可能是陳舊的分析未來的編譯時,可以甚至記憶體洩漏,如果它阻止舊語法節點或符號被垃圾收集。

如果您需要保留狀態,整個行動,應致電 RegisterCodeBlockStartAction 或 RegisterCompilationStartAction,作為該行動方法中的區域變數存儲狀態。傳遞給這些操作的上下文物件允許您註冊嵌套的操作為 lambda 運算式和這些嵌套的操作可以關閉當地人在外在的行為以保持狀態。

圖 A 註冊方法掛接各種事件

RegisterSyntaxNodeAction 當一種特殊的語法節點已解析時觸發
RegisterSymbolAction 在分析了一種特定符號時觸發
RegisterSyntaxTreeAction 當解析檔的整個語法樹時觸發
RegisterSemanticModelAction 當一個語義模型可用於整個檔時觸發

RegisterCodeBlockStartAction

RegisterCodeBlockEndAction

觸發前分析方法體或其他代碼塊後

RegisterCompilationStartAction

RegisterCompilationEndAction

觸發之前之後的整個專案分析

Alex Turner 是的微軟,在那裡他已經醞釀了.NET 編譯器平臺 ("羅斯林") 專案的 C# 和Visual Basic善的託管語言團隊高級專案經理。他畢業于紐約州立大學石溪分校的電腦科學碩士學位,並已生成、 PDC、 TechEd、 TechDays 和混合的會議上講了話。

感謝以下的微軟技術專家對本文的審閱:Bill輔以辣椒和盧西安 Wischik
Bill辣椒在語言 (CMU Common Lisp,狄倫、 IronPython 和 C#) 工作和開發人員工具他職業生涯的大部分。他花了過去 17 年來在工作一切從核心功能Visual Studio,向動態語言運行時的語言,C# 的微軟開發部。

盧西恩 • Wischik 是Visual Basic/C# 語言設計團隊在微軟的Visual Basic基本負起特殊的責任。在加入微軟之前他在學術界對併發性理論與非同步工作。他是一個熱衷於水手和長距離游泳的選手。