diff --git a/.editorconfig b/.editorconfig index 0d64ba1..a00e5bc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,568 @@ +# Version: 2.0.1 (Using https://semver.org/) +# Updated: 2020-12-11 +# See https://github.com/RehanSaeed/EditorConfig/releases for release notes. +# See https://github.com/RehanSaeed/EditorConfig for updates to this file. +# See http://EditorConfig.org for more information about .editorconfig files. + +########################################## +# Common Settings +########################################## + +# This file is the top-most EditorConfig file +root = true + +# All Files [*] +charset = utf-8 indent_style = space indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +########################################## +# File Extension Settings +########################################## + +# Visual Studio Solution Files +[*.sln] +indent_style = tab + +# Visual Studio XML Project Files +[*.{csproj,vbproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML Configuration Files +[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}] +indent_size = 2 + +# JSON Files +[*.{json,json5,webmanifest}] +indent_size = 2 + +# YAML Files +[*.{yml,yaml}] +indent_size = 2 + +# Markdown Files +[*.md] +trim_trailing_whitespace = false + +# Web Files +[*.{htm,html,js,jsm,ts,tsx,css,sass,scss,less,svg,vue}] +indent_size = 2 + +# Batch Files +[*.{cmd,bat}] end_of_line = crlf -charset = utf-8 -trim_trailing_whitespace = true \ No newline at end of file + +# Bash Files +[*.sh] +end_of_line = lf + +# Makefiles +[Makefile] +indent_style = tab + +########################################## +# Default .NET Code Style Severities +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/configuration-options#scope +########################################## + +[*.{cs,csx,cake,vb,vbx}] +# Default Severity for all .NET Code Style rules below +dotnet_analyzer_diagnostic.severity = silent + +########################################## +# File Header (Uncomment to support file headers) +# https://docs.microsoft.com/visualstudio/ide/reference/add-file-header +########################################## + +# [*.{cs,csx,cake,vb,vbx}] +# file_header_template = \n© PROJECT-AUTHOR\n + +# SA1636: File header copyright text should match +# Justification: .editorconfig supports file headers. If this is changed to a value other than "none", a stylecop.json file will need to added to the project. +# dotnet_diagnostic.SA1636.severity = none + +########################################## +# .NET Language Conventions +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions +########################################## + +# .NET Code Style Settings +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#net-code-style-settings +[*.{cs,csx,cake,vb,vbx}] +# "this." and "Me." qualifiers +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#this-and-me +#dotnet_style_qualification_for_field = true:warning +#dotnet_style_qualification_for_property = true:warning +#dotnet_style_qualification_for_method = true:warning +#dotnet_style_qualification_for_event = true:warning +# Language keywords instead of framework type names for type references +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#language-keywords +dotnet_style_predefined_type_for_locals_parameters_members = true:warning +dotnet_style_predefined_type_for_member_access = true:warning +# Modifier preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#normalize-modifiers +dotnet_style_require_accessibility_modifiers = always:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:warning +dotnet_style_readonly_field = true:warning +# Parentheses preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parentheses-preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_other_operators = always_for_clarity:suggestion +# Expression-level preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences +dotnet_style_object_initializer = true:warning +dotnet_style_collection_initializer = true:warning +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:warning +dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +dotnet_style_prefer_conditional_expression_over_assignment = false:suggestion +dotnet_diagnostic.IDE0045.severity = suggestion +dotnet_style_prefer_conditional_expression_over_return = false:suggestion +dotnet_diagnostic.IDE0046.severity = suggestion +dotnet_style_prefer_compound_assignment = true:warning +# Null-checking preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#null-checking-preferences +dotnet_style_coalesce_expression = true:warning +dotnet_style_null_propagation = true:warning +# Parameter preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parameter-preferences +dotnet_code_quality_unused_parameters = all:warning +# More style options (Undocumented) +# https://github.com/MicrosoftDocs/visualstudio-docs/issues/3641 +dotnet_style_operator_placement_when_wrapping = end_of_line +# https://github.com/dotnet/roslyn/pull/40070 +dotnet_style_prefer_simplified_interpolation = true:warning + +# C# Code Style Settings +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings +[*.{cs,csx,cake}] +# Implicit and explicit types +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#implicit-and-explicit-types +csharp_style_var_for_built_in_types = true:warning +csharp_style_var_when_type_is_apparent = true:warning +csharp_style_var_elsewhere = true:warning +# Expression-bodied members +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members +csharp_style_expression_bodied_methods = true:warning +csharp_style_expression_bodied_constructors = true:warning +csharp_style_expression_bodied_operators = true:warning +csharp_style_expression_bodied_properties = true:warning +csharp_style_expression_bodied_indexers = true:warning +csharp_style_expression_bodied_accessors = true:warning +csharp_style_expression_bodied_lambdas = true:warning +csharp_style_expression_bodied_local_functions = true:warning +# Pattern matching +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#pattern-matching +csharp_style_pattern_matching_over_is_with_cast_check = true:warning +csharp_style_pattern_matching_over_as_with_null_check = true:warning +# Inlined variable declarations +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#inlined-variable-declarations +csharp_style_inlined_variable_declaration = true:warning +# Expression-level preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences +csharp_prefer_simple_default_expression = true:warning +# "Null" checking preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-null-checking-preferences +csharp_style_throw_expression = true:warning +csharp_style_conditional_delegate_call = true:warning +# Code block preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#code-block-preferences +csharp_prefer_braces = true:warning +# Unused value preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#unused-value-preferences +csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion +dotnet_diagnostic.IDE0058.severity = suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +dotnet_diagnostic.IDE0059.severity = suggestion +# Index and range preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#index-and-range-preferences +csharp_style_prefer_index_operator = true:warning +csharp_style_prefer_range_operator = true:warning +# Miscellaneous preferences +# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#miscellaneous-preferences +csharp_style_deconstructed_variable_declaration = true:warning +csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_using_directive_placement = outside_namespace:warning +csharp_prefer_static_local_function = true:warning +csharp_prefer_simple_using_statement = true:suggestion +dotnet_diagnostic.IDE0063.severity = suggestion + +########################################## +# .NET Formatting Conventions +# https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions +########################################## + +# Organize usings +# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#organize-using-directives +dotnet_sort_system_directives_first = true +# Newline options +# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#new-line-options +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation options +# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#indentation-options +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = no_change +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents_when_block = false +# Spacing options +# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#spacing-options +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_after_comma = true +csharp_space_before_comma = false +csharp_space_after_dot = false +csharp_space_before_dot = false +csharp_space_after_semicolon_in_for_statement = true +csharp_space_before_semicolon_in_for_statement = false +csharp_space_around_declaration_statements = false +csharp_space_before_open_square_brackets = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_square_brackets = false +# Wrapping options +# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options +csharp_preserve_single_line_statements = false +csharp_preserve_single_line_blocks = true + +csharp_style_namespace_declarations = file_scoped + +########################################## +# .NET Naming Conventions +# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions +########################################## + +[*.{cs,csx,cake,vb,vbx}] +dotnet_diagnostic.CA1000.severity = suggestion +dotnet_diagnostic.CA1001.severity = error +dotnet_diagnostic.CA1018.severity = error +dotnet_diagnostic.CA1036.severity = silent +dotnet_diagnostic.CA1051.severity = suggestion +dotnet_diagnostic.CA1068.severity = error +dotnet_diagnostic.CA1069.severity = error +dotnet_diagnostic.CA1304.severity = error +dotnet_diagnostic.CA1305.severity = suggestion +dotnet_diagnostic.CA1307.severity = suggestion +dotnet_diagnostic.CA1309.severity = suggestion +dotnet_diagnostic.CA1310.severity = error +dotnet_diagnostic.CA1507.severity = suggestion +dotnet_diagnostic.CA1513.severity = suggestion +dotnet_diagnostic.CA1707.severity = suggestion +dotnet_diagnostic.CA1708.severity = suggestion +dotnet_diagnostic.CA1711.severity = suggestion +dotnet_diagnostic.CA1716.severity = suggestion +dotnet_diagnostic.CA1720.severity = suggestion +dotnet_diagnostic.CA1725.severity = suggestion +dotnet_diagnostic.CA1805.severity = suggestion +dotnet_diagnostic.CA1816.severity = suggestion +dotnet_diagnostic.CA1822.severity = suggestion +dotnet_diagnostic.CA1825.severity = error +dotnet_diagnostic.CA1826.severity = silent +dotnet_diagnostic.CA1827.severity = error +dotnet_diagnostic.CA1829.severity = suggestion +dotnet_diagnostic.CA1834.severity = error +dotnet_diagnostic.CA1845.severity = suggestion +dotnet_diagnostic.CA1848.severity = suggestion +dotnet_diagnostic.CA1852.severity = suggestion +dotnet_diagnostic.CA1860.severity = silent +dotnet_diagnostic.CA2016.severity = suggestion +dotnet_diagnostic.CA2201.severity = error +dotnet_diagnostic.CA2206.severity = error +dotnet_diagnostic.CA2208.severity = error +dotnet_diagnostic.CA2211.severity = error +dotnet_diagnostic.CA2249.severity = error +dotnet_diagnostic.CA2251.severity = error +dotnet_diagnostic.CA2252.severity = none +dotnet_diagnostic.CA2254.severity = suggestion + +dotnet_diagnostic.CS0169.severity = error +dotnet_diagnostic.CS0219.severity = error +dotnet_diagnostic.CS0649.severity = suggestion +dotnet_diagnostic.CS1998.severity = error +dotnet_diagnostic.CS8602.severity = error +dotnet_diagnostic.CS8604.severity = error +dotnet_diagnostic.CS8618.severity = error +dotnet_diagnostic.CS0618.severity = suggestion +dotnet_diagnostic.CS1998.severity = error +dotnet_diagnostic.CS4014.severity = error +dotnet_diagnostic.CS8600.severity = error +dotnet_diagnostic.CS8603.severity = error +dotnet_diagnostic.CS8625.severity = error + +dotnet_diagnostic.BL0005.severity = suggestion + +dotnet_diagnostic.MVC1000.severity = suggestion + +dotnet_diagnostic.RZ10012.severity = error + +dotnet_diagnostic.IDE0004.severity = error # redundant cast +dotnet_diagnostic.IDE0005.severity = error +dotnet_diagnostic.IDE0007.severity = error # Use var +dotnet_diagnostic.IDE0011.severity = error # Use braces on if statements +dotnet_diagnostic.IDE0010.severity = silent # populate switch +dotnet_diagnostic.IDE0017.severity = suggestion # initialization can be simplified +dotnet_diagnostic.IDE0021.severity = silent # expression body for constructors +dotnet_diagnostic.IDE0022.severity = silent # expression body for methods +dotnet_diagnostic.IDE0023.severity = suggestion # use expression body for operators +dotnet_diagnostic.IDE0024.severity = silent # expression body for operators +dotnet_diagnostic.IDE0025.severity = suggestion # use expression body for properties +dotnet_diagnostic.IDE0027.severity = suggestion # Use expression body for accessors +dotnet_diagnostic.IDE0028.severity = silent # expression body for accessors +dotnet_diagnostic.IDE0032.severity = suggestion # Use auto property +dotnet_diagnostic.IDE0033.severity = error # prefer tuple name +dotnet_diagnostic.IDE0037.severity = suggestion # simplify anonymous type +dotnet_diagnostic.IDE0040.severity = error # modifiers required +dotnet_diagnostic.IDE0041.severity = error # simplify null +dotnet_diagnostic.IDE0042.severity = error # deconstruct variable +dotnet_diagnostic.IDE0044.severity = suggestion # make field only when possible +dotnet_diagnostic.IDE0047.severity = suggestion # parameter name +dotnet_diagnostic.IDE0051.severity = error # unused field +dotnet_diagnostic.IDE0052.severity = error # unused member +dotnet_diagnostic.IDE0053.severity = suggestion # lambda not needed +dotnet_diagnostic.IDE0055.severity = suggestion # Fix formatting +dotnet_diagnostic.IDE0057.severity = suggestion # substring can be simplified +dotnet_diagnostic.IDE0060.severity = suggestion # unused parameters +dotnet_diagnostic.IDE0061.severity = suggestion # local expression body +dotnet_diagnostic.IDE0062.severity = suggestion # local to static +dotnet_diagnostic.IDE0063.severity = error # simplify using +dotnet_diagnostic.IDE0066.severity = suggestion # switch expression +dotnet_diagnostic.IDE0072.severity = suggestion # Populate switch - forces population of all cases even when default specified +dotnet_diagnostic.IDE0078.severity = suggestion # use pattern matching +dotnet_diagnostic.IDE0090.severity = suggestion # new can be simplified +dotnet_diagnostic.IDE0130.severity = suggestion # namespace folder structure +dotnet_diagnostic.IDE0160.severity = silent # Use block namespaces ARE NOT required +dotnet_diagnostic.IDE0161.severity = error # Please use file namespaces +dotnet_diagnostic.IDE0200.severity = suggestion # lambda not needed +dotnet_diagnostic.IDE1006.severity = suggestion # Naming rule violation: These words cannot contain lower case characters +dotnet_diagnostic.IDE0260.severity = suggestion # Use pattern matching +dotnet_diagnostic.IDE0270.severity = suggestion # Null check simplifcation +dotnet_diagnostic.IDE0290.severity = error # Primary Constructor +dotnet_diagnostic.IDE0300.severity = suggestion # Collection +dotnet_diagnostic.IDE0305.severity = suggestion # Collection ToList + +dotnet_diagnostic.NX0001.severity = error +dotnet_diagnostic.NX0002.severity = silent +dotnet_diagnostic.NX0003.severity = silent + +########################################## +# Styles +########################################## + +# camel_case_style - Define the camelCase style +dotnet_naming_style.camel_case_style.capitalization = camel_case +# pascal_case_style - Define the PascalCase style +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# constant_case - Define the CONSTANT_CASE style +dotnet_naming_style.constant_case.capitalization = all_upper +dotnet_naming_style.constant_case.word_separator = _ +# first_upper_style - The first character must start with an upper-case character +dotnet_naming_style.first_upper_style.capitalization = first_word_upper +# prefix_interface_with_i_style - Interfaces must be PascalCase and the first character of an interface must be an 'I' +dotnet_naming_style.prefix_interface_with_i_style.capitalization = pascal_case +dotnet_naming_style.prefix_interface_with_i_style.required_prefix = I +# prefix_type_parameters_with_t_style - Generic Type Parameters must be PascalCase and the first character must be a 'T' +dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization = pascal_case +dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T +# disallowed_style - Anything that has this style applied is marked as disallowed +dotnet_naming_style.disallowed_style.capitalization = pascal_case +dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____ +dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____ +# internal_error_style - This style should never occur... if it does, it indicates a bug in file or in the parser using the file +dotnet_naming_style.internal_error_style.capitalization = pascal_case +dotnet_naming_style.internal_error_style.required_prefix = ____INTERNAL_ERROR____ +dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR____ + +# prefix_interface_with_i_style - Interfaces must be PascalCase and the first character of an interface must be an 'I' +dotnet_naming_style.underscore_camel_case_style.capitalization = camel_case +dotnet_naming_style.underscore_camel_case_style.required_prefix = _ + +########################################## +# .NET Design Guideline Field Naming Rules +# Naming rules for fields follow the .NET Framework design guidelines +# https://docs.microsoft.com/dotnet/standard/design-guidelines/index +########################################## + +# All public/protected/protected_internal constant fields must be constant_case +# https://docs.microsoft.com/dotnet/standard/design-guidelines/field +dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal +dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers = const +dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds = field +dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols = public_protected_constant_fields_group +dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.style = constant_case +dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.severity = warning + +# All public/protected/protected_internal static readonly fields must be constant_case +# https://docs.microsoft.com/dotnet/standard/design-guidelines/field +dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_accessibilities = public, protected, protected_internal +dotnet_naming_symbols.public_protected_static_readonly_fields_group.required_modifiers = static, readonly +dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_kinds = field +dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.symbols = public_protected_static_readonly_fields_group +dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.style = constant_case +dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.severity = warning + +# No other public/protected/protected_internal fields are allowed +# https://docs.microsoft.com/dotnet/standard/design-guidelines/field +dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibilities = public, protected, protected_internal +dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field +dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group +dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style +dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = error + +########################################## +# StyleCop Field Naming Rules +# Naming rules for fields follow the StyleCop analyzers +# This does not override any rules using disallowed_style above +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers +########################################## + +# All constant fields must be constant_case +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md +dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private +dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const +dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field +dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group +dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = constant_case +dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity = warning + +# All static readonly fields must be constant_case +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md +dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private +dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly +dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field +dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group +dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = constant_case +dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity = warning + +# No non-private instance fields are allowed +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md +dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected +dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field +dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group +dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style +dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error + +# Private fields must be camelCase +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md +dotnet_naming_symbols.stylecop_private_fields_group.applicable_accessibilities = private +dotnet_naming_symbols.stylecop_private_fields_group.applicable_kinds = field +dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols = stylecop_private_fields_group +dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.style = underscore_camel_case_style +dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.severity = warning + +# Local variables must be camelCase +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md +dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local +dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local +dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group +dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style +dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity = warning + +# This rule should never fire. However, it's included for at least two purposes: +# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers. +# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#). +dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_accessibilities = * +dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_kinds = field +dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols = sanity_check_uncovered_field_case_group +dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style = internal_error_style +dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error + + +########################################## +# Other Naming Rules +########################################## + +# All of the following must be PascalCase: +# - Namespaces +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-namespaces +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md +# - Classes and Enumerations +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md +# - Delegates +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces#names-of-common-types +# - Constructors, Properties, Events, Methods +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-type-members +dotnet_naming_symbols.element_group.applicable_kinds = namespace, class, enum, struct, delegate, event, method, property +dotnet_naming_rule.element_rule.symbols = element_group +dotnet_naming_rule.element_rule.style = pascal_case_style +dotnet_naming_rule.element_rule.severity = warning + +# Interfaces use PascalCase and are prefixed with uppercase 'I' +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces +dotnet_naming_symbols.interface_group.applicable_kinds = interface +dotnet_naming_rule.interface_rule.symbols = interface_group +dotnet_naming_rule.interface_rule.style = prefix_interface_with_i_style +dotnet_naming_rule.interface_rule.severity = warning + +# Generics Type Parameters use PascalCase and are prefixed with uppercase 'T' +# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces +dotnet_naming_symbols.type_parameter_group.applicable_kinds = type_parameter +dotnet_naming_rule.type_parameter_rule.symbols = type_parameter_group +dotnet_naming_rule.type_parameter_rule.style = prefix_type_parameters_with_t_style +dotnet_naming_rule.type_parameter_rule.severity = warning + +# Function parameters use camelCase +# https://docs.microsoft.com/dotnet/standard/design-guidelines/naming-parameters +dotnet_naming_symbols.parameters_group.applicable_kinds = parameter +dotnet_naming_rule.parameters_rule.symbols = parameters_group +dotnet_naming_rule.parameters_rule.style = camel_case_style +dotnet_naming_rule.parameters_rule.severity = warning + +########################################## +# License +########################################## +# The following applies as to the .editorconfig file ONLY, and is +# included below for reference, per the requirements of the license +# corresponding to this .editorconfig file. +# See: https://github.com/RehanSaeed/EditorConfig +# +# MIT License +# +# Copyright (c) 2017-2019 Muhammad Rehan Saeed +# Copyright (c) 2019 Henry Gabryjelski +# +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, +# sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject +# to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +########################################## diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 78f0f48..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms - -github: [StefH] -patreon: # Replace with a single Patreon username -open_collective: # wiremocknet -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: https://www.paypal.me/stefheyenrath \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3413224 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" # search for actions - there are other options available + directory: "/" # search in .github/workflows under root `/` + schedule: + interval: "weekly" # check for action update every week \ No newline at end of file diff --git a/.github/workflows/CreateRelease.yml b/.github/workflows/CreateRelease.yml deleted file mode 100644 index fc7a348..0000000 --- a/.github/workflows/CreateRelease.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: CreateRelease - -on: - push: - tags: - - "*.*.*" - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Release - uses: softprops/action-gh-release@v1 diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml new file mode 100644 index 0000000..f5e8b3d --- /dev/null +++ b/.github/workflows/nuget.yml @@ -0,0 +1,34 @@ +name: "Deploy to Nuget.org" + +on: + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + env: + DOTNET_CLI_TELEMETRY_OPTOUT: true + + steps: + - name: Checkout Project + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Test + run: dotnet test --no-build --configuration Release --verbosity normal + + - name: Pack + run: dotnet pack --no-build --configuration Release Speckle.InterfaceGenerator/Speckle.InterfaceGenerator.csproj --output . + + - name: Push to nuget.org + run: dotnet nuget push *.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{secrets.CONNECTORS_NUGET_TOKEN }} --skip-duplicate \ No newline at end of file diff --git a/.gitignore b/.gitignore index 313797d..78e8c22 100644 --- a/.gitignore +++ b/.gitignore @@ -350,4 +350,6 @@ MigrationBackup/ .ionide/ /tests/ProxyInterfaceSourceGeneratorTests/Destination/Disposable/*.g.cs -.idea/.idea.ProxyInterfaceSourceGenerator Solution/.idea/ +.idea/ + +Speckle.ProxyGenerator.sln.DotSettings diff --git a/ProxyInterfaceSourceGenerator Solution.sln.DotSettings b/ProxyInterfaceSourceGenerator Solution.sln.DotSettings deleted file mode 100644 index 860b238..0000000 --- a/ProxyInterfaceSourceGenerator Solution.sln.DotSettings +++ /dev/null @@ -1,3 +0,0 @@ - - True - True \ No newline at end of file diff --git a/ProxyInterfaceSourceGenerator Solution.sln b/Speckle.ProxyGenerator.sln similarity index 95% rename from ProxyInterfaceSourceGenerator Solution.sln rename to Speckle.ProxyGenerator.sln index 56171fc..971f104 100644 --- a/ProxyInterfaceSourceGenerator Solution.sln +++ b/Speckle.ProxyGenerator.sln @@ -1,93 +1,93 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31521.260 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED3DA9DD-1E07-444B-A2D7-2DBA280F96D4}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2CE637DC-E8F5-4603-8B57-E51A32F631F1}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - Generate-ReleaseNotes.bat = Generate-ReleaseNotes.bat - PackageReadme.md = PackageReadme.md - PackageReleaseNotes.template = PackageReleaseNotes.template - PackageReleaseNotes.txt = PackageReleaseNotes.txt - README.md = README.md - ReleaseNotes.md = ReleaseNotes.md - ToDo.md = ToDo.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{19009F5B-3267-45E2-A8B6-89F2AB47D72C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src-examples", "src-examples", "{38BA087F-EDA1-4F8A-A140-85B84791B815}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceSourceGenerator", "src\ProxyInterfaceSourceGenerator\ProxyInterfaceSourceGenerator.csproj", "{12344228-91F4-4502-9595-39584E5ABB34}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceConsumer", "src-examples\ProxyInterfaceConsumer\ProxyInterfaceConsumer.csproj", "{7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceSourceGeneratorTests", "tests\ProxyInterfaceSourceGeneratorTests\ProxyInterfaceSourceGeneratorTests.csproj", "{1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceConsumerForPnP", "src-examples\ProxyInterfaceConsumerForPnP\ProxyInterfaceConsumerForPnP.csproj", "{5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceConsumerForAkka", "src-examples\ProxyInterfaceConsumerForAkka\ProxyInterfaceConsumerForAkka.csproj", "{590908DF-A813-467A-94E4-3500020D0D54}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceConsumerViaNuGet", "src-examples\ProxyInterfaceConsumerViaNuGet\ProxyInterfaceConsumerViaNuGet.csproj", "{1EA000E4-6103-4577-8D98-BDDA3BE458A2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - DebugAttach|Any CPU = DebugAttach|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {12344228-91F4-4502-9595-39584E5ABB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {12344228-91F4-4502-9595-39584E5ABB34}.Debug|Any CPU.Build.0 = Debug|Any CPU - {12344228-91F4-4502-9595-39584E5ABB34}.DebugAttach|Any CPU.ActiveCfg = DebugAttach|Any CPU - {12344228-91F4-4502-9595-39584E5ABB34}.DebugAttach|Any CPU.Build.0 = DebugAttach|Any CPU - {12344228-91F4-4502-9595-39584E5ABB34}.Release|Any CPU.ActiveCfg = Release|Any CPU - {12344228-91F4-4502-9595-39584E5ABB34}.Release|Any CPU.Build.0 = Release|Any CPU - {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.DebugAttach|Any CPU.ActiveCfg = Debug|Any CPU - {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.Release|Any CPU.Build.0 = Release|Any CPU - {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.DebugAttach|Any CPU.ActiveCfg = DebugAttach|Any CPU - {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.DebugAttach|Any CPU.Build.0 = DebugAttach|Any CPU - {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.Release|Any CPU.Build.0 = Release|Any CPU - {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.DebugAttach|Any CPU.ActiveCfg = Debug|Any CPU - {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.DebugAttach|Any CPU.Build.0 = Debug|Any CPU - {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.Release|Any CPU.Build.0 = Release|Any CPU - {590908DF-A813-467A-94E4-3500020D0D54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {590908DF-A813-467A-94E4-3500020D0D54}.Debug|Any CPU.Build.0 = Debug|Any CPU - {590908DF-A813-467A-94E4-3500020D0D54}.DebugAttach|Any CPU.ActiveCfg = Debug|Any CPU - {590908DF-A813-467A-94E4-3500020D0D54}.DebugAttach|Any CPU.Build.0 = Debug|Any CPU - {590908DF-A813-467A-94E4-3500020D0D54}.Release|Any CPU.ActiveCfg = Release|Any CPU - {590908DF-A813-467A-94E4-3500020D0D54}.Release|Any CPU.Build.0 = Release|Any CPU - {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.DebugAttach|Any CPU.ActiveCfg = Debug|Any CPU - {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.DebugAttach|Any CPU.Build.0 = Debug|Any CPU - {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {12344228-91F4-4502-9595-39584E5ABB34} = {ED3DA9DD-1E07-444B-A2D7-2DBA280F96D4} - {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9} = {38BA087F-EDA1-4F8A-A140-85B84791B815} - {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A} = {19009F5B-3267-45E2-A8B6-89F2AB47D72C} - {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C} = {38BA087F-EDA1-4F8A-A140-85B84791B815} - {590908DF-A813-467A-94E4-3500020D0D54} = {38BA087F-EDA1-4F8A-A140-85B84791B815} - {1EA000E4-6103-4577-8D98-BDDA3BE458A2} = {38BA087F-EDA1-4F8A-A140-85B84791B815} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {585F071D-051D-441C-9C6B-226D9E15A1F5} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31521.260 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED3DA9DD-1E07-444B-A2D7-2DBA280F96D4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2CE637DC-E8F5-4603-8B57-E51A32F631F1}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + Generate-ReleaseNotes.bat = Generate-ReleaseNotes.bat + PackageReadme.md = PackageReadme.md + PackageReleaseNotes.template = PackageReleaseNotes.template + PackageReleaseNotes.txt = PackageReleaseNotes.txt + README.md = README.md + ReleaseNotes.md = ReleaseNotes.md + ToDo.md = ToDo.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{19009F5B-3267-45E2-A8B6-89F2AB47D72C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src-examples", "src-examples", "{38BA087F-EDA1-4F8A-A140-85B84791B815}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.ProxyGenerator", "src\Speckle.ProxyGenerator\Speckle.ProxyGenerator.csproj", "{12344228-91F4-4502-9595-39584E5ABB34}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceConsumer", "src-examples\ProxyInterfaceConsumer\ProxyInterfaceConsumer.csproj", "{7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceSourceGeneratorTests", "tests\ProxyInterfaceSourceGeneratorTests\ProxyInterfaceSourceGeneratorTests.csproj", "{1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceConsumerForPnP", "src-examples\ProxyInterfaceConsumerForPnP\ProxyInterfaceConsumerForPnP.csproj", "{5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceConsumerForAkka", "src-examples\ProxyInterfaceConsumerForAkka\ProxyInterfaceConsumerForAkka.csproj", "{590908DF-A813-467A-94E4-3500020D0D54}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyInterfaceConsumerViaNuGet", "src-examples\ProxyInterfaceConsumerViaNuGet\ProxyInterfaceConsumerViaNuGet.csproj", "{1EA000E4-6103-4577-8D98-BDDA3BE458A2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + DebugAttach|Any CPU = DebugAttach|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {12344228-91F4-4502-9595-39584E5ABB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12344228-91F4-4502-9595-39584E5ABB34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12344228-91F4-4502-9595-39584E5ABB34}.DebugAttach|Any CPU.ActiveCfg = DebugAttach|Any CPU + {12344228-91F4-4502-9595-39584E5ABB34}.DebugAttach|Any CPU.Build.0 = DebugAttach|Any CPU + {12344228-91F4-4502-9595-39584E5ABB34}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12344228-91F4-4502-9595-39584E5ABB34}.Release|Any CPU.Build.0 = Release|Any CPU + {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.DebugAttach|Any CPU.ActiveCfg = Debug|Any CPU + {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9}.Release|Any CPU.Build.0 = Release|Any CPU + {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.DebugAttach|Any CPU.ActiveCfg = DebugAttach|Any CPU + {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.DebugAttach|Any CPU.Build.0 = DebugAttach|Any CPU + {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A}.Release|Any CPU.Build.0 = Release|Any CPU + {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.DebugAttach|Any CPU.ActiveCfg = Debug|Any CPU + {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.DebugAttach|Any CPU.Build.0 = Debug|Any CPU + {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C}.Release|Any CPU.Build.0 = Release|Any CPU + {590908DF-A813-467A-94E4-3500020D0D54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {590908DF-A813-467A-94E4-3500020D0D54}.Debug|Any CPU.Build.0 = Debug|Any CPU + {590908DF-A813-467A-94E4-3500020D0D54}.DebugAttach|Any CPU.ActiveCfg = Debug|Any CPU + {590908DF-A813-467A-94E4-3500020D0D54}.DebugAttach|Any CPU.Build.0 = Debug|Any CPU + {590908DF-A813-467A-94E4-3500020D0D54}.Release|Any CPU.ActiveCfg = Release|Any CPU + {590908DF-A813-467A-94E4-3500020D0D54}.Release|Any CPU.Build.0 = Release|Any CPU + {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.DebugAttach|Any CPU.ActiveCfg = Debug|Any CPU + {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.DebugAttach|Any CPU.Build.0 = Debug|Any CPU + {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EA000E4-6103-4577-8D98-BDDA3BE458A2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {12344228-91F4-4502-9595-39584E5ABB34} = {ED3DA9DD-1E07-444B-A2D7-2DBA280F96D4} + {7E0A10EE-CCC3-4281-9541-B0AF037D3DF9} = {38BA087F-EDA1-4F8A-A140-85B84791B815} + {1BDB9046-D6D1-4FB4-AAB5-F24E33EEAE0A} = {19009F5B-3267-45E2-A8B6-89F2AB47D72C} + {5F7DA2C5-B908-4B57-9F5F-BADF1216D89C} = {38BA087F-EDA1-4F8A-A140-85B84791B815} + {590908DF-A813-467A-94E4-3500020D0D54} = {38BA087F-EDA1-4F8A-A140-85B84791B815} + {1EA000E4-6103-4577-8D98-BDDA3BE458A2} = {38BA087F-EDA1-4F8A-A140-85B84791B815} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {585F071D-051D-441C-9C6B-226D9E15A1F5} + EndGlobalSection +EndGlobal diff --git a/global.json b/global.json new file mode 100644 index 0000000..18b689d --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.0", + "rollForward": "latestMinor", + "allowPrerelease": false + } +} diff --git a/omnisharp.json b/omnisharp.json deleted file mode 100644 index 0bad48f..0000000 --- a/omnisharp.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "RoslynExtensionsOptions": { - "enableAnalyzersSupport": true - } -} \ No newline at end of file diff --git a/src-examples/ProxyInterfaceConsumer/Address.cs b/src-examples/ProxyInterfaceConsumer/Address.cs index f440fc2..cc6333a 100644 --- a/src-examples/ProxyInterfaceConsumer/Address.cs +++ b/src-examples/ProxyInterfaceConsumer/Address.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace ProxyInterfaceConsumer { @@ -6,11 +7,12 @@ namespace ProxyInterfaceConsumer { public int HouseNumber { get; set; } - public event EventHandler MyEvent; + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public event EventHandler MyEvent = null!; public int Weird { get; set; } public int Weird2() { return 0; } } -} \ No newline at end of file +} diff --git a/src-examples/ProxyInterfaceConsumer/Person.cs b/src-examples/ProxyInterfaceConsumer/Person.cs index 209f687..f1d4b25 100644 --- a/src-examples/ProxyInterfaceConsumer/Person.cs +++ b/src-examples/ProxyInterfaceConsumer/Person.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace ProxyInterfaceConsumer @@ -8,24 +9,29 @@ namespace ProxyInterfaceConsumer private int PrivateId { get; } public int Id { get; } - public object @object { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public object @object { get; set; }= null!; public long? NullableLong { get; } - public string Name { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public string Name { get; set; }= null!; public string? StringNullable { get; set; } - public Address Address { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public Address Address { get; set; }= null!; - public List
AddressesList { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public List
AddressesList { get; set; }= null!; public Dictionary AddressesDict { get; set; } = new Dictionary(); public Dictionary AddressesDict2 { get; set; } = new Dictionary(); public E E { get; set; } - public IMyInterface MyInterface { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public IMyInterface MyInterface { get; set; }= null!; public bool TMethod(int x, T1 t1, T2 t2) where T1 : struct @@ -102,4 +108,4 @@ namespace ProxyInterfaceConsumer V1, V2 } -} \ No newline at end of file +} diff --git a/src-examples/ProxyInterfaceConsumer/Program.cs b/src-examples/ProxyInterfaceConsumer/Program.cs index 35e7a76..79a5759 100644 --- a/src-examples/ProxyInterfaceConsumer/Program.cs +++ b/src-examples/ProxyInterfaceConsumer/Program.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; @@ -65,9 +66,10 @@ public class Test { public int Id { get; set; } - public Clazz C { get; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public Clazz C { get; } = null!; - public IList Cs { get; set; } + public IList Cs { get; set; } = new List(); public int AddString(string s) { @@ -87,7 +89,7 @@ public class Test public sealed class Clazz { - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } [ProxyInterfaceGenerator.Proxy(typeof(Test))] @@ -98,4 +100,4 @@ public partial interface ITest [ProxyInterfaceGenerator.Proxy(typeof(Clazz))] public partial interface IClazz { -} \ No newline at end of file +} diff --git a/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj b/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj index acaa276..9f03ec9 100644 --- a/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj +++ b/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj @@ -24,7 +24,7 @@ - + \ No newline at end of file diff --git a/src-examples/ProxyInterfaceConsumerForAkka/Program.cs b/src-examples/ProxyInterfaceConsumerForAkka/Program.cs index 7171963..4849601 100644 --- a/src-examples/ProxyInterfaceConsumerForAkka/Program.cs +++ b/src-examples/ProxyInterfaceConsumerForAkka/Program.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Akka.Actor; using ProxyInterfaceConsumerForAkka.Interfaces; @@ -5,12 +6,13 @@ namespace ProxyInterfaceConsumerForAkka; public class Program { + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] public static void Main() { - Akka.Remote.AddressUid auid = null; + Akka.Remote.AddressUid auid = null!; IAddressUid addressUidProxy = new AddressUidProxy(auid); - LocalActorRefProvider p = null; + LocalActorRefProvider p = null!; ILocalActorRefProvider proxy = new LocalActorRefProviderProxy(p); } -} \ No newline at end of file +} diff --git a/src-examples/ProxyInterfaceConsumerForAkka/ProxyInterfaceConsumerForAkka.csproj b/src-examples/ProxyInterfaceConsumerForAkka/ProxyInterfaceConsumerForAkka.csproj index c79aed1..2c28d19 100644 --- a/src-examples/ProxyInterfaceConsumerForAkka/ProxyInterfaceConsumerForAkka.csproj +++ b/src-examples/ProxyInterfaceConsumerForAkka/ProxyInterfaceConsumerForAkka.csproj @@ -24,7 +24,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive --> - + \ No newline at end of file diff --git a/src-examples/ProxyInterfaceConsumerForPnP/ProxyInterfaceConsumerForPnP.csproj b/src-examples/ProxyInterfaceConsumerForPnP/ProxyInterfaceConsumerForPnP.csproj index 33321fc..573790a 100644 --- a/src-examples/ProxyInterfaceConsumerForPnP/ProxyInterfaceConsumerForPnP.csproj +++ b/src-examples/ProxyInterfaceConsumerForPnP/ProxyInterfaceConsumerForPnP.csproj @@ -20,7 +20,7 @@ - + diff --git a/src-examples/ProxyInterfaceConsumerViaNuGet/Person.cs b/src-examples/ProxyInterfaceConsumerViaNuGet/Person.cs index b999002..68f67d4 100644 --- a/src-examples/ProxyInterfaceConsumerViaNuGet/Person.cs +++ b/src-examples/ProxyInterfaceConsumerViaNuGet/Person.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace ProxyInterfaceConsumer @@ -8,24 +9,29 @@ namespace ProxyInterfaceConsumer private int PrivateId { get; } public int Id { get; } - public object @object { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public object @object { get; set; }= null!; public long? NullableLong { get; } - public string Name { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public string Name { get; set; }= null!; public string? StringNullable { get; set; } - public Address Address { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public Address Address { get; set; }= null!; - public List
AddressesLIst { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public List
AddressesLIst { get; set; }= null!; public Dictionary AddressesDict { get; set; } = new Dictionary(); public Dictionary AddressesDict2 { get; set; } = new Dictionary(); public E E { get; set; } - public IMyInterface MyInterface { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public IMyInterface MyInterface { get; set; }= null!; public string Add(string s, string @string) { @@ -83,4 +89,4 @@ namespace ProxyInterfaceConsumer V1, V2 } -} \ No newline at end of file +} diff --git a/src-examples/ProxyInterfaceConsumerViaNuGet/ProxyInterfaceConsumerViaNuGet.csproj b/src-examples/ProxyInterfaceConsumerViaNuGet/ProxyInterfaceConsumerViaNuGet.csproj index 82e25f8..3c7d3b6 100644 --- a/src-examples/ProxyInterfaceConsumerViaNuGet/ProxyInterfaceConsumerViaNuGet.csproj +++ b/src-examples/ProxyInterfaceConsumerViaNuGet/ProxyInterfaceConsumerViaNuGet.csproj @@ -12,10 +12,10 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - \ No newline at end of file + diff --git a/src/ProxyInterfaceSourceGenerator/Builders/MethodParameterBuilder.cs b/src/Speckle.ProxyGenerator/Builders/MethodParameterBuilder.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Builders/MethodParameterBuilder.cs rename to src/Speckle.ProxyGenerator/Builders/MethodParameterBuilder.cs diff --git a/src/ProxyInterfaceSourceGenerator/Enums/TypeEnum.cs b/src/Speckle.ProxyGenerator/Enums/TypeEnum.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Enums/TypeEnum.cs rename to src/Speckle.ProxyGenerator/Enums/TypeEnum.cs diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/MethodSymbolExtensions.cs b/src/Speckle.ProxyGenerator/Extensions/MethodSymbolExtensions.cs similarity index 98% rename from src/ProxyInterfaceSourceGenerator/Extensions/MethodSymbolExtensions.cs rename to src/Speckle.ProxyGenerator/Extensions/MethodSymbolExtensions.cs index 818fcd8..2feb5b2 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/MethodSymbolExtensions.cs +++ b/src/Speckle.ProxyGenerator/Extensions/MethodSymbolExtensions.cs @@ -1,12 +1,12 @@ -using Microsoft.CodeAnalysis; - -namespace ProxyInterfaceSourceGenerator.Extensions; - -internal static class MethodSymbolExtensions -{ - public static string GetMethodNameWithOptionalTypeParameters(this IMethodSymbol method) => - !method.IsGenericMethod ? method.Name : $"{method.Name}<{string.Join(", ", method.TypeParameters.Select(tp => tp.Name))}>"; - - //public static string GetWhereStatement(this IMethodSymbol method) => - // !method.IsGenericMethod ? string.Empty : string.Join("", method.TypeParameters.Select(tp => tp.GetWhereConstraints())); +using Microsoft.CodeAnalysis; + +namespace ProxyInterfaceSourceGenerator.Extensions; + +internal static class MethodSymbolExtensions +{ + public static string GetMethodNameWithOptionalTypeParameters(this IMethodSymbol method) => + !method.IsGenericMethod ? method.Name : $"{method.Name}<{string.Join(", ", method.TypeParameters.Select(tp => tp.Name))}>"; + + //public static string GetWhereStatement(this IMethodSymbol method) => + // !method.IsGenericMethod ? string.Empty : string.Join("", method.TypeParameters.Select(tp => tp.GetWhereConstraints())); } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/NamedTypeSymbolExtensions.cs b/src/Speckle.ProxyGenerator/Extensions/NamedTypeSymbolExtensions.cs similarity index 97% rename from src/ProxyInterfaceSourceGenerator/Extensions/NamedTypeSymbolExtensions.cs rename to src/Speckle.ProxyGenerator/Extensions/NamedTypeSymbolExtensions.cs index 1f65fd6..c74ef98 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/NamedTypeSymbolExtensions.cs +++ b/src/Speckle.ProxyGenerator/Extensions/NamedTypeSymbolExtensions.cs @@ -1,88 +1,88 @@ -using Microsoft.CodeAnalysis; - -namespace ProxyInterfaceSourceGenerator.Extensions; - -internal static class NamedTypeSymbolExtensions -{ - public static List GetBaseTypes(this INamedTypeSymbol? type) - { - var types = new List(); - - bool me = true; - while (type != null && type.SpecialType != SpecialType.System_Object) - { - if (!me) - { - types.Add(type); - } - - type = type.BaseType; - me = false; - } - - return types; - } - - public static string GetFullType(this INamedTypeSymbol namedTypeSymbol) - { - // https://www.codeproject.com/Articles/861548/Roslyn-Code-Analysis-in-Easy-Samples-Part - //var str = new StringBuilder(namedTypeSymbol.Name); - - //if (namedTypeSymbol.TypeArguments.Count() > 0) - //{ - // str.AppendFormat("<{0}>", string.Join(", ", namedTypeSymbol.TypeArguments.OfType().Select(typeArg => typeArg.GetFullType()))); - //} - - return namedTypeSymbol.OriginalDefinition.ToString(); - } - - /// - /// See https://stackoverflow.com/questions/24157101/roslyns-gettypebymetadataname-and-generic-types - /// - public static string ResolveProxyClassName(this INamedTypeSymbol namedTypeSymbol) - { - return !namedTypeSymbol.IsGenericType ? - $"{namedTypeSymbol.Name}Proxy" : - $"{namedTypeSymbol.Name}Proxy<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(ta => ta.Name))}>"; - } - - public static List ResolveImplementedInterfaces(this INamedTypeSymbol symbol, bool proxyBaseClasses) - { - // Members implemented by us or base classes should go here. - var publicMembers = symbol.GetMembers().Where(m => m.DeclaredAccessibility == Accessibility.Public).ToList(); - - // Direct interfaces, recursive interfaces or base class interfaces should go here. - var interfaces = new List(symbol.Interfaces); - var baseType = symbol.BaseType; - while (proxyBaseClasses && baseType != null && baseType.SpecialType != SpecialType.System_Object) - { - publicMembers.AddRange(baseType.GetMembers().Where(m => m.DeclaredAccessibility == Accessibility.Public)); - interfaces.AddRange(baseType.Interfaces); - baseType = baseType.BaseType; - } - - // Filter explicitly implemented interfaces. - var realizedInterfaces = new List(); - foreach (var @interface in interfaces) - { - var isRealized = true; - var allMembers = @interface.AllInterfaces.Aggregate(@interface.GetMembers(), (xs, x) => xs.AddRange(x.GetMembers())); - foreach (var member in allMembers) - { - var implementation = symbol.FindImplementationForInterfaceMember(member); - if (!publicMembers.Contains(implementation!)) - { - isRealized = false; - break; - } - } - - if (isRealized) - { - realizedInterfaces.Add(@interface); - } - } - - return realizedInterfaces; - } +using Microsoft.CodeAnalysis; + +namespace ProxyInterfaceSourceGenerator.Extensions; + +internal static class NamedTypeSymbolExtensions +{ + public static List GetBaseTypes(this INamedTypeSymbol? type) + { + var types = new List(); + + bool me = true; + while (type != null && type.SpecialType != SpecialType.System_Object) + { + if (!me) + { + types.Add(type); + } + + type = type.BaseType; + me = false; + } + + return types; + } + + public static string GetFullType(this INamedTypeSymbol namedTypeSymbol) + { + // https://www.codeproject.com/Articles/861548/Roslyn-Code-Analysis-in-Easy-Samples-Part + //var str = new StringBuilder(namedTypeSymbol.Name); + + //if (namedTypeSymbol.TypeArguments.Count() > 0) + //{ + // str.AppendFormat("<{0}>", string.Join(", ", namedTypeSymbol.TypeArguments.OfType().Select(typeArg => typeArg.GetFullType()))); + //} + + return namedTypeSymbol.OriginalDefinition.ToString(); + } + + /// + /// See https://stackoverflow.com/questions/24157101/roslyns-gettypebymetadataname-and-generic-types + /// + public static string ResolveProxyClassName(this INamedTypeSymbol namedTypeSymbol) + { + return !namedTypeSymbol.IsGenericType ? + $"{namedTypeSymbol.Name}Proxy" : + $"{namedTypeSymbol.Name}Proxy<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(ta => ta.Name))}>"; + } + + public static List ResolveImplementedInterfaces(this INamedTypeSymbol symbol, bool proxyBaseClasses) + { + // Members implemented by us or base classes should go here. + var publicMembers = symbol.GetMembers().Where(m => m.DeclaredAccessibility == Accessibility.Public).ToList(); + + // Direct interfaces, recursive interfaces or base class interfaces should go here. + var interfaces = new List(symbol.Interfaces); + var baseType = symbol.BaseType; + while (proxyBaseClasses && baseType != null && baseType.SpecialType != SpecialType.System_Object) + { + publicMembers.AddRange(baseType.GetMembers().Where(m => m.DeclaredAccessibility == Accessibility.Public)); + interfaces.AddRange(baseType.Interfaces); + baseType = baseType.BaseType; + } + + // Filter explicitly implemented interfaces. + var realizedInterfaces = new List(); + foreach (var @interface in interfaces) + { + var isRealized = true; + var allMembers = @interface.AllInterfaces.Aggregate(@interface.GetMembers(), (xs, x) => xs.AddRange(x.GetMembers())); + foreach (var member in allMembers) + { + var implementation = symbol.FindImplementationForInterfaceMember(member); + if (!publicMembers.Contains(implementation!)) + { + isRealized = false; + break; + } + } + + if (isRealized) + { + realizedInterfaces.Add(@interface); + } + } + + return realizedInterfaces; + } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/ParameterSymbolExtensions.cs b/src/Speckle.ProxyGenerator/Extensions/ParameterSymbolExtensions.cs similarity index 96% rename from src/ProxyInterfaceSourceGenerator/Extensions/ParameterSymbolExtensions.cs rename to src/Speckle.ProxyGenerator/Extensions/ParameterSymbolExtensions.cs index 0e45469..7b88b22 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/ParameterSymbolExtensions.cs +++ b/src/Speckle.ProxyGenerator/Extensions/ParameterSymbolExtensions.cs @@ -1,60 +1,60 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using ProxyInterfaceSourceGenerator.Enums; - -namespace ProxyInterfaceSourceGenerator.Extensions; - -internal static class ParameterSymbolExtensions -{ - private const string ParameterValueNull = "null"; - - public static bool IsRef(this IParameterSymbol ps) - { - return ps.RefKind is RefKind.Ref or RefKind.RefReadOnly; - } - - public static string GetRefKindPrefix(this IParameterSymbol ps) - { - return ps.RefKind switch - { - RefKind.In => "in ", - RefKind.Out => "out ", - RefKind.Ref => "ref ", - _ => string.Empty - }; - } - - public static string GetParamsPrefix(this IParameterSymbol ps) => ps.IsParams ? "params " : string.Empty; - - public static string GetDefaultValue(this IParameterSymbol ps) - { - if (!ps.HasExplicitDefaultValue) - { - return string.Empty; - } - - string defaultValue; - if (ps.ExplicitDefaultValue == null) - { - if (ps.NullableAnnotation == NullableAnnotation.Annotated) - { - // The parameter is defined as Nullable, so always use "null". - defaultValue = ParameterValueNull; - } - else - { - defaultValue = ps.Type.IsReferenceType - ? ParameterValueNull : // The parameter is a ReferenceType, so use "null". - $"default({ps.Type})"; // The parameter is not a ReferenceType, so use "default(T)". - } - } - else - { - defaultValue = SymbolDisplay.FormatPrimitive(ps.ExplicitDefaultValue, true, false); - } - - return $" = {defaultValue}"; - } - - public static TypeEnum GetTypeEnum(this IParameterSymbol p) => p.Type.GetTypeEnum(); +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using ProxyInterfaceSourceGenerator.Enums; + +namespace ProxyInterfaceSourceGenerator.Extensions; + +internal static class ParameterSymbolExtensions +{ + private const string ParameterValueNull = "null"; + + public static bool IsRef(this IParameterSymbol ps) + { + return ps.RefKind is RefKind.Ref or RefKind.RefReadOnly; + } + + public static string GetRefKindPrefix(this IParameterSymbol ps) + { + return ps.RefKind switch + { + RefKind.In => "in ", + RefKind.Out => "out ", + RefKind.Ref => "ref ", + _ => string.Empty + }; + } + + public static string GetParamsPrefix(this IParameterSymbol ps) => ps.IsParams ? "params " : string.Empty; + + public static string GetDefaultValue(this IParameterSymbol ps) + { + if (!ps.HasExplicitDefaultValue) + { + return string.Empty; + } + + string defaultValue; + if (ps.ExplicitDefaultValue == null) + { + if (ps.NullableAnnotation == NullableAnnotation.Annotated) + { + // The parameter is defined as Nullable, so always use "null". + defaultValue = ParameterValueNull; + } + else + { + defaultValue = ps.Type.IsReferenceType + ? ParameterValueNull : // The parameter is a ReferenceType, so use "null". + $"default({ps.Type})"; // The parameter is not a ReferenceType, so use "default(T)". + } + } + else + { + defaultValue = SymbolDisplay.FormatPrimitive(ps.ExplicitDefaultValue, true, false); + } + + return $" = {defaultValue}"; + } + + public static TypeEnum GetTypeEnum(this IParameterSymbol p) => p.Type.GetTypeEnum(); } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/PropertySymbolExtensions.cs b/src/Speckle.ProxyGenerator/Extensions/PropertySymbolExtensions.cs similarity index 97% rename from src/ProxyInterfaceSourceGenerator/Extensions/PropertySymbolExtensions.cs rename to src/Speckle.ProxyGenerator/Extensions/PropertySymbolExtensions.cs index 07b7e5b..d38ca83 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/PropertySymbolExtensions.cs +++ b/src/Speckle.ProxyGenerator/Extensions/PropertySymbolExtensions.cs @@ -1,29 +1,29 @@ -using Microsoft.CodeAnalysis; -using ProxyInterfaceSourceGenerator.Enums; -using ProxyInterfaceSourceGenerator.FileGenerators; - -namespace ProxyInterfaceSourceGenerator.Extensions; - -internal static class PropertySymbolExtensions -{ - public static TypeEnum GetTypeEnum(this IPropertySymbol p) => p.Type.GetTypeEnum(); - - public static (string PropertyType, string? PropertyName, string GetSet)? ToPropertyDetails(this IPropertySymbol property, string? overrideType = null) - { - var getIsPublic = property.GetMethod.IsPublic(); - var setIsPublic = property.SetMethod.IsPublic(); - - if (!getIsPublic && !setIsPublic) - { - return null; - } - - var get = getIsPublic ? "get; " : string.Empty; - var set = setIsPublic ? "set; " : string.Empty; - - var type = !string.IsNullOrEmpty(overrideType) ? overrideType - : BaseGenerator.FixType(property.Type.ToFullyQualifiedDisplayString(), property.NullableAnnotation); - - return (type!, property.GetSanitizedName(), $"{{ {get}{set}}}"); - } +using Microsoft.CodeAnalysis; +using ProxyInterfaceSourceGenerator.Enums; +using ProxyInterfaceSourceGenerator.FileGenerators; + +namespace ProxyInterfaceSourceGenerator.Extensions; + +internal static class PropertySymbolExtensions +{ + public static TypeEnum GetTypeEnum(this IPropertySymbol p) => p.Type.GetTypeEnum(); + + public static (string PropertyType, string? PropertyName, string GetSet)? ToPropertyDetails(this IPropertySymbol property, string? overrideType = null) + { + var getIsPublic = property.GetMethod.IsPublic(); + var setIsPublic = property.SetMethod.IsPublic(); + + if (!getIsPublic && !setIsPublic) + { + return null; + } + + var get = getIsPublic ? "get; " : string.Empty; + var set = setIsPublic ? "set; " : string.Empty; + + var type = !string.IsNullOrEmpty(overrideType) ? overrideType + : BaseGenerator.FixType(property.Type.ToFullyQualifiedDisplayString(), property.NullableAnnotation); + + return (type!, property.GetSanitizedName(), $"{{ {get}{set}}}"); + } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/StringExtensions.cs b/src/Speckle.ProxyGenerator/Extensions/StringExtensions.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Extensions/StringExtensions.cs rename to src/Speckle.ProxyGenerator/Extensions/StringExtensions.cs diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs b/src/Speckle.ProxyGenerator/Extensions/SymbolExtensions.cs similarity index 97% rename from src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs rename to src/Speckle.ProxyGenerator/Extensions/SymbolExtensions.cs index cbf5c74..5c34313 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs +++ b/src/Speckle.ProxyGenerator/Extensions/SymbolExtensions.cs @@ -1,74 +1,74 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using System.Text; - -namespace ProxyInterfaceSourceGenerator.Extensions; - -internal static class SymbolExtensions -{ - private static readonly string[] ExcludedAttributes = - { - "System.Runtime.CompilerServices.NullableAttribute", - "System.Runtime.CompilerServices.NullableContextAttribute", - "System.Runtime.CompilerServices.AsyncStateMachineAttribute" - }; - - public static IReadOnlyList GetAttributesAsList(this ISymbol symbol) - { - return symbol - .GetAttributes() - .Where(a => a.AttributeClass.IsPublic() && !ExcludedAttributes.Contains(a.AttributeClass?.ToString(), StringComparer.OrdinalIgnoreCase)) - .Select(a => $"[{a}]") - .ToArray(); - } - - public static string GetAttributesPrefix(this ISymbol symbol) - { - var attributes = symbol.GetAttributesAsList(); - - return attributes.Any() ? $"{string.Join(" ", attributes)} " : string.Empty; - } - - //https://stackoverflow.com/questions/27105909/get-fully-qualified-metadata-name-in-roslyn - public static string GetFullMetadataName(this ISymbol? s) - { - if (s == null || IsRootNamespace(s)) - { - return string.Empty; - } - - var sb = new StringBuilder(s.MetadataName); - var last = s; - - s = s.ContainingSymbol; - - while (!IsRootNamespace(s)) - { - if (s is ITypeSymbol && last is ITypeSymbol) - { - sb.Insert(0, '+'); - } - else - { - sb.Insert(0, '.'); - } - - sb.Insert(0, s.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); - s = s.ContainingSymbol; - } - - return sb.ToString(); - } - - public static string GetSanitizedName(this ISymbol symbol) => - symbol.IsKeywordOrReserved() ? $"@{symbol.Name}" : symbol.Name; - - public static bool IsKeywordOrReserved(this ISymbol symbol) => - SyntaxFacts.GetKeywordKind(symbol.Name) != SyntaxKind.None || SyntaxFacts.GetContextualKeywordKind(symbol.Name) != SyntaxKind.None; - - public static bool IsPublic(this ISymbol? symbol) => - symbol is { DeclaredAccessibility: Accessibility.Public }; - - private static bool IsRootNamespace(ISymbol symbol) => - symbol is INamespaceSymbol { IsGlobalNamespace: true }; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System.Text; + +namespace ProxyInterfaceSourceGenerator.Extensions; + +internal static class SymbolExtensions +{ + private static readonly string[] ExcludedAttributes = + { + "System.Runtime.CompilerServices.NullableAttribute", + "System.Runtime.CompilerServices.NullableContextAttribute", + "System.Runtime.CompilerServices.AsyncStateMachineAttribute" + }; + + public static IReadOnlyList GetAttributesAsList(this ISymbol symbol) + { + return symbol + .GetAttributes() + .Where(a => a.AttributeClass.IsPublic() && !ExcludedAttributes.Contains(a.AttributeClass?.ToString(), StringComparer.OrdinalIgnoreCase)) + .Select(a => $"[{a}]") + .ToArray(); + } + + public static string GetAttributesPrefix(this ISymbol symbol) + { + var attributes = symbol.GetAttributesAsList(); + + return attributes.Any() ? $"{string.Join(" ", attributes)} " : string.Empty; + } + + //https://stackoverflow.com/questions/27105909/get-fully-qualified-metadata-name-in-roslyn + public static string GetFullMetadataName(this ISymbol? s) + { + if (s == null || IsRootNamespace(s)) + { + return string.Empty; + } + + var sb = new StringBuilder(s.MetadataName); + var last = s; + + s = s.ContainingSymbol; + + while (!IsRootNamespace(s)) + { + if (s is ITypeSymbol && last is ITypeSymbol) + { + sb.Insert(0, '+'); + } + else + { + sb.Insert(0, '.'); + } + + sb.Insert(0, s.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); + s = s.ContainingSymbol; + } + + return sb.ToString(); + } + + public static string GetSanitizedName(this ISymbol symbol) => + symbol.IsKeywordOrReserved() ? $"@{symbol.Name}" : symbol.Name; + + public static bool IsKeywordOrReserved(this ISymbol symbol) => + SyntaxFacts.GetKeywordKind(symbol.Name) != SyntaxKind.None || SyntaxFacts.GetContextualKeywordKind(symbol.Name) != SyntaxKind.None; + + public static bool IsPublic(this ISymbol? symbol) => + symbol is { DeclaredAccessibility: Accessibility.Public }; + + private static bool IsRootNamespace(ISymbol symbol) => + symbol is INamespaceSymbol { IsGlobalNamespace: true }; } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/SyntaxNodeExtensions.cs b/src/Speckle.ProxyGenerator/Extensions/SyntaxNodeExtensions.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Extensions/SyntaxNodeExtensions.cs rename to src/Speckle.ProxyGenerator/Extensions/SyntaxNodeExtensions.cs diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/TypeSymbolExtensions.cs b/src/Speckle.ProxyGenerator/Extensions/TypeSymbolExtensions.cs similarity index 96% rename from src/ProxyInterfaceSourceGenerator/Extensions/TypeSymbolExtensions.cs rename to src/Speckle.ProxyGenerator/Extensions/TypeSymbolExtensions.cs index 7327ae1..9aec671 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/TypeSymbolExtensions.cs +++ b/src/Speckle.ProxyGenerator/Extensions/TypeSymbolExtensions.cs @@ -1,28 +1,28 @@ -using Microsoft.CodeAnalysis; -using ProxyInterfaceSourceGenerator.Enums; - -namespace ProxyInterfaceSourceGenerator.Extensions; - -internal static class TypeSymbolExtensions -{ - public static TypeEnum GetTypeEnum(this ITypeSymbol ts) - { - if (ts.IsValueType || ts.IsString()) - { - return TypeEnum.ValueTypeOrString; - } - - if (ts.TypeKind == TypeKind.Interface) - { - return TypeEnum.Interface; - } - - return TypeEnum.Complex; - } - - public static bool IsString(this ITypeSymbol ts) => - ts.ToString().ToLowerInvariant() == "string" || ts.ToString().ToLowerInvariant() == "string?"; - - public static string ToFullyQualifiedDisplayString(this ITypeSymbol property) => - property.ToDisplayString(NullableFlowState.None, SymbolDisplayFormat.FullyQualifiedFormat); +using Microsoft.CodeAnalysis; +using ProxyInterfaceSourceGenerator.Enums; + +namespace ProxyInterfaceSourceGenerator.Extensions; + +internal static class TypeSymbolExtensions +{ + public static TypeEnum GetTypeEnum(this ITypeSymbol ts) + { + if (ts.IsValueType || ts.IsString()) + { + return TypeEnum.ValueTypeOrString; + } + + if (ts.TypeKind == TypeKind.Interface) + { + return TypeEnum.Interface; + } + + return TypeEnum.Complex; + } + + public static bool IsString(this ITypeSymbol ts) => + ts.ToString().ToLowerInvariant() == "string" || ts.ToString().ToLowerInvariant() == "string?"; + + public static string ToFullyQualifiedDisplayString(this ITypeSymbol property) => + property.ToDisplayString(NullableFlowState.None, SymbolDisplayFormat.FullyQualifiedFormat); } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs b/src/Speckle.ProxyGenerator/FileGenerators/BaseGenerator.cs similarity index 97% rename from src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs rename to src/Speckle.ProxyGenerator/FileGenerators/BaseGenerator.cs index c399d93..6ba0275 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs +++ b/src/Speckle.ProxyGenerator/FileGenerators/BaseGenerator.cs @@ -1,262 +1,262 @@ -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Text; -using Microsoft.CodeAnalysis; -using ProxyInterfaceSourceGenerator.Builders; -using ProxyInterfaceSourceGenerator.Enums; -using ProxyInterfaceSourceGenerator.Extensions; -using ProxyInterfaceSourceGenerator.Models; - -namespace ProxyInterfaceSourceGenerator.FileGenerators; - -internal abstract class BaseGenerator -{ - protected readonly Context Context; - protected readonly bool SupportsNullable; - - protected BaseGenerator(Context context, bool supportsNullable) - { - Context = context; - SupportsNullable = supportsNullable; - } - - protected string GetPropertyType(IPropertySymbol property, out bool isReplaced) - { - return GetReplacedTypeAsString(property.Type, out isReplaced); - } - - protected string GetParameterType(IParameterSymbol property, out bool isReplaced) - { - return GetReplacedTypeAsString(property.Type, out isReplaced); - } - - protected bool TryFindProxyDataByTypeName(string type, [NotNullWhen(true)] out ProxyData? proxyData) - { - proxyData = Context.Candidates.Values.FirstOrDefault(x => x.FullQualifiedTypeName == type); - return proxyData != null; - } - - protected string GetWhereStatementFromMethod(IMethodSymbol method) - { - if (!method.IsGenericMethod) - { - return string.Empty; - } - - var list = new List(); - foreach (var typeParameterSymbol in method.TypeParameters) - { - if (TryGetWhereConstraints(typeParameterSymbol, false, out var constraint)) - { - list.Add(constraint.ToString()); - } - } - - return string.Concat(list); - } - - protected string ResolveInterfaceNameWithOptionalTypeConstraints(INamedTypeSymbol namedTypeSymbol, string interfaceName) - { - if (!namedTypeSymbol.IsGenericType) - { - return interfaceName; - } - - var str = new StringBuilder($"{interfaceName}<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(ta => ta.Name))}>"); - - foreach (var typeParameterSymbol in namedTypeSymbol.TypeArguments.OfType()) - { - if (TryGetWhereConstraints(typeParameterSymbol, false, out var constraint)) - { - str.Append(constraint); - } - } - - return str.ToString(); - } - - /// - /// https://www.codeproject.com/Articles/871704/Roslyn-Code-Analysis-in-Easy-Samples-Part-2 - /// - public bool TryGetWhereConstraints(ITypeParameterSymbol typeParameterSymbol, bool replaceIt, [NotNullWhen(true)] out ConstraintInfo? constraint) - { - var constraints = new List(); - if (typeParameterSymbol.HasReferenceTypeConstraint) - { - constraints.Add("class"); - } - - if (typeParameterSymbol.HasValueTypeConstraint) - { - constraints.Add("struct"); - } - - foreach (var namedTypeSymbol in typeParameterSymbol.ConstraintTypes.OfType()) - { - if (replaceIt) - { - constraints.Add(GetReplacedTypeAsString(namedTypeSymbol, out _)); - } - else - { - constraints.Add(namedTypeSymbol.GetFullType()); - } - } - - // The new() constraint must be the last constraint specified. - if (typeParameterSymbol.HasConstructorConstraint) - { - constraints.Add("new()"); - } - - if (constraints.Count > 0) - { - constraint = new(typeParameterSymbol.Name, constraints); - return true; - } - - constraint = null; - return false; - } - - internal readonly SymbolDisplayFormat NullableDisplayFormat = new SymbolDisplayFormat( - globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included, - typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, - genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, - miscellaneousOptions: - SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | - SymbolDisplayMiscellaneousOptions.UseSpecialTypes | - SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier); - - protected string GetReplacedTypeAsString(ITypeSymbol typeSymbol, out bool isReplaced) - { - isReplaced = false; - - var typeSymbolAsString = typeSymbol.ToFullyQualifiedDisplayString(); - var nullableTypeSymbolAsString = typeSymbol.ToDisplayString(NullableFlowState.None, NullableDisplayFormat); - - if (TryFindProxyDataByTypeName(typeSymbolAsString, out var existing)) - { - if (!Context.ReplacedTypes.ContainsKey(typeSymbolAsString)) - { - Context.ReplacedTypes.Add(typeSymbolAsString, existing.FullInterfaceName); - } - - isReplaced = true; - return FixType(existing.FullInterfaceName, typeSymbol.NullableAnnotation); - } - - ITypeSymbol[] typeArguments; - if (typeSymbol is INamedTypeSymbol namedTypedSymbol1) - { - typeArguments = namedTypedSymbol1.TypeArguments.ToArray(); - } - else if (typeSymbol is IArrayTypeSymbol arrayTypeSymbol) - { - typeArguments = new[] { arrayTypeSymbol.ElementType }; - } - else - { - return FixType(typeSymbolAsString, typeSymbol.NullableAnnotation); - } - - var propertyTypeAsStringToBeModified = nullableTypeSymbolAsString; - foreach (var typeArgument in typeArguments) - { - var typeArgumentAsString = typeArgument.ToFullyQualifiedDisplayString(); - - if (TryFindProxyDataByTypeName(typeArgumentAsString, out var existingTypeArgument)) - { - isReplaced = true; - - if (!Context.ReplacedTypes.ContainsKey(typeArgumentAsString)) - { - Context.ReplacedTypes.Add(typeArgumentAsString, existingTypeArgument.FullInterfaceName); - } - - propertyTypeAsStringToBeModified = propertyTypeAsStringToBeModified.Replace(typeArgumentAsString, existingTypeArgument.FullInterfaceName); - } - } - - return FixType(propertyTypeAsStringToBeModified, typeSymbol.NullableAnnotation); - } - - protected bool TryGetNamedTypeSymbolByFullName(TypeKind kind, string name, IEnumerable usings, [NotNullWhen(true)] out ClassSymbol? classSymbol) - { - classSymbol = default; - const string globalPrefix = "global::"; - if (name.StartsWith(globalPrefix, StringComparison.Ordinal)) - { - name = name.Substring(globalPrefix.Length); - } - - // The GetTypeByMetadataName method returns null if no type matches the full name or if 2 or more types (in different assemblies) match the full name. - var symbol = Context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName(name); - - if (symbol is not null && symbol.TypeKind == kind) - { - classSymbol = new ClassSymbol(symbol, symbol.GetBaseTypes(), symbol.AllInterfaces.ToList()); - return true; - } - - foreach (var @using in usings) - { - symbol = Context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName($"{@using}.{name}"); - if (symbol is not null && symbol.TypeKind == kind) - { - classSymbol = new ClassSymbol(symbol, symbol.GetBaseTypes(), symbol.AllInterfaces.ToList()); - return true; - } - } - - return false; - } - - protected IReadOnlyList GetMethodParameters(ImmutableArray parameterSymbols, bool includeType) - { - var methodParameters = new List(); - foreach (var parameterSymbol in parameterSymbols) - { - string? type = null; - if (includeType) - { - if (parameterSymbol.GetTypeEnum() == TypeEnum.Complex) - { - type = GetParameterType(parameterSymbol, out _); - } - else - { - type = FixType(parameterSymbol.Type.ToFullyQualifiedDisplayString(), parameterSymbol.NullableAnnotation); - } - } - - methodParameters.Add(MethodParameterBuilder.Build(parameterSymbol, type)); - } - - return methodParameters; - } - - protected IReadOnlyList GetExtendsProxyData(ProxyData proxyData, ClassSymbol targetClassSymbol) - { - var extendsProxyClasses = new List(); - foreach (var baseType in targetClassSymbol.BaseTypes) - { - var candidate = Context.Candidates.Values.FirstOrDefault(ci => ci.FullQualifiedTypeName == baseType.ToFullyQualifiedDisplayString()); - if (candidate is not null) - { - extendsProxyClasses.Add(candidate); - break; - } - } - return extendsProxyClasses; - } - - internal static string FixType(string type, NullableAnnotation nullableAnnotation) - { - if (nullableAnnotation == NullableAnnotation.Annotated && !type.EndsWith("?", StringComparison.Ordinal)) - { - return $"{type}?"; - } - return type; - } +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using Microsoft.CodeAnalysis; +using ProxyInterfaceSourceGenerator.Builders; +using ProxyInterfaceSourceGenerator.Enums; +using ProxyInterfaceSourceGenerator.Extensions; +using ProxyInterfaceSourceGenerator.Models; + +namespace ProxyInterfaceSourceGenerator.FileGenerators; + +internal abstract class BaseGenerator +{ + protected readonly Context Context; + protected readonly bool SupportsNullable; + + protected BaseGenerator(Context context, bool supportsNullable) + { + Context = context; + SupportsNullable = supportsNullable; + } + + protected string GetPropertyType(IPropertySymbol property, out bool isReplaced) + { + return GetReplacedTypeAsString(property.Type, out isReplaced); + } + + protected string GetParameterType(IParameterSymbol property, out bool isReplaced) + { + return GetReplacedTypeAsString(property.Type, out isReplaced); + } + + protected bool TryFindProxyDataByTypeName(string type, [NotNullWhen(true)] out ProxyData? proxyData) + { + proxyData = Context.Candidates.Values.FirstOrDefault(x => x.FullQualifiedTypeName == type); + return proxyData != null; + } + + protected string GetWhereStatementFromMethod(IMethodSymbol method) + { + if (!method.IsGenericMethod) + { + return string.Empty; + } + + var list = new List(); + foreach (var typeParameterSymbol in method.TypeParameters) + { + if (TryGetWhereConstraints(typeParameterSymbol, false, out var constraint)) + { + list.Add(constraint.ToString()); + } + } + + return string.Concat(list); + } + + protected string ResolveInterfaceNameWithOptionalTypeConstraints(INamedTypeSymbol namedTypeSymbol, string interfaceName) + { + if (!namedTypeSymbol.IsGenericType) + { + return interfaceName; + } + + var str = new StringBuilder($"{interfaceName}<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(ta => ta.Name))}>"); + + foreach (var typeParameterSymbol in namedTypeSymbol.TypeArguments.OfType()) + { + if (TryGetWhereConstraints(typeParameterSymbol, false, out var constraint)) + { + str.Append(constraint); + } + } + + return str.ToString(); + } + + /// + /// https://www.codeproject.com/Articles/871704/Roslyn-Code-Analysis-in-Easy-Samples-Part-2 + /// + public bool TryGetWhereConstraints(ITypeParameterSymbol typeParameterSymbol, bool replaceIt, [NotNullWhen(true)] out ConstraintInfo? constraint) + { + var constraints = new List(); + if (typeParameterSymbol.HasReferenceTypeConstraint) + { + constraints.Add("class"); + } + + if (typeParameterSymbol.HasValueTypeConstraint) + { + constraints.Add("struct"); + } + + foreach (var namedTypeSymbol in typeParameterSymbol.ConstraintTypes.OfType()) + { + if (replaceIt) + { + constraints.Add(GetReplacedTypeAsString(namedTypeSymbol, out _)); + } + else + { + constraints.Add(namedTypeSymbol.GetFullType()); + } + } + + // The new() constraint must be the last constraint specified. + if (typeParameterSymbol.HasConstructorConstraint) + { + constraints.Add("new()"); + } + + if (constraints.Count > 0) + { + constraint = new(typeParameterSymbol.Name, constraints); + return true; + } + + constraint = null; + return false; + } + + internal readonly SymbolDisplayFormat NullableDisplayFormat = new SymbolDisplayFormat( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | + SymbolDisplayMiscellaneousOptions.UseSpecialTypes | + SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier); + + protected string GetReplacedTypeAsString(ITypeSymbol typeSymbol, out bool isReplaced) + { + isReplaced = false; + + var typeSymbolAsString = typeSymbol.ToFullyQualifiedDisplayString(); + var nullableTypeSymbolAsString = typeSymbol.ToDisplayString(NullableFlowState.None, NullableDisplayFormat); + + if (TryFindProxyDataByTypeName(typeSymbolAsString, out var existing)) + { + if (!Context.ReplacedTypes.ContainsKey(typeSymbolAsString)) + { + Context.ReplacedTypes.Add(typeSymbolAsString, existing.FullInterfaceName); + } + + isReplaced = true; + return FixType(existing.FullInterfaceName, typeSymbol.NullableAnnotation); + } + + ITypeSymbol[] typeArguments; + if (typeSymbol is INamedTypeSymbol namedTypedSymbol1) + { + typeArguments = namedTypedSymbol1.TypeArguments.ToArray(); + } + else if (typeSymbol is IArrayTypeSymbol arrayTypeSymbol) + { + typeArguments = new[] { arrayTypeSymbol.ElementType }; + } + else + { + return FixType(typeSymbolAsString, typeSymbol.NullableAnnotation); + } + + var propertyTypeAsStringToBeModified = nullableTypeSymbolAsString; + foreach (var typeArgument in typeArguments) + { + var typeArgumentAsString = typeArgument.ToFullyQualifiedDisplayString(); + + if (TryFindProxyDataByTypeName(typeArgumentAsString, out var existingTypeArgument)) + { + isReplaced = true; + + if (!Context.ReplacedTypes.ContainsKey(typeArgumentAsString)) + { + Context.ReplacedTypes.Add(typeArgumentAsString, existingTypeArgument.FullInterfaceName); + } + + propertyTypeAsStringToBeModified = propertyTypeAsStringToBeModified.Replace(typeArgumentAsString, existingTypeArgument.FullInterfaceName); + } + } + + return FixType(propertyTypeAsStringToBeModified, typeSymbol.NullableAnnotation); + } + + protected bool TryGetNamedTypeSymbolByFullName(TypeKind kind, string name, IEnumerable usings, [NotNullWhen(true)] out ClassSymbol? classSymbol) + { + classSymbol = default; + const string globalPrefix = "global::"; + if (name.StartsWith(globalPrefix, StringComparison.Ordinal)) + { + name = name.Substring(globalPrefix.Length); + } + + // The GetTypeByMetadataName method returns null if no type matches the full name or if 2 or more types (in different assemblies) match the full name. + var symbol = Context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName(name); + + if (symbol is not null && symbol.TypeKind == kind) + { + classSymbol = new ClassSymbol(symbol, symbol.GetBaseTypes(), symbol.AllInterfaces.ToList()); + return true; + } + + foreach (var @using in usings) + { + symbol = Context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName($"{@using}.{name}"); + if (symbol is not null && symbol.TypeKind == kind) + { + classSymbol = new ClassSymbol(symbol, symbol.GetBaseTypes(), symbol.AllInterfaces.ToList()); + return true; + } + } + + return false; + } + + protected IReadOnlyList GetMethodParameters(ImmutableArray parameterSymbols, bool includeType) + { + var methodParameters = new List(); + foreach (var parameterSymbol in parameterSymbols) + { + string? type = null; + if (includeType) + { + if (parameterSymbol.GetTypeEnum() == TypeEnum.Complex) + { + type = GetParameterType(parameterSymbol, out _); + } + else + { + type = FixType(parameterSymbol.Type.ToFullyQualifiedDisplayString(), parameterSymbol.NullableAnnotation); + } + } + + methodParameters.Add(MethodParameterBuilder.Build(parameterSymbol, type)); + } + + return methodParameters; + } + + protected IReadOnlyList GetExtendsProxyData(ProxyData proxyData, ClassSymbol targetClassSymbol) + { + var extendsProxyClasses = new List(); + foreach (var baseType in targetClassSymbol.BaseTypes) + { + var candidate = Context.Candidates.Values.FirstOrDefault(ci => ci.FullQualifiedTypeName == baseType.ToFullyQualifiedDisplayString()); + if (candidate is not null) + { + extendsProxyClasses.Add(candidate); + break; + } + } + return extendsProxyClasses; + } + + internal static string FixType(string type, NullableAnnotation nullableAnnotation) + { + if (nullableAnnotation == NullableAnnotation.Annotated && !type.EndsWith("?", StringComparison.Ordinal)) + { + return $"{type}?"; + } + return type; + } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ExtraFilesGenerator.cs b/src/Speckle.ProxyGenerator/FileGenerators/ExtraFilesGenerator.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/FileGenerators/ExtraFilesGenerator.cs rename to src/Speckle.ProxyGenerator/FileGenerators/ExtraFilesGenerator.cs diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/IFileGenerator.cs b/src/Speckle.ProxyGenerator/FileGenerators/IFileGenerator.cs similarity index 96% rename from src/ProxyInterfaceSourceGenerator/FileGenerators/IFileGenerator.cs rename to src/Speckle.ProxyGenerator/FileGenerators/IFileGenerator.cs index 237d747..cb0457c 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/IFileGenerator.cs +++ b/src/Speckle.ProxyGenerator/FileGenerators/IFileGenerator.cs @@ -1,8 +1,8 @@ -using ProxyInterfaceSourceGenerator.Models; - -namespace ProxyInterfaceSourceGenerator.FileGenerators; - -internal interface IFileGenerator -{ - FileData GenerateFile(); +using ProxyInterfaceSourceGenerator.Models; + +namespace ProxyInterfaceSourceGenerator.FileGenerators; + +internal interface IFileGenerator +{ + FileData GenerateFile(); } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/IFilesGenerator.cs b/src/Speckle.ProxyGenerator/FileGenerators/IFilesGenerator.cs similarity index 96% rename from src/ProxyInterfaceSourceGenerator/FileGenerators/IFilesGenerator.cs rename to src/Speckle.ProxyGenerator/FileGenerators/IFilesGenerator.cs index 4126668..6a98c78 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/IFilesGenerator.cs +++ b/src/Speckle.ProxyGenerator/FileGenerators/IFilesGenerator.cs @@ -1,8 +1,8 @@ -using ProxyInterfaceSourceGenerator.Models; - -namespace ProxyInterfaceSourceGenerator.FileGenerators; - -internal interface IFilesGenerator -{ - IEnumerable GenerateFiles(); +using ProxyInterfaceSourceGenerator.Models; + +namespace ProxyInterfaceSourceGenerator.FileGenerators; + +internal interface IFilesGenerator +{ + IEnumerable GenerateFiles(); } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs b/src/Speckle.ProxyGenerator/FileGenerators/PartialInterfacesGenerator.cs similarity index 97% rename from src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs rename to src/Speckle.ProxyGenerator/FileGenerators/PartialInterfacesGenerator.cs index c4c7448..505cb71 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs +++ b/src/Speckle.ProxyGenerator/FileGenerators/PartialInterfacesGenerator.cs @@ -1,184 +1,184 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using ProxyInterfaceSourceGenerator.Enums; -using ProxyInterfaceSourceGenerator.Extensions; -using ProxyInterfaceSourceGenerator.Models; -using ProxyInterfaceSourceGenerator.Utils; - -namespace ProxyInterfaceSourceGenerator.FileGenerators; - -internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator -{ - private IReadOnlyCollection _implementedInterfaces = new List(); - - public PartialInterfacesGenerator(Context context, bool supportsNullable) : base(context, supportsNullable) - { - } - - public IEnumerable GenerateFiles() - { - foreach (var ci in Context.Candidates) - { - if (TryGenerateFile(ci.Key, ci.Value, out var file)) - { - yield return file; - } - } - } - - private bool TryGenerateFile(InterfaceDeclarationSyntax ci, ProxyData pd, [NotNullWhen(true)] out FileData? fileData) - { - fileData = default; - - if (!TryGetNamedTypeSymbolByFullName(TypeKind.Interface, ci.Identifier.ToString(), pd.Usings, out var sourceInterfaceSymbol)) - { - return false; - } - - if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.FullMetadataTypeName, pd.Usings, out var targetClassSymbol)) - { - return false; - } - - var interfaceName = ResolveInterfaceNameWithOptionalTypeConstraints(targetClassSymbol.Symbol, pd.ShortInterfaceName); - - fileData = new FileData( - $"{sourceInterfaceSymbol.Symbol.GetFullMetadataName()}.g.cs", - CreatePartialInterfaceCode(pd.Namespace, targetClassSymbol, interfaceName, pd) - ); - - return true; - } - - private string CreatePartialInterfaceCode( - string ns, - ClassSymbol classSymbol, - string interfaceName, - ProxyData proxyData) - { - var extendsProxyClasses = GetExtendsProxyData(proxyData, classSymbol); - _implementedInterfaces = classSymbol.Symbol.ResolveImplementedInterfaces(proxyData.ProxyBaseClasses); - var implementedInterfacesNames = _implementedInterfaces.Select(i => i.ToFullyQualifiedDisplayString()).ToArray(); - var implements = implementedInterfacesNames.Any() ? $" : {string.Join(", ", implementedInterfacesNames)}" : string.Empty; - var @new = extendsProxyClasses.Any() ? "new " : string.Empty; - var (namespaceStart, namespaceEnd) = NamespaceBuilder.Build(ns); - var events = GenerateEvents(classSymbol, proxyData); - var properties = GenerateProperties(classSymbol, proxyData); - var methods = GenerateMethods(classSymbol, proxyData).TrimEnd(); - - return $@"//---------------------------------------------------------------------------------------- -// -// This code was generated by https://github.com/StefH/ProxyInterfaceSourceGenerator. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//---------------------------------------------------------------------------------------- - -{SupportsNullable.IIf("#nullable enable")} -using System; - -{namespaceStart} - public partial interface {interfaceName}{implements} - {{ - {@new}{classSymbol} _Instance {{ get; }} - -{events + -properties + -methods} - }} -{namespaceEnd} -{SupportsNullable.IIf("#nullable restore")}"; - } - - private Func InterfaceFilter() where T : ISymbol - { - var hashSet = new HashSet(); - foreach (var @interface in _implementedInterfaces) - { - var members = @interface.AllInterfaces.Aggregate(@interface.GetMembers(), (xs, x) => xs.AddRange(x.GetMembers())); - foreach (var member in members) - { - hashSet.Add(member.Name); - } - } - - // Member is not already implemented in another interface. - return t => !hashSet.Contains(t.Name); - } - - private string GenerateProperties(ClassSymbol targetClassSymbol, ProxyData proxyData) - { - var str = new StringBuilder(); - - foreach (var property in MemberHelper.GetPublicProperties(targetClassSymbol, proxyData, InterfaceFilter())) - { - var type = GetPropertyType(property, out var isReplaced); - - var getterSetter = isReplaced ? property.ToPropertyDetails(type) : property.ToPropertyDetails(); - if (getterSetter is null) - { - continue; - } - - var propertyName = getterSetter.Value.PropertyName; - - if (property.IsIndexer) - { - var methodParameters = GetMethodParameters(property.Parameters, true); - propertyName = $"this[{string.Join(", ", methodParameters)}]"; - } - - foreach (var attribute in property.GetAttributesAsList()) - { - str.AppendLine($" {attribute}"); - } - - str.AppendLine($" {getterSetter.Value.PropertyType} {propertyName} {getterSetter.Value.GetSet}"); - str.AppendLine(); - } - return str.ToString(); - } - - private string GenerateMethods(ClassSymbol targetClassSymbol, ProxyData proxyData) - { - var str = new StringBuilder(); - foreach (var method in MemberHelper.GetPublicMethods(targetClassSymbol, proxyData, InterfaceFilter())) - { - var methodParameters = GetMethodParameters(method.Parameters, true); - var whereStatement = GetWhereStatementFromMethod(method); - - foreach (var attribute in method.GetAttributesAsList()) - { - str.AppendLine($" {attribute}"); - } - - str.AppendLine($" {GetReplacedTypeAsString(method.ReturnType, out _)} {method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", methodParameters)}){whereStatement};"); - str.AppendLine(); - } - - return str.ToString(); - } - - private string GenerateEvents(ClassSymbol targetClassSymbol, ProxyData proxyData) - { - var str = new StringBuilder(); - foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol, proxyData, InterfaceFilter())) - { - var ps = @event.First().Parameters.First(); - var type = ps.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(ps, out _) : ps.Type.ToString(); - - foreach (var attribute in ps.GetAttributesAsList()) - { - str.AppendLine($" {attribute}"); - } - - str.AppendLine($" event {type} {@event.Key.GetSanitizedName()};"); - str.AppendLine(); - } - - return str.ToString(); - } +using System.Diagnostics.CodeAnalysis; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using ProxyInterfaceSourceGenerator.Enums; +using ProxyInterfaceSourceGenerator.Extensions; +using ProxyInterfaceSourceGenerator.Models; +using ProxyInterfaceSourceGenerator.Utils; + +namespace ProxyInterfaceSourceGenerator.FileGenerators; + +internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator +{ + private IReadOnlyCollection _implementedInterfaces = new List(); + + public PartialInterfacesGenerator(Context context, bool supportsNullable) : base(context, supportsNullable) + { + } + + public IEnumerable GenerateFiles() + { + foreach (var ci in Context.Candidates) + { + if (TryGenerateFile(ci.Key, ci.Value, out var file)) + { + yield return file; + } + } + } + + private bool TryGenerateFile(InterfaceDeclarationSyntax ci, ProxyData pd, [NotNullWhen(true)] out FileData? fileData) + { + fileData = default; + + if (!TryGetNamedTypeSymbolByFullName(TypeKind.Interface, ci.Identifier.ToString(), pd.Usings, out var sourceInterfaceSymbol)) + { + return false; + } + + if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.FullMetadataTypeName, pd.Usings, out var targetClassSymbol)) + { + return false; + } + + var interfaceName = ResolveInterfaceNameWithOptionalTypeConstraints(targetClassSymbol.Symbol, pd.ShortInterfaceName); + + fileData = new FileData( + $"{sourceInterfaceSymbol.Symbol.GetFullMetadataName()}.g.cs", + CreatePartialInterfaceCode(pd.Namespace, targetClassSymbol, interfaceName, pd) + ); + + return true; + } + + private string CreatePartialInterfaceCode( + string ns, + ClassSymbol classSymbol, + string interfaceName, + ProxyData proxyData) + { + var extendsProxyClasses = GetExtendsProxyData(proxyData, classSymbol); + _implementedInterfaces = classSymbol.Symbol.ResolveImplementedInterfaces(proxyData.ProxyBaseClasses); + var implementedInterfacesNames = _implementedInterfaces.Select(i => i.ToFullyQualifiedDisplayString()).ToArray(); + var implements = implementedInterfacesNames.Any() ? $" : {string.Join(", ", implementedInterfacesNames)}" : string.Empty; + var @new = extendsProxyClasses.Any() ? "new " : string.Empty; + var (namespaceStart, namespaceEnd) = NamespaceBuilder.Build(ns); + var events = GenerateEvents(classSymbol, proxyData); + var properties = GenerateProperties(classSymbol, proxyData); + var methods = GenerateMethods(classSymbol, proxyData).TrimEnd(); + + return $@"//---------------------------------------------------------------------------------------- +// +// This code was generated by https://github.com/StefH/ProxyInterfaceSourceGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//---------------------------------------------------------------------------------------- + +{SupportsNullable.IIf("#nullable enable")} +using System; + +{namespaceStart} + public partial interface {interfaceName}{implements} + {{ + {@new}{classSymbol} _Instance {{ get; }} + +{events + +properties + +methods} + }} +{namespaceEnd} +{SupportsNullable.IIf("#nullable restore")}"; + } + + private Func InterfaceFilter() where T : ISymbol + { + var hashSet = new HashSet(); + foreach (var @interface in _implementedInterfaces) + { + var members = @interface.AllInterfaces.Aggregate(@interface.GetMembers(), (xs, x) => xs.AddRange(x.GetMembers())); + foreach (var member in members) + { + hashSet.Add(member.Name); + } + } + + // Member is not already implemented in another interface. + return t => !hashSet.Contains(t.Name); + } + + private string GenerateProperties(ClassSymbol targetClassSymbol, ProxyData proxyData) + { + var str = new StringBuilder(); + + foreach (var property in MemberHelper.GetPublicProperties(targetClassSymbol, proxyData, InterfaceFilter())) + { + var type = GetPropertyType(property, out var isReplaced); + + var getterSetter = isReplaced ? property.ToPropertyDetails(type) : property.ToPropertyDetails(); + if (getterSetter is null) + { + continue; + } + + var propertyName = getterSetter.Value.PropertyName; + + if (property.IsIndexer) + { + var methodParameters = GetMethodParameters(property.Parameters, true); + propertyName = $"this[{string.Join(", ", methodParameters)}]"; + } + + foreach (var attribute in property.GetAttributesAsList()) + { + str.AppendLine($" {attribute}"); + } + + str.AppendLine($" {getterSetter.Value.PropertyType} {propertyName} {getterSetter.Value.GetSet}"); + str.AppendLine(); + } + return str.ToString(); + } + + private string GenerateMethods(ClassSymbol targetClassSymbol, ProxyData proxyData) + { + var str = new StringBuilder(); + foreach (var method in MemberHelper.GetPublicMethods(targetClassSymbol, proxyData, InterfaceFilter())) + { + var methodParameters = GetMethodParameters(method.Parameters, true); + var whereStatement = GetWhereStatementFromMethod(method); + + foreach (var attribute in method.GetAttributesAsList()) + { + str.AppendLine($" {attribute}"); + } + + str.AppendLine($" {GetReplacedTypeAsString(method.ReturnType, out _)} {method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", methodParameters)}){whereStatement};"); + str.AppendLine(); + } + + return str.ToString(); + } + + private string GenerateEvents(ClassSymbol targetClassSymbol, ProxyData proxyData) + { + var str = new StringBuilder(); + foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol, proxyData, InterfaceFilter())) + { + var ps = @event.First().Parameters.First(); + var type = ps.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(ps, out _) : ps.Type.ToString(); + + foreach (var attribute in ps.GetAttributesAsList()) + { + str.AppendLine($" {attribute}"); + } + + str.AppendLine($" event {type} {@event.Key.GetSanitizedName()};"); + str.AppendLine(); + } + + return str.ToString(); + } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.AutoMapper.cs b/src/Speckle.ProxyGenerator/FileGenerators/ProxyClassesGenerator.AutoMapper.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.AutoMapper.cs rename to src/Speckle.ProxyGenerator/FileGenerators/ProxyClassesGenerator.AutoMapper.cs diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.Mapster.cs b/src/Speckle.ProxyGenerator/FileGenerators/ProxyClassesGenerator.Mapster.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.Mapster.cs rename to src/Speckle.ProxyGenerator/FileGenerators/ProxyClassesGenerator.Mapster.cs diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs b/src/Speckle.ProxyGenerator/FileGenerators/ProxyClassesGenerator.cs similarity index 97% rename from src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs rename to src/Speckle.ProxyGenerator/FileGenerators/ProxyClassesGenerator.cs index 58b0cc9..4e9a0bc 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs +++ b/src/Speckle.ProxyGenerator/FileGenerators/ProxyClassesGenerator.cs @@ -1,383 +1,383 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text; -using Microsoft.CodeAnalysis; -using ProxyInterfaceSourceGenerator.Builders; -using ProxyInterfaceSourceGenerator.Enums; -using ProxyInterfaceSourceGenerator.Extensions; -using ProxyInterfaceSourceGenerator.Models; -using ProxyInterfaceSourceGenerator.Types; -using ProxyInterfaceSourceGenerator.Utils; - -namespace ProxyInterfaceSourceGenerator.FileGenerators; - -internal partial class ProxyClassesGenerator : BaseGenerator, IFilesGenerator -{ - public ProxyClassesGenerator(Context context, bool supportsNullable) : base(context, supportsNullable) - { - } - - public IEnumerable GenerateFiles() - { - foreach (var ci in Context.Candidates) - { - if (TryGenerateFile(ci.Value, out var file)) - { - yield return file; - } - } - } - - [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1024:Compare symbols correctly", Justification = "")] - private bool TryGenerateFile(ProxyData pd, [NotNullWhen(true)] out FileData? fileData) - { - fileData = default; - - if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.FullMetadataTypeName, pd.Usings, out var targetClassSymbol)) - { - return false; - } - - var interfaceName = ResolveInterfaceNameWithOptionalTypeConstraints(targetClassSymbol.Symbol, pd.FullInterfaceName); - var className = targetClassSymbol.Symbol.ResolveProxyClassName(); - var constructorName = $"{targetClassSymbol.Symbol.Name}Proxy"; - - var extendsProxyClasses = GetExtendsProxyData(pd, targetClassSymbol); - - fileData = new FileData( - $"{targetClassSymbol.Symbol.GetFullMetadataName()}Proxy.g.cs", - CreateProxyClassCode(pd, targetClassSymbol, extendsProxyClasses, interfaceName, className, constructorName) - ); - - return true; - } - - private string CreateProxyClassCode( - ProxyData pd, - ClassSymbol targetClassSymbol, - IReadOnlyList extendsProxyClasses, - string interfaceName, - string className, - string constructorName) - { - var firstExtends = extendsProxyClasses.FirstOrDefault(); - var extends = string.Empty; - var @base = string.Empty; - var @new = string.Empty; - var instanceBaseDefinition = string.Empty; - var instanceBaseSetter = string.Empty; - - if (firstExtends is not null) - { - extends = $"global::{firstExtends.NamespaceDot}{firstExtends.ShortMetadataName}Proxy, "; - @base = " : base(instance)"; - @new = "new "; - instanceBaseDefinition = $"public {firstExtends.FullQualifiedTypeName} _Instance{firstExtends.FullQualifiedTypeName.GetLastPart()} {{ get; }}"; - instanceBaseSetter = $"_Instance{firstExtends.FullQualifiedTypeName.GetLastPart()} = instance;"; - } - - var @abstract = string.Empty; // targetClassSymbol.Symbol.IsAbstract ? "abstract " : string.Empty; - var properties = GeneratePublicProperties(targetClassSymbol, pd); - var methods = GeneratePublicMethods(targetClassSymbol, pd); - var events = GenerateEvents(targetClassSymbol, pd); - var operators = GenerateOperators(targetClassSymbol, pd); - - var configurationForMapster = string.Empty; - if (Context.ReplacedTypes.Count > 0) - { - configurationForMapster = GenerateMapperConfigurationForMapster(); - } - - var (namespaceStart, namespaceEnd) = NamespaceBuilder.Build(pd.Namespace); - - var accessibility = pd.Accessibility == ProxyClassAccessibility.Internal ? "internal" : "public"; - - return $@"//---------------------------------------------------------------------------------------- -// -// This code was generated by https://github.com/StefH/ProxyInterfaceSourceGenerator. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//---------------------------------------------------------------------------------------- - -{SupportsNullable.IIf("#nullable enable")} -using System; - -{namespaceStart} - {accessibility} {@abstract}partial class {className} : {extends}{interfaceName} - {{ - public {@new}{targetClassSymbol} _Instance {{ get; }} - {instanceBaseDefinition} -{events + -properties + -methods + -operators} - public {constructorName}({targetClassSymbol} instance){@base} - {{ - _Instance = instance; - {instanceBaseSetter} - -{configurationForMapster} - }} - }} -{namespaceEnd} -{SupportsNullable.IIf("#nullable restore")}"; - } - - private string GeneratePublicProperties(ClassSymbol targetClassSymbol, ProxyData proxyData) - { - var str = new StringBuilder(); - - foreach (var property in MemberHelper.GetPublicProperties(targetClassSymbol, proxyData)) - { - var type = GetPropertyType(property, out var isReplaced); - - var instance = !property.IsStatic ? - "_Instance" : - $"{targetClassSymbol.Symbol}"; - - var propertyName = property.GetSanitizedName(); - var instancePropertyName = $"{instance}.{propertyName}"; - if (property.IsIndexer) - { - var parameters = GetMethodParameters(property.Parameters, true); - propertyName = $"this[{string.Join(", ", parameters)}]"; - - var instanceParameters = GetMethodParameters(property.Parameters, false); - instancePropertyName = $"{instance}[{string.Join(", ", instanceParameters)}]"; - } - - var overrideOrVirtual = string.Empty; - if (property.IsOverride) - { - overrideOrVirtual = "override "; - } - else if (property.IsVirtual) - { - overrideOrVirtual = "virtual "; - } - - var getIsPublic = property.GetMethod.IsPublic(); - var setIsPublic = property.SetMethod.IsPublic(); - - if (!getIsPublic && !setIsPublic) - { - continue; - } - - string get; - string set; - if (isReplaced) - { - get = getIsPublic ? $"get => Mapster.TypeAdapter.Adapt<{type}>({instancePropertyName}); " : string.Empty; - set = setIsPublic ? $"set => {instancePropertyName} = Mapster.TypeAdapter.Adapt<{property.Type}>(value); " : string.Empty; - } - else - { - get = getIsPublic ? $"get => {instancePropertyName}; " : string.Empty; - set = setIsPublic ? $"set => {instancePropertyName} = value; " : string.Empty; - } - - foreach (var attribute in property.GetAttributesAsList()) - { - str.AppendLine($" {attribute}"); - } - - str.AppendLine($" public {overrideOrVirtual}{type} {propertyName} {{ {get}{set}}}"); - str.AppendLine(); - } - - return str.ToString(); - } - - private string GeneratePublicMethods(ClassSymbol targetClassSymbol, ProxyData proxyData) - { - var str = new StringBuilder(); - - var methods = MemberHelper.GetPublicMethods(targetClassSymbol, proxyData); - - foreach (var method in methods) - { - var methodParameters = new List(); - var invokeParameters = new List(); - - foreach (var parameterSymbol in method.Parameters) - { - var type = GetParameterType(parameterSymbol, out _); - - methodParameters.Add(MethodParameterBuilder.Build(parameterSymbol, type)); - - // Do not add the '_' for a 'ref' parameter. - invokeParameters.Add($"{parameterSymbol.GetRefKindPrefix()}{parameterSymbol.GetSanitizedName()}{(!parameterSymbol.IsRef()).IIf("_")}"); - } - - string overrideOrVirtual = string.Empty; - if (method.IsOverride && method.OverriddenMethod != null) - { - var baseType = method.OverriddenMethod.ContainingType.GetFullType(); - if (TryGetNamedTypeSymbolByFullName(TypeKind.Class, baseType, Enumerable.Empty(), out _)) - { - overrideOrVirtual = "override "; - } - } - else if (method.IsVirtual) - { - overrideOrVirtual = "virtual "; - } - - string returnTypeAsString = GetReplacedTypeAsString(method.ReturnType, out var returnIsReplaced); - - var whereStatement = GetWhereStatementFromMethod(method); - - foreach (var attribute in method.GetAttributesAsList()) - { - str.AppendLine($" {attribute}"); - } - - str.AppendLine($" public {overrideOrVirtual}{returnTypeAsString} {method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", methodParameters)}){whereStatement}"); - str.AppendLine(@" {"); - - foreach (var ps in method.Parameters.Where(p => !p.IsRef())) - { - var type = FixType(ps.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), ps.Type.NullableAnnotation); - string normalOrMap = $" = {ps.GetSanitizedName()}"; - if (ps.RefKind == RefKind.Out) - { - normalOrMap = string.Empty; - } - else - { - _ = GetParameterType(ps, out var isReplaced); // TODO : response is not used? - if (isReplaced) - { - normalOrMap = $" = Mapster.TypeAdapter.Adapt<{type}>({ps.GetSanitizedName()})"; - } - } - - str.AppendLine($" {type} {ps.GetSanitizedName()}_{normalOrMap};"); - } - - var methodName = method.GetMethodNameWithOptionalTypeParameters(); - var alternateReturnVariableName = $"result_{methodName.GetDeterministicHashCodeAsString()}"; - - string instance = method.IsStatic ? targetClassSymbol.Symbol.ToFullyQualifiedDisplayString() : "_Instance"; - - if (returnTypeAsString == "void") - { - str.AppendLine($" {instance}.{methodName}({string.Join(", ", invokeParameters)});"); - } - else - { - str.AppendLine($" var {alternateReturnVariableName} = {instance}.{methodName}({string.Join(", ", invokeParameters)});"); - } - - foreach (var ps in method.Parameters.Where(p => p.RefKind == RefKind.Out)) - { - string normalOrMap = $" = {ps.GetSanitizedName()}_"; - if (ps.GetTypeEnum() == TypeEnum.Complex) - { - var type = GetParameterType(ps, out var isReplaced); - if (isReplaced) - { - normalOrMap = $" = Mapster.TypeAdapter.Adapt<{type}>({ps.GetSanitizedName()}_)"; - } - } - - str.AppendLine($" {ps.GetSanitizedName()}{normalOrMap};"); - } - - if (returnTypeAsString != "void") - { - if (returnIsReplaced) - { - str.AppendLine($" return Mapster.TypeAdapter.Adapt<{returnTypeAsString}>({alternateReturnVariableName});"); - } - else - { - str.AppendLine($" return {alternateReturnVariableName};"); - } - } - - str.AppendLine(" }"); - str.AppendLine(); - } - - return str.ToString(); - } - - private string GenerateEvents(ClassSymbol targetClassSymbol, ProxyData proxyData) - { - var str = new StringBuilder(); - foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol, proxyData)) - { - var name = @event.Key.GetSanitizedName(); - var ps = @event.First().Parameters.First(); - var type = ps.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(ps, out _) : ps.Type.ToString(); - - foreach (var attribute in ps.GetAttributesAsList()) - { - str.AppendLine($" {attribute}"); - } - - str.Append($" public event {type} {name} {{"); - - if (@event.Any(e => e.MethodKind == MethodKind.EventAdd)) - { - str.Append($" add {{ _Instance.{name} += value; }}"); - } - if (@event.Any(e => e.MethodKind == MethodKind.EventRemove)) - { - str.Append($" remove {{ _Instance.{name} -= value; }}"); - } - - str.AppendLine(" }"); - str.AppendLine(); - } - - return str.ToString(); - } - - private string GenerateOperators(ClassSymbol targetClassSymbol, ProxyData proxyData) - { - var str = new StringBuilder(); - foreach (var @operator in MemberHelper.GetPublicStaticOperators(targetClassSymbol, proxyData)) - { - foreach (var attribute in @operator.GetAttributesAsList()) - { - str.AppendLine($" {attribute}"); - } - - if (!@operator.Parameters.Any()) - { - continue; - } - - var parameter = @operator.Parameters.First(); - var proxyClassName = targetClassSymbol.Symbol.ResolveProxyClassName(); - - var operatorType = @operator.Name.ToLowerInvariant().Replace("op_", string.Empty); - if (operatorType == "explicit") - { - var returnTypeAsString = GetReplacedTypeAsString(@operator.ReturnType, out _); - - str.AppendLine($" public static explicit operator {returnTypeAsString}({proxyClassName} {parameter.Name})"); - str.AppendLine(@" {"); - str.AppendLine($" return ({returnTypeAsString}) {parameter.Name}._Instance;"); - str.AppendLine(@" }"); - } - else - { - var returnTypeAsString = GetReplacedTypeAsString(parameter.Type, out _); - - str.AppendLine($" public static implicit operator {proxyClassName}({returnTypeAsString} {parameter.Name})"); - str.AppendLine(@" {"); - str.AppendLine($" return new {proxyClassName}(({targetClassSymbol.Symbol.Name}) {parameter.Name});"); - str.AppendLine(@" }"); - } - - str.AppendLine(); - } - - return str.ToString(); - } +using System.Diagnostics.CodeAnalysis; +using System.Text; +using Microsoft.CodeAnalysis; +using ProxyInterfaceSourceGenerator.Builders; +using ProxyInterfaceSourceGenerator.Enums; +using ProxyInterfaceSourceGenerator.Extensions; +using ProxyInterfaceSourceGenerator.Models; +using ProxyInterfaceSourceGenerator.Types; +using ProxyInterfaceSourceGenerator.Utils; + +namespace ProxyInterfaceSourceGenerator.FileGenerators; + +internal partial class ProxyClassesGenerator : BaseGenerator, IFilesGenerator +{ + public ProxyClassesGenerator(Context context, bool supportsNullable) : base(context, supportsNullable) + { + } + + public IEnumerable GenerateFiles() + { + foreach (var ci in Context.Candidates) + { + if (TryGenerateFile(ci.Value, out var file)) + { + yield return file; + } + } + } + + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1024:Compare symbols correctly", Justification = "")] + private bool TryGenerateFile(ProxyData pd, [NotNullWhen(true)] out FileData? fileData) + { + fileData = default; + + if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.FullMetadataTypeName, pd.Usings, out var targetClassSymbol)) + { + return false; + } + + var interfaceName = ResolveInterfaceNameWithOptionalTypeConstraints(targetClassSymbol.Symbol, pd.FullInterfaceName); + var className = targetClassSymbol.Symbol.ResolveProxyClassName(); + var constructorName = $"{targetClassSymbol.Symbol.Name}Proxy"; + + var extendsProxyClasses = GetExtendsProxyData(pd, targetClassSymbol); + + fileData = new FileData( + $"{targetClassSymbol.Symbol.GetFullMetadataName()}Proxy.g.cs", + CreateProxyClassCode(pd, targetClassSymbol, extendsProxyClasses, interfaceName, className, constructorName) + ); + + return true; + } + + private string CreateProxyClassCode( + ProxyData pd, + ClassSymbol targetClassSymbol, + IReadOnlyList extendsProxyClasses, + string interfaceName, + string className, + string constructorName) + { + var firstExtends = extendsProxyClasses.FirstOrDefault(); + var extends = string.Empty; + var @base = string.Empty; + var @new = string.Empty; + var instanceBaseDefinition = string.Empty; + var instanceBaseSetter = string.Empty; + + if (firstExtends is not null) + { + extends = $"global::{firstExtends.NamespaceDot}{firstExtends.ShortMetadataName}Proxy, "; + @base = " : base(instance)"; + @new = "new "; + instanceBaseDefinition = $"public {firstExtends.FullQualifiedTypeName} _Instance{firstExtends.FullQualifiedTypeName.GetLastPart()} {{ get; }}"; + instanceBaseSetter = $"_Instance{firstExtends.FullQualifiedTypeName.GetLastPart()} = instance;"; + } + + var @abstract = string.Empty; // targetClassSymbol.Symbol.IsAbstract ? "abstract " : string.Empty; + var properties = GeneratePublicProperties(targetClassSymbol, pd); + var methods = GeneratePublicMethods(targetClassSymbol, pd); + var events = GenerateEvents(targetClassSymbol, pd); + var operators = GenerateOperators(targetClassSymbol, pd); + + var configurationForMapster = string.Empty; + if (Context.ReplacedTypes.Count > 0) + { + configurationForMapster = GenerateMapperConfigurationForMapster(); + } + + var (namespaceStart, namespaceEnd) = NamespaceBuilder.Build(pd.Namespace); + + var accessibility = pd.Accessibility == ProxyClassAccessibility.Internal ? "internal" : "public"; + + return $@"//---------------------------------------------------------------------------------------- +// +// This code was generated by https://github.com/StefH/ProxyInterfaceSourceGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//---------------------------------------------------------------------------------------- + +{SupportsNullable.IIf("#nullable enable")} +using System; + +{namespaceStart} + {accessibility} {@abstract}partial class {className} : {extends}{interfaceName} + {{ + public {@new}{targetClassSymbol} _Instance {{ get; }} + {instanceBaseDefinition} +{events + +properties + +methods + +operators} + public {constructorName}({targetClassSymbol} instance){@base} + {{ + _Instance = instance; + {instanceBaseSetter} + +{configurationForMapster} + }} + }} +{namespaceEnd} +{SupportsNullable.IIf("#nullable restore")}"; + } + + private string GeneratePublicProperties(ClassSymbol targetClassSymbol, ProxyData proxyData) + { + var str = new StringBuilder(); + + foreach (var property in MemberHelper.GetPublicProperties(targetClassSymbol, proxyData)) + { + var type = GetPropertyType(property, out var isReplaced); + + var instance = !property.IsStatic ? + "_Instance" : + $"{targetClassSymbol.Symbol}"; + + var propertyName = property.GetSanitizedName(); + var instancePropertyName = $"{instance}.{propertyName}"; + if (property.IsIndexer) + { + var parameters = GetMethodParameters(property.Parameters, true); + propertyName = $"this[{string.Join(", ", parameters)}]"; + + var instanceParameters = GetMethodParameters(property.Parameters, false); + instancePropertyName = $"{instance}[{string.Join(", ", instanceParameters)}]"; + } + + var overrideOrVirtual = string.Empty; + if (property.IsOverride) + { + overrideOrVirtual = "override "; + } + else if (property.IsVirtual) + { + overrideOrVirtual = "virtual "; + } + + var getIsPublic = property.GetMethod.IsPublic(); + var setIsPublic = property.SetMethod.IsPublic(); + + if (!getIsPublic && !setIsPublic) + { + continue; + } + + string get; + string set; + if (isReplaced) + { + get = getIsPublic ? $"get => Mapster.TypeAdapter.Adapt<{type}>({instancePropertyName}); " : string.Empty; + set = setIsPublic ? $"set => {instancePropertyName} = Mapster.TypeAdapter.Adapt<{property.Type}>(value); " : string.Empty; + } + else + { + get = getIsPublic ? $"get => {instancePropertyName}; " : string.Empty; + set = setIsPublic ? $"set => {instancePropertyName} = value; " : string.Empty; + } + + foreach (var attribute in property.GetAttributesAsList()) + { + str.AppendLine($" {attribute}"); + } + + str.AppendLine($" public {overrideOrVirtual}{type} {propertyName} {{ {get}{set}}}"); + str.AppendLine(); + } + + return str.ToString(); + } + + private string GeneratePublicMethods(ClassSymbol targetClassSymbol, ProxyData proxyData) + { + var str = new StringBuilder(); + + var methods = MemberHelper.GetPublicMethods(targetClassSymbol, proxyData); + + foreach (var method in methods) + { + var methodParameters = new List(); + var invokeParameters = new List(); + + foreach (var parameterSymbol in method.Parameters) + { + var type = GetParameterType(parameterSymbol, out _); + + methodParameters.Add(MethodParameterBuilder.Build(parameterSymbol, type)); + + // Do not add the '_' for a 'ref' parameter. + invokeParameters.Add($"{parameterSymbol.GetRefKindPrefix()}{parameterSymbol.GetSanitizedName()}{(!parameterSymbol.IsRef()).IIf("_")}"); + } + + string overrideOrVirtual = string.Empty; + if (method.IsOverride && method.OverriddenMethod != null) + { + var baseType = method.OverriddenMethod.ContainingType.GetFullType(); + if (TryGetNamedTypeSymbolByFullName(TypeKind.Class, baseType, Enumerable.Empty(), out _)) + { + overrideOrVirtual = "override "; + } + } + else if (method.IsVirtual) + { + overrideOrVirtual = "virtual "; + } + + string returnTypeAsString = GetReplacedTypeAsString(method.ReturnType, out var returnIsReplaced); + + var whereStatement = GetWhereStatementFromMethod(method); + + foreach (var attribute in method.GetAttributesAsList()) + { + str.AppendLine($" {attribute}"); + } + + str.AppendLine($" public {overrideOrVirtual}{returnTypeAsString} {method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", methodParameters)}){whereStatement}"); + str.AppendLine(@" {"); + + foreach (var ps in method.Parameters.Where(p => !p.IsRef())) + { + var type = FixType(ps.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), ps.Type.NullableAnnotation); + string normalOrMap = $" = {ps.GetSanitizedName()}"; + if (ps.RefKind == RefKind.Out) + { + normalOrMap = string.Empty; + } + else + { + _ = GetParameterType(ps, out var isReplaced); // TODO : response is not used? + if (isReplaced) + { + normalOrMap = $" = Mapster.TypeAdapter.Adapt<{type}>({ps.GetSanitizedName()})"; + } + } + + str.AppendLine($" {type} {ps.GetSanitizedName()}_{normalOrMap};"); + } + + var methodName = method.GetMethodNameWithOptionalTypeParameters(); + var alternateReturnVariableName = $"result_{methodName.GetDeterministicHashCodeAsString()}"; + + string instance = method.IsStatic ? targetClassSymbol.Symbol.ToFullyQualifiedDisplayString() : "_Instance"; + + if (returnTypeAsString == "void") + { + str.AppendLine($" {instance}.{methodName}({string.Join(", ", invokeParameters)});"); + } + else + { + str.AppendLine($" var {alternateReturnVariableName} = {instance}.{methodName}({string.Join(", ", invokeParameters)});"); + } + + foreach (var ps in method.Parameters.Where(p => p.RefKind == RefKind.Out)) + { + string normalOrMap = $" = {ps.GetSanitizedName()}_"; + if (ps.GetTypeEnum() == TypeEnum.Complex) + { + var type = GetParameterType(ps, out var isReplaced); + if (isReplaced) + { + normalOrMap = $" = Mapster.TypeAdapter.Adapt<{type}>({ps.GetSanitizedName()}_)"; + } + } + + str.AppendLine($" {ps.GetSanitizedName()}{normalOrMap};"); + } + + if (returnTypeAsString != "void") + { + if (returnIsReplaced) + { + str.AppendLine($" return Mapster.TypeAdapter.Adapt<{returnTypeAsString}>({alternateReturnVariableName});"); + } + else + { + str.AppendLine($" return {alternateReturnVariableName};"); + } + } + + str.AppendLine(" }"); + str.AppendLine(); + } + + return str.ToString(); + } + + private string GenerateEvents(ClassSymbol targetClassSymbol, ProxyData proxyData) + { + var str = new StringBuilder(); + foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol, proxyData)) + { + var name = @event.Key.GetSanitizedName(); + var ps = @event.First().Parameters.First(); + var type = ps.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(ps, out _) : ps.Type.ToString(); + + foreach (var attribute in ps.GetAttributesAsList()) + { + str.AppendLine($" {attribute}"); + } + + str.Append($" public event {type} {name} {{"); + + if (@event.Any(e => e.MethodKind == MethodKind.EventAdd)) + { + str.Append($" add {{ _Instance.{name} += value; }}"); + } + if (@event.Any(e => e.MethodKind == MethodKind.EventRemove)) + { + str.Append($" remove {{ _Instance.{name} -= value; }}"); + } + + str.AppendLine(" }"); + str.AppendLine(); + } + + return str.ToString(); + } + + private string GenerateOperators(ClassSymbol targetClassSymbol, ProxyData proxyData) + { + var str = new StringBuilder(); + foreach (var @operator in MemberHelper.GetPublicStaticOperators(targetClassSymbol, proxyData)) + { + foreach (var attribute in @operator.GetAttributesAsList()) + { + str.AppendLine($" {attribute}"); + } + + if (!@operator.Parameters.Any()) + { + continue; + } + + var parameter = @operator.Parameters.First(); + var proxyClassName = targetClassSymbol.Symbol.ResolveProxyClassName(); + + var operatorType = @operator.Name.ToLowerInvariant().Replace("op_", string.Empty); + if (operatorType == "explicit") + { + var returnTypeAsString = GetReplacedTypeAsString(@operator.ReturnType, out _); + + str.AppendLine($" public static explicit operator {returnTypeAsString}({proxyClassName} {parameter.Name})"); + str.AppendLine(@" {"); + str.AppendLine($" return ({returnTypeAsString}) {parameter.Name}._Instance;"); + str.AppendLine(@" }"); + } + else + { + var returnTypeAsString = GetReplacedTypeAsString(parameter.Type, out _); + + str.AppendLine($" public static implicit operator {proxyClassName}({returnTypeAsString} {parameter.Name})"); + str.AppendLine(@" {"); + str.AppendLine($" return new {proxyClassName}(({targetClassSymbol.Symbol.Name}) {parameter.Name});"); + str.AppendLine(@" }"); + } + + str.AppendLine(); + } + + return str.ToString(); + } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Models/ClassSymbol.cs b/src/Speckle.ProxyGenerator/Models/ClassSymbol.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Models/ClassSymbol.cs rename to src/Speckle.ProxyGenerator/Models/ClassSymbol.cs diff --git a/src/ProxyInterfaceSourceGenerator/Models/ConstraintInfo.cs b/src/Speckle.ProxyGenerator/Models/ConstraintInfo.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Models/ConstraintInfo.cs rename to src/Speckle.ProxyGenerator/Models/ConstraintInfo.cs diff --git a/src/ProxyInterfaceSourceGenerator/Models/Context.cs b/src/Speckle.ProxyGenerator/Models/Context.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Models/Context.cs rename to src/Speckle.ProxyGenerator/Models/Context.cs diff --git a/src/ProxyInterfaceSourceGenerator/Models/FileData.cs b/src/Speckle.ProxyGenerator/Models/FileData.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Models/FileData.cs rename to src/Speckle.ProxyGenerator/Models/FileData.cs diff --git a/src/ProxyInterfaceSourceGenerator/Models/ProxyData.cs b/src/Speckle.ProxyGenerator/Models/ProxyData.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Models/ProxyData.cs rename to src/Speckle.ProxyGenerator/Models/ProxyData.cs diff --git a/src/ProxyInterfaceSourceGenerator/Properties/launchSettings.json b/src/Speckle.ProxyGenerator/Properties/launchSettings.json similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Properties/launchSettings.json rename to src/Speckle.ProxyGenerator/Properties/launchSettings.json diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs b/src/Speckle.ProxyGenerator/ProxyInterfaceCodeGenerator.cs similarity index 97% rename from src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs rename to src/Speckle.ProxyGenerator/ProxyInterfaceCodeGenerator.cs index 40febd6..e63dee5 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs +++ b/src/Speckle.ProxyGenerator/ProxyInterfaceCodeGenerator.cs @@ -1,106 +1,106 @@ -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Text; -using ProxyInterfaceSourceGenerator.FileGenerators; -using ProxyInterfaceSourceGenerator.Models; -using ProxyInterfaceSourceGenerator.SyntaxReceiver; - -namespace ProxyInterfaceSourceGenerator; - -[Generator] -#if DEBUG -public -#else -internal -#endif -class ProxyInterfaceCodeGenerator : ISourceGenerator -{ - private readonly ExtraFilesGenerator _proxyAttributeGenerator = new(); - - public void Initialize(GeneratorInitializationContext context) - { -#if DEBUGATTACH - if (!System.Diagnostics.Debugger.IsAttached) - { - System.Diagnostics.Debugger.Launch(); - } -#endif - context.RegisterForSyntaxNotifications(() => new ProxySyntaxReceiver()); - } - - public void Execute(GeneratorExecutionContext context) - { - try - { - if (context.ParseOptions is not CSharpParseOptions csharpParseOptions) - { - throw new NotSupportedException("Only C# is supported."); - } - - if (context.SyntaxContextReceiver is not ProxySyntaxReceiver receiver) - { - throw new NotSupportedException($"Only {nameof(ProxySyntaxReceiver)} is supported."); - } - - // https://github.com/reactiveui/refit/blob/main/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs - var supportsNullable = csharpParseOptions.LanguageVersion >= LanguageVersion.CSharp8; - - GenerateProxyAttribute(context, receiver); - GeneratePartialInterfaces(context, receiver, supportsNullable); - GenerateProxyClasses(context, receiver, supportsNullable); - } - catch (Exception exception) - { - GenerateError(context, exception); - } - } - - private void GenerateError(GeneratorExecutionContext context, Exception exception) - { - var message = $"/*\r\n{nameof(ProxyInterfaceCodeGenerator)}\r\n\r\n[Exception]\r\n{exception}\r\n\r\n[StackTrace]\r\n{exception.StackTrace}*/"; - context.AddSource("Error.g", SourceText.From(message, Encoding.UTF8)); - } - - private void GenerateProxyAttribute(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver) - { - var context = new Context - { - GeneratorExecutionContext = ctx, - Candidates = receiver.CandidateInterfaces - }; - - var attributeData = _proxyAttributeGenerator.GenerateFile(); - context.GeneratorExecutionContext.AddSource(attributeData.FileName, SourceText.From(attributeData.Text, Encoding.UTF8)); - } - - private static void GeneratePartialInterfaces(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver, bool supportsNullable) - { - var context = new Context - { - GeneratorExecutionContext = ctx, - Candidates = receiver.CandidateInterfaces - }; - - var partialInterfacesGenerator = new PartialInterfacesGenerator(context, supportsNullable); - foreach (var (fileName, text) in partialInterfacesGenerator.GenerateFiles()) - { - context.GeneratorExecutionContext.AddSource(fileName, SourceText.From(text, Encoding.UTF8)); - } - } - - private static void GenerateProxyClasses(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver, bool supportsNullable) - { - var context = new Context - { - GeneratorExecutionContext = ctx, - Candidates = receiver.CandidateInterfaces - }; - - var proxyClassesGenerator = new ProxyClassesGenerator(context, supportsNullable); - foreach (var (fileName, text) in proxyClassesGenerator.GenerateFiles()) - { - context.GeneratorExecutionContext.AddSource(fileName, SourceText.From(text, Encoding.UTF8)); - } - } +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; +using ProxyInterfaceSourceGenerator.FileGenerators; +using ProxyInterfaceSourceGenerator.Models; +using ProxyInterfaceSourceGenerator.SyntaxReceiver; + +namespace ProxyInterfaceSourceGenerator; + +[Generator] +#if DEBUG +public +#else +internal +#endif +class ProxyInterfaceCodeGenerator : ISourceGenerator +{ + private readonly ExtraFilesGenerator _proxyAttributeGenerator = new(); + + public void Initialize(GeneratorInitializationContext context) + { +#if DEBUGATTACH + if (!System.Diagnostics.Debugger.IsAttached) + { + System.Diagnostics.Debugger.Launch(); + } +#endif + context.RegisterForSyntaxNotifications(() => new ProxySyntaxReceiver()); + } + + public void Execute(GeneratorExecutionContext context) + { + try + { + if (context.ParseOptions is not CSharpParseOptions csharpParseOptions) + { + throw new NotSupportedException("Only C# is supported."); + } + + if (context.SyntaxContextReceiver is not ProxySyntaxReceiver receiver) + { + throw new NotSupportedException($"Only {nameof(ProxySyntaxReceiver)} is supported."); + } + + // https://github.com/reactiveui/refit/blob/main/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs + var supportsNullable = csharpParseOptions.LanguageVersion >= LanguageVersion.CSharp8; + + GenerateProxyAttribute(context, receiver); + GeneratePartialInterfaces(context, receiver, supportsNullable); + GenerateProxyClasses(context, receiver, supportsNullable); + } + catch (Exception exception) + { + GenerateError(context, exception); + } + } + + private void GenerateError(GeneratorExecutionContext context, Exception exception) + { + var message = $"/*\r\n{nameof(ProxyInterfaceCodeGenerator)}\r\n\r\n[Exception]\r\n{exception}\r\n\r\n[StackTrace]\r\n{exception.StackTrace}*/"; + context.AddSource("Error.g", SourceText.From(message, Encoding.UTF8)); + } + + private void GenerateProxyAttribute(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver) + { + var context = new Context + { + GeneratorExecutionContext = ctx, + Candidates = receiver.CandidateInterfaces + }; + + var attributeData = _proxyAttributeGenerator.GenerateFile(); + context.GeneratorExecutionContext.AddSource(attributeData.FileName, SourceText.From(attributeData.Text, Encoding.UTF8)); + } + + private static void GeneratePartialInterfaces(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver, bool supportsNullable) + { + var context = new Context + { + GeneratorExecutionContext = ctx, + Candidates = receiver.CandidateInterfaces + }; + + var partialInterfacesGenerator = new PartialInterfacesGenerator(context, supportsNullable); + foreach (var (fileName, text) in partialInterfacesGenerator.GenerateFiles()) + { + context.GeneratorExecutionContext.AddSource(fileName, SourceText.From(text, Encoding.UTF8)); + } + } + + private static void GenerateProxyClasses(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver, bool supportsNullable) + { + var context = new Context + { + GeneratorExecutionContext = ctx, + Candidates = receiver.CandidateInterfaces + }; + + var proxyClassesGenerator = new ProxyClassesGenerator(context, supportsNullable); + foreach (var (fileName, text) in proxyClassesGenerator.GenerateFiles()) + { + context.GeneratorExecutionContext.AddSource(fileName, SourceText.From(text, Encoding.UTF8)); + } + } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj b/src/Speckle.ProxyGenerator/Speckle.ProxyGenerator.csproj similarity index 90% rename from src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj rename to src/Speckle.ProxyGenerator/Speckle.ProxyGenerator.csproj index 6cb3c51..1c374e5 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj +++ b/src/Speckle.ProxyGenerator/Speckle.ProxyGenerator.csproj @@ -4,19 +4,20 @@ 0.1.0 netstandard2.0 {12344228-91F4-4502-9595-39584E5ABB34} - 10 + Latest enable - Stef Heyenrath + Speckle + Speckle.ProxyGenerator ProxyInterfaceGenerator - ProxyInterfaceGenerator + Speckle.ProxyGenerator This project uses Source Generation to generate an interface and a Proxy class for classes. This makes it possible to wrap external classes which do not have an interface, in a Proxy class which makes it easier to Mock and use DI. class;interface;proxy;SourceGenerator;Analyzer;Generation;Generate;wrap MIT $([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/../../PackageReleaseNotes.txt")) - https://github.com/StefH/ProxyInterfaceSourceGenerator + https://github.com/specklesystems/ProxyGenerator git - https://github.com/StefH/ProxyInterfaceSourceGenerator + https://github.com/specklesystems/ProxyGenerator PackageReadme.md icon.png false @@ -43,7 +44,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -79,4 +80,4 @@ - \ No newline at end of file + diff --git a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/AttributeArgumentListParser.cs b/src/Speckle.ProxyGenerator/SyntaxReceiver/AttributeArgumentListParser.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/SyntaxReceiver/AttributeArgumentListParser.cs rename to src/Speckle.ProxyGenerator/SyntaxReceiver/AttributeArgumentListParser.cs diff --git a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs b/src/Speckle.ProxyGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs similarity index 97% rename from src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs rename to src/Speckle.ProxyGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs index 121b340..1294b5e 100644 --- a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs +++ b/src/Speckle.ProxyGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs @@ -1,86 +1,86 @@ -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using ProxyInterfaceSourceGenerator.Extensions; -using ProxyInterfaceSourceGenerator.Models; - -namespace ProxyInterfaceSourceGenerator.SyntaxReceiver; - -internal class ProxySyntaxReceiver : ISyntaxContextReceiver -{ - private const string GlobalPrefix = "global::"; - private static readonly string[] GenerateProxyAttributes = { "ProxyInterfaceGenerator.Proxy", "Proxy" }; - private static readonly string[] Modifiers = { "public", "partial" }; - public IDictionary CandidateInterfaces { get; } = new Dictionary(); - - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) - { - var syntaxNode = context.Node; - var semanticModel = context.SemanticModel; - - if (syntaxNode is InterfaceDeclarationSyntax interfaceDeclarationSyntax && TryGet(interfaceDeclarationSyntax, out var data, semanticModel!)) - { - CandidateInterfaces.Add(interfaceDeclarationSyntax, data); - } - } - - private static string CreateFullInterfaceName(string ns, BaseTypeDeclarationSyntax classDeclarationSyntax) - { - return !string.IsNullOrEmpty(ns) ? $"{ns}.{classDeclarationSyntax.Identifier}" : classDeclarationSyntax.Identifier.ToString(); - } - private static bool TryGet(InterfaceDeclarationSyntax interfaceDeclarationSyntax, [NotNullWhen(true)] out ProxyData? data, SemanticModel semanticModel) - { - data = null; - - if (interfaceDeclarationSyntax.Modifiers.Select(m => m.ToString()).Except(Modifiers).Any()) - { - // InterfaceDeclarationSyntax should be "public" and "partial" - return false; - } - - var attributeList = interfaceDeclarationSyntax.AttributeLists.FirstOrDefault(x => x.Attributes.Any(a => GenerateProxyAttributes.Contains(a.Name.ToString()))); - if (attributeList is null) - { - // InterfaceDeclarationSyntax should have the correct attribute - return false; - } - - var usings = new List(); - - string ns = interfaceDeclarationSyntax.GetNamespace(); - if (!string.IsNullOrEmpty(ns)) - { - usings.Add(ns); - } - - if (interfaceDeclarationSyntax.TryGetParentSyntax(out CompilationUnitSyntax? cc)) - { - foreach (var @using in cc.Usings) - { - usings.Add(@using.Name.ToString()); - } - } - - var fluentBuilderAttributeArguments = AttributeArgumentListParser.ParseAttributeArguments(attributeList.Attributes.FirstOrDefault()?.ArgumentList, semanticModel); - - var metadataName = fluentBuilderAttributeArguments.MetadataName; - var globalNamespace = string.IsNullOrEmpty(ns) ? string.Empty : $"{GlobalPrefix}{ns}"; - var namespaceDot = string.IsNullOrEmpty(ns) ? string.Empty : $"{ns}."; - - data = new ProxyData( - @namespace: ns, - namespaceDot: namespaceDot, - shortInterfaceName: interfaceDeclarationSyntax.Identifier.ToString(), - fullInterfaceName: CreateFullInterfaceName(globalNamespace, interfaceDeclarationSyntax), // $"{ns}.{interfaceDeclarationSyntax.Identifier}", - fullQualifiedTypeName: fluentBuilderAttributeArguments.FullyQualifiedDisplayString, - fullMetadataTypeName: metadataName, - shortMetadataTypeName: metadataName.Split('.').Last(), - usings: usings, - proxyBaseClasses: fluentBuilderAttributeArguments.ProxyBaseClasses, - accessibility: fluentBuilderAttributeArguments.Accessibility, - membersToIgnore: fluentBuilderAttributeArguments.MembersToIgnore - ); - - return true; - } +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using ProxyInterfaceSourceGenerator.Extensions; +using ProxyInterfaceSourceGenerator.Models; + +namespace ProxyInterfaceSourceGenerator.SyntaxReceiver; + +internal class ProxySyntaxReceiver : ISyntaxContextReceiver +{ + private const string GlobalPrefix = "global::"; + private static readonly string[] GenerateProxyAttributes = { "ProxyInterfaceGenerator.Proxy", "Proxy" }; + private static readonly string[] Modifiers = { "public", "partial" }; + public IDictionary CandidateInterfaces { get; } = new Dictionary(); + + public void OnVisitSyntaxNode(GeneratorSyntaxContext context) + { + var syntaxNode = context.Node; + var semanticModel = context.SemanticModel; + + if (syntaxNode is InterfaceDeclarationSyntax interfaceDeclarationSyntax && TryGet(interfaceDeclarationSyntax, out var data, semanticModel!)) + { + CandidateInterfaces.Add(interfaceDeclarationSyntax, data); + } + } + + private static string CreateFullInterfaceName(string ns, BaseTypeDeclarationSyntax classDeclarationSyntax) + { + return !string.IsNullOrEmpty(ns) ? $"{ns}.{classDeclarationSyntax.Identifier}" : classDeclarationSyntax.Identifier.ToString(); + } + private static bool TryGet(InterfaceDeclarationSyntax interfaceDeclarationSyntax, [NotNullWhen(true)] out ProxyData? data, SemanticModel semanticModel) + { + data = null; + + if (interfaceDeclarationSyntax.Modifiers.Select(m => m.ToString()).Except(Modifiers).Any()) + { + // InterfaceDeclarationSyntax should be "public" and "partial" + return false; + } + + var attributeList = interfaceDeclarationSyntax.AttributeLists.FirstOrDefault(x => x.Attributes.Any(a => GenerateProxyAttributes.Contains(a.Name.ToString()))); + if (attributeList is null) + { + // InterfaceDeclarationSyntax should have the correct attribute + return false; + } + + var usings = new List(); + + string ns = interfaceDeclarationSyntax.GetNamespace(); + if (!string.IsNullOrEmpty(ns)) + { + usings.Add(ns); + } + + if (interfaceDeclarationSyntax.TryGetParentSyntax(out CompilationUnitSyntax? cc)) + { + foreach (var @using in cc.Usings) + { + usings.Add(@using.Name.ToString()); + } + } + + var fluentBuilderAttributeArguments = AttributeArgumentListParser.ParseAttributeArguments(attributeList.Attributes.FirstOrDefault()?.ArgumentList, semanticModel); + + var metadataName = fluentBuilderAttributeArguments.MetadataName; + var globalNamespace = string.IsNullOrEmpty(ns) ? string.Empty : $"{GlobalPrefix}{ns}"; + var namespaceDot = string.IsNullOrEmpty(ns) ? string.Empty : $"{ns}."; + + data = new ProxyData( + @namespace: ns, + namespaceDot: namespaceDot, + shortInterfaceName: interfaceDeclarationSyntax.Identifier.ToString(), + fullInterfaceName: CreateFullInterfaceName(globalNamespace, interfaceDeclarationSyntax), // $"{ns}.{interfaceDeclarationSyntax.Identifier}", + fullQualifiedTypeName: fluentBuilderAttributeArguments.FullyQualifiedDisplayString, + fullMetadataTypeName: metadataName, + shortMetadataTypeName: metadataName.Split('.').Last(), + usings: usings, + proxyBaseClasses: fluentBuilderAttributeArguments.ProxyBaseClasses, + accessibility: fluentBuilderAttributeArguments.Accessibility, + membersToIgnore: fluentBuilderAttributeArguments.MembersToIgnore + ); + + return true; + } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Types/FluentBuilderAttributeArguments.cs b/src/Speckle.ProxyGenerator/Types/FluentBuilderAttributeArguments.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Types/FluentBuilderAttributeArguments.cs rename to src/Speckle.ProxyGenerator/Types/FluentBuilderAttributeArguments.cs diff --git a/src/ProxyInterfaceSourceGenerator/Types/ProxyInterfaceGeneratorAccessibility.cs b/src/Speckle.ProxyGenerator/Types/ProxyInterfaceGeneratorAccessibility.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Types/ProxyInterfaceGeneratorAccessibility.cs rename to src/Speckle.ProxyGenerator/Types/ProxyInterfaceGeneratorAccessibility.cs diff --git a/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs b/src/Speckle.ProxyGenerator/Utils/MemberHelper.cs similarity index 97% rename from src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs rename to src/Speckle.ProxyGenerator/Utils/MemberHelper.cs index 00cce5c..2eb4a60 100644 --- a/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs +++ b/src/Speckle.ProxyGenerator/Utils/MemberHelper.cs @@ -1,125 +1,125 @@ -using Microsoft.CodeAnalysis; -using ProxyInterfaceSourceGenerator.Models; - -namespace ProxyInterfaceSourceGenerator.Utils; - -internal static class MemberHelper -{ - private static readonly string[] ExcludedMethods = { "ToString", "GetHashCode" }; - - public static IReadOnlyList GetPublicProperties( - ClassSymbol classSymbol, - ProxyData proxyData, - params Func[] filters) - { - var allFilters = new List>(filters) - { - p => p.Kind == SymbolKind.Property - }; - - return GetPublicMembers(classSymbol, proxyData, allFilters.ToArray()).ToArray(); - } - - public static IReadOnlyList GetPublicMethods( - ClassSymbol classSymbol, - ProxyData proxyData, - Func? filter = null) - { - filter ??= _ => true; - - return - GetPublicMembers( - classSymbol, - proxyData, - m => m.Kind == SymbolKind.Method, - m => m.MethodKind == MethodKind.Ordinary, - m => !ExcludedMethods.Contains(m.Name), - filter) - .ToArray(); - } - - public static IReadOnlyList GetPublicStaticOperators( - ClassSymbol classSymbol, - ProxyData proxyData, - Func? filter = null) - { - filter ??= _ => true; - - return - GetPublicMembers( - classSymbol, - proxyData, - m => m.Kind == SymbolKind.Method, - m => m.MethodKind == MethodKind.Conversion, - m => !ExcludedMethods.Contains(m.Name), - filter) - .ToArray(); - } - - public static IReadOnlyList> GetPublicEvents( - ClassSymbol classSymbol, - ProxyData proxyData, - Func? filter = null) - { - filter ??= _ => true; - -#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type. -#pragma warning disable RS1024 // Compare symbols correctly - return GetPublicMembers( - classSymbol, - proxyData, - m => m.MethodKind is MethodKind.EventAdd or MethodKind.EventRemove/* || m.MethodKind == MethodKind.EventRaise*/, - filter) - .GroupBy(e => e.AssociatedSymbol) - .ToArray(); -#pragma warning restore RS1024 // Compare symbols correctly -#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type. - } - - // TODO : do we need also to check for "SanitizedName()" here? - private static IReadOnlyList GetPublicMembers( - ClassSymbol classSymbol, - ProxyData proxyData, - params Func[] filters - ) where T : ISymbol - { - var membersQuery = classSymbol.Symbol.GetMembers().OfType() - .Where(m => m.DeclaredAccessibility == Accessibility.Public); - - var f = filters.ToList(); - f.Add(x => !proxyData.MembersToIgnore.Contains(x.Name)); - foreach (var filter in f) - { - membersQuery = membersQuery.Where(filter); - } - - var ownMembers = membersQuery.ToList(); - var ownMemberNames = ownMembers.Select(x => x.Name); - - if (!proxyData.ProxyBaseClasses) - { - return ownMembers; - } - - var allMembers = ownMembers; - var baseType = classSymbol.Symbol.BaseType; - - while (baseType != null && baseType.SpecialType != SpecialType.System_Object) - { - var baseMembers = baseType.GetMembers().OfType() - .Where(m => m.DeclaredAccessibility == Accessibility.Public) - .Where(x => !ownMemberNames.Contains(x.Name)); - - foreach (var filter in filters) - { - baseMembers = baseMembers.Where(filter); - } - - allMembers.AddRange(baseMembers); - - baseType = baseType.BaseType; - } - - return allMembers; - } +using Microsoft.CodeAnalysis; +using ProxyInterfaceSourceGenerator.Models; + +namespace ProxyInterfaceSourceGenerator.Utils; + +internal static class MemberHelper +{ + private static readonly string[] ExcludedMethods = { "ToString", "GetHashCode" }; + + public static IReadOnlyList GetPublicProperties( + ClassSymbol classSymbol, + ProxyData proxyData, + params Func[] filters) + { + var allFilters = new List>(filters) + { + p => p.Kind == SymbolKind.Property + }; + + return GetPublicMembers(classSymbol, proxyData, allFilters.ToArray()).ToArray(); + } + + public static IReadOnlyList GetPublicMethods( + ClassSymbol classSymbol, + ProxyData proxyData, + Func? filter = null) + { + filter ??= _ => true; + + return + GetPublicMembers( + classSymbol, + proxyData, + m => m.Kind == SymbolKind.Method, + m => m.MethodKind == MethodKind.Ordinary, + m => !ExcludedMethods.Contains(m.Name), + filter) + .ToArray(); + } + + public static IReadOnlyList GetPublicStaticOperators( + ClassSymbol classSymbol, + ProxyData proxyData, + Func? filter = null) + { + filter ??= _ => true; + + return + GetPublicMembers( + classSymbol, + proxyData, + m => m.Kind == SymbolKind.Method, + m => m.MethodKind == MethodKind.Conversion, + m => !ExcludedMethods.Contains(m.Name), + filter) + .ToArray(); + } + + public static IReadOnlyList> GetPublicEvents( + ClassSymbol classSymbol, + ProxyData proxyData, + Func? filter = null) + { + filter ??= _ => true; + +#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type. +#pragma warning disable RS1024 // Compare symbols correctly + return GetPublicMembers( + classSymbol, + proxyData, + m => m.MethodKind is MethodKind.EventAdd or MethodKind.EventRemove/* || m.MethodKind == MethodKind.EventRaise*/, + filter) + .GroupBy(e => e.AssociatedSymbol) + .ToArray(); +#pragma warning restore RS1024 // Compare symbols correctly +#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type. + } + + // TODO : do we need also to check for "SanitizedName()" here? + private static IReadOnlyList GetPublicMembers( + ClassSymbol classSymbol, + ProxyData proxyData, + params Func[] filters + ) where T : ISymbol + { + var membersQuery = classSymbol.Symbol.GetMembers().OfType() + .Where(m => m.DeclaredAccessibility == Accessibility.Public); + + var f = filters.ToList(); + f.Add(x => !proxyData.MembersToIgnore.Contains(x.Name)); + foreach (var filter in f) + { + membersQuery = membersQuery.Where(filter); + } + + var ownMembers = membersQuery.ToList(); + var ownMemberNames = ownMembers.Select(x => x.Name); + + if (!proxyData.ProxyBaseClasses) + { + return ownMembers; + } + + var allMembers = ownMembers; + var baseType = classSymbol.Symbol.BaseType; + + while (baseType != null && baseType.SpecialType != SpecialType.System_Object) + { + var baseMembers = baseType.GetMembers().OfType() + .Where(m => m.DeclaredAccessibility == Accessibility.Public) + .Where(x => !ownMemberNames.Contains(x.Name)); + + foreach (var filter in filters) + { + baseMembers = baseMembers.Where(filter); + } + + allMembers.AddRange(baseMembers); + + baseType = baseType.BaseType; + } + + return allMembers; + } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Utils/NamespaceHelper.cs b/src/Speckle.ProxyGenerator/Utils/NamespaceHelper.cs similarity index 100% rename from src/ProxyInterfaceSourceGenerator/Utils/NamespaceHelper.cs rename to src/Speckle.ProxyGenerator/Utils/NamespaceHelper.cs diff --git a/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTests.csproj b/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTests.csproj index 6266499..7546c3a 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTests.csproj +++ b/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTests.csproj @@ -32,7 +32,7 @@ - + @@ -69,4 +69,4 @@ - \ No newline at end of file + diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Source/Foo.cs b/tests/ProxyInterfaceSourceGeneratorTests/Source/Foo.cs index 217f2e8..55d3ddd 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Source/Foo.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Source/Foo.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace ProxyInterfaceSourceGeneratorTests.Source; public class Foo @@ -17,7 +19,8 @@ public class Foo // return new Foo(); //} - public Foo[] Foos { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public Foo[] Foos { get; set; } = null!; public Foo[] DoSomethingAndGetAnArrayOfFoos() { @@ -28,4 +31,4 @@ public class Foo //{ // return new[] { new Foo() }.ToList(); //} -} \ No newline at end of file +} diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Source/Foo2.cs b/tests/ProxyInterfaceSourceGeneratorTests/Source/Foo2.cs index 01dfeb8..00b7e99 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Source/Foo2.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Source/Foo2.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace ProxyInterfaceSourceGeneratorTests.Source; public class Foo2 @@ -17,7 +19,8 @@ public class Foo2 // return new Foo(); //} - public Foo2[] Foos { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public Foo2[] Foos { get; set; }= null!; public Foo2[] DoSomethingAndGetAnArrayOfFoos() { @@ -33,4 +36,4 @@ public class Foo2 //{ // return new[] { new Foo() }.ToList(); //} -} \ No newline at end of file +} diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Source/MixedVisibility.cs b/tests/ProxyInterfaceSourceGeneratorTests/Source/MixedVisibility.cs index 49aa832..035b30b 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Source/MixedVisibility.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Source/MixedVisibility.cs @@ -1,6 +1,9 @@ +using System.Diagnostics.CodeAnalysis; + namespace ProxyInterfaceSourceGeneratorTests.Source; public class MixedVisibility { - public string Foo { get; protected set; } //<- this will generate bad code -} \ No newline at end of file + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public string Foo { get; protected set; } = null!;//<- this will generate bad code +} diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Source/Person.cs b/tests/ProxyInterfaceSourceGeneratorTests/Source/Person.cs index 4608016..e98a4fb 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Source/Person.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Source/Person.cs @@ -29,13 +29,15 @@ namespace ProxyInterfaceSourceGeneratorTests.Source } [Display(ResourceType = typeof(PeriodicTimer))] - public string Name { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public string Name { get; set; }= null!; public string? StringNullable { get; set; } public long? NullableLong { get; } - public object @object { get; set; } + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] + public object @object { get; set; }= null!; public void Void() { @@ -131,4 +133,4 @@ namespace ProxyInterfaceSourceGeneratorTests.Source return true; } } -} \ No newline at end of file +}