From 6df22b1c8773f7bfdff8b4357b8d86f122b82e05 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Tue, 3 Mar 2026 07:24:52 +0000 Subject: [PATCH] Fix: Handle empty namespace in CodeWriter to prevent 'global::.' output When a CSharpType has an empty namespace (which can occur when Roslyn cannot resolve a type reference, such as in Custom code that inherits from generated types), the CodeWriter was outputting 'global::.' followed by the type name, resulting in invalid C# syntax. This fix adds a check to only append the namespace and dot separator when the namespace is not empty. Fixes https://github.com/microsoft/typespec/issues/9880 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/Writers/CodeWriter.cs | 7 ++- .../test/Writers/CodeWriterTests.cs | 49 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Writers/CodeWriter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Writers/CodeWriter.cs index f57b5435a4d..24156f94b49 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Writers/CodeWriter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Writers/CodeWriter.cs @@ -658,8 +658,11 @@ private void AppendType(CSharpType type, bool isDeclaration, bool writeTypeNameO UseNamespace(type.Namespace); AppendRaw("global::"); - AppendRaw(type.Namespace); - AppendRaw("."); + if (!string.IsNullOrEmpty(type.Namespace)) + { + AppendRaw(type.Namespace); + AppendRaw("."); + } if (type.DeclaringType is not null) AppendRaw($"{type.DeclaringType.Name}."); AppendRaw(type.Name); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Writers/CodeWriterTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Writers/CodeWriterTests.cs index 6fec1f24139..cfa5e02c23e 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Writers/CodeWriterTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Writers/CodeWriterTests.cs @@ -727,5 +727,54 @@ public void CodeWriter_WriteMethod_AbstractMethodWithoutBody() var result = codeWriter.ToString(false); Assert.AreEqual(expected, result); } + + // Regression test for https://github.com/microsoft/typespec/issues/9880 + // When a CSharpType has an empty namespace (can happen when Roslyn cannot resolve the type), + // the CodeWriter should not output "global::." (global prefix followed by empty namespace and dot). + [Test] + public void TypeWithEmptyNamespaceDoesNotOutputGlobalDot() + { + // Create a CSharpType with an empty namespace (simulating an unresolved error type) + var typeWithEmptyNamespace = new CSharpType( + "UnresolvedType", + "", // empty namespace + isValueType: false, + isNullable: false, + declaringType: null, + args: [], + isPublic: true, + isStruct: false); + + using var writer = new CodeWriter(); + writer.Append($"{typeWithEmptyNamespace}"); + var result = writer.ToString(false); + + // Should contain "global::UnresolvedType" (without extra dot after global::) + Assert.That(result, Does.Contain("global::UnresolvedType"), + "Type with empty namespace should be prefixed with 'global::' directly followed by the type name"); + } + + // Verifies that types with proper namespaces still work correctly + [Test] + public void TypeWithNamespaceOutputsCorrectly() + { + var typeWithNamespace = new CSharpType( + "ResolvedType", + "Sample.Models", + isValueType: false, + isNullable: false, + declaringType: null, + args: [], + isPublic: true, + isStruct: false); + + using var writer = new CodeWriter(); + writer.Append($"{typeWithNamespace}"); + var result = writer.ToString(false); + + // Should contain the full qualified name + Assert.That(result, Does.Contain("global::Sample.Models.ResolvedType"), + "Type with namespace should include the full namespace in output"); + } } }