开发者

Analysing C# source with Irony

开发者 https://www.devze.com 2023-04-08 17:24 出处:网络
This is what my team and I chose to do for our school project. Well, actually we haven\'t decided on how to parse the C# source files yet.

This is what my team and I chose to do for our school project. Well, actually we haven't decided on how to parse the C# source files yet.

What we are aiming to achieve is, perform a full analysis on a C# source file, and produce up a report. In which the report is going to contain stuff th开发者_开发百科at happening in the codes.

The report only has to contain:

  • string literals
  • method names
  • variable names
  • field names
  • etc

I'm in charge of looking into this Irony library. To be honest, I don't know the best way to sort the data out into a clean readable report. I am using the C# grammar class packed with the zip.

Is there any step where I can properly identify each node children? (eg: using directives, namespace declaration, class declaration etc, method body)

Any help or advice would be very much appreciated. Thanks.

EDIT: Sorry I forgot to say we need to analysis the method calls too.


Your main goal is to master the basics of formal languages. A good start-up might be found here. This article describes the way to use Irony on the sample of a grammar of a simple numeric calculator.

Suppose you want to parse a certain file containing C# code the path to which you know:

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();
        }
    }

And here is the traversal method itself with counting some info in the nodes. Actually this code counts the number of statements in every method of the class. If you have any question you are always welcome to ask me

 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); }
        }


I'm not sure this is what you need but you could use the CodeDom and CodeDom.Compiler namespaces to compile the C# code, and than analyze the results using Reflection, something like:

        // 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
        }

Update: In VS2015 you could use the new C# compiler (AKA Roslyn) to do the same, for example:

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);
}
0

精彩评论

暂无评论...
验证码 换一张
取 消