Я пишу систему для обработки фрагментов, написанных как единичные тесты для Noda Time, поэтому я могу включить фрагменты в документацию. У меня есть первый проход, но я хотел привести в порядок код. Одна из вещей, которые это необходимо сделать при обработке фрагмента, - это то, что из директив using
действительно требуется для этого фрагмента. (В одном исходном файле может быть несколько фрагментов, но каждый фрагмент будет отображаться отдельно в документации - я не хочу, чтобы импорт из одного фрагмента повлиял на другой.)
Рабочий код имеет дело с экземплярами Document
- я создаю отдельный Document
для каждого фрагмента, содержащий единственный метод и весь потенциальный импорт, добавляя его в проект, а затем удаляю ненужные директивы using
следующим образом:
private async static Task<Document> RemoveUnusedImportsAsync(Document document)
{
var compilation = await document.Project.GetCompilationAsync();
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var unusedImportNodes = compilation.GetDiagnostics()
.Where(d => d.Id == "CS8019")
.Where(d => d.Location?.SourceTree == tree)
.Select(d => root.FindNode(d.Location.SourceSpan))
.ToList();
return document.WithSyntaxRoot(
root.RemoveNodes(unusedImportNodes, SyntaxRemoveOptions.KeepNoTrivia));
}
С тех пор я узнал, что при работе с документом я мог бы использовать IOrganizeImportsService
, но я бы просто написал его как Script
, так как он чувствует себя намного чище разными способами.
Создание script очень просто, поэтому я хотел бы просто проанализировать, что для неиспользуемых импортов (после некоторых предыдущих шагов очистки). Здесь код, который, как я надеялся, будет работать для script:
private static Script RemoveUnusedImports(Script script)
{
var compilation = script.GetCompilation();
var tree = compilation.SyntaxTrees.Single();
var root = tree.GetRoot();
var unusedImportNodes = compilation.GetDiagnostics()
.Where(d => d.Id == "CS8019")
.Where(d => d.Location?.SourceTree == tree)
.Select(d => root.FindNode(d.Location.SourceSpan))
.ToList();
var newRoot = root.RemoveNodes(unusedImportNodes, SyntaxRemoveOptions.KeepNoTrivia);
return CSharpScript.Create(newRoot.ToFullString(), script.Options);
}
К сожалению, это вообще не находит никакой диагностики - они просто не создаются в компиляции: (
Вот небольшое примерное приложение, демонстрирующее, что:
using System;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
class Program
{
static void Main(string[] args)
{
string text = @"
using System;
using System.Collections.Generic;
Console.WriteLine(""I only need to use System"");";
Script script = CSharpScript.Create(text);
// Not sure whether this *should* be required, but it doesn't help...
script.Compile();
var compilation = script.GetCompilation();
foreach (var d in compilation.GetDiagnostics())
{
Console.WriteLine($"{d.Id}: {d.GetMessage()}");
}
}
}
Необходимый пакет: Microsoft.CodeAnalysis.CSharp.Scripting(например, v2.1.0)
Это не производит вывод: (
Я предполагаю, что это предназначено, потому что сценарии обычно имеют разные варианты использования. Но есть ли способ дать больше возможностей для диагностики сценариев? Или существует альтернативный способ обнаружения неиспользуемых импортов в Script
? Если нет, я вернусь к моему подходу, основанному на Document
, что было бы очень жалко, поскольку все остальное, по-видимому, прекрасно работает со сценариями...