From cd866af765f04d7c88823b3850a874935b5f23e4 Mon Sep 17 00:00:00 2001
From: Beakona <branislav@guarami.com>
Date: Fri, 1 Dec 2023 14:49:02 +0100
Subject: [PATCH] initial implementation..

---
 .editorconfig                                 | 247 +++++++++++
 .github/workflows/codeql-analysis.yml         |  68 +++
 .github/workflows/dotnet-core.yml             |  31 ++
 .github/workflows/nuget.yml                   |  72 ++++
 AutoAs.sln                                    |  37 ++
 AutoAsSample/AutoAsSample.csproj              |  15 +
 AutoAsSample/Program.cs                       |  56 +++
 .../BeaKona.AutoAsAttributes.csproj           |  18 +
 .../GenerateAutoAsAttribute.cs                |  12 +
 .../AutoAsResource.Designer.cs                | 171 ++++++++
 BeaKona.AutoAsGenerator/AutoAsResource.resx   | 156 +++++++
 .../BeaKona.AutoAsGenerator.csproj            |  52 +++
 .../CSharpCodeTextWriter.cs                   | 388 ++++++++++++++++++
 .../GenerateAutoAsSourceGenerator.cs          | 320 +++++++++++++++
 BeaKona.AutoAsGenerator/Globals.cs            |   4 +
 BeaKona.AutoAsGenerator/Helpers.cs            |  26 ++
 BeaKona.AutoAsGenerator/ICodeTextWriter.cs    |  18 +
 .../IEnumerableExtensions.cs                  |   8 +
 .../INamedTypeSymbolExtensions.cs             |  23 ++
 .../INamespaceSymbolExtensions.cs             |  16 +
 BeaKona.AutoAsGenerator/ScopeInfo.cs          |  95 +++++
 BeaKona.AutoAsGenerator/SemanticFacts.cs      |  88 ++++
 BeaKona.AutoAsGenerator/SourceBuilder.cs      | 226 ++++++++++
 .../SourceBuilderOptions.cs                   |  54 +++
 24 files changed, 2201 insertions(+)
 create mode 100644 .editorconfig
 create mode 100644 .github/workflows/codeql-analysis.yml
 create mode 100644 .github/workflows/dotnet-core.yml
 create mode 100644 .github/workflows/nuget.yml
 create mode 100644 AutoAs.sln
 create mode 100644 AutoAsSample/AutoAsSample.csproj
 create mode 100644 AutoAsSample/Program.cs
 create mode 100644 BeaKona.AutoAsAttributes/BeaKona.AutoAsAttributes.csproj
 create mode 100644 BeaKona.AutoAsAttributes/GenerateAutoAsAttribute.cs
 create mode 100644 BeaKona.AutoAsGenerator/AutoAsResource.Designer.cs
 create mode 100644 BeaKona.AutoAsGenerator/AutoAsResource.resx
 create mode 100644 BeaKona.AutoAsGenerator/BeaKona.AutoAsGenerator.csproj
 create mode 100644 BeaKona.AutoAsGenerator/CSharpCodeTextWriter.cs
 create mode 100644 BeaKona.AutoAsGenerator/GenerateAutoAsSourceGenerator.cs
 create mode 100644 BeaKona.AutoAsGenerator/Globals.cs
 create mode 100644 BeaKona.AutoAsGenerator/Helpers.cs
 create mode 100644 BeaKona.AutoAsGenerator/ICodeTextWriter.cs
 create mode 100644 BeaKona.AutoAsGenerator/IEnumerableExtensions.cs
 create mode 100644 BeaKona.AutoAsGenerator/INamedTypeSymbolExtensions.cs
 create mode 100644 BeaKona.AutoAsGenerator/INamespaceSymbolExtensions.cs
 create mode 100644 BeaKona.AutoAsGenerator/ScopeInfo.cs
 create mode 100644 BeaKona.AutoAsGenerator/SemanticFacts.cs
 create mode 100644 BeaKona.AutoAsGenerator/SourceBuilder.cs
 create mode 100644 BeaKona.AutoAsGenerator/SourceBuilderOptions.cs

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..ed8729d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,247 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_predefined_type_for_member_access = true
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# Field preferences
+dotnet_style_readonly_field = true
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# New line preferences
+dotnet_style_allow_multiple_blank_lines_experimental = true
+dotnet_style_allow_statement_immediately_after_block_experimental = true
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false
+csharp_style_var_for_built_in_types = false
+csharp_style_var_when_type_is_apparent = false
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_prefer_not_pattern = true
+csharp_style_prefer_pattern_matching = true
+csharp_style_prefer_switch_expression = true
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true
+
+# Modifier preferences
+csharp_prefer_static_local_function = true
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_pattern_local_over_anonymous_function = true
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable
+csharp_style_unused_value_expression_statement_preference = discard_variable
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+# New line preferences
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
+csharp_style_allow_embedded_statements_on_same_line_experimental = true
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_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_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers = 
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers = 
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers = 
+
+# Naming styles
+
+dotnet_naming_style.pascal_case.required_prefix = 
+dotnet_naming_style.pascal_case.required_suffix = 
+dotnet_naming_style.pascal_case.word_separator = 
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix = 
+dotnet_naming_style.begins_with_i.word_separator = 
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+csharp_style_namespace_declarations = file_scoped:silent
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_style_prefer_null_check_over_type_check = true:suggestion
+csharp_style_prefer_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_tuple_swap = true:suggestion
+csharp_style_prefer_utf8_string_literals = true:suggestion
+
+[*.{cs,vb}]
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 4
+indent_size = 4
+end_of_line = crlf
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_auto_properties = true:silent
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_prefer_collection_expression = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+dotnet_style_namespace_match_folder = true:suggestion
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..4af58cd
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,68 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+# ******** NOTE ********
+
+name: "CodeQL"
+
+on:
+  push:
+    branches: [ main, master ]
+  pull_request:
+    # The branches below must be a subset of the branches above
+    branches: [ main, master ]
+  schedule:
+    - cron: '22 10 * * 6'
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+
+    strategy:
+      fail-fast: false
+      matrix:
+        language: [ 'csharp' ]
+        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
+        # Learn more...
+        # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v3
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v2
+      with:
+        languages: ${{ matrix.language }}
+        # If you wish to specify custom queries, you can do so here or in a config file.
+        # By default, queries listed here will override any specified in a config file.
+        # Prefix the list here with "+" to use these queries and those in the config file.
+        # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
+    # If this step fails, then you should remove it and run the build manually (see below)
+    - name: Autobuild
+      uses: github/codeql-action/autobuild@v2
+
+    # ℹ️ Command-line programs to run using the OS shell.
+    # 📚 https://git.io/JvXDl
+
+    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+    #    and modify them (or add more) to build your code if your project
+    #    uses a compiled language
+
+    #- run: |
+    #   make bootstrap
+    #   make release
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml
new file mode 100644
index 0000000..6abed5e
--- /dev/null
+++ b/.github/workflows/dotnet-core.yml
@@ -0,0 +1,31 @@
+name: .NET Core
+
+on:
+  push:
+    branches: [ main, master ]
+  pull_request:
+    branches: [ main, master ]
+
+env:
+  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+  DOTNET_VERSION: 8.0.x
+  DOTNET_CLI_TELEMETRY_OPTOUT: 1
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+    - name: Setup dotnet
+      uses: actions/setup-dotnet@v2
+      with:
+        dotnet-version: ${{ env.DOTNET_VERSION }}
+    - name: Install dependencies
+      run: dotnet restore
+    - name: Build
+      run: dotnet build --configuration Release --no-restore
+    - name: Test
+      run: dotnet test --no-restore --verbosity normal
diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml
new file mode 100644
index 0000000..19f65b2
--- /dev/null
+++ b/.github/workflows/nuget.yml
@@ -0,0 +1,72 @@
+name: NuGet
+
+on:
+  push:
+    branches: [ main, master ]
+  pull_request:
+    branches: [ main, master ]
+  release:
+    types:
+      - published
+
+env:
+  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+  DOTNET_VERSION: 8.0.x
+  DOTNET_CLI_TELEMETRY_OPTOUT: 1
+
+jobs:
+  build:
+    if: github.event_name == 'release'
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+    - name: Setup dotnet
+      uses: actions/setup-dotnet@v2
+      with:
+        dotnet-version: ${{ env.DOTNET_VERSION }}
+    - name: Install dependencies
+      run: dotnet restore
+    - name: Build
+      run: dotnet build --configuration Release --no-restore
+    - name: Test
+      run: dotnet test --no-restore --verbosity normal
+
+  pack:
+    needs: build
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+    - name: Setup dotnet
+      uses: actions/setup-dotnet@v2
+      with:
+        dotnet-version: ${{ env.DOTNET_VERSION }}
+    - name: Pack
+      run: |
+        arrTag=(${GITHUB_REF//\// })
+        VERSION="${arrTag[2]}"
+        echo Version: $VERSION
+        VERSION="${VERSION//v}"
+        dotnet pack -c Release -p:PackageVersion=$VERSION BeaKona.AutoAsGenerator
+    - name: Upload Artifact
+      uses: actions/upload-artifact@v2
+      with:
+        name: nupkg
+        path: |
+         ./**/bin/Release/*.nupkg
+
+  push:
+    needs: pack
+    runs-on: ubuntu-latest
+    steps:
+    - name: Setup dotnet
+      uses: actions/setup-dotnet@v2
+      with:
+        dotnet-version: ${{ env.DOTNET_VERSION }}
+    - name: Download Artifact
+      uses: actions/download-artifact@v1
+      with:
+        name: nupkg
+    - name: Push to nuget.org
+      run: dotnet nuget push ./nupkg/**/*.* --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NugetApiKey }}
diff --git a/AutoAs.sln b/AutoAs.sln
new file mode 100644
index 0000000..1c25518
--- /dev/null
+++ b/AutoAs.sln
@@ -0,0 +1,37 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34321.82
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeaKona.AutoAsGenerator", "BeaKona.AutoAsGenerator\BeaKona.AutoAsGenerator.csproj", "{26A63262-E5F2-4652-8911-1AF2231F65F7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeaKona.AutoAsAttributes", "BeaKona.AutoAsAttributes\BeaKona.AutoAsAttributes.csproj", "{0579C9D8-A6A4-42C4-981E-0BCC853A04E9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoAsSample", "AutoAsSample\AutoAsSample.csproj", "{DBA68BE8-4627-4CFF-96FE-3688253861B4}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{26A63262-E5F2-4652-8911-1AF2231F65F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{26A63262-E5F2-4652-8911-1AF2231F65F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{26A63262-E5F2-4652-8911-1AF2231F65F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{26A63262-E5F2-4652-8911-1AF2231F65F7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{0579C9D8-A6A4-42C4-981E-0BCC853A04E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{0579C9D8-A6A4-42C4-981E-0BCC853A04E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{0579C9D8-A6A4-42C4-981E-0BCC853A04E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{0579C9D8-A6A4-42C4-981E-0BCC853A04E9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{DBA68BE8-4627-4CFF-96FE-3688253861B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DBA68BE8-4627-4CFF-96FE-3688253861B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DBA68BE8-4627-4CFF-96FE-3688253861B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{DBA68BE8-4627-4CFF-96FE-3688253861B4}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {1DF42858-9E11-42F2-9681-9F55C549F816}
+	EndGlobalSection
+EndGlobal
diff --git a/AutoAsSample/AutoAsSample.csproj b/AutoAsSample/AutoAsSample.csproj
new file mode 100644
index 0000000..10c6d8d
--- /dev/null
+++ b/AutoAsSample/AutoAsSample.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+	<ItemGroup>
+		<Analyzer Include="..\BeaKona.AutoAsGenerator\bin\Debug\netstandard2.0\BeaKona.AutoAsGenerator.dll" />
+		<ProjectReference Include="..\BeaKona.AutoAsAttributes\BeaKona.AutoAsAttributes.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
+	</ItemGroup>
+
+</Project>
diff --git a/AutoAsSample/Program.cs b/AutoAsSample/Program.cs
new file mode 100644
index 0000000..e0e5e3b
--- /dev/null
+++ b/AutoAsSample/Program.cs
@@ -0,0 +1,56 @@
+using BeaKona;
+using System.Collections;
+
+namespace AutoAsSample;
+
+internal partial class Program
+{
+    static void Main()
+    {
+        //System.Diagnostics.Debug.WriteLine(BeaKona.Output.Debug_TestClass_1.Info);
+
+        TestClass<int> t = new TestClass<int>();
+        var x = t.AsMy1();
+    }
+}
+
+public interface IMy1Base
+{
+}
+
+public interface IMy1<H> : IMy1Base
+{
+}
+
+internal interface IMy2<T>
+{
+}
+
+internal interface IMy2<T1, T2>
+{
+}
+
+internal interface IMy3
+{
+}
+
+internal interface @internal
+{
+}
+
+public abstract class TestClassBase : IMy3
+{
+}
+
+[GenerateAutoAs(EntireInterfaceHierarchy = false, SkipSystemInterfaces = true)]
+public partial class TestClass<T> : TestClassBase, IMy1<T>, IMy2<int>, IMy2<string>, IMy2<string, string>, IEnumerable<int>, @internal
+{
+    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
+
+    public IEnumerator<int> GetEnumerator()
+    {
+        throw new NotImplementedException();
+    }
+
+    //internal IMy2<int> AsMy2_0() => this;
+}
diff --git a/BeaKona.AutoAsAttributes/BeaKona.AutoAsAttributes.csproj b/BeaKona.AutoAsAttributes/BeaKona.AutoAsAttributes.csproj
new file mode 100644
index 0000000..0f49060
--- /dev/null
+++ b/BeaKona.AutoAsAttributes/BeaKona.AutoAsAttributes.csproj
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+	  <LangVersion>latest</LangVersion>
+	  <Nullable>enable</Nullable>
+	  <PackageId>BeaKona.AutoAsAttributes</PackageId>
+	  <Authors>BeaKona</Authors>
+	  <Description>Shared BeaKona.AutoAs source generator attributes.</Description>
+	  <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
+	  <PackageLicenseExpression>MIT</PackageLicenseExpression>
+	  <RepositoryUrl>https://github.com/beakona/AutoAs</RepositoryUrl>
+	  <RepositoryType>git</RepositoryType>
+	  <Version>1.0.0</Version>
+	  <GeneratePackageOnBuild>False</GeneratePackageOnBuild>
+  </PropertyGroup>
+
+</Project>
diff --git a/BeaKona.AutoAsAttributes/GenerateAutoAsAttribute.cs b/BeaKona.AutoAsAttributes/GenerateAutoAsAttribute.cs
new file mode 100644
index 0000000..377aa9e
--- /dev/null
+++ b/BeaKona.AutoAsAttributes/GenerateAutoAsAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Diagnostics;
+
+namespace BeaKona;
+
+[Conditional("CodeGeneration")]
+[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+public sealed class GenerateAutoAsAttribute : Attribute
+{
+    public bool EntireInterfaceHierarchy { get; set; } = false;
+    public bool SkipSystemInterfaces { get; set; } = true;
+}
diff --git a/BeaKona.AutoAsGenerator/AutoAsResource.Designer.cs b/BeaKona.AutoAsGenerator/AutoAsResource.Designer.cs
new file mode 100644
index 0000000..1a7e6ce
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/AutoAsResource.Designer.cs
@@ -0,0 +1,171 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace BeaKona.AutoAsGenerator {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class AutoAsResource {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal AutoAsResource() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BeaKona.AutoAsGenerator.AutoAsResource", typeof(AutoAsResource).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to AA01..
+        /// </summary>
+        internal static string AA01_description {
+            get {
+                return ResourceManager.GetString("AA01_description", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Type &apos;{0}&apos; is not marked as partial..
+        /// </summary>
+        internal static string AA01_message {
+            get {
+                return ResourceManager.GetString("AA01_message", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Type is not marked as partial..
+        /// </summary>
+        internal static string AA01_title {
+            get {
+                return ResourceManager.GetString("AA01_title", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to AA02..
+        /// </summary>
+        internal static string AA02_description {
+            get {
+                return ResourceManager.GetString("AA02_description", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Type &apos;{0}&apos; is static..
+        /// </summary>
+        internal static string AA02_message {
+            get {
+                return ResourceManager.GetString("AA02_message", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Type is static..
+        /// </summary>
+        internal static string AA02_title {
+            get {
+                return ResourceManager.GetString("AA02_title", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to AA03..
+        /// </summary>
+        internal static string AA03_description {
+            get {
+                return ResourceManager.GetString("AA03_description", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Only class, record or struct can auto-implement interface..
+        /// </summary>
+        internal static string AA03_message {
+            get {
+                return ResourceManager.GetString("AA03_message", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Type is not a class, record, or struct..
+        /// </summary>
+        internal static string AA03_title {
+            get {
+                return ResourceManager.GetString("AA03_title", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to AA04..
+        /// </summary>
+        internal static string AA04_description {
+            get {
+                return ResourceManager.GetString("AA04_description", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Internal exception [{0}]..
+        /// </summary>
+        internal static string AA04_message {
+            get {
+                return ResourceManager.GetString("AA04_message", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Internal exception..
+        /// </summary>
+        internal static string AA04_title {
+            get {
+                return ResourceManager.GetString("AA04_title", resourceCulture);
+            }
+        }
+    }
+}
diff --git a/BeaKona.AutoAsGenerator/AutoAsResource.resx b/BeaKona.AutoAsGenerator/AutoAsResource.resx
new file mode 100644
index 0000000..177f26b
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/AutoAsResource.resx
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="AA01_description" xml:space="preserve">
+    <value>AA01.</value>
+  </data>
+  <data name="AA01_message" xml:space="preserve">
+    <value>Type '{0}' is not marked as partial.</value>
+  </data>
+  <data name="AA01_title" xml:space="preserve">
+    <value>Type is not marked as partial.</value>
+  </data>
+  <data name="AA02_description" xml:space="preserve">
+    <value>AA02.</value>
+  </data>
+  <data name="AA02_message" xml:space="preserve">
+    <value>Type '{0}' is static.</value>
+  </data>
+  <data name="AA02_title" xml:space="preserve">
+    <value>Type is static.</value>
+  </data>
+  <data name="AA03_description" xml:space="preserve">
+    <value>AA03.</value>
+  </data>
+  <data name="AA03_message" xml:space="preserve">
+    <value>Only class, record or struct can auto-implement interface.</value>
+  </data>
+  <data name="AA03_title" xml:space="preserve">
+    <value>Type is not a class, record, or struct.</value>
+  </data>
+  <data name="AA04_description" xml:space="preserve">
+    <value>AA04.</value>
+  </data>
+  <data name="AA04_message" xml:space="preserve">
+    <value>Internal exception [{0}].</value>
+  </data>
+  <data name="AA04_title" xml:space="preserve">
+    <value>Internal exception.</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/BeaKona.AutoAsGenerator/BeaKona.AutoAsGenerator.csproj b/BeaKona.AutoAsGenerator/BeaKona.AutoAsGenerator.csproj
new file mode 100644
index 0000000..0621e65
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/BeaKona.AutoAsGenerator.csproj
@@ -0,0 +1,52 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <LangVersion>latest</LangVersion>
+    <Nullable>enable</Nullable>
+    <IncludeBuildOutput>false</IncludeBuildOutput>
+    <PackageId>BeaKona.AutoAsGenerator</PackageId>
+    <Authors>BeaKona</Authors>
+    <Description>Fluent AsInterface() method C# source generator.</Description>
+    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
+    <PackageLicenseExpression>MIT</PackageLicenseExpression>
+    <RepositoryUrl>https://github.com/beakona/AutoAs</RepositoryUrl>
+    <RepositoryType>git</RepositoryType>
+    <TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput</TargetsForTfmSpecificContentInPackage>
+    <Version>1.0.0</Version>
+    <IsRoslynComponent>true</IsRoslynComponent>
+    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CSharp" Version="4.7.0" PrivateAssets="all" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\BeaKona.AutoAsAttributes\BeaKona.AutoAsAttributes.csproj" PrivateAssets="all" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Update="AutoAsResource.Designer.cs">
+      <DesignTime>True</DesignTime>
+      <AutoGen>True</AutoGen>
+      <DependentUpon>AutoAsResource.resx</DependentUpon>
+    </Compile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <EmbeddedResource Update="AutoAsResource.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>AutoAsResource.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+  </ItemGroup>
+  <Target Name="_AddAnalyzersToOutput">
+    <ItemGroup>
+      <TfmSpecificPackageFile Include="$(OutputPath)\BeaKona.AutoAsGenerator.dll" PackagePath="analyzers/dotnet/cs" Pack="true" Visible="false" />
+      <TfmSpecificPackageFile Include="$(OutputPath)\BeaKona.AutoAsAttributes.dll" PackagePath="analyzers/dotnet/cs" Pack="true" Visible="false" />
+      <TfmSpecificPackageFile Include="$(OutputPath)\BeaKona.AutoAsAttributes.dll" PackagePath="lib/netstandard2.0" Pack="true" Visible="true" />
+	</ItemGroup>
+  </Target>
+
+</Project>
diff --git a/BeaKona.AutoAsGenerator/CSharpCodeTextWriter.cs b/BeaKona.AutoAsGenerator/CSharpCodeTextWriter.cs
new file mode 100644
index 0000000..27de71a
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/CSharpCodeTextWriter.cs
@@ -0,0 +1,388 @@
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace BeaKona.AutoAsGenerator;
+
+internal sealed class CSharpCodeTextWriter : ICodeTextWriter
+{
+    public CSharpCodeTextWriter(GeneratorExecutionContext context, Compilation compilation)
+    {
+        this.Context = context;
+        this.Compilation = compilation;
+    }
+
+    public GeneratorExecutionContext Context { get; }
+    public Compilation Compilation { get; }
+
+    public void WriteTypeReference(SourceBuilder builder, ITypeSymbol type, ScopeInfo scope)
+    {
+        if (scope.TryGetAlias(type, out string? typeName))
+        {
+            if (typeName != null)
+            {
+                builder.Append(typeName);
+            }
+        }
+        else
+        {
+            bool processed = false;
+            if (type.SpecialType != SpecialType.None)
+            {
+                processed = true;
+                switch (type.SpecialType)
+                {
+                default: processed = false; break;
+                case SpecialType.System_Object: builder.Append("object"); break;
+                case SpecialType.System_Void: builder.Append("void"); break;
+                case SpecialType.System_Boolean: builder.Append("bool"); break;
+                case SpecialType.System_Char: builder.Append("char"); break;
+                case SpecialType.System_SByte: builder.Append("sbyte"); break;
+                case SpecialType.System_Byte: builder.Append("byte"); break;
+                case SpecialType.System_Int16: builder.Append("short"); break;
+                case SpecialType.System_UInt16: builder.Append("ushort"); break;
+                case SpecialType.System_Int32: builder.Append("int"); break;
+                case SpecialType.System_UInt32: builder.Append("uint"); break;
+                case SpecialType.System_Int64: builder.Append("long"); break;
+                case SpecialType.System_UInt64: builder.Append("ulong"); break;
+                case SpecialType.System_Decimal: builder.Append("decimal"); break;
+                case SpecialType.System_Single: builder.Append("float"); break;
+                case SpecialType.System_Double: builder.Append("double"); break;
+                //case SpecialType.System_Half: builder.Append("half"); break;
+                case SpecialType.System_String: builder.Append("string"); break;
+                }
+            }
+
+            if (processed == false)
+            {
+                if (type is IArrayTypeSymbol array)
+                {
+                    this.WriteTypeReference(builder, array.ElementType, scope);
+                    builder.Append('[');
+                    for (int i = 1; i < array.Rank; i++)
+                    {
+                        builder.Append(',');
+                    }
+                    builder.Append(']');
+                }
+                else
+                {
+                    static bool IsTupleWithAliases(INamedTypeSymbol tuple)
+                    {
+                        return tuple.TupleElements.Any(i => i.CorrespondingTupleField != null && i.Equals(i.CorrespondingTupleField, SymbolEqualityComparer.Default) == false);
+                    }
+
+                    if (type.IsTupleType && type is INamedTypeSymbol tupleType && IsTupleWithAliases(tupleType))
+                    {
+                        builder.Append('(');
+                        bool first = true;
+                        foreach (IFieldSymbol field in tupleType.TupleElements)
+                        {
+                            if (first)
+                            {
+                                first = false;
+                            }
+                            else
+                            {
+                                builder.Append(", ");
+                            }
+                            this.WriteTypeReference(builder, field.Type, scope);
+                            builder.Append(' ');
+                            this.WriteIdentifier(builder, field);
+                        }
+                        builder.Append(')');
+                    }
+                    else if (type is INamedTypeSymbol nt && SemanticFacts.IsNullableT(this.Compilation, nt))
+                    {
+                        this.WriteTypeReference(builder, nt.TypeArguments[0], scope);
+                    }
+                    else
+                    {
+                        if (type is ITypeParameterSymbol == false)
+                        {
+                            if (type.Equals(scope.Type, SymbolEqualityComparer.Default) == false)
+                            {
+                                string? alias = SemanticFacts.ResolveAssemblyAlias(this.Compilation, type.ContainingAssembly);
+                                ISymbol[] symbols;
+                                if (alias == null)
+                                {
+                                    symbols = SemanticFacts.GetRelativeSymbols(type, scope.Type);
+                                }
+                                else
+                                {
+                                    symbols = SemanticFacts.GetContainingSymbols(type, false);
+                                    builder.Append(alias);
+                                    builder.Append("::");
+                                    builder.RegisterAlias(alias);
+                                }
+
+                                foreach (ISymbol symbol in symbols)
+                                {
+                                    this.WriteIdentifier(builder, symbol);
+
+                                    if (symbol is INamedTypeSymbol snt && snt.IsGenericType)
+                                    {
+                                        builder.Append('<');
+                                        this.WriteTypeArgumentsDefinition(builder, snt.TypeArguments, scope);
+                                        builder.Append('>');
+                                    }
+
+                                    builder.Append('.');
+                                }
+                            }
+                        }
+
+                        this.WriteIdentifier(builder, type);
+
+                        {
+                            if (type is INamedTypeSymbol tnt && tnt.IsGenericType)
+                            {
+                                builder.Append('<');
+                                this.WriteTypeArgumentsDefinition(builder, tnt.TypeArguments, scope);
+                                builder.Append('>');
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (type.NullableAnnotation == NullableAnnotation.Annotated)
+        {
+            builder.Append('?');
+        }
+    }
+
+    public void WriteTypeArgumentsDefinition(SourceBuilder builder, IEnumerable<ITypeSymbol> typeArguments, ScopeInfo scope)
+    {
+        bool first = true;
+        foreach (ITypeSymbol t in typeArguments)
+        {
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                builder.Append(", ");
+            }
+            this.WriteTypeReference(builder, t, scope);
+        }
+    }
+
+    public void WriteTypeDeclarationBeginning(SourceBuilder builder, INamedTypeSymbol type, ScopeInfo scope)
+    {
+        builder.Append("partial");
+        builder.Append(' ');
+        if (type.TypeKind == TypeKind.Class)
+        {
+            bool isRecord = type.DeclaringSyntaxReferences.Any(i => i.GetSyntax() is RecordDeclarationSyntax);
+            builder.Append(isRecord ? "record" : "class");
+        }
+        else if (type.TypeKind == TypeKind.Struct)
+        {
+            builder.Append("struct");
+        }
+        else if (type.TypeKind == TypeKind.Interface)
+        {
+            builder.Append("interface");
+        }
+        else
+        {
+            throw new NotSupportedException(nameof(WriteTypeDeclarationBeginning));
+        }
+        builder.Append(' ');
+        this.WriteTypeReference(builder, type, scope);
+    }
+
+    public void WriteNamespaceBeginning(SourceBuilder builder, INamespaceSymbol @namespace)
+    {
+        if (@namespace != null && @namespace.ConstituentNamespaces.Length > 0)
+        {
+            List<INamespaceSymbol> containingNamespaces = [];
+            for (INamespaceSymbol? ct = @namespace; ct != null && ct.IsGlobalNamespace == false; ct = ct.ContainingNamespace)
+            {
+                containingNamespaces.Insert(0, ct);
+            }
+
+            if (containingNamespaces.Count > 0)
+            {
+                builder.AppendIndentation();
+                builder.Append("namespace");
+                builder.Append(' ');
+                builder.AppendLine(string.Join(".", containingNamespaces.Select(i => GetSourceIdentifier(i))));
+                builder.AppendIndentation();
+                builder.AppendLine('{');
+                builder.IncrementIndentation();
+            }
+        }
+    }
+
+    public void WriteHolderReference(SourceBuilder builder, ISymbol member, ScopeInfo scope)
+    {
+        if (member.IsStatic)
+        {
+            this.WriteTypeReference(builder, member.ContainingType, scope);
+        }
+        else
+        {
+            builder.Append("this");
+        }
+    }
+
+    public void WriteIdentifier(SourceBuilder builder, ISymbol symbol)
+    {
+        builder.Append(this.GetSourceIdentifier(symbol));
+    }
+
+    #region helper members
+
+    private string GetSourceIdentifier(ISymbol symbol)
+    {
+        if (symbol is IPropertySymbol propertySymbol && propertySymbol.IsIndexer)
+        {
+            return "this";
+        }
+        else if (symbol is INamespaceSymbol ns)
+        {
+            return ns.Name;
+            //return string.Join("+", ns.ConstituentNamespaces.Select(i => i.Name));
+            //return $"<{@namespace.Name};{symbol}>" + this.GetSourceIdentifier(@namespace.Name);
+        }
+        else if (symbol.DeclaringSyntaxReferences.Length == 0)
+        {
+            return symbol.Name;
+        }
+        else
+        {
+            foreach (SyntaxReference syntaxReference in symbol.DeclaringSyntaxReferences)
+            {
+                SyntaxNode syntax = syntaxReference.GetSyntax();
+                if (syntax is BaseTypeDeclarationSyntax type)
+                {
+                    return this.GetSourceIdentifier(type.Identifier);
+                }
+                else if (syntax is MethodDeclarationSyntax method)
+                {
+                    return this.GetSourceIdentifier(method.Identifier);
+                }
+                else if (syntax is ParameterSyntax parameter)
+                {
+                    return this.GetSourceIdentifier(parameter.Identifier);
+                }
+                else if (syntax is VariableDeclaratorSyntax variableDeclarator)
+                {
+                    return this.GetSourceIdentifier(variableDeclarator.Identifier);
+                }
+                else if (syntax is VariableDeclarationSyntax variableDeclaration)
+                {
+                    if (variableDeclaration.Variables.Any(i => i.Identifier.IsVerbatimIdentifier()))
+                    {
+                        return "@" + symbol;
+                    }
+                    else
+                    {
+                        return symbol.ToString();
+                    }
+                }
+                else if (syntax is BaseFieldDeclarationSyntax field)
+                {
+                    if (field.Declaration.Variables.Any(i => i.Identifier.IsVerbatimIdentifier()))
+                    {
+                        return "@" + symbol;
+                    }
+                    else
+                    {
+                        return symbol.ToString();
+                    }
+                }
+                else if (syntax is PropertyDeclarationSyntax property)
+                {
+                    return this.GetSourceIdentifier(property.Identifier);
+                }
+                else if (syntax is IndexerDeclarationSyntax)
+                {
+                    throw new InvalidOperationException("trying to resolve indexer name");
+                }
+                else if (syntax is EventDeclarationSyntax @event)
+                {
+                    return this.GetSourceIdentifier(@event.Identifier);
+                }
+                else if (syntax is TypeParameterSyntax typeParameter)
+                {
+                    return this.GetSourceIdentifier(typeParameter.Identifier);
+                }
+                else if (syntax is TupleTypeSyntax)
+                {
+                    return symbol.Name;
+                }
+                else if (syntax is TupleElementSyntax tupleElement)
+                {
+                    return this.GetSourceIdentifier(tupleElement.Identifier);
+                }
+                else if (syntax is NamespaceDeclarationSyntax @namespace)
+                {
+                    throw new NotSupportedException(syntax.GetType().ToString());
+                }
+                else
+                {
+                    throw new NotSupportedException(syntax.GetType().ToString());
+                }
+            }
+
+            throw new NotSupportedException();
+        }
+    }
+
+    private string GetSourceIdentifier(SyntaxToken identifier)
+    {
+        if (identifier.IsVerbatimIdentifier())
+        {
+            return "@" + identifier.ValueText;
+        }
+        else
+        {
+            return identifier.ValueText;
+        }
+    }
+
+    private string GetSourceIdentifier(NameSyntax name)
+    {
+        if (name is SimpleNameSyntax simpleName)
+        {
+            return this.GetSourceIdentifier(simpleName.Identifier);
+        }
+        else if (name is QualifiedNameSyntax qualifiedName)
+        {
+            string left = this.GetSourceIdentifier(qualifiedName.Left);
+            string right = this.GetSourceIdentifier(qualifiedName.Right);
+            if (string.IsNullOrEmpty(left))
+            {
+                if (string.IsNullOrEmpty(right))
+                {
+                    throw new NotSupportedException("both are null_or_empty.");
+                }
+                else
+                {
+                    return right;
+                }
+            }
+            else
+            {
+                if (string.IsNullOrEmpty(right))
+                {
+                    return left;
+                }
+                else
+                {
+                    return left + "." + right;
+                }
+            }
+        }
+        else
+        {
+            throw new NotSupportedException(name.GetType().ToString());
+        }
+    }
+
+    #endregion
+}
diff --git a/BeaKona.AutoAsGenerator/GenerateAutoAsSourceGenerator.cs b/BeaKona.AutoAsGenerator/GenerateAutoAsSourceGenerator.cs
new file mode 100644
index 0000000..989d42e
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/GenerateAutoAsSourceGenerator.cs
@@ -0,0 +1,320 @@
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+using System.Text;
+
+namespace BeaKona.AutoAsGenerator;
+
+[Generator]
+public sealed class GenerateAutoAsSourceGenerator : ISourceGenerator
+{
+    public void Initialize(GeneratorInitializationContext context)
+    {
+        // Register a syntax receiver that will be created for each generation pass
+        context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+    }
+
+    public void Execute(GeneratorExecutionContext context)
+    {
+        Compilation compilation = context.Compilation;
+        if (compilation is CSharpCompilation)
+        {
+            //retrieve the populated receiver
+            if (context.SyntaxReceiver is SyntaxReceiver receiver)
+            {
+                // get newly bound attribute
+                if (compilation.GetTypeByMetadataName(typeof(GenerateAutoAsAttribute).FullName) is INamedTypeSymbol generateAutoAsAttributeSymbol)
+                {
+                    GenerateAutoAsAttribute? GetGenerateAutoAsAttribute(INamedTypeSymbol type)
+                    {
+                        foreach (AttributeData attribute in type.GetAttributes())
+                        {
+                            if (attribute.AttributeClass != null && attribute.AttributeClass.Equals(generateAutoAsAttributeSymbol, SymbolEqualityComparer.Default))
+                            {
+                                var result = new GenerateAutoAsAttribute();
+
+                                foreach (KeyValuePair<string, TypedConstant> arg in attribute.NamedArguments)
+                                {
+                                    switch (arg.Key)
+                                    {
+                                        case nameof(GenerateAutoAsAttribute.EntireInterfaceHierarchy):
+                                            {
+                                                if (arg.Value.Value is bool b)
+                                                {
+                                                    result.EntireInterfaceHierarchy = b;
+                                                }
+                                            }
+                                            break;
+                                        case nameof(GenerateAutoAsAttribute.SkipSystemInterfaces):
+                                            {
+                                                if (arg.Value.Value is bool b)
+                                                {
+                                                    result.SkipSystemInterfaces = b;
+                                                }
+                                            }
+                                            break;
+                                    }
+                                }
+
+                                return result;
+                            }
+                        }
+
+                        return null;
+                    }
+
+                    var types = new List<INamedTypeSymbol>();
+
+                    foreach (TypeDeclarationSyntax candidate in receiver.Candidates)
+                    {
+                        SemanticModel model = compilation.GetSemanticModel(candidate.SyntaxTree);
+
+                        if (model.GetDeclaredSymbol(candidate) is INamedTypeSymbol type)
+                        {
+                            if (GetGenerateAutoAsAttribute(type) is GenerateAutoAsAttribute attribute)
+                            {
+                                if (type.IsPartial() == false)
+                                {
+                                    Helpers.ReportDiagnostic(context, "BKAA01", nameof(AutoAsResource.AA01_title), nameof(AutoAsResource.AA01_message), nameof(AutoAsResource.AA01_description), DiagnosticSeverity.Error, type,
+                                        type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat));
+                                    continue;
+                                }
+
+                                if (type.IsStatic)
+                                {
+                                    Helpers.ReportDiagnostic(context, "BKAA02", nameof(AutoAsResource.AA02_title), nameof(AutoAsResource.AA02_message), nameof(AutoAsResource.AA02_description), DiagnosticSeverity.Error, type,
+                                        type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat));
+                                    continue;
+                                }
+
+                                if (type.TypeKind != TypeKind.Class && type.TypeKind != TypeKind.Struct)
+                                {
+                                    Helpers.ReportDiagnostic(context, "BKAA03", nameof(AutoAsResource.AA03_title), nameof(AutoAsResource.AA03_message), nameof(AutoAsResource.AA03_description), DiagnosticSeverity.Error, type);
+                                    continue;
+                                }
+
+                                try
+                                {
+                                    string? code = GenerateAutoAsSourceGenerator.ProcessClass(context, compilation, type, attribute);
+                                    if (code != null)
+                                    {
+                                        string name = type.Arity > 0 ? $"{type.Name}_{type.Arity}" : type.Name;
+#if PEEK_1
+                                        GeneratePreview(context, name, code);
+#else
+                                        context.AddSource($"{name}_GenerateAutoAs.g.cs", SourceText.From(code, Encoding.UTF8));
+#endif
+                                    }
+                                }
+                                catch (Exception ex)
+                                {
+                                    Helpers.ReportDiagnostic(context, "BKAA09", nameof(AutoAsResource.AA04_title), nameof(AutoAsResource.AA04_message), nameof(AutoAsResource.AA04_description), DiagnosticSeverity.Error, type,
+                                        ex.ToString().Replace("\r", "").Replace("\n", ""));
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+#if PEEK_1
+    private static void GeneratePreview(GeneratorExecutionContext context, string name, string code)
+    {
+        var output = new StringBuilder();
+        output.AppendLine("namespace BeaKona.Output {");
+        output.AppendLine($"public static class Debug_{name}");
+        output.AppendLine("{");
+        output.AppendLine($"public static readonly string Info = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(\"{Convert.ToBase64String(Encoding.UTF8.GetBytes(code ?? ""))}\"));");
+        output.AppendLine("}");
+        output.AppendLine("}");
+        context.AddSource($"Output_Debug_{name}.g.cs", SourceText.From(output.ToString(), Encoding.UTF8));
+    }
+#endif
+
+    private static string? ProcessClass(GeneratorExecutionContext context, Compilation compilation, INamedTypeSymbol type, GenerateAutoAsAttribute attribute)
+    {
+        var scope = new ScopeInfo(type);
+
+        List<INamedTypeSymbol> interfaces;
+
+        if (attribute.EntireInterfaceHierarchy)
+        {
+            interfaces = type.AllInterfaces.Where(i => i.CanBeReferencedByName).ToList();
+        }
+        else
+        {
+            interfaces = [];
+
+            //interface list is small, we will not use HashSet here
+            for (var t = type; t != null; t = t.BaseType)
+            {
+                foreach (var @interface in t.Interfaces)
+                {
+                    if (@interface.CanBeReferencedByName && interfaces.Contains(@interface) == false)
+                    {
+                        interfaces.Add(@interface);
+                    }
+                }
+            }
+        }
+
+        if (attribute.SkipSystemInterfaces)
+        {
+            for (int i = 0; i < interfaces.Count; i++)
+            {
+                var @interface = interfaces[i];
+
+                if (@interface.ContainingNamespace is INamespaceSymbol @namespace)
+                {
+                    if (@namespace.FirstNonGlobalNamespace() is INamespaceSymbol first)
+                    {
+                        if (first.Name.Equals("System", StringComparison.InvariantCulture) || first.Name.StartsWith("System.", StringComparison.InvariantCulture))
+                        {
+                            interfaces.RemoveAt(i--);
+                        }
+                    }
+                }
+            }
+        }
+
+        var options = SourceBuilderOptions.Load(context, null);
+        var builder = new SourceBuilder(options);
+
+        ICodeTextWriter writer = new CSharpCodeTextWriter(context, compilation);
+        bool anyReasonToEmitSourceFile = false;
+        bool error = false;
+
+        builder.AppendLine("// <auto-generated />");
+        //bool isNullable = compilation.Options.NullableContextOptions == NullableContextOptions.Enable;
+        builder.AppendLine("#nullable enable");
+        builder.AppendLine();
+        writer.WriteNamespaceBeginning(builder, type.ContainingNamespace);
+
+        List<INamedTypeSymbol> containingTypes = [];
+        for (INamedTypeSymbol? ct = type.ContainingType; ct != null; ct = ct.ContainingType)
+        {
+            containingTypes.Insert(0, ct);
+        }
+
+        foreach (INamedTypeSymbol ct in containingTypes)
+        {
+            builder.AppendIndentation();
+            writer.WriteTypeDeclarationBeginning(builder, ct, new ScopeInfo(ct));
+            builder.AppendLine();
+            builder.AppendIndentation();
+            builder.AppendLine('{');
+            builder.IncrementIndentation();
+        }
+
+        builder.AppendIndentation();
+        writer.WriteTypeDeclarationBeginning(builder, type, scope);
+        builder.AppendLine();
+        builder.AppendIndentation();
+        builder.AppendLine('{');
+        builder.IncrementIndentation();
+
+        if (interfaces.Any())
+        {
+            var existingMethods = type.GetMembers().OfType<IMethodSymbol>().Where(i => i.MethodKind != MethodKind.ExplicitInterfaceImplementation).Select(i => i.Name).ToHashSet();
+
+            string GetModifiedMethodName(string methodName, int? index)
+            {
+                if (methodName.StartsWith("I", StringComparison.InvariantCulture) && methodName.Length > 1 && char.IsUpper(methodName[1]))
+                {
+                    methodName = methodName.Substring(1);
+                }
+                if (index.HasValue)
+                {
+                    methodName = $"{methodName}_{index.Value}";
+                }
+                return "As" + methodName;
+            }
+
+            void WriteMethod(INamedTypeSymbol @interface, int? index)
+            {
+                var desiredMethodName = GetModifiedMethodName(@interface.Name, index);
+                if (existingMethods.Add(desiredMethodName))
+                {
+                    anyReasonToEmitSourceFile = true;
+
+                    builder.AppendIndentation();
+                    builder.Append(@interface.DeclaredAccessibility == Accessibility.Internal ? "internal" : "public");
+                    builder.Append(' ');
+                    writer.WriteTypeReference(builder, @interface, scope);
+                    builder.Append(' ');
+                    builder.Append(desiredMethodName);
+                    builder.Append("() => ");
+                    writer.WriteHolderReference(builder, type, scope);
+                    builder.Append(';');
+                    builder.AppendLine();
+                }
+            }
+
+            foreach (var group in interfaces.GroupBy(i => i.Name))
+            {
+                if (group.Count() == 1)
+                {
+                    WriteMethod(group.First(), null);
+                }
+                else
+                {
+                    int index = 0;
+
+                    foreach (var @interface in group.OrderBy(i => i.TypeArguments.Length).ThenBy(i => i.Name))
+                    {
+                        WriteMethod(@interface, index++);
+                    }
+                }
+            }
+        }
+
+        builder.DecrementIndentation();
+        builder.AppendIndentation();
+        builder.Append('}');
+
+        for (int i = 0; i < containingTypes.Count; i++)
+        {
+            builder.AppendLine();
+            builder.DecrementIndentation();
+            builder.AppendIndentation();
+            builder.Append('}');
+        }
+
+        if (type.ContainingNamespace != null && type.ContainingNamespace.ConstituentNamespaces.Length > 0)
+        {
+            builder.AppendLine();
+            builder.DecrementIndentation();
+            builder.AppendIndentation();
+            builder.Append('}');
+        }
+
+        if (builder.Options.InsertFinalNewLine)
+        {
+            builder.AppendLine();
+        }
+
+        return error == false && anyReasonToEmitSourceFile ? builder.ToString() : null;
+    }
+
+    /// <summary>
+    /// Created on demand before each generation pass
+    /// </summary>
+    private sealed class SyntaxReceiver : ISyntaxReceiver
+    {
+        public List<TypeDeclarationSyntax> Candidates { get; } = [];
+
+        /// <summary>
+        /// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
+        /// </summary>
+        public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+        {
+            // any type with at least one attribute is a candidate for source generation
+            if (syntaxNode is TypeDeclarationSyntax typeDeclarationSyntax && typeDeclarationSyntax.AttributeLists.Count > 0)
+            {
+                this.Candidates.Add(typeDeclarationSyntax);
+            }
+        }
+    }
+}
diff --git a/BeaKona.AutoAsGenerator/Globals.cs b/BeaKona.AutoAsGenerator/Globals.cs
new file mode 100644
index 0000000..edc4fbc
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/Globals.cs
@@ -0,0 +1,4 @@
+global using Microsoft.CodeAnalysis;
+global using System;
+global using System.Collections.Generic;
+global using System.Linq;
diff --git a/BeaKona.AutoAsGenerator/Helpers.cs b/BeaKona.AutoAsGenerator/Helpers.cs
new file mode 100644
index 0000000..5247ac3
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/Helpers.cs
@@ -0,0 +1,26 @@
+namespace BeaKona.AutoAsGenerator;
+internal static class Helpers
+{
+    public static void ReportDiagnostic(GeneratorExecutionContext context, string id, string title, string message, string description, DiagnosticSeverity severity, SyntaxNode? node, params object?[] messageArgs)
+    {
+        Helpers.ReportDiagnostic(context, id, title, message, description, severity, node?.GetLocation(), messageArgs);
+    }
+
+    public static void ReportDiagnostic(GeneratorExecutionContext context, string id, string title, string message, string description, DiagnosticSeverity severity, ISymbol? member, params object?[] messageArgs)
+    {
+        Helpers.ReportDiagnostic(context, id, title, message, description, severity, member != null && member.Locations.Length > 0 ? member.Locations[0] : null, messageArgs);
+    }
+
+    public static void ReportDiagnostic(GeneratorExecutionContext context, string id, string title, string message, string description, DiagnosticSeverity severity, Location? location, params object?[] messageArgs)
+    {
+        var lTitle = new LocalizableResourceString(title, AutoAsResource.ResourceManager, typeof(AutoAsResource));
+        var lMessage = new LocalizableResourceString(message, AutoAsResource.ResourceManager, typeof(AutoAsResource));
+        var lDescription = new LocalizableResourceString(description, AutoAsResource.ResourceManager, typeof(AutoAsResource));
+        var category = typeof(GenerateAutoAsSourceGenerator).Namespace;
+        var link = "https://github.com/beakona/AutoAs";
+
+        var dd = new DiagnosticDescriptor(id, lTitle, lMessage, category, severity, true, lDescription, link, WellKnownDiagnosticTags.NotConfigurable);
+        var d = Diagnostic.Create(dd, location, messageArgs);
+        context.ReportDiagnostic(d);
+    }
+}
diff --git a/BeaKona.AutoAsGenerator/ICodeTextWriter.cs b/BeaKona.AutoAsGenerator/ICodeTextWriter.cs
new file mode 100644
index 0000000..b67e581
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/ICodeTextWriter.cs
@@ -0,0 +1,18 @@
+namespace BeaKona.AutoAsGenerator;
+
+internal interface ICodeTextWriter
+{
+    Compilation Compilation { get; }
+
+    void WriteTypeReference(SourceBuilder builder, ITypeSymbol type, ScopeInfo scope);
+
+    void WriteTypeArgumentsDefinition(SourceBuilder builder, IEnumerable<ITypeSymbol> typeArguments, ScopeInfo scope);
+    void WriteTypeDeclarationBeginning(SourceBuilder builder, INamedTypeSymbol type, ScopeInfo scope);
+
+    void WriteNamespaceBeginning(SourceBuilder builder, INamespaceSymbol @namespace);
+
+    void WriteHolderReference(SourceBuilder builder, ISymbol member, ScopeInfo scope);
+
+    void WriteIdentifier(SourceBuilder builder, ISymbol symbol);
+
+}
diff --git a/BeaKona.AutoAsGenerator/IEnumerableExtensions.cs b/BeaKona.AutoAsGenerator/IEnumerableExtensions.cs
new file mode 100644
index 0000000..b6625b2
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/IEnumerableExtensions.cs
@@ -0,0 +1,8 @@
+namespace BeaKona.AutoAsGenerator;
+internal static class IEnumerableExtensions
+{
+    public static HashSet<T> ToHashSet<T>(this IEnumerable<T> @this)
+    {
+        return @this != null ? new HashSet<T>(@this) : [];
+    }
+}
diff --git a/BeaKona.AutoAsGenerator/INamedTypeSymbolExtensions.cs b/BeaKona.AutoAsGenerator/INamedTypeSymbolExtensions.cs
new file mode 100644
index 0000000..05eed70
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/INamedTypeSymbolExtensions.cs
@@ -0,0 +1,23 @@
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace BeaKona.AutoAsGenerator;
+
+internal static class INamedTypeSymbolExtensions
+{
+    public static bool IsPartial(this ITypeSymbol @this)
+    {
+        foreach (SyntaxReference syntax in @this.DeclaringSyntaxReferences)
+        {
+            if (syntax.GetSyntax() is MemberDeclarationSyntax declaration)
+            {
+                if (declaration.Modifiers.Any(i => i.IsKind(SyntaxKind.PartialKeyword)))
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/BeaKona.AutoAsGenerator/INamespaceSymbolExtensions.cs b/BeaKona.AutoAsGenerator/INamespaceSymbolExtensions.cs
new file mode 100644
index 0000000..b39e248
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/INamespaceSymbolExtensions.cs
@@ -0,0 +1,16 @@
+namespace BeaKona.AutoAsGenerator;
+
+internal static class INamespaceSymbolExtensions
+{
+    public static INamespaceSymbol? FirstNonGlobalNamespace(this INamespaceSymbol @this)
+    {
+        INamespaceSymbol? last = null;
+
+        for (var n = @this; n.IsGlobalNamespace == false; n = n.ContainingNamespace)
+        {
+            last = n;
+        }
+
+        return last;
+    }
+}
diff --git a/BeaKona.AutoAsGenerator/ScopeInfo.cs b/BeaKona.AutoAsGenerator/ScopeInfo.cs
new file mode 100644
index 0000000..04ccda7
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/ScopeInfo.cs
@@ -0,0 +1,95 @@
+using System.Collections.Immutable;
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+namespace BeaKona.AutoAsGenerator;
+
+internal sealed class ScopeInfo
+{
+    public ScopeInfo(ITypeSymbol type)
+    {
+        this.Type = type;
+        this.usedTypeArguments = new HashSet<string>(ScopeInfo.AllTypeArguments(type).Select(i => i.Name));
+    }
+
+    public ScopeInfo(ScopeInfo parentScope)
+    {
+        this.Type = parentScope.Type;
+        this.usedTypeArguments = new HashSet<string>(parentScope.usedTypeArguments);
+    }
+
+    public ITypeSymbol Type { get; }
+
+    private readonly HashSet<string> usedTypeArguments;
+    private readonly Dictionary<ITypeSymbol, string> aliasTypeParameterNameByCanonicalType = new(SymbolEqualityComparer.Default);
+
+    private static ImmutableList<ITypeSymbol> AllTypeArguments(ISymbol symbol)
+    {
+        List<ITypeSymbol> types = [];
+
+        for (ISymbol s = symbol; s != null; s = s.ContainingType)
+        {
+            if (s is INamedTypeSymbol ts)
+            {
+                types.AddRange(ts.TypeArguments);
+            }
+            else if (s is IMethodSymbol m)
+            {
+                types.AddRange(m.TypeArguments);
+            }
+        }
+
+        return types.ToImmutableList();
+    }
+
+    public void CreateAliases(ImmutableArray<ITypeSymbol> typeArguments)
+    {
+        foreach (ITypeSymbol t in typeArguments)
+        {
+            if (this.aliasTypeParameterNameByCanonicalType.ContainsKey(t))
+            {
+                throw new InvalidOperationException();
+            }
+
+            if (this.usedTypeArguments.Contains(t.Name))
+            {
+                if (ScopeInfo.TrySplitAsBaseNameAndInteger(t.Name, out string? baseName, out int? index) == false)
+                {
+                    baseName = t.Name;
+                    index = 0;
+                }
+                string typeName;
+                do
+                {
+                    typeName = $"{baseName}{++index}";
+                }
+                while (this.usedTypeArguments.Contains(typeName));
+                this.aliasTypeParameterNameByCanonicalType.Add(t, typeName);
+            }
+        }
+    }
+
+    public bool TryGetAlias(ITypeSymbol symbol, /*[NotNullWhen(true)]*/ out string? alias)
+    {
+        return this.aliasTypeParameterNameByCanonicalType.TryGetValue(symbol, out alias);
+    }
+
+    private static readonly Regex rxSplitter = new(@"^\s*(?<n>\w+)(?<value>\d+)\s*$", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.CultureInvariant);
+
+    private static bool TrySplitAsBaseNameAndInteger(string name, /*[NotNullWhen(true)]*/ out string? baseName, /*[NotNullWhen(true)]*/ out int? value)
+    {
+        Match m = rxSplitter.Match(name);
+        if (m.Success)
+        {
+            baseName = m.Groups["n"].Value;
+            value = int.Parse(m.Groups["v"].Value, CultureInfo.InvariantCulture);
+            return true;
+        }
+        else
+        {
+            baseName = null;
+            value = null;
+            return false;
+        }
+    }
+}
diff --git a/BeaKona.AutoAsGenerator/SemanticFacts.cs b/BeaKona.AutoAsGenerator/SemanticFacts.cs
new file mode 100644
index 0000000..467b568
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/SemanticFacts.cs
@@ -0,0 +1,88 @@
+namespace BeaKona.AutoAsGenerator;
+
+internal static class SemanticFacts
+{
+    public static string? ResolveAssemblyAlias(Compilation compilation, IAssemblySymbol assembly)
+    {
+        //MetadataReferenceProperties.GlobalAlias
+        if (compilation.GetMetadataReference(assembly) is MetadataReference mr)
+        {
+            foreach (string alias in mr.Properties.Aliases)
+            {
+                if (string.IsNullOrEmpty(alias) == false)
+                {
+                    return alias;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public static ISymbol[] GetRelativeSymbols(ITypeSymbol type, ITypeSymbol scope)
+    {
+        ISymbol[] typeSymbols = GetContainingSymbols(type, false);
+        ISymbol[] scopeSymbols = GetContainingSymbols(scope, true);
+
+        int count = Math.Min(typeSymbols.Length, scopeSymbols.Length);
+        for (int i = 0; i < count; i++)
+        {
+            if (typeSymbols[i].Equals(scopeSymbols[i], SymbolEqualityComparer.Default) == false)
+            {
+                int remaining = typeSymbols.Length - i;
+                if (remaining > 0)
+                {
+                    ISymbol[] result = new ISymbol[remaining];
+                    Array.Copy(typeSymbols, i, result, 0, result.Length);
+                    return result;
+                }
+                else
+                {
+                    break;
+                }
+            }
+        }
+
+        return [];
+    }
+
+    public static ISymbol[] GetContainingSymbols(ITypeSymbol type, bool includeSelf)
+    {
+        List<ISymbol> symbols = [];
+
+        for (ISymbol t = includeSelf ? type : type.ContainingSymbol; t != null; t = t.ContainingSymbol)
+        {
+            if (t is IModuleSymbol || t is IAssemblySymbol)
+            {
+                break;
+            }
+            if (t is INamespaceSymbol tn && tn.IsGlobalNamespace)
+            {
+                break;
+            }
+            symbols.Insert(0, t);
+        }
+
+        return [.. symbols];
+    }
+
+    public static bool IsNullableT(Compilation compilation, INamedTypeSymbol type)
+    {
+        if (type == null)
+        {
+            throw new ArgumentNullException(nameof(type));
+        }
+
+        if (type.IsValueType)
+        {
+            if (type.IsGenericType && type.IsUnboundGenericType == false)
+            {
+                INamedTypeSymbol symbolNullableT = compilation.GetSpecialType(SpecialType.System_Nullable_T);
+
+                return symbolNullableT.ConstructUnboundGenericType().Equals(type.ConstructUnboundGenericType(), SymbolEqualityComparer.Default);
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/BeaKona.AutoAsGenerator/SourceBuilder.cs b/BeaKona.AutoAsGenerator/SourceBuilder.cs
new file mode 100644
index 0000000..790aba8
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/SourceBuilder.cs
@@ -0,0 +1,226 @@
+using System.Text;
+
+namespace BeaKona.AutoAsGenerator;
+internal sealed class SourceBuilder
+{
+    public SourceBuilder(SourceBuilderOptions options)
+    {
+        this.Options = options;
+    }
+
+    private SourceBuilder(SourceBuilder owner, SourceBuilderOptions options) : this(options)
+    {
+        this.owner = owner;
+    }
+
+    private readonly SourceBuilder? owner;
+
+    public SourceBuilderOptions Options { get; }
+
+    private readonly List<object> elements = [];
+    private readonly HashSet<string> aliases = [];
+
+    public void Clear()
+    {
+        this.elements.Clear();
+    }
+
+    public void RegisterAlias(string alias)
+    {
+        if (this.owner != null)
+        {
+            this.owner.RegisterAlias(alias);
+        }
+        else
+        {
+            this.aliases.Add(alias);
+        }
+    }
+
+    public void AppendLine() => this.elements.Add(new LineSeparatorMarker());
+
+    public void AppendLine(string? text)
+    {
+        if (text != null)
+        {
+            this.Append(text);
+        }
+        this.AppendLine();
+    }
+
+    public void AppendLine(char c)
+    {
+        this.Append(c);
+        this.AppendLine();
+    }
+
+    public void Append(string text)
+    {
+        if (text != null)
+        {
+            this.elements.Add(text);
+        }
+    }
+
+    public void Append(object? value)
+    {
+        if (value != null)
+        {
+            this.Append(value.ToString());
+        }
+    }
+
+    public void Append(char c) => this.elements.Add(c);
+
+    public void AppendSeparated(string text)
+    {
+        if (text != null)
+        {
+            this.AppendSpaceIfNecessary();
+            this.Append(text);
+        }
+    }
+
+    public void AppendSpaceIfNecessary()
+    {
+        this.elements.Add(new FlexibleSpaceMarker());
+    }
+
+    private int currentDepth = 0;
+
+    public void IncrementIndentation() => this.currentDepth++;
+
+    public void DecrementIndentation()
+    {
+        if (this.currentDepth > 0)
+        {
+            this.currentDepth--;
+        }
+    }
+
+    public void AppendIndentation()
+    {
+        this.elements.Add(new IndentationMarker(this.currentDepth));
+    }
+
+    public SourceBuilder AppendNewBuilder(bool register = true)
+    {
+        var builder = new SourceBuilder(this, this.Options);
+        if (register)
+        {
+            this.elements.Add(builder);
+        }
+        return builder;
+    }
+
+    public SourceBuilder AppendBuilder(Action<SourceBuilder> builder)
+    {
+        if (builder != null)
+        {
+            SourceBuilder sb = this.AppendNewBuilder();
+            builder(sb);
+        }
+        return this;
+    }
+
+    public override string ToString()
+    {
+        var text = new StringBuilder();
+        foreach (string alias in this.aliases.OrderByDescending(i => i))
+        {
+            text.Append("extern alias ");
+            text.Append(alias);
+            text.AppendLine(";");
+        }
+        this.Write(text);
+        return text.ToString();
+    }
+
+    private void Write(StringBuilder builder)
+    {
+        Dictionary<int, string>? cache = null;
+        foreach (object? element in this.elements)
+        {
+            if (element != null)
+            {
+                switch (element)
+                {
+                default: throw new NotSupportedException(nameof(Write));
+                case string text:
+                    builder.Append(text);
+                    break;
+                case char ch:
+                    builder.Append(ch);
+                    break;
+                case LineSeparatorMarker:
+                    builder.Append(this.Options.NewLine);
+                    break;
+                case IndentationMarker indentation:
+                {
+                    cache ??= [];
+                    int depth = indentation.Depth;
+                    if (cache.TryGetValue(depth, out string value) == false)
+                    {
+                        var sb = new StringBuilder();
+                        for (int i = 0; i < depth; i++)
+                        {
+                            sb.Append(this.Options.Indentation);
+                        }
+                        cache[depth] = value = sb.ToString();
+                    }
+                    builder.Append(value);
+                    break;
+                }
+                case FlexibleSpaceMarker:
+                {
+                    if (builder.Length > 0)
+                    {
+                        char c = builder[builder.Length - 1];
+                        switch (c)
+                        {
+                        case ' ':
+                        case '\t':
+                        case '\r':
+                        case '\n':
+                        case '(':
+                        case '[':
+                        case '{':
+                        case ')':
+                        case ']':
+                        case '}':
+                        case ';':
+                            break;
+                        default:
+                            builder.Append(' ');
+                            break;
+                        }
+                    }
+
+                    break;
+                }
+                case SourceBuilder sb:
+                    sb.Append(builder);
+                    break;
+                }
+            }
+        }
+    }
+
+    private sealed class LineSeparatorMarker
+    {
+    }
+
+    private sealed class IndentationMarker
+    {
+        public IndentationMarker(int depth)
+        {
+            this.Depth = depth;
+        }
+
+        public readonly int Depth;
+    }
+
+    private sealed class FlexibleSpaceMarker
+    {
+    }
+}
diff --git a/BeaKona.AutoAsGenerator/SourceBuilderOptions.cs b/BeaKona.AutoAsGenerator/SourceBuilderOptions.cs
new file mode 100644
index 0000000..d961d80
--- /dev/null
+++ b/BeaKona.AutoAsGenerator/SourceBuilderOptions.cs
@@ -0,0 +1,54 @@
+using System.Globalization;
+
+namespace BeaKona.AutoAsGenerator;
+
+internal sealed class SourceBuilderOptions
+{
+    public string Indentation { get; set; } = "    ";
+    public string NewLine { get; set; } = "\r\n";
+    public bool InsertFinalNewLine { get; set; } = true;
+
+    public static SourceBuilderOptions Load(GeneratorExecutionContext context, SyntaxTree? syntaxTree)
+    {
+        var builderOptions = new SourceBuilderOptions();
+
+        var configOptions = syntaxTree != null ? context.AnalyzerConfigOptions.GetOptions(syntaxTree) : context.AnalyzerConfigOptions.GlobalOptions;
+
+        var character = ' ';
+        if (configOptions.TryGetValue("indent_style", out var indentStyle))
+        {
+            character = indentStyle.Equals("space", StringComparison.OrdinalIgnoreCase) ? ' ' : '\t';
+        }
+
+        var indentSize = 4;
+        if (configOptions.TryGetValue("indent_size", out var indentSizeText) && int.TryParse(indentSizeText, NumberStyles.Integer, CultureInfo.InvariantCulture, out var indentSizeValue))
+        {
+            indentSize = indentSizeValue;
+        }
+
+        builderOptions.Indentation = new string(character, indentSize);
+
+        if (configOptions.TryGetValue("end_of_line", out var endOfLine))
+        {
+            builderOptions.NewLine = (object)(endOfLine ?? "") switch
+            {
+                "" => "\r\n",
+                "native" => "\r\n",
+                "autocrlf" => "\r\n",
+                "cr" => "\r",
+                "lf" => "\n",
+                "crlf" => "\r\n",
+                "lfcr" => "\n\r",
+                "nel" => "\u0085",
+                _ => "\r\n",
+            };
+        }
+
+        if (configOptions.TryGetValue("insert_final_newline", out var insertFinalNewline))
+        {
+            builderOptions.InsertFinalNewLine = insertFinalNewline.Equals("true", StringComparison.OrdinalIgnoreCase);
+        }
+
+        return builderOptions;
+    }
+}