예제 #1
0
        private static void BuildNodes(CodeFileTokensBuilder builder, List <NavigationItem> navigation, List <CodeDiagnostic> diagnostic, CppAstNode root, string packageNamespace)
        {
            //Mapping of each namespace to it's leaf namespace nodes
            //These leaf nodes are processed to generate and group them together
            //C++ ast has declarations under same namespace in multiple files so these needs to be grouped for better presentation
            var namespaceLeafMap = new Dictionary <string, List <CppAstNode> >();
            var types            = new HashSet <string>();

            var  namespaceNodes       = root.inner.Where(n => n.kind == NamespaceDeclKind && n.name == RootNamespace);
            bool foundFilterNamespace = false;

            foreach (var node in namespaceNodes)
            {
                var namespacebldr     = new StringBuilder();
                var leafNamespaceNode = node;
                var currentNode       = node;
                //Iterate until leaf namespace node and generate full namespace
                while (currentNode?.kind == NamespaceDeclKind)
                {
                    if (namespacebldr.Length > 0)
                    {
                        namespacebldr.Append("::");
                    }
                    namespacebldr.Append(currentNode.name);
                    leafNamespaceNode = currentNode;
                    currentNode       = currentNode.inner?.FirstOrDefault(n => n.kind == NamespaceDeclKind);
                }

                var nameSpace = namespacebldr.ToString();
                // Skip <partialnamespace>::Details namespace
                if (nameSpace.EndsWith(DetailsNamespacePostfix))
                {
                    continue;
                }

                if (!foundFilterNamespace && nameSpace.StartsWith(packageNamespace))
                {
                    foundFilterNamespace = true;
                }

                if (!namespaceLeafMap.ContainsKey(nameSpace))
                {
                    namespaceLeafMap[nameSpace] = new List <CppAstNode>();
                }
                namespaceLeafMap[nameSpace].Add(leafNamespaceNode);
            }

            foreach (var nameSpace in namespaceLeafMap.Keys)
            {
                // Filter namespace based on file name if any of the namespace matches file name pattern
                // If no namespace matches file name then allow all namespaces to be part of review to avoid mandating file name convention
                if (!foundFilterNamespace || nameSpace.StartsWith(packageNamespace))
                {
                    ProcessNamespaceNode(nameSpace);
                    builder.NewLine();
                    builder.NewLine();
                }
            }

            void ProcessNamespaceNode(string nameSpace)
            {
                NavigationItem currentNamespace = new NavigationItem()
                {
                    NavigationId = nameSpace,
                    Text         = nameSpace,
                    Tags         = { { "TypeKind", "namespace" } }
                };
                List <NavigationItem> currentNamespaceMembers = new List <NavigationItem>();

                builder.Keyword("namespace");
                builder.Space();
                var namespaceTokens = nameSpace.Split("::");

                foreach (var token in namespaceTokens)
                {
                    builder.Text(token);
                    builder.Space();
                    builder.Punctuation("{");
                    builder.Space();
                }
                builder.NewLine();
                //Process all nodes in namespace
                foreach (var leafNamespaceNode in namespaceLeafMap[nameSpace])
                {
                    if (leafNamespaceNode.inner != null)
                    {
                        foreach (var member in leafNamespaceNode.inner)
                        {
                            builder.IncrementIndent();
                            ProcessNode(member, currentNamespaceMembers, nameSpace);
                            builder.DecrementIndent();
                        }
                    }
                }

                currentNamespace.ChildItems = currentNamespaceMembers.ToArray();
                navigation.Add(currentNamespace);
                builder.NewLine();
                for (int i = 0; i < namespaceTokens.Length; i++)
                {
                    builder.Punctuation("}");
                }
                builder.NewLine();
            }

            NavigationItem BuildDeclaration(string name, string kind, string parentId = "")
            {
                string definitionId = name;

                if (!string.IsNullOrEmpty(parentId))
                {
                    definitionId = parentId + "::" + name;
                }

                builder.Append(new CodeFileToken()
                {
                    DefinitionId = definitionId,
                    Kind         = CodeFileTokenKind.TypeName,
                    Value        = name,
                });
                types.Add(name);
                return(new NavigationItem()
                {
                    NavigationId = definitionId,
                    Text = name,
                    Tags = { { "TypeKind", kind } }
                });
            }

            void BuildMemberDeclaration(string containerName, string name, string id = "")
            {
                if (string.IsNullOrEmpty(id))
                {
                    id = name;
                }
                builder.Append(new CodeFileToken()
                {
                    DefinitionId = containerName + "." + id,
                    Kind         = CodeFileTokenKind.MemberName,
                    Value        = name,
                });
            }

            string GenerateUniqueMethodId(CppAstNode methodNode)
            {
                var bldr = new StringBuilder();

                bldr.Append(methodNode.name);

                if (methodNode.inner != null)
                {
                    foreach (var parameterNode in methodNode.inner)
                    {
                        if (parameterNode.kind == ParmVarDeclKind)
                        {
                            bldr.Append(":");
                            bldr.Append(parameterNode.type.Replace(" ", "_"));
                        }
                    }
                }

                bldr.Append("::");
                var type = string.IsNullOrEmpty(methodNode.type) ? "void" : methodNode.type;

                bldr.Append(type.Replace(" ", "_"));
                return(bldr.ToString());
            }

            NavigationItem ProcessClassNode(CppAstNode node, string parentName)
            {
                NavigationItem navigationItem = null;

                // Skip empty forward declarations
                if (node.inner == null)
                {
                    return(navigationItem);
                }

                builder.Keyword(node.tagUsed);
                builder.Space();

                if (!string.IsNullOrEmpty(node.name))
                {
                    navigationItem = BuildDeclaration(node.name, node.tagUsed, parentName);
                    builder.Space();
                }

                var memberNavigations = new List <NavigationItem>();
                var parents           = node.inner?.Where(n => parentTypes.Contains(n.kind));

                if (parents != null)
                {
                    bool first = true;
                    //Show inheritance details
                    foreach (var parent in parents)
                    {
                        if (first)
                        {
                            builder.Punctuation(":");
                            builder.Space();
                            first = false;
                        }
                        else
                        {
                            builder.Punctuation(",");
                            builder.Space();
                        }
                        builder.Keyword(parent.access);
                        builder.Space();
                        if (parent.name != null)
                        {
                            BuildType(builder, parent.name, types);
                        }
                    }
                    builder.Space();
                }

                builder.NewLine();
                builder.WriteIndent();
                builder.Punctuation("{");
                builder.NewLine();
                //Double indentation for members since access modifier is not parent for members
                builder.IncrementIndent();
                builder.IncrementIndent();
                bool hasFoundDefaultAccessMembers = false;
                var  id = parentName + "::" + node.name;

                if (node.inner != null)
                {
                    bool   isPrivateMember       = false;
                    string currentAccessModifier = "";
                    foreach (var childNode in node.inner)
                    {
                        if (childNode.kind == CxxRecordDeclKind && childNode.name == node.name)
                        {
                            continue;
                        }

                        // add public or protected access specifier
                        if (childNode.kind == AccessSpecDeclKind)
                        {
                            //Skip all private members
                            isPrivateMember = (childNode.access == AccessModifierPrivate);
                            if (isPrivateMember)
                            {
                                continue;
                            }
                            currentAccessModifier = childNode.access;
                            builder.DecrementIndent();
                            builder.WriteIndent();
                            builder.Keyword(childNode.access);
                            builder.Punctuation(":");
                            builder.IncrementIndent();
                            builder.NewLine();
                        }
                        else if (!isPrivateMember && !parentTypes.Contains(childNode.kind))
                        {
                            if (string.IsNullOrEmpty(currentAccessModifier) && !hasFoundDefaultAccessMembers)
                            {
                                hasFoundDefaultAccessMembers = true;
                            }
                            ProcessNode(childNode, memberNavigations, id);
                        }
                    }
                }

                builder.DecrementIndent();
                builder.DecrementIndent();
                builder.WriteIndent();
                builder.Punctuation("}");
                builder.Punctuation(";");
                builder.NewLine();
                builder.NewLine();

                if (navigationItem != null)
                {
                    navigationItem.ChildItems = memberNavigations.ToArray();
                }

                if (node.tagUsed == "class")
                {
                    if (node.inner?.Any(n => n.isimplicit == true && n.kind == CxxConstructorDeclKind) == true)
                    {
                        diagnostic.Add(new CodeDiagnostic("", navigationItem.NavigationId, ImplicitConstrucorHintError, ""));
                    }

                    if (hasFoundDefaultAccessMembers)
                    {
                        diagnostic.Add(new CodeDiagnostic("", navigationItem.NavigationId, NonAccessModifierMemberError, ""));
                    }
                }

                return(navigationItem);
            }

            NavigationItem ProcessEnumNode(CppAstNode node)
            {
                builder.Keyword("enum");
                builder.Space();
                var navigationItem = BuildDeclaration(node.name, "enum");

                builder.NewLine();
                builder.WriteIndent();
                builder.Punctuation("{");
                builder.NewLine();
                builder.IncrementIndent();

                if (node.inner != null)
                {
                    foreach (var parameterNode in node.inner)
                    {
                        if (parameterNode.kind == EnumConstantDeclKind)
                        {
                            builder.WriteIndent();
                            BuildMemberDeclaration("", parameterNode.name);
                            if (parameterNode.inner?.FirstOrDefault(n => n.kind == "ConstantExpr") is CppAstNode
                                exprNode)
                            {
                                builder.Space();
                                builder.Punctuation("=");
                                builder.Space();
                                BuildExpression(builder, exprNode);
                            }

                            builder.Punctuation(",");
                            builder.NewLine();
                        }
                    }
                }

                builder.DecrementIndent();
                builder.WriteIndent();
                builder.Punctuation("};");
                builder.NewLine();
                builder.NewLine();
                return(navigationItem);
            }

            void ProcessFunctionDeclNode(CppAstNode node, string parentName)
            {
                if (node.isimplicit == true)
                {
                    builder.Keyword("implicit");
                    builder.Space();
                }

                if (node.isvirtual == true)
                {
                    builder.Keyword("virtual");
                    builder.Space();
                }

                if (node.inline == true)
                {
                    builder.Keyword("inline");
                    builder.Space();
                }

                if (!string.IsNullOrEmpty(node.storageClass))
                {
                    builder.Keyword(node.storageClass);
                    builder.Space();
                }

                if (node.type != null)
                {
                    BuildType(builder, node.type, types);
                    builder.Space();
                }

                BuildMemberDeclaration(parentName, node.name, GenerateUniqueMethodId(node));
                builder.Punctuation("(");
                bool first = true;

                if (node.inner != null)
                {
                    bool isMultiLineArgs = node.inner.Count > 1;
                    builder.IncrementIndent();
                    foreach (var parameterNode in node.inner)
                    {
                        if (parameterNode.kind == ParmVarDeclKind)
                        {
                            if (first)
                            {
                                first = false;
                            }
                            else
                            {
                                builder.Punctuation(",");
                                builder.Space();
                            }

                            if (isMultiLineArgs)
                            {
                                builder.NewLine();
                                builder.WriteIndent();
                            }
                            BuildType(builder, parameterNode.type, types);

                            if (!string.IsNullOrEmpty(parameterNode.name))
                            {
                                builder.Space();
                                builder.Text(parameterNode.name);
                            }
                        }
                    }
                    builder.DecrementIndent();
                }

                builder.Punctuation(")");
                //Add any postfix keywords if signature has any.
                // Few expamples are 'const noexcept'
                if (!string.IsNullOrEmpty(node.keywords))
                {
                    foreach (var key in node.keywords.Split())
                    {
                        builder.Space();
                        builder.Keyword(key);
                    }
                }

                // If method is tagged as pure, delete, or default then it should be marked as "=<0|default|delete>"
                if (node.ispure == true)
                {
                    builder.Space();
                    builder.Punctuation("=");
                    builder.Space();
                    builder.Text("0");
                }
                else if (node.isdefault == true)
                {
                    builder.Space();
                    builder.Punctuation("=");
                    builder.Space();
                    builder.Keyword("default");
                }
                else if (node.isdelete == true)
                {
                    builder.Space();
                    builder.Punctuation("=");
                    builder.Space();
                    builder.Keyword("delete");
                }

                builder.Punctuation(";");
                builder.NewLine();
            }

            void ProcessTemplateFuncDeclNode(CppAstNode node, string parentName)
            {
                builder.Keyword("template");
                builder.Space();

                if (node.inner != null)
                {
                    bool first = true;
                    builder.Punctuation("<");
                    foreach (var childnode in node.inner.Where(n => n.kind == TemplateTypeParmDeclKind))
                    {
                        if (!first)
                        {
                            builder.Punctuation(",");
                            builder.Space();
                        }
                        builder.Text(childnode.name);
                    }
                    builder.Punctuation(">");

                    builder.Space();
                    var methodNode = node.inner.FirstOrDefault(node => node.kind == CxxMethodDeclKind);
                    if (methodNode != null)
                    {
                        ProcessFunctionDeclNode(methodNode, parentName);
                    }
                }
            }

            void ProcessTypeAlias(CppAstNode node)
            {
                builder.Keyword("using");
                builder.Space();
                builder.Text(node.name);
                builder.Space();
                builder.Punctuation("=");
                builder.Space();
                BuildType(builder, node.type, types);
                builder.Punctuation(";");
                builder.NewLine();
            }

            void ProcessVarDecNode(CppAstNode node, string parentName)
            {
                if (node.constexpr == true)
                {
                    builder.Keyword("constexpr");
                    builder.Space();
                }
                if (!string.IsNullOrEmpty(node.storageClass))
                {
                    builder.Keyword(node.storageClass);
                    builder.Space();
                }

                //Remove left most const from type if it is constexpr
                string type = node.type;

                if (node.constexpr == true && type.StartsWith("const"))
                {
                    var regex = new Regex(Regex.Escape("const"));
                    type = regex.Replace(type, "", 1).Trim();
                }

                BuildType(builder, type, types);
                builder.Space();
                BuildMemberDeclaration(parentName, node.name);
                if (node.inner?.FirstOrDefault() is CppAstNode
                    exprNode)
                {
                    builder.Space();
                    builder.Punctuation("=");
                    builder.Space();
                    BuildExpression(builder, exprNode);
                }
                builder.Punctuation(";");
                builder.NewLine();
            }

            void ProcessNode(CppAstNode node, List <NavigationItem> navigationItems, string parentName)
            {
                NavigationItem currentNavItem = null;

                builder.WriteIndent();
                switch (node.kind)
                {
                case CxxRecordDeclKind:
                {
                    currentNavItem = ProcessClassNode(node, parentName);
                    builder.NewLine();
                    break;
                }

                case CxxConstructorDeclKind:
                case CxxDestructorDeclKind:
                case FunctionDeclKind:
                case CxxMethodDeclKind:
                {
                    ProcessFunctionDeclNode(node, parentName);
                    break;
                }

                case EnumDeclKind:
                {
                    currentNavItem = ProcessEnumNode(node);
                    builder.NewLine();
                    break;
                }

                case FieldDeclKind:
                case VarDeclKind:
                {
                    ProcessVarDecNode(node, parentName);
                    break;
                }

                case TypeAliasDeclKind:
                {
                    ProcessTypeAlias(node);
                    break;
                }

                case FunctionTemplateDeclKind:
                {
                    ProcessTemplateFuncDeclNode(node, parentName);
                    break;
                }

                default:
                    builder.Text(node.ToString());
                    builder.NewLine();
                    break;
                }

                if (currentNavItem != null && navigationItems != null)
                {
                    navigationItems.Add(currentNavItem);
                }
            }

            void BuildType(CodeFileTokensBuilder builder, string type, HashSet <string> types)
            {
                foreach (Match typePartMatch in _typeTokenizer.Matches(type))
                {
                    var typePart = typePartMatch.ToString();
                    if (_keywords.Contains(typePart))
                    {
                        builder.Keyword(typePart);
                    }
                    else if (typePart.Contains("::"))
                    {
                        // Handle type usage before it's defintition
                        var    typeNamespace = typePart.Substring(0, typePart.LastIndexOf("::"));
                        string typeValue     = typePart;
                        string navigateToId  = "";
                        if (types.Contains(typePart) || namespaceLeafMap.ContainsKey(typeNamespace))
                        {
                            typeValue    = typePart.Substring(typePart.LastIndexOf("::") + 2);
                            navigateToId = typePart;
                        }
                        builder.Append(new CodeFileToken()
                        {
                            Kind         = CodeFileTokenKind.TypeName,
                            NavigateToId = navigateToId,
                            Value        = typeValue
                        });
                    }
                    else
                    {
                        builder.Text(typePart);
                    }
                }
            }
        }
