/// <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) { var builder = new StringBuilder(); for (var i = 0; i < diagnostics.Length; ++i) { builder.AppendLine($"// {diagnostics[i]}"); var analyzerType = analyzer?.GetType(); if (analyzerType == null) { continue; } var rules = analyzer !.SupportedDiagnostics; foreach (var rule in rules) { if (rule == null || rule.Id != diagnostics[i].Id) { continue; } var 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"); var fileIsCSharp = diagnostics[i].Location.SourceTree?.FilePath.EndsWith(".cs") ?? false; var resultMethodName = fileIsCSharp ? "GetCSharpResultAt" : "GetBasicResultAt"; var 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()); }
public static void LogAnalyzerCrashCount(DiagnosticAnalyzer analyzer, Exception ex, LogAggregator logAggregator, ProjectId projectId) { if (logAggregator == null || analyzer == null || ex == null || ex is OperationCanceledException) { return; } // TODO: once we create description manager, pass that into here. bool telemetry = DiagnosticAnalyzerLogger.AllowsTelemetry(null, analyzer, projectId); var tuple = ValueTuple.Create(telemetry, analyzer.GetType(), ex.GetType()); logAggregator.IncreaseCount(tuple); }
public void UpdateAnalyzerTypeCount(DiagnosticAnalyzer analyzer, AnalyzerTelemetryInfo analyzerTelemetryInfo) { var isTelemetryAllowed = DiagnosticAnalyzerLogger.AllowsTelemetry(analyzer, _analyzerService); ImmutableInterlocked.AddOrUpdate( ref _analyzerInfoMap, analyzer.GetType(), addValue: new AnalyzerInfo(analyzer, analyzerTelemetryInfo, isTelemetryAllowed), updateValueFactory: (k, ai) => { ai.SetAnalyzerTypeCount(analyzerTelemetryInfo); return(ai); }); }
public AnalyzerInfo(DiagnosticAnalyzer analyzer, AnalyzerActions analyzerActions, bool telemetry) { CLRType = analyzer.GetType(); Telemetry = telemetry; Counts[0] = analyzerActions.CodeBlockEndActionsCount; Counts[1] = analyzerActions.CodeBlockStartActionsCount; Counts[2] = analyzerActions.CompilationEndActionsCount; Counts[3] = analyzerActions.CompilationStartActionsCount; Counts[4] = analyzerActions.SemanticModelActionsCount; Counts[5] = analyzerActions.SymbolActionsCount; Counts[6] = analyzerActions.SyntaxNodeActionsCount; Counts[7] = analyzerActions.SyntaxTreeActionsCount; }
public AnalyzerInfo(DiagnosticAnalyzer analyzer, AnalyzerActions analyzerActions, bool telemetry) { CLRType = analyzer.GetType(); Telemetry = telemetry; Counts[0] = analyzerActions.CodeBlockEndActionsCount; Counts[1] = analyzerActions.CodeBlockStartActionsCount; Counts[2] = analyzerActions.CompilationEndActionsCount; Counts[3] = analyzerActions.CompilationStartActionsCount; Counts[4] = analyzerActions.SemanticModelActionsCount; Counts[5] = analyzerActions.SymbolActionsCount; Counts[6] = analyzerActions.SyntaxNodeActionsCount; Counts[7] = analyzerActions.SyntaxTreeActionsCount; }
public StateSet(string language, DiagnosticAnalyzer analyzer, string errorSourceName) { _language = language; _analyzer = analyzer; _errorSourceName = errorSourceName; var(analyzerId, version) = _analyzer.GetAnalyzerIdAndVersion(); _analyzerVersion = version; _analyzerTypeData = AnalyzerTypeData.ForType(_analyzer.GetType()); Debug.Assert(_analyzerTypeData.StateName == $"{AnalyzerTypeData.UserDiagnosticsPrefixTableName}_{analyzerId}", "Expected persistence information for analyzer instance to be derived from its type alone."); _activeFileStates = new ConcurrentDictionary <DocumentId, ActiveFileState>(concurrencyLevel: 2, capacity: 10); _projectStates = new ConcurrentDictionary <ProjectId, ProjectState>(concurrencyLevel: 2, capacity: 1); }
public void UpdateAnalyzerTypeCount(DiagnosticAnalyzer analyzer, ActionCounts analyzerActions, Project projectOpt) { var telemetry = DiagnosticAnalyzerLogger.AllowsTelemetry(_owner, analyzer, projectOpt?.Id); ImmutableInterlocked.AddOrUpdate( ref _analyzerInfoMap, analyzer.GetType(), addValue: new AnalyzerInfo(analyzer, analyzerActions, telemetry), updateValueFactory: (k, ai) => { ai.SetAnalyzerTypeCount(analyzerActions); return(ai); }); }
public DescriptorInfo(DiagnosticAnalyzer analyzer) { this.DiagnosticAnalyzer = analyzer; this.DocFileName = Path.Combine(DocumentsDirectory, this.DiagnosticDescriptor.Id + ".md"); this.CodeFileName = Directory.EnumerateFiles( SolutionDirectory, analyzer.GetType().Name + ".cs", SearchOption.AllDirectories) .FirstOrDefault(); this.CodeFileUri = this.CodeFileName != null ? @"https://github.com/DotNetAnalyzers/WpfAnalyzers/blob/master/" + this.CodeFileName.Substring(SolutionDirectory.Length).Replace("\\", "/") : "missing"; }
public void UpdateAnalyzerTypeCount(DiagnosticAnalyzer analyzer, ActionCounts analyzerActions, Project projectOpt) { var telemetry = DiagnosticAnalyzerLogger.AllowsTelemetry(_owner, analyzer, projectOpt?.Id); ImmutableInterlocked.AddOrUpdate( ref _analyzerInfoMap, analyzer.GetType(), addValue: new AnalyzerInfo(analyzer, analyzerActions, telemetry), updateValueFactory: (k, ai) => { ai.SetAnalyzerTypeCount(analyzerActions); return ai; }); }
public static void SetParameterValues(DiagnosticAnalyzer parameteredAnalyzer, AnalyzerOptions options) { if (ProcessedAnalyzers.ContainsKey(parameteredAnalyzer)) { return; } var additionalFile = options.AdditionalFiles .FirstOrDefault(f => ConfigurationFilePathMatchesExpected(f.Path)); if (additionalFile == null) { return; } var filePath = additionalFile.Path; var xml = XDocument.Load(filePath); var parameters = ParseParameters(xml); var propertyParameterPairs = parameteredAnalyzer.GetType() .GetProperties() .Select(p => new { Property = p, Descriptor = p.GetCustomAttributes <RuleParameterAttribute>().SingleOrDefault() }) .Where(p => p.Descriptor != null); foreach (var propertyParameterPair in propertyParameterPairs) { var parameter = parameters .FirstOrDefault(p => p.RuleId == parameteredAnalyzer.SupportedDiagnostics.Single().Id); if (parameter == null) { return; } var parameterValue = parameter.ParameterValues .FirstOrDefault(pv => pv.ParameterKey == propertyParameterPair.Descriptor.Key); if (parameterValue == null) { return; } var value = parameterValue.ParameterValue; var convertedValue = ChangeParameterType(value, propertyParameterPair.Descriptor.Type); propertyParameterPair.Property.SetValue(parameteredAnalyzer, convertedValue); } ProcessedAnalyzers.AddOrUpdate(parameteredAnalyzer, 0, (a, b) => b); }
/// <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> static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics) { var builder = new StringBuilder(); for (var i = 0; i < diagnostics.Length; ++i) { builder.AppendLine("// " + diagnostics[i]); var analyzerType = analyzer.GetType(); var rules = analyzer.SupportedDiagnostics; foreach (var rule in rules) { if ((rule != null) && (rule.Id == diagnostics[i].Id)) { var location = diagnostics[i].Location; if (location == Location.None) { builder.AppendFormat(CultureInfo.InvariantCulture, "GetGlobalResult({0}.{1})", analyzerType.Name, rule.Id); } else { Assert.That(location.IsInSource, Is.True, $"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[i]}\r\n"); var resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) ? "GetCSharpResultAt" : "GetBasicResultAt"; var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition; builder.AppendFormat( CultureInfo.InvariantCulture, "{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> /// 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 (var i = 0; i < diagnostics.Length; ++i) { builder.AppendLine("// " + diagnostics[i]); Type analyzerType = analyzer.GetType(); 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.IsTrue(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"; 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> /// 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) { var 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(CultureInfo.InvariantCulture, "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:\r\n" + diagnostics[i]); string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) ? "GetCSharpResultAt" : "GetBasicResultAt"; Microsoft.CodeAnalysis.Text.LinePosition linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition; builder.AppendFormat(CultureInfo.InvariantCulture, "{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()); }
public static bool IsCompilerAnalyzer(DiagnosticAnalyzer analyzer) { // TODO: find better way. var typeString = analyzer.GetType().ToString(); if (typeString == CSharpCompilerAnalyzerTypeName) { return true; } if (typeString == VisualBasicCompilerAnalyzerTypeName) { return true; } return false; }
static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics) { var builder = new StringBuilder(); for (var diagnosticIndex = 0; diagnosticIndex < diagnostics.Length; ++diagnosticIndex) { builder.AppendLine("// " + diagnostics[diagnosticIndex]); var analyzerType = analyzer.GetType(); var descriptors = analyzer.SupportedDiagnostics; foreach (var descriptor in descriptors .Where(descriptor => descriptor?.Id == diagnostics[diagnosticIndex].Id)) { var location = diagnostics[diagnosticIndex].Location; if (location == Location.None) { builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, descriptor.Id); } else { Assert.IsTrue( location.IsInSource, $"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[diagnosticIndex]}\r\n"); var position = diagnostics[diagnosticIndex].Location.GetLineSpan().StartLinePosition; builder.AppendFormat("{0}({1}, {2}, {3}.{4})", "GetResultAt", position.Line + 1, position.Character + 1, analyzerType.Name, descriptor.Id); } if (diagnosticIndex != 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) { var builder = new StringBuilder(); for (int i = 0; i < diagnostics.Length; ++i) { builder.AppendLine("// " + diagnostics[i].ToString()); var analyzerType = analyzer.GetType(); var rules = analyzer.SupportedDiagnostics; foreach (var rule in rules) { if (rule != null && rule.Id == diagnostics[i].Id) { var location = diagnostics[i].Location; if (location == Location.None) { builder.Append(FormattableString.Invariant($"GetGlobalResult({analyzerType.Name}.{rule.Id})")); } else { location.IsInSource.Should() .BeTrue(FormattableString.Invariant( $"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", StringComparison.OrdinalIgnoreCase) ? "GetCSharpResultAt" : "GetBasicResultAt"; var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition; builder.Append( FormattableString.Invariant( $"{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> /// 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.Append("// ").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 { Check.That(location.IsInSource).IsTrue(); 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()); }
public static bool IsCompilerAnalyzer(DiagnosticAnalyzer analyzer) { // TODO: find better way. var typeString = analyzer.GetType().ToString(); if (typeString == CSharpCompilerAnalyzerTypeName) { return(true); } if (typeString == VisualBasicCompilerAnalyzerTypeName) { return(true); } return(false); }
/// <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(); foreach (Diagnostic diagnostic in diagnostics) { builder = builder.Append("// ") .AppendLine(diagnostic.ToString()); Type analyzerType = analyzer.GetType(); ImmutableArray <DiagnosticDescriptor> rules = analyzer.SupportedDiagnostics; DiagnosticDescriptor?rule = rules.FirstOrDefault(rule => rule.Id == diagnostic.Id); if (rule == null) { continue; } Location location = diagnostic.Location; if (location == Location.None) { builder = builder.Append(provider: CultureInfo.InvariantCulture, $"GetGlobalResult({analyzerType.Name}.{rule.Id})"); } else { Assert.True(condition: location.IsInSource, $"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostic}\r\n"); string resultMethodName = GetResultMethodName(diagnostic); LinePosition linePosition = diagnostic.Location.GetLineSpan() .StartLinePosition; builder = builder.Append(provider: CultureInfo.InvariantCulture, $"{resultMethodName}({linePosition.Line + 1}, {linePosition.Character + 1}, {analyzerType.Name}.{rule.Id})"); } builder = builder.Append(value: ',') .AppendLine(); } return(builder.ToString() .TrimEnd() .TrimEnd(',') + Environment.NewLine); }
private static string GetTestCaseFileName(DiagnosticAnalyzer analyzer) { var typeName = analyzer.GetType().Name; return(typeName switch { "ConfiguringLoggers" => "ConfiguringLoggers_AspNetCore", "CookieShouldBeHttpOnly" => "CookieShouldBeHttpOnly_Nancy", "CookieShouldBeSecure" => "CookiesShouldBeSecure_Nancy", "DoNotHardcodeCredentials" => "DoNotHardcodeCredentials_DefaultValues", #if NETFRAMEWORK "ExecutingSqlQueries" => "ExecutingSqlQueries_Net46", "UsingCookies" => "UsingCookies_Net46", #else "ExecutingSqlQueries" => "ExecutingSqlQueries_NetCore", "UsingCookies" => "UsingCookies_NetCore", #endif _ => typeName });
private static void VerifyDiagnosticResults(IOrderedEnumerable <Diagnostic> actualResults, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expectedResults) { var analyzerName = analyzer.GetType().Name; var actualResultsArray = actualResults.ToArray(); VerifyCorrectNumberOfDiagnostics(analyzerName, actualResultsArray, expectedResults); for (var i = 0; i < expectedResults.Length; i++) { var actual = actualResultsArray[i]; var expected = expectedResults[i]; var diagnosticNum = i + 1; VerifyLocationOfDiagnostic(analyzerName, diagnosticNum, expected, actual); VerifyCategoryOfDiagnostic(analyzerName, diagnosticNum, expected, actual); VerifyIdOfDiagnostic(analyzerName, diagnosticNum, expected, actual); VerifyTitleOfDiagnostic(analyzerName, diagnosticNum, expected, actual); VerifyDescriptionOfDiagnostic(analyzerName, diagnosticNum, expected, actual); VerifyMessageOfDiagnostic(analyzerName, diagnosticNum, expected, actual); VerifySeverityOfDiagnostic(analyzerName, diagnosticNum, expected, actual); } }
internal void VerifySupportedDiagnostics(DiagnosticAnalyzer analyzer, Diagnostic diagnostic) { if (analyzer.SupportedDiagnostics.IndexOf(diagnostic.Descriptor, DiagnosticDescriptorComparer.Id) == -1) { Assert.True(false, $"Diagnostic \"{diagnostic.Id}\" is not supported by '{analyzer.GetType().Name}'."); } }
/// <summary> /// Get the unique state name for the given {type, provider} tuple. /// Note that this name is used by the underlying persistence stream of the corresponding <see cref="DiagnosticState"/> to Read/Write diagnostic data into the stream. /// If any two distinct {type, provider} tuples have the same diagnostic state name, we will end up sharing the persistence stream between them, leading to duplicate/missing/incorrect diagnostic data. /// </summary> private static ValueTuple<string, VersionStamp> GetUniqueDiagnosticStateNameAndVersion(StateType type, ProviderId providerId, DiagnosticAnalyzer provider) { Contract.ThrowIfNull(provider); // Get the unique ID for given diagnostic analyzer. // note that we also put version stamp so that we can detect changed provider var providerType = provider.GetType(); var location = providerType.Assembly.Location; return ValueTuple.Create(UserDiagnosticsPrefixTableName + "_" + type.ToString() + "_" + providerType.AssemblyQualifiedName, GetProviderVersion(location)); }
private async Task GetCompilationDiagnosticsAsync(DiagnosticAnalyzer analyzer, List <Diagnostic> diagnostics, Action <Project, DiagnosticAnalyzer, CancellationToken> forceAnalyzeAllDocuments) { Contract.ThrowIfFalse(_project.SupportsCompilation); using (var pooledObject = SharedPools.Default <List <Diagnostic> >().GetPooledObject()) { var localDiagnostics = pooledObject.Object; var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false); DiagnosticAnalyzer clonedAnalyzer = null; try { // Get all the analyzer actions, including the per-compilation actions. var analyzerExecutor = GetAnalyzerExecutor(compilation, localDiagnostics.Add); var analyzerActions = await this.GetAnalyzerActionsAsync(analyzer, analyzerExecutor).ConfigureAwait(false); var hasDependentCompilationEndAction = await AnalyzerManager.Instance.GetAnalyzerHasDependentCompilationEndAsync(analyzer, analyzerExecutor).ConfigureAwait(false); if (hasDependentCompilationEndAction && forceAnalyzeAllDocuments != null) { // Analyzer registered a compilation end action and at least one other analyzer action during its compilation start action. // We need to ensure that we have force analyzed all documents in this project for this analyzer before executing the end actions. // Doing so on the original analyzer instance might cause duplicate callbacks into the analyzer. // So we create a new instance of this analyzer and compute compilation diagnostics on that instance. try { clonedAnalyzer = Activator.CreateInstance(analyzer.GetType()) as DiagnosticAnalyzer; } catch { // Unable to created a new analyzer instance, bail out on reporting diagnostics. return; } // Report analyzer exception diagnostics on original analyzer, not the temporary cloned one. analyzerExecutor = GetAnalyzerExecutorForClone(compilation, localDiagnostics.Add, analyzerForExceptionDiagnostics: analyzer); analyzerActions = await this.GetAnalyzerActionsAsync(clonedAnalyzer, analyzerExecutor).ConfigureAwait(false); forceAnalyzeAllDocuments(_project, clonedAnalyzer, _cancellationToken); } // Compilation actions. analyzerExecutor.ExecuteCompilationActions(analyzerActions.CompilationActions); // CompilationEnd actions. analyzerExecutor.ExecuteCompilationActions(analyzerActions.CompilationEndActions); var filteredDiagnostics = CompilationWithAnalyzers.GetEffectiveDiagnostics(localDiagnostics, compilation); diagnostics.AddRange(filteredDiagnostics); } finally { if (clonedAnalyzer != null) { AnalyzerManager.Instance.ClearAnalyzerState(ImmutableArray.Create(clonedAnalyzer)); } } } }
public static void VerifyNoDiagnostic( IEnumerable <string> sources, DiagnosticDescriptor descriptor, DiagnosticAnalyzer analyzer, string language) { Assert.True(analyzer.Supports(descriptor), $"Diagnostic \"{descriptor.Id}\" is not supported by analyzer \"{analyzer.GetType().Name}\"."); IEnumerable <Document> documents = WorkspaceFactory.CreateDocuments(sources, language); VerifyNoCompilerError(documents); Diagnostic[] diagnostics = DiagnosticUtility.GetSortedDiagnostics(documents, analyzer); Assert.True(diagnostics.Length == 0 || diagnostics.All(f => !string.Equals(f.Id, descriptor.Id, StringComparison.Ordinal)), $"No diagnostic expected{diagnostics.Where(f => string.Equals(f.Id, descriptor.Id, StringComparison.Ordinal)).ToDebugString()}"); }
/// <summary> /// The caller is free to provide his own <see cref="FormatDiagnosticsCallback"/>, /// however, most likely, the default formatter is sufficient for eighty to ninety /// percent of what you want to do. /// </summary> /// <param name="language"></param> /// <param name="analyzer"></param> /// <param name="diagnostics"></param> /// <returns></returns> internal static string DefaultDiagnosticFormatter(string language , DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics) { string FormatDiagnostic(Diagnostic diagnostic) { var sb = new StringBuilder(); sb.AppendLine($"// {diagnostic}"); var analyzerType = analyzer.GetType(); var rules = analyzer.SupportedDiagnostics; // TODO: TBD: is this just a fancy way of selecting the "single" rule corresponding to the Diagnostic Id? foreach (var rule in from r in rules where r != null && r.Id == diagnostic.Id select r) { var location = diagnostic.Location; string message; if (location == Location.None) { message = $"Global '{language}' result '{analyzerType.FullName}.{rule.Id}'"; } else { // Positively assert when Location IsInMetadata, as contrasted with !IsInSource. if (location.IsInMetadata) { throw new BadFormatterLocationException( "Verifiers do not currently handle diagnostics" + $" in metadata locations, diagnostic in metadata: {diagnostic}" , BadLocation, diagnostic); } //try //{ // Assert.True(location.IsInSource); //} //catch (TrueException) //{ // OutputHelper.WriteLine( // "Test base does not currently handle diagnostics in metadata locations." // + $"Diagnostic in metadata: {diagnostic}"); // throw; //} var linePosition = diagnostic.Location.GetLineSpan().StartLinePosition; message = $"'{language}' result at " + $"line {linePosition.Line + 1} " + $"column {linePosition.Character + 1}" + $", rule '{analyzerType.FullName}.{rule.Id}')"; } // TODO: TBD: we'll have to watch this, but I suspect we are formatting only a SINGLE rule/diagnostic pair here... sb.AppendLine(message); } return($"{sb}"); } return(Join(",\r\n", diagnostics.Select(FormatDiagnostic))); }
/// <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="atmosphere"> /// The compilation environment. /// </param> /// <param name="diagnostics"> /// The Diagnostics to be formatted. /// </param> /// <returns> /// The Diagnostics formatted as a string. /// </returns> private static string FormatDiagnostics( DiagnosticAnalyzer analyzer, Atmosphere atmosphere, params Diagnostic[] diagnostics) { var builder = new StringBuilder(); for (var i = 0; i < diagnostics.Length; ++i) { builder.Append("// ") .AppendLine(diagnostics[i].ToString()); var analyzerType = analyzer.GetType(); var rule = analyzer.SupportedDiagnostics .FirstOrDefault(r => !(r is null) && r.Id == diagnostics[i].Id); if (rule is null) { continue; } var location = diagnostics[i].Location; if (location == Location.None) { builder.AppendFormat( CultureInfo.CurrentCulture, "GetGlobalResult({0}.{1})", analyzerType.Name, rule.Id); } else if (atmosphere.ForceLocationValid) { var linePosition = diagnostics[i].Location .GetLineSpan() .StartLinePosition; builder.AppendFormat( CultureInfo.CurrentCulture, "GetExternalResult({0}, {1}, {2}.{3})", linePosition.Line + 1, linePosition.Character + 1, analyzerType.Name, rule.Id); } else { AssertFailIfFalse( location.IsInSource, () => "Test base does not currently handle " + "diagnostics in metadata locations. " + "Diagnostic in metadata: " + $"{diagnostics[i]}{NewLine}"); var sourceTree = diagnostics[i].Location .SourceTree; if (sourceTree is null) { throw new NullReferenceException(); } var filePath = sourceTree.FilePath; AssertFailIfFalse( filePath.EndsWith(".cs", StringComparison.Ordinal), () => "The file path does not end '.cs': " + $"{filePath}"); var resultMethodName = "GetCSharpResultAt"; var linePosition = diagnostics[i].Location .GetLineSpan() .StartLinePosition; builder.AppendFormat( CultureInfo.CurrentCulture, "{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(); } return(builder.ToString()); }
public AnalyzerInfo(DiagnosticAnalyzer analyzer, AnalyzerTelemetryInfo analyzerTelemetryInfo, bool telemetry) { CLRType = analyzer.GetType(); Telemetry = telemetry; SetAnalyzerTypeCount(analyzerTelemetryInfo); }
private async Task GetCompilationDiagnosticsAsync(DiagnosticAnalyzer analyzer, List<Diagnostic> diagnostics, Action<Project, DiagnosticAnalyzer, CancellationToken> forceAnalyzeAllDocuments) { Contract.ThrowIfFalse(_project.SupportsCompilation); using (var pooledObject = SharedPools.Default<List<Diagnostic>>().GetPooledObject()) { var localDiagnostics = pooledObject.Object; var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false); DiagnosticAnalyzer clonedAnalyzer = null; try { // Get all the analyzer actions, including the per-compilation actions. var analyzerExecutor = GetAnalyzerExecutor(compilation, localDiagnostics.Add); var analyzerActions = await this.GetAnalyzerActionsAsync(analyzer, analyzerExecutor).ConfigureAwait(false); var hasDependentCompilationEndAction = await AnalyzerManager.Instance.GetAnalyzerHasDependentCompilationEndAsync(analyzer, analyzerExecutor).ConfigureAwait(false); if (hasDependentCompilationEndAction && forceAnalyzeAllDocuments != null) { // Analyzer registered a compilation end action and at least one other analyzer action during its compilation start action. // We need to ensure that we have force analyzed all documents in this project for this analyzer before executing the end actions. // Doing so on the original analyzer instance might cause duplicate callbacks into the analyzer. // So we create a new instance of this analyzer and compute compilation diagnostics on that instance. try { clonedAnalyzer = Activator.CreateInstance(analyzer.GetType()) as DiagnosticAnalyzer; } catch { // Unable to created a new analyzer instance, bail out on reporting diagnostics. return; } // Report analyzer exception diagnostics on original analyzer, not the temporary cloned one. analyzerExecutor = GetAnalyzerExecutorForClone(compilation, localDiagnostics.Add, analyzerForExceptionDiagnostics: analyzer); analyzerActions = await this.GetAnalyzerActionsAsync(clonedAnalyzer, analyzerExecutor).ConfigureAwait(false); forceAnalyzeAllDocuments(_project, clonedAnalyzer, _cancellationToken); } // Compilation actions. analyzerExecutor.ExecuteCompilationActions(analyzerActions.CompilationActions); // CompilationEnd actions. analyzerExecutor.ExecuteCompilationActions(analyzerActions.CompilationEndActions); var filteredDiagnostics = CompilationWithAnalyzers.GetEffectiveDiagnostics(localDiagnostics, compilation); diagnostics.AddRange(filteredDiagnostics); } finally { if (clonedAnalyzer != null) { AnalyzerManager.Instance.ClearAnalyzerState(ImmutableArray.Create(clonedAnalyzer)); } } } }
public AnalyzerInfo(DiagnosticAnalyzer analyzer, AnalyzerTelemetryInfo analyzerTelemetryInfo, bool telemetry) { CLRType = analyzer.GetType(); Telemetry = telemetry; SetAnalyzerTypeCount(analyzerTelemetryInfo); }
public static void VerifyDiagnostic( IEnumerable <string> sources, DiagnosticAnalyzer analyzer, string language, params Diagnostic[] expectedDiagnostics) { foreach (Diagnostic diagnostic in expectedDiagnostics) { Assert.True(analyzer.Supports(diagnostic.Descriptor), $"Diagnostic \"{diagnostic.Descriptor.Id}\" is not supported by analyzer \"{analyzer.GetType().Name}\"."); } Diagnostic[] diagnostics = DiagnosticUtility.GetSortedDiagnostics(sources, analyzer, language); if (diagnostics.Length > 0 && analyzer.SupportedDiagnostics.Length > 1) { diagnostics = diagnostics .Where(diagnostic => expectedDiagnostics.Any(expectedDiagnostic => DiagnosticComparer.Id.Equals(diagnostic, expectedDiagnostic))) .ToArray(); } VerifyDiagnostics(diagnostics, expectedDiagnostics); }
private static string CreateStub(DiagnosticAnalyzer analyzer, DiagnosticDescriptor descriptor) { var builder = new StringBuilder(); builder.Append($"|{(builder.Length == 0 ? " Code " : " ")}| "); builder.Append($"[{analyzer.GetType().Name}]({CodeFile.Find(analyzer.GetType()).Uri})"); var text = builder.ToString(); var stub = $@"# {descriptor.Id} ## {EscapeTags(descriptor.Title)} | Topic | Value | :-- | :-- | Id | {descriptor.Id} | Severity | {descriptor.DefaultSeverity} | Enabled | {(descriptor.IsEnabledByDefault ? "True" : "False")} | Category | {descriptor.Category} | Code | [<TYPENAME>](<URL>) ## Description {EscapeTags(descriptor.Description)} ## Motivation ADD MOTIVATION HERE ## How to fix violations ADD HOW TO FIX VIOLATIONS HERE <!-- start generated config severity --> ## Configure severity ### Via ruleset file Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). ### Via .editorconfig file ```ini # {descriptor.Id}: {descriptor.Title.ToString(CultureInfo.InvariantCulture)} dotnet_diagnostic.{descriptor.Id}.severity = chosenSeverity ``` where `chosenSeverity` can be one of `none`, `silent`, `suggestion`, `warning`, or `error`. ### Via #pragma directive ```csharp #pragma warning disable {descriptor.Id} // {descriptor.Title.ToString(CultureInfo.InvariantCulture)} Code violating the rule here #pragma warning restore {descriptor.Id} // {descriptor.Title.ToString(CultureInfo.InvariantCulture)} ``` Or put this at the top of the file to disable all instances. ```csharp #pragma warning disable {descriptor.Id} // {descriptor.Title.ToString(CultureInfo.InvariantCulture)} ``` ### Via attribute `[SuppressMessage]` ```csharp [System.Diagnostics.CodeAnalysis.SuppressMessage(""{descriptor.Category}"", ""{descriptor.Id}:{descriptor.Title.ToString(CultureInfo.InvariantCulture)}"", Justification = ""Reason..."")] ``` <!-- end generated config severity --> "; return(Replace(stub, "| Code | [<TYPENAME>](<URL>)", text)); }
public void AnalyzersBenchmark(DiagnosticAnalyzer analyzer) { var id = analyzer.SupportedDiagnostics.Single().Id; var expectedName = id + (id.Contains("_") ? "_" : string.Empty) + "Benchmarks"; var fileName = Path.Combine(Program.BenchmarksDirectory, expectedName + ".cs"); var code = new StringBuilder().AppendLine($"namespace {this.GetType().Namespace}") .AppendLine("{") .AppendLine($" public class {expectedName}") .AppendLine(" {") .AppendLine($" private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.AnalyzersProject, new {analyzer.GetType().FullName}());") .AppendLine() .AppendLine(" [BenchmarkDotNet.Attributes.Benchmark]") .AppendLine(" public void RunOnIDisposableAnalyzers()") .AppendLine(" {") .AppendLine(" Benchmark.Run();") .AppendLine(" }") .AppendLine(" }") .AppendLine("}") .ToString(); if (!File.Exists(fileName) || File.ReadAllText(fileName) != code) { File.WriteAllText(fileName, code); Assert.Fail(); } }