提交 fc051d39 编写于 作者: C CyrusNajmabadi

Add feature to convert from ReferenceEquals to 'is null'

上级 6b99a66a
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.UseIsNullCheck;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseIsNullCheck
{
public partial class UseIsNullCheckTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (new CSharpUseIsNullCheckDiagnosticAnalyzer(), new CSharpUseIsNullCheckCodeFixProvider());
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestIdentifierName()
{
await TestInRegularAndScriptAsync(
@"using System;
class C
{
void M(string s)
{
if ([||]ReferenceEquals(s, Nothing))
return;
}
end class",
@"using System;
class C
{
void M(string s)
{
if (s Is Nothing)
return;
}
end class");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestBuiltInType()
{
await TestInRegularAndScriptAsync(
@"using System;
class C
{
void M(string s)
{
if (object.[||]ReferenceEquals(s, Nothing))
return;
}
end class",
@"using System;
class C
{
void M(string s)
{
if (s Is Nothing)
return;
}
end class");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestNamedType()
{
await TestInRegularAndScriptAsync(
@"using System;
class C
{
void M(string s)
{
if (Object.[||]ReferenceEquals(s, Nothing))
return;
}
end class",
@"using System;
class C
{
void M(string s)
{
if (s Is Nothing)
return;
}
end class");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestReversed()
{
await TestInRegularAndScriptAsync(
@"using System;
class C
{
void M(string s)
{
if ([||]ReferenceEquals(Nothing, s))
return;
}
end class",
@"using System;
class C
{
void M(string s)
{
if (s Is Nothing)
return;
}
end class");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestNegated()
{
await TestInRegularAndScriptAsync(
@"using System;
class C
{
void M(string s)
{
if (![||]ReferenceEquals(Nothing, s))
return;
}
end class",
@"using System;
class C
{
void M(string s)
{
if (!(s Is Nothing))
return;
}
end class");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestNotInCSharp6()
{
await TestMissingAsync(
@"using System;
class C
{
void M(string s)
{
if ([||]ReferenceEquals(Nothing, s))
return;
}
end class", parameters: new TestParameters(parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6)));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestFixAll1()
{
await TestInRegularAndScriptAsync(
@"using System;
class C
{
void M(string s1, string s2)
{
if ({|FixAllInDocument:ReferenceEquals|}(s1, Nothing) ||
ReferenceEquals(s2, Nothing))
return;
}
end class",
@"using System;
class C
{
void M(string s1, string s2)
{
if (s1 Is Nothing ||
s2 Is Nothing)
return;
}
end class", ignoreTrivia: false);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestFixAll2()
{
await TestInRegularAndScriptAsync(
@"using System;
class C
{
void M(string s1, string s2)
{
if (ReferenceEquals(s1, Nothing) ||
{|FixAllInDocument:ReferenceEquals|}(s2, Nothing))
return;
}
end class",
@"using System;
class C
{
void M(string s1, string s2)
{
if (s1 Is Nothing ||
s2 Is Nothing)
return;
}
end class", ignoreTrivia: false);
}
}
}
......@@ -106,6 +106,7 @@ public static class Features
public const string CodeActionsUseExplicitType = "CodeActions.UseExplicitType";
public const string CodeActionsUseExplicitTupleName = "CodeActions.UseExplicitTupleName";
public const string CodeActionsUseFrameworkType = "CodeActions.UseFrameworkType";
public const string CodeActionsUseIsNullCheck = "CodeActions.UseIsNullCheck";
public const string CodeActionsUseLocalFunction = "CodeActions.UseLocalFunction";
public const string CodeActionsUseNullPropagation = "CodeActions.UseNullPropagation";
public const string CodeActionsUseNamedArguments = "CodeActions.UseNamedArguments";
......
' 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 System.Threading.Tasks
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.UseIsNullCheck
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Roslyn.Test.Utilities
Imports Xunit
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.UseIsNullCheck
Partial Public Class UseIsNullCheckTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider)
Return (New VisualBasicUseIsNullCheckDiagnosticAnalyzer(), New VisualBasicUseIsNullCheckCodeFixProvider())
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)>
Public Async Function TestIdentifierName() As Task
Await TestInRegularAndScriptAsync(
"Imports System
class C
sub M(s as string)
if ([||]ReferenceEquals(s, Nothing))
return
end if
end sub
end class",
"Imports System
class C
sub M(s as string)
if (s Is Nothing)
return
end if
end sub
end class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)>
Public Async Function TestBuiltInType() As Task
Await TestInRegularAndScriptAsync(
"Imports System
class C
sub M(s as string)
if (object.[||]ReferenceEquals(s, Nothing))
return
end if
end sub
end class",
"Imports System
class C
sub M(s as string)
if (s Is Nothing)
return
end if
end sub
end class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)>
Public Async Function TestNamedType() As Task
Await TestInRegularAndScriptAsync(
"Imports System
class C
sub M(s as string)
if (Object.[||]ReferenceEquals(s, Nothing))
return
end if
end sub
end class",
"Imports System
class C
sub M(s as string)
if (s Is Nothing)
return
end if
end sub
end class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)>
Public Async Function TestReversed() As Task
Await TestInRegularAndScriptAsync(
"Imports System
class C
sub M(s as string)
if ([||]ReferenceEquals(Nothing, s))
return
end if
end sub
end class",
"Imports System
class C
sub M(s as string)
if (s Is Nothing)
return
end if
end sub
end class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)>
Public Async Function TestNegated() As Task
Await TestInRegularAndScriptAsync(
"Imports System
class C
sub M(s as string)
if (not [||]ReferenceEquals(Nothing, s))
return
end if
end sub
end class",
"Imports System
class C
sub M(s as string)
if (s IsNot Nothing)
return
end if
end sub
end class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)>
Public Async Function TestFixAll1() As Task
Await TestInRegularAndScriptAsync(
"Imports System
class C
sub M(s1 as string, s2 as string)
if ({|FixAllInDocument:ReferenceEquals|}(s1, Nothing) orelse
ReferenceEquals(s2, Nothing))
return
end if
end sub
end class",
"Imports System
class C
sub M(s1 as string, s2 as string)
if (s1 Is Nothing orelse
s2 Is Nothing)
return
end if
end sub
end class", ignoreTrivia:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)>
Public Async Function TestFixAll2() As Task
Await TestInRegularAndScriptAsync(
"Imports System
class C
sub M(s1 as string, s2 as string)
if (ReferenceEquals(s1, Nothing) orelse
{|FixAllInDocument:ReferenceEquals|}(s2, Nothing))
return
end if
end sub
end class",
"Imports System
class C
sub M(s1 as string, s2 as string)
if (s1 Is Nothing orelse
s2 Is Nothing)
return
end if
end sub
end class", ignoreTrivia:=False)
End Function
End Class
End Namespace
......@@ -1126,6 +1126,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Use &apos;is null&apos; check.
/// </summary>
internal static string Use_is_null_check {
get {
return ResourceManager.GetString("Use_is_null_check", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to use &apos;var&apos; instead of explicit type.
/// </summary>
......
......@@ -521,4 +521,7 @@
<data name="Remove_unused_function" xml:space="preserve">
<value>Remove unused function</value>
</data>
<data name="Use_is_null_check" xml:space="preserve">
<value>Use 'is null' check</value>
</data>
</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 Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.UseIsNullCheck;
namespace Microsoft.CodeAnalysis.CSharp.UseIsNullCheck
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
internal class CSharpUseIsNullCheckCodeFixProvider : AbstractUseIsNullCheckCodeFixProvider
{
protected override string GetIsNullTitle()
=> CSharpFeaturesResources.Use_is_null_check;
protected override string GetIsNotNullTitle()
=> GetIsNullTitle();
protected override SyntaxNode CreateIsNullCheck(SyntaxNode argument)
=> SyntaxFactory.IsPatternExpression(
(ExpressionSyntax)argument,
SyntaxFactory.ConstantPattern(SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression))).Parenthesize();
protected override SyntaxNode CreateIsNotNullCheck(SyntaxNode notExpression, SyntaxNode argument)
=> ((PrefixUnaryExpressionSyntax)notExpression).WithOperand((ExpressionSyntax)CreateIsNullCheck(argument));
}
}
// 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.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.UseIsNullCheck;
namespace Microsoft.CodeAnalysis.CSharp.UseIsNullCheck
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class CSharpUseIsNullCheckDiagnosticAnalyzer : AbstractUseIsNullCheckDiagnosticAnalyzer<SyntaxKind>
{
public CSharpUseIsNullCheckDiagnosticAnalyzer()
: base(CSharpFeaturesResources.Use_is_null_check)
{
}
protected override bool IsLanguageVersionSupported(ParseOptions options)
=> ((CSharpParseOptions)options).LanguageVersion >= LanguageVersion.CSharp7;
protected override SyntaxKind GetInvocationExpressionKind()
=> SyntaxKind.InvocationExpression;
protected override ISyntaxFactsService GetSyntaxFactsService()
=> CSharpSyntaxFactsService.Instance;
}
}
......@@ -56,6 +56,8 @@ internal static class IDEDiagnosticIds
public const string UseLocalFunctionDiagnosticId = "IDE0039";
public const string UseIsNullCheckDiagnosticId = "IDE0041";
// Analyzer error Ids
public const string AnalyzerChangedId = "IDE1001";
public const string AnalyzerDependencyConflictId = "IDE1002";
......
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.UseIsNullCheck
{
internal abstract class AbstractUseIsNullCheckCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
public const string Negated = nameof(Negated);
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.UseIsNullCheckDiagnosticId);
protected abstract string GetIsNullTitle();
protected abstract string GetIsNotNullTitle();
protected abstract SyntaxNode CreateIsNullCheck(SyntaxNode argument);
protected abstract SyntaxNode CreateIsNotNullCheck(SyntaxNode notExpression, SyntaxNode argument);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
var negated = diagnostic.Properties.ContainsKey(Negated);
var title = negated ? GetIsNotNullTitle() : GetIsNullTitle();
context.RegisterCodeFix(
new MyCodeAction(title, c => this.FixAsync(context.Document, diagnostic, c)),
context.Diagnostics);
return SpecializedTasks.EmptyTask;
}
protected override Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
foreach (var diagnostic in diagnostics)
{
var invocation = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken: cancellationToken);
var negate = diagnostic.Properties.ContainsKey(Negated);
var arguments = syntaxFacts.GetArgumentsOfInvocationExpression(invocation);
var argument = syntaxFacts.IsNullLiteralExpression(syntaxFacts.GetExpressionOfArgument(arguments[0]))
? syntaxFacts.GetExpressionOfArgument(arguments[1])
: syntaxFacts.GetExpressionOfArgument(arguments[0]);
var toReplace = negate ? invocation.Parent : invocation;
var replacement = negate ? CreateIsNotNullCheck(invocation.Parent, argument) : CreateIsNullCheck(argument);
editor.ReplaceNode(
toReplace,
replacement.WithTriviaFrom(toReplace));
}
return SpecializedTasks.EmptyTask;
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument)
: base(title, createChangedDocument, title)
{
}
}
}
}
// 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 System.Linq;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
namespace Microsoft.CodeAnalysis.UseIsNullCheck
{
internal abstract class AbstractUseIsNullCheckDiagnosticAnalyzer<
TLanguageKindEnum>
: AbstractCodeStyleDiagnosticAnalyzer
where TLanguageKindEnum : struct
{
protected AbstractUseIsNullCheckDiagnosticAnalyzer(LocalizableString title)
: base(IDEDiagnosticIds.UseIsNullCheckDiagnosticId,
title,
new LocalizableResourceString(nameof(FeaturesResources.Null_check_can_be_simplified), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
{
}
public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
public override bool OpenFileOnly(Workspace workspace)
=> false;
protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterCompilationStartAction(compilationContext => {
var objectType = compilationContext.Compilation.GetSpecialType(SpecialType.System_Object);
if (objectType != null)
{
var referenceEqualsMethod = objectType.GetMembers(nameof(ReferenceEquals))
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m.DeclaredAccessibility == Accessibility.Public &&
m.Parameters.Length == 2);
if (referenceEqualsMethod != null)
{
context.RegisterSyntaxNodeAction(c => AnalyzeSyntax(c, referenceEqualsMethod), GetInvocationExpressionKind());
}
}
});
protected abstract bool IsLanguageVersionSupported(ParseOptions options);
protected abstract TLanguageKindEnum GetInvocationExpressionKind();
protected abstract ISyntaxFactsService GetSyntaxFactsService();
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, IMethodSymbol referenceEqualsMethod)
{
var cancellationToken = context.CancellationToken;
var semanticModel = context.SemanticModel;
var syntaxTree = semanticModel.SyntaxTree;
if (!IsLanguageVersionSupported(syntaxTree.Options))
{
return;
}
var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();
if (optionSet == null)
{
return;
}
var option = optionSet.GetOption(CodeStyleOptions.PreferIsNullCheckOverReferenceEqualityMethod, semanticModel.Language);
if (!option.Value)
{
return;
}
var invocation = context.Node;
var syntaxFacts = GetSyntaxFactsService();
var expression = syntaxFacts.GetExpressionOfInvocationExpression(invocation);
var nameNode = syntaxFacts.IsIdentifierName(expression)
? expression
: syntaxFacts.IsSimpleMemberAccessExpression(expression)
? syntaxFacts.GetNameOfMemberAccessExpression(expression)
: null;
if (!syntaxFacts.IsIdentifierName(nameNode))
{
return;
}
syntaxFacts.GetNameAndArityOfSimpleName(nameNode, out var name, out _);
if (!syntaxFacts.StringComparer.Equals(name, nameof(ReferenceEquals)))
{
return;
}
var arguments = syntaxFacts.GetArgumentsOfInvocationExpression(invocation);
if (arguments.Count != 2)
{
return;
}
if (!MatchesPattern(syntaxFacts, arguments[0], arguments[1]) &&
!MatchesPattern(syntaxFacts, arguments[1], arguments[0]))
{
return;
}
var symbol = semanticModel.GetSymbolInfo(invocation, cancellationToken).Symbol;
if (!referenceEqualsMethod.Equals(symbol))
{
return;
}
var additionalLocations = ImmutableArray.Create(invocation.GetLocation());
var properties = ImmutableDictionary<string, string>.Empty;
var negated = syntaxFacts.IsLogicalNotExpression(invocation.Parent);
if (negated)
{
properties = properties.Add(AbstractUseIsNullCheckCodeFixProvider.Negated, "");
}
var severity = option.Notification.Value;
context.ReportDiagnostic(
Diagnostic.Create(
GetDescriptorWithSeverity(severity), nameNode.GetLocation(),
additionalLocations, properties));
}
private bool MatchesPattern(ISyntaxFactsService syntaxFacts, SyntaxNode node1, SyntaxNode node2)
=> syntaxFacts.IsNullLiteralExpression(syntaxFacts.GetExpressionOfArgument(node1)) &&
!syntaxFacts.IsNullLiteralExpression(syntaxFacts.GetExpressionOfArgument(node2));
}
}
' 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.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.UseIsNullCheck
Namespace Microsoft.CodeAnalysis.VisualBasic.UseIsNullCheck
<ExportCodeFixProvider(LanguageNames.VisualBasic)>
Friend Class VisualBasicUseIsNullCheckCodeFixProvider
Inherits AbstractUseIsNullCheckCodeFixProvider
Protected Overrides Function GetIsNullTitle() As String
Return VBFeaturesResources.use_Is_Nothing_check
End Function
Protected Overrides Function GetIsNotNullTitle() As String
Return VBFeaturesResources.use_IsNot_Nothing_check
End Function
Protected Overrides Function CreateIsNullCheck(argument As SyntaxNode) As SyntaxNode
Return SyntaxFactory.IsExpression(
DirectCast(argument, ExpressionSyntax).Parenthesize(),
SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword))).Parenthesize()
End Function
Protected Overrides Function CreateIsNotNullCheck(notExpression As SyntaxNode, argument As SyntaxNode) As SyntaxNode
Return SyntaxFactory.IsNotExpression(
DirectCast(argument, ExpressionSyntax).Parenthesize(),
SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword))).Parenthesize()
End Function
End Class
End Namespace
' 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.UseIsNullCheck
Namespace Microsoft.CodeAnalysis.VisualBasic.UseIsNullCheck
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend Class VisualBasicUseIsNullCheckDiagnosticAnalyzer
Inherits AbstractUseIsNullCheckDiagnosticAnalyzer(Of SyntaxKind)
Public Sub New()
MyBase.New(VBFeaturesResources.use_is_nothing_check)
End Sub
Protected Overrides Function IsLanguageVersionSupported(options As ParseOptions) As Boolean
Return True
End Function
Protected Overrides Function GetInvocationExpressionKind() As SyntaxKind
Return SyntaxKind.InvocationExpression
End Function
Protected Overrides Function GetSyntaxFactsService() As ISyntaxFactsService
Return VisualBasicSyntaxFactsService.Instance
End Function
End Class
End Namespace
......@@ -23,7 +23,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VBFeaturesResources
'''<summary>
''' A strongly-typed resource class, for looking up localized strings, etc.
'''</summary>
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0"), _
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0"), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.Microsoft.VisualBasic.HideModuleNameAttribute()> _
......@@ -3243,6 +3243,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VBFeaturesResources
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Use &apos;Is Nothing&apos; check.
'''</summary>
Friend ReadOnly Property Use_Is_Nothing_check() As String
Get
Return ResourceManager.GetString("Use_Is_Nothing_check", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Use &apos;IsNot Nothing&apos; check.
'''</summary>
Friend ReadOnly Property Use_IsNot_Nothing_check() As String
Get
Return ResourceManager.GetString("Use_IsNot_Nothing_check", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Use &apos;Out&apos; for a type that will only be used as a return from functions..
'''</summary>
......
......@@ -1241,4 +1241,10 @@ Sub(&lt;parameterList&gt;) &lt;statement&gt;</value>
<data name="Convert_If_to_Select_Case" xml:space="preserve">
<value>Convert 'If' to 'Select Case'</value>
</data>
<data name="Use_Is_Nothing_check" xml:space="preserve">
<value>Use 'Is Nothing' check</value>
</data>
<data name="Use_IsNot_Nothing_check" xml:space="preserve">
<value>Use 'IsNot Nothing' check</value>
</data>
</root>
\ No newline at end of file
......@@ -933,6 +933,15 @@ internal class CSharpVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to Prefer &apos;is null&apos; over &apos;object.ReferenceEquals(..., null)&apos;.
/// </summary>
internal static string Prefer_is_null_over_ReferenceEquals {
get {
return ResourceManager.GetString("Prefer_is_null_over_ReferenceEquals", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Prefer pattern matching over &apos;as&apos; with &apos;null&apos; check.
/// </summary>
......
......@@ -519,4 +519,7 @@
<data name="Fade_out_unused_usings" xml:space="preserve">
<value>Fade out unused usings</value>
</data>
<data name="Prefer_is_null_over_ReferenceEquals" xml:space="preserve">
<value>Prefer 'is null' over 'object.ReferenceEquals(..., null)'</value>
</data>
</root>
\ No newline at end of file
......@@ -548,6 +548,26 @@ int fibonacci(int n)
//]
}}
}}
";
private static readonly string s_preferIsNullOverReferenceEquals = $@"
using System;
class Customer
{{
public Customer(string value)
{{
//[
// {ServicesVSResources.Prefer_colon}
if (value is null)
return;
// {ServicesVSResources.Over_colon}
if (object.ReferenceEquals(value, null))
return;
//]
}}
}}
";
private const string s_preferExpressionBodyForMethods = @"
......@@ -784,6 +804,7 @@ internal StyleViewModel(OptionSet optionSet, IServiceProvider serviceProvider) :
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferConditionalDelegateCall, CSharpVSResources.Prefer_conditional_delegate_call, s_preferConditionalDelegateCall, s_preferConditionalDelegateCall, this, optionSet, nullCheckingGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions.PreferCoalesceExpression, ServicesVSResources.Prefer_coalesce_expression, s_preferCoalesceExpression, s_preferCoalesceExpression, this, optionSet, nullCheckingGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions.PreferNullPropagation, ServicesVSResources.Prefer_null_propagation, s_preferNullPropagation, s_preferNullPropagation, this, optionSet, nullCheckingGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions.PreferIsNullCheckOverReferenceEqualityMethod, CSharpVSResources.Prefer_is_null_over_ReferenceEquals, s_preferIsNullOverReferenceEquals, s_preferIsNullOverReferenceEquals, this, optionSet, nullCheckingGroupTitle));
}
private void AddExpressionBodyOptions(OptionSet optionSet, string expressionPreferencesGroupTitle)
......
......@@ -379,6 +379,15 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Prefer &apos;Is Nothing&apos; over &apos;Object.ReferenceEquals(..., Nothing)&apos;.
'''</summary>
Friend Shared ReadOnly Property Prefer_Is_Nothing_over_ReferenceEquals() As String
Get
Return ResourceManager.GetString("Prefer_Is_Nothing_over_ReferenceEquals", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Prefer &apos;Me.&apos;.
'''</summary>
......
......@@ -270,4 +270,7 @@
<data name="Fade_out_unused_imports" xml:space="preserve">
<value>Fade out unused imports</value>
</data>
<data name="Prefer_Is_Nothing_over_ReferenceEquals" xml:space="preserve">
<value>Prefer 'Is Nothing' over 'Object.ReferenceEquals(..., Nothing)'</value>
</data>
</root>
\ No newline at end of file
......@@ -271,6 +271,25 @@ Class Customer
End Sub
End Class"
Private Shared ReadOnly s_preferIsNothingCheckOverReferenceEquals As String = $"
Imports System
Class Customer
Sub New(value as object)
//[
' {ServicesVSResources.Prefer_colon}
If value Is Nothing
Return
End If
' {ServicesVSResources.Over_colon}
If Object.ReferenceEquals(value, Nothing)
Return
End If
//]
End Sub
End Class"
#End Region
Public Sub New(optionSet As OptionSet, serviceProvider As IServiceProvider)
......@@ -316,6 +335,7 @@ End Class"
' nothing preferences
Me.CodeStyleItems.Add(New BooleanCodeStyleOptionViewModel(CodeStyleOptions.PreferCoalesceExpression, ServicesVSResources.Prefer_coalesce_expression, s_preferCoalesceExpression, s_preferCoalesceExpression, Me, optionSet, nothingPreferencesGroupTitle))
Me.CodeStyleItems.Add(New BooleanCodeStyleOptionViewModel(CodeStyleOptions.PreferNullPropagation, ServicesVSResources.Prefer_null_propagation, s_preferNullPropagation, s_preferNullPropagation, Me, optionSet, nothingPreferencesGroupTitle))
Me.CodeStyleItems.Add(New BooleanCodeStyleOptionViewModel(CodeStyleOptions.PreferIsNullCheckOverReferenceEqualityMethod, BasicVSResources.Prefer_Is_Nothing_over_ReferenceEquals, s_preferIsNothingCheckOverReferenceEquals, s_preferIsNothingCheckOverReferenceEquals, Me, optionSet, nothingPreferencesGroupTitle))
End Sub
End Class
End Namespace
......@@ -2563,6 +2563,7 @@ public static OperatorPrecedence GetOperatorPrecedence(this ExpressionSyntax exp
case SyntaxKind.GreaterThanOrEqualExpression:
case SyntaxKind.IsExpression:
case SyntaxKind.AsExpression:
case SyntaxKind.IsPatternExpression:
// From C# spec, 7.3.1:
// Relational and type testing: < > <= >= is as
......
......@@ -1483,9 +1483,7 @@ public SyntaxToken GetIdentifierOfVariableDeclarator(SyntaxNode node)
}
public bool IsIdentifierName(SyntaxNode node)
{
return node.IsKind(SyntaxKind.IdentifierName);
}
=> node.IsKind(SyntaxKind.IdentifierName);
public bool IsLocalDeclarationStatement(SyntaxNode node)
{
......
......@@ -132,5 +132,14 @@ public class CodeStyleOptions
storageLocations: new OptionStorageLocation[] {
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_explicit_tuple_names"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferExplicitTupleNames") });
internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferIsNullCheckOverReferenceEqualityMethod = new PerLanguageOption<CodeStyleOption<bool>>(
nameof(CodeStyleOptions),
nameof(PreferIsNullCheckOverReferenceEqualityMethod),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[]{
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_prefer_is_null_check_over_reference_equality_method"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferIsNullCheckOverReferenceEquals") });
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册