парсинг и генерация кода с помощью директивы препроцессора - PullRequest
0 голосов
/ 27 августа 2018

Я экспериментирую с roslyn, анализирую и генерирую код на C #. Я пытаюсь выяснить, как метод CSharpSyntaxTree.ParseText обрабатывает символы препроцессора.

Вот мой метод испытаний. Он принимает код C # в виде строки, извлекает операторы using и возвращает новую строку с этими операторами using с учетом директив препроцессора.

private static string Process(string input, string[] preprocessorSymbols)
{
    var options = CSharpParseOptions.Default.WithPreprocessorSymbols(preprocessorSymbols);
    var syntaxTree = CSharpSyntaxTree.ParseText(input, options);
    var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
    var usings = compilationUnit.Usings.ToArray();
    var cs = SyntaxFactory.CompilationUnit()
            .AddUsings(usings)
            .NormalizeWhitespace();
    var result = cs.ToString();
    return result;
}

При подаче этого метода со следующим вводом, он работает как ожидалось:

var input = "using MyUsing1;\r\nusing MyUsing2;";
string result = Process(input, new[] { "" });
Assert.AreEqual("using MyUsing1;\r\nusing MyUsing2;", result);

Когда добавляется директива препроцессора, но не передается указанная директива парсеру, результат остается ожидаемым (условный оператор using удаляется):

var input =
    "using MyUsing1;\r\n" +
    "#if CONDITIONAL\r\n" +
    "using MyUsing2;\r\n" +
    "#endif";
string result = Process(input, new[] { "" });
Assert.AreEqual("using MyUsing1;", result);

Однако, добавив директиву препроцессора CONDITIONAL к CSharpParseOptions, я получаю странный результат

var input = 
    "using MyUsing1;\r\n" +
    "#if CONDITIONAL\r\n" +
    "using MyUsing2;\r\n" +
    "#endif";
string result = Process(input, new[] { "CONDITIONAL" });
Assert.AreEqual("using MyUsing1;\r\nusing MyUsing2;", result); // fails??

Фактическое значение возврата "using MyUsing1;\r\n#if CONDITIONAL\r\nusing MyUsing2;". Часть #if CONDITIONAL сохраняется, а #endif удаляется.

Это ошибка, или я что-то не так делаю?

1 Ответ

0 голосов
/ 27 августа 2018

Чтобы понять это поведение, я добавил еще один контрольный пример:

var input =
    "using MyUsing1;\r\n" +
    "#if CONDITIONAL\r\n" +
    "using MyUsing2;\r\n" +
    "#endif" +
    "using MyUsing3;\r\n";
string result = Process(input, new[] { "CONDITIONAL" });

И в этом случае, #if и #endif сохраняются.

Если вы остановитесь в отладчике и посмотрите на массив usings, кажется, что каждый UsingDirectiveSyntax знает как минимальный диапазон символов для оператора using (Span), так и "более широкий" диапазон символы из исходного потока (FullSpan), который включает в себя такие вещи, как, в данном случае, директива #if.

Копая немного глубже, документы ссылаются на предыдущий код, такой как директива preproc, как на "ведущие мелочи", и он присоединен к узлу использования как дочерний.

Интересно, что если вы передадите .AddUsings() только одну из директив using, то, похоже, пропущены начальные мелочи; но если вы дадите ему массив, кратный UsingDirectiveSyntax с, то для каждого, кроме первого, он будет содержать начальные пустяки. (Это, вероятно, не совсем верно; я работаю только из наблюдений черного ящика.)

Я не собираюсь притворяться, что понимаю причину такого поведения. В результате многие биты кода, которые выглядят нормально - как ваш пример - будут вызывать тревожные результаты. (Если вы передадите new[] {usings[0], usings[2], usings[1]}, вы получите еще худший результат, с #endif перед #if. Но ... вы знаете ... Я думаю, зачем вам это делать?)

Так что, если вы хотите использовать эти инструменты для генерации исходного кода, который будет возвращаться в полный конвейер сборки, вы можете увидеть это как ошибку (или, по крайней мере, странное поведение, которое может легко стать источником ошибок). Если есть предполагаемое использование, которое поможет вам избежать этого, я не могу найти прямую документацию об этом. В этом случае вы можете удалить мелочи из usings перед добавлением их в вывод; но в других случаях это может привести к тому, что вы захотите сохранить то, что я думаю.

...