Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
7c6cbead
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
7c6cbead
编写于
8月 06, 2020
作者:
A
Allison Chou
提交者:
GitHub
8月 06, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Replace property with method: Add more support for null catching (#46144)
上级
7628d776
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
59 addition
and
31 deletion
+59
-31
src/Analyzers/CSharp/CodeFixes/RemoveConfusingSuppression/CSharpRemoveConfusingSuppressionCodeFixProvider.cs
...ession/CSharpRemoveConfusingSuppressionCodeFixProvider.cs
+1
-1
src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs
...ement/CSharpReverseForStatementCodeRefactoringProvider.cs
+1
-1
src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs
...mberFromParameterCodeRefactoringProviderMemberCreation.cs
+1
-1
src/Features/Core/Portable/MakeClassAbstract/AbstractMakeClassAbstractCodeFixProvider.cs
...ClassAbstract/AbstractMakeClassAbstractCodeFixProvider.cs
+2
-1
src/Features/Core/Portable/ReplaceMethodWithProperty/AbstractReplaceMethodWithPropertyService.cs
...dWithProperty/AbstractReplaceMethodWithPropertyService.cs
+4
-2
src/Features/Core/Portable/ReplaceMethodWithProperty/ReplaceMethodWithPropertyCodeRefactoringProvider.cs
...perty/ReplaceMethodWithPropertyCodeRefactoringProvider.cs
+28
-16
src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs
...yWithMethods/AbstractReplacePropertyWithMethodsService.cs
+6
-2
src/Features/Core/Portable/ReplacePropertyWithMethods/ReplacePropertyWithMethodsCodeRefactoringProvider.cs
...hods/ReplacePropertyWithMethodsCodeRefactoringProvider.cs
+1
-1
src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs
...UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs
+1
-1
src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs
src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs
+14
-5
未找到文件。
src/Analyzers/CSharp/CodeFixes/RemoveConfusingSuppression/CSharpRemoveConfusingSuppressionCodeFixProvider.cs
浏览文件 @
7c6cbead
...
...
@@ -68,7 +68,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
bool
negate
,
CancellationToken
cancellationToken
)
{
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
document
.
Get
Required
SyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
editor
=
new
SyntaxEditor
(
root
,
document
.
Project
.
Solution
.
Workspace
);
var
generator
=
editor
.
Generator
;
var
generatorInternal
=
document
.
GetRequiredLanguageService
<
SyntaxGeneratorInternal
>();
...
...
src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs
浏览文件 @
7c6cbead
...
...
@@ -264,7 +264,7 @@ private static bool IsVariableReference(VariableDeclaratorSyntax variable, Expre
var
condition
=
(
BinaryExpressionSyntax
)
forStatement
.
Condition
!;
var
after
=
forStatement
.
Incrementors
[
0
];
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
document
.
Get
Required
SyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
editor
=
new
SyntaxEditor
(
root
,
document
.
Project
.
Solution
.
Workspace
);
var
generator
=
editor
.
Generator
;
...
...
src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs
浏览文件 @
7c6cbead
...
...
@@ -420,7 +420,7 @@ private static string GenerateUniqueName(IParameterSymbol parameter, ImmutableAr
CancellationToken
cancellationToken
)
{
var
workspace
=
document
.
Project
.
Solution
.
Workspace
;
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
document
.
Get
Required
SyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
editor
=
new
SyntaxEditor
(
root
,
workspace
);
var
generator
=
editor
.
Generator
;
var
options
=
await
document
.
GetOptionsAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
...
...
src/Features/Core/Portable/MakeClassAbstract/AbstractMakeClassAbstractCodeFixProvider.cs
浏览文件 @
7c6cbead
...
...
@@ -6,6 +6,7 @@
using
System
;
using
System.Collections.Immutable
;
using
System.Diagnostics.CodeAnalysis
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.CodeActions
;
...
...
@@ -18,7 +19,7 @@ namespace Microsoft.CodeAnalysis.MakeClassAbstract
internal
abstract
class
AbstractMakeClassAbstractCodeFixProvider
<
TClassDeclarationSyntax
>
:
SyntaxEditorBasedCodeFixProvider
where
TClassDeclarationSyntax
:
SyntaxNode
{
protected
abstract
bool
IsValidRefactoringContext
(
SyntaxNode
?
node
,
out
TClassDeclarationSyntax
?
classDeclaration
);
protected
abstract
bool
IsValidRefactoringContext
(
SyntaxNode
?
node
,
[
NotNullWhen
(
true
)]
out
TClassDeclarationSyntax
?
classDeclaration
);
internal
sealed
override
CodeFixCategory
CodeFixCategory
=>
CodeFixCategory
.
Compile
;
...
...
src/Features/Core/Portable/ReplaceMethodWithProperty/AbstractReplaceMethodWithPropertyService.cs
浏览文件 @
7c6cbead
...
...
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#
nullable
enable
using
System.Linq
;
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.CodeRefactorings
;
...
...
@@ -12,11 +14,11 @@ namespace Microsoft.CodeAnalysis.ReplaceMethodWithProperty
internal
abstract
class
AbstractReplaceMethodWithPropertyService
<
TMethodDeclarationSyntax
>
where
TMethodDeclarationSyntax
:
SyntaxNode
{
#pragma warning disable CA1822 // Mark members as static - implements interface method for sub-types.
public
async
Task
<
SyntaxNode
>
GetMethodDeclarationAsync
(
CodeRefactoringContext
context
)
public
async
Task
<
SyntaxNode
?
>
GetMethodDeclarationAsync
(
CodeRefactoringContext
context
)
#pragma warning restore CA1822 // Mark members as static
=>
await
context
.
TryGetRelevantNodeAsync
<
TMethodDeclarationSyntax
>().
ConfigureAwait
(
false
);
protected
static
string
GetWarning
(
GetAndSetMethods
getAndSetMethods
)
protected
static
string
?
GetWarning
(
GetAndSetMethods
getAndSetMethods
)
{
if
(
OverridesMetadataSymbol
(
getAndSetMethods
.
GetMethod
)
||
OverridesMetadataSymbol
(
getAndSetMethods
.
SetMethod
))
...
...
src/Features/Core/Portable/ReplaceMethodWithProperty/ReplaceMethodWithPropertyCodeRefactoringProvider.cs
浏览文件 @
7c6cbead
...
...
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#
nullable
enable
using
System
;
using
System.Collections.Generic
;
using
System.Collections.Immutable
;
...
...
@@ -54,9 +56,9 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
var
generator
=
SyntaxGenerator
.
GetGenerator
(
document
);
var
methodName
=
generator
.
GetName
(
methodDeclaration
);
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
methodSymbol
=
semanticModel
.
GetDeclaredSymbol
(
methodDeclaration
)
as
IMethodSymbol
;
if
(
!
IsValidGetMethod
(
methodSymbol
))
var
semanticModel
=
await
document
.
Get
Required
SemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
if
(!(
semanticModel
.
GetDeclaredSymbol
(
methodDeclaration
)
is
IMethodSymbol
methodSymbol
)
||
!
IsValidGetMethod
(
methodSymbol
))
{
return
;
}
...
...
@@ -161,7 +163,7 @@ private static bool IsValidSetMethod(IMethodSymbol setMethod)
string
propertyName
,
bool
nameChanged
,
IMethodSymbol
getMethod
,
IMethodSymbol
setMethod
,
IMethodSymbol
?
setMethod
,
CancellationToken
cancellationToken
)
{
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
...
...
@@ -219,10 +221,10 @@ private static async Task<Solution> UpdateReferencesAsync(Solution updatedSoluti
IEnumerable
<
ReferenceLocation
>
setReferences
,
CancellationToken
cancellationToken
)
{
var
root
=
await
originalDocument
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
originalDocument
.
Get
Required
SyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
editor
=
new
SyntaxEditor
(
root
,
originalDocument
.
Project
.
Solution
.
Workspace
);
var
service
=
originalDocument
.
GetLanguageService
<
IReplaceMethodWithPropertyService
>();
var
service
=
originalDocument
.
Get
Required
LanguageService
<
IReplaceMethodWithPropertyService
>();
ReplaceGetReferences
(
propertyName
,
nameChanged
,
getReferences
,
root
,
editor
,
service
,
cancellationToken
);
ReplaceSetReferences
(
propertyName
,
nameChanged
,
setReferences
,
root
,
editor
,
service
,
cancellationToken
);
...
...
@@ -247,6 +249,11 @@ private static async Task<Solution> UpdateReferencesAsync(Solution updatedSoluti
var
location
=
referenceLocation
.
Location
;
var
nameToken
=
root
.
FindToken
(
location
.
SourceSpan
.
Start
);
if
(
nameToken
.
Parent
==
null
)
{
Debug
.
Fail
(
$"Parent node of
{
nameToken
}
is null."
);
continue
;
}
if
(
referenceLocation
.
IsImplicit
)
{
...
...
@@ -277,6 +284,11 @@ private static async Task<Solution> UpdateReferencesAsync(Solution updatedSoluti
var
location
=
referenceLocation
.
Location
;
var
nameToken
=
root
.
FindToken
(
location
.
SourceSpan
.
Start
);
if
(
nameToken
.
Parent
==
null
)
{
Debug
.
Fail
(
$"Parent node of
{
nameToken
}
is null."
);
continue
;
}
if
(
referenceLocation
.
IsImplicit
)
{
...
...
@@ -330,19 +342,19 @@ private static async Task<Solution> UpdateReferencesAsync(Solution updatedSoluti
bool
updateSetMethod
,
CancellationToken
cancellationToken
)
{
var
updatedDocument
=
updatedSolution
.
GetDocument
(
documentId
);
var
compilation
=
await
updatedDocument
.
Project
.
GetCompilationAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
updatedDocument
=
updatedSolution
.
Get
Required
Document
(
documentId
);
var
compilation
=
await
updatedDocument
.
Project
.
Get
Required
CompilationAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
// We've already gone and updated all references. So now re-resolve all the definitions
// in the current compilation to find their updated location.
var
getSetPairs
=
await
GetGetSetPairsAsync
(
updatedSolution
,
compilation
,
documentId
,
originalGetDefinitions
,
updateSetMethod
,
cancellationToken
).
ConfigureAwait
(
false
);
var
service
=
updatedDocument
.
GetLanguageService
<
IReplaceMethodWithPropertyService
>();
var
service
=
updatedDocument
.
Get
Required
LanguageService
<
IReplaceMethodWithPropertyService
>();
var
semanticModel
=
await
updatedDocument
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
syntaxTree
=
await
updatedDocument
.
GetSyntaxTreeAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
updatedDocument
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
semanticModel
=
await
updatedDocument
.
Get
Required
SemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
syntaxTree
=
await
updatedDocument
.
Get
Required
SyntaxTreeAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
updatedDocument
.
Get
Required
SyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
editor
=
new
SyntaxEditor
(
root
,
updatedSolution
.
Workspace
);
...
...
@@ -391,7 +403,7 @@ private static async Task<Solution> UpdateReferencesAsync(Solution updatedSoluti
cancellationToken
.
ThrowIfCancellationRequested
();
var
getMethod
=
GetSymbolInCurrentCompilation
(
compilation
,
originalDefinition
,
cancellationToken
);
if
(
IsValidGetMethod
(
getMethod
))
if
(
getMethod
!=
null
&&
IsValidGetMethod
(
getMethod
))
{
var
setMethod
=
updateSetMethod
?
FindSetMethod
(
getMethod
)
:
null
;
var
getMethodDeclaration
=
await
GetMethodDeclarationAsync
(
getMethod
,
cancellationToken
).
ConfigureAwait
(
false
);
...
...
@@ -407,13 +419,13 @@ private static async Task<Solution> UpdateReferencesAsync(Solution updatedSoluti
return
result
.
ToImmutable
();
}
private
static
TSymbol
GetSymbolInCurrentCompilation
<
TSymbol
>(
Compilation
compilation
,
TSymbol
originalDefinition
,
CancellationToken
cancellationToken
)
private
static
TSymbol
?
GetSymbolInCurrentCompilation
<
TSymbol
>(
Compilation
compilation
,
TSymbol
originalDefinition
,
CancellationToken
cancellationToken
)
where
TSymbol
:
class
,
ISymbol
{
return
originalDefinition
.
GetSymbolKey
(
cancellationToken
).
Resolve
(
compilation
,
cancellationToken
:
cancellationToken
).
GetAnySymbol
()
as
TSymbol
;
}
private
static
async
Task
<
SyntaxNode
>
GetMethodDeclarationAsync
(
IMethodSymbol
method
,
CancellationToken
cancellationToken
)
private
static
async
Task
<
SyntaxNode
?>
GetMethodDeclarationAsync
(
IMethodSymbol
?
method
,
CancellationToken
cancellationToken
)
{
if
(
method
==
null
)
{
...
...
@@ -454,7 +466,7 @@ private static async Task<SyntaxNode> GetMethodDeclarationAsync(IMethodSymbol me
}
#pragma warning disable IDE0060 // Remove unused parameter - Method not completely implemented.
private
static
string
GetDefinitionIssues
(
IEnumerable
<
ReferencedSymbol
>
getMethodReferences
)
private
static
string
?
GetDefinitionIssues
(
IEnumerable
<
ReferencedSymbol
>
getMethodReferences
)
#pragma warning restore IDE0060 // Remove unused parameter
{
// TODO: add things to be concerned about here. For example:
...
...
src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs
浏览文件 @
7c6cbead
...
...
@@ -5,7 +5,6 @@
#
nullable
enable
using
System
;
using
System.Collections.Generic
;
using
System.Collections.Immutable
;
using
System.Threading
;
using
System.Threading.Tasks
;
...
...
@@ -120,6 +119,8 @@ protected static SyntaxNode GetFieldReference(SyntaxGenerator generator, IFieldS
{
_expression
=
(
TExpressionSyntax
)
_expression
.
Parent
!;
}
Contract
.
ThrowIfNull
(
_expression
.
Parent
,
$"Parent of
{
_expression
}
is null."
);
}
// To avoid allocating lambdas each time we hit a reference, we instead
...
...
@@ -258,7 +259,8 @@ public void Do()
_identifierName
.
WithoutTrivia
(),
readExpression
);
_editor
.
ReplaceNode
(
declarator
,
newDeclarator
);
// We know declarator isn't null due to the earlier call to IsInferredAnonymousObjectMemberDeclarator
_editor
.
ReplaceNode
(
declarator
!,
newDeclarator
);
}
else
if
(
_syntaxFacts
.
IsRightSideOfQualifiedName
(
_identifierName
))
{
...
...
@@ -286,6 +288,8 @@ private void ReplaceRead(bool keepTrivia, string? conflictMessage)
bool
keepTrivia
,
string
?
conflictMessage
)
{
Contract
.
ThrowIfNull
(
_expression
.
Parent
,
$"Parent of
{
_expression
}
is null."
);
// Call this overload so we can see this node after already replacing any
// references in the writing side of it.
_editor
.
ReplaceNode
(
...
...
src/Features/Core/Portable/ReplacePropertyWithMethods/ReplacePropertyWithMethodsCodeRefactoringProvider.cs
浏览文件 @
7c6cbead
...
...
@@ -349,7 +349,7 @@ private static bool HasAnyMatchingSetMethods(IPropertySymbol property, string na
var
service
=
updatedDocument
.
GetRequiredLanguageService
<
IReplacePropertyWithMethodsService
>();
var
root
=
await
updatedDocument
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
updatedDocument
.
Get
Required
SyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
editor
=
new
SyntaxEditor
(
root
,
updatedSolution
.
Workspace
);
...
...
src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs
浏览文件 @
7c6cbead
...
...
@@ -202,7 +202,7 @@ private async Task<Solution> ProcessResultAsync(CodeFixContext context, Diagnost
if
(
fieldDocument
==
propertyDocument
)
{
// Same file. Have to do this in a slightly complicated fashion.
var
declaratorTreeRoot
=
await
fieldDocument
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
declaratorTreeRoot
=
await
fieldDocument
.
Get
Required
SyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
editor
=
new
SyntaxEditor
(
declaratorTreeRoot
,
fieldDocument
.
Project
.
Solution
.
Workspace
);
editor
.
ReplaceNode
(
property
,
updatedProperty
);
...
...
src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs
浏览文件 @
7c6cbead
...
...
@@ -2,9 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#
nullable
enable
using
System
;
using
System.Collections.Generic
;
using
System.Diagnostics.CodeAnalysis
;
using
System.Linq
;
using
Roslyn.Utilities
;
namespace
Microsoft.CodeAnalysis.Editing
{
...
...
@@ -16,7 +20,7 @@ public class SyntaxEditor
private
readonly
SyntaxGenerator
_generator
;
private
readonly
List
<
Change
>
_changes
;
private
bool
_allowEditsOnLazilyCreatedTrackedNewNodes
;
private
HashSet
<
SyntaxNode
>
_lazyTrackedNewNodesOpt
;
private
HashSet
<
SyntaxNode
>
?
_lazyTrackedNewNodesOpt
;
/// <summary>
/// Creates a new <see cref="SyntaxEditor"/> instance.
...
...
@@ -40,7 +44,8 @@ internal SyntaxEditor(SyntaxNode root, SyntaxGenerator generator)
_changes
=
new
List
<
Change
>();
}
private
SyntaxNode
ApplyTrackingToNewNode
(
SyntaxNode
node
)
[
return
:
NotNullIfNotNull
(
"node"
)]
private
SyntaxNode
?
ApplyTrackingToNewNode
(
SyntaxNode
?
node
)
{
if
(
node
==
null
)
{
...
...
@@ -60,7 +65,8 @@ private IEnumerable<SyntaxNode> ApplyTrackingToNewNodes(IEnumerable<SyntaxNode>
{
foreach
(
var
node
in
nodes
)
{
yield
return
ApplyTrackingToNewNode
(
node
);
var
result
=
ApplyTrackingToNewNode
(
node
);
yield
return
result
;
}
}
...
...
@@ -295,15 +301,16 @@ public override SyntaxNode Apply(SyntaxNode root, SyntaxGenerator generator)
private
class
ReplaceChange
:
Change
{
private
readonly
Func
<
SyntaxNode
,
SyntaxGenerator
,
SyntaxNode
>
_modifier
;
private
readonly
Func
<
SyntaxNode
,
SyntaxGenerator
,
SyntaxNode
?
>
_modifier
;
private
readonly
SyntaxEditor
_editor
;
public
ReplaceChange
(
SyntaxNode
node
,
Func
<
SyntaxNode
,
SyntaxGenerator
,
SyntaxNode
>
modifier
,
Func
<
SyntaxNode
,
SyntaxGenerator
,
SyntaxNode
?
>
modifier
,
SyntaxEditor
editor
)
:
base
(
node
)
{
Contract
.
ThrowIfNull
(
node
,
"Passed in node is null."
);
_modifier
=
modifier
;
_editor
=
editor
;
}
...
...
@@ -313,6 +320,8 @@ public override SyntaxNode Apply(SyntaxNode root, SyntaxGenerator generator)
var
current
=
root
.
GetCurrentNode
(
this
.
Node
);
var
newNode
=
_modifier
(
current
,
generator
);
newNode
=
_editor
.
ApplyTrackingToNewNode
(
newNode
);
Contract
.
ThrowIfNull
(
current
,
$"GetCurrentNode returned null with the following node:
{
this
.
Node
}
"
);
return
generator
.
ReplaceNode
(
root
,
current
,
newNode
);
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录