0

这就是我和我的团队选择为我们的学校项目做的事情。好吧,实际上我们还没有决定如何解析 C# 源文件。

我们的目标是,对 C# 源文件进行全面分析,并生成报告。报告将在其中包含代码中发生的内容。

报告只需包含:

  • 字符串文字
  • 方法名称
  • 变量名
  • 字段名称
  • ETC

我负责调查这个 Irony 图书馆。老实说,我不知道将数据整理成清晰可读的报告的最佳方法。我正在使用压缩包中的 C# 语法类。

有没有可以正确识别每个节点子节点的步骤?(例如:使用指令、命名空间声明、类声明等、方法体)

任何帮助或建议将不胜感激。谢谢。

编辑:对不起,我忘了说我们也需要分析方法调用。

4

2 回答 2

1

你的主要目标是掌握形式语言的基础知识。一个好的初创公司可能会在这里找到。本文介绍了在一个简单的数字计算器的语法样本上使用 Irony 的方法。

假设您要解析某个包含 C# 代码的文件,该文件的路径是您知道的:

private void ParseForLongMethods(string path)
    {
        _parser = new Parser(new CSharpGrammar());
        if (_parser == null || !_parser.Language.CanParse()) return;
        _parseTree = null;
        GC.Collect(); //to avoid disruption of perf times with occasional collections
        _parser.Context.SetOption(ParseOptions.TraceParser, true);
        try
        {
            string contents = File.ReadAllText(path);
            _parser.Parse(contents);//, "<source>");
        }
        catch (Exception ex)
        {
        }
        finally
        {
            _parseTree = _parser.Context.CurrentParseTree;
            TraverseParseTree();
        }
    }

这是遍历方法本身,计算节点中的一些信息。实际上,这段代码计算了类的每个方法中的语句数。如果您有任何问题,欢迎随时问我

 private void TraverseParseTree()
        {
            if (_parseTree == null) return;
            ParseNodeRec(_parseTree.Root);
        }
        private void ParseNodeRec(ParseTreeNode node)
        {
            if (node == null) return;
            string functionName = "";
            if (node.ToString().CompareTo("class_declaration") == 0)
            {
                ParseTreeNode tmpNode = node.ChildNodes[2];
                currentClass = tmpNode.AstNode.ToString();
            }
            if (node.ToString().CompareTo("method_declaration") == 0)
            {
                foreach (var child in node.ChildNodes)
                {
                    if (child.ToString().CompareTo("qual_name_with_targs") == 0)
                    {
                        ParseTreeNode tmpNode = child.ChildNodes[0];
                        while (tmpNode.ChildNodes.Count != 0)
                        { tmpNode = tmpNode.ChildNodes[0]; }
                        functionName = tmpNode.AstNode.ToString();
                    }
                    if (child.ToString().CompareTo("method_body") == 0)  //method_declaration
                    {
                        int statementsCount = FindStatements(child);
                        //Register bad smell
                        if (statementsCount>(((LongMethodsOptions)this.Options).MaxMethodLength))
                        {
                            //function.StartPoint.Line
                            int functionLine = GetLine(functionName);
                            foundSmells.Add(new BadSmellRegistry(name, functionLine,currentFile,currentProject,currentSolution,false));
                        }
                    }
                }
            }
            foreach (var child in node.ChildNodes)
            { ParseNodeRec(child); }
        }
于 2011-09-28T18:18:32.667 回答
1

我不确定这是您需要的,但您可以使用 CodeDom 和 CodeDom.Compiler 命名空间来编译 C# 代码,然后使用反射分析结果,例如:

        // Create assamblly in Memory
        CodeSnippetCompileUnit code = new CodeSnippetCompileUnit(classCode);
        CSharpCodeProvider provider = new CSharpCodeProvider();
        CompilerResults results = provider.CompileAssemblyFromDom(compileParams, code);
        foreach(var type in results.CompiledAssembly)
        {
              // Your analysis go here
        }

更新:在 VS2015 中,您可以使用新的 C# 编译器(AKA Roslyn)来做同样的事情,例如:

var root = (CompilationUnitSyntax)tree.GetRoot();
var compilation = CSharpCompilation.Create("HelloTDN")
            .AddReferences(references: new[] { MetadataReference.CreateFromAssembly(typeof(object).Assembly) })
            .AddSyntaxTrees(tree);
var model = compilation.GetSemanticModel(tree);
var nameInfo = model.GetSymbolInfo(root.Usings[0].Name);
var systemSymbol = (INamespaceSymbol)nameInfo.Symbol;
foreach (var ns in systemSymbol.GetNamespaceMembers())
{
   Console.WriteLine(ns.Name);
}
于 2011-09-28T05:21:50.563 回答