From 68e8fee5fcaa63bc62f565aa62aed6d7267b0a9f Mon Sep 17 00:00:00 2001 From: Dmitri Hrybenko Date: Thu, 17 Apr 2014 15:42:51 +0000 Subject: [PATCH] Comment parsing: put more information into the generated XML documents This commit adds infrastructure for conversion and testing it. The conversion is still incomplete, pending discussion about which tags should we use in the XML documents. I copied the RelaxNG schema from Clang, and will edit it accordingly. Swift SVN r16451 --- CMakeLists.txt | 6 + bindings/xml/comment-xml-schema.rng | 592 ++++++++++++++++++ include/swift/AST/Comment.h | 1 + include/swift/IDE/CommentConversion.h | 38 ++ include/swift/IDE/Utils.h | 9 +- include/swift/ReST/XMLUtils.h | 53 ++ lib/AST/RawComment.cpp | 10 + lib/IDE/CMakeLists.txt | 1 + lib/IDE/CommentConversion.cpp | 550 ++++++++++++++++ lib/IDE/Utils.cpp | 120 +--- lib/PrintAsObjC/PrintAsObjC.cpp | 226 +------ lib/ReST/Parser.cpp | 5 +- test/IDE/comment_to_xml.swift | 9 + .../comment_to_something_conversion.swift | 183 ++++++ .../Inputs/comments-expected-output.h | 106 +++- tools/swift-ide-test/CMakeLists.txt | 8 + tools/swift-ide-test/XMLValidator.cpp | 96 +++ tools/swift-ide-test/XMLValidator.h | 51 ++ unittests/ReST/ReSTTest.cpp | 34 +- 19 files changed, 1715 insertions(+), 383 deletions(-) create mode 100644 bindings/xml/comment-xml-schema.rng create mode 100644 include/swift/IDE/CommentConversion.h create mode 100644 include/swift/ReST/XMLUtils.h create mode 100644 lib/IDE/CommentConversion.cpp create mode 100644 test/IDE/comment_to_xml.swift create mode 100644 test/Inputs/comment_to_something_conversion.swift create mode 100644 tools/swift-ide-test/XMLValidator.cpp create mode 100644 tools/swift-ide-test/XMLValidator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 921663d9da298..a6f44080b6946 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,6 +206,12 @@ else() set(SWIFT_COMPILER "${PATH_TO_SWIFT_BUILD}/${CMAKE_CFG_INTDIR}/bin/swift") endif() +# FIXME: unify with CLANG_HAVE_LIBXML, which is set in LLVM anyway. +find_package(LibXml2) +if(LIBXML2_FOUND) + set(SWIFT_HAVE_LIBXML 1) +endif() + # Xcode: use libc++ and c++11 using proper build settings. if ( XCODE ) # Force usage of Clang. diff --git a/bindings/xml/comment-xml-schema.rng b/bindings/xml/comment-xml-schema.rng new file mode 100644 index 0000000000000..a8913a360b79d --- /dev/null +++ b/bindings/xml/comment-xml-schema.rng @@ -0,0 +1,592 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + template + specialization + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + template + specialization + partialSpecialization + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .*\S.* + + + + + + + + + + + + + + + + + + .*\S.* + + + + + + + + + .*\S.* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .*\S.* + + + + + + + + + + + + + + + + + + + + + + + + + + .*\S.* + + + + + + + + + + + + + + + + + + in + out + in,out + + + + + + + + + + + + + + + + + + + + + + + + + + + + \d+|\d+\.\d+|\d+\.\d+.\d+ + + + + + + + \d+|\d+\.\d+|\d+\.\d+.\d+ + + + + + + + \d+|\d+\.\d+|\d+\.\d+.\d+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + attention + author + authors + bug + copyright + date + invariant + note + post + pre + remark + remarks + sa + see + since + todo + version + warning + + + + + + + + + + preserve + + + + + code + verbatim + + + + + + + + + + + + + + .*\S.* + + + + + + .*\S.* + + + + + + .*\S.* + + + + + + .*\S.* + + + + + + + diff --git a/include/swift/AST/Comment.h b/include/swift/AST/Comment.h index 1dadb3ccd4190..a3c6bd8622d64 100644 --- a/include/swift/AST/Comment.h +++ b/include/swift/AST/Comment.h @@ -38,6 +38,7 @@ class FullComment { public: SmallVector Params; SmallVector Returns; + const llvm::rest::Paragraph *Brief = nullptr; SmallVector MiscTopLevelNodes; }; diff --git a/include/swift/IDE/CommentConversion.h b/include/swift/IDE/CommentConversion.h new file mode 100644 index 0000000000000..c9122ebc6229e --- /dev/null +++ b/include/swift/IDE/CommentConversion.h @@ -0,0 +1,38 @@ +//===--- CommentConversion.h - Conversion of comments to other formats ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_COMMENT_CONVERSION_H +#define SWIFT_IDE_COMMENT_CONVERSION_H + +#include "swift/Basic/LLVM.h" +#include + +namespace swift { +class Decl; +class FullComment; + +namespace ide { + +/// If the declaration has a documentation comment, prints the comment to \p OS +/// in Clang-like XML format. +/// +/// \returns true if the declaration has a documentation comment. +bool getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS); + +/// Converts the given comment to Doxygen. +void getDocumentationCommentAsDoxygen(const FullComment *FC, raw_ostream &OS); + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_COMMENT_CONVERSION_H + diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index 5e91c1c6092bf..5f4fbbf0c571b 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -21,8 +21,6 @@ namespace llvm { } namespace swift { -class Decl; - namespace ide { /// Returns true if the input source is fully formed, or false if, for example, @@ -30,13 +28,8 @@ namespace ide { bool isSourceInputComplete(std::unique_ptr MemBuf); bool isSourceInputComplete(StringRef Text); -/// If the declaration has a documentation comment, prints the comment to \p OS -/// in Clang-like XML format. -/// -/// \returns true if the declaration has a documentation comment. -bool getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS); - } // namespace ide } // namespace swift #endif // SWIFT_IDE_UTILS_H + diff --git a/include/swift/ReST/XMLUtils.h b/include/swift/ReST/XMLUtils.h new file mode 100644 index 0000000000000..365d85066faa7 --- /dev/null +++ b/include/swift/ReST/XMLUtils.h @@ -0,0 +1,53 @@ +//===--- XMLUtils.h - Various XML utility routines ------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_REST_XML_UTILS_H +#define LLVM_REST_XML_UTILS_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace rest { + +// FIXME: copied from Clang's +// CommentASTToXMLConverter::appendToResultWithXMLEscaping +static inline void appendWithXMLEscaping(raw_ostream &OS, StringRef S) { + for (const char C : S) { + switch (C) { + case '&': + OS << "&"; + break; + case '<': + OS << "<"; + break; + case '>': + OS << ">"; + break; + case '"': + OS << """; + break; + case '\'': + OS << "'"; + break; + default: + OS << C; + break; + } + } +} + +} // namespace rest +} // namespace llvm + +#endif // LLVM_REST_XML_UTILS_H + diff --git a/lib/AST/RawComment.cpp b/lib/AST/RawComment.cpp index a02c439024dc6..a3f5d2051f7d7 100644 --- a/lib/AST/RawComment.cpp +++ b/lib/AST/RawComment.cpp @@ -281,7 +281,17 @@ const FullComment::CommentParts &FullComment::getParts() const { return Parts.getValue(); Parts = CommentParts(); + bool IsFirstChild = true; for (const auto *N : Doc->getChildren()) { + // If the first document child is a paragraph, consider it a brief + // description. + if (IsFirstChild) { + IsFirstChild = false; + if (const auto *P = dyn_cast(N)) { + Parts->Brief = P; + continue; + } + } if (const auto *FL = dyn_cast(N)) { for (const auto *F : FL->getChildren()) { if (isFieldNamed(F, "param")) diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index a5576f04c52b3..016e5f5df9600 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -1,5 +1,6 @@ add_swift_library(swiftIDE CodeCompletion.cpp + CommentConversion.cpp ModuleInterfacePrinting.cpp REPLCodeCompletion.cpp SourceEntityWalker.cpp diff --git a/lib/IDE/CommentConversion.cpp b/lib/IDE/CommentConversion.cpp new file mode 100644 index 0000000000000..0b4cd055d7bb0 --- /dev/null +++ b/lib/IDE/CommentConversion.cpp @@ -0,0 +1,550 @@ +//===--- CommentConversion.cpp - Conversion of comments to other formats --===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/IDE/CommentConversion.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/Comment.h" +#include "swift/AST/Decl.h" +#include "swift/AST/USRGeneration.h" +#include "swift/Basic/SourceManager.h" +#include "swift/ReST/AST.h" +#include "swift/ReST/XMLUtils.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/Index/CommentToXML.h" + +using namespace llvm::rest; +using namespace swift; + +//===----------------------------------------------------------------------===// +// Conversion to XML. +//===----------------------------------------------------------------------===// + +namespace { +struct CommentToXMLConverter { + raw_ostream &OS; + + CommentToXMLConverter(raw_ostream &OS) : OS(OS) {} + + void printASTNode(const ReSTASTNode *N) { + switch (N->getKind()) { + case ASTNodeKind::Document: + llvm_unreachable("should never happen"); + break; + + case ASTNodeKind::Section: + case ASTNodeKind::Topic: + case ASTNodeKind::Sidebar: + case ASTNodeKind::Title: + case ASTNodeKind::Subtitle: + case ASTNodeKind::Transition: + llvm_unreachable("implement"); + + case ASTNodeKind::Paragraph: + printParagraph(cast(N)); + break; + case ASTNodeKind::BulletList: + printBulletList(cast(N)); + break; + case ASTNodeKind::EnumeratedList: + printEnumeratedList(cast(N)); + break; + case ASTNodeKind::DefinitionListItem: + printDefinitionListItem(cast(N)); + break; + case ASTNodeKind::DefinitionList: + printDefinitionList(cast(N)); + break; + case ASTNodeKind::Field: + printField(cast(N)); + break; + case ASTNodeKind::FieldList: + printFieldList(cast(N)); + break; + case ASTNodeKind::BlockQuote: + printBlockQuote(cast
(N)); + break; + case ASTNodeKind::TextAndInline: + printTextAndInline(cast(N)); + break; + } + } + + void printParagraph(const Paragraph *P) { + OS << ""; + printTextAndInline(P->getContent()); + OS << ""; + } + + void printBulletList(const BulletList *BL) { + // FIXME: print block markup. + for (unsigned i = 0, e = BL->getNumItems(); i != e; ++i) { + for (const auto *N : BL->getItemChildren(i)) { + printASTNode(N); + } + } + } + + void printEnumeratedList(const EnumeratedList *EL) { + // FIXME: print block markup. + for (unsigned i = 0, e = EL->getNumItems(); i != e; ++i) { + for (const auto *N : EL->getItemChildren(i)) { + printASTNode(N); + } + } + } + + void printDefinitionListItem(const DefinitionListItem *DLI) { + // FIXME: print real block markup. + OS << ""; + printASTNode(DLI->getTerm()); + OS << ""; + for (const auto *N : DLI->getClassifiers()) { + printASTNode(N); + } + + for (const auto *N : DLI->getDefinitionChildren()) { + printASTNode(N); + } + } + + void printDefinitionList(const DefinitionList *DL) { + // FIXME: print block markup. + for (const auto *N : DL->getChildren()) { + printASTNode(N); + } + } + + void printField(const Field *F) { + // FIXME: print real block markup. + OS << ""; + printASTNode(F->getName()); + OS << ""; + for (const auto *N : F->getBodyChildren()) { + printASTNode(N); + } + } + + void printFieldList(const FieldList *FL) { + // FIXME: print block markup. + for (const auto *F : FL->getChildren()) { + printASTNode(F); + } + } + + void printBlockQuote(const BlockQuote *BQ) { + // FIXME: print block markup. + for (const auto *N : BQ->getChildren()) { + printASTNode(N); + } + } + + void printTextAndInline(const TextAndInline *T) { + if (T->isLinePart()) { + LinePart LP = T->getLinePart(); + appendWithXMLEscaping(OS, LP.Text); + } else { + LineListRef LL = T->getLines(); + for (unsigned i = 0, e = LL.size(); i != e; ++i) { + appendWithXMLEscaping(OS, LL[i].Text.drop_front(LL[i].FirstTextByte)); + if (i != e - 1) + OS << " "; + } + } + } + + void printOrphanField(const Field *F) { + // FIXME: print block markup. + printField(F); + } + + void printAsParameter(const Field *F) { + OS << ""; + // FIXME: extract parameter name. + OS << "x"; + OS << "in"; + for (const auto *N : F->getBodyChildren()) { + printASTNode(N); + } + OS << ""; + } + + void printAsReturns(const Field *F) { + for (const auto *N : F->getBodyChildren()) { + printASTNode(N); + } + } + + void visitFullComment(const FullComment *FC); +}; +} // unnamed namespace + +void CommentToXMLConverter::visitFullComment(const FullComment *FC) { + const Decl *D = FC->getDecl(); + const auto &Parts = FC->getParts(); + + StringRef RootEndTag; + if (isa(D)) { + OS << "getLoc(); + if (Loc.isValid()) { + const auto &SM = D->getASTContext().SourceMgr; + unsigned BufferID = SM.findBufferContainingLoc(Loc); + StringRef FileName = SM->getMemoryBuffer(BufferID)->getBufferIdentifier(); + auto LineAndColumn = SM.getLineAndColumn(Loc); + OS << " file=\""; + appendWithXMLEscaping(OS, FileName); + OS << "\""; + OS << " line=\"" << LineAndColumn.first << "\" column=\"" + << LineAndColumn.second << "\""; + } + } + + // Finish the root tag. + OS << ">"; + + auto *VD = dyn_cast(D); + + OS << ""; + if (VD && VD->hasName()) + OS << VD->getFullName(); + OS << ""; + + if (VD) { + llvm::SmallString<64> SS; + bool Failed; + { + llvm::raw_svector_ostream OS(SS); + Failed = ide::printDeclUSR(VD, OS); + } + if (!Failed && !SS.empty()) { + OS << "" << SS << ""; + } + } + + // FIXME: + if (Parts.Brief) { + OS << ""; + printASTNode(Parts.Brief); + OS << ""; + } + + if (!Parts.MiscTopLevelNodes.empty()) { + OS << ""; + for (const auto *N : Parts.MiscTopLevelNodes) { + if (const auto *F = dyn_cast(N)) { + printOrphanField(F); + continue; + } + printASTNode(N); + } + OS << ""; + } + + if (!Parts.Params.empty()) { + OS << ""; + for (const auto *N : Parts.Params) { + printAsParameter(N); + } + OS << ""; + } + if (!Parts.Returns.empty()) { + OS << ""; + for (const auto *N : Parts.Returns) { + printAsReturns(N); + } + OS << ""; + } + + OS << RootEndTag; +} + +static bool getClangDocumentationCommentAsXML(const clang::Decl *D, + raw_ostream &OS) { + const auto &ClangContext = D->getASTContext(); + const clang::comments::FullComment *FC = + ClangContext.getCommentForDecl(D, /*PP=*/nullptr); + if (!FC) + return false; + + // FIXME: hang the converter object somewhere so that it is persistent + // between requests to this AST. + clang::index::CommentToXMLConverter Converter; + + llvm::SmallString<1024> XML; + Converter.convertCommentToXML(FC, XML, ClangContext); + OS << XML; + return true; +} + +bool ide::getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS) { + auto MaybeClangNode = D->getClangNode(); + if (MaybeClangNode) { + if (auto *CD = MaybeClangNode.getAsDecl()) + return getClangDocumentationCommentAsXML(CD, OS); + return false; + } + + CommentContext TheCommentContext; + auto *FC = getFullComment(TheCommentContext, D); + if (!FC) + return false; + + CommentToXMLConverter Converter(OS); + Converter.visitFullComment(FC); + + OS.flush(); + return true; +} + +//===----------------------------------------------------------------------===// +// Conversion to Doxygen. +//===----------------------------------------------------------------------===// + +namespace { +struct CommentToDoxygenConverter { + raw_ostream &OS; + unsigned PendingNewlines = 1; + + CommentToDoxygenConverter(raw_ostream &OS) : OS(OS) {} + + void print(StringRef Text) { + for (unsigned i = 0; i != PendingNewlines; ++i) { + OS << "\n///"; + if (i == PendingNewlines - 1) + OS << " "; + } + PendingNewlines = 0; + OS << Text; + } + + void printNewline() { + PendingNewlines++; + } + + void printASTNode(const ReSTASTNode *N) { + switch (N->getKind()) { + case ASTNodeKind::Document: + llvm_unreachable("should never happen"); + break; + + case ASTNodeKind::Section: + case ASTNodeKind::Topic: + case ASTNodeKind::Sidebar: + case ASTNodeKind::Title: + case ASTNodeKind::Subtitle: + case ASTNodeKind::Transition: + llvm_unreachable("implement"); + + case ASTNodeKind::Paragraph: + printParagraph(cast(N)); + break; + case ASTNodeKind::BulletList: + printBulletList(cast(N)); + break; + case ASTNodeKind::EnumeratedList: + printEnumeratedList(cast(N)); + break; + case ASTNodeKind::DefinitionListItem: + printDefinitionListItem(cast(N)); + break; + case ASTNodeKind::DefinitionList: + printDefinitionList(cast(N)); + break; + case ASTNodeKind::Field: + printField(cast(N)); + break; + case ASTNodeKind::FieldList: + printFieldList(cast(N)); + break; + case ASTNodeKind::BlockQuote: + printBlockQuote(cast
(N)); + break; + case ASTNodeKind::TextAndInline: + printTextAndInline(cast(N)); + break; + } + } + + void printParagraph(const Paragraph *P) { + print("

"); + printTextAndInline(P->getContent()); + print("

"); + } + + void printBulletList(const BulletList *BL) { + print("
    "); + for (unsigned i = 0, e = BL->getNumItems(); i != e; ++i) { + print("
  • "); + for (const auto *N : BL->getItemChildren(i)) { + printASTNode(N); + } + print("
  • "); + } + print("
"); + } + + void printEnumeratedList(const EnumeratedList *EL) { + print("
    "); + for (unsigned i = 0, e = EL->getNumItems(); i != e; ++i) { + print("
  1. "); + for (const auto *N : EL->getItemChildren(i)) { + printASTNode(N); + } + print("
  2. "); + } + print("
"); + } + + void printDefinitionListItem(const DefinitionListItem *DLI) { + print("
"); + printASTNode(DLI->getTerm()); + for (const auto *N : DLI->getClassifiers()) { + printASTNode(N); + } + print("
"); + + print("
"); + for (const auto *N : DLI->getDefinitionChildren()) { + printASTNode(N); + } + print("
"); + } + + void printDefinitionList(const DefinitionList *DL) { + print("
"); + for (const auto *N : DL->getChildren()) { + printASTNode(N); + } + print("
"); + } + + void printField(const Field *F) { + print("
"); + printASTNode(F->getName()); + print("
"); + print("
"); + for (const auto *N : F->getBodyChildren()) { + printASTNode(N); + } + print("
"); + } + + void printFieldList(const FieldList *FL) { + print("
"); + for (const auto *F : FL->getChildren()) { + printASTNode(F); + } + print("
"); + } + + void printBlockQuote(const BlockQuote *BQ) { + print("
"); + for (const auto *N : BQ->getChildren()) { + printASTNode(N); + } + print("
"); + } + + void printTextAndInline(const TextAndInline *T) { + if (T->isLinePart()) { + LinePart LP = T->getLinePart(); + print(LP.Text); + } else { + LineListRef LL = T->getLines(); + for (unsigned i = 0, e = LL.size(); i != e; ++i) { + print(LL[i].Text.drop_front(LL[i].FirstTextByte)); + if (i != e - 1) + printNewline(); + } + } + } + + void printOrphanField(const Field *F) { + print("
"); + printField(F); + print("
"); + } + + void printBlockCommandContent(ArrayRef Nodes) { + if (Nodes.size() == 1) { + if (const auto *P = dyn_cast(Nodes[0])) { + printTextAndInline(P->getContent()); + return; + } + } + for (const auto *N : Nodes) { + printASTNode(N); + } + } + + void printAsDoxygenParam(const Field *F) { + print("\\param "); + printBlockCommandContent(F->getBodyChildren()); + printNewline(); + } + + void printAsDoxygenReturns(const Field *F) { + print("\\returns "); + printBlockCommandContent(F->getBodyChildren()); + printNewline(); + } +}; +} // unnamed namespace + +void ide::getDocumentationCommentAsDoxygen(const FullComment *FC, raw_ostream &OS) { + CommentToDoxygenConverter Converter(OS); + const auto &Parts = FC->getParts(); + + if (Parts.Brief) { + Converter.printTextAndInline(Parts.Brief->getContent()); + Converter.printNewline(); + Converter.printNewline(); + } + + for (const auto *N : Parts.MiscTopLevelNodes) { + if (const auto *F = dyn_cast(N)) { + Converter.printOrphanField(F); + Converter.printNewline(); + continue; + } + if (const auto *P = dyn_cast(N)) { + Converter.printTextAndInline(P->getContent()); + Converter.printNewline(); + Converter.printNewline(); + continue; + } + Converter.printASTNode(N); + Converter.printNewline(); + } + for (const auto *N : Parts.Params) { + Converter.printAsDoxygenParam(N); + Converter.printNewline(); + } + for (const auto *N : Parts.Returns) { + Converter.printAsDoxygenReturns(N); + Converter.printNewline(); + } + if (Converter.PendingNewlines != 0) + OS << "\n"; +} + diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index d622fa1dbb825..ca3953e07c7c9 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -1,4 +1,4 @@ -//===- Utils.cpp - Misc utilities -----------------------------------------===// +//===--- Utils.cpp - Misc utilities ---------------------------------------===// // // This source file is part of the Swift.org open source project // @@ -17,14 +17,9 @@ #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/Module.h" #include "swift/AST/SearchPathOptions.h" -#include "swift/AST/USRGeneration.h" #include "swift/Parse/Parser.h" #include "swift/Parse/PersistentParserState.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" -#include "clang/Index/CommentToXML.h" using namespace swift; using namespace ide; @@ -59,116 +54,3 @@ bool ide::isSourceInputComplete(StringRef Text) { return ide::isSourceInputComplete(std::move(InputBuf)); } -// FIXME: copied from Clang's -// CommentASTToXMLConverter::appendToResultWithXMLEscaping -static void appendWithXMLEscaping(raw_ostream &OS, StringRef S) { - for (const char C : S) { - switch (C) { - case '&': - OS << "&"; - break; - case '<': - OS << "<"; - break; - case '>': - OS << ">"; - break; - case '"': - OS << """; - break; - case '\'': - OS << "'"; - break; - default: - OS << C; - break; - } - } -} - -static bool getClangDocumentationCommentAsXML(const clang::Decl *D, - raw_ostream &OS) { - const auto &ClangContext = D->getASTContext(); - const clang::comments::FullComment *FC = - ClangContext.getCommentForDecl(D, /*PP=*/nullptr); - if (!FC) - return false; - - // FIXME: hang the converter object somewhere so that it is persistent - // between requests to this AST. - clang::index::CommentToXMLConverter Converter; - - llvm::SmallString<1024> XML; - Converter.convertCommentToXML(FC, XML, ClangContext); - OS << XML; - return true; -} - -bool ide::getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS) { - auto MaybeClangNode = D->getClangNode(); - if (MaybeClangNode) { - if (auto *CD = MaybeClangNode.getAsDecl()) - return getClangDocumentationCommentAsXML(CD, OS); - return false; - } - - StringRef BriefComment = D->getBriefComment(); - if (BriefComment.empty()) - return false; - - StringRef RootEndTag; - if (isa(D)) { - OS << "getLoc(); - if (Loc.isValid()) { - const auto &SM = D->getASTContext().SourceMgr; - unsigned BufferID = SM.findBufferContainingLoc(Loc); - StringRef FileName = SM->getMemoryBuffer(BufferID)->getBufferIdentifier(); - auto LineAndColumn = SM.getLineAndColumn(Loc); - OS << " file=\""; - appendWithXMLEscaping(OS, FileName); - OS << "\""; - OS << " line=\"" << LineAndColumn.first - << "\" column=\"" << LineAndColumn.second << "\""; - } - - // Finish the root tag. - OS << ">"; - - auto *VD = dyn_cast(D); - - OS << ""; - if (VD && VD->hasName()) - OS << VD->getFullName(); - OS << ""; - - if (VD) { - llvm::SmallString<64> SS; - bool Failed; - { - llvm::raw_svector_ostream OS(SS); - Failed = ide::printDeclUSR(VD, OS); - } - if (!Failed && !SS.empty()) { - OS << "" << SS << ""; - } - } - - // FIXME: - OS << ""; - appendWithXMLEscaping(OS, BriefComment); - OS << ""; - - OS << RootEndTag; - - OS.flush(); - return true; -} - diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp index d3c42143f08f4..3750fa957c836 100644 --- a/lib/PrintAsObjC/PrintAsObjC.cpp +++ b/lib/PrintAsObjC/PrintAsObjC.cpp @@ -19,237 +19,15 @@ #include "swift/Basic/Version.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" -#include "swift/ReST/AST.h" +#include "swift/IDE/CommentConversion.h" #include "clang/AST/Decl.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Path.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" -using namespace llvm::rest; using namespace swift; -namespace { -struct CommentToDoxygenConverter { - raw_ostream &OS; - unsigned PendingNewlines = 1; - - CommentToDoxygenConverter(raw_ostream &OS) : OS(OS) {} - - void print(StringRef Text) { - for (unsigned i = 0; i != PendingNewlines; ++i) { - OS << "\n///"; - if (i == PendingNewlines - 1) - OS << " "; - } - PendingNewlines = 0; - OS << Text; - } - - void printNewline() { - PendingNewlines++; - } - - void printASTNode(const ReSTASTNode *N) { - switch (N->getKind()) { - case ASTNodeKind::Document: - llvm_unreachable("should never happen"); - break; - - case ASTNodeKind::Section: - case ASTNodeKind::Topic: - case ASTNodeKind::Sidebar: - case ASTNodeKind::Title: - case ASTNodeKind::Subtitle: - case ASTNodeKind::Transition: - llvm_unreachable("implement"); - - case ASTNodeKind::Paragraph: - printParagraph(cast(N)); - break; - case ASTNodeKind::BulletList: - printBulletList(cast(N)); - break; - case ASTNodeKind::EnumeratedList: - printEnumeratedList(cast(N)); - break; - case ASTNodeKind::DefinitionListItem: - printDefinitionListItem(cast(N)); - break; - case ASTNodeKind::DefinitionList: - printDefinitionList(cast(N)); - break; - case ASTNodeKind::Field: - printField(cast(N)); - break; - case ASTNodeKind::FieldList: - printFieldList(cast(N)); - break; - case ASTNodeKind::BlockQuote: - printBlockQuote(cast
(N)); - break; - case ASTNodeKind::TextAndInline: - printTextAndInline(cast(N)); - break; - } - } - - void printParagraph(const Paragraph *P) { - print("

"); - printTextAndInline(P->getContent()); - print("

"); - } - - void printBulletList(const BulletList *BL) { - print("
    "); - for (unsigned i = 0, e = BL->getNumItems(); i != e; ++i) { - print("
  • "); - for (const auto *N : BL->getItemChildren(i)) { - printASTNode(N); - } - print("
  • "); - } - print("
"); - } - - void printEnumeratedList(const EnumeratedList *EL) { - print("
    "); - for (unsigned i = 0, e = EL->getNumItems(); i != e; ++i) { - print("
  1. "); - for (const auto *N : EL->getItemChildren(i)) { - printASTNode(N); - } - print("
  2. "); - } - print("
"); - } - - void printDefinitionListItem(const DefinitionListItem *DLI) { - print("
"); - printASTNode(DLI->getTerm()); - for (const auto *N : DLI->getClassifiers()) { - printASTNode(N); - } - print("
"); - - print("
"); - for (const auto *N : DLI->getDefinitionChildren()) { - printASTNode(N); - } - print("
"); - } - - void printDefinitionList(const DefinitionList *DL) { - print("
"); - for (const auto *N : DL->getChildren()) { - printASTNode(N); - } - print("
"); - } - - void printField(const Field *F) { - print("
"); - printASTNode(F->getName()); - print("
"); - print("
"); - for (const auto *N : F->getBodyChildren()) { - printASTNode(N); - } - print("
"); - } - - void printFieldList(const FieldList *FL) { - print("
"); - for (const auto *F : FL->getChildren()) { - printASTNode(F); - } - print("
"); - } - - void printBlockQuote(const BlockQuote *BQ) { - print("
"); - for (const auto *N : BQ->getChildren()) { - printASTNode(N); - } - print("
"); - } - - void printTextAndInline(const TextAndInline *T) { - if (T->isLinePart()) { - LinePart LP = T->getLinePart(); - print(LP.Text); - } else { - LineListRef LL = T->getLines(); - for (unsigned i = 0, e = LL.size(); i != e; ++i) { - print(LL[i].Text.drop_front(LL[i].FirstTextByte)); - if (i != e - 1) - printNewline(); - } - } - } - - void printOrphanField(const Field *F) { - print("
"); - printField(F); - print("
"); - } - - void printBlockCommandContent(ArrayRef Nodes) { - if (Nodes.size() == 1) { - if (const auto *P = dyn_cast(Nodes[0])) { - printTextAndInline(P->getContent()); - return; - } - } - for (const auto *N : Nodes) { - printASTNode(N); - } - } - - void printAsDoxygenParam(const Field *F) { - print("\\param "); - printBlockCommandContent(F->getBodyChildren()); - printNewline(); - } - - void printAsDoxygenReturns(const Field *F) { - print("\\returns "); - printBlockCommandContent(F->getBodyChildren()); - printNewline(); - } -}; -} // unnamed namespace - -static void convertCommentToDoxygen(const FullComment *FC, raw_ostream &OS) { - CommentToDoxygenConverter Converter(OS); - auto &Parts = FC->getParts(); - for (const auto *N : Parts.MiscTopLevelNodes) { - if (const auto *F = dyn_cast(N)) { - Converter.printOrphanField(F); - Converter.printNewline(); - continue; - } - if (const auto *P = dyn_cast(N)) { - Converter.printTextAndInline(P->getContent()); - Converter.printNewline(); - Converter.printNewline(); - continue; - } - Converter.printASTNode(N); - Converter.printNewline(); - } - for (const auto *N : Parts.Params) { - Converter.printAsDoxygenParam(N); - Converter.printNewline(); - } - for (const auto *N : Parts.Returns) { - Converter.printAsDoxygenReturns(N); - Converter.printNewline(); - } - if (Converter.PendingNewlines != 0) - OS << "\n"; -} - namespace { class ObjCPrinter : private DeclVisitor, private TypeVisitor { @@ -336,7 +114,7 @@ class ObjCPrinter : private DeclVisitor, void printDocumentationComment(Decl *D) { CommentContext TheCommentContext; if (auto *FC = getFullComment(TheCommentContext, D)) - convertCommentToDoxygen(FC, os); + ide::getDocumentationCommentAsDoxygen(FC, os); } void visitClassDecl(ClassDecl *CD) { diff --git a/lib/ReST/Parser.cpp b/lib/ReST/Parser.cpp index 650ab836ea26d..9366702319977 100644 --- a/lib/ReST/Parser.cpp +++ b/lib/ReST/Parser.cpp @@ -13,6 +13,7 @@ #include "swift/ReST/Parser.h" #include "Detail.h" #include "swift/ReST/LineList.h" +#include "swift/ReST/XMLUtils.h" #include "llvm/Support/ErrorHandling.h" #include "clang/Basic/CharInfo.h" @@ -950,11 +951,11 @@ struct CommentToDocutilsXMLConverter { void printTextAndInline(const TextAndInline *T) { if (T->isLinePart()) { LinePart LP = T->getLinePart(); - OS << LP.Text; + appendWithXMLEscaping(OS, LP.Text); } else { LineListRef LL = T->getLines(); for (unsigned i = 0, e = LL.size(); i != e; ++i) { - OS << LL[i].Text.drop_front(LL[i].FirstTextByte); + appendWithXMLEscaping(OS, LL[i].Text.drop_front(LL[i].FirstTextByte)); if (i != e - 1) OS << '\n'; } diff --git a/test/IDE/comment_to_xml.swift b/test/IDE/comment_to_xml.swift new file mode 100644 index 0000000000000..84be9bf0519c8 --- /dev/null +++ b/test/IDE/comment_to_xml.swift @@ -0,0 +1,9 @@ +//===--- Check that we convert comments to XML correctly. + +// RUN: %swift -parse -verify %S/../Inputs/comment_to_something_conversion.swift +// RUN: %swift-ide-test -print-comments -source-filename %S/../Inputs/comment_to_something_conversion.swift -comments-xml-schema %S/../../bindings/xml/comment-xml-schema.rng > %t.txt +// RUN: FileCheck %S/../Inputs/comment_to_something_conversion.swift < %t.txt +// RUN: FileCheck %s -check-prefix=WRONG < %t.txt + +// WRONG-NOT: CommentXMLInvalid + diff --git a/test/Inputs/comment_to_something_conversion.swift b/test/Inputs/comment_to_something_conversion.swift new file mode 100644 index 0000000000000..7264866b8363b --- /dev/null +++ b/test/Inputs/comment_to_something_conversion.swift @@ -0,0 +1,183 @@ +// This is an input file for comment-to-{XML,Doxygen} conversion tests. +// +// Please keep this file in alphabetical order! + +@objc class A000 {} +// CHECK:.swift:[[@LINE-1]]:13: Class/A000 {{.*}} FullCommentAsXML=none + +/// Aaa. A010. Bbb. +@objc class A010_AttachToEntities { +// CHECK: swift:[[@LINE-1]]:13: Class/A010_AttachToEntities {{.*}} FullCommentAsXML=[A010_AttachToEntitiess:C14swift_ide_test21A010_AttachToEntitiesAaa. A010. Bbb.] + + /// Aaa. init(). + init() {} +// CHECK: swift:[[@LINE-1]]:3: Constructor/A010_AttachToEntities.init {{.*}} FullCommentAsXML=[init()s:FC14swift_ide_test21A010_AttachToEntitiescFMS0_FT_S0_Aaa. init().] + + /// Aaa. subscript(i: Int). + subscript(i: Int) -> Int { +// CHECK: swift:[[@LINE-1]]:3: Subscript/A010_AttachToEntities.subscript {{.*}} FullCommentAsXML=[subscripts:sC14swift_ide_test21A010_AttachToEntities9subscriptFT1iSi_SiAaa. subscript(i: Int).] + get { +// CHECK: swift:[[@LINE-1]]:5: Func/A010_AttachToEntities. {{.*}} FullCommentAsXML=none + return 0 + } + set {} +// CHECK: swift:[[@LINE-1]]:5: Func/A010_AttachToEntities. {{.*}} FullCommentAsXML=none + } + + /// Aaa. v1. + var v1: Int = 0 +// CHECK: swift:[[@LINE-1]]:7: Var/A010_AttachToEntities.v1 {{.*}} FullCommentAsXML=[v1s:vC14swift_ide_test21A010_AttachToEntities2v1SiAaa. v1.] +} + +@objc class A100_EmptyComments { + /// + func f0() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A100_EmptyComments.f0 {{.*}} FullCommentAsXML=[f0()s:FC14swift_ide_test18A100_EmptyComments2f0FS0_FT_T_] + + /// Aaa. + func f1() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A100_EmptyComments.f1 {{.*}} FullCommentAsXML=[f1()s:FC14swift_ide_test18A100_EmptyComments2f1FS0_FT_T_Aaa.] + + /** */ + func f2() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A100_EmptyComments.f2 {{.*}} FullCommentAsXML=[f2()s:FC14swift_ide_test18A100_EmptyComments2f2FS0_FT_T_] + + /** + */ + func f3() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A100_EmptyComments.f3 {{.*}} FullCommentAsXML=[f3()s:FC14swift_ide_test18A100_EmptyComments2f3FS0_FT_T_] + + /** + * Aaa. + */ + func f4() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A100_EmptyComments.f4 {{.*}} FullCommentAsXML=[f4()s:FC14swift_ide_test18A100_EmptyComments2f4FS0_FT_T_Aaa.] +} + +@objc class A110_Escaping { + /// & < > " ' + func f0() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A110_Escaping.f0 {{.*}} FullCommentAsXML=[f0()s:FC14swift_ide_test13A110_Escaping2f0FS0_FT_T_& < > " '] +} + +@objc class A120_Brief { + /// Aaa. + func f0() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A120_Brief.f0 {{.*}} FullCommentAsXML=[f0()s:FC14swift_ide_test10A120_Brief2f0FS0_FT_T_Aaa.] + + /// Aaa. + /// + /// Bbb. + func f1() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A120_Brief.f1 {{.*}} FullCommentAsXML=[f1()s:FC14swift_ide_test10A120_Brief2f1FS0_FT_T_Aaa.Bbb.] + + ///Aaa. + /// + /// Bbb. + func f2() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A120_Brief.f2 {{.*}} FullCommentAsXML=[f2()s:FC14swift_ide_test10A120_Brief2f2FS0_FT_T_Aaa.Bbb.] + + ///Aaa. + /// + ///Bbb. + func f3() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A120_Brief.f3 {{.*}} FullCommentAsXML=[f3()s:FC14swift_ide_test10A120_Brief2f3FS0_FT_T_Aaa.Bbb.] +} + +@objc class A200_ParamAndReturns { + /// Aaa. f0. + /// + /// :param: first Bbb. + /// + /// :param: second Ccc. Ddd. + /// Eee. + func f0(first: Int, second: Double) {} +// CHECK: swift:[[@LINE-1]]:8: Func/A200_ParamAndReturns.f0 {{.*}} FullCommentAsXML=[f0(first:second:)s:FC14swift_ide_test20A200_ParamAndReturns2f0FS0_FT5firstSi6secondSd_T_Aaa. f0.xinfirst Bbb.xinsecond Ccc. Ddd. Eee.] + + /// Aaa. f1. + /// + /// :param: first Bbb. + /// + /// :returns: Ccc. + /// Ddd. + func f1(first: Int) {} +// CHECK: swift:[[@LINE-1]]:8: Func/A200_ParamAndReturns.f1 {{.*}} FullCommentAsXML=[f1(first:)s:FC14swift_ide_test20A200_ParamAndReturns2f1FS0_FT5firstSi_T_Aaa. f1.xinfirst Bbb.Ccc. Ddd.] + + /// Aaa. f2. + /// + /// :returns: Ccc. + /// Ddd. + /// + /// :returns: Eee. + /// Fff. + func f2() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A200_ParamAndReturns.f2 {{.*}} FullCommentAsXML=[f2()s:FC14swift_ide_test20A200_ParamAndReturns2f2FS0_FT_T_Aaa. f2.Ccc. Ddd.Eee. Fff.] +} + +@objc class A210_BulletList { + /// * Aaa. + /// + /// * Bbb. + /// Ccc. + func f0() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A210_BulletList.f0 {{.*}} FullCommentAsXML=[f0()s:FC14swift_ide_test15A210_BulletList2f0FS0_FT_T_Aaa.Bbb. Ccc.] +} + +@objc class A220_EnumeratedList { + /// 1. Aaa. + /// + /// 2. Bbb. + /// Ccc. + func f0() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A220_EnumeratedList.f0 {{.*}} FullCommentAsXML=[f0()s:FC14swift_ide_test19A220_EnumeratedList2f0FS0_FT_T_Aaa.Bbb. Ccc.] +} + +@objc class A230_DefinitionList { + /// Aaa + /// Bbb. + /// + /// Ccc + /// Ddd. + /// + /// Eee : Fff + /// Ggg. + /// + /// ``Hhh`` + /// Jjj. + func f0() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A230_DefinitionList.f0 {{.*}} FullCommentAsXML=[f0()s:FC14swift_ide_test19A230_DefinitionList2f0FS0_FT_T_AaaBbb.CccDdd.Eee : FffGgg.``Hhh``Jjj.] +} + +@objc class A240_FieldList { + /// :unknown: Aaa. + /// Bbb. + func f0() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A240_FieldList.f0 {{.*}} FullCommentAsXML=[f0()s:FC14swift_ide_test14A240_FieldList2f0FS0_FT_T_unknownAaa. Bbb.] + + /// * Aaa. + /// + /// :param: Aaa. + /// :returns: Bbb. + /// :unknown: Ccc. + func f1() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A240_FieldList.f1 {{.*}} FullCommentAsXML=[f1()s:FC14swift_ide_test14A240_FieldList2f1FS0_FT_T_Aaa.paramAaa. :returns: Bbb. :unknown: Ccc.] +} + +@objc class A250_OptionList { + /// -a Aaa. + /// -b Bbb. + /// Ccc. + func f0() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A250_OptionList.f0 {{.*}} FullCommentAsXML=[f0()s:FC14swift_ide_test15A250_OptionList2f0FS0_FT_T_-a Aaa. -b Bbb.Ccc.] +} + +@objc class A260_BlockQuote { + /// Aaa. + /// + /// Bbb. + /// + /// Ccc. + func f0() {} +// CHECK: swift:[[@LINE-1]]:8: Func/A260_BlockQuote.f0 {{.*}} FullCommentAsXML=[f0()s:FC14swift_ide_test15A260_BlockQuote2f0FS0_FT_T_Aaa.Bbb.Ccc.] +} + diff --git a/test/PrintAsObjC/Inputs/comments-expected-output.h b/test/PrintAsObjC/Inputs/comments-expected-output.h index 045898fa7febd..eddbaa8208d39 100644 --- a/test/PrintAsObjC/Inputs/comments-expected-output.h +++ b/test/PrintAsObjC/Inputs/comments-expected-output.h @@ -5,9 +5,22 @@ SWIFT_CLASS("_TtC8comments4A000") -/// Aaa. A10. Bbb. -SWIFT_CLASS("_TtC8comments4A010") -@interface A010 +/// Aaa. A010. Bbb. +SWIFT_CLASS("_TtC8comments21A010_AttachToEntities") +@interface A010_AttachToEntities + +/// Aaa. init(). +- (instancetype)init OBJC_DESIGNATED_INITIALIZER; +- (NSInteger)objectAtIndexedSubscript:(NSInteger)i; +- (void)setObject:(NSInteger)newValue atIndexedSubscript:(NSInteger)i; + +/// Aaa. v1. +@property (nonatomic) NSInteger v1; +@end + + +SWIFT_CLASS("_TtC8comments18A100_EmptyComments") +@interface A100_EmptyComments - (void)f0; @@ -20,44 +33,76 @@ SWIFT_CLASS("_TtC8comments4A010") ///
  • Aaa.

- (void)f4; +- (instancetype)init OBJC_DESIGNATED_INITIALIZER; +@end + + +SWIFT_CLASS("_TtC8comments13A110_Escaping") +@interface A110_Escaping + +/// & < > " ' +- (void)f0; +- (instancetype)init OBJC_DESIGNATED_INITIALIZER; +@end + + +SWIFT_CLASS("_TtC8comments10A120_Brief") +@interface A120_Brief + +/// Aaa. +- (void)f0; + +/// Aaa. +/// +/// Bbb. +- (void)f1; -/// Aaa. f5. +/// Aaa. +/// +///

Bbb.

+- (void)f2; + +/// Aaa. +/// +/// Bbb. +- (void)f3; +- (instancetype)init OBJC_DESIGNATED_INITIALIZER; +@end + + +SWIFT_CLASS("_TtC8comments20A200_ParamAndReturns") +@interface A200_ParamAndReturns + +/// Aaa. f0. /// /// \param first Bbb. /// /// \param second Ccc. Ddd. /// Eee. -- (void)f5:(NSInteger)first second:(double)second; +- (void)f0:(NSInteger)first second:(double)second; -/// Aaa. f6. +/// Aaa. f1. /// /// \param first Bbb. /// /// \returns Ccc. /// Ddd. -- (void)f6:(NSInteger)first; +- (void)f1:(NSInteger)first; -/// Aaa. f7. +/// Aaa. f2. /// /// \returns Ccc. /// Ddd. /// /// \returns Eee. /// Fff. -- (void)f7; - -/// Aaa. init(). +- (void)f2; - (instancetype)init OBJC_DESIGNATED_INITIALIZER; -- (NSInteger)objectAtIndexedSubscript:(NSInteger)i; -- (void)setObject:(NSInteger)newValue atIndexedSubscript:(NSInteger)i; - -/// Aaa. v1. -@property (nonatomic) NSInteger v1; @end -SWIFT_CLASS("_TtC8comments15A020_BulletList") -@interface A020_BulletList +SWIFT_CLASS("_TtC8comments15A210_BulletList") +@interface A210_BulletList ///
  • Aaa.

  • Bbb. /// Ccc.

@@ -66,8 +111,8 @@ SWIFT_CLASS("_TtC8comments15A020_BulletList") @end -SWIFT_CLASS("_TtC8comments19A030_EnumeratedList") -@interface A030_EnumeratedList +SWIFT_CLASS("_TtC8comments19A220_EnumeratedList") +@interface A220_EnumeratedList ///
  1. Aaa.

  2. Bbb. /// Ccc.

@@ -76,8 +121,8 @@ SWIFT_CLASS("_TtC8comments19A030_EnumeratedList") @end -SWIFT_CLASS("_TtC8comments19A040_DefinitionList") -@interface A040_DefinitionList +SWIFT_CLASS("_TtC8comments19A230_DefinitionList") +@interface A230_DefinitionList ///
Aaa

Bbb.

Ccc

Ddd.

Eee : Fff

Ggg.

``Hhh``

Jjj.

- (void)f0; @@ -85,18 +130,23 @@ SWIFT_CLASS("_TtC8comments19A040_DefinitionList") @end -SWIFT_CLASS("_TtC8comments14A050_FieldList") -@interface A050_FieldList +SWIFT_CLASS("_TtC8comments14A240_FieldList") +@interface A240_FieldList ///
unknown

Aaa. /// Bbb.

- (void)f0; + +///
  • Aaa.

    param

    Aaa. +/// :returns: Bbb. +/// :unknown: Ccc.

+- (void)f1; - (instancetype)init OBJC_DESIGNATED_INITIALIZER; @end -SWIFT_CLASS("_TtC8comments15A060_OptionList") -@interface A060_OptionList +SWIFT_CLASS("_TtC8comments15A250_OptionList") +@interface A250_OptionList /// -a Aaa. /// -b Bbb. @@ -107,8 +157,8 @@ SWIFT_CLASS("_TtC8comments15A060_OptionList") @end -SWIFT_CLASS("_TtC8comments15A070_BlockQuote") -@interface A070_BlockQuote +SWIFT_CLASS("_TtC8comments15A260_BlockQuote") +@interface A260_BlockQuote /// Aaa. /// diff --git a/tools/swift-ide-test/CMakeLists.txt b/tools/swift-ide-test/CMakeLists.txt index 09dcd9e982ecf..45b7c694c2d6b 100644 --- a/tools/swift-ide-test/CMakeLists.txt +++ b/tools/swift-ide-test/CMakeLists.txt @@ -1,5 +1,6 @@ add_swift_executable(swift-ide-test swift-ide-test.cpp + XMLValidator.cpp DEPENDS swiftFrontend swiftIDE COMPONENT_DEPENDS bitreader bitwriter support ${LLVM_TARGETS_TO_BUILD}) @@ -7,6 +8,13 @@ if(MODULES_SDK) add_definitions( -DSWIFT_MODULES_SDK="${MODULES_SDK}" ) endif() +# If libxml2 is available, make it available for swift-ide-test. +if (SWIFT_HAVE_LIBXML) + include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) + target_link_libraries(swift-ide-test ${LIBXML2_LIBRARIES}) + add_definitions( -DSWIFT_HAVE_LIBXML="1" ) +endif() + install(TARGETS swift-ide-test RUNTIME DESTINATION bin) diff --git a/tools/swift-ide-test/XMLValidator.cpp b/tools/swift-ide-test/XMLValidator.cpp new file mode 100644 index 0000000000000..51224cf8929cc --- /dev/null +++ b/tools/swift-ide-test/XMLValidator.cpp @@ -0,0 +1,96 @@ +//===-- XMLValidator.cpp - XML validation ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "XMLValidator.h" + +#ifdef SWIFT_HAVE_LIBXML + +// libxml headers use their own variant of documentation comments that Clang +// does not understand well. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" + +#include +#include +#include + +#pragma clang diagnostic pop + +using namespace swift; + +struct XMLValidator::Implementation { + std::string SchemaFileName; + xmlRelaxNGParserCtxtPtr RNGParser; + xmlRelaxNGPtr Schema; +}; + +XMLValidator::XMLValidator() : Impl(new Implementation()) {} + +XMLValidator::~XMLValidator() { delete Impl; } + +void XMLValidator::setSchema(StringRef FileName) { + assert(Impl->SchemaFileName.empty()); + Impl->SchemaFileName = FileName; +} + +XMLValidator::Status XMLValidator::validate(const std::string &XML) { + if (Impl->SchemaFileName.empty()) + return Status{ErrorCode::NoSchema, ""}; + + if (!Impl->RNGParser) { + Impl->RNGParser = xmlRelaxNGNewParserCtxt(Impl->SchemaFileName.c_str()); + Impl->Schema = xmlRelaxNGParse(Impl->RNGParser); + } + if (!Impl->RNGParser) + return Status{ErrorCode::InternalError, ""}; + + if (!Impl->Schema) + return Status{ErrorCode::BadSchema, ""}; + + xmlDocPtr Doc = xmlParseDoc(reinterpret_cast(XML.data())); + if (!Doc) + return Status{ErrorCode::NotWellFormed, xmlGetLastError()->message}; + + xmlRelaxNGValidCtxtPtr ValidationCtxt = xmlRelaxNGNewValidCtxt(Impl->Schema); + int ValidationStatus = xmlRelaxNGValidateDoc(ValidationCtxt, Doc); + Status Result; + if (ValidationStatus == 0) { + Result = Status{ErrorCode::Valid, ""}; + } else if (ValidationStatus > 0) { + Result = Status{ErrorCode::NotValid, xmlGetLastError()->message}; + } else + Result = Status{ErrorCode::InternalError, ""}; + + xmlRelaxNGFreeValidCtxt(ValidationCtxt); + xmlFreeDoc(Doc); + + return Result; +} + +#else // !SWIFT_HAVE_LIBXML + +using namespace swift; + +struct XMLValidator::Implementation {}; + +XMLValidator::XMLValidator() : Impl(new Implementation()) {} + +XMLValidator::~XMLValidator() { delete Impl; } + +void XMLValidator::setSchema(StringRef FileName) {} + +XMLValidator::Status XMLValidator::validate(const std::string &XML) { + return Status{ErrorCode::NotCompiledIn, ""}; +} + +#endif // SWIFT_HAVE_LIBXML + diff --git a/tools/swift-ide-test/XMLValidator.h b/tools/swift-ide-test/XMLValidator.h new file mode 100644 index 0000000000000..4480f030d48cf --- /dev/null +++ b/tools/swift-ide-test/XMLValidator.h @@ -0,0 +1,51 @@ +//===-- XMLValidator.h - XML validation -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_TEST_XML_VALIDATOR_H +#define SWIFT_IDE_TEST_XML_VALIDATOR_H + +#include "swift/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" + +namespace swift { + +class XMLValidator { + struct Implementation; + Implementation *Impl; + +public: + XMLValidator(); + ~XMLValidator(); + + void setSchema(StringRef FileName); + + enum class ErrorCode { + Valid, + NotCompiledIn, + NoSchema, + BadSchema, + NotWellFormed, + NotValid, + InternalError, + }; + struct Status { + ErrorCode Code; + std::string Message; + }; + + Status validate(const std::string &XML); +}; + +} // end namespace swift + +#endif // SWIFT_IDE_TEST_XML_VALIDATOR_H + diff --git a/unittests/ReST/ReSTTest.cpp b/unittests/ReST/ReSTTest.cpp index 3173a550cf8ba..4e6753aa83644 100644 --- a/unittests/ReST/ReSTTest.cpp +++ b/unittests/ReST/ReSTTest.cpp @@ -492,6 +492,9 @@ struct ExtractBriefTestData ExtractBriefTests[] = { { { "aaa", "bbb" }, "aaa bbb", "aaa\nbbb" }, // Correct. + { { "& < > \" '" }, + "& < > \" '", + "& < > " '" }, // Correct. { { "aaa", " " }, "aaa", "aaa" }, // Correct. @@ -1248,6 +1251,33 @@ struct ExtractBriefTestData ExtractBriefTests[] = { "bbb" "" }, // Correct. + { { "* aaa", + " :bbb: ccc", + " :ddd: eee", + " :fff: ggg" }, "", + "" + "" + "aaa\n:bbb: ccc\n:ddd: eee\n:fff: ggg" + "" + "" }, // Correct. + { { "* aaa", + "", + " :bbb: ccc", + " :ddd: eee", + " :fff: ggg" }, "", + "" + "" + "aaa" + "" + "" + "bbb" + "" + "ccc\n:ddd: eee\n:fff: ggg" + "" + "" + "" + "" + "" }, // FIXME: WRONG // Definition lists. { { "aaa", @@ -1543,10 +1573,10 @@ struct ExtractBriefTestData ExtractBriefTests[] = { { { "`aaa`__" }, "`aaa`__", "`aaa`__" }, { { "`aaa `_" }, "`aaa `_", - "`aaa `_"}, + "`aaa <http://example.org/>`_"}, { { "`aaa `__" }, "`aaa `__", - "`aaa `__" }, + "`aaa <foo.txt\\_>`__" }, }; INSTANTIATE_TEST_CASE_P( ReSTTest, ExtractBriefTest,