Il presente articolo è stato tradotto automaticamente.

C# e Visual Basic

Roslyn uso scrivere un analizzatore di codice dal vivo per le API

Alex Turner

Per 10 anni, analisi del codiceVisual Studio ha fornito analisi del tempo di compilazione degli assembly c# e Visual Basic , in esecuzione di un insieme specifico di regole FxCop scritti per la Microsoft .NET Framework 2.0. Queste regole sono grandi ad aiutare a evitare le insidie nel codice, ma essi stanno mirati a problemi gli sviluppatori ha colpiti nel 2005. Che dire di oggi nuove funzionalità del linguaggio e le API?

Con analizzatori di codice live, basati su progetti in anteprima Visual Studio 2015, autori di API possono spedire analisi codice specifico di dominio come parte dei loro pacchetti NuGet. Perché questi analizzatori sono alimentati dalla piattaforma .NET compilatore (nome in codice "Roslyn"), possono produrre avvisi nel codice durante la digitazione, prima ancora di aver finito la linea — non più in attesa di compilare il codice per scoprire che hai fatto un errore. Analizzatori possono anche superficie una correzione automatica del codice attraverso il nuovo Visual Studio lampadina prompt per farti ripulire il codice immediatamente.

Molti di questi analizzatori di codice dal vivo pesa appena 50 a 100 righe di codice, e non devi essere un atleta di compilatore per scriverle. In questo articolo vi mostrerò come scrivere un analizzatore che gli obiettivi di un problema di codifica comune colpito da coloro che utilizzano le espressioni regolari (regex) di .NET: Come assicurarsi che il pattern regex che hai scritto è sintatticamente valido prima di eseguire l'app. Farò questo mostrando come scrivere un analizzatore che contiene una diagnostica segnalare pattern regex non valido. Io seguirò questo articolo con una seconda rata dopo, dove ti mostrerò come aggiungere in una correzione del codice per ripulire i vostro analizzatore rileva errori.

Preparazione

Per iniziare, assicuratevi di che avere i necessari Visual Studio 2015 bit di anteprima:

  • Impostare una casella con Visual Studio 2015 anteprima in uno di questi due modi:
    1. Installare Visual Studio 2015 anteprima da aka.ms/vs2015preview.
    2. Afferrare un'immagine di Azure VM precompilata da aka.ms/vs2015azuregallery.
  • Installare il SDK di anteprima Visual Studio 2015 da aka.ms/vs2015preview. Sarà necessario farlo anche se si sta utilizzando l'immagine di Azure VM.
  • Installare il pacchetto SDK modelli VSIX da aka.ms/roslynsdktemplates per ottenere i modelli di progetto di piattaforma del compilatore .NET.
  • Installare il pacchetto VSIX Visualizer sintassi da aka.ms/roslynsyntaxvisualizer per ottenere una finestra Visualizzatore sintassi per aiutare ad esplorare gli alberi di sintassi si analizzando.
  • È inoltre necessario installare il visualizzatore di sintassi in sandbox Visual Studio sperimentale che userai per vostro analizzatore di debug. Vado a piedi attraverso come farlo nella sezione sintassi Visualizer più tardi.

Esplorare il modello Analyzer

Una volta sei in su e in esecuzione con Visual Studio 2015, la Visual Studio SDK e i pacchetti necessari VSIX, Scopri il modello di progetto per la costruzione di un analizzatore.

All'interno di Visual Studio 2015, andare su File | Nuovo progetto | C# | Estensibilità e scegliere la diagnostica con il modello di codice Difficoltà (NuGet + VSIX), come mostrato Figura 1. È possibile creare analizzatori in Visual Basic, pure, ma per questo articolo userò c#. Assicurarsi che il framework di destinazione è impostato su .NET Framework 4.5 in cima. Dare al progetto il nome RegexAnalyzer e selezionare OK per creare il progetto.

creazione del progetto Analyzer
Figura 1 creazione del progetto Analyzer

