Анализ источника C # с помощью Irony - PullRequest
0 голосов
/ 28 сентября 2011

Это то, что я и моя команда решили сделать для нашего школьного проекта. Ну, на самом деле мы еще не решили, как анализировать исходные файлы C #.

То, чего мы стремимся достичь, это выполнить полный анализ исходного файла C # и подготовить отчет. В котором отчет будет содержать то, что происходит в кодах.

Отчет должен содержать только:

  • строковые литералы
  • имена методов
  • имена переменных
  • имена полей
  • и т.д.

Я отвечаю за изучение этой библиотеки Иронии. Если честно, я не знаю лучшего способа сортировки данных в чистый читаемый отчет. Я использую класс грамматики C #, упакованный с почтовым индексом.

Есть ли какой-нибудь шаг, где я могу правильно определить дочерние узлы каждого узла? (например: использование директив, объявление пространства имен, объявление класса и т. д., тело метода)

Буду очень признателен за любую помощь или совет. Спасибо.

РЕДАКТИРОВАТЬ: Извините, я забыл сказать, что мы должны проанализировать вызовы метода тоже.

Ответы [ 2 ]

1 голос
/ 28 сентября 2011

Ваша главная цель - овладеть основами формальных языков.Хороший запуск может быть найден здесь .В этой статье описывается способ использования 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); }
        }
1 голос
/ 28 сентября 2011

Я не уверен, что это то, что вам нужно, но вы можете использовать пространства имен CodeDom и CodeDom.Compiler для компиляции кода C #, а затем анализировать результаты с помощью Reflection, что-то вроде:

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