/// <summary> /// Helper method to VerifyDiagnosticResult that checks the location of a diagnostic and compares it with the location in the expected DiagnosticResult. /// </summary> /// <param name="analyzer">The analyzer that was being run on the sources</param> /// <param name="diagnostic">The diagnostic that was found in the code</param> /// <param name="actual">The Location of the Diagnostic found in the code</param> /// <param name="expected">The DiagnosticResultLocation that should have been found</param> private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected) { FileLinePositionSpan actualSpan = actual.GetLineSpan(); Assert.True(actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")), string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic))); Microsoft.CodeAnalysis.Text.LinePosition actualLinePosition = actualSpan.StartLinePosition; // Only check line position if there is an actual line in the real diagnostic if (actualLinePosition.Line > 0) { if (actualLinePosition.Line + 1 != expected.Line) { Assert.True(false, string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Line, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic))); } } // Only check column position if there is an actual column position in the real diagnostic if (actualLinePosition.Character > 0) { if (actualLinePosition.Character + 1 != expected.Column) { Assert.True(false, string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Column, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic))); } } }
/// <param name="isAssertEnabled">Indicates that unit test assertions are enabled for non-matches.</param> /// <returns>True if actual matches expected, false otherwise.</returns> private static bool VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticLocation expected, bool isAssertEnabled) { FileLinePositionSpan actualSpan = actual.GetLineSpan(); if (isAssertEnabled) { Assert.True(actualSpan.Path == expected.Span.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Span.Path.Contains("Test.")), string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Span.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic))); } else if (!(actualSpan.Path == expected.Span.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Span.Path.Contains("Test.")))) { return(false); } Microsoft.CodeAnalysis.Text.LinePosition actualLinePosition = actualSpan.StartLinePosition; // Only check line position if there is an actual line in the real diagnostic if (expected.Span.StartLinePosition.Line > 0) { if (actualLinePosition.Line != expected.Span.StartLinePosition.Line) { if (isAssertEnabled) { Assert.True(false, string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Span.StartLinePosition.Line + 1, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic))); } else { return(false); } } } // Only check column position if there is an actual column position in the real diagnostic if (expected.Span.StartLinePosition.Character > 0) { if (actualLinePosition.Character != expected.Span.StartLinePosition.Character) { if (isAssertEnabled) { Assert.True(false, string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Span.StartLinePosition.Character + 1, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic))); } else { return(false); } } } // Matches. return(true); }
private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics) { var builder = new StringBuilder(); for (int i = 0; i < diagnostics.Length; ++i) { builder.AppendLine("// " + diagnostics[i].ToString()); Type analyzerType = analyzer.GetType(); IEnumerable <FieldInfo> ruleFields = analyzerType .GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy) .Where(f => f.IsStatic && f.FieldType == typeof(DiagnosticDescriptor)); foreach (FieldInfo field in ruleFields) { var rule = field.GetValue(null) as DiagnosticDescriptor; if (rule != null && rule.Id == diagnostics[i].Id) { Location location = diagnostics[i].Location; if (location == Location.None) { builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, field.Name); } else { Assert.False(location.IsInMetadata, "Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata:\r\n" + diagnostics[i]); string resultMethodName = GetResultMethodName(diagnostics[i]); Microsoft.CodeAnalysis.Text.LinePosition linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition; builder.AppendFormat("{0}({1}, {2}, {3}.{4})", resultMethodName, linePosition.Line + 1, linePosition.Character + 1, field.DeclaringType.Name, field.Name); } if (i != diagnostics.Length - 1) { builder.Append(','); } builder.AppendLine(); break; } } } return(builder.ToString()); }
/// <summary> /// Helper method to format a Diagnostic into an easily readable string /// </summary> /// <param name="analyzer">The analyzer that this verifier tests</param> /// <param name="diagnostics">The Diagnostics to be formatted</param> /// <returns>The Diagnostics formatted as a string</returns> private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < diagnostics.Length; ++i) { builder.AppendLine("// " + diagnostics[i].ToString()); System.Type analyzerType = analyzer.GetType(); System.Collections.Immutable.ImmutableArray <DiagnosticDescriptor> rules = analyzer.SupportedDiagnostics; foreach (DiagnosticDescriptor rule in rules) { if (rule != null && rule.Id == diagnostics[i].Id) { Location location = diagnostics[i].Location; if (location == Location.None) { builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, rule.Id); } else { Assert.True( location.IsInSource, $"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[i]}\r\n"); string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt"; Microsoft.CodeAnalysis.Text.LinePosition linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition; builder.AppendFormat( "{0}({1}, {2}, {3}.{4})", resultMethodName, linePosition.Line + 1, linePosition.Character + 1, analyzerType.Name, rule.Id); } if (i != diagnostics.Length - 1) { builder.Append(','); } builder.AppendLine(); break; } } } return(builder.ToString()); }
/// <summary> /// Adds error message with a link that opens the C# file and goes to the specified position. /// </summary> /// <param name="pos">Position in code.</param> /// <param name="message">Text to append. Example: "error: message".</param> /// <param name="formatArgs">If not null/empty, calls StringBuilder.AppendFormat(message, formatArgs).</param> public void AddError(FileNode f, Microsoft.CodeAnalysis.Text.LinePosition pos, string message, params object[] formatArgs) { _StartAdd(); _Append(f, pos.Line, pos.Character, message, formatArgs); }
public LinePosition(Microsoft.CodeAnalysis.Text.LinePosition source) { this.Line = source.Line; this.Character = source.Character; }