Il presente articolo è stato tradotto automaticamente.

C#

Aggiunta di una correzione di codice all'analizzatore Roslyn

Alex Turner

Se avete seguito i passaggi nel mio precedente articolo, "Uso Roslyn per scrivere un Live codice Analyzer per Your API" (msdn.microsoft.com/magazine/dn879356), hai scritto un analizzatore che visualizza errori diretta per stringhe modello valido espressioni regolari (regex). Ogni schema non valido ottiene un zigzag rosso nell'editor, proprio come si vedrebbe per errori del compilatore e i ghirigori appaiono dal vivo durante la digitazione del codice. Questo è reso possibile dal nuovo compilatore piattaforma .NET ("Roslyn") API, quale potere di c# e Visual Basic modifica esperienze in Visual Studio 2015 anteprima.

Può fare di più? Se hai la conoscenza del dominio per vedere non solo ciò che è sbagliato, ma anche come rimediare, si può suggerire la correzione codice rilevante attraverso la nuova lampadina Visual Studio . Questa correzione codice permetterà uno sviluppatore che utilizza il vostro analizzatore di trovare non solo un errore nel suo codice — egli può anche pulirlo immediatamente.

In questo articolo, vi mostreremo come aggiungere un provider di codice correzione al vostro analizzatore diagnostico regex che offre correzioni a ogni scarabocchio regex. La correzione verrà aggiunto come elemento nel menu lampadina, lasciando all'utente la correzione in anteprima e applicare automaticamente al suo codice.

Picking Up dove avete lasciato

Per iniziare, essere sicuro che hai seguito i passi nel precedente articolo. In questo articolo, vi ho mostrato come scrivere la prima metà del vostro analizzatore, che genera la diagnostiche ghirigori sotto ogni stringa regex non valido. Tale articolo si camminato attraverso:

  • L'installazione di anteprima Visual Studio 2015, relativo SDK e i pacchetti di Roslyn VSIX pertinenti.
  • Creazione di una nuova diagnostica con Difficoltà codice progetto.
  • Aggiunta di codice DiagnosticAnalyzer.cs per implementare il rilevamento di pattern regex non valido.

Se stai cercando di raggiungere rapidamente, check out Figura 1, ­che elenca il codice finale per DiagnosticAnalyzer.cs.

Figura 1 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);
      }
    }
  }
}

Trasformare gli alberi immutabili sintassi

L'ultima volta, quando hai scritto l'analizzatore diagnostico per rilevare pattern regex non valido, il primo passo è stato utilizzare il visualizzatore di sintassi per identificare i modelli negli alberi sintassi che indicava il codice del problema. Hai scritto allora è stato trovato un metodo di analisi che viene eseguito ogni volta che il tipo di nodo pertinenti. Il metodo controllato per il modello di nodi di sintassi che ha garantito un linee a zigzag errore.

Una difficoltà di scrittura è un processo simile. Si tratta degli alberi di sintassi, concentrandosi sullo stato desiderato nuovo file di codice, dopo che l'utente applica il fix. La maggior parte delle correzioni di codice coinvolgono aggiunta, rimozione o sostituzione dei nodi sintassi dagli alberi per produrre nuovi alberi di sintassi correnti. È possibile operare direttamente sui nodi di sintassi o uso API che consentono di apportano modifiche di tutto il progetto, come Rinomina.

Una proprietà molto importante da capire sui nodi di sintassi, alberi e simboli nella piattaforma .NET del compilatore è che sono immutabili. Una volta creato un nodo di sintassi o albero, non può essere modificata — un determinato oggetto albero o nodo rappresenterà sempre lo stesso codice c# o Visual Basic .

Immutabilità in un'API per trasformare il codice sorgente può sembrare controintuitivo. Come si può aggiungere, rimuovere e sostituire i nodi figlio in un albero sintattico se né l'albero né suoi nodi possono essere cambiati? Qui è utile per considerare il tipo di stringa .NET, un altro tipo non modificabile è probabilmente utilizzare ogni giorno. Si eseguono operazioni per trasformare stringhe molto spesso, li concatena insieme e persino sostituire sottostringhe usando Replace. Tuttavia, nessuna di queste operazioni effettivamente cambiare oggetto stringa originale. Invece, ogni chiamata restituisce un nuovo oggetto string che rappresenta il nuovo stato della stringa. È possibile assegnare a questo nuovo oggetto al vostra variabile originale, ma qualsiasi metodo che hai superato la vecchia stringa avrà ancora il valore originale.

