Skip to content

Commit 5f7fd3b

Browse files
Merge pull request #19 from jonathanalgar/quality-improvements
Small code quality improvements
2 parents 8801004 + 24590df commit 5f7fd3b

2 files changed

Lines changed: 89 additions & 70 deletions

File tree

src/CustomCode-Analyzer/Analyzer.cs

Lines changed: 87 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -421,9 +421,7 @@ private void InitializeCompilationAnalysis(CompilationStartAnalysisContext compi
421421
private void AnalyzeStruct(SymbolAnalysisContext context, INamedTypeSymbol typeSymbol)
422422
{
423423
// Check if the struct has the OSStructure attribute
424-
bool hasOSStructureAttribute = typeSymbol.GetAttributes()
425-
.Any(a => a.AttributeClass?.Name is "OSStructureAttribute" or "OSStructure");
426-
424+
bool hasOSStructureAttribute = HasAttribute(typeSymbol, OSStructureAttributeNames);
427425
if (!hasOSStructureAttribute) return;
428426

429427
// Retrieve the actual syntax node for reporting precise locations
@@ -472,8 +470,7 @@ Location GetMemberLocation()
472470
}
473471

474472
// Check if the member has the OSStructureField attribute
475-
var hasOSStructureField = member.GetAttributes()
476-
.Any(a => a.AttributeClass?.Name is "OSStructureFieldAttribute" or "OSStructureField");
473+
bool hasOSStructureField = HasAttribute(member, OSStructureFieldAttributeNames);
477474

478475
// If the member is decorated with OSStructureField but not public, report a diagnostic
479476
if (hasOSStructureField && !member.DeclaredAccessibility.HasFlag(Accessibility.Public))
@@ -520,8 +517,7 @@ Location GetMemberLocation()
520517
}
521518

522519
// Check if the member has the OSIgnore attribute
523-
var hasOSIgnore = member.GetAttributes()
524-
.Any(a => a.AttributeClass?.Name is "OSIgnoreAttribute" or "OSIgnore");
520+
bool hasOSIgnore = HasAttribute(member, OSIgnoreAttributeNames);
525521