예제 #2
0
            public void ParseToAstTree(ZipArchiveEntry zipEntry, CppAstNode astRoot)
            {
                StreamReader reader = new StreamReader(zipEntry.Open());
                //Use a stack to track tree level
                var patternStack = new Stack <string>();
                var astnodeStack = new Stack <CppAstNode>();

                astnodeStack.Push(astRoot);

                string line = reader.ReadLine();

                while (line != null)
                {
                    CppAstNode node = new CppAstNode();
                    node.kind = ParseNodeKind(line);
                    var prefix = line.Substring(0, line.IndexOf(node.kind));
                    if (ShouldProcessLine(line, node, astnodeStack.Peek()))
                    {
                        ParseLine(line, ref node);
                        //Prefix string in ast-dump is compared to identify tree depth
                        //If prefix stack is empty or last element is same type as new prefix then node is at same depth
                        //If new prefix is larger than last element then this new node is sub node
                        //If new prefix is smaller than last element in stack then this new node is at higher level.(Traverse all the way to find a matching level in stack)
                        //Stack always keep track of items equivalent to max depth so it will be less items in stack)
                        if (patternStack.Count > 0)
                        {
                            if (patternStack.Peek().Length == prefix.Length)
                            {
                                patternStack.Pop();
                                astnodeStack.Pop();
                            }
                            else if (patternStack.Peek().Length > prefix.Length)
                            {
                                while (patternStack.Count > 0 && patternStack.Peek().Length >= prefix.Length)
                                {
                                    patternStack.Pop();
                                    astnodeStack.Pop();
                                }
                            }
                        }

                        var parentNode = astnodeStack.Count > 0 ? astnodeStack.Pop() : astRoot;
                        if (parentNode.inner == null)
                        {
                            parentNode.inner = new List <CppAstNode>();
                        }

                        parentNode.inner.Add(node);
                        patternStack.Push(prefix);
                        astnodeStack.Push(parentNode);
                        astnodeStack.Push(node);
                    }
                    else if (!_skipOnlyCurrentNodeKinds.Contains(node.kind))
                    {
                        //skip anychild nodes of excluded node
                        line = reader.ReadLine();
                        while (line != null)
                        {
                            var newLinePrefix = line.Substring(0, line.IndexOf(ParseNodeKind(line)));
                            //Should not skip new line if it is at parent level or sibling
                            if (newLinePrefix.Length <= prefix.Length)
                            {
                                break;
                            }
                            line = reader.ReadLine();
                        }
                        continue;
                    }

                    line = reader.ReadLine();
                }
            }
