Skip to content

Add try-catch-finally code generation support#10

Merged
fengb3 merged 3 commits into
mainfrom
copilot/add-try-catch-finally-support
Apr 15, 2026
Merged

Add try-catch-finally code generation support#10
fengb3 merged 3 commits into
mainfrom
copilot/add-try-catch-finally-support

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 15, 2026

The library had no support for generating try/catch/finally blocks. This adds first-class options for all three clauses, consistent with the existing control-flow option pattern (IfOption, LoopOption, etc.).

New types (TryCatchFinallyOption.cs)

  • TryOption — renders the try { } block; owns its catch/finally clauses internally so the entire construct is encapsulated in a single child added to the parent
  • CatchOption — supports bare catch, catch (Type), catch (Type var), and catch (Type var) when (filter)
  • FinallyOption — renders the finally { } block
  • TryCatchFinallyOptionExtensions — fluent .Catch(), .Finally(), .WithExceptionType(), .WithVariableName(), .WithWhenFilter()

Extension on MethodOption

Added MethodOption.Try(configure) to integrate naturally with the existing fluent method-building API.

Usage

new MethodOption()
    .WithKeyword("public")
    .WithName("Run")
    .WithReturnType("void")
    .Try(@try =>
    {
        @try.AppendLine("DoWork();");
        @try.Catch(c =>
        {
            c.WithExceptionType("Exception")
             .WithVariableName("ex")
             .WithWhenFilter("ex.Message.Contains(\"timeout\")")
             .AppendLine("HandleTimeout(ex);");
        });
        @try.Finally(f => f.AppendLine("Cleanup();"));
    })
    .Build();

Generates:

public void Run()
{
  try
  {
    DoWork();
  }
  catch (Exception ex) when (ex.Message.Contains("timeout"))
  {
    HandleTimeout(ex);
  }
  finally
  {
    Cleanup();
  }
}

Copilot AI linked an issue Apr 15, 2026 that may be closed by this pull request
Agent-Logs-Url: https://github.com/fengb3/EasyCodeBuilder/sessions/6e5dcf18-3a2b-4da1-a078-791854f18069

Co-authored-by: fengb3 <45181245+fengb3@users.noreply.github.com>
Copilot AI changed the title [WIP] Add try-catch-finally support in syntax Add try-catch-finally code generation support Apr 15, 2026
Copilot AI requested a review from fengb3 April 15, 2026 08:55
@fengb3 fengb3 marked this pull request as ready for review April 15, 2026 09:07
Copilot AI review requested due to automatic review settings April 15, 2026 09:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class C# code generation support for try/catch/finally blocks, integrating it into the existing option-based/fluent builder API used throughout Fengb3.EasyCodeBuilder.Csharp.

Changes:

  • Introduces TryOption, CatchOption, FinallyOption, plus fluent extension methods for configuring catch signatures and filters.
  • Extends MethodOption with .Try(...) to add a try statement into a generated method body.
  • Adds xUnit tests covering common try/catch/finally generation scenarios.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
EasyCodeBuilder/Csharp/TryCatchFinallyOption.cs Implements the new try/catch/finally option types and fluent configuration extensions.
EasyCodeBuilder/Csharp/MethodOption.cs Adds MethodOption.Try(...) to integrate try/catch/finally into method generation.
EasyCodeBuilder.Test/Csharp/TryCatchFinallyTests.cs Adds test coverage for generated try/catch/finally output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +21
BeforeChildren?.Invoke(cb);
cb.CodeBlock(OnChildren, "try");
_clauses?.Invoke(cb);
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TryOption.Build can emit a try { ... } block with no catch or finally when no clauses are added (_clauses is null). In C#, a try statement must have at least one catch or a finally, so this output can never be valid. Consider validating in Build (e.g., throw an InvalidOperationException with a clear message) or otherwise ensuring at least one clause is present before rendering.