526522
// If the member is decorated with OSIgnore but not public, report a diagnostic
527523
if (hasOSIgnore && !member.DeclaredAccessibility.HasFlag(Accessibility.Public))
@@ -576,8 +572,7 @@ private static void AnalyzeInterface(
576572
ConcurrentDictionary<string, (InterfaceDeclarationSyntax Syntax, INamedTypeSymbol Symbol)> osInterfaces)
577573
{
578574
// Check if the interface has the OSInterface attribute
579-
var osInterfaceAttr = typeSymbol.GetAttributes()
580-
.FirstOrDefault(a => a.AttributeClass?.Name is "OSInterfaceAttribute" or "OSInterface");
575+
var osInterfaceAttr = GetAttribute(typeSymbol, OSInterfaceAttributeNames);
581576
if (osInterfaceAttr == null) return;
582577

583578
var syntaxRef = typeSymbol.DeclaringSyntaxReferences.FirstOrDefault();
@@ -675,15 +670,12 @@ private static void AnalyzeMethod(SymbolAnalysisContext context, IMethodSymbol m
675670
var containingType = methodSymbol.ContainingType;
676671

677672
// Determine if the method is in an OSInterface or in a class that implements one
678-
var hasOSInterfaceAttribute = containingType.GetAttributes()
679-
.Any(a => a.AttributeClass?.Name is "OSInterfaceAttribute" or "OSInterface");
673+
var hasOSInterfaceAttribute = HasAttribute(containingType, OSInterfaceAttributeNames);
680674

681675
bool implementsOSInterface = false;
682676
if (!hasOSInterfaceAttribute)
683677
{
684-
implementsOSInterface = containingType.Interfaces
685-
.Any(i => i.GetAttributes()
686-
.Any(a => a.AttributeClass?.Name is "OSInterfaceAttribute" or "OSInterface"));
678+
implementsOSInterface = containingType.Interfaces.Any(i => HasAttribute(i, OSInterfaceAttributeNames));
687679
}
688680

689681
// If this method is part of the OSInterface or an implementation of it, check for underscores
@@ -746,7 +738,7 @@ private static void AnalyzeMethod(SymbolAnalysisContext context, IMethodSymbol m
746738
context.Compilation,
747739
t => !t.DeclaringSyntaxReferences.IsEmpty &&
748740
t.TypeKind == TypeKind.Struct &&
749-
!t.GetAttributes().Any(attr => attr.AttributeClass?.Name is "OSStructureAttribute" or "OSStructure"));
741+
!HasAttribute(t, OSStructureAttributeNames));
750742

751743
// Determine if any structure type used in the parameter is not decorated with OSStructure
752744
bool usesUndecoratedStruct = allStructuresNotExposed.Any(s =>
@@ -785,8 +777,7 @@ private static void AnalyzeClass(SymbolAnalysisContext context, INamedTypeSymbol
785777
// Check each interface implemented by this class to see if it has [OSInterface]
786778
foreach (var implementedInterface in typeSymbol.Interfaces)
787779
{
788-
var hasOSInterfaceAttribute = implementedInterface.GetAttributes()
789-
.Any(a => a.AttributeClass?.Name is "OSInterfaceAttribute" or "OSInterface");
780+
bool hasOSInterfaceAttribute = HasAttribute(implementedInterface, OSInterfaceAttributeNames);
790781

791782
if (hasOSInterfaceAttribute)
792783
{
@@ -921,8 +912,7 @@ private static void AnalyzeCompilationEnd(
921912
// Check for duplicate struct names across the compilation
922913
var allStructures = GetAllTypesInCompilation(
923914
context.Compilation,
924-
t => t.TypeKind == TypeKind.Struct &&
925-
t.GetAttributes().Any(a => a.AttributeClass?.Name is "OSStructureAttribute" or "OSStructure"));
915+
t => t.TypeKind == TypeKind.Struct && HasAttribute(t, OSStructureAttributeNames));
926916

927917
#pragma warning disable RS1024
928918
var duplicates = allStructures.GroupBy(x => x.Name).Where(g => g.Count() > 1);
@@ -945,6 +935,61 @@ private static void AnalyzeCompilationEnd(
945935
}
946936
}
947937

938+
939+
/// <summary>
940+
/// Valid names for the OSInterface attribute.
941+
/// </summary>
942+
private static readonly HashSet<string> OSInterfaceAttributeNames = new()
943+
{
944+
"OSInterfaceAttribute",
945+
"OSInterface"
946+
};
947+
948+
/// <summary>
949+
/// Valid names for the OSStructure attribute.
950+
/// </summary>
951+
private static readonly HashSet<string> OSStructureAttributeNames = new()
952+
{
953+
"OSStructureAttribute",
954+
"OSStructure"
955+
};
956+
957+
/// <summary>
958+
/// Valid names for the OSStructureField attribute.
959+
/// </summary>
960+
private static readonly HashSet<string> OSStructureFieldAttributeNames = new()
961+
{
962+
"OSStructureFieldAttribute",
963+
"OSStructureField"
964+
};
965+
966+
/// <summary>
967+
/// Valid names for the OSIgnore attribute.
968+
/// </summary>
969+
private static readonly HashSet<string> OSIgnoreAttributeNames = new()
970+
{
971+
"OSIgnoreAttribute",
972+
"OSIgnore"
973+
};
974+
975+
/// <summary>
976+
/// Contains valid names for OSIgnore attribute, used to mark fields that should be ignored during serialization.
977+
/// </summary>
978+
private static bool HasAttribute(ISymbol symbol, HashSet<string> attributeNames)
979+
{
980+
return symbol.GetAttributes()
981+
.Any(attr => attributeNames.Contains(attr.AttributeClass?.Name));
982+
}
983+
984+
/// <summary>
985+
/// Checks if a symbol has any of the specified attributes from the provided set of names.
986+
/// </summary>
987+
private static AttributeData GetAttribute(ISymbol symbol, HashSet<string> attributeNames)
988+
{
989+
return symbol.GetAttributes()
990+
.FirstOrDefault(attr => attributeNames.Contains(attr.AttributeClass?.Name));
991+
}
992+
948993
/// <summary>
949994
/// Retrieves all <see cref="INamedTypeSymbol"/>s in the current compilation
950995
/// that match the given predicate. Traverses through all namespaces in a DFS manner.
@@ -978,18 +1023,16 @@ private static IEnumerable<INamedTypeSymbol> GetAllTypesInCompilation(
9781023
/// A set of valid parameter types for <see cref="UnsupportedDefaultValueRule"/>.
9791024
/// Anything not in this set (and is not null for reference types) is considered invalid.
9801025
/// </summary>
981-
private static readonly HashSet<string> ValidParameterTypes = new()
982-
{
983-
"String",
984-
"Int32",
985-
"Int64",
986-
"Single",
987-
"Double",
988-
"Decimal",
989-
"Boolean",
990-
"DateTime",
991-
"Byte[]"
992-
};
1026+
private static readonly ImmutableHashSet<SpecialType> ValidParameterSpecialTypes = ImmutableHashSet.Create(
1027+
SpecialType.System_String,
1028+
SpecialType.System_Int32,
1029+
SpecialType.System_Int64,
1030+
SpecialType.System_Single,
1031+
SpecialType.System_Double,
1032+
SpecialType.System_Decimal,
1033+
SpecialType.System_Boolean,
1034+
SpecialType.System_DateTime
1035+
);
9931036

9941037
/// <summary>
9951038
/// Checks whether a parameter's default value is a compile-time constant of a supported type.
@@ -1006,8 +1049,11 @@ private static bool IsValidParameterDefaultValue(IParameterSymbol parameter)
10061049
{
10071050
return true;
10081051
}
1009-
// Check if the type is in the set of valid parameter types
1010-
if (!ValidParameterTypes.Contains(parameter.Type.Name))
1052+
1053+
// Check if type is supported
1054+
if (!ValidParameterSpecialTypes.Contains(parameter.Type.SpecialType) &&
1055+
!(parameter.Type is IArrayTypeSymbol arrayType &&
1056+
arrayType.ElementType.SpecialType == SpecialType.System_Byte))
10111057
{
10121058
return false;
10131059
}
@@ -1039,37 +1085,34 @@ private static bool IsValidParameterType(ITypeSymbol typeSymbol, Compilation com
10391085
{
10401086
return false;
10411087
}
1088+
10421089
// Check for primitive or special types
1043-
if (typeSymbol.SpecialType is
1044-
SpecialType.System_String or
1045-
SpecialType.System_Int32 or
1046-
SpecialType.System_Int64 or
1047-
SpecialType.System_Boolean or
1048-
SpecialType.System_Decimal or
1049-
SpecialType.System_Single or
1050-
SpecialType.System_Double or
1051-
SpecialType.System_DateTime)
1090+
if (ValidParameterSpecialTypes.Contains(typeSymbol.SpecialType))
10521091
{
10531092
return true;
10541093
}
1094+
10551095
// Check if the type is a byte array
10561096
if (typeSymbol is IArrayTypeSymbol { ElementType.SpecialType: SpecialType.System_Byte })
10571097
{
10581098
return true;
10591099
}
1100+
10601101
// Check if the type is a struct with [OSStructure]
1061-
if (typeSymbol.TypeKind == TypeKind.Struct &&
1062-
typeSymbol.GetAttributes().Any(a => a.AttributeClass?.Name is "OSStructureAttribute" or "OSStructure"))
1102+
if (typeSymbol is INamedTypeSymbol { TypeKind: TypeKind.Struct } structType &&
1103+
HasAttribute(structType, OSStructureAttributeNames))
10631104
{
10641105
return true;
10651106
}
1107+
10661108
// Check if the type is a generic type that implements IEnumerable
10671109
if (typeSymbol is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol &&
10681110
namedTypeSymbol.AllInterfaces.Any(i => i.ToDisplayString() == "System.Collections.IEnumerable"))
10691111
{
10701112
var typeArg = namedTypeSymbol.TypeArguments.FirstOrDefault();
10711113
return typeArg != null && IsValidParameterType(typeArg, compilation);
10721114
}
1115+
10731116
// If none match, it's not a supported parameter type
10741117
return false;
10751118
}

0 commit comments

Comments
 (0)