예제 #3
0
            private static void ParseLine(string line, ref CppAstNode node)
            {
                string[] tokens = line.Split();
                //Set any common properties
                node.storageClass = tokens.Any(token => token == "static") ? "static" : "";
                node.constexpr    = tokens.Any(token => token == "constexpr");
                node.inline       = tokens.Any(token => token == "inline");
                node.isimplicit   = tokens.Any(token => token == "implicit");

                switch (node.kind)
                {
                case NamespaceDeclKind:
                case FunctionTemplateDeclKind:
                case TemplateTypeParmDeclKind:
                    node.name = tokens.LastOrDefault();
                    break;

                case VarDeclKind:
                case ParmVarDeclKind:
                case TypeAliasDeclKind:
                    ParseFieldDecl(line, ref node, ref _varOrParamParser);
                    break;

                case CxxMethodDeclKind:
                case CxxConstructorDeclKind:
                case CxxDestructorDeclKind:
                case FunctionDeclKind:
                    ParseMethodDecl(line, ref node, ref _methodOrParamParser);
                    break;

                case CxxRecordDeclKind:
                    ParseClassDecl(line, ref node);
                    break;

                case FieldDeclKind:
                    ParseFieldDecl(line, ref node, ref _fieldDefParser);
                    break;

                case StringLiteralKind:
                case IntegerLiteralKind:
                    ParseLiteralDecl(line, ref node);
                    break;

                case AccessSpecDeclKind:
                    ParseAccessType(line, ref node);
                    break;

                case EnumDeclKind:
                    ParseEnumDecl(line, ref node);
                    break;

                case EnumConstantDeclKind:
                    ParseEnumConstDecl(line, ref node);
                    break;

                case AccessModifierPublic:
                case AccessModifierProtected:
                case AccessModifierPrivate:
                    //Inheritence declaration has inheritance access level as token kinds
                    ParseInheritanceDecl(line, node);
                    break;

                default:
                    node.name = "Not implemented";
                    node.type = "";
                    break;
                }
            }