internal static async Task <long> GetLinesOfCodeAsync(ImmutableArray <SyntaxReference> declarations, ISymbol symbol, SemanticModelProvider semanticModelProvider, CancellationToken cancellationToken) { long linesOfCode = 0; foreach (var decl in declarations) { SyntaxNode declSyntax = await GetTopmostSyntaxNodeForDeclarationAsync(decl, symbol, semanticModelProvider, cancellationToken).ConfigureAwait(false); // 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. if (symbol.Kind == SymbolKind.Namespace) { var model = semanticModelProvider.GetSemanticModel(declSyntax); if (model.GetDeclaredSymbol(declSyntax, cancellationToken) != (object)symbol) { continue; } } FileLinePositionSpan linePosition = declSyntax.SyntaxTree.GetLineSpan(declSyntax.FullSpan, cancellationToken); long delta = linePosition.EndLinePosition.Line - linePosition.StartLinePosition.Line; if (delta == 0) { // Declaration on a single line, we count it as a separate line. delta = 1; } linesOfCode += delta; } return(linesOfCode); }
internal static async Task <SyntaxNode> GetTopmostSyntaxNodeForDeclarationAsync(SyntaxReference declaration, ISymbol declaredSymbol, SemanticModelProvider semanticModelProvider, CancellationToken cancellationToken) { var declSyntax = await declaration.GetSyntaxAsync(cancellationToken).ConfigureAwait(false); if (declSyntax.Language == LanguageNames.VisualBasic) { SemanticModel model = semanticModelProvider.GetSemanticModel(declSyntax); while (declSyntax.Parent != null && Equals(model.GetDeclaredSymbol(declSyntax.Parent, cancellationToken), declaredSymbol)) { declSyntax = declSyntax.Parent; } } return(declSyntax); }
internal static async Task <(int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics)> ComputeCoupledTypesAndComplexityExcludingMemberDeclsAsync( ImmutableArray <SyntaxReference> declarations, ISymbol symbol, ImmutableHashSet <INamedTypeSymbol> .Builder builder, SemanticModelProvider semanticModelProvider, CancellationToken cancellationToken) { int cyclomaticComplexity = 0; ComputationalComplexityMetrics computationalComplexityMetrics = ComputationalComplexityMetrics.Default; var nodesToProcess = new Queue <SyntaxNode>(); foreach (var declaration in declarations) { SyntaxNode syntax = await GetTopmostSyntaxNodeForDeclarationAsync(declaration, symbol, semanticModelProvider, cancellationToken).ConfigureAwait(false); nodesToProcess.Enqueue(syntax); // Ensure we process parameter initializers and attributes. var parameters = GetParameters(symbol); foreach (var parameter in parameters) { var parameterSyntaxRef = parameter.DeclaringSyntaxReferences.FirstOrDefault(); if (parameterSyntaxRef != null) { var parameterSyntax = await parameterSyntaxRef.GetSyntaxAsync(cancellationToken).ConfigureAwait(false); nodesToProcess.Enqueue(parameterSyntax); } } var attributes = symbol.GetAttributes(); if (symbol is IMethodSymbol methodSymbol) { attributes = attributes.AddRange(methodSymbol.GetReturnTypeAttributes()); } foreach (var attribute in attributes) { if (attribute.ApplicationSyntaxReference != null) { var attributeSyntax = await attribute.ApplicationSyntaxReference.GetSyntaxAsync(cancellationToken).ConfigureAwait(false); nodesToProcess.Enqueue(attributeSyntax); } } do { var node = nodesToProcess.Dequeue(); var model = semanticModelProvider.GetSemanticModel(node); if (!ReferenceEquals(node, syntax)) { var declaredSymbol = model.GetDeclaredSymbol(node, cancellationToken); if (declaredSymbol != null && !Equals(symbol, declaredSymbol) && declaredSymbol.Kind != SymbolKind.Parameter) { // Skip member declarations. continue; } } var typeInfo = model.GetTypeInfo(node, cancellationToken); AddCoupledNamedTypesCore(builder, typeInfo.Type); var operationBlock = model.GetOperation(node, cancellationToken); if (operationBlock != null && operationBlock.Parent == null) { switch (operationBlock.Kind) { case OperationKind.Block: case OperationKind.MethodBodyOperation: case OperationKind.ConstructorBodyOperation: cyclomaticComplexity += 1; break; } computationalComplexityMetrics = computationalComplexityMetrics.Union(ComputationalComplexityMetrics.Compute(operationBlock)); // Add used types within executable code in the operation tree. foreach (var operation in operationBlock.DescendantsAndSelf()) { #if LEGACY_CODE_METRICS_MODE // Legacy mode does not account for code within lambdas/local functions for code metrics. if (operation.IsWithinLambdaOrLocalFunction()) { continue; } #endif if (!operation.IsImplicit && hasConditionalLogic(operation)) { cyclomaticComplexity += 1; } AddCoupledNamedTypesCore(builder, operation.Type); // 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); } else if (operation is IInvocationOperation invocation && (invocation.TargetMethod.IsStatic || invocation.TargetMethod.IsExtensionMethod)) { AddCoupledNamedTypesCore(builder, invocation.TargetMethod.ContainingType); } } }