提交 30964146 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #14816 from CyrusNajmabadi/useNullCoalescingExpression

Add feature that offers to convert from a conditional expression to CoalesceExpression.
...@@ -93,8 +93,10 @@ ...@@ -93,8 +93,10 @@
<AssemblyName>Roslyn.Services.Editor.CSharp.UnitTests</AssemblyName> <AssemblyName>Roslyn.Services.Editor.CSharp.UnitTests</AssemblyName>
<RoslynProjectType>UnitTest</RoslynProjectType> <RoslynProjectType>UnitTest</RoslynProjectType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
...@@ -278,6 +280,8 @@ ...@@ -278,6 +280,8 @@
<Compile Include="Formatting\Indentation\SmartTokenFormatterFormatTokenTests.cs" /> <Compile Include="Formatting\Indentation\SmartTokenFormatterFormatTokenTests.cs" />
<Compile Include="InlineDeclaration\CSharpInlineDeclarationTests_FixAllTests.cs" /> <Compile Include="InlineDeclaration\CSharpInlineDeclarationTests_FixAllTests.cs" />
<Compile Include="InlineDeclaration\CSharpInlineDeclarationTests.cs" /> <Compile Include="InlineDeclaration\CSharpInlineDeclarationTests.cs" />
<Compile Include="UseCoalesceExpression\UseCoalesceExpressionForNullableTests.cs" />
<Compile Include="UseCoalesceExpression\UseCoalesceExpressionTests.cs" />
<Compile Include="UsePatternMatching\CSharpIsAndCastCheckTests_FixAllTests.cs" /> <Compile Include="UsePatternMatching\CSharpIsAndCastCheckTests_FixAllTests.cs" />
<Compile Include="UsePatternMatching\CSharpIsAndCastCheckTests.cs" /> <Compile Include="UsePatternMatching\CSharpIsAndCastCheckTests.cs" />
<Compile Include="UsePatternMatching\CSharpAsAndNullCheckTests.cs" /> <Compile Include="UsePatternMatching\CSharpAsAndNullCheckTests.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.UseCoalesceExpression;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.UseCoalesceExpression;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseCoalesceExpression
{
public class UseCoalesceExpressionForNullableTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override Tuple<DiagnosticAnalyzer, CodeFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace)
{
return new Tuple<DiagnosticAnalyzer, CodeFixProvider>(
new CSharpUseCoalesceExpressionForNullableDiagnosticAnalyzer(),
new UseCoalesceExpressionForNullableCodeFixProvider());
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestOnLeft_Equals()
{
await TestAsync(
@"
using System;
class C
{
void M(int? x, int? y)
{
var z = [||]!x.HasValue ? y : x.Value;
}
}",
@"using System;
class C
{
void M(int? x, int? y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestOnLeft_NotEquals()
{
await TestAsync(
@"
using System;
class C
{
void M(int? x, int? y)
{
var z = [||]x.HasValue ? x.Value : y;
}
}",
@"using System;
class C
{
void M(int? x, int? y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestComplexExpression()
{
await TestAsync(
@"
using System;
class C
{
void M(int? x, int? y)
{
var z = [||]!(x + y).HasValue ? y : (x + y).Value;
}
}",
@"using System;
class C
{
void M(int? x, int? y)
{
var z = (x + y) ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestParens1()
{
await TestAsync(
@"
using System;
class C
{
void M(int? x, int? y)
{
var z = [||](x.HasValue) ? x.Value : y;
}
}",
@"using System;
class C
{
void M(int? x, int? y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestFixAll1()
{
await TestAsync(
@"
using System;
class C
{
void M(int? x, int? y)
{
var z1 = {|FixAllInDocument:x|}.HasValue ? x.Value : y;
var z2 = !x.HasValue ? y : x.Value;
}
}",
@"using System;
class C
{
void M(int? x, int? y)
{
var z1 = x ?? y;
var z2 = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestFixAll2()
{
await TestAsync(
@"
using System;
class C
{
void M(int? x, int? y, int? z)
{
var w = {|FixAllInDocument:x|}.HasValue ? x.Value : y.ToString(z.HasValue ? z.Value : y);
}
}",
@"using System;
class C
{
void M(int? x, int? y, int? z)
{
var w = x ?? y.ToString(z ?? y);
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestFixAll3()
{
await TestAsync(
@"
using System;
class C
{
void M(int? x, int? y, int? z)
{
var w = {|FixAllInDocument:x|}.HasValue ? x.Value : y.HasValue ? y.Value : z;
}
}",
@"using System;
class C
{
void M(int? x, int? y, int? z)
{
var w = x ?? y ?? z;
}
}");
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.UseCoalesceExpression;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.UseCoalesceExpression;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseCoalesceExpression
{
public class UseCoalesceExpressionTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override Tuple<DiagnosticAnalyzer, CodeFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace)
{
return new Tuple<DiagnosticAnalyzer, CodeFixProvider>(
new CSharpUseCoalesceExpressionDiagnosticAnalyzer(),
new UseCoalesceExpressionCodeFixProvider());
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestOnLeft_Equals()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z = [||]x == null ? y : x;
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestOnLeft_NotEquals()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z = [||]x != null ? x : y;
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestOnRight_Equals()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z = [||]null == x ? y : x;
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestOnRight_NotEquals()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z = [||]null != x ? x : y;
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestComplexExpression()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z = [||]x.ToString() == null ? y : x.ToString();
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z = x.ToString() ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestParens1()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z = [||](x == null) ? y : x;
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestParens2()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z = [||](x) == null ? y : x;
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestParens3()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z = [||]x == null ? y : (x);
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestParens4()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z = [||]x == null ? (y) : x;
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestFixAll1()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y)
{
var z1 = {|FixAllInDocument:x|} == null ? y : x;
var z2 = x != null ? x : y;
}
}",
@"using System;
class C
{
void M(string x, string y)
{
var z1 = x ?? y;
var z2 = x ?? y;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestFixAll2()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y, string z)
{
var w = {|FixAllInDocument:x|} != null ? x : y.ToString(z != null ? z : y);
}
}",
@"using System;
class C
{
void M(string x, string y, string z)
{
var w = x ?? y.ToString(z ?? y);
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public async Task TestFixAll3()
{
await TestAsync(
@"
using System;
class C
{
void M(string x, string y, string z)
{
var w = {|FixAllInDocument:x|} != null ? x : y != null ? y : z;
}
}",
@"using System;
class C
{
void M(string x, string y, string z)
{
var w = x ?? y ?? z;
}
}");
}
}
}
\ No newline at end of file
...@@ -84,6 +84,7 @@ public static class Features ...@@ -84,6 +84,7 @@ public static class Features
public const string CodeActionsSpellcheck = "CodeActions.Spellcheck"; public const string CodeActionsSpellcheck = "CodeActions.Spellcheck";
public const string CodeActionsSuppression = "CodeActions.Suppression"; public const string CodeActionsSuppression = "CodeActions.Suppression";
public const string CodeActionsUseAutoProperty = "CodeActions.UseAutoProperty"; public const string CodeActionsUseAutoProperty = "CodeActions.UseAutoProperty";
public const string CodeActionsUseCoalesceExpression = "CodeActions.UseCoalesceExpression";
public const string CodeActionsUseExpressionBody = "CodeActions.UseExpressionBody"; public const string CodeActionsUseExpressionBody = "CodeActions.UseExpressionBody";
public const string CodeActionsUseImplicitType = "CodeActions.UseImplicitType"; public const string CodeActionsUseImplicitType = "CodeActions.UseImplicitType";
public const string CodeActionsUseExplicitType = "CodeActions.UseExplicitType"; public const string CodeActionsUseExplicitType = "CodeActions.UseExplicitType";
......
...@@ -504,6 +504,7 @@ ...@@ -504,6 +504,7 @@
<Compile Include="TextStructureNavigation\TextStructureNavigatorTests.vb" /> <Compile Include="TextStructureNavigation\TextStructureNavigatorTests.vb" />
<Compile Include="TodoComment\TodoCommentTests.vb" /> <Compile Include="TodoComment\TodoCommentTests.vb" />
<Compile Include="TypeInferrer\TypeInferrerTests.vb" /> <Compile Include="TypeInferrer\TypeInferrerTests.vb" />
<Compile Include="UseCoalesceExpression\UseCoalesceExpressionForNullableTests.vb" />
<Compile Include="UseObjectInitializer\UseObjectInitializerTests.vb" /> <Compile Include="UseObjectInitializer\UseObjectInitializerTests.vb" />
<Compile Include="Utilities\CodeSnippets.vb" /> <Compile Include="Utilities\CodeSnippets.vb" />
<Compile Include="Utils.vb" /> <Compile Include="Utils.vb" />
...@@ -601,6 +602,7 @@ ...@@ -601,6 +602,7 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="UseCoalesceExpression\UseCoalesceExpressionTests.vb" />
<None Include="PerfTests\BasicPerfGoldilocksTypingFullSolutionDiagnosticsThirdPartyAnalyzers.xml"> <None Include="PerfTests\BasicPerfGoldilocksTypingFullSolutionDiagnosticsThirdPartyAnalyzers.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<SubType>Designer</SubType> <SubType>Designer</SubType>
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.UseCoalesceExpression
Imports Microsoft.CodeAnalysis.VisualBasic.UseCoalesceExpression
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.UseCoalesceExpression
Public Class UseCoalesceExpressionForNullableTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider)
Return New Tuple(Of DiagnosticAnalyzer, CodeFixProvider)(
New VisualBasicUseCoalesceExpressionForNullableDiagnosticAnalyzer(),
New UseCoalesceExpressionForNullableCodeFixProvider())
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestOnLeft_Equals() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z = [||]If (Not x.HasValue, y, x.Value)
End Sub
End Class",
"Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestOnLeft_NotEquals() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z = [||]If(x.HasValue, x.Value, y)
End Sub
End Class",
"Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestComplexExpression() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z = [||]If (Not (x + y).HasValue, y, (x + y).Value)
End Sub
End Class",
"Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z = If((x + y), y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestParens1() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z = [||]If ((Not x.HasValue), y, x.Value)
End Sub
End Class",
"Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestFixAll1() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z1 = {|FixAllInDocument:If|} (Not x.HasValue, y, x.Value)
Dim z2 = If(x.HasValue, x.Value, y)
End Sub
End Class",
"Imports System
Class C
Sub M(x as Integer?, y as Integer?)
Dim z1 = If(x, y)
Dim z2 = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestFixAll2() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as Integer?, y as Integer?, z as Integer?)
dim w = {|FixAllInDocument:If|} (x.HasValue, x.Value, If(y.HasValue, y.Value, z))
End Sub
End Class",
"Imports System
Class C
Sub M(x as Integer?, y as Integer?, z as Integer?)
dim w = If(x, If(y, z))
End Sub
End Class")
End Function
End Class
End Namespace
\ No newline at end of file
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.UseCoalesceExpression
Imports Microsoft.CodeAnalysis.VisualBasic.UseCoalesceExpression
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.UseCoalesceExpression
Public Class UseCoalesceExpressionTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider)
Return New Tuple(Of DiagnosticAnalyzer, CodeFixProvider)(
New VisualBasicUseCoalesceExpressionDiagnosticAnalyzer(),
New UseCoalesceExpressionCodeFixProvider())
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestOnLeft_Equals() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z = [||]If (x Is Nothing, y, x)
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestOnLeft_NotEquals() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z = [||]If(x IsNot Nothing, x, y)
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestOnRight_Equals() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z = [||]If(Nothing Is x, y, x)
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestOnRight_NotEquals() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z = [||]If(Nothing IsNot x, x, y)
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestComplexExpression() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z = [||]If (x.ToString() is Nothing, y, x.ToString())
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z = If(x.ToString(), y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestParens1() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z = [||]If ((x Is Nothing), y, x)
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestParens2() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z = [||]If ((x) Is Nothing, y, x)
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestParens3() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z = [||]If (x Is Nothing, y, (x))
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestParens4() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z = [||]If (x Is Nothing, (y), x)
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestFixAll1() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string)
Dim z1 = {|FixAllInDocument:If|} (x is Nothing, y, x)
Dim z2 = If(x IsNot Nothing, x, y)
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string)
Dim z1 = If(x, y)
Dim z2 = If(x, y)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)>
Public Async Function TestFixAll2() As Task
Await TestAsync(
"
Imports System
Class C
Sub M(x as string, y as string, z as string)
dim w = {|FixAllInDocument:If|} (x isnot Nothing, x, If(y isnot Nothing, y, z))
End Sub
End Class",
"Imports System
Class C
Sub M(x as string, y as string, z as string)
dim w = If(x, If(y, z))
End Sub
End Class")
End Function
End Class
End Namespace
\ No newline at end of file
...@@ -55,6 +55,8 @@ ...@@ -55,6 +55,8 @@
<Compile Include="..\..\..\Compilers\CSharp\Portable\Syntax\LambdaUtilities.cs"> <Compile Include="..\..\..\Compilers\CSharp\Portable\Syntax\LambdaUtilities.cs">
<Link>InternalUtilities\LambdaUtilities.cs</Link> <Link>InternalUtilities\LambdaUtilities.cs</Link>
</Compile> </Compile>
<Compile Include="UseCoalesceExpression\CSharpUseCoalesceExpressionForNullableDiagnosticAnalyzer.cs" />
<Compile Include="UseCoalesceExpression\CSharpUseCoalesceExpressionDiagnosticAnalyzer.cs" />
<Compile Include="UseExpressionBody\Accessors\UseExpressionBodyForAccessorsCodeFixProvider.cs" /> <Compile Include="UseExpressionBody\Accessors\UseExpressionBodyForAccessorsCodeFixProvider.cs" />
<Compile Include="UseExpressionBody\Accessors\UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs" /> <Compile Include="UseExpressionBody\Accessors\UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs" />
<Compile Include="UseExpressionBody\Indexers\UseExpressionBodyForIndexersCodeFixProvider.cs" /> <Compile Include="UseExpressionBody\Indexers\UseExpressionBodyForIndexersCodeFixProvider.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.UseCoalesceExpression;
namespace Microsoft.CodeAnalysis.CSharp.UseCoalesceExpression
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class CSharpUseCoalesceExpressionDiagnosticAnalyzer :
AbstractUseCoalesceExpressionDiagnosticAnalyzer<
SyntaxKind,
ExpressionSyntax,
ConditionalExpressionSyntax,
BinaryExpressionSyntax>
{
protected override ISyntaxFactsService GetSyntaxFactsService()
=> CSharpSyntaxFactsService.Instance;
protected override SyntaxKind GetSyntaxKindToAnalyze()
=> SyntaxKind.ConditionalExpression;
protected override bool IsEquals(BinaryExpressionSyntax condition)
=> condition.Kind() == SyntaxKind.EqualsExpression;
protected override bool IsNotEquals(BinaryExpressionSyntax condition)
=> condition.Kind() == SyntaxKind.NotEqualsExpression;
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.UseCoalesceExpression;
namespace Microsoft.CodeAnalysis.CSharp.UseCoalesceExpression
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class CSharpUseCoalesceExpressionForNullableDiagnosticAnalyzer :
AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer<
SyntaxKind,
ExpressionSyntax,
ConditionalExpressionSyntax,
BinaryExpressionSyntax,
MemberAccessExpressionSyntax,
PrefixUnaryExpressionSyntax>
{
protected override ISyntaxFactsService GetSyntaxFactsService()
=> CSharpSyntaxFactsService.Instance;
protected override SyntaxKind GetSyntaxKindToAnalyze()
=> SyntaxKind.ConditionalExpression;
}
}
\ No newline at end of file
...@@ -33,6 +33,9 @@ internal static class IDEDiagnosticIds ...@@ -33,6 +33,9 @@ internal static class IDEDiagnosticIds
public const string UseExpressionBodyForIndexersDiagnosticId = "IDE0025"; public const string UseExpressionBodyForIndexersDiagnosticId = "IDE0025";
public const string UseExpressionBodyForAccessorsDiagnosticId = "IDE0026"; public const string UseExpressionBodyForAccessorsDiagnosticId = "IDE0026";
public const string UseCoalesceExpressionDiagnosticId = "IDE0028";
public const string UseCoalesceExpressionForNullableDiagnosticId = "IDE0029";
// Analyzer error Ids // Analyzer error Ids
public const string AnalyzerChangedId = "IDE1001"; public const string AnalyzerChangedId = "IDE1001";
public const string AnalyzerDependencyConflictId = "IDE1002"; public const string AnalyzerDependencyConflictId = "IDE1002";
......
...@@ -121,6 +121,10 @@ ...@@ -121,6 +121,10 @@
<Compile Include="Remote\RemoteArguments.cs" /> <Compile Include="Remote\RemoteArguments.cs" />
<Compile Include="Structure\BlockStructureOptions.cs" /> <Compile Include="Structure\BlockStructureOptions.cs" />
<Compile Include="AddImport\CodeActions\SymbolReference.SymbolReferenceCodeAction.cs" /> <Compile Include="AddImport\CodeActions\SymbolReference.SymbolReferenceCodeAction.cs" />
<Compile Include="UseCoalesceExpression\AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer.cs" />
<Compile Include="UseCoalesceExpression\UseCoalesceExpressionForNullableCodeFixProvider.cs" />
<Compile Include="UseCoalesceExpression\UseCoalesceExpressionCodeFixProvider.cs" />
<Compile Include="UseCoalesceExpression\AbstractUseCoalesceExpressionDiagnosticAnalyzer.cs" />
<Compile Include="UseThrowExpression\AbstractUseThrowExpressionDiagnosticAnalyzer.cs" /> <Compile Include="UseThrowExpression\AbstractUseThrowExpressionDiagnosticAnalyzer.cs" />
<Compile Include="UseThrowExpression\UseThrowExpressionCodeFixProvider.cs" /> <Compile Include="UseThrowExpression\UseThrowExpressionCodeFixProvider.cs" />
<Compile Include="UseThrowExpression\UseThrowExpressionCodeFixProvider.FixAllProvider.cs" /> <Compile Include="UseThrowExpression\UseThrowExpressionCodeFixProvider.FixAllProvider.cs" />
......
...@@ -3037,6 +3037,15 @@ internal class FeaturesResources { ...@@ -3037,6 +3037,15 @@ internal class FeaturesResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Use coalesce expression.
/// </summary>
internal static string Use_coalesce_expression {
get {
return ResourceManager.GetString("Use_coalesce_expression", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Use expression body for accessors. /// Looks up a localized string similar to Use expression body for accessors.
/// </summary> /// </summary>
......
...@@ -1151,4 +1151,7 @@ This version used in: {2}</value> ...@@ -1151,4 +1151,7 @@ This version used in: {2}</value>
<data name="Pascal_Case" xml:space="preserve"> <data name="Pascal_Case" xml:space="preserve">
<value>Pascal Case</value> <value>Pascal Case</value>
</data> </data>
<data name="Use_coalesce_expression" xml:space="preserve">
<value>Use coalesce expression</value>
</data>
</root> </root>
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
namespace Microsoft.CodeAnalysis.UseCoalesceExpression
{
internal abstract class AbstractUseCoalesceExpressionDiagnosticAnalyzer<
TSyntaxKind,
TExpressionSyntax,
TConditionalExpressionSyntax,
TBinaryExpressionSyntax> : AbstractCodeStyleDiagnosticAnalyzer
where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
where TConditionalExpressionSyntax : TExpressionSyntax
where TBinaryExpressionSyntax : TExpressionSyntax
{
protected AbstractUseCoalesceExpressionDiagnosticAnalyzer()
: base(IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Use_coalesce_expression), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
{
}
protected abstract TSyntaxKind GetSyntaxKindToAnalyze();
protected abstract ISyntaxFactsService GetSyntaxFactsService();
protected abstract bool IsEquals(TBinaryExpressionSyntax condition);
protected abstract bool IsNotEquals(TBinaryExpressionSyntax condition);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeSyntax, GetSyntaxKindToAnalyze());
}
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
var conditionalExpression = (TConditionalExpressionSyntax)context.Node;
var optionSet = context.Options.GetOptionSet();
var option = optionSet.GetOption(CodeStyleOptions.PreferCoalesceExpression, conditionalExpression.Language);
if (!option.Value)
{
return;
}
var syntaxFacts = this.GetSyntaxFactsService();
SyntaxNode conditionNode, whenTrueNodeHigh, whenFalseNodeHigh;
syntaxFacts.GetPartsOfConditionalExpression(
conditionalExpression, out conditionNode, out whenTrueNodeHigh, out whenFalseNodeHigh);
conditionNode = syntaxFacts.WalkDownParentheses(conditionNode);
var whenTrueNodeLow = syntaxFacts.WalkDownParentheses(whenTrueNodeHigh);
var whenFalseNodeLow = syntaxFacts.WalkDownParentheses(whenFalseNodeHigh);
var condition = conditionNode as TBinaryExpressionSyntax;
if (condition == null)
{
return;
}
var isEquals = IsEquals(condition);
var isNotEquals = IsNotEquals(condition);
if (!isEquals && !isNotEquals)
{
return;
}
SyntaxNode conditionLeftHigh;
SyntaxNode conditionRightHigh;
syntaxFacts.GetPartsOfBinaryExpression(condition, out conditionLeftHigh, out conditionRightHigh);
var conditionLeftLow = syntaxFacts.WalkDownParentheses(conditionLeftHigh);
var conditionRightLow = syntaxFacts.WalkDownParentheses(conditionRightHigh);
var conditionLeftIsNull = syntaxFacts.IsNullLiteralExpression(conditionLeftLow);
var conditionRightIsNull = syntaxFacts.IsNullLiteralExpression(conditionRightLow);
if (conditionRightIsNull && conditionLeftIsNull)
{
// null == null nothing to do here.
return;
}
if (!conditionRightIsNull && !conditionLeftIsNull)
{
return;
}
if (!syntaxFacts.AreEquivalent(
conditionRightIsNull ? conditionLeftLow : conditionRightLow,
isEquals ? whenFalseNodeLow : whenTrueNodeLow))
{
return;
}
var conditionPartToCheck = conditionRightIsNull ? conditionLeftHigh : conditionRightHigh;
var whenPartToKeep = isEquals ? whenTrueNodeHigh : whenFalseNodeHigh;
var locations = ImmutableArray.Create(
conditionalExpression.GetLocation(),
conditionPartToCheck.GetLocation(),
whenPartToKeep.GetLocation());
context.ReportDiagnostic(Diagnostic.Create(
this.CreateDescriptor(this.DescriptorId, option.Notification.Value),
conditionalExpression.GetLocation(),
locations));
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
namespace Microsoft.CodeAnalysis.UseCoalesceExpression
{
internal abstract class AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer<
TSyntaxKind,
TExpressionSyntax,
TConditionalExpressionSyntax,
TBinaryExpressionSyntax,
TMemberAccessExpression,
TPrefixUnaryExpressionSyntax> : AbstractCodeStyleDiagnosticAnalyzer
where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
where TConditionalExpressionSyntax : TExpressionSyntax
where TBinaryExpressionSyntax : TExpressionSyntax
where TMemberAccessExpression : TExpressionSyntax
where TPrefixUnaryExpressionSyntax : TExpressionSyntax
{
protected AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer()
: base(IDEDiagnosticIds.UseCoalesceExpressionForNullableDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Use_coalesce_expression), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
{
}
protected abstract TSyntaxKind GetSyntaxKindToAnalyze();
protected abstract ISyntaxFactsService GetSyntaxFactsService();
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeSyntax, GetSyntaxKindToAnalyze());
}
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
var conditionalExpression = (TConditionalExpressionSyntax)context.Node;
var optionSet = context.Options.GetOptionSet();
var option = optionSet.GetOption(CodeStyleOptions.PreferCoalesceExpression, conditionalExpression.Language);
if (!option.Value)
{
return;
}
var syntaxFacts = this.GetSyntaxFactsService();
SyntaxNode conditionNode, whenTrueNodeHigh, whenFalseNodeHigh;
syntaxFacts.GetPartsOfConditionalExpression(
conditionalExpression, out conditionNode, out whenTrueNodeHigh, out whenFalseNodeHigh);
conditionNode = syntaxFacts.WalkDownParentheses(conditionNode);
var whenTrueNodeLow = syntaxFacts.WalkDownParentheses(whenTrueNodeHigh);
var whenFalseNodeLow = syntaxFacts.WalkDownParentheses(whenFalseNodeHigh);
var notHasValueExpression = false;
if (syntaxFacts.IsLogicalNotExpression(conditionNode))
{
notHasValueExpression = true;
conditionNode = syntaxFacts.GetOperandOfPrefixUnaryExpression(conditionNode);
}
var conditionMemberAccess = conditionNode as TMemberAccessExpression;
if (conditionMemberAccess == null)
{
return;
}
SyntaxNode conditionExpression, conditionSimpleName;
syntaxFacts.GetPartsOfMemberAccessExpression(conditionMemberAccess, out conditionExpression, out conditionSimpleName);
string conditionName; int unused;
syntaxFacts.GetNameAndArityOfSimpleName(conditionSimpleName, out conditionName, out unused);
if (conditionName != nameof(Nullable<int>.HasValue))
{
return;
}
var whenPartToCheck = notHasValueExpression ? whenFalseNodeLow : whenTrueNodeLow;
var whenPartMemberAccess = whenPartToCheck as TMemberAccessExpression;
if (whenPartMemberAccess == null)
{
return;
}
SyntaxNode whenPartExpression, whenPartSimpleName;
syntaxFacts.GetPartsOfMemberAccessExpression(whenPartMemberAccess, out whenPartExpression, out whenPartSimpleName);
string whenPartName;
syntaxFacts.GetNameAndArityOfSimpleName(whenPartSimpleName, out whenPartName, out unused);
if (whenPartName != nameof(Nullable<int>.Value))
{
return;
}
if (!syntaxFacts.AreEquivalent(conditionExpression, whenPartExpression))
{
return;
}
// Syntactically this looks like something we can simplify. Make sure we're
// actually looking at something Nullable (and not some type that uses a similar
// syntactic pattern).
var semanticModel = context.SemanticModel;
var nullableType = semanticModel.Compilation.GetTypeByMetadataName("System.Nullable`1");
if (nullableType == null)
{
return;
}
var cancellationToken = context.CancellationToken;
var type = semanticModel.GetTypeInfo(conditionExpression, cancellationToken);
if (!nullableType.Equals(type.Type?.OriginalDefinition))
{
return;
}
var whenPartToKeep = notHasValueExpression ? whenTrueNodeHigh : whenFalseNodeHigh;
var locations = ImmutableArray.Create(
conditionalExpression.GetLocation(),
conditionExpression.GetLocation(),
whenPartToKeep.GetLocation());
context.ReportDiagnostic(Diagnostic.Create(
this.CreateDescriptor(this.DescriptorId, option.Notification.Value),
conditionalExpression.GetLocation(),
locations));
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixes.FixAllOccurrences;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.UseCoalesceExpression
{
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared]
internal class UseCoalesceExpressionCodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId);
public override FixAllProvider GetFixAllProvider() => new UseCoalesceExpressionFixAllProvider(this);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(new MyCodeAction(
c => FixAsync(context.Document, context.Diagnostics[0], c)),
context.Diagnostics);
return SpecializedTasks.EmptyTask;
}
private Task<Document> FixAsync(
Document document,
Diagnostic diagnostic,
CancellationToken cancellationToken)
{
return FixAllAsync(document, ImmutableArray.Create(diagnostic), cancellationToken);
}
private async Task<Document> FixAllAsync(
Document document,
ImmutableArray<Diagnostic> diagnostics,
CancellationToken cancellationToken)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
var generator = editor.Generator;
foreach (var diagnostic in diagnostics)
{
var conditionalExpression = root.FindNode(diagnostic.AdditionalLocations[0].SourceSpan, getInnermostNodeForTie: true);
var conditionalPartHigh = root.FindNode(diagnostic.AdditionalLocations[1].SourceSpan);
var whenPart = root.FindNode(diagnostic.AdditionalLocations[2].SourceSpan);
SyntaxNode condition, whenTrue, whenFalse;
syntaxFacts.GetPartsOfConditionalExpression(
conditionalExpression, out condition, out whenTrue, out whenFalse);
var conditionalPartLow = syntaxFacts.WalkDownParentheses(conditionalPartHigh);
editor.ReplaceNode(conditionalExpression,
(c, g) => {
SyntaxNode currentCondition, currentWhenTrue, currentWhenFalse;
syntaxFacts.GetPartsOfConditionalExpression(
c, out currentCondition, out currentWhenTrue, out currentWhenFalse);
return whenPart == whenTrue
? g.CoalesceExpression(conditionalPartLow, syntaxFacts.WalkDownParentheses(currentWhenTrue))
: g.CoalesceExpression(conditionalPartLow, syntaxFacts.WalkDownParentheses(currentWhenFalse));
});
}
var newRoot = editor.GetChangedRoot();
return document.WithSyntaxRoot(newRoot);
}
private class UseCoalesceExpressionFixAllProvider : DocumentBasedFixAllProvider
{
private readonly UseCoalesceExpressionCodeFixProvider _provider;
public UseCoalesceExpressionFixAllProvider(UseCoalesceExpressionCodeFixProvider provider)
{
_provider = provider;
}
protected override Task<Document> FixDocumentAsync(
Document document, ImmutableArray<Diagnostic> diagnostics, CancellationToken cancellationToken)
{
var filteredDiagnostics = diagnostics.WhereAsArray(
d => !d.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary));
return _provider.FixAllAsync(document, filteredDiagnostics, cancellationToken);
}
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(FeaturesResources.Use_coalesce_expression, createChangedDocument)
{
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixes.FixAllOccurrences;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.UseCoalesceExpression
{
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared]
internal class UseCoalesceExpressionForNullableCodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.UseCoalesceExpressionForNullableDiagnosticId);
public override FixAllProvider GetFixAllProvider() => new UseCoalesceExpressionForNullableFixAllProvider(this);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(new MyCodeAction(
c => FixAsync(context.Document, context.Diagnostics[0], c)),
context.Diagnostics);
return SpecializedTasks.EmptyTask;
}
private Task<Document> FixAsync(
Document document,
Diagnostic diagnostic,
CancellationToken cancellationToken)
{
return FixAllAsync(document, ImmutableArray.Create(diagnostic), cancellationToken);
}
private async Task<Document> FixAllAsync(
Document document,
ImmutableArray<Diagnostic> diagnostics,
CancellationToken cancellationToken)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
var generator = editor.Generator;
foreach (var diagnostic in diagnostics)
{
var conditionalExpression = root.FindNode(diagnostic.AdditionalLocations[0].SourceSpan, getInnermostNodeForTie: true);
var conditionExpression = root.FindNode(diagnostic.AdditionalLocations[1].SourceSpan);
var whenPart = root.FindNode(diagnostic.AdditionalLocations[2].SourceSpan);
SyntaxNode condition, whenTrue, whenFalse;
syntaxFacts.GetPartsOfConditionalExpression(
conditionalExpression, out condition, out whenTrue, out whenFalse);
editor.ReplaceNode(conditionalExpression,
(c, g) => {
SyntaxNode currentCondition, currentWhenTrue, currentWhenFalse;
syntaxFacts.GetPartsOfConditionalExpression(
c, out currentCondition, out currentWhenTrue, out currentWhenFalse);
return whenPart == whenTrue
? g.CoalesceExpression(conditionExpression, syntaxFacts.WalkDownParentheses(currentWhenTrue))
: g.CoalesceExpression(conditionExpression, syntaxFacts.WalkDownParentheses(currentWhenFalse));
});
}
var newRoot = editor.GetChangedRoot();
return document.WithSyntaxRoot(newRoot);
}
private class UseCoalesceExpressionForNullableFixAllProvider : DocumentBasedFixAllProvider
{
private readonly UseCoalesceExpressionForNullableCodeFixProvider _provider;
public UseCoalesceExpressionForNullableFixAllProvider(UseCoalesceExpressionForNullableCodeFixProvider provider)
{
_provider = provider;
}
protected override Task<Document> FixDocumentAsync(
Document document, ImmutableArray<Diagnostic> diagnostics, CancellationToken cancellationToken)
{
var filteredDiagnostics = diagnostics.WhereAsArray(
d => !d.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary));
return _provider.FixAllAsync(document, filteredDiagnostics, cancellationToken);
}
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(FeaturesResources.Use_coalesce_expression, createChangedDocument)
{
}
}
}
}
\ No newline at end of file
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
<Platform Condition="'$(Platform)' == ''">AnyCPU</Platform> <Platform Condition="'$(Platform)' == ''">AnyCPU</Platform>
<ProjectGuid>{A1BCD0CE-6C2F-4F8C-9A48-D9D93928E26D}</ProjectGuid> <ProjectGuid>{A1BCD0CE-6C2F-4F8C-9A48-D9D93928E26D}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RootNamespace></RootNamespace> <RootNamespace>
</RootNamespace>
<AssemblyName>Microsoft.CodeAnalysis.VisualBasic.Features</AssemblyName> <AssemblyName>Microsoft.CodeAnalysis.VisualBasic.Features</AssemblyName>
<TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier> <TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion> <TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
...@@ -429,6 +430,8 @@ ...@@ -429,6 +430,8 @@
<Compile Include="SolutionCrawler\VisualBasicDocumentDifferenceService.vb" /> <Compile Include="SolutionCrawler\VisualBasicDocumentDifferenceService.vb" />
<Compile Include="Structure\Providers\SyncLockBlockStructureProvider.vb" /> <Compile Include="Structure\Providers\SyncLockBlockStructureProvider.vb" />
<Compile Include="Structure\Providers\TryBlockStructureProvider.vb" /> <Compile Include="Structure\Providers\TryBlockStructureProvider.vb" />
<Compile Include="UseCoalesceExpression\VisualBasicUseCoalesceExpressionForNullableDiagnosticAnalyzer.vb" />
<Compile Include="UseCoalesceExpression\VisualBasicUseCoalesceExpressionDiagnosticAnalyzer.vb" />
<Compile Include="UseObjectInitializer\VisualBasicUseObjectInitializerCodeFixProvider.vb" /> <Compile Include="UseObjectInitializer\VisualBasicUseObjectInitializerCodeFixProvider.vb" />
<Compile Include="UseObjectInitializer\VisualBasicUseObjectInitializerDiagnosticAnalyzer.vb" /> <Compile Include="UseObjectInitializer\VisualBasicUseObjectInitializerDiagnosticAnalyzer.vb" />
<Compile Include="Structure\Providers\UsingBlockStructureProvider.vb" /> <Compile Include="Structure\Providers\UsingBlockStructureProvider.vb" />
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.UseCoalesceExpression
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.UseCoalesceExpression
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend Class VisualBasicUseCoalesceExpressionDiagnosticAnalyzer
Inherits AbstractUseCoalesceExpressionDiagnosticAnalyzer(Of
SyntaxKind,
ExpressionSyntax,
TernaryConditionalExpressionSyntax,
BinaryExpressionSyntax)
Protected Overrides Function GetSyntaxFactsService() As ISyntaxFactsService
Return VisualBasicSyntaxFactsService.Instance
End Function
Protected Overrides Function GetSyntaxKindToAnalyze() As SyntaxKind
Return SyntaxKind.TernaryConditionalExpression
End Function
Protected Overrides Function IsEquals(condition As BinaryExpressionSyntax) As Boolean
Return condition.Kind() = SyntaxKind.IsExpression
End Function
Protected Overrides Function IsNotEquals(condition As BinaryExpressionSyntax) As Boolean
Return condition.Kind() = SyntaxKind.IsNotExpression
End Function
End Class
End Namespace
\ No newline at end of file
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.UseCoalesceExpression
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.UseCoalesceExpression
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend Class VisualBasicUseCoalesceExpressionForNullableDiagnosticAnalyzer
Inherits AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer(Of
SyntaxKind,
ExpressionSyntax,
TernaryConditionalExpressionSyntax,
BinaryExpressionSyntax,
MemberAccessExpressionSyntax,
UnaryExpressionSyntax)
Protected Overrides Function GetSyntaxFactsService() As ISyntaxFactsService
Return VisualBasicSyntaxFactsService.Instance
End Function
Protected Overrides Function GetSyntaxKindToAnalyze() As SyntaxKind
Return SyntaxKind.TernaryConditionalExpression
End Function
End Class
End Namespace
\ No newline at end of file
...@@ -249,6 +249,27 @@ public C(string s) ...@@ -249,6 +249,27 @@ public C(string s)
//] //]
} }
} }
";
private static readonly string s_preferCoalesceExpression = @"
using System;
class C
{
private string s;
public C(string s)
{
//[
// Prefer:
var v = x ?? y;
// Over:
var v = x != null ? x : y; // or
var v = x == null ? y : x;
//]
}
}
"; ";
private static readonly string s_preferConditionalDelegateCall = @" private static readonly string s_preferConditionalDelegateCall = @"
...@@ -598,6 +619,7 @@ internal StyleViewModel(OptionSet optionSet, IServiceProvider serviceProvider) : ...@@ -598,6 +619,7 @@ internal StyleViewModel(OptionSet optionSet, IServiceProvider serviceProvider) :
// Null preferences. // Null preferences.
CodeStyleItems.Add(new SimpleCodeStyleOptionViewModel(CodeStyleOptions.PreferThrowExpression, CSharpVSResources.Prefer_throw_expression, s_preferThrowExpression, s_preferThrowExpression, this, optionSet, nullCheckingGroupTitle)); CodeStyleItems.Add(new SimpleCodeStyleOptionViewModel(CodeStyleOptions.PreferThrowExpression, CSharpVSResources.Prefer_throw_expression, s_preferThrowExpression, s_preferThrowExpression, this, optionSet, nullCheckingGroupTitle));
CodeStyleItems.Add(new SimpleCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferConditionalDelegateCall, CSharpVSResources.Prefer_conditional_delegate_call, s_preferConditionalDelegateCall, s_preferConditionalDelegateCall, this, optionSet, nullCheckingGroupTitle)); CodeStyleItems.Add(new SimpleCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferConditionalDelegateCall, CSharpVSResources.Prefer_conditional_delegate_call, s_preferConditionalDelegateCall, s_preferConditionalDelegateCall, this, optionSet, nullCheckingGroupTitle));
CodeStyleItems.Add(new SimpleCodeStyleOptionViewModel(CodeStyleOptions.PreferCoalesceExpression, ServicesVSResources.Prefer_coalesce_expression, s_preferCoalesceExpression, s_preferCoalesceExpression, this, optionSet, nullCheckingGroupTitle));
} }
} }
} }
\ No newline at end of file
...@@ -1365,6 +1365,15 @@ internal class ServicesVSResources { ...@@ -1365,6 +1365,15 @@ internal class ServicesVSResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Prefer coalesce expression.
/// </summary>
internal static string Prefer_coalesce_expression {
get {
return ResourceManager.GetString("Prefer_coalesce_expression", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Prefer framework type. /// Looks up a localized string similar to Prefer framework type.
/// </summary> /// </summary>
......
...@@ -804,4 +804,7 @@ Additional information: {1}</value> ...@@ -804,4 +804,7 @@ Additional information: {1}</value>
<data name="This_item_cannot_be_deleted_because_it_is_used_by_an_existing_Naming_Rule" xml:space="preserve"> <data name="This_item_cannot_be_deleted_because_it_is_used_by_an_existing_Naming_Rule" xml:space="preserve">
<value>This item cannot be deleted because it is used by an existing Naming Rule.</value> <value>This item cannot be deleted because it is used by an existing Naming Rule.</value>
</data> </data>
<data name="Prefer_coalesce_expression" xml:space="preserve">
<value>Prefer coalesce expression</value>
</data>
</root> </root>
\ No newline at end of file
...@@ -325,6 +325,15 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic ...@@ -325,6 +325,15 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic
End Get End Get
End Property End Property
'''<summary>
''' Looks up a localized string similar to &apos;nothing&apos; checking:.
'''</summary>
Friend Shared ReadOnly Property nothing_checking_colon() As String
Get
Return ResourceManager.GetString("nothing_checking_colon", resourceCulture)
End Get
End Property
'''<summary> '''<summary>
''' Looks up a localized string similar to _Only add new line on enter after end of fully typed word. ''' Looks up a localized string similar to _Only add new line on enter after end of fully typed word.
'''</summary> '''</summary>
......
...@@ -267,4 +267,7 @@ ...@@ -267,4 +267,7 @@
<data name="Show_completion_list_after_a_character_is_typed" xml:space="preserve"> <data name="Show_completion_list_after_a_character_is_typed" xml:space="preserve">
<value>_Show completion list after a character is typed</value> <value>_Show completion list after a character is typed</value>
</data> </data>
<data name="nothing_checking_colon" xml:space="preserve">
<value>'nothing' checking:</value>
</data>
</root> </root>
\ No newline at end of file
...@@ -165,6 +165,24 @@ Class Customer ...@@ -165,6 +165,24 @@ Class Customer
End Sub End Sub
End Class" End Class"
Private Shared ReadOnly s_preferCoalesceExpression As String = "
Imports System
Class Customer
Private Age As Integer
Sub New()
//[
' Prefer:
Dim v = If(x, y)
' Over:
Dim v = If(x Is Nothing, y, x) ' or
Dim v = If(x IsNot Nothing, x, y)
//]
End Sub
End Class"
#End Region #End Region
Public Sub New(optionSet As OptionSet, serviceProvider As IServiceProvider) Public Sub New(optionSet As OptionSet, serviceProvider As IServiceProvider)
...@@ -188,7 +206,7 @@ End Class" ...@@ -188,7 +206,7 @@ End Class"
} }
Dim expressionPreferencesGroupTitle = ServicesVSResources.Expression_preferences_colon Dim expressionPreferencesGroupTitle = ServicesVSResources.Expression_preferences_colon
Dim nothingPreferencesGroupTitle = BasicVSResources.nothing_checking_colon
' qualify with Me. group ' qualify with Me. group
Me.CodeStyleItems.Add(New SimpleCodeStyleOptionViewModel(CodeStyleOptions.QualifyFieldAccess, BasicVSResources.Qualify_field_access_with_Me, s_fieldDeclarationPreviewTrue, s_fieldDeclarationPreviewFalse, Me, optionSet, qualifyGroupTitle, qualifyMemberAccessPreferences)) Me.CodeStyleItems.Add(New SimpleCodeStyleOptionViewModel(CodeStyleOptions.QualifyFieldAccess, BasicVSResources.Qualify_field_access_with_Me, s_fieldDeclarationPreviewTrue, s_fieldDeclarationPreviewFalse, Me, optionSet, qualifyGroupTitle, qualifyMemberAccessPreferences))
...@@ -203,6 +221,8 @@ End Class" ...@@ -203,6 +221,8 @@ End Class"
' expression preferences ' expression preferences
Me.CodeStyleItems.Add(New SimpleCodeStyleOptionViewModel(CodeStyleOptions.PreferObjectInitializer, ServicesVSResources.Prefer_object_initializer, s_preferObjectInitializer, s_preferObjectInitializer, Me, optionSet, expressionPreferencesGroupTitle)) Me.CodeStyleItems.Add(New SimpleCodeStyleOptionViewModel(CodeStyleOptions.PreferObjectInitializer, ServicesVSResources.Prefer_object_initializer, s_preferObjectInitializer, s_preferObjectInitializer, Me, optionSet, expressionPreferencesGroupTitle))
' nothing preferences
Me.CodeStyleItems.Add(New SimpleCodeStyleOptionViewModel(CodeStyleOptions.PreferCoalesceExpression, ServicesVSResources.Prefer_coalesce_expression, s_preferCoalesceExpression, s_preferCoalesceExpression, Me, optionSet, nothingPreferencesGroupTitle))
End Sub End Sub
End Class End Class
End Namespace End Namespace
\ No newline at end of file
...@@ -1871,6 +1871,40 @@ public bool IsPossibleTupleContext(SyntaxTree syntaxTree, int position, Cancella ...@@ -1871,6 +1871,40 @@ public bool IsPossibleTupleContext(SyntaxTree syntaxTree, int position, Cancella
return syntaxTree.IsPossibleTupleContext(token, position); return syntaxTree.IsPossibleTupleContext(token, position);
} }
public bool IsNullLiteralExpression(SyntaxNode node)
=> node.Kind() == SyntaxKind.NullLiteralExpression;
public void GetPartsOfBinaryExpression(SyntaxNode node, out SyntaxNode left, out SyntaxNode right)
{
var binaryExpression = (BinaryExpressionSyntax)node;
left = binaryExpression.Left;
right = binaryExpression.Right;
}
public void GetPartsOfConditionalExpression(SyntaxNode node, out SyntaxNode condition, out SyntaxNode whenTrue, out SyntaxNode whenFalse)
{
var conditionalExpression = (ConditionalExpressionSyntax)node;
condition = conditionalExpression.Condition;
whenTrue = conditionalExpression.WhenTrue;
whenFalse = conditionalExpression.WhenFalse;
}
public SyntaxNode WalkDownParentheses(SyntaxNode node)
=> (node as ExpressionSyntax)?.WalkDownParentheses() ?? node;
public bool IsLogicalNotExpression(SyntaxNode node)
=> node.Kind() == SyntaxKind.LogicalNotExpression;
public SyntaxNode GetOperandOfPrefixUnaryExpression(SyntaxNode node)
=> ((PrefixUnaryExpressionSyntax)node).Operand;
public void GetPartsOfMemberAccessExpression(SyntaxNode node, out SyntaxNode expression, out SyntaxNode name)
{
var memberAccessExpression = (MemberAccessExpressionSyntax)node;
expression = memberAccessExpression.Expression;
name = memberAccessExpression.Name;
}
private class AddFirstMissingCloseBaceRewriter: CSharpSyntaxRewriter private class AddFirstMissingCloseBaceRewriter: CSharpSyntaxRewriter
{ {
private readonly SyntaxNode _contextNode; private readonly SyntaxNode _contextNode;
......
...@@ -70,6 +70,12 @@ public class CodeStyleOptions ...@@ -70,6 +70,12 @@ public class CodeStyleOptions
defaultValue: false, defaultValue: false,
storageLocations: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferObjectInitializer")); storageLocations: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferObjectInitializer"));
internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferCoalesceExpression = new PerLanguageOption<CodeStyleOption<bool>>(
nameof(CodeStyleOptions),
nameof(PreferCoalesceExpression),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferCoalesceExpression"));
internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferInlinedVariableDeclaration = new PerLanguageOption<CodeStyleOption<bool>>( internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferInlinedVariableDeclaration = new PerLanguageOption<CodeStyleOption<bool>>(
nameof(CodeStyleOptions), nameof(CodeStyleOptions),
nameof(PreferInlinedVariableDeclaration), nameof(PreferInlinedVariableDeclaration),
......
...@@ -29,7 +29,7 @@ internal interface ISyntaxFactsService : ILanguageService ...@@ -29,7 +29,7 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsLiteral(SyntaxToken token); bool IsLiteral(SyntaxToken token);
bool IsStringLiteralOrInterpolatedStringLiteral(SyntaxToken token); bool IsStringLiteralOrInterpolatedStringLiteral(SyntaxToken token);
bool IsStringLiteral(SyntaxToken token); bool IsStringLiteral(SyntaxToken token);
bool IsNumericLiteralExpression(SyntaxNode node);
bool IsTypeNamedVarInVariableOrFieldDeclaration(SyntaxToken token, SyntaxNode parent); bool IsTypeNamedVarInVariableOrFieldDeclaration(SyntaxToken token, SyntaxNode parent);
bool IsTypeNamedDynamic(SyntaxToken token, SyntaxNode parent); bool IsTypeNamedDynamic(SyntaxToken token, SyntaxNode parent);
bool IsDocumentationComment(SyntaxNode node); bool IsDocumentationComment(SyntaxNode node);
...@@ -37,6 +37,9 @@ internal interface ISyntaxFactsService : ILanguageService ...@@ -37,6 +37,9 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsGlobalAttribute(SyntaxNode node); bool IsGlobalAttribute(SyntaxNode node);
bool IsDeclaration(SyntaxNode node); bool IsDeclaration(SyntaxNode node);
bool IsNumericLiteralExpression(SyntaxNode node);
bool IsNullLiteralExpression(SyntaxNode node);
string GetText(int kind); string GetText(int kind);
bool IsInInactiveRegion(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken); bool IsInInactiveRegion(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken);
...@@ -52,6 +55,9 @@ internal interface ISyntaxFactsService : ILanguageService ...@@ -52,6 +55,9 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsObjectCreationExpression(SyntaxNode node); bool IsObjectCreationExpression(SyntaxNode node);
SyntaxNode GetObjectCreationInitializer(SyntaxNode objectCreationExpression); SyntaxNode GetObjectCreationInitializer(SyntaxNode objectCreationExpression);
void GetPartsOfBinaryExpression(SyntaxNode node, out SyntaxNode left, out SyntaxNode right);
void GetPartsOfConditionalExpression(SyntaxNode node, out SyntaxNode condition, out SyntaxNode whenTrue, out SyntaxNode whenFalse);
bool IsInvocationExpression(SyntaxNode node); bool IsInvocationExpression(SyntaxNode node);
bool IsExpressionOfInvocationExpression(SyntaxNode node); bool IsExpressionOfInvocationExpression(SyntaxNode node);
SyntaxNode GetExpressionOfInvocationExpression(SyntaxNode node); SyntaxNode GetExpressionOfInvocationExpression(SyntaxNode node);
...@@ -59,6 +65,9 @@ internal interface ISyntaxFactsService : ILanguageService ...@@ -59,6 +65,9 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsExpressionOfAwaitExpression(SyntaxNode node); bool IsExpressionOfAwaitExpression(SyntaxNode node);
SyntaxNode GetExpressionOfAwaitExpression(SyntaxNode node); SyntaxNode GetExpressionOfAwaitExpression(SyntaxNode node);
bool IsLogicalNotExpression(SyntaxNode node);
SyntaxNode GetOperandOfPrefixUnaryExpression(SyntaxNode node);
// Left side of = assignment. // Left side of = assignment.
bool IsLeftSideOfAssignment(SyntaxNode node); bool IsLeftSideOfAssignment(SyntaxNode node);
...@@ -81,9 +90,10 @@ internal interface ISyntaxFactsService : ILanguageService ...@@ -81,9 +90,10 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsNameOfMemberAccessExpression(SyntaxNode node); bool IsNameOfMemberAccessExpression(SyntaxNode node);
bool IsExpressionOfMemberAccessExpression(SyntaxNode node); bool IsExpressionOfMemberAccessExpression(SyntaxNode node);
SyntaxNode GetNameOfMemberAccessExpression(SyntaxNode memberAccessExpression); SyntaxNode GetNameOfMemberAccessExpression(SyntaxNode node);
SyntaxNode GetExpressionOfMemberAccessExpression(SyntaxNode memberAccessExpression); SyntaxNode GetExpressionOfMemberAccessExpression(SyntaxNode node);
SyntaxToken GetOperatorTokenOfMemberAccessExpression(SyntaxNode memberAccessExpression); SyntaxToken GetOperatorTokenOfMemberAccessExpression(SyntaxNode node);
void GetPartsOfMemberAccessExpression(SyntaxNode node, out SyntaxNode expression, out SyntaxNode name);
bool IsSimpleMemberAccessExpression(SyntaxNode node); bool IsSimpleMemberAccessExpression(SyntaxNode node);
bool IsPointerMemberAccessExpression(SyntaxNode node); bool IsPointerMemberAccessExpression(SyntaxNode node);
...@@ -176,6 +186,7 @@ internal interface ISyntaxFactsService : ILanguageService ...@@ -176,6 +186,7 @@ internal interface ISyntaxFactsService : ILanguageService
SyntaxToken FindTokenOnRightOfPosition(SyntaxNode node, int position, bool includeSkipped = true, bool includeDirectives = false, bool includeDocumentationComments = false); SyntaxToken FindTokenOnRightOfPosition(SyntaxNode node, int position, bool includeSkipped = true, bool includeDirectives = false, bool includeDocumentationComments = false);
SyntaxNode Parenthesize(SyntaxNode expression, bool includeElasticTrivia = true); SyntaxNode Parenthesize(SyntaxNode expression, bool includeElasticTrivia = true);
SyntaxNode WalkDownParentheses(SyntaxNode node);
SyntaxNode ConvertToSingleLine(SyntaxNode node, bool useElasticTrivia = false); SyntaxNode ConvertToSingleLine(SyntaxNode node, bool useElasticTrivia = false);
......
...@@ -1546,5 +1546,41 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ...@@ -1546,5 +1546,41 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken) Dim token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken)
Return syntaxTree.IsPossibleTupleContext(token, position) Return syntaxTree.IsPossibleTupleContext(token, position)
End Function End Function
Public Function IsNullLiteralExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsNullLiteralExpression
Return node.Kind() = SyntaxKind.NothingLiteralExpression
End Function
Public Sub GetPartsOfBinaryExpression(node As SyntaxNode, ByRef left As SyntaxNode, ByRef right As SyntaxNode) Implements ISyntaxFactsService.GetPartsOfBinaryExpression
Dim binaryExpression = DirectCast(node, BinaryExpressionSyntax)
left = binaryExpression.Left
right = binaryExpression.Right
End Sub
Public Sub GetPartsOfConditionalExpression(node As SyntaxNode, ByRef condition As SyntaxNode, ByRef whenTrue As SyntaxNode, ByRef whenFalse As SyntaxNode) Implements ISyntaxFactsService.GetPartsOfConditionalExpression
Dim conditionalExpression = DirectCast(node, TernaryConditionalExpressionSyntax)
condition = conditionalExpression.Condition
whenTrue = conditionalExpression.WhenTrue
whenFalse = conditionalExpression.WhenFalse
End Sub
Public Function WalkDownParentheses(node As SyntaxNode) As SyntaxNode Implements ISyntaxFactsService.WalkDownParentheses
Return If(TryCast(node, ExpressionSyntax)?.WalkDownParentheses(), node)
End Function
Public Function IsLogicalNotExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsLogicalNotExpression
Return node.IsKind(SyntaxKind.NotExpression)
Throw New NotImplementedException()
End Function
Public Function GetOperandOfPrefixUnaryExpression(node As SyntaxNode) As SyntaxNode Implements ISyntaxFactsService.GetOperandOfPrefixUnaryExpression
Return DirectCast(node, UnaryExpressionSyntax).Operand
End Function
Public Sub GetPartsOfMemberAccessExpression(node As SyntaxNode, ByRef expression As SyntaxNode, ByRef name As SyntaxNode) Implements ISyntaxFactsService.GetPartsOfMemberAccessExpression
Dim memberAccess = DirectCast(node, MemberAccessExpressionSyntax)
expression = memberAccess.Expression
name = memberAccess.Name
End Sub
End Class End Class
End Namespace End Namespace
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册