Skip to content

Commit

Permalink
Review update
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-mikula-sonarsource committed Jun 6, 2024
1 parent 4ebcc90 commit 2c735df
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
namespace SonarAnalyzer.Rules.CSharp;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ClassShouldNotBeAbstract : SonarDiagnosticAnalyzer<SyntaxKind>
public sealed class AbstractClassToInterface : SonarDiagnosticAnalyzer<SyntaxKind>
{
private const string DiagnosticId = "S1694";

Expand All @@ -30,7 +30,7 @@ public sealed class ClassShouldNotBeAbstract : SonarDiagnosticAnalyzer<SyntaxKin
protected override string MessageFormat => "Convert this 'abstract' {0} to an interface.";
protected override ILanguageFacade<SyntaxKind> Language => CSharpFacade.Instance;

public ClassShouldNotBeAbstract() : base(DiagnosticId) { }
public AbstractClassToInterface() : base(DiagnosticId) { }

protected override void Initialize(SonarAnalysisContext context) =>
context.RegisterSymbolAction(
Expand All @@ -39,9 +39,9 @@ protected override void Initialize(SonarAnalysisContext context) =>
var symbol = (INamedTypeSymbol)c.Symbol;
if (symbol.IsClass()
&& symbol.IsAbstract
&& !HasInheritedAbstractMembers(symbol)
&& symbol.BaseType.Is(KnownType.System_Object)
&& !IsRecordWithParameters(symbol)
&& AbstractTypeShouldBeInterface(symbol))
&& AllMethodsAreAbstract(symbol))
{
foreach (var declaringSyntaxReference in symbol.DeclaringSyntaxReferences)
{
Expand All @@ -61,36 +61,18 @@ protected override void Initialize(SonarAnalysisContext context) =>
},
SymbolKind.NamedType);

private static bool HasInheritedAbstractMembers(INamedTypeSymbol symbol)
{
var baseTypes = symbol.BaseType.GetSelfAndBaseTypes().ToList();
var abstractMethods = baseTypes.SelectMany(GetAllAbstractMethods);
var baseTypesAndSelf = baseTypes.Concat(new[] { symbol }).ToList();
var overrideMethods = baseTypesAndSelf.SelectMany(GetAllOverrideMethods);
var overriddenMethods = overrideMethods.Select(x => x.OverriddenMethod);
var stillAbstractMethods = abstractMethods.Except(overriddenMethods);

return stillAbstractMethods.Any();
}

private static IEnumerable<IMethodSymbol> GetAllAbstractMethods(INamedTypeSymbol symbol) =>
GetAllMethods(symbol).Where(x => x.IsAbstract);

private static IEnumerable<IMethodSymbol> GetAllOverrideMethods(INamedTypeSymbol symbol) =>
GetAllMethods(symbol).Where(x => x.IsOverride);

private static bool IsRecordWithParameters(ISymbol symbol) =>
symbol.DeclaringSyntaxReferences.Any(x => x.GetSyntax() is { } node
&& RecordDeclarationSyntaxWrapper.IsInstance(node)
&& ((RecordDeclarationSyntaxWrapper)node).ParameterList is { } parameterList
&& parameterList.Parameters.Count > 0);

private static bool AbstractTypeShouldBeInterface(INamedTypeSymbol symbol)
private static bool AllMethodsAreAbstract(INamedTypeSymbol symbol)
{
var methods = GetAllMethods(symbol);
return symbol.BaseType.Is(KnownType.System_Object) && methods.Any() && methods.All(x => x.IsAbstract);
return methods.Any() && methods.All(x => x.IsAbstract);
}

private static IList<IMethodSymbol> GetAllMethods(INamedTypeSymbol symbol) =>
symbol.GetMembers().OfType<IMethodSymbol>().Where(x => !x.IsImplicitlyDeclared && !ConstructorKinds.Contains(x.MethodKind)).ToList();
private static IMethodSymbol[] GetAllMethods(INamedTypeSymbol symbol) =>
symbol.GetMembers().OfType<IMethodSymbol>().Where(x => !x.IsImplicitlyDeclared && !ConstructorKinds.Contains(x.MethodKind)).ToArray();
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public partial class SonarAnalysisContextTest
// S2953 - MAIN only
new TestSetup("DisposeNotImplementingDispose.cs", new DisposeNotImplementingDispose()),
// S1694 - MAIN only
new TestSetup("ClassShouldNotBeAbstract.cs", new ClassShouldNotBeAbstract()),
new TestSetup("ClassShouldNotBeAbstract.cs", new AbstractClassToInterface()),
});

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,25 @@
namespace SonarAnalyzer.Test.Rules;

[TestClass]
public class ClassShouldNotBeAbstractTest
public class AbstractClassToInterfaceTest
{
private readonly VerifierBuilder builder = new VerifierBuilder<ClassShouldNotBeAbstract>();
private readonly VerifierBuilder builder = new VerifierBuilder<AbstractClassToInterface>();

[TestMethod]
public void ClassShouldNotBeAbstract() =>
builder.AddPaths("ClassShouldNotBeAbstract.cs").Verify();
builder.AddPaths("AbstractClassToInterface.cs").Verify();

#if NET

[TestMethod]
public void ClassShouldNotBeAbstract_CSharp9() =>
builder.AddPaths("ClassShouldNotBeAbstract.CSharp9.cs")
builder.AddPaths("AbstractClassToInterface.CSharp9.cs")
.WithOptions(ParseOptionsHelper.FromCSharp9)
.Verify();

[TestMethod]
public void ClassShouldNotBeAbstract_CSharp11() =>
builder.AddPaths("ClassShouldNotBeAbstract.CSharp11.cs")
builder.AddPaths("AbstractClassToInterface.CSharp11.cs")
.WithOptions(ParseOptionsHelper.FromCSharp11)
.Verify();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
using System;
using System.Collections.Generic;

public abstract partial class A
public abstract partial class PartialMixed
{
public abstract void X();
}
public abstract partial class A
public abstract partial class PartialMixed
{
public void Y() { }
}

public abstract partial class PartialAbstract // Noncompliant
{
public abstract void X();
}
public abstract partial class PartialAbstract // Noncompliant
{
public abstract void Y();
}

public abstract class Empty
{
}
Expand Down Expand Up @@ -67,7 +76,6 @@ public int getRed()

public abstract class LampCompliant
{

private bool switchLamp = false;

public abstract void glow();
Expand Down Expand Up @@ -105,3 +113,18 @@ public abstract class View3Derived : SomeUnknownType // Error [CS0246]
public string Content3 { get; }
public override int Content1 { get { return 1; } }
}

public abstract class WithConstructor // Noncompliant
{
public abstract void ToOverride();

static WithConstructor()
{
// Do something here
}

public WithConstructor()
{
// Do something here
}
}

0 comments on commit 2c735df

Please sign in to comment.