Vedrete un set di tre progetti che genera il modello:

  • RegexAnalyzer: Questo è il progetto principale che costruisce l'analizzatore DLL contenente la diagnostica e il codice di Difficoltà. Questo progetto di costruzione produce anche un pacchetto di NuGet progetto locale (file .nupkg) contenente l'analizzatore.
  • RegexAnalyzer.VSIX: Questo è il progetto che impacchetta il vostro analizzatore DLL in uno Visual Studio-pacchetto di vasta estensione (VSIX file). Se vostro analizzatore non ha bisogno di aggiungere avvisi che riguardano Build, è possibile distribuire questo file VSIX, piuttosto che il pacchetto di NuGet. In entrambi i casi, il progetto VSIX consente di premere F5 e testare il vostro analizzatore in un'istanza separata (oggetto) del Visual Studio.
  • RegexAnalyzer.Test: Questo è un progetto di unit test che consente di assicurarsi che il vostro analizzatore sta producendo la giusta diagnosi e correzioni, senza in esecuzione l'istanza oggetto di Visual Studio ogni volta.

Se aprite il file DiagnosticAnalyzer.cs nel progetto principale, si può vedere il codice di default del modello che produce diagnostica. Diagnostico fa qualcosa di un po ' sciocco — "scarabocchi" nomi di qualsiasi tipo che hanno le lettere minuscole. Tuttavia, perché la maggior parte dei programmi avranno tali nomi tipo, consente di vedere facilmente l'analizzatore in azione.

Assicurarsi che il progetto RegexAnalyzer.VSIX è il progetto di avvio e premere F5. Esecuzione del progetto VSIX carica una copia sandbox sperimentale di Visual Studio, che permette di tenere traccia di un insieme distinto di estensioni Visual Studio Visual Studio . Questo è utile come si sviluppano le tue estensioni e necessario eseguire il debug Visual Studio con Visual Studio. Perché l'istanza di debug Visual Studio è un nuovo sandbox sperimentale, vedrete le finestre di dialogo stessi che ottenuto prima Visual Studio 2015. Clicca attraverso di loro, come normale. Si potrebbero anche vedere alcuni ritardi come si scarica i simboli di debug per Visual Studio stesso. Dopo la prima volta, Visual Studio dovrebbe memorizzare quei simboli per voi.

Una volta che l'istanza di Visual Studio oggetto del debug è in esecuzione, utilizzarlo per creare un'applicazione console c#. Perché l'analizzatore si stai debug è il Visual Studio-VSIX ampia estensione, si dovrebbe vedere un verde squiggle appaiono nella definizione di classe del programma entro pochi secondi. Se si passa il mouse sopra questo scarabocchio o guardare la lista di errore, vedrai il messaggio, "il nome del tipo 'Programma' contiene lettere minuscole," come mostrato Figura 2. Quando si fa clic su linee a zigzag, vedrai un'icona lampadina appaiono sulla sinistra. Cliccando sull'icona lampadina mostrerà una difficoltà di "Rendere maiuscola" che pulisce la diagnostica cambiando il nome del tipo essere maiuscoli.

la correzione del codice dal modello Analyzer
Figura 2 la correzione del codice dal modello Analyzer

Sei in grado di eseguire il debug l'analizzatore da qui, pure. Nella vostra istanza principale di Visual Studio, impostare un punto di interruzione all'interno del metodo Initialize nell'ambito diagnostico­Analyzer.cs. Durante la digitazione nell'editor, analizzatori di ricalcolare continuamente diagnostica. La prossima volta che si digita in Program.cs nell'istanza oggetto del debug Visual Studio , vedrai il debugger arrestarsi a quel punto di interruzione.

Tenere aperto per ora, il progetto di applicazione console come userete ulteriormente nella sezione successiva.

Ispezionando il codice corrispondente utilizzando il visualizzatore di sintassi

Ora che sei orientato nel modello di analizzatore, è il momento di iniziare a pianificare quali modelli di codice cercherai nel codice analizzato per decidere quando scarabocchio.

Il vostro obiettivo è quello di introdurre un errore che si presenta quando si scrive un pattern regex non valido. In primo luogo, all'interno del metodo Main dell'applicazione console, aggiungere la seguente riga che chiama Regex con un pattern regex non valido:

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

Guardando questo codice e si aggirano su Regex e Match, è possibile utilizzare le condizioni per quando si desidera generare un linee a zigzag:

  • C'è una chiamata al metodo Regex.
  • Il tipo Regex coinvolto è quello dello spazio dei nomi System.Text.RegularExpressions.
  • Il secondo parametro del metodo è una stringa letterale che rappresenta un modello di espressione regolare non valida. In pratica, l'espressione potrebbe anche essere un riferimento costante o variabile — o una stringa calcolata — ma per questa versione iniziale di analizzatore, mi concentrerò prima su valori letterali stringa. Spesso è meglio ottenere un analizzatore to end di lavoro per un semplice caso prima di spostare supportare ulteriori modelli di codice.