Aggiunta di un nodo di parametro a un albero immutabile per esplorare come immutabilità si applica agli alberi di sintassi, potrai eseguire una semplice trasformazione manualmente nell'editor di codice e vedere come colpisce l'albero sintattico.

All'interno di Visual Studio 2015 anteprima (con la sintassi Visualizer estensione installato, vedere l'articolo precedente), creare un nuovo file di codice c#. Sostituire tutti i suoi contenuti con il seguente codice:

class C
{
  void M()
  }
}

Aprire il visualizzatore di sintassi scegliendo vista | Altri Windows | Roslyn sintassi Visualizer e clicca ovunque all'interno del file di codice per la struttura ad albero. Nella sintassi Visual­finestra izer, tasto destro del mouse il nodo principale CompilationUnit e scegliere visualizzazione diretta sintassi grafico. Visualizzando questo albero di sintassi si traduce in un grafico come quello di Figura 2 (il grafico mostrato qui omette i nodi curiosità grigio e bianco che rappresentano gli spazi vuoti). Il parametro blu­nodo sintassi elenco ha due token bambino verde che rappresenta le sue parentesi e nessun nodo di sintassi bambino blu, come l'elenco non contiene parametri.

albero di sintassi prima della trasformazione
Figura 2 albero di sintassi prima della trasformazione

La trasformazione che è potrai simulare qui è quello che vuoi aggiungere un nuovo parametro di tipo int. Digitare il codice "int io" all'interno delle parentesi dell'elenco di parametri di metodo di M e guarda i cambiamenti all'interno del visualizzatore di sintassi come si digita:

class C
{
  void M(int i)
  {
  }
}

Si noti che anche prima di finire digitando, quando il codice incompleto contiene errori di compilazione (mostrati nel visualizzatore di sintassi come nodi con sfondo rosso), l'albero è ancora coerente e il compilatore congetture che il nuovo codice si forma un nodo parametro valido. Questa resilienza degli alberi sintassi per errori del compilatore è ciò che permette la diagnostica per funzionare bene contro codice incompleto e funzionalità dell'IDE.

Tasto destro del mouse sul nodo radice CompilationUnit nuovamente e generare un nuovo grafico, che dovrebbe assomigliare a Figura 3 (di nuovo, qui raffigurati senza banalità).

sintassi albero dopo la trasformazione
Figura 3 sintassi albero dopo la trasformazione

Si noti che il ParameterList ora ha tre figli, i token di due parentesi che aveva prima, più un nuovo nodo di sintassi del parametro. Come hai digitato "int io" nell'editor, Visual Studio sostituito albero di sintassi precedente del documento con questo nuovo albero di sintassi che rappresenta il nuovo codice sorgente.

Effettuazione di una sostituzione completa funziona abbastanza bene per piccole stringhe, che sono i singoli oggetti, ma per quanto riguarda gli alberi di sintassi? Un file di codice di grandi dimensioni può contenere migliaia o decine di migliaia di nodi di sintassi, e certamente non volete tutti quei nodi per essere ricreato ogni volta che qualcuno digita un carattere all'interno di un file. Che vuoi generare tonnellate di oggetti orfani per il garbage collector ripulire e seriamente compromettere le prestazioni.

Fortunatamente, la natura immutabile dei nodi sintassi fornisce anche qui la fuga. Perché la maggior parte dei nodi nel documento non sono colpita quando si effettua una piccola modifica, quei nodi possono essere tranquillamente riutilizzati come bambini nella nuova struttura. Il nodo interno dietro le quinte che memorizza i dati per un nodo determinata sintassi punti solo verso il basso per i bambini del nodo. Perché quei nodi interni non hanno padre puntatori, è sicuro per il nodo interno stesso di presentarsi ripetutamente in molte iterazioni di un albero di sintassi dato, finché quella parte del codice rimane lo stesso.

Questo riutilizzo nodo significa che solo i nodi in un albero che ha bisogno di essere ricreato su ogni battitura sono quelli con almeno un discendente che è cambiato, e cioè la catena stretta nodi predecessori fino alla radice, come raffigurato Figura 4. Tutti gli altri nodi vengono riutilizzati come è.

