From ac1fb3434233e8f2c707a40ec979153406b94f8f Mon Sep 17 00:00:00 2001 From: GBNikola Date: Mon, 10 Aug 2026 14:54:50 +0200 Subject: [PATCH] AVRO-4331: [csharp] Fix unreachable code in generated Get()/Put() avrogen built the Get()/Put() switch body via CodeSnippetExpression, which CodeDom wraps in a CodeExpressionStatement and always suffixes with a ";". Since every switch arm returns or throws, that trailing ";" is unreachable. Roslyn stays quiet about it, but IDE analyzers with fuller flow analysis (ReSharper/Rider) flag it, forcing consumers to suppress CS0162 for all avrogen output. Use CodeSnippetStatement instead, which emits the block verbatim with no appended semicolon. --- .../csharp/src/apache/main/CodeGen/CodeGen.cs | 8 +++--- .../src/apache/test/CodGen/CodeGenTest.cs | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lang/csharp/src/apache/main/CodeGen/CodeGen.cs b/lang/csharp/src/apache/main/CodeGen/CodeGen.cs index 73b95852d7b..36c83a98496 100644 --- a/lang/csharp/src/apache/main/CodeGen/CodeGen.cs +++ b/lang/csharp/src/apache/main/CodeGen/CodeGen.cs @@ -874,15 +874,15 @@ protected virtual CodeTypeDeclaration processRecord(Schema schema) // end switch block for Get() getFieldStmt.AppendLine("\t\t\tdefault: throw new global::Avro.AvroRuntimeException(\"Bad index \" + fieldPos + \" in Get()\");") .Append("\t\t\t}"); - var cseGet = new CodeSnippetExpression(getFieldStmt.ToString()); - cmmGet.Statements.Add(cseGet); + var cssGet = new CodeSnippetStatement(getFieldStmt.ToString()); + cmmGet.Statements.Add(cssGet); ctd.Members.Add(cmmGet); // end switch block for Put() putFieldStmt.AppendLine("\t\t\tdefault: throw new global::Avro.AvroRuntimeException(\"Bad index \" + fieldPos + \" in Put()\");") .Append("\t\t\t}"); - var csePut = new CodeSnippetExpression(putFieldStmt.ToString()); - cmmPut.Statements.Add(csePut); + var cssPut = new CodeSnippetStatement(putFieldStmt.ToString()); + cmmPut.Statements.Add(cssPut); ctd.Members.Add(cmmPut); string nspace = recordSchema.Namespace; diff --git a/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs b/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs index 33c7f0cf6ee..f10f11df8dd 100644 --- a/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs +++ b/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs @@ -110,6 +110,32 @@ public void GetTypesShouldReturnTypes() Assert.That(Regex.Matches(planetEnumCode, "public enum PlanetEnum").Count, Is.EqualTo(1)); } + [Test] + public void RecordGetAndPutSwitchesShouldNotEmitUnreachableStatement() + { + AddSchema(@" +{ + ""name"": ""Sample"", + ""namespace"": ""Avro.Test.CodeGen.UnreachableCodeRegression"", + ""type"": ""record"", + ""fields"": [ + { ""name"": ""value"", ""type"": ""string"" } + ] +} +"); + GenerateCode(); + var types = GetTypes(); + bool hasSampleCode = types.TryGetValue("Sample", out string sampleCode); + Assert.That(hasSampleCode); + + // The exhaustive switch in Get()/Put() must not be followed by a stray ";" - + // that empty statement is unreachable (every case returns or throws) and + // trips IDE-only unreachable-code analysis (e.g. ReSharper) even though Roslyn + // does not flag it. + Assert.That(Regex.IsMatch(sampleCode, @"\}\s*;\s*\}\s*public virtual void Put"), Is.False); + Assert.That(Regex.IsMatch(sampleCode, @"in Put\(\)""\);\s*\}\s*;"), Is.False); + } + [Test] public void EnumWithKeywordSymbolsShouldHavePrefixedSymbols() {