From fe19988d535f9f07c006256d82dcff104161959f Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 28 Dec 2023 13:43:54 -0600 Subject: [PATCH 01/12] Avoid using SyntaxReference.GetSyntaxAsync The implementation of this method is no longer asynchronous, so we use SyntaxReference.GetSyntax instead. --- .../CodeAnalysisMetricData.EventMetricData.cs | 4 ++-- .../CodeAnalysisMetricData.FieldMetricData.cs | 7 +++---- .../CodeAnalysisMetricData.MethodMetricData.cs | 7 +++---- ...odeAnalysisMetricData.NamedTypeMetricData.cs | 4 ++-- ...odeAnalysisMetricData.NamespaceMetricData.cs | 2 +- ...CodeAnalysisMetricData.PropertyMetricData.cs | 4 ++-- .../CodeMetrics/CodeAnalysisMetricData.cs | 4 ++-- .../Compiler/CodeMetrics/MetricsHelper.cs | 17 ++++++++--------- 8 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs index d699f17bf3..c83842ebd3 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs @@ -33,9 +33,9 @@ internal static async Task ComputeAsync(IEventSymbol @event, Co var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = @event.DeclaringSyntaxReferences; - long linesOfCode = await MetricsHelper.GetLinesOfCodeAsync(declarations, @event, context).ConfigureAwait(false); + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, @event, context); (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = - await MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDeclsAsync(declarations, @event, coupledTypesBuilder, context).ConfigureAwait(false); + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, @event, coupledTypesBuilder, context); MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, @event.Type); ImmutableArray children = await ComputeAsync(GetAccessors(@event), context).ConfigureAwait(false); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs index 6bfd2e4b96..a80f3cdb68 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using System.Threading.Tasks; using Analyzer.Utilities; namespace Microsoft.CodeAnalysis.CodeMetrics @@ -26,15 +25,15 @@ internal FieldMetricData( { } - internal static async Task ComputeAsync(IFieldSymbol field, CodeMetricsAnalysisContext context) + internal static FieldMetricData Compute(IFieldSymbol field, CodeMetricsAnalysisContext context) { var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = field.DeclaringSyntaxReferences; - long linesOfCode = await MetricsHelper.GetLinesOfCodeAsync(declarations, field, context).ConfigureAwait(false); + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, field, context); (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = - await MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDeclsAsync(declarations, field, coupledTypesBuilder, context).ConfigureAwait(false); + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, field, coupledTypesBuilder, context); MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, field.Type); int? depthOfInheritance = null; int maintainabilityIndex = CalculateMaintainabilityIndex(computationalComplexityMetrics, cyclomaticComplexity); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs index b89947a694..7c746e4276 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using System.Threading.Tasks; using Analyzer.Utilities; namespace Microsoft.CodeAnalysis.CodeMetrics @@ -26,15 +25,15 @@ internal MethodMetricData( { } - internal static async Task ComputeAsync(IMethodSymbol method, CodeMetricsAnalysisContext context) + internal static MethodMetricData Compute(IMethodSymbol method, CodeMetricsAnalysisContext context) { var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = method.DeclaringSyntaxReferences; - long linesOfCode = await MetricsHelper.GetLinesOfCodeAsync(declarations, method, context).ConfigureAwait(false); + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, method, context); (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = - await MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDeclsAsync(declarations, method, coupledTypesBuilder, context).ConfigureAwait(false); + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, method, coupledTypesBuilder, context); MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, method.Parameters); if (!method.ReturnsVoid) { diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs index 1626d41a12..f4d5f46f6a 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs @@ -35,7 +35,7 @@ internal static async Task ComputeAsync(INamedTypeSymbol na var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = namedType.DeclaringSyntaxReferences; (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = - await MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDeclsAsync(declarations, namedType, coupledTypesBuilder, context).ConfigureAwait(false); + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, namedType, coupledTypesBuilder, context); // Compat: Filter out nested types as they are children of most closest containing namespace. var members = namedType.GetMembers().Where(m => m.Kind != SymbolKind.NamedType); @@ -78,7 +78,7 @@ internal static async Task ComputeAsync(INamedTypeSymbol na } int depthOfInheritance = CalculateDepthOfInheritance(namedType, context.IsExcludedFromInheritanceCountFunc); - long linesOfCode = await MetricsHelper.GetLinesOfCodeAsync(declarations, namedType, context).ConfigureAwait(false); + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, namedType, context); int maintainabilityIndex = singleEffectiveChildMaintainabilityIndex != -1 ? singleEffectiveChildMaintainabilityIndex : CalculateMaintainabilityIndex(computationalComplexityMetrics, cyclomaticComplexity, effectiveChildrenCountForComplexity); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs index 3612da3b50..6e443f7ab6 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs @@ -55,7 +55,7 @@ internal static async Task ComputeAsync(INamespaceSymbol @n long linesOfCode = @namespace.IsImplicitlyDeclared ? childrenLinesOfCode : - await MetricsHelper.GetLinesOfCodeAsync(@namespace.DeclaringSyntaxReferences, @namespace, context).ConfigureAwait(false); + MetricsHelper.GetLinesOfCode(@namespace.DeclaringSyntaxReferences, @namespace, context); int maintainabilityIndex = !children.IsEmpty ? MetricsHelper.GetAverageRoundedMetricValue(maintainabilityIndexTotal, children.Length) : 100; return new NamespaceMetricData(@namespace, maintainabilityIndex, coupledTypesBuilder.ToImmutable(), linesOfCode, cyclomaticComplexity, depthOfInheritance, children); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs index 1c0dde16fc..eb7b162615 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs @@ -33,9 +33,9 @@ internal static async Task ComputeAsync(IPropertySymbol prop var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = property.DeclaringSyntaxReferences; - long linesOfCode = await MetricsHelper.GetLinesOfCodeAsync(declarations, property, context).ConfigureAwait(false); + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, property, context); (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = - await MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDeclsAsync(declarations, property, coupledTypesBuilder, context).ConfigureAwait(false); + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, property, coupledTypesBuilder, context); MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, property.Parameters); MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, property.Type); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs index d31a5742ee..d293154923 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs @@ -252,11 +252,11 @@ static async Task ComputeAsync(ISymbol symbol, CodeMetri SymbolKind.NamedType => await NamedTypeMetricData.ComputeAsync((INamedTypeSymbol)symbol, context).ConfigureAwait(false), - SymbolKind.Method => await MethodMetricData.ComputeAsync((IMethodSymbol)symbol, context).ConfigureAwait(false), + SymbolKind.Method => MethodMetricData.Compute((IMethodSymbol)symbol, context), SymbolKind.Property => await PropertyMetricData.ComputeAsync((IPropertySymbol)symbol, context).ConfigureAwait(false), - SymbolKind.Field => await FieldMetricData.ComputeAsync((IFieldSymbol)symbol, context).ConfigureAwait(false), + SymbolKind.Field => FieldMetricData.Compute((IFieldSymbol)symbol, context), SymbolKind.Event => await EventMetricData.ComputeAsync((IEventSymbol)symbol, context).ConfigureAwait(false), diff --git a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs index 39e4802591..8bc9663b2d 100644 --- a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs +++ b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs @@ -9,7 +9,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Threading.Tasks; using Analyzer.Utilities; using Analyzer.Utilities.Lightup; using Analyzer.Utilities.PooledObjects; @@ -64,12 +63,12 @@ internal static void AddCoupledNamedTypes(ImmutableHashSet.Bui } } - internal static async Task GetLinesOfCodeAsync(ImmutableArray declarations, ISymbol symbol, CodeMetricsAnalysisContext context) + internal static long GetLinesOfCode(ImmutableArray declarations, ISymbol symbol, CodeMetricsAnalysisContext context) { long linesOfCode = 0; foreach (var decl in declarations) { - SyntaxNode declSyntax = await GetTopmostSyntaxNodeForDeclarationAsync(decl, symbol, context).ConfigureAwait(false); + SyntaxNode declSyntax = GetTopmostSyntaxNodeForDeclaration(decl, symbol, context); // For namespace symbols, don't count lines of code for declarations of child namespaces. // For example, "namespace N1.N2 { }" is a declaration reference for N1, but the actual declaration is for N2. @@ -126,9 +125,9 @@ static int GetNewlineCount(ImmutableArray triviaParts, bool leading) } } - internal static async Task GetTopmostSyntaxNodeForDeclarationAsync(SyntaxReference declaration, ISymbol declaredSymbol, CodeMetricsAnalysisContext context) + internal static SyntaxNode GetTopmostSyntaxNodeForDeclaration(SyntaxReference declaration, ISymbol declaredSymbol, CodeMetricsAnalysisContext context) { - var declSyntax = await declaration.GetSyntaxAsync(context.CancellationToken).ConfigureAwait(false); + var declSyntax = declaration.GetSyntax(context.CancellationToken); if (declSyntax.Language == LanguageNames.VisualBasic) { SemanticModel model = context.GetSemanticModel(declSyntax); @@ -141,7 +140,7 @@ internal static async Task GetTopmostSyntaxNodeForDeclarationAsync(S return declSyntax; } - internal static async Task<(int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics)> ComputeCoupledTypesAndComplexityExcludingMemberDeclsAsync( + internal static (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) ComputeCoupledTypesAndComplexityExcludingMemberDecls( ImmutableArray declarations, ISymbol symbol, ImmutableHashSet.Builder builder, @@ -157,7 +156,7 @@ internal static async Task GetTopmostSyntaxNodeForDeclarationAsync(S foreach (var declaration in declarations) { - SyntaxNode syntax = await GetTopmostSyntaxNodeForDeclarationAsync(declaration, symbol, context).ConfigureAwait(false); + SyntaxNode syntax = GetTopmostSyntaxNodeForDeclaration(declaration, symbol, context); nodesToProcess.Enqueue(syntax); // Ensure we process parameter initializers and attributes. @@ -167,7 +166,7 @@ internal static async Task GetTopmostSyntaxNodeForDeclarationAsync(S var parameterSyntaxRef = parameter.DeclaringSyntaxReferences.FirstOrDefault(); if (parameterSyntaxRef != null) { - var parameterSyntax = await parameterSyntaxRef.GetSyntaxAsync(context.CancellationToken).ConfigureAwait(false); + var parameterSyntax = parameterSyntaxRef.GetSyntax(context.CancellationToken); nodesToProcess.Enqueue(parameterSyntax); } } @@ -183,7 +182,7 @@ internal static async Task GetTopmostSyntaxNodeForDeclarationAsync(S if (attribute.ApplicationSyntaxReference != null && attribute.ApplicationSyntaxReference.SyntaxTree == declaration.SyntaxTree) { - var attributeSyntax = await attribute.ApplicationSyntaxReference.GetSyntaxAsync(context.CancellationToken).ConfigureAwait(false); + var attributeSyntax = attribute.ApplicationSyntaxReference.GetSyntax(context.CancellationToken); if (applicableAttributeNodes.Add(attributeSyntax)) { nodesToProcess.Enqueue(attributeSyntax); From 1288dddf27babb86a3d2dae7b2cfae4be50bfc2f Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 28 Dec 2023 13:52:38 -0600 Subject: [PATCH 02/12] Provide WellKnownTypeProvider through CodeMetricsAnalysisContext --- .../PublicAPI.Unshipped.txt | 1 + .../CodeAnalysisMetricData.AssemblyMetricData.cs | 5 +---- .../CodeAnalysisMetricData.EventMetricData.cs | 7 ++----- .../CodeAnalysisMetricData.FieldMetricData.cs | 5 +---- .../CodeAnalysisMetricData.MethodMetricData.cs | 7 ++----- .../CodeAnalysisMetricData.NamedTypeMetricData.cs | 5 +---- .../CodeAnalysisMetricData.NamespaceMetricData.cs | 5 +---- .../CodeAnalysisMetricData.PropertyMetricData.cs | 9 +++------ .../Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs | 3 +++ src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs | 10 ++++------ 10 files changed, 19 insertions(+), 38 deletions(-) diff --git a/src/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt index 1886857934..1568bff429 100644 --- a/src/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt @@ -19,6 +19,7 @@ Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ExecutableLines.get -> Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.MaintainabilityIndex.get -> int Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.SourceLines.get -> long Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.Symbol.get -> Microsoft.CodeAnalysis.ISymbol! +Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext.WellKnownTypeProvider.get -> Analyzer.Utilities.WellKnownTypeProvider! Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.BranchValue.get -> Microsoft.CodeAnalysis.IOperation? Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.ControlFlowConditionKind.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowConditionKind diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs index d2b8492319..2ac4cc397c 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using System.Linq; using System.Threading.Tasks; -using Analyzer.Utilities; using Microsoft.CodeAnalysis; namespace Microsoft.CodeAnalysis.CodeMetrics @@ -38,12 +37,10 @@ internal static async Task ComputeAsync(IAssemblySymbol asse int depthOfInheritance = 0; int grandChildCount = 0; - var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); - ImmutableArray children = await ComputeAsync(GetChildSymbols(assembly), context).ConfigureAwait(false); foreach (CodeAnalysisMetricData child in children) { - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, child.CoupledNamedTypes); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); linesOfCode += child.SourceLines; cyclomaticComplexity += child.CyclomaticComplexity; depthOfInheritance = Math.Max(child.DepthOfInheritance.GetValueOrDefault(), depthOfInheritance); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs index c83842ebd3..40cc989148 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Threading.Tasks; -using Analyzer.Utilities; namespace Microsoft.CodeAnalysis.CodeMetrics { @@ -29,20 +28,18 @@ internal EventMetricData( internal static async Task ComputeAsync(IEventSymbol @event, CodeMetricsAnalysisContext context) { - var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); - var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = @event.DeclaringSyntaxReferences; long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, @event, context); (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, @event, coupledTypesBuilder, context); - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, @event.Type); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, @event.Type); ImmutableArray children = await ComputeAsync(GetAccessors(@event), context).ConfigureAwait(false); int maintainabilityIndexTotal = 0; foreach (CodeAnalysisMetricData child in children) { - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, child.CoupledNamedTypes); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); maintainabilityIndexTotal += child.MaintainabilityIndex; cyclomaticComplexity += child.CyclomaticComplexity; computationalComplexityMetrics = computationalComplexityMetrics.Union(child.ComputationalComplexityMetrics); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs index a80f3cdb68..aa644e7786 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using Analyzer.Utilities; namespace Microsoft.CodeAnalysis.CodeMetrics { @@ -27,14 +26,12 @@ internal FieldMetricData( internal static FieldMetricData Compute(IFieldSymbol field, CodeMetricsAnalysisContext context) { - var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); - var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = field.DeclaringSyntaxReferences; long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, field, context); (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, field, coupledTypesBuilder, context); - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, field.Type); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, field.Type); int? depthOfInheritance = null; int maintainabilityIndex = CalculateMaintainabilityIndex(computationalComplexityMetrics, cyclomaticComplexity); MetricsHelper.RemoveContainingTypes(field, coupledTypesBuilder); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs index 7c746e4276..a617c54ecc 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using Analyzer.Utilities; namespace Microsoft.CodeAnalysis.CodeMetrics { @@ -27,17 +26,15 @@ internal MethodMetricData( internal static MethodMetricData Compute(IMethodSymbol method, CodeMetricsAnalysisContext context) { - var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); - var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = method.DeclaringSyntaxReferences; long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, method, context); (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, method, coupledTypesBuilder, context); - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, method.Parameters); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, method.Parameters); if (!method.ReturnsVoid) { - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, method.ReturnType); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, method.ReturnType); } int? depthOfInheritance = null; diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs index f4d5f46f6a..98baf64880 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; -using Analyzer.Utilities; namespace Microsoft.CodeAnalysis.CodeMetrics { @@ -30,8 +29,6 @@ internal NamedTypeMetricData( internal static async Task ComputeAsync(INamedTypeSymbol namedType, CodeMetricsAnalysisContext context) { - var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); - var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = namedType.DeclaringSyntaxReferences; (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = @@ -57,7 +54,7 @@ internal static async Task ComputeAsync(INamedTypeSymbol na int singleEffectiveChildMaintainabilityIndex = -1; foreach (CodeAnalysisMetricData child in children) { - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, child.CoupledNamedTypes); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); if (child.Symbol.Kind != SymbolKind.Field || filteredFieldsForComplexity.Contains((IFieldSymbol)child.Symbol)) diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs index 6e443f7ab6..97b41c7623 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; -using Analyzer.Utilities; namespace Microsoft.CodeAnalysis.CodeMetrics { @@ -36,12 +35,10 @@ internal static async Task ComputeAsync(INamespaceSymbol @n int depthOfInheritance = 0; long childrenLinesOfCode = 0; - var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); - ImmutableArray children = await ComputeAsync(GetChildSymbols(@namespace), context).ConfigureAwait(false); foreach (CodeAnalysisMetricData child in children) { - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, child.CoupledNamedTypes); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); maintainabilityIndexTotal += child.MaintainabilityIndex; cyclomaticComplexity += child.CyclomaticComplexity; depthOfInheritance = Math.Max(child.DepthOfInheritance.GetValueOrDefault(), depthOfInheritance); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs index eb7b162615..c524eeca47 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Threading.Tasks; -using Analyzer.Utilities; namespace Microsoft.CodeAnalysis.CodeMetrics { @@ -29,21 +28,19 @@ internal PropertyMetricData( internal static async Task ComputeAsync(IPropertySymbol property, CodeMetricsAnalysisContext context) { - var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); - var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = property.DeclaringSyntaxReferences; long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, property, context); (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, property, coupledTypesBuilder, context); - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, property.Parameters); - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, property.Type); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, property.Parameters); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, property.Type); ImmutableArray children = await ComputeAsync(GetAccessors(property), context).ConfigureAwait(false); int maintainabilityIndexTotal = 0; foreach (CodeAnalysisMetricData child in children) { - MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, wellKnownTypeProvider, child.CoupledNamedTypes); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); maintainabilityIndexTotal += child.MaintainabilityIndex; cyclomaticComplexity += child.CyclomaticComplexity; computationalComplexityMetrics = computationalComplexityMetrics.Union(child.ComputationalComplexityMetrics); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs b/src/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs index be912b96d4..14dbc80903 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Concurrent; using System.Threading; +using Analyzer.Utilities; namespace Microsoft.CodeAnalysis.CodeMetrics { @@ -16,12 +17,14 @@ public CodeMetricsAnalysisContext(Compilation compilation, CancellationToken can Func? isExcludedFromInheritanceCountFunc = null) { Compilation = compilation; + WellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); CancellationToken = cancellationToken; _semanticModelMap = new ConcurrentDictionary(); IsExcludedFromInheritanceCountFunc = isExcludedFromInheritanceCountFunc ?? (x => false); // never excluded by default } public Compilation Compilation { get; } + public WellKnownTypeProvider WellKnownTypeProvider { get; } public CancellationToken CancellationToken { get; } public Func IsExcludedFromInheritanceCountFunc { get; } diff --git a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs index 8bc9663b2d..478c47ceb3 100644 --- a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs +++ b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs @@ -152,8 +152,6 @@ internal static (int cyclomaticComplexity, ComputationalComplexityMetrics comput var nodesToProcess = new Queue(); using var applicableAttributeNodes = PooledHashSet.GetInstance(); - var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); - foreach (var declaration in declarations) { SyntaxNode syntax = GetTopmostSyntaxNodeForDeclaration(declaration, symbol, context); @@ -206,7 +204,7 @@ internal static (int cyclomaticComplexity, ComputationalComplexityMetrics comput } var typeInfo = model.GetTypeInfo(node, context.CancellationToken); - AddCoupledNamedTypesCore(builder, typeInfo.Type, wellKnownTypeProvider); + AddCoupledNamedTypesCore(builder, typeInfo.Type, context.WellKnownTypeProvider); var operationBlock = model.GetOperation(node, context.CancellationToken); if (operationBlock != null && operationBlock.Parent == null) @@ -248,18 +246,18 @@ internal static (int cyclomaticComplexity, ComputationalComplexityMetrics comput cyclomaticComplexity += 1; } - AddCoupledNamedTypesCore(builder, operation.Type, wellKnownTypeProvider); + AddCoupledNamedTypesCore(builder, operation.Type, context.WellKnownTypeProvider); // Handle static member accesses specially as there is no operation for static type off which the member is accessed. if (operation is IMemberReferenceOperation memberReference && memberReference.Member.IsStatic) { - AddCoupledNamedTypesCore(builder, memberReference.Member.ContainingType, wellKnownTypeProvider); + AddCoupledNamedTypesCore(builder, memberReference.Member.ContainingType, context.WellKnownTypeProvider); } else if (operation is IInvocationOperation invocation && (invocation.TargetMethod.IsStatic || invocation.TargetMethod.IsExtensionMethod)) { - AddCoupledNamedTypesCore(builder, invocation.TargetMethod.ContainingType, wellKnownTypeProvider); + AddCoupledNamedTypesCore(builder, invocation.TargetMethod.ContainingType, context.WellKnownTypeProvider); } } } From 51564e177b3a29ae7c6d34fe7a25a8bf2d459942 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 28 Dec 2023 13:56:15 -0600 Subject: [PATCH 03/12] Avoid captures in AddCoupledNamedTypesCore --- src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs index 478c47ceb3..f8bda10f3e 100644 --- a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs +++ b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs @@ -341,9 +341,9 @@ or SpecialType.System_Object or SpecialType.System_ValueType or SpecialType.System_Void => true, _ => namedType.IsAnonymousType - || namedType.GetAttributes().Any(a => + || namedType.GetAttributes().Any(static (a, wellKnownTypeProvider) => a.AttributeClass.Equals(wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesCompilerGeneratedAttribute)) || - a.AttributeClass.Equals(wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCodeDomCompilerGeneratedCodeAttribute))), + a.AttributeClass.Equals(wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCodeDomCompilerGeneratedCodeAttribute)), wellKnownTypeProvider), }; } } From a36c40c625ccb25293fe7d59b5cd17e8f72f99f7 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 28 Dec 2023 14:41:48 -0600 Subject: [PATCH 04/12] Avoid string split allocations in GetLinesOfCode --- .../Compiler/CodeMetrics/MetricsHelper.cs | 74 ++++++++++++++++--- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs index f8bda10f3e..71b8dc4d96 100644 --- a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs +++ b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs @@ -102,25 +102,77 @@ internal static long GetLinesOfCode(ImmutableArray declarations static int GetNewlineCount(SyntaxTriviaList trivialList, bool leading) { - var triviaParts = trivialList.ToFullString().Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToImmutableArray(); - return GetNewlineCount(triviaParts, leading); + var fullTrivia = trivialList.ToFullString(); + ReadOnlySpan remainingTrivia = fullTrivia.AsSpan(); - static int GetNewlineCount(ImmutableArray triviaParts, bool leading) + return GetNewlineCount(remainingTrivia, leading); + + static bool TryTakeNextLine(ref ReadOnlySpan remaining, out ReadOnlySpan next, bool leading) + { + if (remaining.IsEmpty) + { + next = ReadOnlySpan.Empty; + return false; + } + + if (leading) + { + var index = remaining.IndexOfAny('\r', '\n'); + if (index < 0) + { + next = remaining; + remaining = ReadOnlySpan.Empty; + return false; + } + + next = remaining[..index]; + if (remaining[index] == '\r' && remaining.Length > index + 1 && remaining[index + 1] == '\n') + { + remaining = remaining[(index + 2)..]; + } + else + { + remaining = remaining[(index + 1)..]; + } + + return true; + } + else + { + var index = remaining.LastIndexOfAny('\r', '\n'); + if (index < 0) + { + next = remaining; + remaining = ReadOnlySpan.Empty; + return false; + } + + next = remaining[(index + 1)..]; + if (remaining[index] == '\n' && index > 0 && remaining[index - 1] == '\r') + { + remaining = remaining[..(index - 1)]; + } + else + { + remaining = remaining[..index]; + } + + return true; + } + } + + static int GetNewlineCount(ReadOnlySpan trivia, bool leading) { - var index = leading ? 0 : triviaParts.Length - 1; - var loopCondition = leading ? LoopConditionForLeading : (Func)LoopConditionForTrailing; - var incrementOrDecrement = leading ? 1 : -1; var count = 0; - while (loopCondition(index, triviaParts.Length) && string.IsNullOrWhiteSpace(triviaParts[index])) + while (TryTakeNextLine(ref trivia, out var next, leading)) { - index += incrementOrDecrement; + if (!next.IsWhiteSpace()) + break; + count++; } return count; - - static bool LoopConditionForLeading(int index, int length) => index < length - 1; - static bool LoopConditionForTrailing(int index, int _) => index > 0; } } } From 70ad1758e7b5a022a3812e522597af4dbf13d7ad Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 28 Dec 2023 14:44:14 -0600 Subject: [PATCH 05/12] Avoid delegate allocations in GetSemanticModel --- .../Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs b/src/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs index 14dbc80903..ef54dceba0 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs @@ -12,6 +12,7 @@ namespace Microsoft.CodeAnalysis.CodeMetrics public sealed class CodeMetricsAnalysisContext { private readonly ConcurrentDictionary _semanticModelMap; + private readonly Func _getSemanticModel; public CodeMetricsAnalysisContext(Compilation compilation, CancellationToken cancellationToken, Func? isExcludedFromInheritanceCountFunc = null) @@ -21,6 +22,8 @@ public CodeMetricsAnalysisContext(Compilation compilation, CancellationToken can CancellationToken = cancellationToken; _semanticModelMap = new ConcurrentDictionary(); IsExcludedFromInheritanceCountFunc = isExcludedFromInheritanceCountFunc ?? (x => false); // never excluded by default + + _getSemanticModel = tree => Compilation.GetSemanticModel(tree); } public Compilation Compilation { get; } @@ -29,7 +32,7 @@ public CodeMetricsAnalysisContext(Compilation compilation, CancellationToken can public Func IsExcludedFromInheritanceCountFunc { get; } internal SemanticModel GetSemanticModel(SyntaxNode node) - => _semanticModelMap.GetOrAdd(node.SyntaxTree, tree => Compilation.GetSemanticModel(node.SyntaxTree)); + => _semanticModelMap.GetOrAdd(node.SyntaxTree, _getSemanticModel); } } From 88372f6003d80aca29fcdb11274e5d0f2d064ef4 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 28 Dec 2023 14:52:06 -0600 Subject: [PATCH 06/12] Avoid enumerator allocations calling AddCoupledNamedTypes --- src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs index 71b8dc4d96..37f4b05bc4 100644 --- a/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs +++ b/src/Utilities/Compiler/CodeMetrics/MetricsHelper.cs @@ -37,7 +37,7 @@ internal static int NormalizeAndRoundMaintainabilityIndex(double maintIndex) } internal static void AddCoupledNamedTypes(ImmutableHashSet.Builder builder, WellKnownTypeProvider wellKnownTypeProvider, - IEnumerable coupledTypes) + ImmutableHashSet coupledTypes) { foreach (var coupledType in coupledTypes) { @@ -46,12 +46,9 @@ internal static void AddCoupledNamedTypes(ImmutableHashSet.Bui } internal static void AddCoupledNamedTypes(ImmutableHashSet.Builder builder, WellKnownTypeProvider wellKnownTypeProvider, - params ITypeSymbol[] coupledTypes) + ITypeSymbol coupledType) { - foreach (var coupledType in coupledTypes) - { - AddCoupledNamedTypesCore(builder, coupledType, wellKnownTypeProvider); - } + AddCoupledNamedTypesCore(builder, coupledType, wellKnownTypeProvider); } internal static void AddCoupledNamedTypes(ImmutableHashSet.Builder builder, WellKnownTypeProvider wellKnownTypeProvider, From f841010fe7fba4fb35601fbfdb2605cd1ab4c3ab Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 28 Dec 2023 15:00:57 -0600 Subject: [PATCH 07/12] Avoid parallel code metrics calculations within a single member --- .../PublicAPI.Unshipped.txt | 2 + ...deAnalysisMetricData.AssemblyMetricData.cs | 13 +++- .../CodeAnalysisMetricData.EventMetricData.cs | 5 +- ...eAnalysisMetricData.NamedTypeMetricData.cs | 32 ++++++++-- ...eAnalysisMetricData.NamespaceMetricData.cs | 13 +++- ...deAnalysisMetricData.PropertyMetricData.cs | 5 +- .../CodeMetrics/CodeAnalysisMetricData.cs | 59 ++++++++++++++++++- 7 files changed, 114 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt index 1568bff429..5a43e9a49b 100644 --- a/src/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt @@ -75,6 +75,8 @@ static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 v static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 value2, T3 value3) -> int static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 value2) -> int static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1) -> int +static Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ComputeSynchronously(Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext! context) -> Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData! +static Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ComputeSynchronously(Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext! context) -> Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData! static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Create(Microsoft.CodeAnalysis.ISymbol? symbol, System.Collections.Immutable.ImmutableArray indices, Microsoft.CodeAnalysis.ITypeSymbol! type, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? parent, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? entityForInstanceLocation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysis.TryGetOrComputeResult(Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.ISymbol! owningSymbol, Analyzer.Utilities.WellKnownTypeProvider! wellKnownTypeProvider, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, System.Collections.Immutable.ImmutableHashSet! disposeOwnershipTransferLikelyTypes, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind defaultPointsToAnalysisKind, bool trackInstanceFields, bool exceptionPathsAnalysis, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind interproceduralAnalysisKind = Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind.ContextSensitive, bool performCopyAnalysisIfNotUserConfigured = false, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null, bool defaultDisposeOwnershipTransferAtConstructor = false, bool defaultDisposeOwnershipTransferAtMethodCall = false) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysisResult? static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.Create(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.Compilation! compilation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind defaultInterproceduralAnalysisKind, uint defaultMaxInterproceduralMethodCallChain = 3, uint defaultMaxInterproceduralLambdaOrLocalFunctionCallChain = 3) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs index 2ac4cc397c..3d15138dca 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs @@ -29,6 +29,18 @@ private AssemblyMetricData( } internal static async Task ComputeAsync(IAssemblySymbol assembly, CodeMetricsAnalysisContext context) + { + ImmutableArray children = await ComputeAsync(GetChildSymbols(assembly), context).ConfigureAwait(false); + return ComputeFromChildren(assembly, children, context); + } + + internal static AssemblyMetricData ComputeSynchronously(IAssemblySymbol assembly, CodeMetricsAnalysisContext context) + { + ImmutableArray children = ComputeSynchronously(GetChildSymbols(assembly), context); + return ComputeFromChildren(assembly, children, context); + } + + private static AssemblyMetricData ComputeFromChildren(IAssemblySymbol assembly, ImmutableArray children, CodeMetricsAnalysisContext context) { var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); long linesOfCode = 0; @@ -37,7 +49,6 @@ internal static async Task ComputeAsync(IAssemblySymbol asse int depthOfInheritance = 0; int grandChildCount = 0; - ImmutableArray children = await ComputeAsync(GetChildSymbols(assembly), context).ConfigureAwait(false); foreach (CodeAnalysisMetricData child in children) { MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs index 40cc989148..4821ecbcc6 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.CodeMetrics { @@ -26,7 +25,7 @@ internal EventMetricData( { } - internal static async Task ComputeAsync(IEventSymbol @event, CodeMetricsAnalysisContext context) + internal static EventMetricData Compute(IEventSymbol @event, CodeMetricsAnalysisContext context) { var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = @event.DeclaringSyntaxReferences; @@ -35,7 +34,7 @@ internal static async Task ComputeAsync(IEventSymbol @event, Co MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, @event, coupledTypesBuilder, context); MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, @event.Type); - ImmutableArray children = await ComputeAsync(GetAccessors(@event), context).ConfigureAwait(false); + ImmutableArray children = ComputeSynchronously(GetAccessors(@event), context); int maintainabilityIndexTotal = 0; foreach (CodeAnalysisMetricData child in children) { diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs index 98baf64880..8009577b09 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs @@ -3,6 +3,7 @@ #if HAS_IOPERATION using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; @@ -29,11 +30,24 @@ internal NamedTypeMetricData( internal static async Task ComputeAsync(INamedTypeSymbol namedType, CodeMetricsAnalysisContext context) { - var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); - ImmutableArray declarations = namedType.DeclaringSyntaxReferences; - (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = - MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, namedType, coupledTypesBuilder, context); + var members = GetMembers(namedType, context); + + ImmutableArray children = await ComputeAsync(members, context).ConfigureAwait(false); + + return ComputeFromChildren(namedType, children, context); + } + + internal static NamedTypeMetricData ComputeSynchronously(INamedTypeSymbol namedType, CodeMetricsAnalysisContext context) + { + var members = GetMembers(namedType, context); + + ImmutableArray children = ComputeSynchronously(members, context); + return ComputeFromChildren(namedType, children, context); + } + + private static IEnumerable GetMembers(INamedTypeSymbol namedType, CodeMetricsAnalysisContext context) + { // Compat: Filter out nested types as they are children of most closest containing namespace. var members = namedType.GetMembers().Where(m => m.Kind != SymbolKind.NamedType); @@ -45,7 +59,15 @@ internal static async Task ComputeAsync(INamedTypeSymbol na members = members.Where(m => m.Kind != SymbolKind.Method || ((IMethodSymbol)m).AssociatedSymbol == null); #endif - ImmutableArray children = await ComputeAsync(members, context).ConfigureAwait(false); + return members; + } + + private static NamedTypeMetricData ComputeFromChildren(INamedTypeSymbol namedType, ImmutableArray children, CodeMetricsAnalysisContext context) + { + var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); + ImmutableArray declarations = namedType.DeclaringSyntaxReferences; + (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, namedType, coupledTypesBuilder, context); // Heuristic to prevent simple fields (no initializer or simple initializer) from skewing the complexity. ImmutableHashSet filteredFieldsForComplexity = getFilteredFieldsForComplexity(); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs index 97b41c7623..1704fd3d85 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs @@ -28,6 +28,18 @@ internal NamespaceMetricData( } internal static async Task ComputeAsync(INamespaceSymbol @namespace, CodeMetricsAnalysisContext context) + { + ImmutableArray children = await ComputeAsync(GetChildSymbols(@namespace), context).ConfigureAwait(false); + return ComputeFromChildren(@namespace, children, context); + } + + internal static NamespaceMetricData ComputeSynchronously(INamespaceSymbol @namespace, CodeMetricsAnalysisContext context) + { + ImmutableArray children = ComputeSynchronously(GetChildSymbols(@namespace), context); + return ComputeFromChildren(@namespace, children, context); + } + + private static NamespaceMetricData ComputeFromChildren(INamespaceSymbol @namespace, ImmutableArray children, CodeMetricsAnalysisContext context) { var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); int maintainabilityIndexTotal = 0; @@ -35,7 +47,6 @@ internal static async Task ComputeAsync(INamespaceSymbol @n int depthOfInheritance = 0; long childrenLinesOfCode = 0; - ImmutableArray children = await ComputeAsync(GetChildSymbols(@namespace), context).ConfigureAwait(false); foreach (CodeAnalysisMetricData child in children) { MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs index c524eeca47..efb71a2ccb 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.CodeMetrics { @@ -26,7 +25,7 @@ internal PropertyMetricData( { } - internal static async Task ComputeAsync(IPropertySymbol property, CodeMetricsAnalysisContext context) + internal static PropertyMetricData Compute(IPropertySymbol property, CodeMetricsAnalysisContext context) { var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); ImmutableArray declarations = property.DeclaringSyntaxReferences; @@ -36,7 +35,7 @@ internal static async Task ComputeAsync(IPropertySymbol prop MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, property.Parameters); MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, property.Type); - ImmutableArray children = await ComputeAsync(GetAccessors(property), context).ConfigureAwait(false); + ImmutableArray children = ComputeSynchronously(GetAccessors(property), context); int maintainabilityIndexTotal = 0; foreach (CodeAnalysisMetricData child in children) { diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs index d293154923..ee1fb65656 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs @@ -206,6 +206,19 @@ public static Task ComputeAsync(CodeMetricsAnalysisConte return ComputeAsync(context.Compilation.Assembly, context); } + /// + /// Computes for the given . + /// + public static CodeAnalysisMetricData ComputeSynchronously(CodeMetricsAnalysisContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return ComputeSynchronously(context.Compilation.Assembly, context); + } + /// /// Computes for the given from the given . /// @@ -254,17 +267,52 @@ static async Task ComputeAsync(ISymbol symbol, CodeMetri SymbolKind.Method => MethodMetricData.Compute((IMethodSymbol)symbol, context), - SymbolKind.Property => await PropertyMetricData.ComputeAsync((IPropertySymbol)symbol, context).ConfigureAwait(false), + SymbolKind.Property => PropertyMetricData.Compute((IPropertySymbol)symbol, context), SymbolKind.Field => FieldMetricData.Compute((IFieldSymbol)symbol, context), - SymbolKind.Event => await EventMetricData.ComputeAsync((IEventSymbol)symbol, context).ConfigureAwait(false), + SymbolKind.Event => EventMetricData.Compute((IEventSymbol)symbol, context), _ => throw new NotSupportedException(), }; } } + /// + /// Computes for the given from the given . + /// + public static CodeAnalysisMetricData ComputeSynchronously(ISymbol symbol, CodeMetricsAnalysisContext context) + { + if (symbol == null) + { + throw new ArgumentNullException(nameof(symbol)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return symbol.Kind switch + { + SymbolKind.Assembly => AssemblyMetricData.ComputeSynchronously((IAssemblySymbol)symbol, context), + + SymbolKind.Namespace => NamespaceMetricData.ComputeSynchronously((INamespaceSymbol)symbol, context), + + SymbolKind.NamedType => NamedTypeMetricData.ComputeSynchronously((INamedTypeSymbol)symbol, context), + + SymbolKind.Method => MethodMetricData.Compute((IMethodSymbol)symbol, context), + + SymbolKind.Property => PropertyMetricData.Compute((IPropertySymbol)symbol, context), + + SymbolKind.Field => FieldMetricData.Compute((IFieldSymbol)symbol, context), + + SymbolKind.Event => EventMetricData.Compute((IEventSymbol)symbol, context), + + _ => throw new NotSupportedException(), + }; + } + internal static async Task> ComputeAsync(IEnumerable children, CodeMetricsAnalysisContext context) => (await Task.WhenAll( from child in children @@ -272,6 +320,13 @@ from child in children where !child.IsImplicitlyDeclared || child is INamespaceSymbol { IsGlobalNamespace: true } #endif select Task.Run(() => ComputeAsync(child, context))).ConfigureAwait(false)).ToImmutableArray(); + + internal static ImmutableArray ComputeSynchronously(IEnumerable children, CodeMetricsAnalysisContext context) + => (from child in children +#if !LEGACY_CODE_METRICS_MODE // Skip implicitly declared symbols, such as default constructor, for non-legacy mode. + where !child.IsImplicitlyDeclared || child is INamespaceSymbol { IsGlobalNamespace: true } +#endif + select ComputeSynchronously(child, context)).ToImmutableArray(); } } From a0364794bb88c4bbf892f4f8e971bbbe75a335b4 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 28 Dec 2023 15:26:01 -0600 Subject: [PATCH 08/12] Avoid parallel code metrics calculations in the analyzer pipeline --- .../Maintainability/CodeMetricsAnalyzer.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzer.cs index 24b2e52c18..eb8dfcb78e 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzer.cs @@ -142,11 +142,10 @@ public override void Initialize(AnalysisContext context) var metricsAnalysisContext = new CodeMetricsAnalysisContext(compilationContext.Compilation, compilationContext.CancellationToken, namedType => IsConfiguredToSkipFromInheritanceCount(namedType, compilationContext, tree)); - var computeTask = CodeAnalysisMetricData.ComputeAsync(metricsAnalysisContext); - computeTask.Wait(compilationContext.CancellationToken); + var codeAnalysisMetricData = CodeAnalysisMetricData.ComputeSynchronously(metricsAnalysisContext); // Analyze code metrics tree and report diagnostics. - analyzeMetricsData(computeTask.Result); + analyzeMetricsData(codeAnalysisMetricData); void analyzeMetricsData(CodeAnalysisMetricData codeAnalysisMetricData) { From ed6e97c6190fd9653cb228cf927626ebcbb564ed Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 28 Dec 2023 15:38:35 -0600 Subject: [PATCH 09/12] Cancel code metrics calculation when requested --- .../Compiler/CodeMetrics/CodeAnalysisMetricData.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs index ee1fb65656..1e4aea89e2 100644 --- a/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs +++ b/src/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs @@ -253,6 +253,11 @@ public static Task ComputeAsync(ISymbol symbol, CodeMetr throw new ArgumentNullException(nameof(context)); } + if (context.CancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(context.CancellationToken); + } + return ComputeAsync(symbol, context); static async Task ComputeAsync(ISymbol symbol, CodeMetricsAnalysisContext context) @@ -293,6 +298,8 @@ public static CodeAnalysisMetricData ComputeSynchronously(ISymbol symbol, CodeMe throw new ArgumentNullException(nameof(context)); } + context.CancellationToken.ThrowIfCancellationRequested(); + return symbol.Kind switch { SymbolKind.Assembly => AssemblyMetricData.ComputeSynchronously((IAssemblySymbol)symbol, context), From 45b850005c8d24bd9a80a6c94b6e243ca5f715eb Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 29 Dec 2023 07:54:09 -0600 Subject: [PATCH 10/12] Add code metrics tests for other line ending styles --- .../Maintainability/CodeMetricsAnalyzerTests.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzerTests.cs index ada8fb8c75..e4f7eb2a22 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzerTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. +// 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.Threading.Tasks; @@ -18,8 +18,11 @@ public class CodeMetricsAnalyzerTests { #region CA1501: Avoid excessive inheritance - [Fact] - public async Task CA1501_CSharp_VerifyDiagnosticAsync() + [Theory] + [InlineData("\r\n")] + [InlineData("\r")] + [InlineData("\n")] + public async Task CA1501_CSharp_VerifyDiagnosticAsync(string lineEndings) { var source = @" class BaseClass { } @@ -31,7 +34,7 @@ class FifthDerivedClass : FourthDerivedClass { } // This class violates the rule. class SixthDerivedClass : FifthDerivedClass { } -"; +".ReplaceLineEndings(lineEndings); DiagnosticResult[] expected = new[] { GetCSharpCA1501ExpectedDiagnostic(10, 7, "SixthDerivedClass", 6, 6, "FifthDerivedClass, FourthDerivedClass, ThirdDerivedClass, SecondDerivedClass, FirstDerivedClass, BaseClass")}; await VerifyCS.VerifyAnalyzerAsync(source, expected); From cc6286e420efed5d80449bbb60a1cb80c42b9de4 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 29 Dec 2023 07:57:39 -0600 Subject: [PATCH 11/12] Remove non-standard helper NormalizeLineEndings --- ...tPassLiteralsAsLocalizedParametersTests.cs | 8 +-- .../DeclarePublicAPIAnalyzerTestsBase.cs | 6 +-- ...portingConstructorShouldBeObsoleteTests.cs | 54 +++++++++---------- src/Test.Utilities/StringExtensions.cs | 12 ----- 4 files changed, 34 insertions(+), 46 deletions(-) delete mode 100644 src/Test.Utilities/StringExtensions.cs diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DoNotPassLiteralsAsLocalizedParametersTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DoNotPassLiteralsAsLocalizedParametersTests.cs index 3a99bb018e..809ed07807 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DoNotPassLiteralsAsLocalizedParametersTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DoNotPassLiteralsAsLocalizedParametersTests.cs @@ -204,7 +204,7 @@ public void M1(C c) c.M(str); } } -".NormalizeLineEndings(), +", // Test0.cs(16,13): warning CA1303: Method 'void Test.M1(C c)' passes a literal string as parameter 'param' of a call to 'void C.M(string param)'. Retrieve the following string(s) from a resource table instead: "a a". GetCSharpResultAt(16, 13, "void Test.M1(C c)", "param", "void C.M(string param)", "a a")); @@ -223,7 +223,7 @@ Public Sub M1(c As C) c.M(str) End Sub End Class -".NormalizeLineEndings(), +", // Test0.vb(13,13): warning CA1303: Method 'Sub Test.M1(c As C)' passes a literal string as parameter 'param' of a call to 'Sub C.M(param As String)'. Retrieve the following string(s) from a resource table instead: "a a". GetBasicResultAt(13, 13, "Sub Test.M1(c As C)", "param", "Sub C.M(param As String)", "a a")); } @@ -1868,7 +1868,7 @@ public void M1(C c) c.M(str); } } -".NormalizeLineEndings(); +"; var csTest = new VerifyCS.Test() { TestState = @@ -1903,7 +1903,7 @@ Public Sub M1(c As C) c.M(str) End Sub End Class -".NormalizeLineEndings(); +"; var vbTest = new VerifyVB.Test() { TestState = diff --git a/src/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsBase.cs b/src/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsBase.cs index 13d62c956d..5dc7857992 100644 --- a/src/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsBase.cs +++ b/src/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsBase.cs @@ -3021,10 +3021,10 @@ public async Task TestPreserveTrailingNewlineAsync(string originalEndOfFile, str C.Property.get -> int{expectedEndOfFile}"; await VerifyCSharpAdditionalFileFixAsync( - source.NormalizeLineEndings(), + source.ReplaceLineEndings("\r\n"), shippedText, - unshippedText.NormalizeLineEndings(), - fixedUnshippedText.NormalizeLineEndings()); + unshippedText.ReplaceLineEndings("\r\n"), + fixedUnshippedText.ReplaceLineEndings("\r\n")); } [Fact] diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/ImportingConstructorShouldBeObsoleteTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/ImportingConstructorShouldBeObsoleteTests.cs index d9b0e28f7b..8fa33b3451 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/ImportingConstructorShouldBeObsoleteTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/ImportingConstructorShouldBeObsoleteTests.cs @@ -36,7 +36,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; await new VerifyCS.Test { @@ -71,7 +71,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; await new VerifyVB.Test { @@ -105,7 +105,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; var fixedSource = $@" using System; using {mefNamespace}; @@ -123,7 +123,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; await new VerifyCS.Test { @@ -163,7 +163,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; var fixedSource = $@" Imports System Imports {mefNamespace} @@ -182,7 +182,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; await new VerifyVB.Test { @@ -219,7 +219,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; var fixedSource = $@" using System; using {mefNamespace}; @@ -237,7 +237,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; await new VerifyCS.Test { @@ -275,7 +275,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; var fixedSource = $@" Imports System Imports {mefNamespace} @@ -294,7 +294,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; await new VerifyVB.Test { @@ -324,14 +324,14 @@ class C {{ [ImportingConstructor] public C() {{ }} }} -".NormalizeLineEndings(); +"; var helperSource = $@" namespace Microsoft.CodeAnalysis.Host.Mef {{ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; var fixedSource = $@" using System; using {mefNamespace}; @@ -343,7 +343,7 @@ class C {{ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public C() {{ }} }} -".NormalizeLineEndings(); +"; await new VerifyCS.Test { @@ -380,7 +380,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; var fixedSource = $@" Imports System Imports {mefNamespace} @@ -399,7 +399,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; await new VerifyVB.Test { @@ -437,7 +437,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; var fixedSource = $@" using System; using {mefNamespace}; @@ -455,7 +455,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; await new VerifyCS.Test { @@ -494,7 +494,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; var fixedSource = $@" Imports System Imports {mefNamespace} @@ -513,7 +513,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; await new VerifyVB.Test { @@ -552,7 +552,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; var fixedSource = $@" using System; using {mefNamespace}; @@ -570,7 +570,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; await new VerifyCS.Test { @@ -610,7 +610,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; var fixedSource = $@" Imports System Imports {mefNamespace} @@ -629,7 +629,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; await new VerifyVB.Test { @@ -667,7 +667,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; var fixedSource = $@" using System; using {mefNamespace}; @@ -685,7 +685,7 @@ static class MefConstruction {{ internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; }} }} -".NormalizeLineEndings(); +"; await new VerifyCS.Test { @@ -726,7 +726,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; var fixedSource = $@" Imports System Imports {mefNamespace} @@ -745,7 +745,7 @@ Module MefConstruction Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" End Module End Namespace -".NormalizeLineEndings(); +"; await new VerifyVB.Test { diff --git a/src/Test.Utilities/StringExtensions.cs b/src/Test.Utilities/StringExtensions.cs deleted file mode 100644 index 053949ef25..0000000000 --- a/src/Test.Utilities/StringExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. - -using System.Text.RegularExpressions; - -namespace Test.Utilities -{ - public static class StringExtensions - { - public static string NormalizeLineEndings(this string input) - => Regex.Replace(input, "(? Date: Fri, 29 Dec 2023 08:13:05 -0600 Subject: [PATCH 12/12] Add tests covering properties and events --- .../CodeMetricsAnalyzerTests.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzerTests.cs index e4f7eb2a22..4cdcb383f4 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/CodeMetricsAnalyzerTests.cs @@ -621,6 +621,85 @@ void M(bool b) await VerifyCS.VerifyAnalyzerAsync(source, expected); } + [Fact] + public async Task CA1502_CSharp_VerifyDiagnosticInPropertyAsync() + { + var source = @" +class C +{ + bool b; + + bool M + { + get + { + // Default threshold = 25 + var x = b && b && b && b && b && b && b && + b && b && b && b && b && b && b && + b && b && b && b && b && b && b && + b && b && b && b && b && b && b; + return x; + } + set + { + // Default threshold = 25 + var x = b && b && b && b && b && b && b && + b && b && b && b && b && b && b && + b && b && b && b && b && b && b && + b && b && b && b && b && b && b; + } + } +} +"; + DiagnosticResult[] expected = new[] + { + // Test0.cs(8,9): warning CA1502: 'get_M' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. + GetCSharpCA1502ExpectedDiagnostic(8, 9, "get_M", 28, 26), + // Test0.cs(17,9): warning CA1502: 'set_M' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. + GetCSharpCA1502ExpectedDiagnostic(17, 9, "set_M", 28, 26), + }; + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CA1502_CSharp_VerifyDiagnosticInEventAsync() + { + var source = @" +class C +{ + bool b; + + event System.EventHandler M + { + add + { + // Default threshold = 25 + var x = b && b && b && b && b && b && b && + b && b && b && b && b && b && b && + b && b && b && b && b && b && b && + b && b && b && b && b && b && b; + } + remove + { + // Default threshold = 25 + var x = b && b && b && b && b && b && b && + b && b && b && b && b && b && b && + b && b && b && b && b && b && b && + b && b && b && b && b && b && b; + } + } +} +"; + DiagnosticResult[] expected = new[] + { + // Test0.cs(8,9): warning CA1502: 'add_M' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. + GetCSharpCA1502ExpectedDiagnostic(8, 9, "add_M", 28, 26), + // Test0.cs(16,9): warning CA1502: 'remove_M' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. + GetCSharpCA1502ExpectedDiagnostic(16, 9, "remove_M", 28, 26), + }; + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + [Fact] public async Task CA1502_Basic_VerifyDiagnosticAsync() {