diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f387988 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,158 @@ + +[*] +charset = utf-8-bom +end_of_line = crlf +trim_trailing_whitespace = false +insert_final_newline = false +indent_style = space +indent_size = 2 + +# Microsoft .NET properties +csharp_new_line_before_catch = false +csharp_new_line_before_else = false +csharp_new_line_before_finally = false +csharp_new_line_before_members_in_object_initializers = false +csharp_new_line_before_open_brace = none +csharp_preferred_modifier_order = public, protected, async, private, file, new, internal, abstract, virtual, static, override, sealed, extern, unsafe, volatile, required, readonly:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True +dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field +dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef +dotnet_naming_rule.unity_serialized_field_rule.severity = warning +dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style +dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = * +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds = +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +# ReSharper properties +resharper_align_multiline_extends_list = true +resharper_align_multiline_parameter = true +resharper_align_multiline_type_parameter_constraints = true +resharper_align_multiline_type_parameter_list = true +resharper_arguments_skip_single = true +resharper_blank_lines_after_block_statements = 0 +resharper_blank_lines_after_control_transfer_statements = 1 +resharper_blank_lines_after_start_comment = 0 +resharper_blank_lines_after_using_list = 0 +resharper_blank_lines_around_property = 0 +resharper_blank_lines_around_single_line_type = 0 +resharper_braces_for_for = required +resharper_braces_for_foreach = required +resharper_braces_for_ifelse = not_required +resharper_braces_for_using = not_required +resharper_braces_for_while = required +resharper_braces_redundant = false +resharper_csharp_blank_lines_around_invocable = 0 +resharper_csharp_blank_lines_around_namespace = 0 +resharper_csharp_blank_lines_inside_region = 0 +resharper_csharp_continuous_indent_multiplier = 2 +resharper_csharp_empty_block_style = together_same_line +resharper_csharp_indent_size = 2 +resharper_csharp_keep_blank_lines_in_declarations = 100 +resharper_csharp_max_line_length = 301 +resharper_csharp_remove_blank_lines_near_braces_in_code = false +resharper_csharp_remove_blank_lines_near_braces_in_declarations = false +resharper_csharp_tab_width = 2 +resharper_csharp_wrap_ternary_expr_style = wrap_if_long +resharper_formatter_off_tag = @formatter:off +resharper_formatter_on_tag = @formatter:on +resharper_formatter_tags_enabled = true +resharper_indent_preprocessor_region = do_not_change +resharper_instance_members_qualify_declared_in = +resharper_local_function_body = expression_body +resharper_method_or_operator_body = expression_body +resharper_parentheses_redundancy_style = remove +resharper_place_accessorholder_attribute_on_same_line = false +resharper_place_expr_accessor_on_single_line = true +resharper_place_expr_property_on_single_line = true +resharper_place_simple_anonymousmethod_on_single_line = false +resharper_place_simple_embedded_statement_on_same_line = true +resharper_place_simple_initializer_on_single_line = false +resharper_place_simple_property_pattern_on_single_line = false +resharper_place_single_method_argument_lambda_on_same_line = false +resharper_show_autodetect_configure_formatting_tip = false +resharper_use_indent_from_vs = false +resharper_wrap_array_initializer_style = chop_always +resharper_wrap_object_and_collection_initializer_style = chop_always +resharper_wrap_property_pattern = chop_always +resharper_xmldoc_indent_size = 2 +resharper_xmldoc_tab_width = 2 + +# ReSharper inspection severities +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_this_qualifier_highlighting = hint +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +resharper_mvc_action_not_resolved_highlighting = warning +resharper_mvc_area_not_resolved_highlighting = warning +resharper_mvc_controller_not_resolved_highlighting = warning +resharper_mvc_masterpage_not_resolved_highlighting = warning +resharper_mvc_partial_view_not_resolved_highlighting = warning +resharper_mvc_template_not_resolved_highlighting = warning +resharper_mvc_view_component_not_resolved_highlighting = warning +resharper_mvc_view_component_view_not_resolved_highlighting = warning +resharper_mvc_view_not_resolved_highlighting = warning +resharper_razor_assembly_not_resolved_highlighting = warning +resharper_redundant_base_qualifier_highlighting = warning +resharper_suggest_var_or_type_built_in_types_highlighting = hint +resharper_suggest_var_or_type_elsewhere_highlighting = hint +resharper_suggest_var_or_type_simple_types_highlighting = hint +resharper_web_config_module_not_resolved_highlighting = warning +resharper_web_config_type_not_resolved_highlighting = warning +resharper_web_config_wrong_module_highlighting = warning + +[*.cs] +indent_style = space +indent_size = 2 +tab_width = 2 + +[*.resx] +indent_style = space +indent_size = 4 + +[{*.overridetasks,*.targets,*.tasks}] +indent_style = space +indent_size = 4 + +[{*.androidproj,*.ccproj,*.csproj}] +indent_style = space +indent_size = 4 + +[{*.har,*.jsb2,*.jsb3,*.json,*.jsonc,*.postman_collection,*.postman_collection.json,*.postman_environment,*.postman_environment.json,*.sonarlint/*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,.ws-context,connectedMode.json,jest.config}] +indent_style = space +indent_size = 2 + +[{*.mustache}] +indent_style = space +indent_size = 2 + +[{*.markdown,*.md}] +indent_style = space +indent_size = 4 + +[{*.http,*.rest}] +indent_style = space +indent_size = 4 + +[*.{appxmanifest,asax,ascx,aspx,axaml,blockshader,build,c,c++,c++m,cc,ccm,cginc,compute,cp,cpp,cppm,cshtml,cu,cuh,cxx,cxxm,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,h++,hh,hlsl,hlsli,hlslinc,hp,hpp,hxx,icc,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,mxx,nuspec,paml,razor,resw,resx,shader,shaderFoundry,skin,tcc,tpp,urtshader,usf,ush,uxml,vb,xaml,xamlx,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 diff --git a/.idea/.idea.SourceGenerators/.idea/templateLanguages.xml b/.idea/.idea.SourceGenerators/.idea/templateLanguages.xml new file mode 100644 index 0000000..d185621 --- /dev/null +++ b/.idea/.idea.SourceGenerators/.idea/templateLanguages.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/FastInject/src/Retro.FastInject/Generation/DependencyExtensions.cs b/FastInject/src/Retro.FastInject/Generation/DependencyExtensions.cs index 51197b0..d0cfc05 100644 --- a/FastInject/src/Retro.FastInject/Generation/DependencyExtensions.cs +++ b/FastInject/src/Retro.FastInject/Generation/DependencyExtensions.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Retro.FastInject.Annotations; using Retro.FastInject.Comparers; @@ -143,10 +141,7 @@ SpecialType.System_Nullable_T or } private static IEnumerable GetFactoryServices(IMethodSymbol methodSymbol) { - var factoryAttribute = methodSymbol.GetAttributes() - .Select(a => a.TryGetFactoryOverview(out var info) ? info : null) - .FirstOrDefault(); - if (factoryAttribute == null) { + if (!methodSymbol.TryGetFactoryOverview(out var factoryAttribute)) { return []; } diff --git a/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Templates/AttributeInfoTemplate.mustache b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Templates/AttributeInfoTemplate.mustache index d7674d7..b1a4b09 100644 --- a/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Templates/AttributeInfoTemplate.mustache +++ b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Templates/AttributeInfoTemplate.mustache @@ -13,7 +13,63 @@ internal static class {{Name}}Extensions { public static {{Name}} Get{{Name}}(this AttributeData data) { return data.TryGet{{Name}}(out var info) ? info : throw new InvalidOperationException("Cannot create {{AttributeName}}Info"); - } + } + {{^AllowMultiple}} + {{#ValidOnAssembly}} + + public static {{Name}} Get{{Name}}(this IAssemblySymbol assembly) { + return assembly.TryGet{{Name}}(out var info) ? info : throw new InvalidOperationException("Cannot create {{Name}}"); + } + {{/ValidOnAssembly}} + {{#ValidOnModule}} + + public static {{Name}} Get{{Name}}(this IModuleSymbol module) { + return module.TryGet{{Name}}(out var info) ? info : throw new InvalidOperationException("Cannot create {{Name}}"); + } + {{/ValidOnModule}} + {{#ValidOnNamedType}} + + public static {{Name}} Get{{Name}}(this INamedTypeSymbol type) { + return type.TryGet{{Name}}(out var info) ? info : throw new InvalidOperationException("Cannot create {{Name}}"); + } + {{/ValidOnNamedType}} + {{#ValidOnGenericParameter}} + + public static {{Name}} Get{{Name}}(this ITypeParameterSymbol type) { + return type.TryGet{{Name}}(out var info) ? info : throw new InvalidOperationException("Cannot create {{Name}}"); + } + {{/ValidOnGenericParameter}} + {{#ValidOnAnyMethod}} + + public static {{Name}} Get{{Name}}(this IMethodSymbol method) { + return method.TryGet{{Name}}(out var info) ? info : throw new InvalidOperationException("Cannot create {{Name}}"); + } + {{/ValidOnAnyMethod}} + {{#ValidOnProperty}} + + public static {{Name}} Get{{Name}}(this IPropertySymbol property) { + return property.TryGet{{Name}}(out var info) ? info : throw new InvalidOperationException("Cannot create {{Name}}"); + } + {{/ValidOnProperty}} + {{#ValidOnField}} + + public static {{Name}} Get{{Name}}(this IFieldSymbol fieldSymbol) { + return fieldSymbol.TryGet{{Name}}(out var info) ? info : throw new InvalidOperationException("Cannot create {{Name}}"); + } + {{/ValidOnField}} + {{#ValidOnEvent}} + + public static {{Name}} Get{{Name}}(this IEventSymbol eventSymbol) { + return eventSymbol.TryGet{{Name}}(out var info) ? info : throw new InvalidOperationException("Cannot create {{Name}}"); + } + {{/ValidOnEvent}} + {{#ValidOnReturnValue}} + + public static {{Name}} Get{{Name}}OnReturnValue(this IMethodSymbol method) { + return method.TryGet{{Name}}OnReturnValue(out var info) ? info : throw new InvalidOperationException("Cannot create {{Name}}"); + } + {{/ValidOnReturnValue}} + {{/AllowMultiple}} public static bool TryGet{{Name}}(this AttributeData data, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { var args = data.ConstructorArguments; @@ -56,6 +112,104 @@ internal static class {{Name}}Extensions { info = {{#IsValueType}}default{{/IsValueType}}{{^IsValueType}}null{{/IsValueType}};; return false; } + {{^AllowMultiple}} + {{#ValidOnAssembly}} + + public static bool TryGet{{Name}}(this IAssemblySymbol assembly, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + return assembly.GetAttributes().TryGet{{Name}}(out info); + } + {{/ValidOnAssembly}} + {{#ValidOnModule}} + + public static bool TryGet{{Name}}(this IModuleSymbol module, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + return module.GetAttributes().TryGet{{Name}}(out info); + } + {{/ValidOnModule}} + {{#ValidOnNamedType}} + + public static bool TryGet{{Name}}(this INamedTypeSymbol type, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + {{^ValidOnAnyType}} + if (type.TypeKind is not {{#ValidOnTypes}}TypeKind.{{Kind}}{{^IsLast}} or {{/IsLast}}{{/ValidOnTypes}}) { + throw new InvalidOperationException("The attribute '{{AttributeTypeofName}}' is only valid one of the following types: {{#ValidOnTypes}}{{Kind}}{{^IsLast}}, {{/IsLast}}{{/ValidOnTypes}}"); + } + + {{/ValidOnAnyType}} + return type.GetAttributes().TryGet{{Name}}(out info); + } + {{/ValidOnNamedType}} + {{#ValidOnGenericParameter}} + + public static bool TryGet{{Name}}(this ITypeParameterSymbol type, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + return type.GetAttributes().TryGet{{Name}}(out info); + } + {{/ValidOnGenericParameter}} + {{#ValidOnAnyMethod}} + + public static bool TryGet{{Name}}(this IMethodSymbol method, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + {{#OnlyValidOnConstructor}} + if (handlerMethod.MethodKind != MethodKind.Constructor) { + throw new InvalidOperationException("The attribute '{{AttributeTypeofName}}' is only valid on constructors."); + } + + {{/OnlyValidOnConstructor}} + return method.GetAttributes().TryGet{{Name}}(out info); + } + {{/ValidOnAnyMethod}} + {{#ValidOnProperty}} + + public static bool TryGet{{Name}}(this IPropertySymbol property, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + return property.GetAttributes().TryGet{{Name}}(out info); + } + {{/ValidOnProperty}} + {{#ValidOnField}} + + public static bool TryGet{{Name}}(this IFieldSymbol fieldSymbol, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + return fieldSymbol.GetAttributes().TryGet{{Name}}(out info); + } + {{/ValidOnField}} + {{#ValidOnEvent}} + + public static bool TryGet{{Name}}(this IEventSymbol eventSymbol, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + return eventSymbol.GetAttributes().TryGet{{Name}}(out info); + } + {{/ValidOnEvent}} + {{#ValidOnReturnValue}} + + public static bool TryGet{{Name}}OnReturnValue(this IMethodSymbol method, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + return method.GetReturnTypeAttributes().TryGet{{Name}}(out info); + } + {{/ValidOnReturnValue}} + {{#ValidOnSymbols}} + + public static bool TryGet{{../Name}}(this {{Name}} symbol, {{^if ../IsValueType}}[NotNullWhen(true)] {{/if}}out {{../Name}}{{^if ../IsValueType}}?{{/if}} info) { + return symbol.GetAttributes().TryGet{{../Name}}(out info); + } + {{/ValidOnSymbols}} + + private static bool TryGet{{Name}}(this IEnumerable attributes, {{^IsValueType}}[NotNullWhen(true)] {{/IsValueType}}out {{Name}}{{^IsValueType}}?{{/IsValueType}} info) { + var result = attributes.Get{{Name}}s() + {{#IsValueType}} + .Cast<{{Name}}?>() + {{/IsValueType}} + .SingleOrDefault(); + + {{#IsValueType}} + if (result.HasValue) { + info = result.Value; + return true; + } + {{/IsValueType}} + {{^IsValueType}} + if (result is not null) { + info = result; + return true; + } + {{/IsValueType}} + + info = {{#IsValueType}}default{{/IsValueType}}{{^IsValueType}}null{{/IsValueType}}; + return false; + } + {{/AllowMultiple}} public static IEnumerable<{{Name}}> Get{{Name}}s(this IEnumerable attributeDatas) { foreach (var data in attributeDatas) { @@ -64,5 +218,73 @@ internal static class {{Name}}Extensions { } } } + {{#AllowMultiple}} + {{#ValidOnAssembly}} + + public static IEnumerable<{{Name}}> Get{{Name}}s(this IAssemblySymbol assembly) { + return assembly.GetAttributes().Get{{Name}}s(); + } + {{/ValidOnAssembly}} + {{#ValidOnModule}} + + public static IEnumerable<{{Name}}> Get{{Name}}s(this IModuleSymbol module) { + return module.GetAttributes().Get{{Name}}s(); + } + {{/ValidOnModule}} + {{#ValidOnNamedType}} + + public static IEnumerable<{{Name}}> Get{{Name}}s(this INamedTypeSymbol type) { + {{^ValidOnAnyType}} + if (type.TypeKind is not {{#ValidOnTypes}}TypeKind.{{Kind}}{{^IsLast}} or {{/IsLast}}{{/ValidOnTypes}}) { + throw new InvalidOperationException("The attribute '{{AttributeTypeofName}}' is only valid one of the following types: {{#ValidOnTypes}}{{Kind}}{{^IsLast}}, {{/IsLast}}{{/ValidOnTypes}}"); + } + + {{/ValidOnAnyType}} + return type.GetAttributes().Get{{Name}}s(); + } + {{/ValidOnNamedType}} + {{#ValidOnGenericParameter}} + + public static IEnumerable<{{Name}}> Get{{Name}}s(this ITypeParameterSymbol type) { + return type.GetAttributes().Get{{Name}}s(); + } + {{/ValidOnGenericParameter}} + {{#ValidOnAnyMethod}} + + public static IEnumerable<{{Name}}> Get{{Name}}s(this IMethodSymbol method) { + {{#OnlyValidOnConstructor}} + if (handlerMethod.MethodKind != MethodKind.Constructor) { + throw new InvalidOperationException("The attribute '{{AttributeTypeofName}}' is only valid on constructors."); + } + + {{/OnlyValidOnConstructor}} + return method.GetAttributes().Get{{Name}}s(); + } + {{/ValidOnAnyMethod}} + {{#ValidOnProperty}} + + public static IEnumerable<{{Name}}> Get{{Name}}s(this IPropertySymbol property) { + return property.GetAttributes().Get{{Name}}s(); + } + {{/ValidOnProperty}} + {{#ValidOnField}} + + public static IEnumerable<{{Name}}> Get{{Name}}s(this IFieldSymbol fieldSymbol) { + return fieldSymbol.GetAttributes().Get{{Name}}s(); + } + {{/ValidOnField}} + {{#ValidOnEvent}} + + public static IEnumerable<{{Name}}> Get{{Name}}s(this IEventSymbol eventSymbol) { + return eventSymbol.GetAttributes().Get{{Name}}s(); + } + {{/ValidOnEvent}} + {{#ValidOnReturnValue}} + + public static IEnumerable<{{Name}}> Get{{Name}}sOnReturnValue(this IMethodSymbol method) { + return method.GetReturnTypeAttributes().Get{{Name}}s(); + } + {{/ValidOnReturnValue}} + {{/AllowMultiple}} } \ No newline at end of file diff --git a/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Attributes/AttributeExtensions.cs b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Attributes/AttributeExtensions.cs index 10ca1e8..cb5811d 100644 --- a/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Attributes/AttributeExtensions.cs +++ b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Attributes/AttributeExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.CodeAnalysis; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; using Retro.SourceGeneratorUtilities.Utilities.Model.Attributes; using Retro.SourceGeneratorUtilities.Utilities.Types; #if SOURCE_UTILS_GENERATOR @@ -99,7 +100,28 @@ public static bool HasMatchingConstructor(this AttributeData attributeData, para /// it is inheritable. /// public static AttributeUsageInfo GetUsageInfo(this AttributeData attributeData) { - var attributeUsage = attributeData.AttributeClass?.GetAttributes() + return attributeData.AttributeClass?.GetUsageInfo() ?? throw new InvalidOperationException("Symbol is not an attribute"); + } + + /// + /// Retrieves the of the specified + /// if it represents an attribute type. + /// + /// + /// The for which to retrieve the attribute usage information. + /// + /// + /// The for the specified representing an attribute. + /// + /// + /// Thrown if the provided symbol does not represent an attribute or has an invalid attribute usage configuration. + /// + public static AttributeUsageInfo GetUsageInfo(this INamedTypeSymbol symbol) { + if (!symbol.IsAssignableTo()) { + throw new InvalidOperationException("Symbol is not an attribute"); + } + + var attributeUsage = symbol.GetAttributes() .SingleOrDefault(a => a.AttributeClass?.IsAssignableTo() ?? false); if (attributeUsage is null) { diff --git a/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Attributes/AttributeInfoTypeExtensions.cs b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Attributes/AttributeInfoTypeExtensions.cs index 46fae1a..a79bf8a 100644 --- a/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Attributes/AttributeInfoTypeExtensions.cs +++ b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Attributes/AttributeInfoTypeExtensions.cs @@ -125,9 +125,13 @@ public static DiagnosticResult ExtractAttributeInfoTy }) .ToImmutableArray(); + var usageInfo = attributeType.GetUsageInfo(); + return validatedConstructors .Combine(validatedProperties, (c, p) => new AttributeInfoTypeOverview(typeSymbol, attributeType) { + AllowMultiple = usageInfo.AllowMultiple, + ValidOn = usageInfo.ValidOn, Constructors = c, TypeParameters = typeParameters, Properties = p, diff --git a/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Model/Attributes/AttributeInfoTypeOverview.cs b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Model/Attributes/AttributeInfoTypeOverview.cs index a2656ef..5743041 100644 --- a/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Model/Attributes/AttributeInfoTypeOverview.cs +++ b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Model/Attributes/AttributeInfoTypeOverview.cs @@ -66,6 +66,227 @@ public record AttributeInfoTypeOverview(INamedTypeSymbol ModelSymbol, INamedType /// public bool IsUnboundGeneric => AttributeSymbol.IsGenericType && AttributeSymbol.TypeArguments.All(a => a is ITypeParameterSymbol); + /// + /// Indicates whether multiple instances of the attribute can be applied to a target. + /// + /// + /// This property specifies if the attribute is allowable to be applied more than once + /// to the same program element, offering greater flexibility in its usage when set to true. + /// + public required bool AllowMultiple { get; init; } + + /// + /// Gets the attribute targets that specify the program elements + /// on which the attribute is valid. + /// + /// + /// This property provides access to a combination of flags, + /// indicating where the attribute can be applied, such as classes, methods, parameters, etc. + /// + public required AttributeTargets ValidOn { get; init; } + + /// + /// Gets a value indicating whether the attribute can be applied to an assembly. + /// + /// + /// This property evaluates the defined attribute targets and checks if the + /// flag is set, indicating the attribute's applicability. + /// + public bool ValidOnAssembly => ValidOn.HasFlag(AttributeTargets.Assembly); + + /// + /// Indicates whether the attribute is valid on a module. + /// + /// + /// This property evaluates whether the flag + /// is set in the ValidOn property, which determines the applicability of the attribute to a module. + /// + public bool ValidOnModule => ValidOn.HasFlag(AttributeTargets.Module); + + /// + /// Determines whether the attribute can be applied to a class. + /// + /// + /// This property checks if the flag is set in the + /// property, indicating that the attribute is valid for use on class types. + /// + public bool ValidOnClass => ValidOn.HasFlag(AttributeTargets.Class); + + /// + /// Indicates whether the attribute can be applied to struct types. + /// + /// + /// This property evaluates whether the flag + /// is included in the defined targets of the attribute. + /// + public bool ValidOnStruct => ValidOn.HasFlag(AttributeTargets.Struct); + + /// + /// Indicates whether the attribute can be applied to an interface. + /// + /// + /// This property determines if the attribute is valid on interfaces based on the flags. + /// The result evaluates whether the flag is set for the attribute. + /// + public bool ValidOnInterface => ValidOn.HasFlag(AttributeTargets.Interface); + + /// + /// Indicates whether the attribute is valid on enumeration types. + /// + /// + /// This property checks if the attribute's targets include . + /// It is useful for determining if the attribute can be applied to enumeration definitions. + /// + public bool ValidOnEnum => ValidOn.HasFlag(AttributeTargets.Enum); + + /// + /// Gets a value indicating whether the attribute is valid when applied to a delegate. + /// + /// + /// This property checks if the flag is set + /// in the property, determining if the attribute can target delegate types. + /// + public bool ValidOnDelegate => ValidOn.HasFlag(AttributeTargets.Delegate); + + /// + /// Determines whether the attribute can be applied to named type declarations. + /// + /// + /// This property evaluates to true when the attribute is valid on + /// classes, structs, interfaces, enums, or delegates, as represented by the + /// respective flags in the enumeration. + /// + public bool ValidOnNamedType => ValidOnClass || ValidOnStruct || ValidOnInterface || ValidOnEnum || ValidOnDelegate; + + /// + /// Gets the collection of types on which the attribute can be applied. + /// + /// + /// This property aggregates the possible instances based on + /// the flags specified in the ValidOn property. It includes + /// types such as classes, structs, interfaces, enums, and delegates, depending on their validity. + /// + public ImmutableArray ValidOnTypes { + get { + var validOnTypes = new List(); + if (ValidOnClass) validOnTypes.Add(TypeKind.Class); + if (ValidOnStruct) validOnTypes.Add(TypeKind.Struct); + if (ValidOnInterface) validOnTypes.Add(TypeKind.Interface); + if (ValidOnEnum) validOnTypes.Add(TypeKind.Enum); + if (ValidOnDelegate) validOnTypes.Add(TypeKind.Delegate); + + return [..validOnTypes.Select((x, i) => x with { IsLast = i == validOnTypes.Count - 1})]; + } + } + + /// + /// Gets a value indicating whether the attribute is valid on all supported type-level targets, + /// including class, interface, enum, and delegate types. + /// + /// + /// This property evaluates to if the attribute is applicable to + /// , , + /// , and collectively. + /// Otherwise, it returns . + /// + public bool ValidOnAnyType => ValidOnClass && ValidOnInterface && ValidOnEnum && ValidOnDelegate; + + /// + /// Indicates whether the attribute can be applied to methods. + /// + /// + /// This property determines if the flag + /// is set in the ValidOn property, which specifies the allowed application + /// contexts for the attribute. + /// + public bool ValidOnMethod => ValidOn.HasFlag(AttributeTargets.Method); + + /// + /// Indicates whether the attribute can be applied to constructors. + /// + /// + /// This property evaluates if the flag is set + /// in the ValidOn attribute targets, denoting its applicability to constructors. + /// + public bool ValidOnConstructor => ValidOn.HasFlag(AttributeTargets.Constructor); + + /// + /// Gets a value indicating whether the attribute is valid on both methods and constructors. + /// + /// + /// This property evaluates to true if the attribute can be applied to methods + /// or constructors, and false otherwise. It combines the validation checks for + /// both and . + /// + public bool ValidOnAnyMethod => ValidOnMethod || ValidOnConstructor; + + /// + /// Gets a value indicating whether the attribute is valid exclusively on constructors. + /// + /// + /// This property evaluates if the attribute can be applied only to constructors and not to methods. + /// It returns true if the attribute targets constructors but not methods; otherwise, false. + /// + public bool OnlyValidOnConstructor => ValidOnConstructor && !ValidOnMethod; + + /// + /// Gets a value indicating whether the attribute is valid when applied + /// to the return value of a method. + /// + /// + /// This property evaluates whether the + /// flag is present in the associated with the attribute. + /// + public bool ValidOnReturnValue => ValidOn.HasFlag(AttributeTargets.ReturnValue); + + /// + /// Indicates whether the attribute can be applied to parameters. + /// + /// + /// This property returns a boolean value that determines if the flag + /// is included in the property. Use this to check if the attribute is valid for parameters. + /// + public bool ValidOnParameter => ValidOn.HasFlag(AttributeTargets.Parameter); + + /// + /// Determines whether the attribute is applicable to properties. + /// + /// + /// This property evaluates whether the attribute's target includes . + /// It uses the enumeration to identify if properties are a valid declaration context + /// for the associated attribute. + /// + public bool ValidOnProperty => ValidOn.HasFlag(AttributeTargets.Property); + + /// + /// Indicates whether the attribute is valid on a field. + /// + /// + /// This property evaluates whether the attribute target includes . + /// Returns true if the attribute can be applied to fields; otherwise, returns false. + /// + public bool ValidOnField => ValidOn.HasFlag(AttributeTargets.Field); + + /// + /// Indicates whether the attribute is applicable to events. + /// + /// + /// This property evaluates whether the flag + /// is set on the property, determining if the attribute can + /// be applied to event declarations. + /// + public bool ValidOnEvent => ValidOn.HasFlag(AttributeTargets.Event); + + /// + /// Determines whether the attribute can be applied to a generic parameter. + /// + /// + /// This property evaluates if the flag + /// is set within the property, indicating the attribute's applicability + /// to generic parameters. + /// + public bool ValidOnGenericParameter => ValidOn.HasFlag(AttributeTargets.GenericParameter); + /// /// Gets the constructors available for the attribute type overview. diff --git a/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Model/Attributes/ValidOnType.cs b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Model/Attributes/ValidOnType.cs new file mode 100644 index 0000000..d4b60a7 --- /dev/null +++ b/SourceGeneratorUtilities/src/Retro.SourceGeneratorUtilities/Utilities/Model/Attributes/ValidOnType.cs @@ -0,0 +1,29 @@ +using Microsoft.CodeAnalysis; +namespace Retro.SourceGeneratorUtilities.Utilities.Model.Attributes; + +/// +/// Represents a type validation structure that encapsulates the kind of a type +/// and whether it is the last element in the collection of valid types. +/// +/// The string representation of the kind of type +public record struct ValidOnType(string Kind) { + /// + /// Specifies whether the current instance is the last element in a collection of valid types. + /// + /// + /// This property is typically used to identify the position of the current type in a sequence + /// of valid types, especially within type validation logic. + /// + public bool IsLast { get; init; } + + /// + /// Defines an implicit operator that converts a instance + /// to a instance by using the string representation + /// of the specified . + /// + /// The TypeKind to be converted into a ValidOnType instance. + /// A ValidOnType instance with its Kind property set to the string representation of the input TypeKind. + public static implicit operator ValidOnType(TypeKind kind) { + return new ValidOnType(kind.ToString()); + } +} \ No newline at end of file diff --git a/SourceGenerators.sln.DotSettings.user b/SourceGenerators.sln.DotSettings.user index cf35216..00b5ebc 100644 --- a/SourceGenerators.sln.DotSettings.user +++ b/SourceGenerators.sln.DotSettings.user @@ -1,5 +1,6 @@  ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -17,4 +18,5 @@ <Solution /> </SessionState> + \ No newline at end of file