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