Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 18, 2025

The source generator accepts invalid C# identifiers in [DurableTask(name)] attributes (e.g., "Foo.Bar", "My-Task"), generating invalid code like CallFoo.BarAsync().

Changes

  • Validation: Added IsValidCSharpIdentifier() using Roslyn's SyntaxFacts.IsValidIdentifier to check task and event names
  • Diagnostics: Report DURABLE1001 (tasks) and DURABLE1002 (events) errors for invalid identifiers, with precise source locations
  • Code generation: Skip generating methods for invalid names to prevent compilation errors
  • Type tracking: Extended DurableTaskTypeInfo and DurableEventTypeInfo to capture name locations for diagnostic reporting

Example

// Before: Generates invalid code `CallFoo.BarAsync()`
[DurableTask("Foo.Bar")]
class FooBarActivity : TaskActivity<int, string> { }

// After: Reports error DURABLE1001
// error DURABLE1001: The task name 'Foo.Bar' is not a valid C# identifier. 
// Task names must start with a letter or underscore and contain only letters, digits, and underscores.

Follows the restrictive approach discussed in the issue rather than attempting name sanitization.

Original prompt

This section details on the original issue you should resolve

<issue_title>Source generator should sanitize method names</issue_title>
<issue_description>If the "name" value of the [DurableTask(name)] attribute isn't a valid C# identifier, the source generator will still generate a method using this invalid name.

For example, defining an activity like this:

[DurableTask("Foo.Bar")]
class FooBarActivity : TaskActivityBase<object, object>
{
    // ...
}

Results in generated output like this, which doesn't compile:

public static Task<object> CallFoo.BarAsync(this TaskOrchestrationContext ctx, object input, TaskOptions? options = null)
{
    return ctx.CallActivityAsync<object>("Foo.Bar", input, options);
}

Instead, the method name should be normalized into something valid, like this:

public static Task<object> CallFooBarAsync(this TaskOrchestrationContext ctx, object input, TaskOptions? options = null)
{
    return ctx.CallActivityAsync<object>("Foo.Bar", input, options);
}
```</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
<comment_new><author>@jviau</author><body>
How do you propose this behaves with a conflict of `[DurableTask("Foo.Bar")]` and `[DurableTask("FooBar")]`?</body></comment_new>
<comment_new><author>@cgillum</author><body>
Maybe use an underscore instead of stripping out the dot?

Or, perhaps what we should be doing is issuing an error saying that the value needs to meet the rules for valid C# identifiers rather than trying to normalize?</body></comment_new>
<comment_new><author>@jviau</author><body>
I don't have much experience with name sanitization like this. But an underscore could work. What do we do for `"Foo_Bar"` then? Double underscore?

I am also fine with being more restrictive. It may be a safer route anyways for any unforeseen issues in the future.</body></comment_new>
</comments>

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

- Add DiagnosticDescriptors for invalid task and event names
- Add IsValidCSharpIdentifier helper using SyntaxFacts.IsValidIdentifier
- Update DurableTaskTypeInfo and DurableEventTypeInfo to track name locations
- Report diagnostics for invalid identifiers and skip code generation
- Add comprehensive tests for various invalid identifier scenarios
- All 59 tests passing

Co-authored-by: YunchuWang <[email protected]>
Comment on lines +292 to +300
foreach (DurableTaskTypeInfo task in allTasks)
{
if (!IsValidCSharpIdentifier(task.TaskName))
{
Location location = task.TaskNameLocation ?? Location.None;
Diagnostic diagnostic = Diagnostic.Create(InvalidTaskNameRule, location, task.TaskName);
context.ReportDiagnostic(diagnostic);
}
}
Comment on lines +303 to +311
foreach (DurableEventTypeInfo eventInfo in allEvents)
{
if (!IsValidCSharpIdentifier(eventInfo.EventName))
{
Location location = eventInfo.EventNameLocation ?? Location.None;
Diagnostic diagnostic = Diagnostic.Create(InvalidEventNameRule, location, eventInfo.EventName);
context.ReportDiagnostic(diagnostic);
}
}
Comment on lines +348 to +354
foreach (DurableEventTypeInfo eventInfo in allEvents)
{
if (IsValidCSharpIdentifier(eventInfo.EventName))
{
validEvents.Add(eventInfo);
}
}
Copilot AI changed the title [WIP] Fix source generator to sanitize method names Validate C# identifiers in DurableTask source generator Dec 18, 2025
Copilot AI requested a review from YunchuWang December 18, 2025 23:40
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.

Source generator should sanitize method names

2 participants