public override int Compare(SpellingDiagnostic x, SpellingDiagnostic y) { if (object.ReferenceEquals(x, y)) { return(0); } if (x == null) { return(-1); } if (y == null) { return(1); } int result = StringComparer.OrdinalIgnoreCase.Compare( x.FilePath, y.FilePath); if (result != 0) { return(result); } return(x.Span.Start.CompareTo(y.Span.Start)); }
public static SpellingFixResult Create(string sourceText, SpellingDiagnostic diagnostic, SpellingFix fix) { return(new SpellingFixResult( sourceText, new SpellingCapture(diagnostic.Value, diagnostic.Index, diagnostic.Parent, diagnostic.ParentIndex), diagnostic.Span, fix, diagnostic.Location.GetMappedLineSpan())); }
public override int GetHashCode(SpellingDiagnostic obj) { if (obj == null) { throw new ArgumentNullException(nameof(obj)); } return(Hash.Combine( StringComparer.OrdinalIgnoreCase.GetHashCode(obj.FilePath), obj.Span.Start)); }
static void AddResult( List<SpellingFixResult> results, SpellingDiagnostic diagnostic, SpellingFix fix, SourceText sourceText, ref string sourceTextText) { if (sourceTextText == null) sourceTextText = (ShouldWrite(Verbosity.Detailed)) ? sourceText.ToString() : ""; SpellingFixResult result = SpellingFixResult.Create( (sourceTextText.Length == 0) ? null : sourceTextText, diagnostic, fix); results.Add(result); }
public SpellingFix ChooseFix( SpellingDiagnostic diagnostic, List <SpellingFix> fixes, bool interactive) { fixes = fixes .Distinct(SpellingFixComparer.InvariantCulture) .Where(f => { return(f.Kind == SpellingFixKind.Predefined || diagnostic.IsApplicableFix(f.Value)); }) .Select(fix => { if (TextUtility.GetTextCasing(fix.Value) != TextCasing.Undefined) { return(fix.WithValue(TextUtility.SetTextCasing(fix.Value, diagnostic.Casing))); } return(fix); }) .OrderBy(f => f.Kind) .Take(9) .ToList(); if (fixes.Count > 0) { for (int i = 0; i < fixes.Count; i++) { WriteSuggestion(diagnostic, fixes[i], i, interactive); } if (TryReadSuggestion(out int index) && index < fixes.Count) { return(fixes[index]); } } return(default);
public override bool Equals(SpellingDiagnostic x, SpellingDiagnostic y) { if (object.ReferenceEquals(x, y)) { return(true); } if (x == null) { return(false); } if (y == null) { return(false); } return(StringComparer.OrdinalIgnoreCase.Equals( x.FilePath, y.FilePath) && x.Span.Start == y.Span.Start); }
public async Task <ImmutableArray <SpellingFixResult> > FixProjectAsync( Project project, CancellationToken cancellationToken = default) { project = CurrentSolution.GetProject(project.Id); ISpellingService service = MefWorkspaceServices.Default.GetService <ISpellingService>(project.Language); if (service == null) { return(ImmutableArray <SpellingFixResult> .Empty); } ImmutableArray <Diagnostic> previousDiagnostics = ImmutableArray <Diagnostic> .Empty; ImmutableArray <Diagnostic> previousPreviousDiagnostics = ImmutableArray <Diagnostic> .Empty; ImmutableArray <SpellingFixResult> .Builder results = ImmutableArray.CreateBuilder <SpellingFixResult>(); bool nonSymbolsFixed = (Options.ScopeFilter & (SpellingScopeFilter.NonSymbol)) == 0; while (true) { cancellationToken.ThrowIfCancellationRequested(); var compilationWithAnalyzersOptions = new CompilationWithAnalyzersOptions( options: default(AnalyzerOptions), onAnalyzerException: default(Action <Exception, DiagnosticAnalyzer, Diagnostic>), concurrentAnalysis: true, logAnalyzerExecutionTime: false, reportSuppressedDiagnostics: false); Compilation compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); DiagnosticAnalyzer analyzer = service.CreateAnalyzer(SpellingData, Options); var compilationWithAnalyzers = new CompilationWithAnalyzers( compilation, ImmutableArray.Create(analyzer), compilationWithAnalyzersOptions); ImmutableArray <Diagnostic> diagnostics = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync(cancellationToken).ConfigureAwait(false); #if DEBUG foreach (IGrouping <Diagnostic, Diagnostic> grouping in diagnostics .GroupBy(f => f, DiagnosticDeepEqualityComparer.Instance)) { using (IEnumerator <Diagnostic> en = grouping.GetEnumerator()) { if (en.MoveNext() && en.MoveNext()) { Debug.Fail(DiagnosticFormatter.FormatDiagnostic(en.Current)); } } } #endif int length = diagnostics.Length; if (length == 0) { break; } if (length == previousDiagnostics.Length && !diagnostics.Except(previousDiagnostics, DiagnosticDeepEqualityComparer.Instance).Any()) { break; } if (length == previousPreviousDiagnostics.Length && !diagnostics.Except(previousPreviousDiagnostics, DiagnosticDeepEqualityComparer.Instance).Any()) { LogHelpers.WriteInfiniteLoopSummary(diagnostics, previousDiagnostics, project, FormatProvider); break; } var spellingDiagnostics = new List <SpellingDiagnostic>(); foreach (Diagnostic diagnostic in diagnostics) { Debug.Assert(diagnostic.Id == CommonDiagnosticIdentifiers.PossibleMisspellingOrTypo, diagnostic.Id); if (diagnostic.Id != CommonDiagnosticIdentifiers.PossibleMisspellingOrTypo) { if (diagnostic.IsAnalyzerExceptionDiagnostic()) { LogHelpers.WriteDiagnostic(diagnostic, baseDirectoryPath: Path.GetDirectoryName(project.FilePath), formatProvider: FormatProvider, indentation: " ", verbosity: Verbosity.Detailed); } continue; } SpellingDiagnostic spellingDiagnostic = service.CreateSpellingDiagnostic(diagnostic); spellingDiagnostics.Add(spellingDiagnostic); } if (!nonSymbolsFixed) { List <SpellingFixResult> commentResults = await FixCommentsAsync(project, spellingDiagnostics, cancellationToken).ConfigureAwait(false); results.AddRange(commentResults); nonSymbolsFixed = true; } if ((Options.ScopeFilter & SpellingScopeFilter.Symbol) == 0) { break; } (List <SpellingFixResult> symbolResults, bool allIgnored) = await FixSymbolsAsync( project, spellingDiagnostics, service.SyntaxFacts, cancellationToken) .ConfigureAwait(false); results.AddRange(symbolResults); if (allIgnored) { break; } if (Options.DryRun) { break; } project = CurrentSolution.GetProject(project.Id); previousPreviousDiagnostics = previousDiagnostics; previousDiagnostics = diagnostics; } return(results.ToImmutableArray()); }
private async Task <(List <SpellingFixResult>, bool allIgnored)> FixSymbolsAsync( Project project, List <SpellingDiagnostic> spellingDiagnostics, ISyntaxFactsService syntaxFacts, CancellationToken cancellationToken) { var results = new List <SpellingFixResult>(); var allIgnored = true; List <(SyntaxToken identifier, List <SpellingDiagnostic> diagnostics, DocumentId documentId)> symbolDiagnostics = spellingDiagnostics .Where(f => f.IsSymbol) .GroupBy(f => f.Identifier) .Select(f => ( identifier: f.Key, diagnostics: f.OrderBy(f => f.Span.Start).ToList(), documentId: project.GetDocument(f.Key.SyntaxTree).Id)) .OrderBy(f => f.documentId.Id) .ThenByDescending(f => f.identifier.SpanStart) .ToList(); for (int i = 0; i < symbolDiagnostics.Count; i++) { cancellationToken.ThrowIfCancellationRequested(); (SyntaxToken identifier, List <SpellingDiagnostic> diagnostics, DocumentId documentId) = symbolDiagnostics[i]; SyntaxNode node = null; foreach (SpellingDiagnostic diagnostic in diagnostics) { if (diagnostic != null) { node = syntaxFacts.GetSymbolDeclaration(diagnostic.Identifier); break; } } if (node == null) { continue; } Document document = project.GetDocument(documentId); if (document == null) { Debug.Fail(identifier.GetLocation().ToString()); WriteLine($" Cannot find document for '{identifier.ValueText}'", ConsoleColors.Yellow, Verbosity.Detailed); continue; } SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); if (identifier.SyntaxTree != root.SyntaxTree) { SyntaxToken identifier2 = root.FindToken(identifier.SpanStart, findInsideTrivia: false); if (identifier.Span != identifier2.Span || identifier.RawKind != identifier2.RawKind || !string.Equals(identifier.ValueText, identifier2.ValueText, StringComparison.Ordinal)) { continue; } SyntaxNode node2 = identifier2.Parent; SyntaxNode n = identifier.Parent; while (n != node) { node2 = node2.Parent; n = n.Parent; } identifier = identifier2; node = node2; } SourceText sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); string sourceTextText = null; SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); ISymbol symbol = semanticModel.GetDeclaredSymbol(node, cancellationToken) ?? semanticModel.GetSymbol(node, cancellationToken); if (symbol == null) { // 8925 - C# tuple element Debug.Assert(identifier.Parent.RawKind == 8925, identifier.ToString()); if (ShouldWrite(Verbosity.Detailed)) { string message = $" Cannot find symbol for '{identifier.ValueText}'"; string locationText = GetLocationText(identifier.GetLocation(), project); if (locationText != null) { message += $" at {locationText}"; } WriteLine(message, ConsoleColors.Yellow, Verbosity.Detailed); } continue; } if (!symbol.IsKind(SymbolKind.Namespace, SymbolKind.Alias) && !symbol.IsVisible(Options.SymbolVisibility)) { continue; } allIgnored = false; var fixes = new List <(SpellingDiagnostic diagnostic, SpellingFix fix)>(); string newName = identifier.ValueText; int indexOffset = 0; for (int j = 0; j < diagnostics.Count; j++) { SpellingDiagnostic diagnostic = diagnostics[j]; if (diagnostic == null) { continue; } LogHelpers.WriteSpellingDiagnostic(diagnostic, Options, sourceText, Path.GetDirectoryName(project.FilePath), " ", Verbosity.Normal); SpellingFix fix = GetFix(diagnostic); if (!fix.IsDefault) { if (!string.Equals(diagnostic.Value, fix.Value, StringComparison.Ordinal)) { WriteFix(diagnostic, fix); fixes.Add((diagnostic, fix)); newName = TextUtility.ReplaceRange(newName, fix.Value, diagnostic.Offset + indexOffset, diagnostic.Length); indexOffset += fix.Value.Length - diagnostic.Length; } else { for (int k = 0; k < symbolDiagnostics.Count; k++) { List <SpellingDiagnostic> diagnostics2 = symbolDiagnostics[k].diagnostics; for (int l = 0; l < diagnostics2.Count; l++) { if (SpellingData.IgnoredValues.KeyComparer.Equals(diagnostics2[l]?.Value, diagnostic.Value)) { diagnostics2[l] = null; } } } AddIgnoredValue(diagnostic); AddResult(results, diagnostic, default(SpellingFix), sourceText, ref sourceTextText); } } else { AddResult(results, diagnostic, default(SpellingFix), sourceText, ref sourceTextText); } } if (string.Equals(identifier.Text, newName, StringComparison.Ordinal)) { continue; } Solution newSolution = null; if (!Options.DryRun) { WriteLine($" Rename '{identifier.ValueText}' to '{newName}'", ConsoleColors.Green, Verbosity.Minimal); try { //TODO: detect naming conflict newSolution = await Microsoft.CodeAnalysis.Rename.Renamer.RenameSymbolAsync( CurrentSolution, symbol, newName, default(Microsoft.CodeAnalysis.Options.OptionSet), cancellationToken) .ConfigureAwait(false); } catch (InvalidOperationException #if DEBUG ex #endif ) { WriteLine($" Cannot rename '{symbol.Name}'", ConsoleColors.Yellow, Verbosity.Normal); #if DEBUG WriteLine(document.FilePath); WriteLine(identifier.ValueText); WriteLine(ex.ToString()); #endif continue; } } if (newSolution != null) { if (Workspace.TryApplyChanges(newSolution)) { project = CurrentSolution.GetProject(project.Id); } else { Debug.Fail($"Cannot apply changes to solution '{newSolution.FilePath}'"); WriteLine($" Cannot apply changes to solution '{newSolution.FilePath}'", ConsoleColors.Yellow, Verbosity.Normal); continue; } } foreach ((SpellingDiagnostic diagnostic, SpellingFix fix) in fixes) { AddResult(results, diagnostic, fix, sourceText, ref sourceTextText); ProcessFix(diagnostic, fix); } } return(results, allIgnored);
public static SpellingFixResult Create(string sourceText, SpellingDiagnostic diagnostic) { return(Create(sourceText, diagnostic, default)); }