From a5cf339c0e79bbc45b7408ce95b09d12df8a385a Mon Sep 17 00:00:00 2001 From: MoonlightSentinel Date: Sun, 3 May 2020 13:41:22 +0200 Subject: [PATCH 1/3] Extend trivia unittest for empty doc comments --- src/dparse/trivia.d | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/dparse/trivia.d b/src/dparse/trivia.d index f1090cc9..3816ebe0 100644 --- a/src/dparse/trivia.d +++ b/src/dparse/trivia.d @@ -568,6 +568,9 @@ void main() { writeln(":)"); } +/// +unittest {} + /// end of file`; LexerConfig cf; @@ -575,7 +578,7 @@ void main() { const tokens = getTokensForParser(src, cf, &ca); - assert(tokens.length == 19); + assert(tokens.length == 22); assert(tokens[0].type == tok!"module"); assert(tokens[0].leadingTrivia.length == 6); @@ -694,9 +697,30 @@ void main() { assert(tokens[18].type == tok!"}"); assert(!tokens[18].leadingTrivia.length); - assert(tokens[18].trailingTrivia.length == 2); + assert(tokens[18].trailingTrivia.length == 1); assert(tokens[18].trailingTrivia[0].type == tok!"whitespace"); assert(tokens[18].trailingTrivia[0].text == "\n\n"); - assert(tokens[18].trailingTrivia[1].type == tok!"comment"); - assert(tokens[18].trailingTrivia[1].text == "/// end of file"); + + assert(tokens[19].type == tok!"unittest"); + assert(tokens[19].leadingTrivia.length == 2); + assert(tokens[19].leadingTrivia[0].type == tok!"comment"); + assert(tokens[19].leadingTrivia[0].text == "///"); + assert(tokens[19].leadingTrivia[1].type == tok!"whitespace"); + assert(tokens[19].leadingTrivia[1].text == "\n"); + + assert(tokens[19].trailingTrivia.length == 1); + assert(tokens[19].trailingTrivia[0].type == tok!"whitespace"); + assert(tokens[19].trailingTrivia[0].text == " "); + + assert(tokens[20].type == tok!"{"); + assert(!tokens[20].leadingTrivia.length); + assert(!tokens[20].trailingTrivia.length); + + assert(tokens[21].type == tok!"}"); + assert(!tokens[21].leadingTrivia.length); + assert(tokens[21].trailingTrivia.length == 2); + assert(tokens[21].trailingTrivia[0].type == tok!"whitespace"); + assert(tokens[21].trailingTrivia[0].text == "\n\n"); + assert(tokens[21].trailingTrivia[1].type == tok!"comment"); + assert(tokens[21].trailingTrivia[1].text == "/// end of file"); } From e7521c0f88469c91de5af159e21b8f50d9f5299d Mon Sep 17 00:00:00 2001 From: MoonlightSentinel Date: Sun, 3 May 2020 14:49:11 +0200 Subject: [PATCH 2/3] Ensure extractDdocFromTrivia returns non-null for empty comment --- src/dparse/trivia.d | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/dparse/trivia.d b/src/dparse/trivia.d index 3816ebe0..5961db6c 100644 --- a/src/dparse/trivia.d +++ b/src/dparse/trivia.d @@ -518,18 +518,43 @@ unittest string extractDdocFromTrivia(Tokens)(Tokens tokens) pure nothrow @safe if (isInputRange!Tokens && is(ElementType!Tokens : Token)) { + bool hasDoc; auto ret = appender!string; foreach (trivia; tokens) { if (trivia.type == tok!"comment" && trivia.text.determineCommentType.isDocComment) { + hasDoc = true; if (!ret.data.empty) ret.put('\n'); unDecorateComment(trivia.text, ret); } } - return ret.data; + + if (ret.data.length) + return ret.data; + else + return hasDoc ? "" : null; +} + +unittest +{ + Token[] tokens = [ + Token(cast(ubyte) tok!"whitespace", "\n\n", 0, 0, 0), + Token(cast(ubyte) tok!"comment", "///", 0, 0, 0), + Token(cast(ubyte) tok!"whitespace", "\n", 0, 0, 0) + ]; + + // Empty comment is non-null + auto comment = extractDdocFromTrivia(tokens); + assert(comment !is null); + assert(comment == ""); + + // Missing comment is null + comment = extractDdocFromTrivia(tokens[0 .. 1]); + assert(comment is null); + assert(comment == ""); } string extractLeadingDdoc(const Token token) pure nothrow @safe From 8ad3c4667bcd987219ef24946b0fb14063cd4163 Mon Sep 17 00:00:00 2001 From: MoonlightSentinel Date: Sun, 3 May 2020 14:57:26 +0200 Subject: [PATCH 3/3] Add a test regarding DDOC comments on unittests --- src/dparse/ast.d | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/dparse/ast.d b/src/dparse/ast.d index 69925af6..696ab6cf 100644 --- a/src/dparse/ast.d +++ b/src/dparse/ast.d @@ -3686,3 +3686,65 @@ unittest // issue #398: Support extern(C++, ) checkText(ns[1], `"bar"`); checkText(ns[2], `"baz"`); } + +unittest // Differentiate between no and empty DDOC comments, e.g. for DDOC unittests +{ + import dparse.lexer, dparse.parser, dparse.rollback_allocator; + + auto src = q{ + /// + unittest {} + + /// + @safe pure unittest {} + + /****/ unittest {} + + /++++/ unittest {} + + /// This is a comment! + unittest {} + + unittest {} + }; + + RollbackAllocator ra; + LexerConfig cf = LexerConfig("", StringBehavior.source); + StringCache ca = StringCache(16); + Module m = parseModule(getTokensForParser(src, cf, &ca), "", &ra); + + final class UnittestVisitor : ASTVisitor + { + alias visit = ASTVisitor.visit; + bool[size_t] found; + + override void visit(const Unittest test) + { + assert(test.line !in found); + found[test.line] = true; + + switch (test.line) + { + case 3, 6, 8, 10: + assert(test.comment !is null); + assert(test.comment == ""); + break; + + case 13: + assert(test.comment == "This is a comment!"); + break; + + case 15: + assert(test.comment is null); + break; + + default: + assert(false, format("Unknown line: %d", test.line)); + } + } + } + + scope visitor = new UnittestVisitor(); + visitor.visit(m); + assert(visitor.found.length == 6); +}