@@ -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