Quindi, come a tradurre questi vincoli semplici in codice .NET compilatore piattaforma? Il visualizzatore di sintassi è un ottimo strumento per aiutare a capirlo.

Ti consigliamo di installare il visualizzatore all'interno della sandbox sperimentale che sei stato utilizzando analizzatori di debug. Potrebbe aver installato il visualizzatore all'inizio, ma l'installer installa solo il pacchetto nel vostro principale Visual Studio.

Mentre siete ancora nell'istanza oggetto del debug Visual Studio , aprire strumenti | Estensioni & Aggiornamenti | On-line e cercare la Galleria Visual Studio "visualizzatore di sintassi". Scaricare e installare il pacchetto .NET compilatore piattaforma sintassi Visualizer. Quindi scegliere Riavvia ora per riavviare Visual Studio.

Una volta che si riavvia Visual Studio , aprire lo stesso progetto di applicazione console e aprire il visualizzatore di sintassi scegliendo vista | Altri Windows | Roslyn sintassi Visualizer. È possibile spostare il cursore intorno l'editor e guarda come il visualizzatore di sintassi vi mostra la parte pertinente dell'albero della sintassi. Figura 3 viene illustrata la visualizzazione per l'espressione di chiamata Regex che ti interessa qui.

il visualizzatore di sintassi in azione per l'espressione di chiamata di destinazione
Figura 3 il visualizzatore di sintassi in azione per l'espressione di chiamata di destinazione

Le parti dell'albero della sintassi come si naviga intorno nella struttura della sintassi, vedrai vari elementi.

I blu nodi nell'albero sono sintassi, che rappresenta la struttura logica del codice, dopo che il compilatore ha analizzato il file.

I nodi nell'albero verdi sono la sintassi dei token, le singole parole, numeri e simboli il compilatore trovato quando leggono i file di origine. I token sono mostrati nell'albero sotto i nodi di sintassi a cui appartengono.

I nodi nell'albero rossi sono la curiosità, tutto ciò che non è un token che rappresenta: spazi vuoti, commenti e così via. Alcuni compilatori generano queste informazioni via, ma la piattaforma .NET compilatore detiene su di esso, così il fix codice può mantenere la curiosità come necessario quando il fix cambia il codice utente.

Selezionando il codice nell'editor, è possibile vedere i relativi nodi nell'albero e viceversa. Per aiutare a visualizzare i nodi che ti interessa, puoi fare clic destro sull'InvocationExpression nella struttura della sintassi visualizzatore e scegliere visualizzazione diretta sintassi grafico. Questo genererà un diagramma DGML che visualizza la struttura ad albero sotto il nodo selezionato, come mostrato Figura 4.

sintassi grafico per l'espressione di chiamata di destinazione
Figura 4 sintassi grafico per l'espressione di chiamata di destinazione

In questo caso, si può vedere che stai cercando un'invocazione­espressione che rappresenta una chiamata a Regex, dove il ArgumentList ha un secondo nodo argomento che contiene un StringLiteralExpression. Se il valore di stringa rappresenta un pattern regex non valido, ad esempio "\pXXX", hai trovato l'arco a scarabocchio. Tu ora hai raccolto la maggior parte delle informazioni necessarie per scrivere il vostro analizzatore diagnostico.

Simboli e il modello semantico: Andando oltre l'albero sintattico mentre i nodi di sintassi, token e curiosità mostrata nel rappresenti sintassi visualizzatore il testo completo del file, non vi dico ogni­cosa. Devi anche sapere che cosa fa riferimento in realtà ogni identificatore nel codice. Ad esempio, sai che questa chiamata è una chiamata al metodo Match su un tipo di Regex con due parametri, ma non sai quale spazio dei nomi tipo Regex nel o viene chiamato l'overload di Match. Scoprire esattamente quali definizioni fanno riferimento richiede i compilatori per analizzare gli identificatori nel contesto della loro vicina utilizzando direttive.

