DocumentEditor InsertBefore似乎无法在文档中找到节点

时间:2019-04-21 13:04:23

标签: c# visual-studio roslyn

我正在使用roslyn开发Visual Studio的代码重构。

我正在使用DocumentEditor类删除一些SyntaxNode,在删除它们之后,我将插入一些新的。我使用的是语法节点,我知道我不会删除它作为插入新SyntaxNodes的参考。但是当我使用编辑器时似乎找不到它

foreach (var nodeToRemove in nodesToRemove)
{
    editor.RemoveNode(directive);
}

editor.InsertBefore(untouchedNode, newNodesToAdd); // No exception here, only when getting the new document

要调试它,我试图找到参考节点,然后像编辑器一样进行

var ns = editor.OriginalRoot.DescendantNodesAndSelf().OfType<NamespaceDeclarationSyntax>().FirstOrDefault(); // ns is not null
var IshouldExist = editor.OriginalRoot.GetCurrentNode(ns); //it is null, weird

我不知道为什么GetCurrentNode()在这里返回null。

在stacktrace中,引起我注意的是,插入是在更改后的文档中完成的,我不认为这应该是一个问题,因为如果我不尝试插入新节点,那么我正在使用的SytaxNode作为参考仍在更改后的文档中。

And throws an exception with the following stacktrace:
System.InvalidCastException : Unable to cast object of type 'Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax' to type 'Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax'.
   at System.Linq.Enumerable.<CastIterator>d__97`1.MoveNext()
   at System.Collections.Generic.List`1.InsertRange(Int32 index,IEnumerable`1 collection)
   at Microsoft.CodeAnalysis.SyntaxList`1.InsertRange(Int32 index,IEnumerable`1 nodes)
   at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.NodeListEditor.VisitList[TNode](SyntaxList`1 list)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitCompilationUnit(CompilationUnitSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.BaseListEditor.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.NodeListEditor.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.InsertNodeInList(SyntaxNode root,SyntaxNode nodeInList,IEnumerable`1 nodesToInsert,Boolean insertBefore)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode.InsertNodesInListCore(SyntaxNode nodeInList,IEnumerable`1 nodesToInsert,Boolean insertBefore)
   at Microsoft.CodeAnalysis.SyntaxNodeExtensions.InsertNodesBefore[TRoot](TRoot root,SyntaxNode nodeInList,IEnumerable`1 newNodes)
   at Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpSyntaxGenerator.InsertNodesBeforeInternal(SyntaxNode root,SyntaxNode declaration,IEnumerable`1 newDeclarations)
   at Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpSyntaxGenerator.<>c__DisplayClass183_0.<InsertNodesBefore>b__0(SyntaxNode r)
   at Microsoft.CodeAnalysis.Editing.SyntaxGenerator.PreserveTrivia[TNode](TNode node,Func`2 nodeChanger)
   at Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpSyntaxGenerator.InsertNodesBefore(SyntaxNode root,SyntaxNode declaration,IEnumerable`1 newDeclarations)
   at Microsoft.CodeAnalysis.Editing.SyntaxEditor.InsertChange.Apply(SyntaxNode root,SyntaxGenerator generator)
   at Microsoft.CodeAnalysis.Editing.SyntaxEditor.GetChangedRoot()
   at Microsoft.CodeAnalysis.Editing.DocumentEditor.GetChangedDocument()

我在做什么错了?

1 个答案:

答案 0 :(得分:0)

我今天早些时候遇到了类似的问题。我没有使用DocumentEditor,而是在Document对象本身上以更“原始”的方式执行操作,但是,当我尝试通过自定义谓词对Using Directives进行排序时,我注意到如果清除了所有旧的在插入新用法之前使用Directives,使用* .InsertBefore或* .InsertAfter,当引用节点不是 a Using Directive时,整个系统崩溃。 (似乎在内部,它可能试图将引用节点视为它是UsingDirectiveSyntax对象,然后无法对其进行比较,但这只是纯粹的推测,基于异常详细信息...)

如果您正在使用的文档对象不再具有Using语句,则需要考虑使用另一种方法来插入using语句:

List<UsingDirectiveSyntax> myNewUsingList = /* Construct Using List somehow */
NameSpaceDeclarationSyntax newNameSpace = myOldNameSpace.AddUsings(myUsingList);
CompilationUnitSyntax newCompileRootSyntax = (documentRoot as CompilationUnitSyntax).ReplaceNode(myOldNameSpace, newNameSpace);
return document.WithSyntaxRoot(newCompileRootSyntax);

这与使用DocumentEditor实例时使用的语义不同,但是由于我对所修改的当前文档的节点遍历不甚了解,因此我不知道将这种单独的方法转换为您的需求。

这是给我写的令人讨厌的代码,因为这种方法适用于不可变的对象,因此上述所有上述操作都返回了一个新的被操纵对象实例,并通过实现的方法参数进行了直接修改。上面的代码部分

document.WithSyntaxRoot(/*variable*/);

创建一个全新的Document()对象,其内部数据表示从旧文档到新文档的更改,以及您用先前的代码引起的所有替换,插入,删除等。

基于一些初步的基于DocumentEditor的方法,使用VS 2019的SDK工具创建CodeRefactoring项目,DocumentEditor的方法可能更像这样:

DocumentEditor ed = await DocumentEditor.CreateAsync(document, cancellationToken);
NamespaceDeclarationSyntax nameSpace = ed.GetChangedRoot()
  .DescendantNodes()
  .FirstOrDefault(t => t.GetType() == typeof(NamespaceDeclarationSyntax)) as NamespaceDeclarationSyntax;

if (nameSpace != null)
{
  NamespaceDeclarationSyntax modifiedWithMyUsings = nameSpace.AddUsings(myUsingLists);
  ed.ReplaceNode(nameSpace, modifiedWithMyUsings);
}