Skip to content
Open
5 changes: 3 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@

**Formatting**:
- Whitespace formatting preferences are stored in the `.editorconfig` file
- When running `dotnet format whitespace` use the `--folder .` option followed by `--include <relative path to file>` to avoid a design-time build.
- `dotnet format whitespace --folder . --include <relative path to file>` - Applies formatting preferences to a particular .cs or .vb file
- When running `dotnet format whitespace` use the `--folder .` option followed by `--include <path to file>` to avoid a design-time build
- Apply formatting preferences to any modified .cs or .vb file
- **Important**: Blank lines must not contain any whitespace characters (spaces or tabs). This will cause linting errors that must be fixed.

## Code Patterns

Expand Down
5 changes: 4 additions & 1 deletion src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13906,7 +13906,10 @@ private LetClauseSyntax ParseLetClause()
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.LetKeyword);
return _syntaxFactory.LetClause(
this.EatContextualToken(SyntaxKind.LetKeyword),
this.ParseIdentifierToken(),
// If we see a keyword followed by '=', use EatTokenAsKind to produce a better error message and recover well.
SyntaxFacts.IsReservedKeyword(this.CurrentToken.Kind) && this.PeekToken(1).Kind == SyntaxKind.EqualsToken
? this.EatTokenAsKind(SyntaxKind.IdentifierToken)
: this.ParseIdentifierToken(),
this.EatToken(SyntaxKind.EqualsToken),
this.ParseExpressionCore());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2713,7 +2713,7 @@ public static void Main()
var compilation = CreateCompilationWithMscorlib40AndSystemCore(sourceCode, parseOptions: TestOptions.Script);
var tree = compilation.SyntaxTrees[0];
var semanticModel = compilation.GetSemanticModel(tree);
var queryExpr = tree.GetCompilationUnitRoot().DescendantNodes().OfType<QueryExpressionSyntax>().Where(x => x.ToFullString() == "from i in expr1 let ").Single();
var queryExpr = tree.GetCompilationUnitRoot().DescendantNodes().OfType<QueryExpressionSyntax>().Where(x => x.ToFullString() == "from i in expr1 let namespace = expr1 select i").Single();
var symbolInfo = semanticModel.GetSemanticInfoSummary(queryExpr);

Assert.Null(symbolInfo.Symbol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7330,5 +7330,50 @@ public void ObjectInitializerWithColonAndDashInsteadOfEqualsSigns()
}
EOF();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/10446")]
public void LetClauseWithKeywordAsIdentifier()
{
UsingExpression("from m in methods let params = 1 select m",
// (1,23): error CS1041: Identifier expected; 'params' is a keyword
// from m in methods let params = m.GetParameters() select m
Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "params").WithArguments("", "params").WithLocation(1, 23));

N(SyntaxKind.QueryExpression);
{
N(SyntaxKind.FromClause);
{
N(SyntaxKind.FromKeyword);
N(SyntaxKind.IdentifierToken, "m");
N(SyntaxKind.InKeyword);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "methods");
}
}
N(SyntaxKind.QueryBody);
{
N(SyntaxKind.LetClause);
{
N(SyntaxKind.LetKeyword);
M(SyntaxKind.IdentifierToken);
N(SyntaxKind.EqualsToken);
N(SyntaxKind.NumericLiteralExpression);
{
N(SyntaxKind.NumericLiteralToken, "1");
}
}
N(SyntaxKind.SelectClause);
{
N(SyntaxKind.SelectKeyword);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "m");
}
}
}
}
EOF();
}
}
}