diff --git a/include/swift/Markup/Markup.h b/include/swift/Markup/Markup.h index c5caefdd49b2a..b511f0b43c087 100644 --- a/include/swift/Markup/Markup.h +++ b/include/swift/Markup/Markup.h @@ -60,6 +60,7 @@ class MarkupContext final { LineList getLineList(swift::RawComment RC); }; +Document *parseDocument(MarkupContext &MC, StringRef String); Document *parseDocument(MarkupContext &MC, LineList &LL); } // namespace markup diff --git a/lib/Frontend/PrintingDiagnosticConsumer.cpp b/lib/Frontend/PrintingDiagnosticConsumer.cpp index 490bbf7e934db..ab5808c52986b 100644 --- a/lib/Frontend/PrintingDiagnosticConsumer.cpp +++ b/lib/Frontend/PrintingDiagnosticConsumer.cpp @@ -18,6 +18,7 @@ #include "swift/AST/DiagnosticEngine.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/SourceManager.h" +#include "swift/Markup/Markup.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -28,6 +29,7 @@ #include using namespace swift; +using namespace swift::markup; namespace { class ColoredStream : public raw_ostream { @@ -85,6 +87,190 @@ namespace { size_t preferred_buffer_size() const override { return 0; } }; +// MARK: Markdown Printing + class TerminalMarkupPrinter : public MarkupASTVisitor { + llvm::raw_ostream &OS; + unsigned Indent; + unsigned ShouldBold; + + void indent(unsigned Amount = 2) { Indent += Amount; } + + void dedent(unsigned Amount = 2) { + assert(Indent >= Amount && "dedent without matching indent"); + Indent -= Amount; + } + + void bold() { + ++ShouldBold; + updateFormatting(); + } + + void unbold() { + assert(ShouldBold > 0 && "unbolded without matching bold"); + --ShouldBold; + updateFormatting(); + } + + void updateFormatting() { + OS.resetColor(); + if (ShouldBold > 0) + OS.changeColor(raw_ostream::Colors::SAVEDCOLOR, true); + } + + void print(StringRef Str) { + for (auto c : Str) { + OS << c; + if (c == '\n') + for (unsigned i = 0; i < Indent; ++i) + OS << ' '; + } + } + + public: + TerminalMarkupPrinter(llvm::raw_ostream &OS) + : OS(OS), Indent(0), ShouldBold(0) {} + + void printNewline() { print("\n"); } + + void visitDocument(const Document *D) { + for (const auto *Child : D->getChildren()) { + if (Child->getKind() == ASTNodeKind::Paragraph) { + // Add a newline before top-level paragraphs + printNewline(); + } + visit(Child); + } + } + + void visitBlockQuote(const BlockQuote *BQ) { + indent(); + printNewline(); + for (const auto *Child : BQ->getChildren()) + visit(Child); + dedent(); + } + + void visitList(const List *BL) { + indent(); + printNewline(); + for (const auto *Child : BL->getChildren()) + visit(Child); + dedent(); + } + + void visitItem(const Item *I) { + print("- "); + for (const auto *N : I->getChildren()) + visit(N); + } + + void visitCodeBlock(const CodeBlock *CB) { + indent(); + printNewline(); + print(CB->getLiteralContent()); + dedent(); + } + + void visitCode(const Code *C) { + print("'"); + print(C->getLiteralContent()); + print("'"); + } + + void visitHTML(const HTML *H) { print(H->getLiteralContent()); } + + void visitInlineHTML(const InlineHTML *IH) { + print(IH->getLiteralContent()); + } + + void visitSoftBreak(const SoftBreak *SB) { printNewline(); } + + void visitLineBreak(const LineBreak *LB) { + printNewline(); + printNewline(); + } + + void visitLink(const Link *L) { + print("["); + for (const auto *Child : L->getChildren()) + visit(Child); + print("]("); + print(L->getDestination()); + print(")"); + } + + void visitImage(const Image *I) { llvm_unreachable("unsupported"); } + + void visitParagraph(const Paragraph *P) { + for (const auto *Child : P->getChildren()) + visit(Child); + printNewline(); + } + + // TODO: add raw_ostream support for italics ANSI codes in LLVM. + void visitEmphasis(const Emphasis *E) { + for (const auto *Child : E->getChildren()) + visit(Child); + } + + void visitStrong(const Strong *E) { + bold(); + for (const auto *Child : E->getChildren()) + visit(Child); + unbold(); + } + + void visitHRule(const HRule *HR) { + print("--------------"); + printNewline(); + } + + void visitHeader(const Header *H) { + bold(); + for (const auto *Child : H->getChildren()) + visit(Child); + unbold(); + printNewline(); + } + + void visitText(const Text *T) { print(T->getLiteralContent()); } + + void visitPrivateExtension(const PrivateExtension *PE) { + llvm_unreachable("unsupported"); + } + + void visitParamField(const ParamField *PF) { + llvm_unreachable("unsupported"); + } + + void visitReturnField(const ReturnsField *RF) { + llvm_unreachable("unsupported"); + } + + void visitThrowField(const ThrowsField *TF) { + llvm_unreachable("unsupported"); + } + + #define MARKUP_SIMPLE_FIELD(Id, Keyword, XMLKind) \ + void visit##Id(const Id *Field) { llvm_unreachable("unsupported"); } + #include "swift/Markup/SimpleFields.def" + }; + + static void printMarkdown(StringRef Content, raw_ostream &Out, + bool UseColor) { + markup::MarkupContext ctx; + auto document = markup::parseDocument(ctx, Content); + if (UseColor) { + ColoredStream stream{Out}; + TerminalMarkupPrinter printer(stream); + printer.visit(document); + } else { + NoColorStream stream{Out}; + TerminalMarkupPrinter printer(stream); + printer.visit(document); + } + } + // MARK: Experimental diagnostic printing. static void printDiagnosticKind(DiagnosticKind kind, raw_ostream &out) { @@ -716,8 +902,10 @@ void PrintingDiagnosticConsumer::handleDiagnostic(SourceManager &SM, printDiagnostic(SM, Info); for (auto path : Info.EducationalNotePaths) { - if (auto buffer = SM.getFileSystem()->getBufferForFile(path)) - Stream << buffer->get()->getBuffer() << "\n"; + if (auto buffer = SM.getFileSystem()->getBufferForFile(path)) { + printMarkdown(buffer->get()->getBuffer(), Stream, ForceColors); + Stream << "\n"; + } } for (auto ChildInfo : Info.ChildDiagnosticInfo) { diff --git a/lib/Markup/Markup.cpp b/lib/Markup/Markup.cpp index e6f55ab69288c..04f6cb50973cb 100644 --- a/lib/Markup/Markup.cpp +++ b/lib/Markup/Markup.cpp @@ -54,7 +54,7 @@ struct ParseResult { } }; -StringRef getLiteralContent(MarkupContext &MC, LineList &LL, cmark_node *Node) { +StringRef getLiteralContent(MarkupContext &MC, cmark_node *Node) { // Literal content nodes never have start/end column line information. // It is a floating piece of text that inherits location information from // its parent. @@ -63,50 +63,45 @@ StringRef getLiteralContent(MarkupContext &MC, LineList &LL, cmark_node *Node) { return MC.allocateCopy(StringRef(Literal)); } -ParseResult -parseElement(MarkupContext &MC, LineList &LL, ParseState State); +ParseResult parseElement(MarkupContext &MC, ParseState State); -ParseState parseChildren(MarkupContext &MC, LineList &LL, ParseState State, +ParseState parseChildren(MarkupContext &MC, ParseState State, SmallVectorImpl &Children) { auto Root = State.Node; State = State.next(); do { if (Root == State.Node && State.Event == CMARK_EVENT_EXIT) break; - auto Result = parseElement(MC, LL, State); + auto Result = parseElement(MC, State); Children.push_back(Result.Node); State = Result.State; } while (!(Root == State.Node && State.Event == CMARK_EVENT_EXIT)); return State; } -ParseResult parseText(MarkupContext &MC, LineList &LL, ParseState State) { +ParseResult parseText(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_TEXT && State.Event == CMARK_EVENT_ENTER); - return { Text::create(MC, getLiteralContent(MC, LL, State.Node)), - State.next() }; + return {Text::create(MC, getLiteralContent(MC, State.Node)), State.next()}; } -ParseResult
parseBlockQuote(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult
parseBlockQuote(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_BLOCK_QUOTE && State.Event == CMARK_EVENT_ENTER); SmallVector Children; - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); return { BlockQuote::create(MC, Children), ResultState.next() }; } -ParseResult parseCode(MarkupContext &MC, LineList &LL, ParseState State) { +ParseResult parseCode(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_CODE && State.Event == CMARK_EVENT_ENTER); - return { Code::create(MC, getLiteralContent(MC, LL, State.Node)), - State.next() }; + return {Code::create(MC, getLiteralContent(MC, State.Node)), State.next()}; } -ParseResult parseCodeBlock(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult parseCodeBlock(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_CODE_BLOCK && State.Event == CMARK_EVENT_ENTER); @@ -117,69 +112,62 @@ ParseResult parseCodeBlock(MarkupContext &MC, LineList &LL, if (!FenceInfoStr.empty()) Language = MC.allocateCopy(FenceInfoStr); } - return { CodeBlock::create(MC, getLiteralContent(MC, LL, State.Node), - Language), - State.next() }; + return {CodeBlock::create(MC, getLiteralContent(MC, State.Node), Language), + State.next()}; } -ParseResult parseEmphasis(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult parseEmphasis(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_EMPH && State.Event == CMARK_EVENT_ENTER); SmallVector Children; - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); return { Emphasis::create(MC, Children), ResultState.next() }; } -ParseResult parseStrong(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult parseStrong(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_STRONG && State.Event == CMARK_EVENT_ENTER); SmallVector Children; - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); return { Strong::create(MC, Children), ResultState.next() }; } -ParseResult
parseHeader(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult
parseHeader(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_HEADER && State.Event == CMARK_EVENT_ENTER); auto Level = cmark_node_get_header_level(State.Node); SmallVector Children; - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); (void) ResultState; return { Header::create(MC, Level, Children), State.next() }; } -ParseResult parseHRule(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult parseHRule(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_HRULE && State.Event == CMARK_EVENT_ENTER); return { HRule::create(MC), State.next() }; } -ParseResult parseHTML(MarkupContext &MC, LineList &LL, ParseState State) { +ParseResult parseHTML(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_HTML && State.Event == CMARK_EVENT_ENTER); - return { HTML::create(MC, getLiteralContent(MC, LL, State.Node)), - State.next() }; + return {HTML::create(MC, getLiteralContent(MC, State.Node)), State.next()}; } -ParseResult parseInlineHTML(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult parseInlineHTML(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_INLINE_HTML && State.Event == CMARK_EVENT_ENTER); - return { InlineHTML::create(MC, getLiteralContent(MC, LL, State.Node)), - State.next() }; + return {InlineHTML::create(MC, getLiteralContent(MC, State.Node)), + State.next()}; } -ParseResult parseImage(MarkupContext &MC, LineList &LL, ParseState State) { +ParseResult parseImage(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_IMAGE && State.Event == CMARK_EVENT_ENTER); std::string Destination(cmark_node_get_url(State.Node)); @@ -189,72 +177,68 @@ ParseResult parseImage(MarkupContext &MC, LineList &LL, ParseState State) auto Title = TitleString.empty() ? None : Optional(TitleString); SmallVector Children; - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); return { Image::create(MC, Destination, Title, Children), ResultState.next() }; } -ParseResult parseItem(MarkupContext &MC, LineList &LL, ParseState State) { +ParseResult parseItem(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_ITEM && State.Event == CMARK_EVENT_ENTER); SmallVector Children; - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); return { Item::create(MC, Children), ResultState.next() }; } -ParseResult parseLineBreak(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult parseLineBreak(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_LINEBREAK && State.Event == CMARK_EVENT_ENTER); return { LineBreak::create(MC), State.next() }; } -ParseResult parseSoftBreak(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult parseSoftBreak(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_SOFTBREAK && State.Event == CMARK_EVENT_ENTER); return { SoftBreak::create(MC), State.next() }; } -ParseResult parseLink(MarkupContext &MC, LineList &LL, ParseState State) { +ParseResult parseLink(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_LINK && State.Event == CMARK_EVENT_ENTER); std::string Destination(cmark_node_get_url(State.Node)); SmallVector Children; - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); return { Link::create(MC, Destination, Children), ResultState.next() }; } -ParseResult parseList(MarkupContext &MC, LineList &LL, ParseState State) { +ParseResult parseList(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_LIST && State.Event == CMARK_EVENT_ENTER); auto ListRoot = State.Node; auto IsOrdered = cmark_node_get_list_type(ListRoot) == CMARK_ORDERED_LIST; SmallVector Children; - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); return { List::create(MC, Children, IsOrdered), ResultState.next() }; } -ParseResult parseParagraph(MarkupContext &MC, LineList &LL, - ParseState State) { +ParseResult parseParagraph(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_PARAGRAPH && State.Event == CMARK_EVENT_ENTER); SmallVector Children; - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); return { Paragraph::create(MC, Children), ResultState.next() }; } -ParseResult -parseElement(MarkupContext &MC, LineList &LL, ParseState State) { +ParseResult parseElement(MarkupContext &MC, ParseState State) { assert(State.Event == CMARK_EVENT_ENTER); auto NodeType = cmark_node_get_type(State.Node); switch (NodeType) { @@ -262,55 +246,55 @@ parseElement(MarkupContext &MC, LineList &LL, ParseState State) { llvm_unreachable("Markup documents cannot be nested"); } case CMARK_NODE_BLOCK_QUOTE: { - return parseBlockQuote(MC, LL, State); + return parseBlockQuote(MC, State); } case CMARK_NODE_CODE: { - return parseCode(MC, LL, State); + return parseCode(MC, State); } case CMARK_NODE_CODE_BLOCK: { - return parseCodeBlock(MC, LL, State); + return parseCodeBlock(MC, State); } case CMARK_NODE_EMPH: { - return parseEmphasis(MC, LL, State); + return parseEmphasis(MC, State); } case CMARK_NODE_HEADER: { - return parseHeader(MC, LL, State); + return parseHeader(MC, State); } case CMARK_NODE_HRULE: { - return parseHRule(MC, LL, State); + return parseHRule(MC, State); } case CMARK_NODE_HTML: { - return parseHTML(MC, LL, State); + return parseHTML(MC, State); } case CMARK_NODE_IMAGE: { - return parseImage(MC, LL, State); + return parseImage(MC, State); } case CMARK_NODE_INLINE_HTML: { - return parseInlineHTML(MC, LL, State); + return parseInlineHTML(MC, State); } case CMARK_NODE_ITEM: { - return parseItem(MC, LL, State); + return parseItem(MC, State); } case CMARK_NODE_LINEBREAK: { - return parseLineBreak(MC, LL, State); + return parseLineBreak(MC, State); } case CMARK_NODE_LINK: { - return parseLink(MC, LL, State); + return parseLink(MC, State); } case CMARK_NODE_LIST: { - return parseList(MC, LL, State); + return parseList(MC, State); } case CMARK_NODE_PARAGRAPH: { - return parseParagraph(MC, LL, State); + return parseParagraph(MC, State); } case CMARK_NODE_SOFTBREAK: { - return parseSoftBreak(MC, LL, State); + return parseSoftBreak(MC, State); } case CMARK_NODE_STRONG: { - return parseStrong(MC, LL, State); + return parseStrong(MC, State); } case CMARK_NODE_TEXT: { - return parseText(MC, LL, State); + return parseText(MC, State); } default: { llvm_unreachable("Can't parse a Markup node of type 'None'"); @@ -318,10 +302,9 @@ parseElement(MarkupContext &MC, LineList &LL, ParseState State) { } } -Document *swift::markup::parseDocument(MarkupContext &MC, LineList &LL) { - auto Comment = LL.str(); - auto CMarkDoc = cmark_parse_document(Comment.c_str(), Comment.size(), - CMARK_OPT_SMART); +static Document *parseDocumentImpl(MarkupContext &MC, StringRef String) { + auto CMarkDoc = + cmark_parse_document(String.data(), String.size(), CMARK_OPT_SMART); if (CMarkDoc == nullptr) return nullptr; @@ -332,7 +315,7 @@ Document *swift::markup::parseDocument(MarkupContext &MC, LineList &LL) { SmallVector Children; assert(cmark_node_get_type(State.Node) == CMARK_NODE_DOCUMENT && State.Event == CMARK_EVENT_ENTER); - auto ResultState = parseChildren(MC, LL, State, Children); + auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); State = ResultState.next(); @@ -341,3 +324,11 @@ Document *swift::markup::parseDocument(MarkupContext &MC, LineList &LL) { cmark_iter_free(State.Iter); return Document::create(MC, Children); } + +Document *swift::markup::parseDocument(MarkupContext &MC, LineList &LL) { + return parseDocumentImpl(MC, LL.str()); +} + +Document *swift::markup::parseDocument(MarkupContext &MC, StringRef String) { + return parseDocumentImpl(MC, MC.allocateCopy(String)); +} diff --git a/test/diagnostics/educational-notes.swift b/test/diagnostics/educational-notes.swift index 8cafa26c28922..ea794d5ff250f 100644 --- a/test/diagnostics/educational-notes.swift +++ b/test/diagnostics/educational-notes.swift @@ -1,15 +1,68 @@ -// RUN: not %target-swift-frontend -enable-educational-notes -diagnostic-documentation-path %S/test-docs/ -typecheck %s 2>&1 | %FileCheck %s +// RUN: not %target-swift-frontend -color-diagnostics -enable-educational-notes -diagnostic-documentation-path %S/test-docs/ -typecheck %s 2>&1 | %FileCheck %s --match-full-lines --strict-whitespace +// RUN: not %target-swift-frontend -no-color-diagnostics -enable-educational-notes -diagnostic-documentation-path %S/test-docs/ -typecheck %s 2>&1 | %FileCheck %s --match-full-lines --strict-whitespace --check-prefix=NO-COLOR // A diagnostic with no educational notes let x = 1 + -// CHECK: error: expected expression after operator +// CHECK:{{.*}}[0merror: expected expression after operator // CHECK-NOT: {{-+$}} -// A diagnostic with an educational note +// NO-COLOR:{{.*}}error: expected expression after operator +// NO-COLOR-NOT: {{-+$}} + +// A diagnostic with an educational note using supported markdown features extension (Int, Int) {} -// CHECK: error: non-nominal type '(Int, Int)' cannot be extended -// CHECK-NEXT: extension (Int, Int) {} -// CHECK-NEXT: ^ ~~~~~~~~~~ -// CHECK-NEXT: Nominal Types -// CHECK-NEXT: ------------- -// CHECK-NEXT: Nominal types documentation content +// CHECK:{{.*}}[0merror: non-nominal type '(Int, Int)' cannot be extended +// CHECK-NEXT:extension (Int, Int) {} +// CHECK-NEXT:^ ~~~~~~~~~~ +// CHECK-NEXT:Nominal Types +// CHECK-NEXT:-------------- +// CHECK-EMPTY: +// CHECK-NEXT:Nominal types documentation content. This is a paragraph +// CHECK-EMPTY: +// CHECK-NEXT: blockquote +// CHECK-NEXT: {{$}} +// CHECK-NEXT: - item 1 +// CHECK-NEXT: - item 2 +// CHECK-NEXT: - item 3 +// CHECK-NEXT: {{$}} +// CHECK-NEXT: let x = 42 +// CHECK-NEXT: if x > 0 { +// CHECK-NEXT: print("positive") +// CHECK-NEXT: } +// CHECK-NEXT: {{$}} +// CHECK-NEXT:Type 'MyClass' +// CHECK-EMPTY: +// CHECK-NEXT:[Swift](swift.org) +// CHECK-EMPTY: +// CHECK-NEXT:bold italics +// CHECK-NEXT:-------------- +// CHECK-NEXT:Header 1 +// CHECK-NEXT:Header 3 + +// NO-COLOR:{{.*}}error: non-nominal type '(Int, Int)' cannot be extended +// NO-COLOR-NEXT:extension (Int, Int) {} +// NO-COLOR-NEXT:^ ~~~~~~~~~~ +// NO-COLOR-NEXT:Nominal Types +// NO-COLOR-NEXT:-------------- +// NO-COLOR-EMPTY: +// NO-COLOR-NEXT:Nominal types documentation content. This is a paragraph +// NO-COLOR-EMPTY: +// NO-COLOR-NEXT: blockquote +// NO-COLOR-NEXT: {{$}} +// NO-COLOR-NEXT: - item 1 +// NO-COLOR-NEXT: - item 2 +// NO-COLOR-NEXT: - item 3 +// NO-COLOR-NEXT: {{$}} +// NO-COLOR-NEXT: let x = 42 +// NO-COLOR-NEXT: if x > 0 { +// NO-COLOR-NEXT: print("positive") +// NO-COLOR-NEXT: } +// NO-COLOR-NEXT: {{$}} +// NO-COLOR-NEXT:Type 'MyClass' +// NO-COLOR-EMPTY: +// NO-COLOR-NEXT:[Swift](swift.org) +// NO-COLOR-EMPTY: +// NO-COLOR-NEXT:bold italics +// NO-COLOR-NEXT:-------------- +// NO-COLOR-NEXT:Header 1 +// NO-COLOR-NEXT:Header 3 diff --git a/test/diagnostics/test-docs/nominal-types.md b/test/diagnostics/test-docs/nominal-types.md index 67ccc055740ce..66f53d5befe67 100644 --- a/test/diagnostics/test-docs/nominal-types.md +++ b/test/diagnostics/test-docs/nominal-types.md @@ -1,3 +1,27 @@ -Nominal Types -------------- -Nominal types documentation content +# Nominal Types +*** +Nominal types documentation content. This is a paragraph + +> blockquote + +- item 1 +- item 2 +- item 3 + +``` +let x = 42 +if x > 0 { + print("positive") +} +``` + +Type `MyClass` + +[Swift](swift.org) + +**bold** *italics* + +*** + +# Header 1 +### Header 3 \ No newline at end of file diff --git a/userdocs/diagnostics/nominal-types.md b/userdocs/diagnostics/nominal-types.md index 58d8a2b4244fc..6a4484bdadead 100644 --- a/userdocs/diagnostics/nominal-types.md +++ b/userdocs/diagnostics/nominal-types.md @@ -1,7 +1,7 @@ -Nominal types -------------- -In Swift, a type is considered a nominal type if it is named. In other words, it has been defined by declaring the type somewhere in code. Examples of nominal types include classes, structs and enums, all of which must be declared before using them. Nominal types are an important concept in Swift because they can be extended, explicitly initialized using the 'MyType()' syntax, and may conform to protocols. +## Nominal types +*** +In Swift, a type is considered a nominal type if it is named. In other words, it has been defined by declaring the type somewhere in code. Examples of nominal types include classes, structs and enums, all of which must be declared before using them. Nominal types are an important concept in Swift because they can be extended, explicitly initialized using the `MyType()` syntax, and may conform to protocols. -In contrast, non-nominal types have none of these capabilities. A non-nominal type is any type which is not nominal. They are sometimes called ”structural types” because they are usually obtained by composing other types. Examples include function types like '(Int) -> (String)', tuple types like '(Int, String)', metatypes like 'Int.Type', and special types like 'Any' and 'AnyObject'. +In contrast, non-nominal types have none of these capabilities. A non-nominal type is any type which is not nominal. They are sometimes called "structural types" because they are usually obtained by composing other types. Examples include function types like `(Int) -> (String)`, tuple types like `(Int, String)`, metatypes like `Int.Type`, and special types like `Any` and `AnyObject`. -Whether the name of a protocol refers to a nominal or non-nominal type depends on where it appears in code. When used in a declaration or extension like 'extension MyProtocol { … }', 'MyProtocol' refers to a protocol type, which is nominal. This means that it may be extended and conform to protocols. However, when written as the type of a constant or variable, MyProtocol instead refers to a non-nominal, existential type. As a result, code like 'let value: MyProtocol = MyProtocol()' is not allowed because 'MyProtocol' refers to a non-nominal type in this context and cannot be explicitly initialized. +Whether the name of a protocol refers to a nominal or non-nominal type depends on where it appears in code. When used in a declaration or extension like `extension MyProtocol { … }`, `MyProtocol` refers to a protocol type, which is nominal. This means that it may be extended and conform to protocols. However, when written as the type of a constant or variable, `MyProtocol` instead refers to a non-nominal, existential type. As a result, code like `let value: MyProtocol = MyProtocol()` is not allowed because `MyProtocol` refers to a non-nominal type in this context and cannot be explicitly initialized.