diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -265,6 +265,8 @@ space before parentheses. The custom options can be set using ``SpaceBeforeParensOptions``. +- Improved Cpp20 Modules support. + libclang -------- diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -76,6 +76,7 @@ TYPE(LineComment) \ TYPE(MacroBlockBegin) \ TYPE(MacroBlockEnd) \ + TYPE(ModulePartitionColon) \ TYPE(NamespaceMacro) \ TYPE(NonNullAssertion) \ TYPE(NullCoalescingEqual) \ diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -903,9 +903,13 @@ break; } } - if (Contexts.back().ColonIsDictLiteral || - Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) { + if (Line.First->isOneOf(Keywords.kw_module, Keywords.kw_import) || + Line.First->startsSequence(tok::kw_export, Keywords.kw_module) || + Line.First->startsSequence(tok::kw_export, Keywords.kw_import)) { + Tok->setType(TT_ModulePartitionColon); + } else if (Contexts.back().ColonIsDictLiteral || + Style.Language == FormatStyle::LK_Proto || + Style.Language == FormatStyle::LK_TextProto) { Tok->setType(TT_DictLiteral); if (Style.Language == FormatStyle::LK_TextProto) { if (FormatToken *Previous = Tok->getPreviousNonComment()) @@ -3244,6 +3248,7 @@ auto HasExistingWhitespace = [&Right]() { return Right.WhitespaceRange.getBegin() != Right.WhitespaceRange.getEnd(); }; + if (Right.Tok.getIdentifierInfo() && Left.Tok.getIdentifierInfo()) return true; // Never ever merge two identifiers. @@ -3253,6 +3258,25 @@ return true; if (Style.isCpp()) { + // Space between import . + // or import .....; + if (Left.is(Keywords.kw_import) && Right.isOneOf(tok::less, tok::ellipsis)) + return true; + // No space between module :. + if (Left.isOneOf(Keywords.kw_module, Keywords.kw_import) && + Right.is(TT_ModulePartitionColon)) + return true; + // No space between import foo:bar but keep a space between import :bar; + if (Left.is(tok::identifier) && Right.is(TT_ModulePartitionColon)) + return false; + // No space between :bar; + if (Left.is(TT_ModulePartitionColon) && + Right.isOneOf(tok::identifier, tok::kw_private)) + return false; + if (Left.is(tok::ellipsis) && Right.is(tok::identifier) && + Line.First->is(Keywords.kw_import)) + return false; + if (Left.is(tok::kw_operator)) return Right.is(tok::coloncolon); if (Right.is(tok::l_brace) && Right.is(BK_BracedInit) && diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -110,6 +110,7 @@ void parseCaseLabel(); void parseSwitch(); void parseNamespace(); + void parseModuleImport(); void parseNew(); void parseAccessSpecifier(); bool parseEnum(); diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -1114,6 +1114,35 @@ return Tok->Previous && Tok->Previous->isOneOf(tok::l_paren, tok::comma); } +void UnwrappedLineParser::parseModuleImport() { + nextToken(); + while (!eof()) { + if (FormatTok->is(tok::colon)) { + FormatTok->setType(TT_ModulePartitionColon); + } + // Handle import as we would an include statement. + else if (FormatTok->is(tok::less)) { + nextToken(); + while (!FormatTok->isOneOf(tok::semi, tok::greater, tok::eof)) { + // Mark tokens up to the trailing line comments as implicit string + // literals. + if (FormatTok->isNot(tok::comment) && + !FormatTok->TokenText.startswith("//")) + FormatTok->setType(TT_ImplicitStringLiteral); + nextToken(); + } + } + if (FormatTok->is(tok::semi)) { + nextToken(); + break; + } + nextToken(); + } + + addUnwrappedLine(); + return; +} + // readTokenWithJavaScriptASI reads the next token and terminates the current // line if JavaScript Automatic Semicolon Insertion must // happen between the current token and the next token. @@ -1312,6 +1341,10 @@ addUnwrappedLine(); return; } + if (Style.isCpp()) { + parseModuleImport(); + return; + } } if (Style.isCpp() && FormatTok->isOneOf(Keywords.kw_signals, Keywords.kw_qsignals, diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -22616,6 +22616,71 @@ verifyFormat("auto(*p)() = f;"); // actually a declaration; TODO FIXME } +TEST_F(FormatTest, Cpp20ModulesSupport) { + FormatStyle Style = getLLVMStyle(); + Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never; + Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; + + verifyFormat("export import foo;", Style); + verifyFormat("export import foo:bar;", Style); + verifyFormat("export import foo.bar;", Style); + verifyFormat("export import foo.bar:baz;", Style); + verifyFormat("export import :bar;", Style); + verifyFormat("export module foo:bar;", Style); + verifyFormat("export module foo;", Style); + verifyFormat("export module foo.bar;", Style); + verifyFormat("export module foo.bar:baz;", Style); + verifyFormat("export import ;", Style); + + verifyFormat("export type_name var;", Style); + verifyFormat("template export using A = B;", Style); + verifyFormat("export using A = B;", Style); + verifyFormat("export int func() {\n" + " foo();\n" + "}", + Style); + verifyFormat("export struct {\n" + " int foo;\n" + "};", + Style); + verifyFormat("export {\n" + " int foo;\n" + "};", + Style); + verifyFormat("export export char const *hello() { return \"hello\"; }"); + + verifyFormat("import bar;", Style); + verifyFormat("import foo.bar;", Style); + verifyFormat("import foo:bar;", Style); + verifyFormat("import :bar;", Style); + verifyFormat("import ;", Style); + verifyFormat("import \"header\";", Style); + + verifyFormat("module foo;", Style); + verifyFormat("module foo:bar;", Style); + verifyFormat("module foo.bar;", Style); + verifyFormat("module;", Style); + + verifyFormat("export namespace hi {\n" + "const char *sayhi();\n" + "}", + Style); + + verifyFormat("module :private;", Style); + verifyFormat("import ;", Style); + verifyFormat("import foo...bar;", Style); + verifyFormat("import ..........;", Style); + verifyFormat("module foo:private;", Style); + verifyFormat("import a", Style); + verifyFormat("module a", Style); + verifyFormat("export import a", Style); + verifyFormat("export module a", Style); + + verifyFormat("import", Style); + verifyFormat("module", Style); + verifyFormat("export", Style); +} + } // namespace } // namespace format } // namespace clang