我正在尝试修复以下VBA语句(转换一些旧代码只是为了好玩并学习Roslyn,而不是寻找任何完美的东西)来删除Set
关键字,这样它就是一个有效的VB.NET语句:
Set f = New Foo()
当我通过Syntax Visualizer查看它时,我发现它变成了尾随琐事。
我正在试图弄清楚如何使用查询找到它。我尝试了几种方法,但以下所有方法都是空的:
var attempt1 = root.DescendantTokens().Where(t=>t.IsKind(SyntaxKind.SkippedTokensTrivia));
var attempt2 = root.DescendantTokens().Where(t => t.IsKind(SyntaxKind.SetKeyword));
var attempt3 = root.DescendantTrivia().Where(t => t.IsKind(SyntaxKind.SetKeyword));
var attempt4 = root.DescendantNodes()
.OfType<EmptyStatementSyntax>()
.Where(e => e.DescendantTokens().Any(t => t.IsKeyword()));
(是的,我正在使用C#来处理VisualBasicSyntaxTree
)
我似乎无法找到可视化工具中出现的SetKeyword
令牌,所以我想也许它正在做一些更加繁重的工作来拼凑它的真实含义(结构性琐事是什么意思? )。我在文档中读到了一些内容,提到编译器可以选择用几种不同的方式来表示它,所以我认为这可能就是这里发生的事情。
查询只是我尝试的第一件事,但实际上我有一个SyntaxRewriter
我正在使用访问代码来查找和修复所有这些问题(我已经能够修复丢失的括号例如,ArgumentLists)但在这种情况下,我似乎无法弄清楚要覆盖哪种Visit方法。
再次,1)如何从根查询这些,以及2)从重写器中选择的最佳覆盖。我一直在键盘上敲打我的脸两天,这会增加我有颅骨/直肠插入时刻的可能性,我需要你们中的一个善良的灵魂把我拉出来。
干杯! 布赖恩
编辑:修复了查询尝试1中的拼写错误
答案 0 :(得分:2)
因此,当编译器达到错误条件时,它将跳过所有令牌,直到它可以恢复并继续解析的下一个点(在这种情况下为行的结尾)。表示此错误条件的节点是EmptyStatement
,其尾随语法为trivia,其中包含文本的其余部分作为已解析的标记。
因此,如果您要重写节点,则需要重写EmptyStatements
。但是你不想只写任何空语句,只写那些带有“BC30807”诊断代码的语句。
public override SyntaxNode VisitEmptyStatement(EmptyStatementSyntax node)
{
var diagnostic = GetLetSetDiagnostic(node);
if (diagnostic == null)
return base.VisitEmptyStatement(node);
return RewriteLetSetStatement(node);
}
private Diagnostic GetLetSetDiagnostic(EmptyStatementSyntax node)
{
//'Let' and 'Set' assignment statements are no longer supported.
const string code = "BC30807";
return node.GetDiagnostics().SingleOrDefault(n => n.Id == code);
}
RewriteLetSetStatement()
方法的实现对我来说有点神秘,我不确定它是如何有效地利用编译器服务实现的,我不认为这是一个用例它涵盖得很好。琐事保留了已解析的代币,但是你可以用这些代币AFAIK做很多事情。
理想情况下,我们只想忽略令牌中的Set
令牌并将其重新放回解析器中进行重新解析。据我所知,这是不可能的,我们只能从文本中解析。
所以,我想下一个要做的最好的事情就是获取文本,重写它以删除Set
并再次解析文本。
private SyntaxNode RewriteLetSetStatement(EmptyStatementSyntax node)
{
var letSetTokens = node.GetTrailingTrivia()
.Where(triv => triv.IsKind(SyntaxKind.SkippedTokensTrivia))
.SelectMany(triv => triv.GetStructure().ChildTokens())
.TakeWhile(tok => new[] {SyntaxKind.LetKeyword, SyntaxKind.SetKeyword}
.Contains(tok.VisualBasicKind()));
var span = new RelativeTextSpan(node.FullSpan);
var newText = node.GetText().WithChanges(
// replacement spans must be relative to the text
letSetTokens.Select(tok => new TextChange(span.GetSpan(tok.Span), ""))
);
return SyntaxFactory.ParseExecutableStatement(newText.ToString());
}
private class RelativeTextSpan(private TextSpan span)
{
public TextSpan GetSpan(TextSpan token)
{
return new TextSpan(token.Start - span.Start, token.Length);
}
}