Rispondendo a questi tipi di domande richiede di chiedere il modello semantico per darvi il simbolo associato a un nodo di espressione specificata. I simboli rappresentano le entità logiche che il codice definisce, quali tipi e metodi. Il processo di capire il simbolo a cui fa riferimento una determinata espressione è conosciuto come vincolanti. Simboli possono anche rappresentare le entità che si consumano dalle librerie di riferimento, ad esempio il tipo Regex dalla biblioteca BCL (Base Class).

Se fare clic destro sull'invocazione­espressione e scegliere Mostra simbolo, la griglia delle proprietà di sotto si riempie di informazioni dal simbolo del metodo del metodo richiamato, come mostrato Figura 5 .

visualizzazione nel visualizzatore di sintassi del simbolo del metodo Regex.
Figura 5 visualizzazione nel visualizzatore di sintassi del simbolo del metodo Regex.

In questo caso si può guardare l'originale­proprietà di definizione e vedere che l'invocazione si riferisce al metodo System.Text.RegularExpressions.Regex.Match e non un metodo su alcuni altro tipo di Regex. L'ultimo pezzo del puzzle per iscritto che la diagnostica è possibile associare tale invocazione e verifica che il simbolo si ottiene indietro corrisponde alla stringa System.Text.RegularExpressions.Regex.Match.

Costruzione di diagnostica

Ora che hai la tua strategia, chiudere l'istanza di Visual Studio oggetto del debug e tornare al progetto analyzer per iniziare a costruire il tuo diagnostico.

La proprietà SupportedDiagnostics aprono la diagnostica­file Analyzer.cs e guardare le costanti di quattro stringa vicino la cima. Questo è dove si definiscono i metadati per la regola di diagnostica. Prima ancora di vostro analizzatore produce qualsiasi scarabocchi, l'Editor di Ruleset e altre caratteristiche Visual Studio utilizzerà questi metadati per conoscere i dettagli del sistema diagnostico che potrebbe vostro analizzatore produrre.

Aggiornare queste stringhe per abbinare la Regex diagnostica che si prevede di produrre:

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";

La diagnostica ID e riempita in messaggio stringa sono mostrati agli utenti nella finestra Elenco errori quando è prodotto diagnostico. Un utente può anche utilizzare l'ID nel suo codice sorgente in una direttiva #pragma per eliminare un'istanza di diagnostica. Nell'articolo di follow-up, ti mostrerò come utilizzare questo ID per associare il fix codice a questa regola.

Nella linea che dichiara il campo di regola, è inoltre possibile aggiornare la gravità della diagnostica si potrai essere producendo Errori piuttosto che gli avvisi. Se non analizza la stringa regex, il metodo Match sicuramente verrà generata un'eccezione in fase di esecuzione, e si dovrebbe bloccare la compilazione come si farebbe per un errore del compilatore c#. Modificare la severità della regola a DiagnosticSeverity.Error:

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

Questa è anche la linea dove si decide se la regola dovrebbe essere abilitata per impostazione predefinita. L'analizzatore può definire un più ampio insieme di regole che sono fuori di default, e gli utenti possono scegliere di optare per alcune o tutte le regole. Lasciare questa regola attivata per impostazione predefinita.

La proprietà SupportedDiagnostics restituisce diagnostico­descrittore come singolo elemento di una matrice non modificabile. In questo caso, vostro analizzatore solo produrrà una sorta di diagnostica, quindi non c'è nulla da cambiare qui. Se il vostro analizzatore può produrre più tipi di diagnostica, si potrebbe rendere più descrittori e restituirli tutti da SupportedDiagnostics.

Metodo Initialize il punto di ingresso principale per vostro analizzatore diagnostico è il metodo Initialize. In questo metodo, si registra una serie di azioni per gestire vari eventi, il compilatore genera come cammina attraverso il codice, ad esempio trovando i vari nodi di sintassi o incontrando una dichiarazione di un nuovo simbolo. L'analizzatore di sciocco predefinita si ottiene dal modello chiamate RegisterSymbolAction per scoprire quando digitare simboli cambiamento o vengono introdotti. In tal caso, l'azione simbolo vediamo l'analizzatore ogni simbolo tipo per vedere se indica un tipo con una cattiva reputazione che ha bisogno di uno scarabocchio.

