Generalizing type member order rule
This commit is contained in:
parent
2467cf2ade
commit
bf6a163f46
|
|
@ -1,16 +1,13 @@
|
||||||
using Microsoft.Dnx.Compilation.CSharp;
|
using Microsoft.Dnx.Compilation.CSharp;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.StandardsPolice
|
namespace Microsoft.StandardsPolice
|
||||||
{
|
{
|
||||||
// This project can output the Class library as a NuGet Package.
|
|
||||||
// To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
|
|
||||||
public class StandardsPoliceCompileModule : ICompileModule
|
public class StandardsPoliceCompileModule : ICompileModule
|
||||||
{
|
{
|
||||||
public void BeforeCompile(BeforeCompileContext context)
|
public void BeforeCompile(BeforeCompileContext context)
|
||||||
|
|
@ -71,7 +68,7 @@ namespace Microsoft.StandardsPolice
|
||||||
if (typeSymbol.Locations.Any(location => location.IsInSource))
|
if (typeSymbol.Locations.Any(location => location.IsInSource))
|
||||||
{
|
{
|
||||||
RuleFieldPrivateKeyword(diagnostics, typeSymbol);
|
RuleFieldPrivateKeyword(diagnostics, typeSymbol);
|
||||||
RuleNestedTypesAreLast(diagnostics, typeSymbol);
|
RuleMembersAreInCorrectOrder(diagnostics, typeSymbol, MapClassMembers);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var member in typeSymbol.GetTypeMembers())
|
foreach (var member in typeSymbol.GetTypeMembers())
|
||||||
|
|
@ -80,6 +77,7 @@ namespace Microsoft.StandardsPolice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void RuleFieldPrivateKeyword(IList<Diagnostic> diagnostics, INamedTypeSymbol typeSymbol)
|
private static void RuleFieldPrivateKeyword(IList<Diagnostic> diagnostics, INamedTypeSymbol typeSymbol)
|
||||||
{
|
{
|
||||||
foreach (var member in typeSymbol.GetMembers().OfType<IFieldSymbol>())
|
foreach (var member in typeSymbol.GetMembers().OfType<IFieldSymbol>())
|
||||||
|
|
@ -119,27 +117,72 @@ namespace Microsoft.StandardsPolice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RuleNestedTypesAreLast(IList<Diagnostic> diagnostics, INamedTypeSymbol typeSymbol)
|
enum ClassZone
|
||||||
{
|
{
|
||||||
var otherThingsWereLower = false;
|
Ignored,
|
||||||
var members = typeSymbol.GetMembers().Reverse().ToArray();
|
BeforeStart,
|
||||||
foreach (var member in members)
|
Fields,
|
||||||
|
Constructors,
|
||||||
|
Properties,
|
||||||
|
OtherThings,
|
||||||
|
NestedTypes,
|
||||||
|
AfterEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RuleMembersAreInCorrectOrder(IList<Diagnostic> diagnostics, INamedTypeSymbol typeSymbol, Func<ISymbol, ClassZone> mapZone)
|
||||||
|
{
|
||||||
|
var currentZone = ClassZone.BeforeStart;
|
||||||
|
foreach (var member in typeSymbol.GetMembers())
|
||||||
{
|
{
|
||||||
var namedType = member as INamedTypeSymbol;
|
var memberZone = mapZone(member);
|
||||||
if (namedType == null || (namedType.TypeKind != TypeKind.Class && namedType.TypeKind != TypeKind.Enum && namedType.TypeKind != TypeKind.Struct))
|
if (memberZone == ClassZone.Ignored)
|
||||||
{
|
{
|
||||||
if (member.IsImplicitlyDeclared == false)
|
|
||||||
{
|
|
||||||
otherThingsWereLower = true;
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (otherThingsWereLower)
|
if (currentZone < memberZone)
|
||||||
|
{
|
||||||
|
currentZone = memberZone;
|
||||||
|
}
|
||||||
|
if (memberZone >= ClassZone.OtherThings)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (memberZone < currentZone)
|
||||||
{
|
{
|
||||||
if (member.Locations.Count() == 1)
|
if (member.Locations.Count() == 1)
|
||||||
{
|
{
|
||||||
diagnostics.Add(Diagnostic.Create(
|
diagnostics.Add(Diagnostic.Create(
|
||||||
"SP1003", "StandardsPolice", $"nested types must be last {typeSymbol.Name}:{member.Name}",
|
"SP1003", "StandardsPolice", $"{memberZone} like {typeSymbol.Name}::{member.Name} shouldn't be after {currentZone}",
|
||||||
|
DiagnosticSeverity.Warning,
|
||||||
|
DiagnosticSeverity.Warning,
|
||||||
|
false,
|
||||||
|
3,
|
||||||
|
location: member.Locations.Single()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentZone = ClassZone.AfterEnd;
|
||||||
|
foreach (var member in typeSymbol.GetMembers())
|
||||||
|
{
|
||||||
|
var memberZone = mapZone(member);
|
||||||
|
if (memberZone == ClassZone.Ignored)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (currentZone > memberZone)
|
||||||
|
{
|
||||||
|
currentZone = memberZone;
|
||||||
|
}
|
||||||
|
if (memberZone <= ClassZone.OtherThings)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (memberZone > currentZone)
|
||||||
|
{
|
||||||
|
if (member.Locations.Count() == 1)
|
||||||
|
{
|
||||||
|
diagnostics.Add(Diagnostic.Create(
|
||||||
|
"SP1003", "StandardsPolice", $"{memberZone} like {typeSymbol.Name}::{member.Name} shouldn't be before {currentZone}",
|
||||||
DiagnosticSeverity.Warning,
|
DiagnosticSeverity.Warning,
|
||||||
DiagnosticSeverity.Warning,
|
DiagnosticSeverity.Warning,
|
||||||
false,
|
false,
|
||||||
|
|
@ -150,6 +193,42 @@ namespace Microsoft.StandardsPolice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ClassZone MapClassMembers(ISymbol member)
|
||||||
|
{
|
||||||
|
if (member.IsImplicitlyDeclared)
|
||||||
|
{
|
||||||
|
return ClassZone.Ignored;
|
||||||
|
}
|
||||||
|
if (member.Kind == SymbolKind.Field)
|
||||||
|
{
|
||||||
|
return ClassZone.Fields;
|
||||||
|
}
|
||||||
|
if (member.Kind == SymbolKind.Method)
|
||||||
|
{
|
||||||
|
var method = (IMethodSymbol)member;
|
||||||
|
if (method.MethodKind == MethodKind.Constructor ||
|
||||||
|
method.MethodKind == MethodKind.StaticConstructor)
|
||||||
|
{
|
||||||
|
return ClassZone.Constructors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (member.Kind == SymbolKind.Property)
|
||||||
|
{
|
||||||
|
return ClassZone.Properties;
|
||||||
|
}
|
||||||
|
if (member.Kind == SymbolKind.NamedType)
|
||||||
|
{
|
||||||
|
var namedType = (INamedTypeSymbol)member;
|
||||||
|
if (namedType.TypeKind == TypeKind.Class ||
|
||||||
|
namedType.TypeKind == TypeKind.Enum ||
|
||||||
|
namedType.TypeKind == TypeKind.Struct)
|
||||||
|
{
|
||||||
|
return ClassZone.NestedTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ClassZone.OtherThings;
|
||||||
|
}
|
||||||
|
|
||||||
public void AfterCompile(AfterCompileContext context)
|
public void AfterCompile(AfterCompileContext context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue