From 22ba250a11eac9df1640676f3be5a9e90fa73e99 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Fri, 19 Jan 2024 23:25:24 +0100 Subject: [PATCH 1/7] update --- ...pSpellingService.CSharpSpellingAnalyzer.cs | 3 ++ src/CommandLine/Commands/SpellcheckCommand.cs | 9 ++---- .../Options/SpellcheckCommandLineOptions.cs | 9 ++---- src/CommandLine/Program.cs | 2 +- src/Core/FileSystemHelpers.cs | 29 +++++++++++++++++++ src/Workspaces.Core/DiagnosticFormatter.cs | 20 +++++++++++-- src/Workspaces.Core/Logging/LogHelpers.cs | 14 +++++++++ .../Spelling/Core/Spellchecker.cs | 10 +++++-- .../Spelling/SpellcheckAnalyzer.cs | 7 +++++ .../Spelling/SpellingAnalysisContext.cs | 29 +++++++++++++++++++ .../Spelling/SpellingScopeFilter.cs | 5 ++-- 11 files changed, 116 insertions(+), 21 deletions(-) diff --git a/src/CSharp.Workspaces/CSharp/Spelling/CSharpSpellingService.CSharpSpellingAnalyzer.cs b/src/CSharp.Workspaces/CSharp/Spelling/CSharpSpellingService.CSharpSpellingAnalyzer.cs index e19e50d5de..1ed75b57fe 100644 --- a/src/CSharp.Workspaces/CSharp/Spelling/CSharpSpellingService.CSharpSpellingAnalyzer.cs +++ b/src/CSharp.Workspaces/CSharp/Spelling/CSharpSpellingService.CSharpSpellingAnalyzer.cs @@ -54,6 +54,9 @@ private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) _options, context.CancellationToken); + if ((_options.ScopeFilter & SpellingScopeFilter.FileName) != 0) + analysisContext.AnalyzeFileName(tree); + CSharpSpellingWalker walker = CSharpSpellingWalker.Create(analysisContext); walker.Visit(root); diff --git a/src/CommandLine/Commands/SpellcheckCommand.cs b/src/CommandLine/Commands/SpellcheckCommand.cs index 9b721cedf5..4ae7fa7c58 100644 --- a/src/CommandLine/Commands/SpellcheckCommand.cs +++ b/src/CommandLine/Commands/SpellcheckCommand.cs @@ -60,9 +60,6 @@ public override async Task ExecuteAsync(ProjectOrSoluti MinWordLength = Options.MinWordLength, MaxWordLength = Options.MaxWordLength, IncludeGeneratedCode = Options.IncludeGeneratedCode, -#if DEBUG - Autofix = !Options.NoAutofix, -#endif Interactive = Options.Interactive, DryRun = Options.DryRun, }; @@ -87,7 +84,7 @@ private async Task FixAsync( Solution solution = project.Solution; - spellingFixer = GetSpellingFixer(solution); + spellingFixer = GetSpellcheckAnalyzer(solution); WriteLine($"Analyze '{project.Name}'", ConsoleColors.Cyan, Verbosity.Minimal); @@ -103,7 +100,7 @@ private async Task FixAsync( { Solution solution = projectOrSolution.AsSolution(); - spellingFixer = GetSpellingFixer(solution); + spellingFixer = GetSpellcheckAnalyzer(solution); results = await spellingFixer.FixSolutionAsync(f => IsMatch(f), cancellationToken); } @@ -118,7 +115,7 @@ private async Task FixAsync( : CommandStatus.NotSuccess, results); - SpellcheckAnalyzer GetSpellingFixer(Solution solution) + SpellcheckAnalyzer GetSpellcheckAnalyzer(Solution solution) { return new SpellcheckAnalyzer( solution, diff --git a/src/CommandLine/Options/SpellcheckCommandLineOptions.cs b/src/CommandLine/Options/SpellcheckCommandLineOptions.cs index 14a848f47c..0649e0295b 100644 --- a/src/CommandLine/Options/SpellcheckCommandLineOptions.cs +++ b/src/CommandLine/Options/SpellcheckCommandLineOptions.cs @@ -60,15 +60,10 @@ public sealed class SpellcheckCommandLineOptions : MSBuildCommandLineOptions HelpText = "Specifies minimal word length to be checked. Default value is 3.", MetaValue = "")] public int MinWordLength { get; set; } -#if DEBUG - [Option( - longName: OptionNames.NoAutofix, - HelpText = "Disable applying predefined fixes.")] - public bool NoAutofix { get; set; } -#endif + [Option( longName: OptionNames.Scope, - HelpText = "Defines syntax that should be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol, symbol and all. Literals are not analyzed by default.", + HelpText = "Defines syntax that should be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol, symbol, file-name and all. Literals are not analyzed by default.", MetaValue = "")] public IEnumerable Scope { get; set; } diff --git a/src/CommandLine/Program.cs b/src/CommandLine/Program.cs index 3f36d77f38..f107b2fe6b 100644 --- a/src/CommandLine/Program.cs +++ b/src/CommandLine/Program.cs @@ -575,7 +575,7 @@ private static async Task SpellcheckAsync(SpellcheckCommandLineOptions opti options.Scope, OptionNames.Scope, out SpellingScopeFilter scopeFilter, - SpellingScopeFilter.Comment | SpellingScopeFilter.Region | SpellingScopeFilter.Symbol)) + SpellingScopeFilter.Comment | SpellingScopeFilter.Region | SpellingScopeFilter.Symbol | SpellingScopeFilter.FileName)) { return ExitCodes.Error; } diff --git a/src/Core/FileSystemHelpers.cs b/src/Core/FileSystemHelpers.cs index 6787e9b0d5..94425fb3ae 100644 --- a/src/Core/FileSystemHelpers.cs +++ b/src/Core/FileSystemHelpers.cs @@ -80,4 +80,33 @@ public static string DetermineRelativePath(string baseDirectoryPath, string file return directoryUri.MakeRelativeUri(baseDirectoryUri).ToString().TrimEnd('/'); } + + public static int LastIndexOfDirectorySeparator(string path) + { + for (int i = path.Length - 1; i >= 0; i--) + { + if (IsDirectorySeparator(path[i])) + return i; + } + + return -1; + } + + public static int GetExtensionIndex(string path) + { + int length = path.Length; + + for (int i = length - 1; i >= 0; i--) + { + char ch = path[i]; + + if (ch == '.') + return i; + + if (IsDirectorySeparator(ch)) + break; + } + + return path.Length; + } } diff --git a/src/Workspaces.Core/DiagnosticFormatter.cs b/src/Workspaces.Core/DiagnosticFormatter.cs index b6d54d398a..abccb6e468 100644 --- a/src/Workspaces.Core/DiagnosticFormatter.cs +++ b/src/Workspaces.Core/DiagnosticFormatter.cs @@ -23,10 +23,26 @@ public static string FormatDiagnostic( sb.Append(' '); sb.Append(diagnostic.Id); sb.Append(": "); + sb.Append(diagnostic.GetMessage(formatProvider)); - string message = diagnostic.GetMessage(formatProvider); + return StringBuilderCache.GetStringAndFree(sb); + } - sb.Append(message); + public static string FormatDiagnostic( + Diagnostic diagnostic, + string filePath, + string? baseDirectoryPath = null, + IFormatProvider? formatProvider = null) + { + StringBuilder sb = StringBuilderCache.GetInstance(); + + sb.Append(PathUtilities.TrimStart(filePath, baseDirectoryPath)); + sb.Append(": "); + sb.Append(DiagnosticFormatter.GetSeverityText(diagnostic.Severity)); + sb.Append(' '); + sb.Append(diagnostic.Id); + sb.Append(": "); + sb.Append(diagnostic.GetMessage(formatProvider)); return StringBuilderCache.GetStringAndFree(sb); } diff --git a/src/Workspaces.Core/Logging/LogHelpers.cs b/src/Workspaces.Core/Logging/LogHelpers.cs index 1d676477ff..d218aea299 100644 --- a/src/Workspaces.Core/Logging/LogHelpers.cs +++ b/src/Workspaces.Core/Logging/LogHelpers.cs @@ -63,6 +63,20 @@ public static void WriteDiagnostic( WriteLine(text, diagnostic.Severity.GetColors(), verbosity); } + public static void WriteDiagnostic( + Diagnostic diagnostic, + string filePath, + string? baseDirectoryPath = null, + IFormatProvider? formatProvider = null, + string? indentation = null, + Verbosity verbosity = Verbosity.Diagnostic) + { + string text = DiagnosticFormatter.FormatDiagnostic(diagnostic, filePath, baseDirectoryPath, formatProvider); + + Write(indentation, verbosity); + WriteLine(text, diagnostic.Severity.GetColors(), verbosity); + } + public static void WriteDiagnostics( ImmutableArray diagnostics, string? baseDirectoryPath = null, diff --git a/src/Workspaces.Core/Spelling/Core/Spellchecker.cs b/src/Workspaces.Core/Spelling/Core/Spellchecker.cs index a854e7bce7..d01c610c0f 100644 --- a/src/Workspaces.Core/Spelling/Core/Spellchecker.cs +++ b/src/Workspaces.Core/Spelling/Core/Spellchecker.cs @@ -58,14 +58,18 @@ public Spellchecker( Data = data; Options = options ?? SpellcheckerOptions.Default; } - public ImmutableArray AnalyzeText(string value) + { + return AnalyzeText(value, 0, value.Length); + } + + public ImmutableArray AnalyzeText(string value, int startIndex, int length) { ImmutableArray.Builder? builder = null; - int prevEnd = 0; + int prevEnd = startIndex; - Match match = _urlRegex.Match(value, prevEnd); + Match match = _urlRegex.Match(value, prevEnd, length); while (match.Success) { diff --git a/src/Workspaces.Core/Spelling/SpellcheckAnalyzer.cs b/src/Workspaces.Core/Spelling/SpellcheckAnalyzer.cs index 940e3c342c..059ae558ad 100644 --- a/src/Workspaces.Core/Spelling/SpellcheckAnalyzer.cs +++ b/src/Workspaces.Core/Spelling/SpellcheckAnalyzer.cs @@ -192,6 +192,13 @@ public async Task> FixProjectAsync( continue; } + if (diagnostic.Location.Kind == LocationKind.None) + { + string filePath = diagnostic.Properties["FilePath"]!; + LogHelpers.WriteDiagnostic(diagnostic, filePath, baseDirectoryPath: Path.GetDirectoryName(project.FilePath), formatProvider: FormatProvider, indentation: " ", verbosity: Verbosity.Normal); + continue; + } + SpellingDiagnostic spellingDiagnostic = service.CreateSpellingDiagnostic(diagnostic); spellingDiagnostics.Add(spellingDiagnostic); diff --git a/src/Workspaces.Core/Spelling/SpellingAnalysisContext.cs b/src/Workspaces.Core/Spelling/SpellingAnalysisContext.cs index 513222d064..6a1d7d8c30 100644 --- a/src/Workspaces.Core/Spelling/SpellingAnalysisContext.cs +++ b/src/Workspaces.Core/Spelling/SpellingAnalysisContext.cs @@ -43,6 +43,19 @@ public void AnalyzeText(string value, TextSpan textSpan, SyntaxTree syntaxTree) ProcessMatches(matches, textSpan, syntaxTree); } + public void AnalyzeFileName(SyntaxTree syntaxTree) + { + string path = syntaxTree.FilePath; + int separatorIndex = FileSystemHelpers.LastIndexOfDirectorySeparator(path); + int extensionIndex = FileSystemHelpers.GetExtensionIndex(path); + + if (extensionIndex == -1) + extensionIndex = path.Length; + + ImmutableArray matches = _spellchecker.AnalyzeText(path, separatorIndex + 1, extensionIndex - separatorIndex - 1); + + ProcessMatches(matches, syntaxTree); + } public void AnalyzeIdentifier( SyntaxToken identifier, @@ -90,4 +103,20 @@ private void ProcessMatches( _reportDiagnostic(diagnostic); } } + + private void ProcessMatches(ImmutableArray matches, SyntaxTree syntaxTree) + { + foreach (SpellingMatch match in matches) + { + ImmutableDictionary properties = ImmutableDictionary.CreateRange(new[] { new KeyValuePair("FilePath", syntaxTree.FilePath) }); + + Diagnostic diagnostic = Diagnostic.Create( + SpellcheckAnalyzer.DiagnosticDescriptor, + Location.None, + properties: properties, + messageArgs: match.Value); + + _reportDiagnostic(diagnostic); + } + } } diff --git a/src/Workspaces.Core/Spelling/SpellingScopeFilter.cs b/src/Workspaces.Core/Spelling/SpellingScopeFilter.cs index 397cb685e9..64a6531a5f 100644 --- a/src/Workspaces.Core/Spelling/SpellingScopeFilter.cs +++ b/src/Workspaces.Core/Spelling/SpellingScopeFilter.cs @@ -36,6 +36,7 @@ internal enum SpellingScopeFilter ReturnType = 1 << 21, Symbol = Namespace | Type | Member | Local | Parameter | TypeParameter | UsingAlias | ReturnType, Literal = 1 << 22, - NonSymbol = Comment | Region | Literal, - All = Symbol | NonSymbol, + FileName = 1 << 23, + NonSymbol = Comment | Region | Literal | FileName, + All = Symbol | NonSymbol | FileName, } From 28ab0b641f4b2282fea733815fb8170cfdd938fc Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Fri, 19 Jan 2024 23:27:28 +0100 Subject: [PATCH 2/7] update --- ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.md b/ChangeLog.md index 0c2d5fe434..bad3feb2c8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Publish NuGet packages that provide [refactorings](https://www.nuget.org/packages/roslynator.refactorings) and [code fixes for compiler diagnostics](https://www.nuget.org/packages/roslynator.codefixes) ([PR](https://github.com/dotnet/roslynator/pull/1358)) - These packages are recommended to be used in an environment where Roslynator IDE extension cannot be used, e.g. VS Code + C# Dev Kit (see related [issue](https://github.com/dotnet/vscode-csharp/issues/6790)) +- [CLI] Spellcheck file names ([PR](https://github.com/dotnet/roslynator/pull/1368)) ### Fixed From ea1f6d0173e43edabefdea70b1a0743bbf8fb6d3 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sat, 20 Jan 2024 00:07:14 +0100 Subject: [PATCH 3/7] update --- src/Workspaces.Core/DiagnosticFormatter.cs | 37 ++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/Workspaces.Core/DiagnosticFormatter.cs b/src/Workspaces.Core/DiagnosticFormatter.cs index abccb6e468..11916749ef 100644 --- a/src/Workspaces.Core/DiagnosticFormatter.cs +++ b/src/Workspaces.Core/DiagnosticFormatter.cs @@ -19,11 +19,7 @@ public static string FormatDiagnostic( FormatLocation(diagnostic.Location, baseDirectoryPath, ref sb); - sb.Append(GetSeverityText(diagnostic.Severity)); - sb.Append(' '); - sb.Append(diagnostic.Id); - sb.Append(": "); - sb.Append(diagnostic.GetMessage(formatProvider)); + AppendDiagnosticInfo(diagnostic, formatProvider, sb); return StringBuilderCache.GetStringAndFree(sb); } @@ -38,13 +34,19 @@ public static string FormatDiagnostic( sb.Append(PathUtilities.TrimStart(filePath, baseDirectoryPath)); sb.Append(": "); - sb.Append(DiagnosticFormatter.GetSeverityText(diagnostic.Severity)); + + AppendDiagnosticInfo(diagnostic, formatProvider, sb); + + return StringBuilderCache.GetStringAndFree(sb); + } + + private static void AppendDiagnosticInfo(Diagnostic diagnostic, IFormatProvider? formatProvider, StringBuilder sb) + { + sb.Append(GetSeverityText(diagnostic.Severity)); sb.Append(' '); sb.Append(diagnostic.Id); sb.Append(": "); sb.Append(diagnostic.GetMessage(formatProvider)); - - return StringBuilderCache.GetStringAndFree(sb); } internal static void FormatLocation( @@ -80,18 +82,13 @@ internal static void FormatLocation( private static string GetSeverityText(DiagnosticSeverity diagnosticSeverity) { - switch (diagnosticSeverity) + return diagnosticSeverity switch { - case DiagnosticSeverity.Hidden: - return "hidden"; - case DiagnosticSeverity.Info: - return "info"; - case DiagnosticSeverity.Warning: - return "warning"; - case DiagnosticSeverity.Error: - return "error"; - default: - throw new InvalidOperationException(); - } + DiagnosticSeverity.Hidden => "hidden", + DiagnosticSeverity.Info => "info", + DiagnosticSeverity.Warning => "warning", + DiagnosticSeverity.Error => "error", + _ => throw new InvalidOperationException(), + }; } } From e68e8ce5c24239959f1a3dcab803524b110f9a6f Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sat, 20 Jan 2024 14:19:52 +0100 Subject: [PATCH 4/7] update --- .../Rename/CliSymbolRenameState.cs | 14 +- src/Workspaces.Core/DiagnosticFormatter.cs | 183 ++++++++++++++---- src/Workspaces.Core/Logging/LogHelpers.cs | 141 +------------- .../Spelling/SpellcheckAnalyzer.cs | 9 +- .../Spelling/SpellingAnalysisContext.cs | 10 +- 5 files changed, 172 insertions(+), 185 deletions(-) diff --git a/src/CommandLine/Rename/CliSymbolRenameState.cs b/src/CommandLine/Rename/CliSymbolRenameState.cs index bc88331743..e0bf646866 100644 --- a/src/CommandLine/Rename/CliSymbolRenameState.cs +++ b/src/CommandLine/Rename/CliSymbolRenameState.cs @@ -18,6 +18,16 @@ namespace Roslynator.CommandLine.Rename; internal class CliSymbolRenameState : SymbolRenameState { + private static readonly SymbolDisplayFormat _symbolDefinitionFormat = SymbolDisplayFormat.CSharpErrorMessageFormat.Update( + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + parameterOptions: SymbolDisplayParameterOptions.IncludeParamsRefOut + | SymbolDisplayParameterOptions.IncludeType + | SymbolDisplayParameterOptions.IncludeName + | SymbolDisplayParameterOptions.IncludeDefaultValue, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers + | SymbolDisplayMiscellaneousOptions.UseSpecialTypes + | SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName); + public CliSymbolRenameState( Solution solution, Func predicate, @@ -103,7 +113,9 @@ public override async Task RenameSymbolsAsync(Project project, CancellationToken Document document, CancellationToken cancellationToken) { - LogHelpers.WriteSymbolDefinition(symbol, baseDirectoryPath: Path.GetDirectoryName(document.Project.FilePath), " ", Verbosity.Normal); + string text = DiagnosticFormatter.FormatSymbolDefinition(symbol, baseDirectoryPath: Path.GetDirectoryName(document.Project.FilePath), " ", _symbolDefinitionFormat); + + WriteLine(text, ConsoleColors.Cyan, Verbosity.Normal); if (ShouldWrite(Verbosity.Detailed) || CodeContext >= 0) diff --git a/src/Workspaces.Core/DiagnosticFormatter.cs b/src/Workspaces.Core/DiagnosticFormatter.cs index 11916749ef..bffa806edc 100644 --- a/src/Workspaces.Core/DiagnosticFormatter.cs +++ b/src/Workspaces.Core/DiagnosticFormatter.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -13,46 +14,39 @@ internal static class DiagnosticFormatter public static string FormatDiagnostic( Diagnostic diagnostic, string? baseDirectoryPath = null, - IFormatProvider? formatProvider = null) + IFormatProvider? formatProvider = null, + bool omitSpan = false) { StringBuilder sb = StringBuilderCache.GetInstance(); - FormatLocation(diagnostic.Location, baseDirectoryPath, ref sb); + FormatLocation(diagnostic.Location, baseDirectoryPath, ref sb, omitSpan); - AppendDiagnosticInfo(diagnostic, formatProvider, sb); - - return StringBuilderCache.GetStringAndFree(sb); - } - - public static string FormatDiagnostic( - Diagnostic diagnostic, - string filePath, - string? baseDirectoryPath = null, - IFormatProvider? formatProvider = null) - { - StringBuilder sb = StringBuilderCache.GetInstance(); - - sb.Append(PathUtilities.TrimStart(filePath, baseDirectoryPath)); - sb.Append(": "); - - AppendDiagnosticInfo(diagnostic, formatProvider, sb); - - return StringBuilderCache.GetStringAndFree(sb); - } - - private static void AppendDiagnosticInfo(Diagnostic diagnostic, IFormatProvider? formatProvider, StringBuilder sb) - { sb.Append(GetSeverityText(diagnostic.Severity)); sb.Append(' '); sb.Append(diagnostic.Id); sb.Append(": "); sb.Append(diagnostic.GetMessage(formatProvider)); + + return StringBuilderCache.GetStringAndFree(sb); + + static string GetSeverityText(DiagnosticSeverity severity) + { + return severity switch + { + DiagnosticSeverity.Hidden => "hidden", + DiagnosticSeverity.Info => "info", + DiagnosticSeverity.Warning => "warning", + DiagnosticSeverity.Error => "error", + _ => throw new InvalidOperationException(), + }; + } } - internal static void FormatLocation( + private static void FormatLocation( Location location, string? baseDirectoryPath, - ref StringBuilder sb) + ref StringBuilder sb, + bool omitSpan = false) { switch (location.Kind) { @@ -66,13 +60,20 @@ internal static void FormatLocation( { sb.Append(PathUtilities.TrimStart(span.Path, baseDirectoryPath)); - LinePosition linePosition = span.StartLinePosition; - - sb.Append('('); - sb.Append(linePosition.Line + 1); - sb.Append(','); - sb.Append(linePosition.Character + 1); - sb.Append("): "); + if (omitSpan) + { + sb.Append(": "); + } + else + { + LinePosition linePosition = span.StartLinePosition; + + sb.Append('('); + sb.Append(linePosition.Line + 1); + sb.Append(','); + sb.Append(linePosition.Character + 1); + sb.Append("): "); + } } break; @@ -80,15 +81,113 @@ internal static void FormatLocation( } } - private static string GetSeverityText(DiagnosticSeverity diagnosticSeverity) + public static string FormatSymbolDefinition( + ISymbol symbol, + string? baseDirectoryPath = null, + string? indentation = null, + SymbolDisplayFormat? format = null) { - return diagnosticSeverity switch + StringBuilder sb = StringBuilderCache.GetInstance(); + + sb.Append(indentation); + + FormatLocation(symbol.Locations[0], baseDirectoryPath, ref sb); + + sb.Append(GetSymbolTitle(symbol)); + + if (symbol.IsKind(SymbolKind.Parameter, SymbolKind.TypeParameter)) + { + sb.Append(" '"); + sb.Append(symbol.Name); + sb.Append("': "); + + if (symbol.ContainingSymbol is IMethodSymbol { MethodKind: MethodKind.LambdaMethod }) + { + sb.Append("anonymous function"); + } + else + { + Debug.Assert(symbol.ContainingSymbol.IsKind(SymbolKind.NamedType, SymbolKind.Method, SymbolKind.Property), symbol.Kind.ToString()); + + sb.Append(symbol.ContainingSymbol.ToDisplayString(format)); + } + } + else + { + sb.Append(": "); + sb.Append(symbol.ToDisplayString(format)); + } + + return StringBuilderCache.GetStringAndFree(sb); + + static string GetSymbolTitle(ISymbol symbol) { - DiagnosticSeverity.Hidden => "hidden", - DiagnosticSeverity.Info => "info", - DiagnosticSeverity.Warning => "warning", - DiagnosticSeverity.Error => "error", - _ => throw new InvalidOperationException(), - }; + switch (symbol.Kind) + { + case SymbolKind.Event: + { + return "event"; + } + case SymbolKind.Field: + { + return (symbol.ContainingType.TypeKind == TypeKind.Enum) ? "enum field" : "field"; + } + case SymbolKind.Local: + { + return "local"; + } + case SymbolKind.Method: + { + var methodSymbol = (IMethodSymbol)symbol; + + switch (methodSymbol.MethodKind) + { + case MethodKind.Ordinary: + return "method"; + case MethodKind.LocalFunction: + return "local function"; + } + + Debug.Fail(methodSymbol.MethodKind.ToString()); + break; + } + case SymbolKind.NamedType: + { + var typeSymbol = (INamedTypeSymbol)symbol; + + switch (typeSymbol.TypeKind) + { + case TypeKind.Class: + return "class"; + case TypeKind.Delegate: + return "delegate"; + case TypeKind.Enum: + return "enum"; + case TypeKind.Interface: + return "interface"; + case TypeKind.Struct: + return "struct"; + } + + Debug.Fail(typeSymbol.TypeKind.ToString()); + break; + } + case SymbolKind.Parameter: + { + return "parameter"; + } + case SymbolKind.Property: + { + return (((IPropertySymbol)symbol).IsIndexer) ? "indexer" : "property"; + } + case SymbolKind.TypeParameter: + { + return "type parameter"; + } + } + + Debug.Fail(symbol.Kind.ToString()); + return symbol.Kind.ToString(); + } } } diff --git a/src/Workspaces.Core/Logging/LogHelpers.cs b/src/Workspaces.Core/Logging/LogHelpers.cs index d218aea299..2adfc217a3 100644 --- a/src/Workspaces.Core/Logging/LogHelpers.cs +++ b/src/Workspaces.Core/Logging/LogHelpers.cs @@ -3,10 +3,8 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; @@ -20,16 +18,6 @@ namespace Roslynator; internal static class LogHelpers { - private static readonly SymbolDisplayFormat _symbolDefinitionFormat = SymbolDisplayFormat.CSharpErrorMessageFormat.Update( - typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, - parameterOptions: SymbolDisplayParameterOptions.IncludeParamsRefOut - | SymbolDisplayParameterOptions.IncludeType - | SymbolDisplayParameterOptions.IncludeName - | SymbolDisplayParameterOptions.IncludeDefaultValue, - miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers - | SymbolDisplayMiscellaneousOptions.UseSpecialTypes - | SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName); - public static void WriteElapsedTime(string message, TimeSpan elapsedTime, Verbosity verbosity) { if (!ShouldWrite(verbosity)) @@ -55,23 +43,10 @@ public static void WriteDiagnostic( string? baseDirectoryPath = null, IFormatProvider? formatProvider = null, string? indentation = null, + bool omitSpan = false, Verbosity verbosity = Verbosity.Diagnostic) { - string text = DiagnosticFormatter.FormatDiagnostic(diagnostic, baseDirectoryPath, formatProvider); - - Write(indentation, verbosity); - WriteLine(text, diagnostic.Severity.GetColors(), verbosity); - } - - public static void WriteDiagnostic( - Diagnostic diagnostic, - string filePath, - string? baseDirectoryPath = null, - IFormatProvider? formatProvider = null, - string? indentation = null, - Verbosity verbosity = Verbosity.Diagnostic) - { - string text = DiagnosticFormatter.FormatDiagnostic(diagnostic, filePath, baseDirectoryPath, formatProvider); + string text = DiagnosticFormatter.FormatDiagnostic(diagnostic, baseDirectoryPath, formatProvider, omitSpan); Write(indentation, verbosity); WriteLine(text, diagnostic.Severity.GetColors(), verbosity); @@ -95,7 +70,7 @@ public static void WriteDiagnostics( foreach (Diagnostic diagnostic in diagnostics.OrderBy(f => f, DiagnosticComparer.IdThenFilePathThenSpanStart)) { - WriteDiagnostic(diagnostic, baseDirectoryPath, formatProvider, indentation, verbosity); + WriteDiagnostic(diagnostic, baseDirectoryPath, formatProvider, indentation, verbosity: verbosity); count++; @@ -120,7 +95,7 @@ public static void WriteSpellingDiagnostic( string indentation, Verbosity verbosity) { - WriteDiagnostic(diagnostic.Diagnostic, baseDirectoryPath, default(IFormatProvider), indentation, verbosity); + WriteDiagnostic(diagnostic.Diagnostic, baseDirectoryPath, default(IFormatProvider), indentation, verbosity: verbosity); TextSpan span = diagnostic.Span; TextLineCollection lines = sourceText.Lines; @@ -400,114 +375,6 @@ public static void WriteMultipleOperationsSummary(CodeAction fix) WriteLine($" EquivalenceKey: {fix.EquivalenceKey}", ConsoleColors.Yellow, Verbosity.Diagnostic); } - public static void WriteSymbolDefinition( - ISymbol symbol, - string? baseDirectoryPath = null, - string? indentation = null, - Verbosity verbosity = Verbosity.Diagnostic) - { - StringBuilder sb = StringBuilderCache.GetInstance(); - - sb.Append(indentation); - DiagnosticFormatter.FormatLocation(symbol.Locations[0], baseDirectoryPath, ref sb); - sb.Append(GetSymbolTitle(symbol)); - - if (symbol.IsKind(SymbolKind.Parameter, SymbolKind.TypeParameter)) - { - sb.Append(" '"); - sb.Append(symbol.Name); - sb.Append("': "); - - if (symbol.ContainingSymbol is IMethodSymbol { MethodKind: MethodKind.LambdaMethod }) - { - sb.Append("anonymous function"); - } - else - { - Debug.Assert(symbol.ContainingSymbol.IsKind(SymbolKind.NamedType, SymbolKind.Method, SymbolKind.Property), symbol.Kind.ToString()); - - sb.Append(symbol.ContainingSymbol.ToDisplayString(_symbolDefinitionFormat)); - } - } - else - { - sb.Append(": "); - sb.Append(symbol.ToDisplayString(_symbolDefinitionFormat)); - } - - WriteLine(StringBuilderCache.GetStringAndFree(sb), ConsoleColors.Cyan, verbosity); - - static string GetSymbolTitle(ISymbol symbol) - { - switch (symbol.Kind) - { - case SymbolKind.Event: - { - return "event"; - } - case SymbolKind.Field: - { - return (symbol.ContainingType.TypeKind == TypeKind.Enum) ? "enum field" : "field"; - } - case SymbolKind.Local: - { - return "local"; - } - case SymbolKind.Method: - { - var methodSymbol = (IMethodSymbol)symbol; - - switch (methodSymbol.MethodKind) - { - case MethodKind.Ordinary: - return "method"; - case MethodKind.LocalFunction: - return "local function"; - } - - Debug.Fail(methodSymbol.MethodKind.ToString()); - break; - } - case SymbolKind.NamedType: - { - var typeSymbol = (INamedTypeSymbol)symbol; - - switch (typeSymbol.TypeKind) - { - case TypeKind.Class: - return "class"; - case TypeKind.Delegate: - return "delegate"; - case TypeKind.Enum: - return "enum"; - case TypeKind.Interface: - return "interface"; - case TypeKind.Struct: - return "struct"; - } - - Debug.Fail(typeSymbol.TypeKind.ToString()); - break; - } - case SymbolKind.Parameter: - { - return "parameter"; - } - case SymbolKind.Property: - { - return (((IPropertySymbol)symbol).IsIndexer) ? "indexer" : "property"; - } - case SymbolKind.TypeParameter: - { - return "type parameter"; - } - } - - Debug.Fail(symbol.Kind.ToString()); - return symbol.Kind.ToString(); - } - } - public static int WriteCompilerErrors( ImmutableArray diagnostics, string? baseDirectoryPath = null, diff --git a/src/Workspaces.Core/Spelling/SpellcheckAnalyzer.cs b/src/Workspaces.Core/Spelling/SpellcheckAnalyzer.cs index 059ae558ad..dad4d995d9 100644 --- a/src/Workspaces.Core/Spelling/SpellcheckAnalyzer.cs +++ b/src/Workspaces.Core/Spelling/SpellcheckAnalyzer.cs @@ -194,8 +194,13 @@ public async Task> FixProjectAsync( if (diagnostic.Location.Kind == LocationKind.None) { - string filePath = diagnostic.Properties["FilePath"]!; - LogHelpers.WriteDiagnostic(diagnostic, filePath, baseDirectoryPath: Path.GetDirectoryName(project.FilePath), formatProvider: FormatProvider, indentation: " ", verbosity: Verbosity.Normal); + if (diagnostic.Properties.TryGetValue("FilePath", out string? filePath)) + { + Diagnostic diagnostic2 = Diagnostic.Create(diagnostic.Descriptor, Location.Create(filePath!, default, default), diagnostic.AdditionalLocations, diagnostic.Properties, messageArgs: diagnostic.Properties["Value"]); + + LogHelpers.WriteDiagnostic(diagnostic2, baseDirectoryPath: Path.GetDirectoryName(project.FilePath), formatProvider: FormatProvider, indentation: " ", omitSpan: true, verbosity: Verbosity.Normal); + } + continue; } diff --git a/src/Workspaces.Core/Spelling/SpellingAnalysisContext.cs b/src/Workspaces.Core/Spelling/SpellingAnalysisContext.cs index 6a1d7d8c30..d892c78fc8 100644 --- a/src/Workspaces.Core/Spelling/SpellingAnalysisContext.cs +++ b/src/Workspaces.Core/Spelling/SpellingAnalysisContext.cs @@ -46,13 +46,13 @@ public void AnalyzeText(string value, TextSpan textSpan, SyntaxTree syntaxTree) public void AnalyzeFileName(SyntaxTree syntaxTree) { string path = syntaxTree.FilePath; - int separatorIndex = FileSystemHelpers.LastIndexOfDirectorySeparator(path); + int separatorIndex = FileSystemHelpers.LastIndexOfDirectorySeparator(path) + 1; int extensionIndex = FileSystemHelpers.GetExtensionIndex(path); if (extensionIndex == -1) extensionIndex = path.Length; - ImmutableArray matches = _spellchecker.AnalyzeText(path, separatorIndex + 1, extensionIndex - separatorIndex - 1); + ImmutableArray matches = _spellchecker.AnalyzeText(path, separatorIndex, extensionIndex - separatorIndex); ProcessMatches(matches, syntaxTree); } @@ -108,7 +108,11 @@ private void ProcessMatches(ImmutableArray matches, SyntaxTree sy { foreach (SpellingMatch match in matches) { - ImmutableDictionary properties = ImmutableDictionary.CreateRange(new[] { new KeyValuePair("FilePath", syntaxTree.FilePath) }); + ImmutableDictionary properties = ImmutableDictionary.CreateRange(new[] + { + new KeyValuePair("Value", match.Value), + new KeyValuePair("FilePath", syntaxTree.FilePath), + }); Diagnostic diagnostic = Diagnostic.Create( SpellcheckAnalyzer.DiagnosticDescriptor, From 01b4af79fccc34a5de3bf815c4a7dfff1b49bf51 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sat, 20 Jan 2024 18:19:16 +0100 Subject: [PATCH 5/7] update --- src/CommandLine/Options/SpellcheckCommandLineOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CommandLine/Options/SpellcheckCommandLineOptions.cs b/src/CommandLine/Options/SpellcheckCommandLineOptions.cs index 0649e0295b..48e1d35b4d 100644 --- a/src/CommandLine/Options/SpellcheckCommandLineOptions.cs +++ b/src/CommandLine/Options/SpellcheckCommandLineOptions.cs @@ -33,7 +33,7 @@ public sealed class SpellcheckCommandLineOptions : MSBuildCommandLineOptions [Option( longName: OptionNames.IgnoredScope, - HelpText = "Defines syntax that should not be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol and symbol.", + HelpText = "Defines syntax that should not be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol, symbol and file-name.", MetaValue = "")] public IEnumerable IgnoredScope { get; set; } From 5ee6be9b84b5a7d51288ad08ae57315c27cf3830 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sun, 21 Jan 2024 00:24:38 +0100 Subject: [PATCH 6/7] update --- src/CommandLine/Options/SpellcheckCommandLineOptions.cs | 2 +- src/CommandLine/Program.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CommandLine/Options/SpellcheckCommandLineOptions.cs b/src/CommandLine/Options/SpellcheckCommandLineOptions.cs index 48e1d35b4d..c6d160e325 100644 --- a/src/CommandLine/Options/SpellcheckCommandLineOptions.cs +++ b/src/CommandLine/Options/SpellcheckCommandLineOptions.cs @@ -63,7 +63,7 @@ public sealed class SpellcheckCommandLineOptions : MSBuildCommandLineOptions [Option( longName: OptionNames.Scope, - HelpText = "Defines syntax that should be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol, symbol, file-name and all. Literals are not analyzed by default.", + HelpText = "Defines syntax that should be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol, symbol, file-name and all. Literals and file names are not analyzed by default.", MetaValue = "")] public IEnumerable Scope { get; set; } diff --git a/src/CommandLine/Program.cs b/src/CommandLine/Program.cs index f107b2fe6b..3f36d77f38 100644 --- a/src/CommandLine/Program.cs +++ b/src/CommandLine/Program.cs @@ -575,7 +575,7 @@ private static async Task SpellcheckAsync(SpellcheckCommandLineOptions opti options.Scope, OptionNames.Scope, out SpellingScopeFilter scopeFilter, - SpellingScopeFilter.Comment | SpellingScopeFilter.Region | SpellingScopeFilter.Symbol | SpellingScopeFilter.FileName)) + SpellingScopeFilter.Comment | SpellingScopeFilter.Region | SpellingScopeFilter.Symbol)) { return ExitCodes.Error; } From 160ee9e2ce4187b790681873a8c379f4d8101d04 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sun, 21 Jan 2024 00:27:15 +0100 Subject: [PATCH 7/7] update --- ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.md b/ChangeLog.md index bad3feb2c8..2697985f52 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Publish NuGet packages that provide [refactorings](https://www.nuget.org/packages/roslynator.refactorings) and [code fixes for compiler diagnostics](https://www.nuget.org/packages/roslynator.codefixes) ([PR](https://github.com/dotnet/roslynator/pull/1358)) - These packages are recommended to be used in an environment where Roslynator IDE extension cannot be used, e.g. VS Code + C# Dev Kit (see related [issue](https://github.com/dotnet/vscode-csharp/issues/6790)) - [CLI] Spellcheck file names ([PR](https://github.com/dotnet/roslynator/pull/1368)) + - `roslynator spellcheck --scope file-name` ### Fixed