In questo caso, è necessario registrare un'azione SyntaxNode per sapere quando c'è una nuova chiamata a Regex. Ricordiamo da esplorare nel visualizzatore di sintassi che il tipo di nodo specifico che stai cercando è InvocationExpression, quindi sostituire la chiamata a Register nel metodo Initialize con il seguente chiamare:

context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression);

L'analizzatore di regex è necessaria solo registrare un'azione del nodo di sintassi che produce diagnostica localmente. Riguardo le analisi più complesse che raccoglie dati attraverso più metodi? Per ulteriori informazioni su questo, vedere "Gestione degli altri metodi di registrare" in questo articolo.

Metodo AnalyzeNode eliminare il metodo AnalyzeSymbol del modello che non è più necessario. Al suo posto, si creerà un metodo AnalyzeNode. Clicca su AnalyzeNode all'interno della chiamata RegisterSyntaxNodeAction e premere Ctrl + Dot per tirare sul nuovo menu di lampadina. Da lì, scegliere il metodo Generate per creare un metodo di AnalyzeNode con la firma giusta. Nel metodo AnalyzeNode generato, modificare il nome del parametro da "obj" al "contesto".

Ora che hai ottenuto al nucleo del vostro analizzatore, è il momento di esaminare il nodo di sintassi in questione per vedere se si dovrebbe superficie una diagnostica.

In primo luogo, è necessario premere Ctrl + F5 per avviare il debug istanza di Visual Studio . Aprire l'applicazione console che erano solo guardando e assicurarsi che il visualizzatore di sintassi è disponibile. Guarderemo il visualizzatore un paio di volte per trovare dettagli rilevanti, come si costruisce il metodo AnalyzeNode.

Ottenere il nodo di destinazione il primo passo nel metodo AnalyzeNode viene a prendere l'oggetto nodo stai analizzando ed eseguirne il cast al tipo pertinente. Per trovare quel tipo, utilizzare il visualizzatore di sintassi è appena aperto. Selezionare la chiamata Regex e navigare nella struttura della sintassi al nodo InvocationExpression. Si può vedere appena sopra la griglia delle proprietà che InvocationExpression è il tipo di nodo, mentre il tipo è InvocationExpressionSyntax.

È possibile verificare il tipo di un nodo con un controllo di tipo o suo specifico tipo di test con il metodo appuntamentisu. Tuttavia, qui si può garantire che un cast riuscirà senza una prova, perché hai chiesto per il particolare tipo nel metodo Initialize. Il nodo che analizza l'azione è disponibile dalla proprietà nodo sul parametro context:

var invocationExpr = (InvocationExpressionSyntax)context.Node;

Ora che hai il nodo di invocazione, è necessario verificare se si tratta di una chiamata di Regex che necessita un linee a zigzag.

Controllo n. 1: Si tratta di una chiamata al metodo Match? Il primo controllo che è necessario è quello di assicurarsi che questa chiamata è una chiamata alla Regex corretto. Perché questo analizzatore verrà eseguito su ogni battitura nell'editor, è una buona idea per eseguire i test più rapidi prima e domande più costoso dell'API solo se tali test iniziale passa.

Il test più economico è per vedere se la sintassi di chiamata è una chiamata a un metodo denominato Match. Che può essere determinato prima che il compilatore ha fatto alcun lavoro speciale per capire quale metodo Match particolare questo è.

Guardando indietro il visualizzatore di sintassi, si vede che l'invocazione­espressione ha due nodi figlio principale, il SimpleMemberAccess­espressione e il ArgumentList. Selezionando l'identificatore Match nell'editor, come mostrato Figura 6, si può vedere che il nodo che stai cercando è la seconda IdentifierName entro il semplice ­MemberAccessExpression.

trovare l'identificatore di corrispondenza nella struttura della sintassi
Figura 6 trovare l'identificatore di corrispondenza nella struttura della sintassi

Come si costruisce un analizzatore, sarò molto spesso scavando nella sintassi e simboli come questo per trovare i relativi tipi e valori di proprietà che è necessario fare riferimento nel codice. Quando la costruzione di analizzatori, conviene tenere a un progetto di destinazione con il visualizzatore di sintassi.

