internal static async Task <long> GetLinesOfCodeAsync(ImmutableArray <SyntaxReference> declarations, ISymbol symbol, CodeMetricsAnalysisContext context)
        {
            long linesOfCode = 0;

            foreach (var decl in declarations)
            {
                SyntaxNode declSyntax = await GetTopmostSyntaxNodeForDeclarationAsync(decl, symbol, context).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 = context.GetSemanticModel(declSyntax);
                    if (model.GetDeclaredSymbol(declSyntax, context.CancellationToken) != (object)symbol)
                    {
                        continue;
                    }
                }

                FileLinePositionSpan linePosition = declSyntax.SyntaxTree.GetLineSpan(declSyntax.FullSpan, context.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, CodeMetricsAnalysisContext context)
        {
            var declSyntax = await declaration.GetSyntaxAsync(context.CancellationToken).ConfigureAwait(false);

            if (declSyntax.Language == LanguageNames.VisualBasic)
            {
                SemanticModel model = context.GetSemanticModel(declSyntax);
                while (declSyntax.Parent != null && Equals(model.GetDeclaredSymbol(declSyntax.Parent, context.CancellationToken), declaredSymbol))
                {
                    declSyntax = declSyntax.Parent;
                }
            }

            return(declSyntax);
        }
Example #3
0
        internal static async Task <long> GetLinesOfCodeAsync(ImmutableArray <SyntaxReference> declarations, ISymbol symbol, CodeMetricsAnalysisContext context)
        {
            long linesOfCode = 0;

            foreach (var decl in declarations)
            {
                SyntaxNode declSyntax = await GetTopmostSyntaxNodeForDeclarationAsync(decl, symbol, context).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 = context.GetSemanticModel(declSyntax);
                    if (!Equals(model.GetDeclaredSymbol(declSyntax, context.CancellationToken), symbol))
                    {
                        continue;
                    }
                }

                FileLinePositionSpan linePosition = declSyntax.SyntaxTree.GetLineSpan(declSyntax.FullSpan, context.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;
                }
                else
                {
                    // Ensure that we do not count the leading and trailing emtpy new lines.
                    var additionalNewLines = Math.Max(0, GetNewlineCount(declSyntax.GetLeadingTrivia(), leading: true) + GetNewlineCount(declSyntax.GetTrailingTrivia(), leading: false) - 1);
                    delta -= additionalNewLines;
                }

                linesOfCode += delta;
            }

            return(linesOfCode);
        internal static async Task <(int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics)> ComputeCoupledTypesAndComplexityExcludingMemberDeclsAsync(
            ImmutableArray <SyntaxReference> declarations,
            ISymbol symbol,
            ImmutableHashSet <INamedTypeSymbol> .Builder builder,
            CodeMetricsAnalysisContext context)
        {
            int cyclomaticComplexity = 0;
            ComputationalComplexityMetrics computationalComplexityMetrics = ComputationalComplexityMetrics.Default;

            var nodesToProcess = new Queue <SyntaxNode>();

            using var applicableAttributeNodes = PooledHashSet <SyntaxNode> .GetInstance();

            var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation);

            foreach (var declaration in declarations)
            {
                SyntaxNode syntax = await GetTopmostSyntaxNodeForDeclarationAsync(declaration, symbol, context).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(context.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 &&
                        attribute.ApplicationSyntaxReference.SyntaxTree == declaration.SyntaxTree)
                    {
                        var attributeSyntax = await attribute.ApplicationSyntaxReference.GetSyntaxAsync(context.CancellationToken).ConfigureAwait(false);

                        if (applicableAttributeNodes.Add(attributeSyntax))
                        {
                            nodesToProcess.Enqueue(attributeSyntax);
                        }
                    }
                }

                do
                {
                    var node  = nodesToProcess.Dequeue();
                    var model = context.GetSemanticModel(node);

                    if (!ReferenceEquals(node, syntax))
                    {
                        var declaredSymbol = model.GetDeclaredSymbol(node, context.CancellationToken);
                        if (declaredSymbol != null && !Equals(symbol, declaredSymbol) && declaredSymbol.Kind != SymbolKind.Parameter)
                        {
                            // Skip member declarations.
                            continue;
                        }
                    }

                    var typeInfo = model.GetTypeInfo(node, context.CancellationToken);
                    AddCoupledNamedTypesCore(builder, typeInfo.Type, wellKnownTypeProvider);

                    var operationBlock = model.GetOperation(node, context.CancellationToken);
                    if (operationBlock != null && operationBlock.Parent == null)
                    {
                        switch (operationBlock.Kind)
                        {
                        case OperationKind.Block:
                        case OperationKind.MethodBodyOperation:
                        case OperationKind.ConstructorBodyOperation:
                            cyclomaticComplexity += 1;
                            break;

                        case OperationKind.None:
                            // Skip non-applicable attributes.
                            if (!applicableAttributeNodes.Contains(node))
                            {
                                continue;
                            }

                            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, 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);
                            }
                            else if (operation is IInvocationOperation invocation &&
                                     (invocation.TargetMethod.IsStatic || invocation.TargetMethod.IsExtensionMethod))
                            {
                                AddCoupledNamedTypesCore(builder, invocation.TargetMethod.ContainingType, wellKnownTypeProvider);
                            }
                        }
                    }