nodi antenato sostituiti durante la trasformazione
Figura 4 nodi antenato sostituiti durante la trasformazione

In questo caso, il cambiamento principale è creare il nuovo nodo di parametro e quindi sostituire il ParameterList con un nuovo ParameterList che presenta il nuovo parametro inserito come nodo figlio. Sostituendo il ParameterList richiede anche sostituendo la catena di nodi predecessore, come lista di ogni antenato dei cambiamenti di nodi figlio per includere il nodo sostituito. In questo articolo, farete quel tipo di ricambio per vostro analizzatore regex con il metodo SyntaxNode.ReplaceNode, che si prende cura di sostituire tutti i nodi di antenato per voi.

Ora hai visto il modello generale per la pianificazione di una correzione di codice: Si inizia con il codice la prima dello stato che innesca la diagnostica. Poi apportare manualmente le modifiche dovrebbe fare il fix, osservando l'effetto sulla struttura della sintassi. Infine, si lavora il codice necessario per creare i nodi di ricambio e restituire un nuovo albero di sintassi che li contiene.

Assicurati che hai il progetto aperto, contenente la diagnostica creato l'ultima volta. Per implementare il fix codice, potrai scavare CodeFixProvider.cs.

Metodo GetFixableDiagnosticIds

Correzioni e la diagnostica che si risolvono è ad accoppiamento ridotto di diagnostica IDs. Ogni correzione del codice è destinato a uno o più ID diagnostico. Ogni volta che Visual Studio vede una diagnostica con un ID di corrispondenza, chiederà il provider di codice correzione se hai codice correzioni da offrire. Accoppiamento lasco in base alla stringa ID permette un analizzatore fornire una correzione per una diagnostica prodotta da qualcun altro analizzatore, o anche un fix per avvisi ed errori del compilatore incorporato.

In questo caso, il vostro analizzatore produce sia la diagnostica e la correzione del codice. Si può vedere che il metodo di GetFixableDiagnosticIds già sta tornando l'ID diagnostico definito nel vostro tipo di diagnostico, quindi non c'è nulla da cambiare qui.

Metodo ComputeFixesAsync

Il metodo ComputeFixesAsync è il driver principale per la correzione del codice. Questo metodo viene chiamato ogni volta che uno o più corrispondenti diagnostica si trovata per un determinato arco di codice.

Si può vedere che l'implementazione predefinita del modello del metodo ComputeFixesAsync tira fuori prima diagnostica dal contesto (nella maggior parte dei casi, solo prevedete uno) e si ottiene l'intervallo di diagnostica. La riga successiva cerca quindi l'albero di sintassi da quella campata per trovare il più vicino nodo di tipo dichiarazione. Nel caso di regola del modello predefinito, che è il nodo pertinente cui contenuti necessari di fissaggio.

Nel tuo caso, l'analizzatore diagnostico che hai scritto era alla ricerca di invocazioni vedere se erano chiamate a Regex. Per aiutare a condividere la logica tra la diagnostica e la correzione del codice, modificare il tipo menzionato nel filtro OfType della ricerca dell'albero per trovare quel nodo InvocationExpressionSyntax stesso. Rinominare la variabile locale a "invocationExpr," così:

var invocationExpr = root.FindToken(
  diagnosticSpan.Start).Parent.AncestorsAndSelf()
  .OfType<InvocationExpressionSyntax>().First();

Ora avete un riferimento allo stesso nodo invocazione con cui l'analizzatore diagnostico iniziato. Nell'istruzione successiva, si passa questo nodo al metodo che calcola le modifiche al codice si sug­gesting per questa correzione. Rinominare quel metodo da MakeUppercaseAsync a FixRegexAsync e modificare la descrizione di correzione in correzione regex:

context.RegisterFix(
  CodeAction.Create("Fix regex", c => FixRegexAsync(
  context.Document, invocationExpr, c)), diagnostic);

