public async Task <MethodPerformanceInfo> GetMethodPerformanceInfoAsync(IMethodSymbol methodSymbol)
        {
            // DocumentationCommentId is used in Symbol Editor, since methodSymbols aren't equal accross compilations
            // see https://github.com/dotnet/roslyn/issues/3058
            var documentationCommentId = methodSymbol.GetDocumentationCommentId();

            if (_telemetryDatas.TryGetValue(documentationCommentId, out var performanceInfo))
            {
                return(performanceInfo);
            }

            var newPerformanceInfo = new MockMethodPerformanceInfo(_predictionEngine, methodSymbol, documentationCommentId);

            _telemetryDatas.Add(documentationCommentId, newPerformanceInfo);

            await UpdateCallersAsync(methodSymbol, newPerformanceInfo.MethodPerformanceData.MeanExecutionTime).ConfigureAwait(false);

            return(newPerformanceInfo);
        }
        public async Task <MethodPerformanceInfo> GetMethodPerformanceInfoAsync(IMethodSymbol methodSymbol)
        {
            var documentationCommentId = methodSymbol.GetDocumentationCommentId();

            if (_methodPerformancInfoCache.TryGetValue(documentationCommentId, out var existingMethodPerformanceInfo))
            {
                return(existingMethodPerformanceInfo);
            }

            // TODO RR: Use SyntaxAnnotation https://joshvarty.wordpress.com/2015/09/18/learn-roslyn-now-part-13-keeping-track-of-syntax-nodes-with-syntax-annotations/
            // TODO RR: Do one Dictionary per Class/File
            var methodTelemetry = await _telemetryProvider.GetTelemetryDataAsync(documentationCommentId).ConfigureAwait(false);

            if (methodTelemetry == null)
            {
                return(null);
            }

            var performanceInfo = new MethodPerformanceInfo(_predictionEngine, methodSymbol, methodTelemetry);

            _methodPerformancInfoCache.Add(documentationCommentId, performanceInfo);
            return(performanceInfo);
        }
        private DeclarationNodeContainer VisitMethodTree(MethodDeclarationSyntax node, int currentDepth, int maxDepth = 10)
        {
            if (!_semanticModels.TryGetValue(node.SyntaxTree, out SemanticModel semanticModel))
            {
                throw new Exception($"Couldn't find semantic model for node {node.ToFullString()}");
            }

            IMethodSymbol symbol = semanticModel.GetDeclaredSymbol(node);
            string        methodDocumenationCommentId = symbol.GetDocumentationCommentId();

            if (_visitedNodes.TryGetValue(methodDocumenationCommentId, out DeclarationNodeContainer visitedNode))
            {
                return(visitedNode);
            }

            if (symbol.IsExtern || symbol.IsAbstract)
            {
                DeclarationNodeContainer _result = new DeclarationNodeContainer(node, false);
                _visitedNodes.Add(methodDocumenationCommentId, _result);
                return(_result);
            }

            List <string> notNullParameters = new List <string>();
            IEnumerable <InvocationExpressionSyntax> invocationNodes = node.DescendantNodes().OfType <InvocationExpressionSyntax>();

            if (symbol.DeclaredAccessibility == Accessibility.Internal || symbol.DeclaredAccessibility == Accessibility.Private)
            {
                notNullParameters = AnalyzeDebugAsserts(invocationNodes, semanticModel);
            }

            IEnumerable <IfStatementSyntax> ifStatementNodes = node.DescendantNodes().OfType <IfStatementSyntax>();
            bool hasArgumentValidation = false;

            foreach (IfStatementSyntax ifNode in ifStatementNodes)
            {
                SyntaxNode tmpNode = ifNode.Statement;
                if (tmpNode.IsKind(SyntaxKind.Block))
                {
                    if (tmpNode.ChildNodes().Count() != 1)
                    {
                        continue;
                    }

                    tmpNode = tmpNode.ChildNodes().First();
                }

                if (tmpNode is ExpressionStatementSyntax expression)
                {
                    tmpNode = expression.Expression;
                }

                if (tmpNode is InvocationExpressionSyntax invocationExpression)
                {
                    var access = invocationExpression.Expression as MemberAccessExpressionSyntax;
                    if (access == null)
                    {
                        continue;
                    }

                    var methodSymbol = semanticModel.GetSymbolInfo(access).Symbol as IMethodSymbol;
                    if (methodSymbol.ContainingAssembly != symbol.ContainingAssembly ||
                        methodSymbol.ContainingType.DeclaredAccessibility == Accessibility.Public ||
                        !string.Equals(methodSymbol.ContainingType.Name, "ThrowHelper", StringComparison.Ordinal))
                    {
                        continue;
                    }
                }
                else
                {
                    if (!(tmpNode is ThrowStatementSyntax))
                    {
                        continue;
                    }
                }

                if (!hasArgumentValidation && symbol.DeclaredAccessibility == Accessibility.Private || symbol.DeclaredAccessibility == Accessibility.Internal)
                {
                    hasArgumentValidation = IfConditionHasArgumentValidation(ifNode, semanticModel);
                }

                if (ifNode.Condition is BinaryExpressionSyntax binaryExpression)
                {
                    if (binaryExpression.IsKind(SyntaxKind.EqualsExpression))
                    {
                        if (TryGetParameterIdentifierFromBinaryExpression(binaryExpression, semanticModel, out IParameterSymbol parameter))
                        {
                            notNullParameters.Add(parameter.Name);
                        }
                    }
                }
            }

            foreach (InvocationExpressionSyntax invocationNode in invocationNodes)
            {
                if (semanticModel.GetSymbolInfo(invocationNode).Symbol is IMethodSymbol symbolModel)
                {
                    // Interlocked gives a lot of false positives, so if the method is using Interlocked, let's not annotate it.
                    if (symbolModel.ContainingType.Name == "Interlocked")
                    {
                        var _result = new DeclarationNodeContainer(node, true);
                        _visitedNodes.Add(methodDocumenationCommentId, _result);
                        return(_result);
                    }

                    DeclarationNodeContainer           visitedInvocationNode = default;
                    IEnumerable <IdentifierNameSyntax> invocationIdentifiers = invocationNode.ArgumentList.DescendantNodes().OfType <IdentifierNameSyntax>().Where(idn => idn.Parent is ArgumentSyntax);
                    string invocationDocumentationId = symbolModel.GetDocumentationCommentId();

                    if (string.Equals(methodDocumenationCommentId, invocationDocumentationId))
                    {
                        continue;
                    }

                    if (symbolModel.ContainingType != symbol.ContainingType)
                    {
                        continue;
                    }

                    if (!symbolModel.Name.Contains(symbol.Name))
                    {
                        if (symbol.DeclaredAccessibility != Accessibility.Public || symbolModel.DeclaredAccessibility != Accessibility.Public)
                        {
                            continue;
                        }
                    }

                    if (!_visitedNodes.TryGetValue(invocationDocumentationId, out visitedInvocationNode))
                    {
                        if (currentDepth < maxDepth)
                        {
                            if (ShouldVisitInvocationMember(invocationIdentifiers, semanticModel))
                            {
                                if (_allMethodDeclarations.TryGetValue(invocationDocumentationId, out MethodDeclarationSyntax declarationToVisit))
                                {
                                    // Visit declaration method recursively
                                    visitedInvocationNode = VisitMethodTree(declarationToVisit, ++currentDepth);
                                }
                                else
                                {
                                    throw new Exception("Couldn't find method declaration in _allMethodDeclarations");
                                }
                            }
                        }
                    }

                    if (visitedInvocationNode.Node != null && visitedInvocationNode.ShouldRelyOnArgumentValidation)
                    {
                        int i = 0;
                        foreach (ArgumentSyntax argument in invocationNode.ArgumentList.Arguments)
                        {
                            if (argument.Expression is IdentifierNameSyntax identifier)
                            {
                                if (TryGetParameterIdentifier(identifier, semanticModel, out IParameterSymbol parameter))
                                {
                                    if (!visitedInvocationNode.Node.ParameterList.Parameters[i].Type.ToFullString().TrimEnd().EndsWith("?"))
                                    {
                                        notNullParameters.Add(parameter.Name);
                                    }
                                }
                            }

                            i++;
                        }
                    }
                }
            }

            SeparatedSyntaxList <ParameterSyntax> newParamList = node.ParameterList.Parameters;
            int index = 0;

            foreach (ParameterSyntax parameterSyntax in node.ParameterList.Parameters)
            {
                IParameterSymbol parameterSymbol = symbol.Parameters[index++];
                ParameterSyntax  ps            = newParamList.Where(p => string.Equals(p.Identifier.Value, parameterSyntax.Identifier.Value)).FirstOrDefault();
                string           parameterType = ps.Type.ToFullString().TrimEnd();
                if (parameterSymbol.Type.IsReferenceType && !notNullParameters.Contains(parameterSymbol.Name))
                {
                    if (!parameterType.EndsWith("?"))
                    {
                        newParamList = newParamList.Replace(ps, ps.WithType(SyntaxFactory.ParseTypeName(parameterType + "? ")));
                    }
                }
                else
                {
                    // shouldn't contain ? so if we added it for some reason, let's remove it.
                    if (parameterType.EndsWith("?"))
                    {
                        newParamList = newParamList.Replace(ps, ps.WithType(SyntaxFactory.ParseTypeName(parameterType.Substring(0, parameterType.Length - 1) + " ")));
                    }
                }
            }

            bool shouldRelyOnArgumentValidation = symbol.DeclaredAccessibility == Accessibility.Private || symbol.DeclaredAccessibility == Accessibility.Internal ? hasArgumentValidation : true;
            DeclarationNodeContainer result     = new DeclarationNodeContainer(node.WithParameterList(node.ParameterList.WithParameters(newParamList)), shouldRelyOnArgumentValidation);

            _visitedNodes.Add(methodDocumenationCommentId, result);
            return(result);
        }
        // Can be removed if https://github.com/dotnet/roslyn/issues/67 gets resolved
        // Modeled after Sandcastle implementation: http://tunnelvisionlabs.github.io/SHFB/docs-master/XMLCommentsGuide/html/86453FFB-B978-4A2A-9EB5-70E118CA8073.htm
        private void ProcessInheritDoc(XElement root, ISymbol currentSymbol, List <XElement> inheritDocElements, HashSet <string> inheritedSymbolCommentIds)
        {
            if (inheritDocElements.Count > 0)
            {
                // Gather the documents (first in the list takes precedence)
                List <ISymbol> inheritedSymbols = new List <ISymbol>();
                foreach (XElement inheritDocElement in inheritDocElements)
                {
                    // Remove from the parent
                    inheritDocElement.Remove();

                    // Locate the appropriate symbol
                    string inheritDocElementCref = inheritDocElement.Attribute("cref")?.Value;
                    if (inheritDocElementCref == null && inheritedSymbolCommentIds.Add(currentSymbol.GetDocumentationCommentId()))
                    {
                        INamedTypeSymbol currentTypeSymbol   = currentSymbol as INamedTypeSymbol;
                        IMethodSymbol    currentMethodSymbol = currentSymbol as IMethodSymbol;
                        if (currentTypeSymbol != null)
                        {
                            // Types and interfaces, inherit from all base types
                            List <INamedTypeSymbol> baseTypeSymbols = AnalyzeSymbolVisitor.GetBaseTypes(currentTypeSymbol)
                                                                      .Where(x => inheritedSymbolCommentIds.Add(x.GetDocumentationCommentId()))
                                                                      .ToList();
                            if (baseTypeSymbols.Count > 0)
                            {
                                inheritedSymbols.AddRange(baseTypeSymbols);
                            }

                            // Then inherit from all interfaces
                            List <INamedTypeSymbol> interfaceSymbols = currentTypeSymbol.AllInterfaces
                                                                       .Where(x => inheritedSymbolCommentIds.Add(x.GetDocumentationCommentId()))
                                                                       .ToList();
                            if (interfaceSymbols.Count > 0)
                            {
                                inheritedSymbols.AddRange(interfaceSymbols);
                            }
                        }
                        else if (currentMethodSymbol != null && currentMethodSymbol.Name == currentMethodSymbol.ContainingType.Name)
                        {
                            // Constructor, check base type constructors for the same signature
                            string signature = AnalyzeSymbolVisitor.GetFullName(currentMethodSymbol);
                            signature = signature.Substring(signature.IndexOf('('));
                            foreach (INamedTypeSymbol baseTypeSymbol in AnalyzeSymbolVisitor.GetBaseTypes(currentMethodSymbol.ContainingType))
                            {
                                foreach (IMethodSymbol constructorSymbol in baseTypeSymbol.Constructors.Where(x => !x.IsImplicitlyDeclared))
                                {
                                    string constructorSignature = AnalyzeSymbolVisitor.GetFullName(constructorSymbol);
                                    constructorSignature = constructorSignature.Substring(constructorSignature.IndexOf('('));
                                    if (signature == constructorSignature &&
                                        inheritedSymbolCommentIds.Add(constructorSymbol.GetDocumentationCommentId()))
                                    {
                                        inheritedSymbols.Add(constructorSymbol);
                                    }
                                }
                            }
                        }
                        else if (currentMethodSymbol != null && currentMethodSymbol.IsOverride)
                        {
                            // Override, get overridden method
                            IMethodSymbol overriddenMethodSymbol = currentMethodSymbol.OverriddenMethod;
                            if (overriddenMethodSymbol != null &&
                                inheritedSymbolCommentIds.Add(overriddenMethodSymbol.GetDocumentationCommentId()))
                            {
                                inheritedSymbols.Add(overriddenMethodSymbol);
                            }
                        }
                        else if (currentMethodSymbol != null)
                        {
                            // Check if this is an interface implementation
                            IMethodSymbol interfaceMethodSymbol = currentMethodSymbol.ContainingType.AllInterfaces
                                                                  .SelectMany(x => x.GetMembers().OfType <IMethodSymbol>())
                                                                  .FirstOrDefault(x => currentSymbol.Equals(currentSymbol.ContainingType.FindImplementationForInterfaceMember(x)));
                            if (interfaceMethodSymbol != null &&
                                inheritedSymbolCommentIds.Add(interfaceMethodSymbol.GetDocumentationCommentId()))
                            {
                                inheritedSymbols.Add(interfaceMethodSymbol);
                            }
                        }
                    }
                    else if (inheritDocElementCref != null)
                    {
                        // Explicit cref
                        if (inheritedSymbolCommentIds.Add(inheritDocElementCref))
                        {
                            ISymbol inheritedSymbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(inheritDocElementCref, _compilation);
                            if (inheritedSymbol != null)
                            {
                                inheritedSymbols.Add(inheritedSymbol);
                            }
                        }
                    }
                }

                // Add the inherited comments
                foreach (ISymbol inheritedSymbol in inheritedSymbols)
                {
                    string inheritedXml = inheritedSymbol.GetDocumentationCommentXml(expandIncludes: true);
                    if (!string.IsNullOrEmpty(inheritedXml))
                    {
                        XElement inheritedRoot = GetRootElement(inheritedXml);
                        if (inheritedRoot != null)
                        {
                            // Inherit elements other than <inheritdoc>
                            List <XElement> inheritedInheritDocElements = new List <XElement>();
                            foreach (XElement inheritedElement in inheritedRoot.Elements())
                            {
                                if (inheritedElement.Name == "inheritdoc")
                                {
                                    inheritedInheritDocElements.Add(inheritedElement);
                                }
                                else
                                {
                                    string inheritedElementCref = inheritedElement.Attribute("cref")?.Value;
                                    string inheritedElementName = inheritedElement.Attribute("name")?.Value;
                                    bool   inherit = true;
                                    foreach (XElement rootElement in root.Elements(inheritedElement.Name))
                                    {
                                        if (inheritedElementCref == null && inheritedElementName == null)
                                        {
                                            // Don't inherit if the name is the same and there's no distinguishing attributes
                                            inherit = false;
                                            break;
                                        }
                                        if (inheritedElementCref != null && inheritedElementCref == rootElement.Attribute("cref")?.Value)
                                        {
                                            // Don't inherit if the cref attribute is the same
                                            inherit = false;
                                            break;
                                        }
                                        if (inheritedElementName != null && inheritedElementName == rootElement.Attribute("name")?.Value)
                                        {
                                            // Don't inherit if the name attribute is the same
                                            inherit = false;
                                            break;
                                        }
                                    }
                                    if (inherit)
                                    {
                                        root.Add(inheritedElement);
                                    }
                                }
                            }

                            // Recursively inherit <inheritdoc>
                            if (inheritedInheritDocElements.Count > 0)
                            {
                                ProcessInheritDoc(root, inheritedSymbol, inheritedInheritDocElements, inheritedSymbolCommentIds);
                            }
                        }
                    }
                }
            }
        }
        private static bool TryGetMember(IMethodSymbol method, out AnnotationMember member)
        {
            var attributes = GetAttributes(method).ToImmutableArray();

            var parameters = GetParameters(method).ToImmutableArray();

            if (ShouldIncludeInResult(attributes, parameters))
            {
                var memberName = method.GetDocumentationCommentId();

                member = new AnnotationMember(memberName, attributes, parameters);
                return true;
            }

            member = null;
            return false;
        }