Suggested change
BeforeChildren?.Invoke(cb);
cb.CodeBlock(OnChildren, "try");
_clauses?.Invoke(cb);
if (_clauses is null)
{
throw new InvalidOperationException("A try statement must include at least one catch or finally clause.");
}
BeforeChildren?.Invoke(cb);
cb.CodeBlock(OnChildren, "try");
_clauses.Invoke(cb);

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +28
private CodeRenderFragment? _clauses;

/// <summary>
/// 构建代码
/// </summary>
/// <param name="cb">代码构建器</param>
/// <returns>代码构建器</returns>
public override CodeBuilder Build(CodeBuilder cb)
{
BeforeChildren?.Invoke(cb);
cb.CodeBlock(OnChildren, "try");
_clauses?.Invoke(cb);
return cb;
}

internal void AddClause(CodeRenderFragment clause)
{
_clauses += clause;
}
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clauses are stored via a multicast delegate (_clauses += clause), which makes it easy to generate invalid C# such as multiple finally blocks or catch clauses appearing after a finally (depending on call order). If this API is meant to generate valid C# by construction, consider storing catches in a list plus an optional single FinallyOption, and always render finally last (or throw if Catch() is called after Finally()).

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +125
public static TryOption Catch(this TryOption tryOption, Action<CatchOption> configure)
{
var catchOption = new CatchOption();
configure(catchOption);
tryOption.AddClause(catchOption.Build);
return tryOption;
}

/// <summary>
/// add a finally clause to the try block
/// </summary>
/// <param name="tryOption">try 语句选项</param>
/// <param name="configure">finally 语句配置委托</param>
/// <returns>try 语句选项</returns>
public static TryOption Finally(this TryOption tryOption, Action<FinallyOption> configure)
{
var finallyOption = new FinallyOption();
configure(finallyOption);
tryOption.AddClause(finallyOption.Build);
return tryOption;
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catch(...) and Finally(...) invoke configure(...) directly. For consistency with the existing AddChild helpers (which tolerate null delegates), consider using a null-conditional invoke or explicitly throwing ArgumentNullException so the behavior is intentional and consistent across the fluent API surface.

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +35
[Fact]
public void TryOnly()
{
var code = new MethodOption()
.WithKeyword("public")
.WithName("Run")
.WithReturnType("void")
.Try(@try =>
{
@try.AppendLine("DoWork();");
})
.Build();

var expected = """
public void Run()
{
try
{
DoWork();
}
}
""";

Assert.Equal(Norm(expected), Norm(code));
output.WriteLine(code);
}
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test asserts that generating a try without any catch/finally is supported. Since try { } without at least one clause is not valid C#, this test will need to be updated/removed if TryOption is changed to enforce valid syntax (recommended).

Copilot uses AI. Check for mistakes.
@fengb3
Copy link
Copy Markdown
Owner

fengb3 commented Apr 15, 2026

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 15, 2026

@copilot apply changes based on the comments in this thread

Applied all four review changes in e3f2c34:

  1. Structured clause storage — replaced the multicast delegate with List<CatchOption> + optional FinallyOption? in TryOption, so order is always try → catches → finally regardless of call sequence.
  2. Build() validation — throws InvalidOperationException if no catch or finally clause is present.
  3. Invalid-ordering guardsAddCatch() throws if called after SetFinally(); SetFinally() throws if called more than once.
  4. Null configure guardsCatch() and Finally() throw ArgumentNullException for null delegates.
  5. Tests updated — removed the invalid TryOnly test; added TryWithoutCatchOrFinallyThrows, CatchAfterFinallyThrows, MultipleFinallyThrows, NullCatchConfigureThrows, and NullFinallyConfigureThrows.

@fengb3 fengb3 merged commit 0bb737f into main Apr 15, 2026
4 checks passed
@fengb3 fengb3 deleted the copilot/add-try-catch-finally-support branch April 15, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

try-catch-finally support

3 participants