Ogni chiamata al metodo RegisterFix di contesto associa una nuova azione di codice diagnostico linee a zigzag in questione e produrrà una voce di menu all'interno della lampadina. Si noti che in realtà non stai chiamando il metodo FixRegexAsync che esegue la trasformazione del codice ancora. Invece, la chiamata al metodo è avvolto in un'espressione lambda che Visual Studio può chiamare più tardi. Questo è perché il risultato della tua trasformazione è necessaria solo quando l'utente seleziona effettivamente il tuo oggetto regex di correzione. Quando l'elemento di correzione viene evidenziato o scelto, Visual Studio richiama l'azione per generare un'anteprima o applicare la correzione. Fino ad allora, Visual Studio evita che esegue il metodo di correzione, nel caso in cui si stanno eseguendo operazioni dispendiose, come soluzione a livello ridenominazioni.

Si noti che un provider di correzione del codice non è obbligato a produrre una correzione di codice per ogni istanza di un determinato sistema diagnostico. È spesso il caso che avete una difficoltà di suggerire solo per alcuni casi che vostro analizzatore può scarabocchio. Se ti resta fissa alcuni del tempo, si dovrebbero in primo luogo in ComputeFixesAsync qualsiasi condizioni sperimentali è necessario determinare se è possibile risolvere la situazione specifica. Se tali condizioni non sono soddisfatte, si dovrebbe tornare da ComputeFixesAsync senza chiamare RegisterFix.

In questo esempio, offrirà una correzione per tutte le istanze di diagnostica, quindi ci sono più condizioni per verificare.

Metodo FixRegexAsync

Ora arrivare al cuore della correzione del codice. Il metodo FixRegexAsync come attualmente scritto prende un documento e produce una soluzione aggiornata. Mentre analizzatori diagnostici guardano a simboli e nodi specifici, correzioni di codice possono modificare il codice in tutta l'intera soluzione. Si può vedere che il codice del modello è chiamata Renamer.RenameSymbol­asincrona, che cambia non solo la dichiarazione di tipo di simbolo, ma anche tutti i riferimenti a tale simbolo in tutta la soluzione.

In questo caso, si intende solo apportare modifiche locali alla stringa nel documento corrente, quindi è possibile modificare il tipo restituito dal metodo da attività<soluzione> attività<documento>. Questa firma è ancora compatibile con l'espressione lambda in ComputeFixes­asincrona, come CodeAction.Create ha un altro overload che accetta un documento piuttosto che una soluzione. Sarà anche necessario aggiornare il parametro typeDecl per abbinare il nodo InvocationExpressionSyntax che tu stai passando dal metodo ComputeFixesAsync:

private async Task<Document> FixRegexAsync(Document document,
  InvocationExpressionSyntax invocationExpr, CancellationToken cancellationToken)

Perché non hai bisogno di qualsiasi della logica "rendere maiuscola", eliminare il corpo del metodo, pure.

Trovare il nodo per sostituire della prima metà del tuo fissatore tanto come il primo sarà la metà del vostro analizzatore diagnostico — è necessario scavare il InvocationExpression per trovare le parti pertinenti della chiamata al metodo che informerà il fix. Infatti, è possibile copiare solo nella prima metà del metodo AnalyzeNode giù per il blocco try-catch. Salta la prima riga, però, come hai già prendere invocationExpr come parametro. Perché sai che questo è il codice che hai trovato con successo una diagnostica, è possibile rimuovere tutti i controlli "se". Il solo cambiamento fare è recuperare il modello semantico dall'argomento del documento, come non è più necessario un contesto che fornisce direttamente il modello semantico.

Quando hai finito le modifiche, il corpo del vostro metodo FixRegexAsync dovrebbe assomigliare a questo:

var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
var memberAccessExpr =
  invocationExpr.Expression as MemberAccessExpressionSyntax;
var memberSymbol =
  semanticModel.GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol;
var argumentList = invocationExpr.ArgumentList as ArgumentListSyntax;
var regexLiteral =
  argumentList.Arguments[1].Expression as LiteralExpressionSyntax;
var regexOpt = semanticModel.GetConstantValue(regexLiteral);
var regex = regexOpt.Value as string;

Generare il nodo di ricambio ora che avete ancora regexLiteral, che rappresenta il vecchio valore letterale di stringa, è necessario generare una nuova. Calcolare esattamente la stringa che è necessario fissare un pattern regex arbitrario è un grande compito che è ben oltre la portata di questo articolo. Come controfigura per il momento, si utilizzerà la stringa regex valido, che è in effetti una stringa regex valido. Se si decide di andare oltre proprio, si dovrebbe avviare piccole e indirizzare la vostra difficoltà a regex molto particolari problemi.

