| File: DiagnosticAnalyzer\AnalysisContextInfo.cs | Web Access |
| Project: src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Diagnostics; using System.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { /// <summary> /// this hold onto analyzer executor context which will be used later to put context information in analyzer exception if it occurs. /// </summary> internal readonly struct AnalysisContextInfo { private readonly Compilation? _compilation; private readonly IOperation? _operation; private readonly ISymbol? _symbol; private readonly SourceOrAdditionalFile? _file; private readonly SyntaxNode? _node; public AnalysisContextInfo(Compilation compilation) : this(compilation: compilation, operation: null, symbol: null, file: null, node: null) { } public AnalysisContextInfo(SemanticModel model) : this(model.Compilation, new SourceOrAdditionalFile(model.SyntaxTree)) { } public AnalysisContextInfo(Compilation compilation, ISymbol symbol) : this(compilation: compilation, operation: null, symbol: symbol, file: null, node: null) { } public AnalysisContextInfo(Compilation compilation, SourceOrAdditionalFile file) : this(compilation: compilation, operation: null, symbol: null, file: file, node: null) { } public AnalysisContextInfo(Compilation compilation, SyntaxNode node) : this(compilation: compilation, operation: null, symbol: null, file: new SourceOrAdditionalFile(node.SyntaxTree), node) { } public AnalysisContextInfo(Compilation compilation, IOperation operation) : this(compilation: compilation, operation: operation, symbol: null, file: new SourceOrAdditionalFile(operation.Syntax.SyntaxTree), node: operation.Syntax) { } public AnalysisContextInfo(Compilation compilation, ISymbol symbol, SyntaxNode node) : this(compilation: compilation, operation: null, symbol: symbol, file: new SourceOrAdditionalFile(node.SyntaxTree), node) { } private AnalysisContextInfo( Compilation? compilation, IOperation? operation, ISymbol? symbol, SourceOrAdditionalFile? file, SyntaxNode? node) { Debug.Assert(node == null || file?.SourceTree != null); Debug.Assert(operation == null || file?.SourceTree != null); _compilation = compilation; _operation = operation; _symbol = symbol; _file = file; _node = node; } public string GetContext() { var sb = new StringBuilder(); if (_compilation?.AssemblyName != null) { sb.AppendLine($"{nameof(Compilation)}: {_compilation.AssemblyName}"); } if (_operation != null) { sb.AppendLine($"{nameof(IOperation)}: {_operation.Kind}"); } if (_symbol?.Name != null) { sb.AppendLine($"{nameof(ISymbol)}: {_symbol.Name} ({_symbol.Kind})"); } if (_file.HasValue) { if (_file.Value.SourceTree != null) { sb.AppendLine($"{nameof(SyntaxTree)}: {_file.Value.SourceTree.FilePath}"); } else { RoslynDebug.Assert(_file.Value.AdditionalFile != null); sb.AppendLine($"{nameof(AdditionalText)}: {_file.Value.AdditionalFile.Path}"); } } if (_node != null) { RoslynDebug.Assert(_file.HasValue); RoslynDebug.Assert(_file.Value.SourceTree != null); var text = _file.Value.SourceTree.GetText(); var lineSpan = text?.Lines?.GetLinePositionSpan(_node.Span); // can't use Kind since that is language specific. instead will output typename. sb.AppendLine($"{nameof(SyntaxNode)}: {GetFlattenedNodeText(_node)} [{_node.GetType().Name}]@{_node.Span} {(lineSpan.HasValue ? lineSpan.Value.ToString() : string.Empty)}"); } return sb.ToString(); } private string GetFlattenedNodeText(SyntaxNode node) { const int cutoff = 30; var lastEnd = node.Span.Start; var sb = new StringBuilder(); foreach (var token in node.DescendantTokens(descendIntoTrivia: false)) { if (token.Span.Start - lastEnd > 0) { sb.Append(' '); } sb.Append(token.ToString()); lastEnd = token.Span.End; if (sb.Length > cutoff) { break; } } return sb.ToString() + (sb.Length > cutoff ? " ..." : string.Empty); } } }