なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

独自のツールで Visual Studio のエラーフォーマット形式で出力したい

Visual Studio の PostBuild イベントなどで使うツールで、エラーフォーマットを Visual Studio 側にあわせてあげることで、
それらをエラーリストに綺麗に表示してくれるようになります。
ということで、今日はその実装方法について書いてみます。

今回は Roslyn を使っているアプリで、 C# のシンタックスエラーが発生したケースを考えます。
その場合は、次のようなクラスを作って GetMessage() の結果を標準エラー出力に投げつけると良いです。

using System;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

using SharpX.Compiler.Composition.Interfaces;

namespace SharpX.Compiler.Composition.Abstractions
{
    public class VisualStudioCatchError : IError
    {
        private readonly CapturedType _captured;
        private readonly Diagnostic? _diagnostic;
        private readonly string? _msg;
        private readonly CSharpSyntaxNode? _node;

        public VisualStudioCatchError(CSharpSyntaxNode node, string message)
        {
            _node = node;
            _msg = message;
            _captured = CapturedType.Node;
        }

        public VisualStudioCatchError(Diagnostic diagnostic)
        {
            _diagnostic = diagnostic;
            _captured = CapturedType.Diagnostic;
        }

        public string GetMessage()
        {
            return _captured switch
            {
                CapturedType.Diagnostic => GetMessage(_diagnostic!),
                CapturedType.Node => GetMessage(_node!, _msg!),
                _ => throw new ArgumentOutOfRangeException()
            };
        }

        private static string GetMessage(Diagnostic diagnostic)
        {
            var path = diagnostic.Location.SourceTree?.FilePath ?? "InMemory.cs";
            var position = diagnostic.Location.GetLineSpan().StartLinePosition;
            return $"{path}({position.Line + 1},{position.Character}): Error {diagnostic.Id}: {diagnostic.GetMessage()}";
        }

        private static string GetMessage(CSharpSyntaxNode node, string msg)
        {
            var path = node.SyntaxTree.FilePath;
            var position = node.GetLocation().GetLineSpan().StartLinePosition;
            return $"{path}({position.Line + 1},{position.Character}): Error SXC0001: {msg}";
        }

        private enum CapturedType
        {
            Node,

            Diagnostic
        }
    }
}

フォーマットとしては FilePath(Line, Column): [Error|Warning]: CODE0000: Error Message ですね。
Visual Studio 2010 時代の MSDN 記事しか見つかりませんでしたが、これで良い感じに動いたりします。
(Line, Column) 部はオプションだったりしますが、設定するとコードジャンプも効くのでコードを解析して~ってタイプの場合は、設定しておくのを推奨します。
ということで、ではではー