Il modo di basso livello per produrre nuovi nodi di sintassi di sostituire il vostro albero è tra i membri di SyntaxFactory. Questi metodi consentono di creare ogni tipo di sintassi nodo esattamente la forma che si sceglie. Tuttavia, spesso risulta più facile basta analizzare l'espressione che si desidera dal testo, lasciando al compilatore di fare tutto il lavoro pesante per creare i nodi. Per analizzare un frammento di codice, basta chiamare SyntaxFactory.ParseExpression e specificare il codice per un valore letterale di stringa:

var newLiteral = SyntaxFactory.ParseExpression("\"valid regex\"");

Questo nuovo valore letterale avrebbe funzionato bene come sostituto nella maggior parte dei casi, ma manca qualcosa. Se vi ricordate, token sintassi sia associata a banalità che rappresenta uno spazio vuoto o commenti. Sarà necessario copiare qualsiasi curiosità dall'espressione letterale vecchio affinché che non elimini qualsiasi spaziatura o commenti da vecchio codice. È anche buona pratica applicare tag nuovi nodi che si crea con l'annotazione "Formatter", che informa il motore di correzione del codice che volete il vostro nuovo nodo formattato secondo le impostazioni di stile del codice dell'utente finale. Sarà necessario aggiungere l'utilizzo di una direttiva per lo spazio dei nomi Microsoft.CodeAnalysis.Formatting. Con queste addi­zioni, chiamata ParseExpression assomiglia a questo:

var newLiteral = SyntaxFactory.ParseExpression("\"valid regex\"")
  .WithLeadingTrivia(regexLiteral.GetLeadingTrivia())
  .WithTrailingTrivia(regexLiteral.GetTrailingTrivia())
  .WithAdditionalAnnotations(Formatter.Annotation);

Scambiare il nuovo nodo nell'albero della sintassi ora che avete un nuovo nodo di sintassi per il valore letterale di stringa, è possibile sostituire il vecchio nodo all'interno dell'albero di sintassi, producendo un nuovo albero con una stringa regex fisso.

In primo luogo, si ottiene il nodo radice dall'albero di sintassi del documento corrente:

var root = await document.GetSyntaxRootAsync();

Ora, è possibile chiamare il metodo ReplaceNode su quella radice di sintassi di swap fuori il vecchio nodo di sintassi e swap in quello nuovo:

var newRoot = root.ReplaceNode(regexLiteral, newLiteral);

Ricordo che si sta generando un nuovo nodo principale qui. Sostituendo qualsiasi nodo di sintassi richiede anche di sostituire i suoi genitori tutto il senso fino alla radice. Come si è visto prima, tutti i nodi di sintassi nella piattaforma del compilatore .NET non sono modificabili. Questa operazione di sostituzione restituisce in realtà solo un nuovo nodo radice di sintassi con il nodo di destinazione e i suoi antenati sostituiti come diretto.

Ora che avete una nuova radice di sintassi con una stringa fissa illuminata­semblea, si può camminare uno più livello dell'albero per produrre un nuovo oggetto di documento che contiene la vostra root aggiornato. Per sostituire la radice, utilizzare il metodo WithSyntaxRoot nel documento:

var newDocument = document.WithSyntaxRoot(newRoot);

Questo è lo stesso modello con API che hai appena visto quando si chiama WithLeadingTrivia e altri metodi, l'espressione che è analizzata. Vedrete questo con motivo spesso durante la trasformazione di oggetti esistenti nel modello immutabile Roslyn. L'idea è simile al metodo Replace .NET che restituisce un nuovo oggetto string.

Con il documento trasformato in mano, si può ora tornare da FixRegexAsync:

return newDocument;

Codice in CodeFixProvider.cs dovrebbe ora apparire come Figura 5.

Figura 5 il codice completo per CodeFixProvider.cs

