/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Syntax/LexicalAndXml/XmlDocCommentTests.cs
3 422 строки
148 KB
Brice Lambson
Fix SyntaxFactory.XmlSeeAlsoElement href overloads (#75052)
17 сен 2024, 19:40
Не верифицирован
17 сен 2024, 19:40
6a59e0f
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; using System; using System.Threading; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class XmlDocCommentTests : CSharpTestBase { private CSharpParseOptions GetOptions(string[] defines) { return new CSharpParseOptions( languageVersion: LanguageVersion.CSharp3, documentationMode: DocumentationMode.Diagnose, preprocessorSymbols: defines); } private SyntaxTree Parse(string text, params string[] defines) { var options = this.GetOptions(defines); var itext = SourceText.From(text); return SyntaxFactory.ParseSyntaxTree(itext, options); } [Fact] public void DocCommentWriteException() { var comp = CreateCompilation(@" /// <summary> /// Doc comment for <see href=""C"" /> /// </summary> public class C { /// <summary> /// Doc comment for method M /// </summary> public void M() { } }"); using (new EnsureEnglishUICulture()) { var diags = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); var badStream = new BrokenStream(); badStream.BreakHow = BrokenStream.BreakHowType.ThrowOnWrite; DocumentationCommentCompiler.WriteDocumentationCommentXml( comp, null, badStream, diags, default(CancellationToken)); diags.DiagnosticBag.Verify( // error CS1569: Error writing to XML documentation file: I/O error occurred. Diagnostic(ErrorCode.ERR_DocFileGen).WithArguments("I/O error occurred.").WithLocation(1, 1)); diags.Free(); } } [ClrOnlyFact] public void TestEmptyElementNoAttributes() { var text = "/// <goo />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.LeadingTrivia; Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); } [WorkItem(537500, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537500")] [Fact] public void TestFourOrMoreSlashesIsNotXmlComment() { var text = "//// <goo />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.LeadingTrivia; Assert.Equal(1, leading.Count); Assert.Equal(SyntaxKind.SingleLineCommentTrivia, leading[0].Kind()); Assert.Equal(text, leading[0].ToFullString()); } [WorkItem(537500, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537500")] [Fact] public void TestFourOrMoreSlashesInsideXmlCommentIsNotXmlComment() { var text = @"/// <goo> //// </goo> "; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.NotEqual(0, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.LeadingTrivia; Assert.Equal(3, leading.Count); Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, leading[0].Kind()); Assert.Equal(SyntaxKind.SingleLineCommentTrivia, leading[1].Kind()); } [WorkItem(537500, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537500")] [Fact] public void TestThreeOrMoreAsterisksIsNotXmlComment() { var text = "/*** <goo /> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.LeadingTrivia; Assert.Equal(1, leading.Count); Assert.Equal(SyntaxKind.MultiLineCommentTrivia, leading[0].Kind()); Assert.Equal(text, leading[0].ToFullString()); } [Fact] public void TestEmptyElementNoAttributesPrecedingClass() { var text = @"/// <goo /> class C { }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); Assert.Equal(SyntaxKind.ClassDeclaration, tree.GetCompilationUnitRoot().Members[0].Kind()); var leading = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal($"/// <goo />{Environment.NewLine}", node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestEmptyElementNoAttributesDelimited() { var text = "/** <goo /> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.LeadingTrivia; Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestEmptyElementNoAttributesDelimitedPrecedingClass() { var text = @"/** <goo /> */ class C { }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); Assert.Equal(SyntaxKind.ClassDeclaration, tree.GetCompilationUnitRoot().Members[0].Kind()); var leading = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia(); Assert.Equal(2, leading.Count); // a new line follows the comment var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal("/** <goo /> */", node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestEmptyElementWithAttributes() { var text = @"/// <goo a=""xyz""/>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); } [Fact] public void TestEmptyElementWithAttributesSingleQuoted() { var text = @"/// <goo a='xyz'/>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); } [Fact] public void TestEmptyElementWithAttributesNestedQuote() { var text = @"/// <goo a=""x'y'z""/>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); Assert.Equal(SyntaxKind.XmlTextAttribute, element.Attributes[0].Kind()); var attr = (XmlTextAttributeSyntax)element.Attributes[0]; Assert.Equal(1, attr.TextTokens.Count); Assert.Equal("x'y'z", attr.TextTokens[0].ToString()); } [Fact] public void TestEmptyElementWithAttributesNestedQuoteSingleQuoted() { var text = @"/// <goo a='x""y""z'/>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); Assert.Equal(SyntaxKind.XmlTextAttribute, element.Attributes[0].Kind()); var attr = (XmlTextAttributeSyntax)element.Attributes[0]; Assert.Equal(1, attr.TextTokens.Count); Assert.Equal("x\"y\"z", attr.TextTokens[0].ToString()); } [Fact] public void TestEmptyElementNoAttributesMultipleLines() { var text = @"/// <goo /// />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); Assert.Equal($"<goo {Environment.NewLine}/// />", doc.Content[1].ToFullString()); } [Fact] public void TestEmptyElementNoAttributesMultipleLinesPrecedingClass() { var text = @"/// <goo /// /> class C { }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal($"/// <goo {Environment.NewLine}/// />{Environment.NewLine}", node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); Assert.Equal($"<goo {Environment.NewLine}/// />", doc.Content[1].ToFullString()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestEmptyElementNoAttributesMultipleLinesDelimited() { var text = @"/** <goo * /> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); Assert.Equal($"<goo {Environment.NewLine} * />", doc.Content[1].ToFullString()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [ClrOnlyFact] public void TestEmptyElementNoAttributesMultipleLinesDelimitedPrecedingClass() { var text = @"/** <goo * /> */ class C { }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia(); Assert.Equal(2, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal($"/** <goo {Environment.NewLine} * />{Environment.NewLine} */", node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); Assert.Equal($"<goo {Environment.NewLine} * />", doc.Content[1].ToFullString()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestEmptyElementWithAttributesDoubleQuoteMultipleLines() { var text = @"/// <goo /// a /// = /// ""xyz"" /// />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); } [Fact] public void TestEmptyElementWithAttributesQuoteMultipleLines() { var text = @"/// <goo /// a /// = /// 'xyz' /// />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); } [Fact] public void TestEmptyElementWithAttributesQuoteMultipleLinesDelimited() { var text = @"/** <goo * a * = * 'xyz' * /> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestEmptyElementWithAttributesDoubleQuoteMultipleLinesDelimited() { var text = @"/** <goo * a * = * ""xyz"" * /> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestEmptyElementWithAttributeQuoteAndAttributeTextOnMultipleLines() { var text = @"/// <goo /// a /// = /// ' /// xyz /// ' /// />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); } [Fact] public void TestEmptyElementWithAttributeDoubleQuoteAndAttributeTextOnMultipleLines() { var text = @"/// <goo /// a /// = /// "" /// xyz /// "" /// />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); } [Fact] public void TestEmptyElementWithAttributeDoubleQuoteAndAttributeTextOnMultipleLinesDelimited() { var text = @"/** <goo * a * = * "" * xyz * "" * /> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestEmptyElementWithAttributeQuoteAndAttributeTextOnMultipleLinesDelimited() { var text = @"/** <goo * a * = * ' * xyz * ' * /> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestElementDotInName() { var text = "/// <goo.bar />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal("goo.bar", element.Name.ToString()); } [Fact] public void TestElementColonInName() { var text = "/// <goo:bar />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal("goo:bar", element.Name.ToString()); } [Fact] public void TestElementDashInName() { var text = "/// <abc-def />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal("abc-def", element.Name.ToString()); } [Fact] public void TestElementNumberInName() { var text = "/// <goo123 />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal("goo123", element.Name.ToString()); } [Fact] public void TestElementNumberIsNameError() { var text = "/// <123 />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.NotEqual(0, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.NotEqual(0, doc.ErrorsAndWarnings().Length); } [Fact] public void TestNonEmptyElementNoAttributes() { var text = @"/// <goo> /// bar /// </goo>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlElement, doc.Content[1].Kind()); var element = (XmlElementSyntax)doc.Content[1]; Assert.Equal("goo", element.StartTag.Name.ToString()); Assert.Equal("goo", element.EndTag.Name.ToString()); Assert.Equal(1, element.Content.Count); var textsyntax = (XmlTextSyntax)element.Content[0]; Assert.Equal(4, textsyntax.ChildNodesAndTokens().Count); Assert.Equal(Environment.NewLine, textsyntax.ChildNodesAndTokens()[0].ToString()); Assert.Equal(" bar", textsyntax.ChildNodesAndTokens()[1].ToString()); Assert.Equal(Environment.NewLine, textsyntax.ChildNodesAndTokens()[2].ToString()); Assert.Equal(" ", textsyntax.ChildNodesAndTokens()[3].ToString()); } [Fact] public void TestNonEmptyElementNoAttributesDelimited() { var text = @"/** <goo> * bar * </goo> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlElement, doc.Content[1].Kind()); var element = (XmlElementSyntax)doc.Content[1]; Assert.Equal("goo", element.StartTag.Name.ToString()); Assert.Equal("goo", element.EndTag.Name.ToString()); Assert.Equal(1, element.Content.Count); var textsyntax = (XmlTextSyntax)element.Content[0]; Assert.Equal(4, textsyntax.ChildNodesAndTokens().Count); Assert.Equal(Environment.NewLine, textsyntax.ChildNodesAndTokens()[0].ToString()); Assert.Equal(" bar", textsyntax.ChildNodesAndTokens()[1].ToString()); Assert.Equal(Environment.NewLine, textsyntax.ChildNodesAndTokens()[2].ToString()); Assert.Equal(" ", textsyntax.ChildNodesAndTokens()[3].ToString()); } [Fact] public void TestCDataSection() { var text = @"/// <![CDATA[ this is a test /// of &some; cdata /// */ /** /// ""']]<>/></text]]>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlCDataSection, doc.Content[1].Kind()); var cdata = (XmlCDataSectionSyntax)doc.Content[1]; Assert.Equal(5, cdata.TextTokens.Count); Assert.Equal(" this is a test", cdata.TextTokens[0].ToString()); Assert.Equal(Environment.NewLine, cdata.TextTokens[1].ToString()); Assert.Equal(" of &some; cdata /// */ /**", cdata.TextTokens[2].ToString()); Assert.Equal(Environment.NewLine, cdata.TextTokens[3].ToString()); Assert.Equal(" \"']]<>/></text", cdata.TextTokens[4].ToString()); } [Fact] public void TestCDataSectionDelimited() { var text = @"/** <![CDATA[ this is a test * of &some; cdata * ""']]<>/></text]]> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlCDataSection, doc.Content[1].Kind()); var cdata = (XmlCDataSectionSyntax)doc.Content[1]; Assert.Equal(5, cdata.TextTokens.Count); Assert.Equal(" this is a test", cdata.TextTokens[0].ToString()); Assert.Equal(Environment.NewLine, cdata.TextTokens[1].ToString()); Assert.Equal(" of &some; cdata", cdata.TextTokens[2].ToString()); Assert.Equal(Environment.NewLine, cdata.TextTokens[3].ToString()); Assert.Equal(" \"']]<>/></text", cdata.TextTokens[4].ToString()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestIncompleteEOFCDataSection() { var text = "/// <![CDATA[ incomplete"; // end of file var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().Warnings().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlCDataSection, doc.Content[1].Kind()); var cdata = (XmlCDataSectionSyntax)doc.Content[1]; Assert.Equal(1, cdata.ErrorsAndWarnings().Length); Assert.Equal(1, cdata.TextTokens.Count); Assert.Equal(" incomplete", cdata.TextTokens[0].ToString()); } [Fact] public void TestIncompleteEOLCDataSection() { var text = @"/// <![CDATA[ incomplete class C { }"; // end of line/comment var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal($"/// <![CDATA[ incomplete{Environment.NewLine}", node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlCDataSection, doc.Content[1].Kind()); var cdata = (XmlCDataSectionSyntax)doc.Content[1]; Assert.Equal(1, cdata.ErrorsAndWarnings().Length); Assert.Equal(2, cdata.TextTokens.Count); Assert.Equal(" incomplete", cdata.TextTokens[0].ToString()); Assert.Equal(Environment.NewLine, cdata.TextTokens[1].ToString()); } [Fact] public void TestIncompleteEOLCDataSection_OtherNewline() { Assert.True(SyntaxFacts.IsNewLine('\u0085')); var text = "/// <![CDATA[ incomplete\u0085class C { }"; // end of line/comment var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal("/// <![CDATA[ incomplete\u0085", node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlCDataSection, doc.Content[1].Kind()); var cdata = (XmlCDataSectionSyntax)doc.Content[1]; Assert.Equal(1, cdata.ErrorsAndWarnings().Length); Assert.Equal(2, cdata.TextTokens.Count); Assert.Equal(" incomplete", cdata.TextTokens[0].ToString()); Assert.Equal("\u0085", cdata.TextTokens[1].ToString()); } [Fact] public void TestIncompleteDelimitedCDataSection() { var text = "/** <![CDATA[ incomplete*/"; // end of comment var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlCDataSection, doc.Content[1].Kind()); var cdata = (XmlCDataSectionSyntax)doc.Content[1]; Assert.Equal(1, cdata.ErrorsAndWarnings().Length); Assert.Equal(1, cdata.TextTokens.Count); Assert.Equal(" incomplete", cdata.TextTokens[0].ToString()); } [Fact] public void TestComment() { var text = @"/// <!-- this is a test /// of &some; comment /// ""']]<>/></text-->"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlComment, doc.Content[1].Kind()); var comment = (XmlCommentSyntax)doc.Content[1]; Assert.Equal(5, comment.TextTokens.Count); Assert.Equal(" this is a test", comment.TextTokens[0].ToString()); Assert.Equal(Environment.NewLine, comment.TextTokens[1].ToString()); Assert.Equal(" of &some; comment", comment.TextTokens[2].ToString()); Assert.Equal(Environment.NewLine, comment.TextTokens[3].ToString()); Assert.Equal(" \"']]<>/></text", comment.TextTokens[4].ToString()); } [Fact] public void TestCommentDelimited() { var text = @"/** <!-- this is a test * of &some; comment * ""']]<>/></text--> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlComment, doc.Content[1].Kind()); var comment = (XmlCommentSyntax)doc.Content[1]; Assert.Equal(5, comment.TextTokens.Count); Assert.Equal(" this is a test", comment.TextTokens[0].ToString()); Assert.Equal(Environment.NewLine, comment.TextTokens[1].ToString()); Assert.Equal(" of &some; comment", comment.TextTokens[2].ToString()); Assert.Equal(Environment.NewLine, comment.TextTokens[3].ToString()); Assert.Equal(" \"']]<>/></text", comment.TextTokens[4].ToString()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestIncompleteEOFComment() { var text = "/// <!-- incomplete"; // end of file var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlComment, doc.Content[1].Kind()); var comment = (XmlCommentSyntax)doc.Content[1]; Assert.Equal(1, comment.ErrorsAndWarnings().Length); Assert.Equal(1, comment.TextTokens.Count); Assert.Equal(" incomplete", comment.TextTokens[0].ToString()); } [Fact] public void TestIncompleteEOLComment() { var text = @"/// <!-- incomplete class C { }"; // end of line/comment var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal($"/// <!-- incomplete{Environment.NewLine}", node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlComment, doc.Content[1].Kind()); var comment = (XmlCommentSyntax)doc.Content[1]; Assert.Equal(1, comment.ErrorsAndWarnings().Length); Assert.Equal(2, comment.TextTokens.Count); Assert.Equal(" incomplete", comment.TextTokens[0].ToString()); Assert.Equal(Environment.NewLine, comment.TextTokens[1].ToString()); } [Fact] public void TestIncompleteDelimitedComment() { var text = "/** <!-- incomplete*/"; // end of comment var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlComment, doc.Content[1].Kind()); var comment = (XmlCommentSyntax)doc.Content[1]; Assert.Equal(1, comment.ErrorsAndWarnings().Length); Assert.Equal(1, comment.TextTokens.Count); Assert.Equal(" incomplete", comment.TextTokens[0].ToString()); } [Fact] public void TestProcessingInstruction() { var text = @"/// <?ProcessingInstruction this is a test /// of &a; ProcessingInstruction /// */ /** /// ""']]>/>?</text?>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlProcessingInstruction, doc.Content[1].Kind()); var ProcessingInstruction = (XmlProcessingInstructionSyntax)doc.Content[1]; Assert.Null(ProcessingInstruction.Name.Prefix); Assert.Equal("ProcessingInstruction", ProcessingInstruction.Name.LocalName.Text); Assert.Equal(5, ProcessingInstruction.TextTokens.Count); Assert.Equal(" this is a test", ProcessingInstruction.TextTokens[0].ToString()); Assert.Equal(Environment.NewLine, ProcessingInstruction.TextTokens[1].ToString()); Assert.Equal(" of &a; ProcessingInstruction /// */ /**", ProcessingInstruction.TextTokens[2].ToString()); Assert.Equal(Environment.NewLine, ProcessingInstruction.TextTokens[3].ToString()); Assert.Equal(" \"']]>/>?</text", ProcessingInstruction.TextTokens[4].ToString()); } [Fact] public void TestProcessingInstructionDelimited() { var text = @"/** <?prefix:localname this is a test <!-- * of &a; ProcessingInstruction * ""']]>/></text>]]>?> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlProcessingInstruction, doc.Content[1].Kind()); var ProcessingInstruction = (XmlProcessingInstructionSyntax)doc.Content[1]; Assert.Equal("prefix", ProcessingInstruction.Name.Prefix.Prefix.Text); Assert.Equal(":", ProcessingInstruction.Name.Prefix.ColonToken.Text); Assert.Equal("localname", ProcessingInstruction.Name.LocalName.Text); Assert.Equal(5, ProcessingInstruction.TextTokens.Count); Assert.Equal(" this is a test <!--", ProcessingInstruction.TextTokens[0].ToString()); Assert.Equal(Environment.NewLine, ProcessingInstruction.TextTokens[1].ToString()); Assert.Equal(" of &a; ProcessingInstruction", ProcessingInstruction.TextTokens[2].ToString()); Assert.Equal(Environment.NewLine, ProcessingInstruction.TextTokens[3].ToString()); Assert.Equal(" \"']]>/></text>]]>", ProcessingInstruction.TextTokens[4].ToString()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [Fact] public void TestIncompleteEOFProcessingInstruction() { var text = "/// <?incomplete"; // end of file var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlProcessingInstruction, doc.Content[1].Kind()); var ProcessingInstruction = (XmlProcessingInstructionSyntax)doc.Content[1]; Assert.Null(ProcessingInstruction.Name.Prefix); Assert.Equal("incomplete", ProcessingInstruction.Name.LocalName.Text); Assert.Equal(1, ProcessingInstruction.ErrorsAndWarnings().Length); Assert.Equal(0, ProcessingInstruction.TextTokens.Count); } [Fact] public void TestIncompleteEOLProcessingInstruction_OtherNewline() { Assert.True(SyntaxFacts.IsNewLine('\u0085')); var text = "/// <?name incomplete\u0085class C { }"; // end of line/comment var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal("/// <?name incomplete\u0085", node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlProcessingInstruction, doc.Content[1].Kind()); var ProcessingInstruction = (XmlProcessingInstructionSyntax)doc.Content[1]; Assert.Null(ProcessingInstruction.Name.Prefix); Assert.Equal("name", ProcessingInstruction.Name.LocalName.Text); Assert.Equal(1, ProcessingInstruction.ErrorsAndWarnings().Length); Assert.Equal(2, ProcessingInstruction.TextTokens.Count); Assert.Equal(" incomplete", ProcessingInstruction.TextTokens[0].ToString()); Assert.Equal("\u0085", ProcessingInstruction.TextTokens[1].ToString()); } [Fact] public void TestIncompleteDelimitedProcessingInstruction() { var text = "/** <?name incomplete*/"; // end of comment var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlProcessingInstruction, doc.Content[1].Kind()); var ProcessingInstruction = (XmlProcessingInstructionSyntax)doc.Content[1]; Assert.Null(ProcessingInstruction.Name.Prefix); Assert.Equal("name", ProcessingInstruction.Name.LocalName.Text); Assert.Equal(1, ProcessingInstruction.ErrorsAndWarnings().Length); Assert.Equal(1, ProcessingInstruction.TextTokens.Count); Assert.Equal(" incomplete", ProcessingInstruction.TextTokens[0].ToString()); } [WorkItem(899122, "DevDiv/Personal")] [Fact] public void TestIncompleteXMLComment() { var text = "/**\n"; // end of comment var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); } [Fact] public void TestEarlyTerminationOfXmlParse() { var text = @"/// <goo> /// bar /// </goo> /// </uhoh> /// class C { }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(2, tree.GetCompilationUnitRoot().ChildNodesAndTokens().Count); Assert.Equal(SyntaxKind.ClassDeclaration, tree.GetCompilationUnitRoot().ChildNodesAndTokens()[0].Kind()); var classdecl = (TypeDeclarationSyntax)tree.GetCompilationUnitRoot().ChildNodesAndTokens()[0].AsNode(); Assert.Equal("class C { }", classdecl.ToString()); Assert.True(classdecl.HasLeadingTrivia); var leading = classdecl.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.NotEqual(0, node.ErrorsAndWarnings().Length); Assert.Equal(SyntaxKind.EndOfFileToken, tree.GetCompilationUnitRoot().ChildNodesAndTokens()[1].Kind()); } [Fact] public void TestPredefinedXmlEntity() { var text = @"/// <"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); var xmltext = (XmlTextSyntax)doc.Content[0]; Assert.Equal(2, xmltext.TextTokens.Count); Assert.Equal(" ", xmltext.TextTokens[0].Value); Assert.Equal("<", xmltext.TextTokens[1].Value); } [Fact] public void TestPredefinedXmlEntityDelimited() { var text = @"/** < */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); var xmltext = (XmlTextSyntax)doc.Content[0]; Assert.Equal(3, xmltext.TextTokens.Count); Assert.Equal(" ", xmltext.TextTokens[0].Value); Assert.Equal("<", xmltext.TextTokens[1].Value); Assert.Equal(" ", xmltext.TextTokens[2].Value); } [Fact] public void TestHexCharacterXmlEntity() { var text = @"/// A"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); var xmltext = (XmlTextSyntax)doc.Content[0]; Assert.Equal(2, xmltext.TextTokens.Count); Assert.Equal(" ", xmltext.TextTokens[0].Value); Assert.Equal("A", xmltext.TextTokens[1].Value); } [Fact] public void TestHexCharacterXmlEntityDelimited() { var text = @"/** A */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); var xmltext = (XmlTextSyntax)doc.Content[0]; Assert.Equal(3, xmltext.TextTokens.Count); Assert.Equal(" ", xmltext.TextTokens[0].Value); Assert.Equal("A", xmltext.TextTokens[1].Value); Assert.Equal(" ", xmltext.TextTokens[2].Value); } [Fact] public void TestDecCharacterXmlEntity() { var text = @"/// A"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); var xmltext = (XmlTextSyntax)doc.Content[0]; Assert.Equal(2, xmltext.TextTokens.Count); Assert.Equal(" ", xmltext.TextTokens[0].Value); Assert.Equal("A", xmltext.TextTokens[1].Value); } [Fact] public void TestDecCharacterXmlEntityDelimited() { var text = @"/** A */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); var xmltext = (XmlTextSyntax)doc.Content[0]; Assert.Equal(3, xmltext.TextTokens.Count); Assert.Equal(" ", xmltext.TextTokens[0].Value); Assert.Equal("A", xmltext.TextTokens[1].Value); Assert.Equal(" ", xmltext.TextTokens[2].Value); } [Fact] public void TestLargeHexCharacterXmlEntity() { var text = @"/// 𝄞"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); var xmltext = (XmlTextSyntax)doc.Content[0]; Assert.Equal(2, xmltext.TextTokens.Count); Assert.Equal(" ", xmltext.TextTokens[0].Value); Assert.Equal("\U0001D11E", xmltext.TextTokens[1].Value); } [Fact] public void TestLargeHexCharacterXmlEntityDelimited() { var text = @"/** 𝄞 */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); var xmltext = (XmlTextSyntax)doc.Content[0]; Assert.Equal(3, xmltext.TextTokens.Count); Assert.Equal(" ", xmltext.TextTokens[0].Value); Assert.Equal("\U0001D11E", xmltext.TextTokens[1].Value); Assert.Equal(" ", xmltext.TextTokens[2].Value); } [Fact] public void TestXmlEntityUndefined() { var text = @"///&#abcdef;"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.NotEqual(0, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); } [Fact] public void TestXmlAttributeLessThan() { var text = @"///<goo attr=""less<than"" />"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.NotEqual(0, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); } [Fact] public void TestXmlCommentDashDash() { var text = @"///<!-- A Comment with -- -->"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.NotEqual(0, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); } [Fact] public void TestXmlElementMismatch() { var text = @"///< goo > </ bar >"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.NotEqual(0, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); } [Fact] public void TestXmlElementDuplicateAttributes() { var text = @"///< goo x = ""bar"" x = ""baz"" "; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.NotEqual(0, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); } [Fact] public void TestPredefinedXmlEntityInAttribute() { var text = @"/// <goo a="" < ""/>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); var attribute = (XmlTextAttributeSyntax)element.Attributes[0]; Assert.Equal(3, attribute.TextTokens.Count); Assert.Equal(" ", attribute.TextTokens[0].Value); Assert.Equal("<", attribute.TextTokens[1].Value); Assert.Equal(" ", attribute.TextTokens[2].Value); } [Fact] public void TestPredefinedXmlEntityInAttributeDelimited() { var text = @"/** <goo a="" < ""/>*/"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.True(doc.Content[0].HasLeadingTrivia); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); var attribute = (XmlTextAttributeSyntax)element.Attributes[0]; Assert.Equal(3, attribute.TextTokens.Count); Assert.Equal(" ", attribute.TextTokens[0].Value); Assert.Equal("<", attribute.TextTokens[1].Value); Assert.Equal(" ", attribute.TextTokens[2].Value); } [WorkItem(899590, "DevDiv/Personal")] [Fact] public void TestLessThanInAttributeTextIsError() { var text = @"/// <goo a = '<>'/>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.NotEqual(0, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(2, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.Equal(SyntaxKind.XmlEmptyElement, doc.Content[1].Kind()); var element = (XmlEmptyElementSyntax)doc.Content[1]; Assert.Equal(1, element.Attributes.Count); Assert.NotEqual(0, element.Attributes[0].ErrorsAndWarnings().Length); } [WorkItem(899559, "DevDiv/Personal")] [Fact] public void TestNoZeroWidthTrivia() { var text = @"/** x */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var leading = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); Assert.Equal(1, leading.Count); var node = leading[0]; Assert.Equal(SyntaxKind.MultiLineDocumentationCommentTrivia, node.Kind()); Assert.Equal(text, node.ToFullString()); var doc = (DocumentationCommentTriviaSyntax)node.GetStructure(); Assert.Equal(1, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); var xmltext = (XmlTextSyntax)doc.Content[0]; Assert.Equal(3, xmltext.ChildNodesAndTokens().Count); Assert.Equal(SyntaxKind.XmlTextLiteralNewLineToken, xmltext.ChildNodesAndTokens()[0].Kind()); Assert.True(xmltext.ChildNodesAndTokens()[0].HasLeadingTrivia); Assert.Equal(Environment.NewLine, xmltext.ChildNodesAndTokens()[0].ToString()); Assert.Equal(SyntaxKind.XmlTextLiteralToken, xmltext.ChildNodesAndTokens()[1].Kind()); Assert.False(xmltext.ChildNodesAndTokens()[1].HasLeadingTrivia); Assert.Equal("x", xmltext.ChildNodesAndTokens()[1].ToString()); Assert.Equal(SyntaxKind.XmlTextLiteralNewLineToken, xmltext.ChildNodesAndTokens()[2].Kind()); Assert.False(xmltext.ChildNodesAndTokens()[2].HasLeadingTrivia); Assert.Equal(Environment.NewLine, xmltext.ChildNodesAndTokens()[2].ToString()); } [WorkItem(906364, "DevDiv/Personal")] [Fact] public void TestXmlAttributeWithoutEqualSign() { var text = @"/// <goo a""as""> </goo>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we should get a warning about the = token missing Assert.Equal(1, tree.GetCompilationUnitRoot().Warnings().Length); // we expect one warning VerifyDiagnostics(tree.GetCompilationUnitRoot(), new List<TestError>() { new TestError(1570, true) }); } [WorkItem(906367, "DevDiv/Personal")] [Fact] public void TestXmlAttributeWithoutWhitespaceSeparators() { var text = @"/// <goo a=""as""b=""as""> </goo>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we should get a warning about the = token missing Assert.Equal(1, tree.GetCompilationUnitRoot().Warnings().Length); // we expect one warning VerifyDiagnostics(tree.GetCompilationUnitRoot(), new List<TestError>() { new TestError(1570, true) }); } [Fact] public void TestSingleLineXmlCommentBetweenRegularComments() { var text = @"//Comment /// <goo a=""as""> </goo> //Comment "; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // grab the trivia off the EOF token var trivias = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); // we should have 5 trivias Assert.Equal(5, trivias.Count); // we verify that the regular comments are also there Assert.False(trivias[0].HasStructure); Assert.False(trivias[4].HasStructure); // the 3rd one should be XmlDocComment Assert.True(trivias[2].HasStructure); Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[2].GetStructure().GetType()); var doc = trivias[2].GetStructure() as DocumentationCommentTriviaSyntax; // we validate the xml comment var xmlElement = doc.Content[1] as XmlElementSyntax; // we verify the content of the tag VerifyXmlElement(xmlElement, "goo", " "); VerifyXmlAttributes(xmlElement.StartTag.Attributes, new Dictionary<string, string>() { { "a", "as" } }); } [Fact] public void TestSingleLineXmlCommentAfterMultilineXmlComment() { var text = @"/** <bar a='val'> * text * </bar> */ /// <goo a=""as""> </goo> "; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // grab the trivia off the EOF token var trivias = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); // we should have 4 trivias Assert.Equal(4, trivias.Count); // we should also have two xml comments Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[3].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; var secondComment = trivias[3].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[1] as XmlElementSyntax, "bar", @" * text * "); VerifyXmlAttributes((firstComment.Content[1] as XmlElementSyntax).StartTag.Attributes, new Dictionary<string, string>() { { "a", "val" } }); VerifyXmlElement(secondComment.Content[1] as XmlElementSyntax, "goo", " "); VerifyXmlAttributes((secondComment.Content[1] as XmlElementSyntax).StartTag.Attributes, new Dictionary<string, string>() { { "a", "as" } }); } [Fact] public void TestSingleLineXmlCommentAfterInvalidMultilineXmlComment() { var text = @"/** <bar a='val'> * text */ /// <goo a=""as""> </goo> "; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we expect 1 warning Assert.Equal(1, tree.GetCompilationUnitRoot().Warnings().Length); // grab the trivia off the EOF token var trivias = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia(); // we should have 4 trivias Assert.Equal(4, trivias.Count); // we should also have two xml comments Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[3].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; var secondComment = trivias[3].GetStructure() as DocumentationCommentTriviaSyntax; // we validate that the error is on the firstComment node firstComment.GetDiagnostics().Verify( // (3,1): warning CS1570: XML comment has badly formed XML -- 'Expected an end tag for element 'bar'.' // */ Diagnostic(ErrorCode.WRN_XMLParseError, "").WithArguments("bar")); // verify that the xml elements contain the right info VerifyXmlElement(secondComment.Content[1] as XmlElementSyntax, "goo", " "); VerifyXmlAttributes((secondComment.Content[1] as XmlElementSyntax).StartTag.Attributes, new Dictionary<string, string>() { { "a", "as" } }); } [Fact] public void TestSingleLineXmlCommentBeforeMethodDecl() { var text = @"class C{ ///<goo a=""val""/> void Goo(){} }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // we grab the void keyword Assert.Equal(typeof(MethodDeclarationSyntax), (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0].GetType()); var keyword = ((tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0] as MethodDeclarationSyntax).ReturnType; var trivias = keyword.GetLeadingTrivia(); // we should have 2 trivias Assert.Equal(2, trivias.Count); // we should also have one comment Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlEmptyElementSyntax, "goo"); VerifyXmlAttributes((firstComment.Content[0] as XmlEmptyElementSyntax).Attributes, new Dictionary<string, string>() { { "a", "val" } }); } [Fact] public void TestSingleLineXmlCommentBeforeGenericMethodDecl() { var text = @"class C{ ///<goo a=""val""> </goo> void Goo<T>(){} }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // we grab the void keyword Assert.Equal(typeof(MethodDeclarationSyntax), (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0].GetType()); var keyword = ((tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0] as MethodDeclarationSyntax).ReturnType; var trivias = keyword.GetLeadingTrivia(); // we should have 2 trivias Assert.Equal(2, trivias.Count); // we should also have one comment Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlElementSyntax, "goo", " "); VerifyXmlAttributes((firstComment.Content[0] as XmlElementSyntax).StartTag.Attributes, new Dictionary<string, string>() { { "a", "val" } }); } [Fact] public void TestSingleLineXmlCommentBeforePropertyDecl() { var text = @"class C{ ///<goo a=""val""/> int Goo{get;set;} }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // we grab the void keyword Assert.Equal(typeof(PropertyDeclarationSyntax), (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0].GetType()); var keyword = ((tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0] as PropertyDeclarationSyntax).Type; var trivias = keyword.GetLeadingTrivia(); // we should have 2 trivias Assert.Equal(2, trivias.Count); // we should also have one comment Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlEmptyElementSyntax, "goo"); VerifyXmlAttributes((firstComment.Content[0] as XmlEmptyElementSyntax).Attributes, new Dictionary<string, string>() { { "a", "val" } }); } [Fact] public void TestSingleLineXmlCommentBeforeIndexerDecl() { var text = @"class C{ ///<goo a=""val""/> int this[int x] { get { return 1; } set { } } }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // we grab the void keyword Assert.Equal(typeof(IndexerDeclarationSyntax), (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0].GetType()); var keyword = ((tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0] as IndexerDeclarationSyntax).Type; var trivias = keyword.GetLeadingTrivia(); // we should have 2 trivias Assert.Equal(2, trivias.Count); // we should also have one comment Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlEmptyElementSyntax, "goo"); VerifyXmlAttributes((firstComment.Content[0] as XmlEmptyElementSyntax).Attributes, new Dictionary<string, string>() { { "a", "val" } }); } [WorkItem(906381, "DevDiv/Personal")] [Fact] public void TestMultiLineXmlCommentBeforeGenericTypeParameterOnMethodDecl() { var text = @"class C { void Goo</**<goo>test</goo>*/T>() { } }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // do we parsed a method? Assert.Equal(typeof(MethodDeclarationSyntax), (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0].GetType()); // we grab the open bracket for the Goo method decl var method = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0] as MethodDeclarationSyntax; var typeParameter = method.TypeParameterList.Parameters.Single(); var trivias = typeParameter.GetLeadingTrivia(); // we should have 1 trivia Assert.Equal(1, trivias.Count); // we should also have one XML comment Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlElementSyntax, "goo", "test"); // we don't have any attributes Assert.Equal(0, (firstComment.Content[0] as XmlElementSyntax).StartTag.Attributes.Count); } [WorkItem(906381, "DevDiv/Personal")] [Fact] public void TestMultiLineXmlCommentBeforeGenericTypeParameterOnClassDecl() { var text = @"class C</**<goo>test</goo>*/T>{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // do we parsed a method? Assert.Equal(typeof(ClassDeclarationSyntax), tree.GetCompilationUnitRoot().Members[0].GetType()); // we grab the open bracket for the Goo method decl var typeParameter = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).TypeParameterList.Parameters.Single(); var trivias = typeParameter.GetLeadingTrivia(); // we should have 1 trivia Assert.Equal(1, trivias.Count); // we should also have one XML comment Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlElementSyntax, "goo", "test"); // we don't have any attributes Assert.Equal(0, (firstComment.Content[0] as XmlElementSyntax).StartTag.Attributes.Count); } [Fact] public void TestSingleLineXmlCommentBeforeIncompleteGenericMethodDecl() { var text = @"class C{ ///<goo a=""val""> </goo> void Goo<T(){} }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().Errors().Length); // 4 errors because of the incomplete class decl // we grab the void keyword Assert.Equal(typeof(MethodDeclarationSyntax), (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0].GetType()); var keyword = ((tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Members[0] as MethodDeclarationSyntax).ReturnType; var trivias = keyword.GetLeadingTrivia(); // we should have 2 trivias Assert.Equal(2, trivias.Count); // we should also have one comment Assert.Equal(0, trivias[0].Errors().Length); Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlElementSyntax, "goo", " "); VerifyXmlAttributes((firstComment.Content[0] as XmlElementSyntax).StartTag.Attributes, new Dictionary<string, string>() { { "a", "val" } }); } [Fact] public void TestSingleLineXmlCommentAfterMethodDecl() { var text = @"class C{ void Goo(){} ///<goo a=""val""/> }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var bracket = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).CloseBraceToken; var trivias = bracket.GetLeadingTrivia(); // we should have 2 trivias Assert.Equal(1, trivias.Count); // we should also have one comment Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlEmptyElementSyntax, "goo"); VerifyXmlAttributes((firstComment.Content[0] as XmlEmptyElementSyntax).Attributes, new Dictionary<string, string>() { { "a", "val" } }); } [Fact] public void TestSingleLineXmlCommentAfterIncompleteMethodDecl() { var text = @"class C{ void Goo({} ///<goo a=""val""> </goo> }"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().Errors().Length); // error because of the incomplete class decl // we grab the close bracket for the class var classDecl = (TypeDeclarationSyntax)tree.GetCompilationUnitRoot().Members[0]; var bracket = classDecl.CloseBraceToken; var trivias = bracket.GetLeadingTrivia(); // we should have 2 trivias Assert.Equal(1, trivias.Count); // we should also have one comment Assert.Equal(0, trivias[0].Errors().Length); Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlElementSyntax, "goo", " "); VerifyXmlAttributes((firstComment.Content[0] as XmlElementSyntax).StartTag.Attributes, new Dictionary<string, string>() { { "a", "val" } }); } [Fact] public void TestSingleLineXmlCommentBeforePreprocessorDirective() { var text = @"///<goo></goo> # if DOODAD # endif"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // 4 errors because of the incomplete class decl // we grab the close bracket from the OEF token var bracket = tree.GetCompilationUnitRoot().EndOfFileToken; var trivias = bracket.GetLeadingTrivia(); // we should have 3 trivias Assert.Equal(3, trivias.Count); Assert.Equal(0, trivias[0].Errors().Length); Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlElementSyntax, "goo", string.Empty); } [Fact] public void TestSingleLineXmlCommentAfterPreprocessorDirective() { var text = @"# if DOODAD # endif ///<goo></goo>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // 4 errors because of the incomplete class decl // we grab the close bracket from the OEF token var bracket = tree.GetCompilationUnitRoot().EndOfFileToken; var trivias = bracket.GetLeadingTrivia(); // we should have 3 trivias Assert.Equal(3, trivias.Count); Assert.Equal(0, trivias[0].Errors().Length); Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[2].GetStructure().GetType()); // we grab the xml comments var firstComment = trivias[2].GetStructure() as DocumentationCommentTriviaSyntax; // verify that the xml elements contain the right info VerifyXmlElement(firstComment.Content[0] as XmlElementSyntax, "goo", string.Empty); } [Fact] public void TestSingleLineXmlCommentInsideMultiLineXmlComment() { var text = @"/** <goo> * /// <bar> </bar> * </goo> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // we grab the close bracket from the OEF token var bracket = tree.GetCompilationUnitRoot().EndOfFileToken; var trivias = bracket.GetLeadingTrivia(); // we should have 2 trivias Assert.Equal(1, trivias.Count); Assert.Equal(0, trivias[0].Errors().Length); // make sure that the external node exists Assert.Equal(typeof(DocumentationCommentTriviaSyntax), trivias[0].GetStructure().GetType()); // we grab the xml comments var outerComment = (trivias[0].GetStructure() as DocumentationCommentTriviaSyntax).Content[1] as XmlElementSyntax; var innerComment = outerComment.Content[1] as XmlElementSyntax; // verify that the xml elements contain the right info VerifyXmlElement(outerComment, "goo", @" * /// <bar> </bar> * "); VerifyXmlElement(innerComment, "bar", " "); } [WorkItem(906500, "DevDiv/Personal")] [Fact] public void TestIncompleteMultiLineXmlComment() { var text = @"/** <goo/>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we expect one warning Assert.Equal(1, tree.GetCompilationUnitRoot().Errors().Length); VerifyDiagnostics(tree.GetCompilationUnitRoot(), new List<TestError>() { new TestError(1035, false) }); } [Fact] public void TestSingleLineXmlCommentWithMultipleAttributes() { var text = @"///<goo attr1=""a"" attr2=""b"" attr3=""test""> </goo> class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var trivias = classKeyword.GetLeadingTrivia(); Assert.Equal(1, trivias.Count); // we verify that we parsed a correct XML element VerifyXmlElement((trivias[0].GetStructure() as DocumentationCommentTriviaSyntax).Content[0] as XmlElementSyntax, "goo", " "); VerifyXmlAttributes(((trivias[0].GetStructure() as DocumentationCommentTriviaSyntax).Content[0] as XmlElementSyntax).StartTag.Attributes, new Dictionary<string, string>() { { "attr1", "a" }, { "attr2", "b" }, { "attr3", "test" } }); } [Fact] public void TestNestedXmlTagsInsideSingleLineXmlDocComment() { var text = @"///<goo> /// <bar> /// <baz attr=""a""> /// </baz> /// </bar> ///</goo>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); // we grab the top trivia. var eofToken = tree.GetCompilationUnitRoot().EndOfFileToken; var topTrivias = eofToken.GetLeadingTrivia(); Assert.Equal(1, topTrivias.Count); var doc = topTrivias[0].GetStructure() as DocumentationCommentTriviaSyntax; var topTriviaElement = doc.Content[0] as XmlElementSyntax; VerifyXmlElement(topTriviaElement, "goo", @" /// <bar> /// <baz attr=""a""> /// </baz> /// </bar> "); var secondLevelTrivia = topTriviaElement.Content[1] as XmlElementSyntax; VerifyXmlElement(secondLevelTrivia, "bar", @" /// <baz attr=""a""> /// </baz> /// "); var thirdLevelTrivia = secondLevelTrivia.Content[1] as XmlElementSyntax; VerifyXmlElement(thirdLevelTrivia, "baz", @" /// "); VerifyXmlAttributes(thirdLevelTrivia.StartTag.Attributes, new Dictionary<string, string>() { { "attr", "a" } }); } [Fact] public void TestMultiLineXmlCommentWithNestedTagThatContainsCDATA() { var text = @"/** <goo> <bar> <![CDATA[ Some text ]]> </bar> </goo> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); var eofToken = tree.GetCompilationUnitRoot().EndOfFileToken; var trivias = eofToken.GetLeadingTrivia(); var doc = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; var topNode = doc.Content[1] as XmlElementSyntax; VerifyXmlElement(topNode, "goo", @" <bar> <![CDATA[ Some text ]]> </bar> "); var secondLevel = topNode.Content[1] as XmlElementSyntax; VerifyXmlElement(secondLevel, "bar", @" <![CDATA[ Some text ]]> "); // verify the CDATA content var cdata = secondLevel.Content[1]; var actual = (cdata as XmlCDataSectionSyntax).TextTokens.ToFullString(); Assert.Equal(@" Some text ", actual); } [Fact] public void TestSingleLineXmlCommentWithMismatchedUpperLowerCaseTagName() { var text = @"///<goo> </Goo>"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we should get a warning Assert.Equal(1, tree.GetCompilationUnitRoot().Warnings().Length); // we get to the xml trivia var eofToken = tree.GetCompilationUnitRoot().EndOfFileToken; var trivias = eofToken.GetLeadingTrivia(); // we got a trivia Assert.Equal(1, trivias.Count); VerifyDiagnostics(trivias[0], new List<TestError>() { new TestError(1570, true) }); } [WorkItem(906704, "DevDiv/Personal")] [Fact] public void TestSingleLineXmlCommentWithMissingStartTag() { var text = @"///</Goo> class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); // we get to the xml trivia var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; // we get the xmldoc comment var doc = classKeyword.GetLeadingTrivia()[0].GetStructure() as DocumentationCommentTriviaSyntax; // we get the xmlText var xmlText = doc.Content[0] as XmlTextSyntax; // we have an error on that node VerifyDiagnostics(xmlText, new List<TestError>() { new TestError(1570, true) }); // we should get just 2 nodes Assert.Equal(2, xmlText.TextTokens.Count); Assert.Equal($"///</Goo>{Environment.NewLine}", xmlText.TextTokens.ToFullString()); } [WorkItem(906719, "DevDiv/Personal")] [Fact] public void TestMultiLineXmlCommentWithMissingStartTag() { var text = @"/**</Goo>*/ class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(1, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); // we get to the xml trivia var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; // we get the xmldoc comment var doc = classKeyword.GetLeadingTrivia()[0].GetStructure() as DocumentationCommentTriviaSyntax; // we get the xmlText var xmlText = doc.Content[0] as XmlTextSyntax; // we have an error on that node VerifyDiagnostics(xmlText, new List<TestError>() { new TestError(1570, true) }); // we should get just 2 nodes Assert.Equal(1, xmlText.TextTokens.Count); Assert.Equal("/**</Goo>", xmlText.TextTokens.ToFullString()); } [Fact] public void TestSingleLineXmlCommentWithMissingEndTag() { var text = @"///<Goo> class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var trivias = classKeyword.GetLeadingTrivia(); var doc = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; doc.GetDiagnostics().Verify( // (2,1): warning CS1570: XML comment has badly formed XML -- 'Expected an end tag for element 'Goo'.' // class C{} Diagnostic(ErrorCode.WRN_XMLParseError, "").WithArguments("Goo")); } [WorkItem(906752, "DevDiv/Personal")] [Fact] public void TestMultiLineXmlCommentWithMissingEndTag() { var text = @"/**<Goo>*/ class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we should get 1 warning Assert.Equal(1, tree.GetCompilationUnitRoot().Warnings().Length); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var trivias = classKeyword.GetLeadingTrivia(); var doc = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; doc.GetDiagnostics().Verify( // (1,9): warning CS1570: XML comment has badly formed XML -- 'Expected an end tag for element 'Goo'.' // /**<Goo>*/ Diagnostic(ErrorCode.WRN_XMLParseError, "").WithArguments("Goo")); } [Fact] public void TestMultiLineXmlCommentWithInterleavedTags() { var text = @"/**<goo> <bar></goo> </bar>*/ class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we should get 2 warnings Assert.Equal(2, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var trivias = classKeyword.GetLeadingTrivia(); var doc = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // we have an error on that node VerifyDiagnostics(doc, new List<TestError>() { new TestError(1570, true), new TestError(1570, true) }); } [Fact] public void TestSingleLineXmlCommentWithInterleavedTags() { var text = @"///<goo> ///<bar></goo> ///</bar> class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we should get 2 warnings Assert.Equal(2, tree.GetCompilationUnitRoot().Warnings().Length); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var trivias = classKeyword.GetLeadingTrivia(); var doc = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; // we have an error on that node VerifyDiagnostics(doc, new List<TestError>() { new TestError(1570, true), new TestError(1570, true) }); } [Fact] public void TestMultiLineXmlCommentWithIncompleteInterleavedTags() { var text = @"/**<goo> <bar></goo> */ class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var trivias = classKeyword.LeadingTrivia; var doc = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; doc.GetDiagnostics().Verify( // (2,8): warning CS1570: XML comment has badly formed XML -- 'End tag 'goo' does not match the start tag 'bar'.' // <bar></goo> Diagnostic(ErrorCode.WRN_XMLParseError, "goo").WithArguments("goo", "bar"), // (3,1): warning CS1570: XML comment has badly formed XML -- 'Expected an end tag for element 'goo'.' // */ Diagnostic(ErrorCode.WRN_XMLParseError, "").WithArguments("goo")); } [Fact] public void TestSingleLineXmlCommentWithIncompleteInterleavedTags() { var text = @"///<goo> ///<bar></goo> class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var trivias = classKeyword.GetLeadingTrivia(); var doc = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; doc.GetDiagnostics().Verify( // (2,11): warning CS1570: XML comment has badly formed XML -- 'End tag 'goo' does not match the start tag 'bar'.' // ///<bar></goo> Diagnostic(ErrorCode.WRN_XMLParseError, "goo").WithArguments("goo", "bar"), // (3,1): warning CS1570: XML comment has badly formed XML -- 'Expected an end tag for element 'goo'.' // class C{} Diagnostic(ErrorCode.WRN_XMLParseError, "").WithArguments("goo")); } [Fact] public void TestMultiLineXmlCommentWithMultipleStartTokens() { var text = @"/** <a <b */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we should get 2 warnings Assert.Equal(2, tree.GetCompilationUnitRoot().Warnings().Length); } [Fact] public void TestMultiLineXmlCommentWithMultipleEndTags() { var text = @"/** <a> </a> </a> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we should get 2 warnings Assert.Equal(1, tree.GetCompilationUnitRoot().Warnings().Length); } [Fact] public void TestMultiLineXmlCommentWithMultipleEndTags2() { var text = @"/** <a> </b> </a> */"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); // we should get 2 warnings Assert.Equal(2, tree.GetCompilationUnitRoot().Warnings().Length); } [WorkItem(906814, "DevDiv/Personal")] [Fact] public void TestSingleLineXmlCommentWithInvalidStringAttributeValue() { var text = @"///<goo a=""</>""> </goo> class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var doc = classKeyword.GetLeadingTrivia()[0].GetStructure() as DocumentationCommentTriviaSyntax; Assert.Equal(typeof(XmlElementSyntax), doc.Content[0].GetType()); } [WorkItem(537113, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537113")] [Fact] public void TestSingleLineXmlCommentWithAttributeWithoutQuotes() { var text = @"///<goo a=4></goo> class C{}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var doc = classKeyword.GetLeadingTrivia()[0].GetStructure() as DocumentationCommentTriviaSyntax; // we should still get an XmlElement Assert.IsType<XmlElementSyntax>(doc.Content[0]); } [WorkItem(926873, "DevDiv/Personal")] [Fact] public void TestSomeXmlEntities() { var text = @"/// <doc> /// <line>ٟ</line> /// <line>ٟ</line> /// </doc> class A {}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(0, tree.GetCompilationUnitRoot().Errors().Length); } [WorkItem(926683, "DevDiv/Personal")] [Fact] public void TestSomeBadXmlEntities() { var text = @"/// <doc1></doc1> /// <doc2><![CDATA[]]></doc2> /// <doc3 x = """"></doc3> /// <!--  --> class A {} "; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(4, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); VerifyDiagnostics(tree.GetCompilationUnitRoot(), new List<TestError> { new TestError(1570, true), new TestError(1570, true), new TestError(1570, true), new TestError(1570, true) }); } [WorkItem(926804, "DevDiv/Personal")] [Fact] public void TestSomeBadWhitespaceInTags() { var text = @"/// < doc></doc> /// <abc> </ abc> /// < a/> /// < /// b></b> class A {} "; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); Assert.Equal(4, tree.GetCompilationUnitRoot().ErrorsAndWarnings().Length); VerifyDiagnostics(tree.GetCompilationUnitRoot(), new List<TestError> { new TestError(1570, true), new TestError(1570, true), new TestError(1570, true), new TestError(1570, true) }); } [WorkItem(926807, "DevDiv/Personal")] [Fact] public void TestCDataEndTagInXmlText() { var text = @"/// <doc> ]]> </doc> /// <a>abc]]]>def</a> /// <a attr=""]]>""></a> class A {}"; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var trivias = classKeyword.GetLeadingTrivia(); var doc = trivias[0].GetStructure() as DocumentationCommentTriviaSyntax; Assert.Equal(2, doc.ErrorsAndWarnings().Length); VerifyDiagnostics(doc, new List<TestError>() { new TestError(1570, true), new TestError(1570, true) }); } [WorkItem(536748, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536748")] [Fact] public void AttributesInEndTag() { var text = @" /// <summary attr=""A""> /// </summary attr=""A""> class A { } "; var tree = Parse(text); Assert.NotNull(tree); Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString()); var classKeyword = (tree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax).Keyword; var trivias = classKeyword.GetLeadingTrivia(); var doc = trivias[1].GetStructure() as DocumentationCommentTriviaSyntax; Assert.Equal(3, doc.Content.Count); Assert.Equal(SyntaxKind.XmlText, doc.Content[0].Kind()); Assert.Equal(SyntaxKind.XmlElement, doc.Content[1].Kind()); Assert.Equal(SyntaxKind.XmlText, doc.Content[2].Kind()); } [WorkItem(546989, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546989")] [Fact] public void NonAsciiQuotationMarks() { var text = @" class A { /// <see cref=”A()”/> /// <param name=”x”/> /// <other attr=”value”/> void M(int x) { } }"; var tree = Parse(text); tree.GetDiagnostics().Verify( // (4,19): warning CS1570: XML comment has badly formed XML -- 'Non-ASCII quotations marks may not be used around string literals.' // /// <see cref=”A()”/> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (4,23): warning CS1570: XML comment has badly formed XML -- 'Non-ASCII quotations marks may not be used around string literals.' // /// <see cref=”A()”/> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (5,21): warning CS1570: XML comment has badly formed XML -- 'Non-ASCII quotations marks may not be used around string literals.' // /// <param name=”x”/> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (5,23): warning CS1570: XML comment has badly formed XML -- 'Non-ASCII quotations marks may not be used around string literals.' // /// <param name=”x”/> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // What's happening with the text attribute is that "”/>" is correctly (if unintuitively) being consumed as part of the // attribute value. It then complains about the missing closing quotation mark and '/>'. // (6,21): warning CS1570: XML comment has badly formed XML -- 'Non-ASCII quotations marks may not be used around string literals.' // /// <other attr=”value”/> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (7,1): warning CS1570: XML comment has badly formed XML -- 'Missing closing quotation mark for string literal.' // void M(int x) { } Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (7,1): warning CS1570: XML comment has badly formed XML -- 'Expected '>' or '/>' to close tag 'other'.' // void M(int x) { } Diagnostic(ErrorCode.WRN_XMLParseError, "").WithArguments("other")); } [WorkItem(546989, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546989")] [Fact] public void Microsoft_TeamFoundation_Client_Dll() { var text = @" /// <summary></summary> public class Program { static void Main() { } /// <summary> /// GetEntityConnectionString from the selected path /// path is of the format <project name>\<nodename>\<nodename> /// </summary> /// <param name=”metadata”></param> /// <param name=”provider”></param> protected void GetEntityConnectionString( string metadata, string provider) { } }"; var tree = Parse(text); tree.GetDiagnostics().Verify( // (8,44): warning CS1570: XML comment has badly formed XML -- 'Missing equals sign between attribute and attribute value.' // /// path is of the format <project name>\<nodename>\<nodename> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (9,11): warning CS1570: XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'nodename'.' // /// </summary> Diagnostic(ErrorCode.WRN_XMLParseError, "summary").WithArguments("summary", "nodename"), // (10,21): warning CS1570: XML comment has badly formed XML -- 'Non-ASCII quotations marks may not be used around string literals.' // /// <param name=”metadata”></param> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (10,30): warning CS1570: XML comment has badly formed XML -- 'Non-ASCII quotations marks may not be used around string literals.' // /// <param name=”metadata”></param> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (11,21): warning CS1570: XML comment has badly formed XML -- 'Non-ASCII quotations marks may not be used around string literals.' // /// <param name=”provider”></param> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (11,30): warning CS1570: XML comment has badly formed XML -- 'Non-ASCII quotations marks may not be used around string literals.' // /// <param name=”provider”></param> Diagnostic(ErrorCode.WRN_XMLParseError, ""), // (12,1): warning CS1570: XML comment has badly formed XML -- 'Expected an end tag for element 'nodename'.' // protected void GetEntityConnectionString( Diagnostic(ErrorCode.WRN_XMLParseError, "").WithArguments("nodename"), // (12,1): warning CS1570: XML comment has badly formed XML -- 'Expected an end tag for element 'project'.' // protected void GetEntityConnectionString( Diagnostic(ErrorCode.WRN_XMLParseError, "").WithArguments("project"), // (12,1): warning CS1570: XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.' // protected void GetEntityConnectionString( Diagnostic(ErrorCode.WRN_XMLParseError, "").WithArguments("summary")); } [WorkItem(547188, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547188")] [Fact] public void WhitespaceInXmlName() { var text = @" /// <A:B/> /// <A: B/> /// <A :B/> /// <A : B/> public class Program { }"; var tree = Parse(text); tree.GetDiagnostics().Verify( // (3,8): warning CS1570: XML comment has badly formed XML -- 'Whitespace is not allowed at this location.' // /// <A: B/> Diagnostic(ErrorCode.WRN_XMLParseError, " "), // (4,7): warning CS1570: XML comment has badly formed XML -- 'Whitespace is not allowed at this location.' // /// <A :B/> Diagnostic(ErrorCode.WRN_XMLParseError, " "), // (5,7): warning CS1570: XML comment has badly formed XML -- 'Whitespace is not allowed at this location.' // /// <A : B/> Diagnostic(ErrorCode.WRN_XMLParseError, " "), // (5,9): warning CS1570: XML comment has badly formed XML -- 'Whitespace is not allowed at this location.' // /// <A : B/> Diagnostic(ErrorCode.WRN_XMLParseError, " ")); } [Fact] public void WhitespaceInXmlEndName() { var text = @" /// <A:B> /// good /// </A:B> /// <A:B> /// bad /// </A :B> /// <A:B> /// bad /// </A: B> public class Program { }"; var tree = Parse(text); tree.GetDiagnostics().Verify( // (7,7): warning CS1570: XML comment has badly formed XML -- 'End tag 'A :B' does not match the start tag 'A:B'.' // /// </A :B> Diagnostic(ErrorCode.WRN_XMLParseError, "A :B").WithArguments("A :B", "A:B").WithLocation(7, 7), // (7,8): warning CS1570: XML comment has badly formed XML -- 'Whitespace is not allowed at this location.' // /// </A :B> Diagnostic(ErrorCode.WRN_XMLParseError, " ").WithLocation(7, 8), // (10,7): warning CS1570: XML comment has badly formed XML -- 'End tag 'A: B' does not match the start tag 'A:B'.' // /// </A: B> Diagnostic(ErrorCode.WRN_XMLParseError, "A: B").WithArguments("A: B", "A:B").WithLocation(10, 7), // (10,9): warning CS1570: XML comment has badly formed XML -- 'Whitespace is not allowed at this location.' // /// </A: B> Diagnostic(ErrorCode.WRN_XMLParseError, " ").WithLocation(10, 9) ); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestDocumentationComment() { var expected = @"/// <summary> /// This class provides extension methods for the <see cref=""TypeName""/> class. /// </summary> /// <threadsafety static=""true"" instance=""false""/> /// <preliminary/>"; DocumentationCommentTriviaSyntax documentationComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSummaryElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlText("This class provides extension methods for the "), SyntaxFactory.XmlSeeElement( SyntaxFactory.TypeCref(SyntaxFactory.ParseTypeName("TypeName"))), SyntaxFactory.XmlText(" class."), SyntaxFactory.XmlNewLine(Environment.NewLine)), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlThreadSafetyElement(), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlPreliminaryElement()); var actual = documentationComment.ToFullString(); Assert.Equal<string>(expected, actual); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestXmlSummaryElement() { var expected = @"/// <summary> /// This class provides extension methods. /// </summary>"; DocumentationCommentTriviaSyntax documentationComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSummaryElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlText("This class provides extension methods."), SyntaxFactory.XmlNewLine(Environment.NewLine))); var actual = documentationComment.ToFullString(); Assert.Equal<string>(expected, actual); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestXmlSeeElementAndXmlSeeAlsoElement() { var expected = @"/// <summary> /// This class provides extension methods for the <see cref=""TypeName""/> class and the <seealso cref=""TypeName2""/> class. /// </summary>"; DocumentationCommentTriviaSyntax documentationComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSummaryElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlText("This class provides extension methods for the "), SyntaxFactory.XmlSeeElement( SyntaxFactory.TypeCref(SyntaxFactory.ParseTypeName("TypeName"))), SyntaxFactory.XmlText(" class and the "), SyntaxFactory.XmlSeeAlsoElement( SyntaxFactory.TypeCref(SyntaxFactory.ParseTypeName("TypeName2"))), SyntaxFactory.XmlText(" class."), SyntaxFactory.XmlNewLine(Environment.NewLine))); var actual = documentationComment.ToFullString(); Assert.Equal<string>(expected, actual); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestXmlSeeAlsoElementWithLink() { var docComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSeeAlsoElement(new Uri("https://dotnet.microsoft.com/"), SyntaxFactory.List(new XmlNodeSyntax[] { SyntaxFactory.XmlText(".NET") }))); Assert.Equal( "/// <seealso href=\"https://dotnet.microsoft.com/\">.NET</seealso>", docComment.ToFullString()); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestXmlNewLineElement() { var expected = @"/// <summary> /// This is a summary. /// </summary> /// /// /// <remarks> /// /// </remarks>"; DocumentationCommentTriviaSyntax documentationComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSummaryElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlText("This is a summary."), SyntaxFactory.XmlNewLine(Environment.NewLine)), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlRemarksElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlNewLine(Environment.NewLine))); var actual = documentationComment.ToFullString(); Assert.Equal<string>(expected, actual); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestXmlParamAndParamRefElement() { var expected = @"/// <summary> /// <paramref name=""b""/> /// </summary> /// <param name=""a""></param> /// <param name=""b""></param>"; DocumentationCommentTriviaSyntax documentationComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSummaryElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlParamRefElement("b"), SyntaxFactory.XmlNewLine(Environment.NewLine)), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlParamElement("a"), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlParamElement("b")); var actual = documentationComment.ToFullString(); Assert.Equal<string>(expected, actual); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestXmlReturnsElement() { var expected = @"/// <summary> /// /// </summary> /// <returns> /// Returns a value. /// </returns>"; DocumentationCommentTriviaSyntax documentationComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSummaryElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlNewLine(Environment.NewLine)), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlReturnsElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlText("Returns a value."), SyntaxFactory.XmlNewLine(Environment.NewLine))); var actual = documentationComment.ToFullString(); Assert.Equal<string>(expected, actual); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestXmlRemarksElement() { var expected = @"/// <summary> /// /// </summary> /// <remarks> /// Same as in class <see cref=""TypeName""/>. /// </remarks>"; DocumentationCommentTriviaSyntax documentationComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSummaryElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlNewLine(Environment.NewLine)), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlRemarksElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlText("Same as in class "), SyntaxFactory.XmlSeeElement(SyntaxFactory.TypeCref(SyntaxFactory.ParseTypeName("TypeName"))), SyntaxFactory.XmlText("."), SyntaxFactory.XmlNewLine(Environment.NewLine))); var actual = documentationComment.ToFullString(); Assert.Equal<string>(expected, actual); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestXmlExceptionElement() { var expected = @"/// <summary> /// /// </summary> /// <exception cref=""InvalidOperationException"">This exception will be thrown if the object is in an invalid state when calling this method.</exception>"; DocumentationCommentTriviaSyntax documentationComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSummaryElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlNewLine(Environment.NewLine)), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlExceptionElement( SyntaxFactory.TypeCref( SyntaxFactory.ParseTypeName("InvalidOperationException")), SyntaxFactory.XmlText("This exception will be thrown if the object is in an invalid state when calling this method."))); var actual = documentationComment.ToFullString(); Assert.Equal<string>(expected, actual); } [Fact] [Trait("Feature", "Xml Documentation Comments")] public void TestXmlPermissionElement() { var expected = @"/// <summary> /// /// </summary> /// <permission cref=""MyPermission"">Needs MyPermission to execute.</permission>"; DocumentationCommentTriviaSyntax documentationComment = SyntaxFactory.DocumentationComment( SyntaxFactory.XmlSummaryElement( SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlNewLine(Environment.NewLine)), SyntaxFactory.XmlNewLine(Environment.NewLine), SyntaxFactory.XmlPermissionElement( SyntaxFactory.TypeCref( SyntaxFactory.ParseTypeName("MyPermission")), SyntaxFactory.XmlText("Needs MyPermission to execute."))); var actual = documentationComment.ToFullString(); Assert.Equal<string>(expected, actual); } [Fact] [WorkItem(39315, "https://github.com/dotnet/roslyn/issues/39315")] public void WriteDocumentationCommentXml_01() { var comp = CreateCompilation(new[] { Parse(@" /// <summary> a /// </summary> "), Parse(@" /// <summary> b /// </summary> ")}); var diags = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); DocumentationCommentCompiler.WriteDocumentationCommentXml( comp, assemblyName: null, xmlDocStream: null, diags, default(CancellationToken), filterTree: comp.SyntaxTrees[0]); diags.ToReadOnlyAndFree().Verify( // (2,1): warning CS1587: XML comment is not placed on a valid language element // /// <summary> a Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/").WithLocation(2, 1) ); diags = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); DocumentationCommentCompiler.WriteDocumentationCommentXml( comp, assemblyName: null, xmlDocStream: null, diags, default(CancellationToken), filterTree: comp.SyntaxTrees[0], filterSpanWithinTree: new TextSpan(0, 0)); diags.ToReadOnlyAndFree().Verify(); diags = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); DocumentationCommentCompiler.WriteDocumentationCommentXml( comp, assemblyName: null, xmlDocStream: null, diags, default(CancellationToken), filterTree: comp.SyntaxTrees[1]); diags.ToReadOnlyAndFree().Verify( // (3,1): warning CS1587: XML comment is not placed on a valid language element // /// <summary> b Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/").WithLocation(3, 1) ); diags = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); DocumentationCommentCompiler.WriteDocumentationCommentXml( comp, assemblyName: null, xmlDocStream: null, diags, default(CancellationToken), filterTree: null); diags.ToReadOnlyAndFree().Verify( // (2,1): warning CS1587: XML comment is not placed on a valid language element // /// <summary> a Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/").WithLocation(2, 1), // (3,1): warning CS1587: XML comment is not placed on a valid language element // /// <summary> b Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/").WithLocation(3, 1) ); diags = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); DocumentationCommentCompiler.WriteDocumentationCommentXml( comp, assemblyName: null, xmlDocStream: null, diags, default(CancellationToken), filterTree: null, filterSpanWithinTree: new TextSpan(0, 0)); diags.ToReadOnlyAndFree().Verify( // (2,1): warning CS1587: XML comment is not placed on a valid language element // /// <summary> a Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/").WithLocation(2, 1), // (3,1): warning CS1587: XML comment is not placed on a valid language element // /// <summary> b Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/").WithLocation(3, 1) ); } #region Xml Test helpers /// <summary> /// Verifies that the errors on the given CSharpSyntaxNode match the expected error codes and types /// </summary> /// <param name="node">The node that has errors</param> /// <param name="errors">The list of expected errors</param> private void VerifyDiagnostics(CSharpSyntaxNode node, List<TestError> errors) { VerifyDiagnostics(node.ErrorsAndWarnings(), errors); } private void VerifyDiagnostics(SyntaxTrivia trivia, List<TestError> errors) { VerifyDiagnostics(trivia.ErrorsAndWarnings(), errors); } private void VerifyDiagnostics(IEnumerable<DiagnosticInfo> actual, List<TestError> expected) { Assert.Equal(actual.Count(), expected.Count); var actualErrors = (from e in actual orderby e.Code select new TestError(e.Code, e.Severity == DiagnosticSeverity.Warning)).ToList(); var expectedErrors = (from e in expected orderby e.ErrorCode select new TestError(e.ErrorCode, e.IsWarning)).ToList(); for (int i = 0; i < expected.Count; i++) { Assert.Equal(expectedErrors[i].ErrorCode, actualErrors[i].ErrorCode); Assert.Equal(expectedErrors[i].IsWarning, actualErrors[i].IsWarning); } } /// <summary> /// Verify if a given XmlElement is correct /// </summary> /// <param name="xmlElement">The XmlElement object to validate</param> /// <param name="tagName">The name of the tag the XML element should have</param> /// <param name="innerText">The text inside the XmlElement</param> /// private void VerifyXmlElement(XmlElementSyntax xmlElement, string tagName, string innerText) { // if the innerText is empty, then the content has no nodes. if (innerText == string.Empty) { Assert.Equal(0, xmlElement.Content.Count); } else { var elementInnerText = GetXmlElementText(xmlElement); Assert.Equal(innerText, elementInnerText); } Assert.Equal(tagName, xmlElement.StartTag.Name.LocalName.Value); Assert.Equal(tagName, xmlElement.EndTag.Name.LocalName.Value); } /// <summary> /// Gets the string representation for a XmlElementText /// </summary> /// <param name="xmlElement"></param> /// <returns></returns> private string GetXmlElementText(XmlElementSyntax xmlElement) { StringBuilder sb = new StringBuilder(); foreach (var element in xmlElement.Content) { if (element.GetType() == typeof(XmlElementSyntax)) { sb.Append(element.ToFullString()); } else if (element.GetType() == typeof(XmlTextSyntax)) { sb.Append((element as XmlTextSyntax).TextTokens.ToFullString()); } else if (element.GetType() == typeof(XmlCDataSectionSyntax)) { sb.Append(element.ToFullString()); } } return sb.ToString(); // return getTextFromTextTokens((xmlElement.Content[0] as XmlTextSyntax).TextTokens); } /// <summary> /// Verifies an empty XmlElement /// </summary> /// <param name="xmlElement">The XmlElement object to validate</param> /// <param name="tagName">The name of the tag the XML element should have</param> private void VerifyXmlElement(XmlEmptyElementSyntax xmlElement, string tagName) { Assert.Equal(tagName, xmlElement.Name.LocalName.Value); } /// <summary> /// Verify if the attributes for a given XML node match the expected ones /// </summary> /// <param name="xmlAttributes">The list of attributes to verify</param> /// <param name="attributes">The dictionary contains the key-value pair for the expected attribute values</param> private void VerifyXmlAttributes(SyntaxList<XmlAttributeSyntax> xmlAttributes, Dictionary<string, string> attributes) { // we have the same number of attributes Assert.Equal(attributes.Keys.Count, xmlAttributes.Count); foreach (XmlTextAttributeSyntax attribute in xmlAttributes) { // we make sure we have that attribute Assert.True(attributes.ContainsKey(attribute.Name.LocalName.Value as string)); // we make sure that the value for the attribute is the right one. Assert.Equal(attributes[attribute.Name.LocalName.Value as string], attribute.TextTokens.ToString()); } } /// <summary> /// This class is used to represent the expected errors /// </summary> private class TestError { public bool IsWarning { get; } public int ErrorCode { get; } public TestError(int code, bool warning) { this.IsWarning = warning; this.ErrorCode = code; } } #endregion } }