diff --git a/ChangeLog.md b/ChangeLog.md index 403fe6a98b..64305fad4f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix analyzer [RCS1159](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1159) ([PR](https://github.com/dotnet/roslynator/pull/1390)) - Fix analyzer [RCS1019](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1019) ([PR](https://github.com/dotnet/roslynator/pull/1402)) - Fix analyzer [RCS1250](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1250) ([PR](https://github.com/dotnet/roslynator/pull/1403)) +- Fix analyzer [RCS1060](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1060) ([PR](https://github.com/dotnet/roslynator/pull/1401)) - Fix code fix for [CS8600](https://josefpihrt.github.io/docs/roslynator/fixes/CS8600) changing the wrong type when casts or `var` are involved ([PR](https://github.com/dotnet/roslynator/pull/1393) by @jroessel) ## [4.10.0] - 2024-01-24 diff --git a/src/Analyzers/CSharp/Analysis/DeclareEachTypeInSeparateFileAnalyzer.cs b/src/Analyzers/CSharp/Analysis/DeclareEachTypeInSeparateFileAnalyzer.cs index ea8118e612..5f9c1fdc28 100644 --- a/src/Analyzers/CSharp/Analysis/DeclareEachTypeInSeparateFileAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/DeclareEachTypeInSeparateFileAnalyzer.cs @@ -52,15 +52,15 @@ void Analyze(SyntaxList members) { foreach (MemberDeclarationSyntax member in members) { - SyntaxKind kind = member.Kind(); - - if (kind == SyntaxKind.NamespaceDeclaration) +#if ROSLYN_4_0 + if (member is BaseNamespaceDeclarationSyntax namespaceDeclaration) +#else + if (member is NamespaceDeclarationSyntax namespaceDeclaration) +#endif { - var namespaceDeclaration = (NamespaceDeclarationSyntax)member; - Analyze(namespaceDeclaration.Members); } - else if (SyntaxFacts.IsTypeDeclaration(kind)) + else if (SyntaxFacts.IsTypeDeclaration(member.Kind())) { if (firstTypeDeclaration is null) { @@ -93,16 +93,18 @@ private static void ReportDiagnostic(SyntaxNodeAnalysisContext context, MemberDe private static bool ContainsSingleNamespaceWithSingleNonNamespaceMember(SyntaxList members) { - MemberDeclarationSyntax member = members.SingleOrDefault(shouldThrow: false); - - if (member?.Kind() != SyntaxKind.NamespaceDeclaration) - return false; - - var namespaceDeclaration = (NamespaceDeclarationSyntax)member; +#if ROSLYN_4_0 + if (members.SingleOrDefault(shouldThrow: false) is BaseNamespaceDeclarationSyntax namespaceDeclaration) +#else + if (members.SingleOrDefault(shouldThrow: false) is NamespaceDeclarationSyntax namespaceDeclaration) +#endif + { + MemberDeclarationSyntax member = namespaceDeclaration.Members.SingleOrDefault(shouldThrow: false); - member = namespaceDeclaration.Members.SingleOrDefault(shouldThrow: false); + return member is not null + && member.Kind() != SyntaxKind.NamespaceDeclaration; + } - return member is not null - && member.Kind() != SyntaxKind.NamespaceDeclaration; + return false; } } diff --git a/src/Refactorings.xml b/src/Refactorings.xml index 4244b47c3f..0b530882be 100644 --- a/src/Refactorings.xml +++ b/src/Refactorings.xml @@ -389,7 +389,7 @@ if (y) while statement - + extract_type_declaration_to_new_file class declaration diff --git a/src/Refactorings/CSharp/Refactorings/ExtractTypeDeclarationToNewFileRefactoring.cs b/src/Refactorings/CSharp/Refactorings/ExtractTypeDeclarationToNewFileRefactoring.cs index cf0fb9597c..9e1676b680 100644 --- a/src/Refactorings/CSharp/Refactorings/ExtractTypeDeclarationToNewFileRefactoring.cs +++ b/src/Refactorings/CSharp/Refactorings/ExtractTypeDeclarationToNewFileRefactoring.cs @@ -48,7 +48,12 @@ public static void ComputeRefactorings(RefactoringContext context, DelegateDecla private static void ComputeRefactorings(RefactoringContext context, MemberDeclarationSyntax memberDeclaration, SyntaxToken identifier) { if (identifier.Span.Contains(context.Span) - && memberDeclaration.IsParentKind(SyntaxKind.NamespaceDeclaration, SyntaxKind.CompilationUnit) + && memberDeclaration.IsParentKind( + SyntaxKind.NamespaceDeclaration, +#if ROSLYN_4_0 + SyntaxKind.FileScopedNamespaceDeclaration, +#endif + SyntaxKind.CompilationUnit) && context.IsRootCompilationUnit && context.Workspace.Kind != WorkspaceKind.MiscellaneousFiles && ExtractTypeDeclarationToNewDocumentRefactoring.GetNonNestedTypeDeclarations((CompilationUnitSyntax)context.Root).Skip(1).Any()) diff --git a/src/Tests/Analyzers.Tests/RCS1060DeclareEachTypeInSeparateFileTests.cs b/src/Tests/Analyzers.Tests/RCS1060DeclareEachTypeInSeparateFileTests.cs new file mode 100644 index 0000000000..fa5fb6cc18 --- /dev/null +++ b/src/Tests/Analyzers.Tests/RCS1060DeclareEachTypeInSeparateFileTests.cs @@ -0,0 +1,60 @@ +// Copyright (c) .NET Foundation and Contributors. 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; +using Roslynator.CSharp.CodeFixes; +using Roslynator.Testing.CSharp; +using Xunit; + +namespace Roslynator.CSharp.Analysis.Tests; + +public class RCS1060DeclareEachTypeInSeparateFileTests : AbstractCSharpDiagnosticVerifier +{ + public override DiagnosticDescriptor Descriptor => DiagnosticRules.DeclareEachTypeInSeparateFile; + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.DeclareEachTypeInSeparateFile)] + public async Task Test_Namespace() + { + await VerifyDiagnosticAndFixAsync(""" +namespace N +{ + public class [|C1|] + { + } + + public class [|C2|] + { + } +} +""", """ +namespace N +{ + public class C2 + { + } +} +"""); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.DeclareEachTypeInSeparateFile)] + public async Task Test_FileScopedNamespace() + { + await VerifyDiagnosticAndFixAsync(""" +namespace N; + +public class [|C1|] +{ +} + +public class [|C2|] +{ +} +""", """ +namespace N; + +public class C2 +{ +} +"""); + } +} diff --git a/src/Tests/Refactorings.Tests/RR0046ExtractTypeDeclarationToNewFileTests.cs b/src/Tests/Refactorings.Tests/RR0046ExtractTypeDeclarationToNewFileTests.cs new file mode 100644 index 0000000000..ad45a03848 --- /dev/null +++ b/src/Tests/Refactorings.Tests/RR0046ExtractTypeDeclarationToNewFileTests.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Roslynator.Testing.CSharp; +using Xunit; + +namespace Roslynator.CSharp.Refactorings.Tests; + +public class RR0046ExtractTypeDeclarationToNewFileTests : AbstractCSharpRefactoringVerifier +{ + public override string RefactoringId { get; } = RefactoringIdentifiers.ExtractTypeDeclarationToNewFile; + + [Fact, Trait(Traits.Refactoring, RefactoringIdentifiers.ExtractTypeDeclarationToNewFile)] + public async Task Test_Namespace() + { + await VerifyRefactoringAsync(""" +namespace N +{ + public class C1 + { + + } + + public class [||]C2 + { + + } +} +""", """ +namespace N +{ + public class C1 + { + + } +} +""", equivalenceKey: EquivalenceKey.Create(RefactoringId)); + } + + [Fact, Trait(Traits.Refactoring, RefactoringIdentifiers.ExtractTypeDeclarationToNewFile)] + public async Task Test_FileScopedNamespace() + { + await VerifyRefactoringAsync(""" +namespace N; + +public class C1 +{ +} + +public class [||]C2 +{ +} +""", """ +namespace N; + +public class C1 +{ +} + +""", equivalenceKey: EquivalenceKey.Create(RefactoringId)); + } +} diff --git a/src/VisualStudio/.roslynatorconfig b/src/VisualStudio/.roslynatorconfig index e79f3fc117..4105211ce2 100644 --- a/src/VisualStudio/.roslynatorconfig +++ b/src/VisualStudio/.roslynatorconfig @@ -1,12 +1,9 @@ is_global = true -roslnator.max_line_length = 140 -roslynator.prefix_field_identifier_with_underscore = false - -roslynator.refactoring.RR0010.enabled = false -roslynator.refactoring.RR0012.enabled = false -roslynator.refactoring.RR0066.enabled = false -roslynator.refactoring.RR0088.enabled = false -roslynator.refactoring.RR0138.enabled = false -roslynator.refactoring.RR0171.enabled = false -roslynator.refactoring.RR0188.enabled = false +roslynator_refactoring.convert_foreach_to_for_and_reverse_loop.enabled = false +roslynator_refactoring.expand_initializer.enabled = false +roslynator_refactoring.extract_type_declaration_to_new_file.enabled = false +roslynator_refactoring.introduce_constructor.enabled = false +roslynator_refactoring.remove_all_documentation_comments.enabled = false +roslynator_refactoring.replace_method_with_property.enabled = false +roslynator_refactoring.use_string_empty_instead_of_empty_string_literal.enabled = false diff --git a/src/Workspaces.Common/CSharp/Refactorings/ExtractTypeDeclarationToNewDocumentRefactoring.cs b/src/Workspaces.Common/CSharp/Refactorings/ExtractTypeDeclarationToNewDocumentRefactoring.cs index 6aa1183d02..9d34f13827 100644 --- a/src/Workspaces.Common/CSharp/Refactorings/ExtractTypeDeclarationToNewDocumentRefactoring.cs +++ b/src/Workspaces.Common/CSharp/Refactorings/ExtractTypeDeclarationToNewDocumentRefactoring.cs @@ -54,7 +54,8 @@ private static SyntaxNode RemoveNode(MemberDeclarationSyntax member) int index = members.IndexOf(member); - if (index == 0 + if (member.IsParentKind(SyntaxKind.NamespaceDeclaration) + && index == 0 && index < members.Count - 1) { MemberDeclarationSyntax nextMember = newMemberList[index]; @@ -113,10 +114,12 @@ private static IEnumerable GetNonNestedTypeDeclarations { SyntaxKind kind = member.Kind(); - if (kind == SyntaxKind.NamespaceDeclaration) +#if ROSLYN_4_0 + if (member is BaseNamespaceDeclarationSyntax namespaceDeclaration) +#else + if (member is NamespaceDeclarationSyntax namespaceDeclaration) +#endif { - var namespaceDeclaration = (NamespaceDeclarationSyntax)member; - foreach (MemberDeclarationSyntax member2 in GetNonNestedTypeDeclarations(namespaceDeclaration.Members)) yield return member2; }