using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
namespace RegexAnalyzer
{
  [ExportCodeFixProvider("RegexAnalyzerCodeFixProvider",
    LanguageNames.CSharp), Shared]
  public class RegexAnalyzerCodeFixProvider : CodeFixProvider
  {
    public sealed override ImmutableArray<string> GetFixableDiagnosticIds()
    {
      return ImmutableArray.Create(RegexAnalyzer.DiagnosticId);
    }
    public sealed override FixAllProvider GetFixAllProvider()
    {
      return WellKnownFixAllProviders.BatchFixer;
    }
    public sealed override async Task ComputeFixesAsync(CodeFixContext context)
    {
      var root =
        await context.Document.GetSyntaxRootAsync(context.CancellationToken)
        .ConfigureAwait(false);
      var diagnostic = context.Diagnostics.First();
      var diagnosticSpan = diagnostic.Location.SourceSpan;
      // Find the invocation expression identified by the diagnostic.
      var invocationExpr =   
        root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf()
        .OfType<InvocationExpressionSyntax>().First();
      // Register a code action that will invoke the fix.
      context.RegisterFix(
        CodeAction.Create("Fix regex", c =>
        FixRegexAsync(context.Document, invocationExpr, c)), diagnostic);
    }
    private async Task<Document> FixRegexAsync(Document document,
      InvocationExpressionSyntax invocationExpr,
      CancellationToken cancellationToken)
    {
      var semanticModel =
        await document.GetSemanticModelAsync(cancellationToken);
      var memberAccessExpr =
        invocationExpr.Expression as MemberAccessExpressionSyntax;
      var memberSymbol =
        semanticModel.GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol;
      var argumentList = invocationExpr.ArgumentList as ArgumentListSyntax;
      var regexLiteral =
        argumentList.Arguments[1].Expression as LiteralExpressionSyntax;
      var regexOpt = semanticModel.GetConstantValue(regexLiteral);
      var regex = regexOpt.Value as string;
      var newLiteral = SyntaxFactory.ParseExpression("\"valid regex\"")
        .WithLeadingTrivia(regexLiteral.GetLeadingTrivia())
        .WithTrailingTrivia(regexLiteral.GetTrailingTrivia())
        .WithAdditionalAnnotations(Formatter.Annotation);
      var root = await document.GetSyntaxRootAsync();
      var newRoot = root.ReplaceNode(regexLiteral, newLiteral);
      var newDocument = document.WithSyntaxRoot(newRoot);
      return newDocument;
    }
  }
}

Cercando It Out cosi '! Ora hai definito una correzione codice cui trasformazione viene eseguita quando gli utenti incontrano il tuo diagnostico e scegliere la correzione nel menu lampadina. Per provare la correzione del codice, premere F5 nuovamente nell'istanza principale di Visual Studio e aprire l'applicazione console. Questa volta, quando si posiziona il cursore sul tuo scarabocchio, si dovrebbe vedere una lampadina appaiono a sinistra. Cliccando sulla lampadina dovrebbe far apparire un menu che contiene l'azione del codice regex Fix è definito, come mostrato Figura 6. Questo menu Mostra un'anteprima con un inline diff tra il documento vecchio e il nuovo documento che è stato creato, che rappresenta lo stato del tuo codice se si sceglie di applicare la correzione.

provando il Fix codice
Figura 6 provando il Fix codice

Se si seleziona tale voce di menu, Visual Studio prende il nuovo documento e adotta come lo stato corrente del buffer editor per il file sorgente. Il fix è stato ora applicato!

Complimenti

Hai fatto! In circa 70 linee totale del nuovo codice, identificato un problema nel codice dell'utente, dal vivo, come egli sta scrivendo, squiggled e rosso come un errore ed emerse una correzione del codice che può ripulire. Trasformato alberi di sintassi e generati nuovi nodi di sintassi lungo il percorso, tutto mentre operano all'interno del dominio di destinazione familiare delle espressioni regolari.

Mentre si può continuamente perfezionare la diagnostica e le correzioni di codice che si scrive, ho trovato che gli analizzatori costruito con la piattaforma .NET compilatore consentono di ottenere un sacco fatto entro un breve lasso di tempo. Una volta arrivati gli analizzatori edificio confortevole, potrai iniziare a individuare ogni sorta di problemi comuni nel vostro quotidiano codifica vita e rilevare le correzioni ripetitive, è possibile automatizzare.

Cosa si analizza?


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

Grazie ai seguenti esperti tecnici Microsoft per la revisione di questo articolo: Bill Chiles e Lucian Wischik
Lucian Wischik è VB/C# lingua design team in Microsoft, con particolare responsabilità per VB. Prima di unirsi a Microsoft ha lavorato nel mondo accademico sulla teoria della concorrenza e async. Lui è un appassionato velista e swimmer interurbano.

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#).