Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log all formatter error messages in a csc-style #1016

Merged
merged 5 commits into from Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 7 additions & 18 deletions src/Analyzers/AnalyzerFormatter.cs
Expand Up @@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -141,39 +140,29 @@ internal class AnalyzerFormatter : ICodeFormatter
await _runner.RunCodeAnalysisAsync(result, analyzers, project, formattablePaths, severity, fixableCompilerDiagnostics, logger, cancellationToken).ConfigureAwait(false);
}

LogDiagnosticLocations(solution, result.Diagnostics.SelectMany(kvp => kvp.Value), options.WorkspaceFilePath, options.ChangesAreErrors, logger, formattedFiles);
LogDiagnosticLocations(solution, result.Diagnostics.SelectMany(kvp => kvp.Value), options.SaveFormattedFiles, options.ChangesAreErrors, logger, options.LogLevel, formattedFiles);

return result.Diagnostics.ToImmutableDictionary(kvp => kvp.Key.Id, kvp => kvp.Value.Select(diagnostic => diagnostic.Id).ToImmutableHashSet());

static void LogDiagnosticLocations(Solution solution, IEnumerable<Diagnostic> diagnostics, string workspacePath, bool changesAreErrors, ILogger logger, List<FormattedFile> formattedFiles)
static void LogDiagnosticLocations(Solution solution, IEnumerable<Diagnostic> diagnostics, bool saveFormattedFiles, bool changesAreErrors, ILogger logger, LogLevel logLevel, List<FormattedFile> formattedFiles)
{
var workspaceFolder = Path.GetDirectoryName(workspacePath) ?? workspacePath;

foreach (var diagnostic in diagnostics)
{
var message = $"{diagnostic.GetMessage()} ({diagnostic.Id})";
var document = solution.GetDocument(diagnostic.Location.SourceTree);
if (document is null)
{
continue;
}

var filePath = document.FilePath ?? document.Name;

var mappedLineSpan = diagnostic.Location.GetMappedLineSpan();
var changePosition = mappedLineSpan.StartLinePosition;

var formatMessage = $"{Path.GetRelativePath(workspaceFolder, filePath)}({changePosition.Line + 1},{changePosition.Character + 1}): {message}";
formattedFiles.Add(new FormattedFile(document!, new[] { new FileChange(changePosition, message) }));
var diagnosticPosition = mappedLineSpan.StartLinePosition;

if (changesAreErrors)
if (!saveFormattedFiles || logLevel == LogLevel.Debug)
{
logger.LogError(formatMessage);
}
else
{
logger.LogWarning(formatMessage);
logger.LogDiagnosticIssue(document, diagnosticPosition, diagnostic, changesAreErrors);
}

formattedFiles.Add(new FormattedFile(document, new[] { new FileChange(diagnosticPosition, $"{diagnostic.Severity.ToString().ToLower()} {diagnostic.Id}: {diagnostic.GetMessage()}") }));
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Formatters/CharsetFormatter.cs
Expand Up @@ -20,6 +20,7 @@ internal sealed class CharsetFormatter : DocumentFormatter
private static Encoding Utf8 => new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
private static Encoding Latin1 => Encoding.GetEncoding("iso-8859-1");

public override string Name => "CHARSET";
public override FixCategory Category => FixCategory.Whitespace;

internal override Task<SourceText> FormatFileAsync(
Expand Down
36 changes: 10 additions & 26 deletions src/Formatters/DocumentFormatter.cs
@@ -1,9 +1,7 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
Expand All @@ -20,6 +18,11 @@ internal abstract class DocumentFormatter : ICodeFormatter
{
protected abstract string FormatWarningDescription { get; }

/// <summary>
/// Gets the fix name to use when logging.
/// </summary>
public abstract string Name { get; }

/// <summary>
/// Gets the fix category this formatter belongs to.
/// </summary>
Expand Down Expand Up @@ -113,7 +116,7 @@ internal abstract class DocumentFormatter : ICodeFormatter
/// Applies the changed <see cref="SourceText"/> to each formatted <see cref="Document"/>.
/// </summary>
private async Task<Solution> ApplyFileChangesAsync(
Solution solution,
Solution solution,
ImmutableArray<(Document, Task<(SourceText originalText, SourceText? formattedText)>)> formattedDocuments,
FormatOptions formatOptions,
ILogger logger,
Expand Down Expand Up @@ -141,7 +144,7 @@ internal abstract class DocumentFormatter : ICodeFormatter
continue;
}

var fileChanges = GetFileChanges(formatOptions, formatOptions.WorkspaceFilePath, document.FilePath, originalText, formattedText, formatOptions.ChangesAreErrors, logger);
var fileChanges = GetFileChanges(formatOptions, document, originalText, formattedText, formatOptions.ChangesAreErrors, logger);
formattedFiles.Add(new FormattedFile(document, fileChanges));

formattedSolution = formattedSolution.WithDocumentText(document.Id, formattedText, PreservationMode.PreserveIdentity);
Expand All @@ -150,14 +153,8 @@ internal abstract class DocumentFormatter : ICodeFormatter
return formattedSolution;
}

private ImmutableArray<FileChange> GetFileChanges(FormatOptions formatOptions, string workspacePath, string filePath, SourceText originalText, SourceText formattedText, bool changesAreErrors, ILogger logger)
private ImmutableArray<FileChange> GetFileChanges(FormatOptions formatOptions, Document document, SourceText originalText, SourceText formattedText, bool changesAreErrors, ILogger logger)
{
var workspaceFolder = Path.GetDirectoryName(workspacePath);
if (workspaceFolder is null)
{
throw new Exception($"Unable to find directory name for '{workspacePath}'");
}

var fileChanges = ImmutableArray.CreateBuilder<FileChange>();
var changes = formattedText.GetChangeRanges(originalText);

Expand All @@ -169,28 +166,15 @@ private ImmutableArray<FileChange> GetFileChanges(FormatOptions formatOptions, s
var fileChange = new FileChange(changePosition, FormatWarningDescription);
fileChanges.Add(fileChange);

if (!formatOptions.SaveFormattedFiles || formatOptions.LogLevel == LogLevel.Trace)
if (!formatOptions.SaveFormattedFiles || formatOptions.LogLevel == LogLevel.Debug)
{
LogFormattingChanges(filePath, changesAreErrors, logger, workspaceFolder, fileChange);
logger.LogFormattingIssue(document, Name, fileChange, changesAreErrors);
}
}

return fileChanges.ToImmutable();
}

private static void LogFormattingChanges(string filePath, bool changesAreErrors, ILogger logger, string workspaceFolder, FileChange fileChange)
{
var formatMessage = $"{Path.GetRelativePath(workspaceFolder, filePath)}({fileChange.LineNumber},{fileChange.CharNumber}): {fileChange.FormatDescription}";
if (changesAreErrors)
{
logger.LogError(formatMessage);
}
else
{
logger.LogWarning(formatMessage);
}
}

protected static async Task<bool> IsSameDocumentAndVersionAsync(Document a, Document b, CancellationToken cancellationToken)
{
if (a == b)
Expand Down
1 change: 1 addition & 0 deletions src/Formatters/EndOfLineFormatter.cs
Expand Up @@ -15,6 +15,7 @@ internal sealed class EndOfLineFormatter : DocumentFormatter
{
protected override string FormatWarningDescription => Resources.Fix_end_of_line_marker;

public override string Name => "ENDOFLINE";
public override FixCategory Category => FixCategory.Whitespace;

internal override Task<SourceText> FormatFileAsync(
Expand Down
1 change: 1 addition & 0 deletions src/Formatters/FinalNewlineFormatter.cs
Expand Up @@ -14,6 +14,7 @@ internal sealed class FinalNewlineFormatter : DocumentFormatter
{
protected override string FormatWarningDescription => Resources.Fix_final_newline;

public override string Name => "FINALNEWLINE";
public override FixCategory Category => FixCategory.Whitespace;

internal override async Task<SourceText> FormatFileAsync(
Expand Down
1 change: 1 addition & 0 deletions src/Formatters/OrganizeImportsFormatter.cs
Expand Up @@ -20,6 +20,7 @@ internal sealed class OrganizeImportsFormatter : DocumentFormatter
protected override string FormatWarningDescription => Resources.Fix_imports_ordering;
private readonly DocumentFormatter _endOfLineFormatter = new EndOfLineFormatter();

public override string Name => "IMPORTS";
public override FixCategory Category => FixCategory.Whitespace;

internal override async Task<SourceText> FormatFileAsync(
Expand Down
1 change: 1 addition & 0 deletions src/Formatters/UnnecessaryImportsFormatter.cs
Expand Up @@ -20,6 +20,7 @@ internal sealed class UnnecessaryImportsFormatter : DocumentFormatter

protected override string FormatWarningDescription => Resources.Remove_unnecessary_import;

public override string Name => IDE0005;
public override FixCategory Category => FixCategory.CodeStyle;

internal override async Task<SourceText> FormatFileAsync(
Expand Down
1 change: 1 addition & 0 deletions src/Formatters/WhitespaceFormatter.cs
Expand Up @@ -17,6 +17,7 @@ internal sealed class WhitespaceFormatter : DocumentFormatter
{
protected override string FormatWarningDescription => Resources.Fix_whitespace_formatting;

public override string Name => "WHITESPACE";
public override FixCategory Category => FixCategory.Whitespace;

internal override async Task<SourceText> FormatFileAsync(
Expand Down
9 changes: 9 additions & 0 deletions src/Logging/IIssueFormatter.cs
@@ -0,0 +1,9 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

namespace Microsoft.CodeAnalysis.Tools.Logging
{
public interface IIssueFormatter
{
string FormatIssue(Document document, string severity, string issueId, int lineNumber, int charNumber, string message);
}
}
37 changes: 37 additions & 0 deletions src/Logging/ILoggerExtensions.cs
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Tools.Logging;
using Microsoft.Extensions.Logging;

namespace Microsoft.CodeAnalysis.Tools
{
internal static class ILoggerExtensions
{
private static readonly string s_errorSeverityString = DiagnosticSeverity.Error.ToString().ToLower();

public static IIssueFormatter IssueFormatter { get; set; } = new MSBuildIssueFormatter();

public static string LogFormattingIssue(this ILogger logger, Document document, string formatterName, FileChange fileChange, bool changesAreErrors)
=> LogIssue(logger, document, s_errorSeverityString, formatterName, fileChange.LineNumber, fileChange.CharNumber, fileChange.FormatDescription, changesAreErrors);

public static string LogDiagnosticIssue(this ILogger logger, Document document, LinePosition diagnosticPosition, Diagnostic diagnostic, bool changesAreErrors)
=> LogIssue(logger, document, diagnostic.Severity.ToString().ToLower(), diagnostic.Id, diagnosticPosition.Line + 1, diagnosticPosition.Character + 1, diagnostic.GetMessage(), changesAreErrors);

private static string LogIssue(ILogger logger, Document document, string severity, string issueId, int lineNumber, int charNumber, string message, bool changesAreErrors)
{
var formattedMessage = IssueFormatter.FormatIssue(document, severity, issueId, lineNumber, charNumber, message);

if (changesAreErrors)
{
logger.LogError(formattedMessage);
}
else
{
logger.LogWarning(formattedMessage);
}

return formattedMessage;
}
}
}
10 changes: 10 additions & 0 deletions src/Logging/MSBuildIssueFormatter.cs
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

namespace Microsoft.CodeAnalysis.Tools.Logging
{
internal sealed class MSBuildIssueFormatter : IIssueFormatter
{
public string FormatIssue(Document document, string severity, string issueId, int lineNumber, int charNumber, string message)
=> $"{document.FilePath ?? document.Name}({lineNumber},{charNumber}): {severity} {issueId}: {message} [{document.Project.FilePath}]";
}
}
2 changes: 1 addition & 1 deletion src/Logging/SimpleConsoleLogger.cs
Expand Up @@ -83,7 +83,7 @@ private void LogToConsole(IConsole console, string message, bool logToErrorStrea
{
if (logToErrorStream)
{
console.Error.Write($" {message}{Environment.NewLine}");
console.Error.Write($"{message}{Environment.NewLine}");
}
else
{
Expand Down