Nel codice dell'analizzatore, è possibile esplorare i membri della invocationExpr in IntelliSense e trovare una proprietà corrispondente a ciascuno dei nodi figlio di InvocationExpression, uno denominato espressione e uno denominato ArgumentList. In questo caso, si desidera che la proprietà denominata espressione. Perché la parte di una chiamata che precede l'elenco degli argomenti può avere molte forme (ad esempio, potrebbe essere una chiamata di delegato di una variabile locale), questa proprietà restituisce un tipo generale di base, ExpressionSyntax. Il visualizzatore di sintassi, si può vedere che il tipo di cemento che ci si aspetta è un MemberAccessExpressionSyntax, quindi eseguirne il cast al tipo di:

var memberAccessExpr =
  invocationExpr.Expression as MemberAccessExpressionSyntax;

Vedete una ripartizione simile quando si scava nelle proprietà su memberAccessExpr. C'è una proprietà dell'espressione che rappresenta l'espressione arbitraria prima il puntino e una proprietà Name che rappresenta l'identificatore a destra del punto. Perché si vuole controllare per vedere se stai chiamando un metodo Match, controllare il valore stringa della proprietà Name. Per un nodo di sintassi, ottenendo il valore della stringa è un modo rapido per ottenere il testo di origine per quel nodo. È possibile utilizzare il nuovo linguaggio c# "?." operatore per gestire il caso in cui l'espressione che si sta analizzando non era in realtà un accesso ai membri, causando la linea precedente "come" clausola per restituire un valore null:

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

Se il metodo chiamato non è denominato Match, semplicemente impiccio. L'analisi è completa a costi minimi e non non c'è nessun sistema diagnostico per generare.

Controllo n. 2: Si tratta di una chiamata al metodo Regex vero? Se il metodo è denominato Match, fare un controllo più coinvolto che chiede al compilatore di determinare esattamente quale metodo Match sta chiamando il codice. Determinare la corrispondenza esatta richiede chiedendo il modello semantico di contesto per associare questa espressione per ottenere il simbolo di riferimento.

Chiamare il metodo GetSymbolInfo sul modello semantico e passargli l'espressione di cui si desidera il simbolo:

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

L'oggetto simbolo che torni è lo stesso che potete vedere in anteprima nel visualizzatore di sintassi il SimpleMemberAccessExpression con il tasto destro e scegliendo Mostra simbolo. In questo caso, si sceglie di lanciare il simbolo della comune interfaccia IMethodSymbol. Questa interfaccia viene implementata da interno tipo PEMethodSymbol indicato per quel simbolo nel visualizzatore di sintassi.

Ora che avete il simbolo, è possibile confrontare contro il nome completo che ci si aspetta dal vero metodo Regex. Per un simbolo, ottenendo il valore di stringa vi darà il suo nome completo. Non interessa davvero overload che stai chiamando ancora, così può solo controllare per quanto la parola Match:

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

Come con il precedente test, controllare per vedere se il simbolo corrisponde al nome che ci si aspetta, e se esso non corrisponde, o se non fosse in realtà un simbolo di metodo, impiccio. Anche se potrebbe sentire un po ' strano operare su stringhe qui, i confronti tra stringhe sono un'operazione comune ai compilatori.

I restanti controlli ora i test stanno iniziando a cadere in un ritmo. Ad ogni passo, si scava un po ' più nell'albero e controlla sia i nodi di sintassi o il modello semantico per testare se sei ancora in una situazione di errore. Ogni volta, è possibile utilizzare il visualizzatore di sintassi per vedere quali tipi e valori di proprietà ci si aspetta, quindi sai che in questo caso per tornare e in questo caso continuare. Seguirò questo modello per verificare le condizioni di qualche prossime.

Assicurarsi che il ArgumentList ha almeno due argomenti:

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

Quindi assicurarsi che il secondo argomento è un LiteralExpression, perché sei in attesa di una stringa letterale:

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

Infine, una volta che sai che è un valore letterale, è possibile chiedere il modello semantico per darvi il suo valore costante della fase di compilazione e assicurarsi che sia specificamente una stringa letterale:

 

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

Convalida il Regex Pattern a questo punto, hai tutti i dati necessari. Sai che stai chiamando Regex, e hai il valore stringa dell'espressione modello. Allora come a convalidarlo?

Semplicemente chiamare lo stesso metodo di Regex e passare in quella stringa modello. Perché stai solo cercando errori di analisi nella stringa modello, è possibile passare una stringa di input vuota come primo argomento. Effettuare la chiamata all'interno di un blocco try-catch, così potrete prendere il ArgumentException che Regex genera quando vede una stringa non valida:

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