Example #6
0
        /// <summary>
        /// Prepares the <see cref="MethodMemberBuilder"/> from the <paramref name="symbol"/>
        /// and adds it to <see cref="TypeMemberBuilder.ContentMembers"/>
        /// </summary>
        /// <param name="symbol">Source <see cref="INamedTypeSymbol"/></param>
        /// <param name="root">Builder root</param>
        /// <param name="type">Parent <see cref="TypeMemberBuilder"/></param>
        /// <param name="level">Hierarchy level (used to indent the console output)</param>
        private static void BuildMethod(IMethodSymbol symbol, RootMemberBuilder root, TypeMemberBuilder type, int level)
        {
            if (symbol.IsImplicitlyDeclared ||
                symbol.MethodKind == MethodKind.AnonymousFunction ||
                symbol.MethodKind == MethodKind.BuiltinOperator ||
                symbol.MethodKind == MethodKind.LambdaMethod ||
                symbol.MethodKind == MethodKind.LocalFunction ||
                symbol.MethodKind == MethodKind.PropertyGet ||
                symbol.MethodKind == MethodKind.PropertySet ||
                symbol.MethodKind == MethodKind.EventAdd ||
                symbol.MethodKind == MethodKind.EventRemove)
            {
                return;
            }

            if (symbol.GetAttributes().Any(a => a.AttributeClassString() == "System.Runtime.CompilerServices.CompilerGeneratedAttribute"))
            {
                return;
            }

            var m = new MethodMemberBuilder()
            {
                Name                 = symbol.GetOperatorMethodAliasOrOriginalName(),
                NameBase             = symbol.Name,
                OperatorCSharpSymbol = symbol.GetOperatorCSharpSymbol(),
                Symbol               = symbol,
                SourceFiles          = symbol.DeclaringSyntaxReferences.Select(dsr => dsr.SyntaxTree.FilePath).ToList(),
                DocumentationId      = symbol.GetDocumentationCommentId(),
                DocumentationXml     = symbol.GetDocumentationCommentXml(),
                Documentation        = Documentation.Read(symbol.GetDocumentationCommentXml()),
                Modifier             = ModifierEnumExtensions.Modifier(symbol.DeclaredAccessibility),
                IsAbstract           = symbol.IsAbstract,
                IsExtern             = symbol.IsExtern,
                IsSealed             = symbol.IsSealed,
                IsStatic             = symbol.IsStatic,
                IsOverride           = symbol.IsOverride,
                IsVirtual            = symbol.IsVirtual,
                IsAsync              = symbol.IsAsync,
                IsExtensionMethod    = symbol.IsExtensionMethod,
                IsGeneric            = symbol.IsGenericMethod && symbol.TypeParameters != null &&
                                       symbol.TypeParameters.Length > 0,
                ReturnsVoid          = symbol.ReturnsVoid,
                ReturnsByRef         = symbol.ReturnsByRef,
                ReturnsByRefReadonly = symbol.ReturnsByRefReadonly,
                RefKind         = (RefKindEnum)symbol.RefKind,
                ReturnTypeRef   = TypeRef.GetOrCreate(symbol.ReturnType, root),
                MethodKind      = (MethodKindEnum)symbol.MethodKind,
                IsNew           = symbol.GetIsNew(),
                OverridesSymbol = symbol.OverriddenMethod,
                ExplicitInterfaceImplementationMemberSymbol =
                    symbol.ExplicitInterfaceImplementations != null && symbol.ExplicitInterfaceImplementations.Length > 0
                        ?symbol.ExplicitInterfaceImplementations[0]
                        :null
            };

            if (m.IsConstructor || m.IsDestructor)
            {
                m.Name     = $"{(m.IsDestructor ? "~" : "")}{type.Name}";
                m.NameBase = m.Name;
            }

            if (m.IsGeneric)
            {
                //Process the type parameters
                m.TypeParameters = GetTypeParameters(symbol.TypeParameters, root);
                m.Name          += $"<{string.Join(",", m.TypeParameters.Select(tp => tp.Name))}>"; // add types to name
            }

            if (symbol.Parameters != null && symbol.Parameters.Length > 0)
            {
                //Process the method parameters
                m.Parameters = GetMethodParameters(symbol.Parameters, root, m.IsExtensionMethod);
                m.Name      += $"({string.Join(", ", m.Parameters.Select(p => p.TypeRef.ApplySpecialName(false)))})";
            }
            else
            {
                m.Name += "()";
            }

            m.SetAttributes(root);

            type.ContentMembers.Add(m);
            Console.WriteLine($"{new string(' ', level)} read as {m}");
        }