Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 16, 2025

  • Understand the issue: await keyword not recognized in interpolated strings with parentheses (e.g., $"{await (Task.FromResult(string.Empty))}")
  • Analyze the code: IsInAsync context is not passed to temp parser for interpolation expression parsing
  • Create test cases to reproduce the issue
  • Fix LanguageParser_InterpolatedString.cs to pass IsInAsync to ParseInterpolation
  • Create AsyncContext helper similar to FieldKeywordContext in LanguageParser.cs
  • Build and test the fix - tests passing
  • Run existing async parsing tests (64 tests) - all passing
  • Run interpolated string expression tests - all passing
  • Run full syntax test suite (9731 tests) - all passing
  • Code review completed and addressed
  • Address review feedback: rename temps, use raw strings, add WorkItem, simplify tests

Summary

Fixed the parser to properly recognize the await keyword inside interpolated string expressions when the awaited expression is wrapped in parentheses. The fix involves:

  1. Passing the IsInAsync context from the outer parser to the temp parser created for interpolation expressions
  2. Creating a new AsyncContext struct that follows the same pattern as FieldKeywordContext to manage the async state

Files Changed

  • src/Compilers/CSharp/Portable/Parser/LanguageParser.cs: Added AsyncContext struct
  • src/Compilers/CSharp/Portable/Parser/LanguageParser_InterpolatedString.cs: Updated to pass and use IsInAsync
  • src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs: Added two simplified tests using raw string literals

Fixes #39149

Original prompt

This section details on the original issue you should resolve

<issue_title>Why 'await' causing compiler error when that in an interpolated string?</issue_title>
<issue_description>The following code cannot be compiled:

        private static async Task Main()
        {
            // compiler error CS0103 : The name 'await' does not exist in the current context
            var z = $"{await (Task.FromResult(string.Empty))}";

            // allowed
            var a = await Task.FromResult(string.Empty);
            var b = await (Task.FromResult(string.Empty));
            var c = $"{await Task.FromResult(string.Empty)}";

            Console.ReadLine();
        }
```</issue_description>

<agent_instructions>Copilot, this is likely in: src\Compilers\CSharp\Portable\Parser\LanguageParser_InterpolatedString.cs

note how the appropriate information is passed along such that we can determine if we're a 'FieldKeywordContext' when parsing interpolation contents.  We likely need to pass along the same information to state if IsInAsync is true for the parser.

Tests for this can go in AsyncParsingTests</agent_instructions>

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

<comments>
<comment_new><author>@CyrusNajmabadi</author><body>
Sounds like a bug to me.  Likely a compiler issue where the lexer/parser get confused as to what's inside the interpolation.  </body></comment_new>
</comments>

Fixes #39149


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot AI and others added 2 commits October 16, 2025 15:19
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix compiler error for 'await' in interpolated string Fix await keyword recognition in interpolated strings with parentheses Oct 16, 2025
Copilot AI requested a review from CyrusNajmabadi October 16, 2025 15:27
Copilot finished work on behalf of CyrusNajmabadi October 16, 2025 15:27
Comment on lines 376 to 378
using var _ = new FieldKeywordContext(tempParser, isInFieldKeywordContext);
using var __ = new AsyncContext(tempParser, isInAsync);
Copy link
Member

Choose a reason for hiding this comment

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

@copilot rename these to _1 and _2 as that is the normal patten we use when having multiple unnamed temps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 8c24cdc - renamed to _1 and _2.

public void AwaitInInterpolatedString_WithParentheses()
{
UsingTree(@"
class C
Copy link
Member

Choose a reason for hiding this comment

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

@copilot please use a raw string literals for all the tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 8c24cdc - using raw string literals with triple quotes.

Comment on lines 2834 to 2835
[Fact]
// Tests that await keyword is recognized in interpolated strings with parentheses
Copy link
Member

Choose a reason for hiding this comment

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

@copilot please remove superflous comment. And please add an appropriate [WorkItem("url")] attribute to al the tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 8c24cdc - removed comments and added [WorkItem("https://github.com/dotnet/roslyn/issues/39149")].

{
async System.Threading.Tasks.Task M()
{
var z = $""{await (System.Threading.Tasks.Task.FromResult(string.Empty))}"";
Copy link
Member

Choose a reason for hiding this comment

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

@copilot please simplify the test as it is only testing syntax. First, you an use UsingStatement to just test the var v = $"..."; line. second, the code inside the interpolation can be simpler, without such a complex expression inside the parenthesized region. This applies to both tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 8c24cdc - simplified to minimal async method with just the interpolated string statement. Used UsingTree with simplified expression await (M()) and await M() inside the interpolation.

- Rename _ and __ to _1 and _2 per naming convention
- Use raw string literals for test code
- Add WorkItem attributes with issue URL
- Simplify tests to minimal async method with interpolated string

Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
Copilot finished work on behalf of CyrusNajmabadi October 16, 2025 15:44
Copilot AI requested a review from CyrusNajmabadi October 16, 2025 15:44
@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review October 17, 2025 17:10
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner October 17, 2025 17:10
@CyrusNajmabadi
Copy link
Member

@dotnet/roslyn-compiler ptal.

@CyrusNajmabadi
Copy link
Member

@dotnet/roslyn-compiler ptal.

@CyrusNajmabadi
Copy link
Member

@jjonescz can you ptal?

@CyrusNajmabadi
Copy link
Member

@RikkiGibson could you ptal?

using var tempParser = new LanguageParser(tempLexer, oldTree: null, changes: null);
using var _ = new FieldKeywordContext(tempParser, isInFieldKeywordContext);
using var _1 = new FieldKeywordContext(tempParser, isInFieldKeywordContext);
using var _2 = new AsyncContext(tempParser, isInAsync);
Copy link
Member

Choose a reason for hiding this comment

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

It looks like this does address the current bug, but, I am wondering if a more "holistic" way of forwarding state from a parent to child parser instance is warranted here. Or, at least something which forces the developer to make an explicit decision on whether/how to forward the state here, when state fields used by the parser are added/changed.

It seems likely that any expression whose parsing depends on fields in SyntaxFactoryContext, may behave incorrectly inside interpolations, if it is not forwarded here.

Copy link
Member

@RikkiGibson RikkiGibson left a comment

Choose a reason for hiding this comment

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

LGTM, I think it will be good to follow up and fix parsing state handling for interpolations more categorically.

@CyrusNajmabadi
Copy link
Member

LGTM, I think it will be good to follow up and fix parsing state handling for interpolations more categorically.

Agreed. Will try an immediate followup.

@CyrusNajmabadi CyrusNajmabadi merged commit b1964bb into main Nov 5, 2025
24 checks passed
@CyrusNajmabadi CyrusNajmabadi deleted the copilot/fix-await-in-interpolated-string branch November 5, 2025 01:06
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Nov 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Why 'await' causing compiler error when that in an interpolated string?

4 participants