Se la stringa modello analizza senza errore, il metodo AnalyzeNode uscirà normalmente e non vi è alcuna relazione. Se c'è un errore di analisi, si ti cattura l'eccezione di argomento — siete pronti a segnalare una diagnostica!

Segnalazione di una diagnostica all'interno del blocco catch, si utilizza l'oggetto regola è compilato in precedenza per creare un oggetto diagnostico che rappresenta una particolare squiggle volete produrre. Ciascun diagnostico ha bisogno di due cose principali specifici per quell'istanza: l'intervallo di codice che dovrebbe essere squiggled e le stringhe di Fill-in per inserire il formato del messaggio è definita in precedenza:

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

In questo caso, si desidera scarabocchio la stringa letterale, quindi si passa nella sua posizione l'arco per la diagnostica. Potete anche tirare fuori il messaggio di eccezione che descrive ciò che era sbagliato con la stringa di modello e che includono nel messaggio diagnostico.

L'ultimo passo è quello di prendere questa diagnostica e rapporto di nuovo al contesto passato a AnalyzeNode, così Visual Studio SA aggiungere una riga all'elenco errori e aggiungere un linee a zigzag nell'editor:

context.ReportDiagnostic(diagnostic);

Codice in DiagnosticAnalyzer.cs dovrebbe ora apparire come Figura 7.

Figura 7 il codice completo per 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);
      }
    }
  }
}

Cercando It Out cosi ' — la diagnostica è stata completata! Per provarla basta premere F5 (assicurarsi che RegexAnalyzer.VSIX è il progetto di avvio) e riaprire l'applicazione console nell'istanza oggetto del debug di Visual Studio. Presto vedrete un zigzag rossa sull'espressione modello sottolineando perché non è riuscito ad analizzare, come mostrato Figura 8.

provare vostro analizzatore diagnostico
Figura 8 provare vostro analizzatore diagnostico

Se vedi le linee a zigzag, complimenti! In caso contrario, è possibile impostare un punto di interruzione all'interno del metodo AnalyzeNode, digitare un carattere nella stringa modello per innescare una nuova analisi e quindi eseguire il codice analizzatore per vedere dove l'analizzatore è Mollo presto. È anche possibile controllare il codice contro Figura 7, che viene illustrato il codice completo per DiagnosticAnalyzer.cs.

Casi di utilizzo per analizzatori diagnostici

Per ricapitolare, a partire dal modello di analizzatore e scrivendo circa 30 linee di codice nuovo, vi sono stati in grado di identificare e fornire un linee a zigzag per un vero problema nel codice degli utenti. Più importante, facendo così non richiede di diventare un profondo esperto nelle operazioni del compilatore c#. Sei riuscito a rimanere concentrato sul dominio di destinazione delle espressioni regolari e utilizzare il visualizzatore di sintassi per guidarvi verso il piccolo insieme di nodi di sintassi e simboli rilevanti per l'analisi.

Ci sono molti domini nella vostra quotidiana codifica dove scrivendo un analizzatore diagnostico può essere utile e veloce:

  • Uno sviluppatore o piombo su una squadra, si potrebbe vedere gli altri fare gli stessi errori, spesso quando si codifica recensioni. Ora, è possibile scrivere un semplice analizzatore che scarabocchi quelli anti-pattern e controllare l'analizzatore nel controllo del codice sorgente, assicurando che nessuno che introduce tale bug noterà stai digitando.
  • Come il manutentore di un layer condiviso che definisce gli oggetti di business per l'organizzazione, si potrebbero avere regole di business per il corretto utilizzo di questi oggetti che sono difficili da codificare nel sistema di tipi, soprattutto se coinvolgono valori numerici o se essi comportano passaggi di un processo dove alcune operazioni dovrebbero venire sempre prima di altri. Ora è possibile applicare queste regole più morbide che regolano l'utilizzo del vostro livello, raccogliendo dove il sistema tipo lascia fuori.
  • Come proprietario di un open source o commerciale pacchetto API, potreste essere stanchi di rispondere alle stesse domande più volte nel forum. Potrebbe anche avere scritto la documentazione e carte bianche e trovato che molti dei vostri clienti continuano a colpire quelle stesse questioni, come essi non hanno letto quello che hai scritto. Ora si può racchiudere le API e l'analisi di codice rilevante in un unico pacchetto di NuGet, assicurando che tutti utilizzando le API vedono la stessa guida dall'inizio.

