diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..38f043af6d066a7de27cca54390720f225a4521e --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -0,0 +1,261 @@ +// 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.CodeFixes.DeclareAsNullable; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.DeclareAsNullable +{ + [Trait(Traits.Feature, Traits.Features.CodeActionsDeclareAsNullable)] + public class CSharpDeclareAsNullableCodeFixTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + { + internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) + => (null, new CSharpDeclareAsNullableCodeFixProvider()); + + private static readonly TestParameters s_nullableFeature = new TestParameters(parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8)); + + [Fact] + public async Task FixAll() + { + await TestInRegularAndScript1Async( +@"class Program +{ + static string M() + { + return {|FixAllInDocument:null|}; + } + static string M2(bool b) + { + if (b) + return null; + else + return null; + } +}", +@"class Program +{ + static string? M() + { + return null; + } + static string? M2(bool b) + { + if (b) + return null; + else + return null; + } +}", parameters: s_nullableFeature); + } + + [Fact] + public async Task FixReturnType() + { + await TestInRegularAndScript1Async( +@"class Program +{ + static string M() + { + return [|null|]; + } +}", +@"class Program +{ + static string? M() + { + return null; + } +}", parameters: s_nullableFeature); + } + + [Fact] + public async Task FixReturnType_WithTrivia() + { + await TestInRegularAndScript1Async( +@"class Program +{ + static /*before*/ string /*after*/ M() + { + return [|null|]; + } +}", +@"class Program +{ + static /*before*/ string? /*after*/ M() + { + return null; + } +}", parameters: s_nullableFeature); + } + + [Fact] + public async Task FixReturnType_ArrowBody() + { + await TestInRegularAndScript1Async( +@"class Program +{ + static string M() => [|null|]; +}", +@"class Program +{ + static string? M() => null; +}", parameters: s_nullableFeature); + } + + [Fact] + [WorkItem(26639, "https://github.com/dotnet/roslyn/issues/26639")] + public async Task FixReturnType_LocalFunction_ArrowBody() + { + await TestMissingInRegularAndScriptAsync( +@"class Program +{ + static void M() + { + string local() => [|null|]; + } +}", parameters: s_nullableFeature); + } + + [Fact] + [WorkItem(26639, "https://github.com/dotnet/roslyn/issues/26639")] + public async Task FixLocalFunctionReturnType() + { + await TestMissingInRegularAndScriptAsync( +@"class Program +{ + void M() + { + string local() + { + return [|null|]; + } + } +}", parameters: s_nullableFeature); + } + + [Fact] + public async Task NoFixAlreadyNullableReturnType() + { + await TestMissingInRegularAndScriptAsync( +@"class Program +{ + static string? M() + { + return [|null|]; + } +}", parameters: s_nullableFeature); + } + + [Fact] + [WorkItem(26628, "https://github.com/dotnet/roslyn/issues/26628")] + public async Task FixField() + { + await TestMissingInRegularAndScriptAsync( +@"class Program +{ + string x = [|null|]; +}", parameters: s_nullableFeature); + } + + [Fact] + public async Task FixLocalDeclaration() + { + await TestInRegularAndScript1Async( +@"class Program +{ + static void M() + { + string x = [|null|]; + } +}", +@"class Program +{ + static void M() + { + string? x = null; + } +}", parameters: s_nullableFeature); + } + + [Fact] + public async Task FixLocalDeclaration_WithVar() + { + await TestMissingInRegularAndScriptAsync( +@"class Program +{ + static void M() + { + var x = [|null|]; + } +}", parameters: s_nullableFeature); + } + + [Fact] + public async Task NoFixMultiDeclaration() + { + await TestMissingInRegularAndScriptAsync( +@"class Program +{ + static void M() + { + string x = [|null|], y = null; + } +}", parameters: s_nullableFeature); + } + + [Fact] + [WorkItem(26628, "https://github.com/dotnet/roslyn/issues/26628")] + public async Task FixPropertyDeclaration() + { + await TestMissingInRegularAndScriptAsync( +@"class Program +{ + string x { get; set; } = [|null|]; +}", parameters: s_nullableFeature); + } + + [Fact] + public async Task FixPropertyDeclaration_WithReturnNull() + { + await TestInRegularAndScript1Async( +@"class Program +{ + string x { get { return [|null|]; } } +}", +@"class Program +{ + string? x { get { return null; } } +}", parameters: s_nullableFeature); + } + + [Fact] + public async Task FixPropertyDeclaration_ArrowBody() + { + await TestInRegularAndScript1Async( +@"class Program +{ + string x => [|null|]; +}", +@"class Program +{ + string? x => null; +}", parameters: s_nullableFeature); + } + + [Fact] + [WorkItem(26626, "https://github.com/dotnet/roslyn/issues/26626")] + public async Task FixOptionalParameter() + { + await TestMissingInRegularAndScriptAsync( +@"class Program +{ + static void M(string x = [|null|]) { } +}", parameters: s_nullableFeature); + } + } +} diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs index 881a84f93d3b272b3b85815c7bad9e8ef4ea61bc..45ceef98ee92d82bf87fc4b5927a7031988a4d7e 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs @@ -314,6 +314,15 @@ internal class CSharpFeaturesResources { } } + /// + /// Looks up a localized string similar to Declare as nullable. + /// + internal static string Declare_as_nullable { + get { + return ResourceManager.GetString("Declare_as_nullable", resourceCulture); + } + } + /// /// Looks up a localized string similar to deconstruction. /// diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index dc26cb8b8afb6015bec020922f4a33121f564d58..8f5cd39808120c7ceb6db648c4025c47d55ee48b 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -162,6 +162,9 @@ Remove Unnecessary Cast + + Declare as nullable + Cast is redundant diff --git a/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..36ab46636f470d8766c17c4c2fddbf991ab7dbdc --- /dev/null +++ b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs @@ -0,0 +1,155 @@ +// 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.Generic; +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.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.DeclareAsNullable +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.DeclareAsNullable), Shared] + internal class CSharpDeclareAsNullableCodeFixProvider : SyntaxEditorBasedCodeFixProvider + { + // warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // warning CS8600: Converting null literal or possible null value to non-nullable type. + public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create("CS8625", "CS8600"); + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var diagnostic = context.Diagnostics.First(); + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + + var declarationTypeToFix = TryGetDeclarationTypeToFix(node); + if (declarationTypeToFix == null) + { + return; + } + + context.RegisterCodeFix(new MyCodeAction( + c => FixAsync(context.Document, diagnostic, c)), + context.Diagnostics); + } + + protected override Task FixAllAsync( + Document document, ImmutableArray diagnostics, + SyntaxEditor editor, CancellationToken cancellationToken) + { + var root = editor.OriginalRoot; + + // a method can have multiple `return null;` statements, but we should only fix its return type once + var alreadyHandled = PooledHashSet.GetInstance(); + + foreach (var diagnostic in diagnostics) + { + var node = diagnostic.Location.FindNode(getInnermostNodeForTie: true, cancellationToken); + MakeDeclarationNullable(document, editor, node, alreadyHandled); + } + + alreadyHandled.Free(); + return Task.CompletedTask; + } + + private static void MakeDeclarationNullable(Document document, SyntaxEditor editor, SyntaxNode node, HashSet alreadyHandled) + { + var declarationTypeToFix = TryGetDeclarationTypeToFix(node); + if (declarationTypeToFix != null && alreadyHandled.Add(declarationTypeToFix)) + { + var fixedDeclaration = SyntaxFactory.NullableType(declarationTypeToFix.WithoutTrivia()).WithTriviaFrom(declarationTypeToFix); + editor.ReplaceNode(declarationTypeToFix, fixedDeclaration); + } + } + + private static TypeSyntax TryGetDeclarationTypeToFix(SyntaxNode node) + { + if (!node.IsKind(SyntaxKind.NullLiteralExpression)) + { + return null; + } + + if (node.IsParentKind(SyntaxKind.ReturnStatement)) + { + var containingMember = node.GetAncestors().FirstOrDefault(a => a.IsKind( + SyntaxKind.MethodDeclaration, SyntaxKind.PropertyDeclaration, SyntaxKind.ParenthesizedLambdaExpression, SyntaxKind.SimpleLambdaExpression, + SyntaxKind.LocalFunctionStatement, SyntaxKind.AnonymousMethodExpression, SyntaxKind.ConstructorDeclaration, SyntaxKind.DestructorDeclaration, + SyntaxKind.OperatorDeclaration, SyntaxKind.IndexerDeclaration, SyntaxKind.EventDeclaration)); + + if (containingMember == null) + { + return null; + } + + switch (containingMember) + { + case MethodDeclarationSyntax method: + // string M() { return null; } + return method.ReturnType; + + case PropertyDeclarationSyntax property: + // string x { get { return null; } } + return property.Type; + + default: + return null; + } + } + + // string x = null; + if (node.Parent?.Parent?.IsParentKind(SyntaxKind.VariableDeclaration) == true) + { + var variableDeclaration = (VariableDeclarationSyntax)node.Parent.Parent.Parent; + if (variableDeclaration.Variables.Count != 1) + { + // string x = null, y = null; + return null; + } + + return variableDeclaration.Type; + } + + // string x { get; set; } = null; + if (node.Parent.IsParentKind(SyntaxKind.PropertyDeclaration) == true) + { + var propertyDeclaration = (PropertyDeclarationSyntax)node.Parent.Parent; + return propertyDeclaration.Type; + } + + // void M(string x = null) { } + if (node.Parent.IsParentKind(SyntaxKind.Parameter) == true) + { + var parameter = (ParameterSyntax)node.Parent.Parent; + return parameter.Type; + } + + // static string M() => null; + if (node.IsParentKind(SyntaxKind.ArrowExpressionClause) && node.Parent.IsParentKind(SyntaxKind.MethodDeclaration)) + { + var arrowMethod = (MethodDeclarationSyntax)node.Parent.Parent; + return arrowMethod.ReturnType; + } + + return null; + } + + private class MyCodeAction : CodeAction.DocumentChangeAction + { + public MyCodeAction(Func> createChangedDocument) : + base(CSharpFeaturesResources.Declare_as_nullable, + createChangedDocument, + CSharpFeaturesResources.Declare_as_nullable) + { + } + } + } +} diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index f8761d71a9d41367d115e079390870469e4effd4..e73a09533c0f0ca3930f7110e28cd02666ab1465 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 32411ec4fc6a6f7d922dfae99de474e8f159d1eb..75126ad51d31c385c29414c2f33c3828e13e8ed6 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index ab75cbb73bd4df63dcd67b966492880ddc22ad7a..bb7da9b1016a7a57a2146ef1e7251ee39adcfd61 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index 03ea42f302e91bbdee1a48521ab94802c34ee658..c1717e7069d4469a0ee8a93c7be54074ebf8a9e8 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index 767ba006601940565b1e666c159a09399f5adcbc..e01ead94d152701ad2994102d8c31b496e95ff7e 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index 3d40a2169b5a7e6539de9c119a23e5faa8bb5a88..4896a4b15d8d2bafbd1209d6d991f584886ecf8b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 341cd5e07858a4df820209a40b52a01d52e9b10d..28d5baff4a8c946bfbef9acf243c572b996d8b78 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index 30e151bfa70e5afb86aa990f6c9f71007cf7b07e..611d700ff06c4756612a1c16699dbb9a88ecb032 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index f2e5bd3ed4aa7075a556144f0573d272ee726e21..d4893aa9e06d6cee257f58998f3b212f627e3bcd 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 50b203cf632dfaebc71d53356095cb070875fda5..0fda801d63432c79336dc24008e46cff3b66c492 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index 575fe250f0fbc07b9894486df9685008635f6bc5..d1393cc65258c8ba3a323f7a21efd0ddb81868d1 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index 05c36f2a656ac0644f85b9fd665f6d82cda4be6e..0e011636791cfed1d622f08e2c39b019e4b38a83 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index ff4ec49a7202a8dd4aff785d535c673598bb5af9..99915df38a5c5e8391e16aa5364d405804fa0bd7 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -642,6 +642,11 @@ Convert to 'for' + + Declare as nullable + Declare as nullable + + \ No newline at end of file diff --git a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs index 4d5b57ba0bd0f57e2bae94313bd32fb6db4e8a78..b229b977dfbf6dc794796e0fe4c11051e287f662 100644 --- a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -39,6 +39,7 @@ internal static class PredefinedCodeFixProviderNames public const string PopulateSwitch = nameof(PopulateSwitch); public const string QualifyMemberAccess = nameof(QualifyMemberAccess); public const string RemoveUnnecessaryCast = nameof(RemoveUnnecessaryCast); + public const string DeclareAsNullable = nameof(DeclareAsNullable); public const string RemoveUnnecessaryImports = nameof(RemoveUnnecessaryImports); public const string RemoveUnreachableCode = nameof(RemoveUnreachableCode); public const string RemoveUnusedLocalFunction = nameof(RemoveUnusedLocalFunction); diff --git a/src/Test/Utilities/Portable/Traits/Traits.cs b/src/Test/Utilities/Portable/Traits/Traits.cs index 3e5f506a708de5413dec1430e7008121a3a531e6..0f55451a66b20fc3d82082b346894f7fc1a21d4c 100644 --- a/src/Test/Utilities/Portable/Traits/Traits.cs +++ b/src/Test/Utilities/Portable/Traits/Traits.cs @@ -100,6 +100,7 @@ public static class Features public const string CodeActionsRemoveByVal = "CodeActions.RemoveByVal"; public const string CodeActionsRemoveDocCommentNode = "CodeActions.RemoveDocCommentNode"; public const string CodeActionsRemoveUnnecessaryCast = "CodeActions.RemoveUnnecessaryCast"; + public const string CodeActionsDeclareAsNullable = "CodeActions.DeclareAsNullable"; public const string CodeActionsRemoveUnusedLocalFunction = "CodeActions.RemoveUnusedLocalFunction"; public const string CodeActionsRemoveUnusedVariable = "CodeActions.RemoveUnusedVariable"; public const string CodeActionsRemoveUnnecessaryImports = "CodeActions.RemoveUnnecessaryImports"; diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs index 24da6c93b0b23ffffc1969f2662b19e5c8b082e7..71d09641ab59e9b608b37446936b5b03a0d1040a 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs @@ -100,6 +100,17 @@ public static bool IsKind(this SyntaxNode node, SyntaxKind kind1, SyntaxKind kin return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3 || csharpKind == kind4 || csharpKind == kind5 || csharpKind == kind6; } + public static bool IsKind(this SyntaxNode node, SyntaxKind kind1, SyntaxKind kind2, SyntaxKind kind3, SyntaxKind kind4, SyntaxKind kind5, SyntaxKind kind6, SyntaxKind kind7, SyntaxKind kind8, SyntaxKind kind9, SyntaxKind kind10, SyntaxKind kind11) + { + if (node == null) + { + return false; + } + + var csharpKind = node.Kind(); + return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3 || csharpKind == kind4 || csharpKind == kind5 || csharpKind == kind6 || csharpKind == kind7 || csharpKind == kind8 || csharpKind == kind9 || csharpKind == kind10 || csharpKind == kind11; + } + /// /// Returns the list of using directives that affect . The list will be returned in /// top down order.