Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update S1200: Fixed generic type counting, excluded more types from counting #1472

Merged
merged 1 commit into from
Jun 15, 2018

Conversation

valhristov
Copy link
Contributor

@valhristov valhristov commented Jun 13, 2018

Fix #1180

}
},
SyntaxKind.ClassDeclaration);
SyntaxKind.ClassDeclaration,
SyntaxKind.StructDeclaration);
Copy link
Contributor Author

@valhristov valhristov Jun 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are also analyzing structs and interfaces now.

@valhristov valhristov force-pushed the val/S1200 branch 2 times, most recently from a873c33 to 4396023 Compare June 13, 2018 12:50
SyntaxKind.ClassDeclaration);
SyntaxKind.ClassDeclaration,
SyntaxKind.StructDeclaration,
SyntaxKind.InterfaceDeclaration);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now handling struct and interface too.

@@ -170,6 +179,7 @@ internal sealed class KnownType
internal static readonly KnownType System_IO_StreamReader = new KnownType("System.IO.StreamReader");
internal static readonly KnownType System_IO_StreamWriter = new KnownType("System.IO.StreamWriter");
internal static readonly KnownType System_IO_TextWriter = new KnownType("System.IO.TextWriter");
internal static readonly KnownType System_Lazy = new KnownType("System.Lazy<T>");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided that Lazy is also worth excluding

@valhristov valhristov force-pushed the val/S1200 branch 2 times, most recently from 35f861e to a2c9f07 Compare June 13, 2018 14:27
{
"id": "S1200",
"message": "Split this class into smaller and more specialized ones to reduce its dependencies on other classes from 30 to the maximum authorized 20 or less.",
"message": "Split this class into smaller and more specialized ones to reduce its dependencies on other classes from 31 to the maximum authorized 30 or less.",
Copy link
Contributor

@duncanp-sonar duncanp-sonar Jun 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The number of classes where the detected dependencies count went up. Is this expected?

sources\akka.net\src\core\Akka.Remote\Endpoint.cs 33->35
sources\akka.net\src\core\Akka.Remote\Transport\ThrottleTransportAdapter.cs 33->35

sources\akka.net\src\core\Akka.Cluster\ClusterMetricsCollector.cs 30->31
sources\akka.net\src\contrib\cluster\Akka.Cluster.Tools\Singleton\ClusterSingletonManager.cs 40->41
sources\akka.net\src\core\Akka.Remote\Endpoint.cs 43->44
sources\akka.net\src\core\Akka.Remote\RemoteWatcher.cs 38->39
sources\akka.net\src\core\Akka.Remote\Transport\AkkaPduCodec.cs 33->34
sources\akka.net\src\core\Akka.Remote\Transport\Helios\HeliosTransport.cs 34->35

Copy link
Contributor Author

@valhristov valhristov Jun 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is normal, we started to count some things (method return types for example) and stopped counting other - bounded generic types (e.g. IDictionary<int> and IDictionary<string> are counted as +1, not +2 as before).


var indexerProperty = property as IndexerDeclarationSyntax;
if (indexerProperty?.ExpressionBody != null)
public IList<ITypeSymbol> DependentTypes { get; } = new List<ITypeSymbol>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be a set rather than a list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it could probably save some iterations, I guess

}
public override void VisitClassDeclaration(ClassDeclarationSyntax node)
{
// don't drill down in child classes, but walk the original
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for this - because child classes will be analysed separately?
If so, it would be worth adding as a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you call the base method the walker will continue through all descendant nodes, including inner classes, thus collecting dependent types from inner classes, which I think is not expected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed this morning, this might be worth adding as a comment in the C# RSPEC sub task

namedType.TypeKind != TypeKind.Enum &&
!namedType.IsAny(ignoredTypes);

private static IEnumerable<INamedTypeSymbol> ExpandGenericTypes(INamedTypeSymbol namedType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some documentation of the intention would be useful e.g. what is expected for Dictionary<string, MyClass>?

type.TypeKind != TypeKind.Enum &&
type.TypeKind != TypeKind.Pointer &&
!type.IsAny(typesExcludedFromCoupling);
public override void VisitObjectCreationExpression(ObjectCreationExpressionSyntax node)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are calls to static classes captured?

Copy link
Contributor

@duncanp-sonar duncanp-sonar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is inheritance handled?

  • tests for calls to methods/properties/constants on static classes
  • tests for catch statements e.g. catch (MyException e)
  • attributes?
  • if a class references itself, will that be counted as a reference?

@valhristov valhristov force-pushed the val/S1200 branch 2 times, most recently from 076d756 to 5a12bc0 Compare June 15, 2018 11:52
@valhristov
Copy link
Contributor Author

Added the requested tests. The current class is not counted as dependency.

Attributes are metadata, imo they should not be counted (and are not)

@valhristov valhristov changed the title Exclude Action, Func and Task from counting; included method return t… Update S1200: Fixed generic type counting, excluded more types from counting Jun 15, 2018
@duncanp-sonar duncanp-sonar merged commit e3303ff into master Jun 15, 2018
@ghost ghost removed the Status: Needs Review label Jun 15, 2018
@antoinebj
Copy link

Hi,

I was the author of #1180 for which this PR represents the fix. I must have missed some updates.

Anyway, if it's not too late, I noticed the following comment in the code:
void Foo<T>(Dictionary<string,List<T>>) where T : IEnumerable<int> should return Dictionary, string, List, IEnumerable and int

Have you considered not counting IEnumerable in such cases?
That interface is implemented by Dictionary (and also List), meaning that if the dictionary instance is put in a variable of type IEnumerable, or passed to a private method that only needs to process it as an enumerable, it will count as an additional dependency, which hardly seems fair.
More generically, if a coupling to type A is identified, and A implements or derives from B, should B really also be counted when it is used elsewhere in the class?

@valhristov
Copy link
Contributor Author

Hi @antoinebj it is never late to create a new ticket :)

I played a bit with Visual Studio's Code Metrics (not sure if it is available in all editions, though) and for the following code it seems to count all classes and interfaces:

public class Class1 // Class coupling is 3
{
    public void Foo(Dictionary<string, int> dd, IDictionary<string, int> d, IEnumerable<int> e) { }
}

I decided to try to be relatively close to it's way of counting. VS however, counts Task, Action, etc. types, but we are a bit more permissive and do not count some "core" classes.

@valhristov valhristov deleted the val/S1200 branch June 15, 2018 14:30
@antoinebj
Copy link

@valhristov
Whether Microsoft's method is the most correct or not, I understand the need for some consistency at the expense of whatever might be perceived as more accurate.
On the other hand, it could be argued that the counting "fairness" matters more in Sonar because it's used for quality gates (in a broad sense) that may frustrate developers, whereas VS Code Metrics seems to serve more as a reporting tool that does not have much impact.
Anyway, I'll try the updated rule when it is made available and open a new issue if I have any further questions or suggestions.
Thanks 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants