未验证 提交 10289128 编写于 作者: M msftbot[bot] 提交者: GitHub

Merge pull request #42276 from CyrusNajmabadi/dotDotErrorRecovery

Recover better when seeing .. appear in the middle of a qualified name.
......@@ -5515,7 +5515,8 @@ private NameSyntax ParseQualifiedName(NameOptions options = NameOptions.None)
{
NameSyntax name = this.ParseAliasQualifiedName(options);
while (this.IsDotOrColonColon())
// Handle .. tokens for error recovery purposes.
while (this.IsDotOrColonColon() || this.CurrentToken.Kind == SyntaxKind.DotDotToken)
{
if (this.PeekToken(1).Kind == SyntaxKind.ThisKeyword)
{
......@@ -5534,46 +5535,59 @@ private NameSyntax ParseQualifiedName(NameOptions options = NameOptions.None)
NameSyntax left,
SyntaxToken separator)
{
Debug.Assert(
separator.Kind == SyntaxKind.DotToken ||
separator.Kind == SyntaxKind.DotDotToken ||
separator.Kind == SyntaxKind.ColonColonToken);
var right = this.ParseSimpleName(options);
if (separator.Kind == SyntaxKind.DotToken)
switch (separator.Kind)
{
return _syntaxFactory.QualifiedName(left, separator, right);
}
else if (separator.Kind == SyntaxKind.ColonColonToken)
{
if (left.Kind != SyntaxKind.IdentifierName)
{
separator = this.AddError(separator, ErrorCode.ERR_UnexpectedAliasedName, separator.ToString());
}
case SyntaxKind.DotToken:
return _syntaxFactory.QualifiedName(left, separator, right);
case SyntaxKind.DotDotToken:
// Error recovery. If we have `X..Y` break that into `X.<missing-id>.Y`
// If the left hand side is not an identifier name then the user has done
// something like Goo.Bar::Blah. We've already made an error node for the
// ::, so just pretend that they typed Goo.Bar.Blah and continue on.
var leftDot = SyntaxFactory.Token(separator.LeadingTrivia.Node, SyntaxKind.DotToken, null);
var missingName = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_IdentifierExpected);
var rightDot = SyntaxFactory.Token(null, SyntaxKind.DotToken, separator.TrailingTrivia.Node);
var identifierLeft = left as IdentifierNameSyntax;
if (identifierLeft == null)
{
separator = this.ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken);
return _syntaxFactory.QualifiedName(left, separator, right);
}
else
{
if (identifierLeft.Identifier.ContextualKind == SyntaxKind.GlobalKeyword)
return _syntaxFactory.QualifiedName(
_syntaxFactory.QualifiedName(left, leftDot, missingName),
rightDot, right);
case SyntaxKind.ColonColonToken:
if (left.Kind != SyntaxKind.IdentifierName)
{
identifierLeft = _syntaxFactory.IdentifierName(ConvertToKeyword(identifierLeft.Identifier));
separator = this.AddError(separator, ErrorCode.ERR_UnexpectedAliasedName, separator.ToString());
}
identifierLeft = CheckFeatureAvailability(identifierLeft, MessageID.IDS_FeatureGlobalNamespace);
// If the left hand side is not an identifier name then the user has done
// something like Goo.Bar::Blah. We've already made an error node for the
// ::, so just pretend that they typed Goo.Bar.Blah and continue on.
// If the name on the right had errors or warnings then we need to preserve
// them in the tree.
return WithAdditionalDiagnostics(_syntaxFactory.AliasQualifiedName(identifierLeft, separator, right), left.GetDiagnostics());
}
}
else
{
return left;
var identifierLeft = left as IdentifierNameSyntax;
if (identifierLeft == null)
{
separator = this.ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken);
return _syntaxFactory.QualifiedName(left, separator, right);
}
else
{
if (identifierLeft.Identifier.ContextualKind == SyntaxKind.GlobalKeyword)
{
identifierLeft = _syntaxFactory.IdentifierName(ConvertToKeyword(identifierLeft.Identifier));
}
identifierLeft = CheckFeatureAvailability(identifierLeft, MessageID.IDS_FeatureGlobalNamespace);
// If the name on the right had errors or warnings then we need to preserve
// them in the tree.
return WithAdditionalDiagnostics(_syntaxFactory.AliasQualifiedName(identifierLeft, separator, right), left.GetDiagnostics());
}
default:
throw ExceptionUtilities.Unreachable;
}
}
......
......@@ -7190,5 +7190,193 @@ class C<T> where T : struct? {}
}
EOF();
}
[Fact, WorkItem(35236, "https://github.com/dotnet/roslyn/issues/35236")]
public void TestNamespaceWithDotDot1()
{
var text = @"namespace a..b { }";
var tree = UsingNode(
text, TestOptions.Regular7_3,
// (1,13): error CS1001: Identifier expected
// namespace a..b { }
Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(1, 13));
// verify that we can roundtrip
Assert.Equal(text, tree.ToFullString());
N(SyntaxKind.CompilationUnit);
{
N(SyntaxKind.NamespaceDeclaration);
{
N(SyntaxKind.NamespaceKeyword);
N(SyntaxKind.QualifiedName);
{
N(SyntaxKind.QualifiedName);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "a");
}
N(SyntaxKind.DotToken);
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
}
N(SyntaxKind.DotToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "b");
}
}
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
N(SyntaxKind.EndOfFileToken);
}
EOF();
}
[Fact, WorkItem(35236, "https://github.com/dotnet/roslyn/issues/35236")]
public void TestNamespaceWithDotDot2()
{
var text = @"namespace a
..b { }";
var tree = UsingNode(
text, TestOptions.Regular7_3,
// (2,22): error CS1001: Identifier expected
// ..b { }
Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(2, 22));
// verify that we can roundtrip
Assert.Equal(text, tree.ToFullString());
N(SyntaxKind.CompilationUnit);
{
N(SyntaxKind.NamespaceDeclaration);
{
N(SyntaxKind.NamespaceKeyword);
N(SyntaxKind.QualifiedName);
{
N(SyntaxKind.QualifiedName);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "a");
}
N(SyntaxKind.DotToken);
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
}
N(SyntaxKind.DotToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "b");
}
}
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
N(SyntaxKind.EndOfFileToken);
}
EOF();
}
[Fact, WorkItem(35236, "https://github.com/dotnet/roslyn/issues/35236")]
public void TestNamespaceWithDotDot3()
{
var text = @"namespace a..
b { }";
var tree = UsingNode(
text, TestOptions.Regular7_3,
// (1,13): error CS1001: Identifier expected
// namespace a..
Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(1, 13));
// verify that we can roundtrip
Assert.Equal(text, tree.ToFullString());
N(SyntaxKind.CompilationUnit);
{
N(SyntaxKind.NamespaceDeclaration);
{
N(SyntaxKind.NamespaceKeyword);
N(SyntaxKind.QualifiedName);
{
N(SyntaxKind.QualifiedName);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "a");
}
N(SyntaxKind.DotToken);
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
}
N(SyntaxKind.DotToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "b");
}
}
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
N(SyntaxKind.EndOfFileToken);
}
EOF();
}
[Fact, WorkItem(35236, "https://github.com/dotnet/roslyn/issues/35236")]
public void TestNamespaceWithDotDot4()
{
var text = @"namespace a
..
b { }";
var tree = UsingNode(
text, TestOptions.Regular7_3,
// (2,22): error CS1001: Identifier expected
// ..
Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(2, 22));
// verify that we can roundtrip
Assert.Equal(text, tree.ToFullString());
N(SyntaxKind.CompilationUnit);
{
N(SyntaxKind.NamespaceDeclaration);
{
N(SyntaxKind.NamespaceKeyword);
N(SyntaxKind.QualifiedName);
{
N(SyntaxKind.QualifiedName);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "a");
}
N(SyntaxKind.DotToken);
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
}
N(SyntaxKind.DotToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "b");
}
}
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
N(SyntaxKind.EndOfFileToken);
}
EOF();
}
}
}
......@@ -44,6 +44,26 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
End Using
End Function
<WorkItem(35236, "https://github.com/dotnet/roslyn/issues/35236")>
<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestBetweenTwoDotsInNamespaceName() As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document>
namespace N.O.P
{
}
namespace N$$.P
{
}
</Document>)
state.SendTypeChars(".")
Await state.AssertCompletionSession()
Await state.AssertSelectedCompletionItem(displayText:="O", isHardSelected:=False)
End Using
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestAtEndOfFile() As Task
Using state = TestStateFactory.CreateCSharpTestState(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册