Speriamo che questo articolo ha ispirato a pensare agli analizzatori che si vorrà costruire per migliorare i propri progetti. Con la piattaforma del compilatore .NET, Microsoft ha fatto il sollevamento pesante per esporre lingua profonda comprensione e analisi del codice ricca per C# e Visual Basic— il resto spetta a voi!

Conclusioni

Così ora avete Visual Studio mostrando errore ghirigori sotto modelli regex non valido. Può fare di più?

Se hai le espressioni regolari conoscenza dominio per vedere non solo ciò che è sbagliato con una stringa, ma anche come risolverlo, puoi suggerire una correzione nella lampadina, come si è visto nel modello analizzatore di predefinito.

Nel prossimo articolo, ti mostrerò come scrivere quella Difficoltà di codice, come si impara ad apportare modifiche ai vostri alberi di sintassi. Restate sintonizzati!

Altri metodi del registro delle imprese di gestione

Può scavare nel parametro del metodo Initialize di contesto per vedere la serie completa di metodi di registro che è possibile chiamare. I metodi in Figura A ti permettono di collegare vari eventi nella pipeline del compilatore.

Un punto fondamentale da notare è che un'azione di primo livello registrata con qualsiasi metodo di registro non dovrebbe mai riporre qualsiasi stato nei campi di istanza del tipo di analizzatore. Visual Studio riutilizzerà un'istanza di tale tipo di analizzatore per l'intera sessione di Visual Studio per evitare ripetuti allocazioni. Qualsiasi stato da archiviazione e riutilizzare rischia di non essere aggiornato quando analizzando una compilation futura e anche potrebbe essere una perdita di memoria, se mantiene il vecchio sintassi nodi o simboli dall'immondizia di essere raccolti.

Se è necessario mantenere stato attraverso azioni, si dovrebbe chiamare RegisterCodeBlockStartAction o RegisterCompilationStartAction e memorizzare lo stato come la gente del posto all'interno di tale metodo di azione. L'oggetto di contesto passato a quelle azioni consente di registrare azioni annidate come espressioni lambda e queste azioni annidate possono chiudere sopra i locali nelle azioni esterne al fine di mantenere stato.

Figura A registro metodi per collegare i vari eventi

RegisterSyntaxNodeAction Attivato quando è stato analizzato un particolare tipo di nodo di sintassi
RegisterSymbolAction Attivato quando un particolare tipo di simbolo è stato analizzato
RegisterSyntaxTreeAction Attivato quando è stato analizzato l'albero intero sintassi del file
RegisterSemanticModelAction Attivato quando un modello semantico è disponibile per l'intero file

RegisterCodeBlockStartAction

RegisterCodeBlockEndAction

Innescato prima e dopo l'analisi del corpo di un metodo o l'altro blocco di codice

RegisterCompilationStartAction

RegisterCompilationEndAction

Innescato prima e dopo l'analisi dell'intero progetto

Alex Turner è un senior program manager per il team di lingue gestiti presso Microsoft, dove egli sta fermentando fino bontà C# e Visual Basic su il progetto di piattaforma di compilatore .NET ("Roslyn"). Si laurea con un Master in scienze informatiche presso la Stony Brook University e ha parlato a conferenze Build, PDC, TechEd, TechDays e MIX.

Grazie ai seguenti esperti tecnici Microsoft per la revisione di questo articolo: Bill Chiles e Lucian Wischik
Bill  Chiles ha lavorato sulle lingue (CMU Common Lisp, Dylan, IronPython e c#) e sviluppatore di strumenti la maggior parte della sua carriera. Ha trascorso gli ultimi anni 17 nella divisione Developer di Microsoft, lavorando su tutto, dalle caratteristiche di nucleo Visual Studio , a Dynamic Language Runtime, per c#.

Lucian Wischik è Visual Basic/C# lingua design team in Microsoft, con particolare responsabilità per Visual Basic. Prima di unirsi a Microsoft ha lavorato nel mondo accademico sulla teoria della concorrenza e async. Lui è un appassionato velista e swimmer interurbano.