Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -228,6 +228,8 @@ `const` `volatile` `static` `inline` `constexpr` `restrict` to be controlled relative to the `type`. +- Improved Cpp20 Modules support + libclang -------- Index: clang/lib/Format/FormatToken.h =================================================================== --- clang/lib/Format/FormatToken.h +++ clang/lib/Format/FormatToken.h @@ -76,6 +76,7 @@ TYPE(LineComment) \ TYPE(MacroBlockBegin) \ TYPE(MacroBlockEnd) \ + TYPE(ModulePartitionColon) \ TYPE(NamespaceMacro) \ TYPE(NonNullAssertion) \ TYPE(NullCoalescingEqual) \ Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -902,9 +902,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()) @@ -3214,6 +3218,17 @@ if (Right.Tok.getIdentifierInfo() && Left.Tok.getIdentifierInfo()) return true; // Never ever merge two identifiers. if (Style.isCpp()) { + // Space between import . + if (Left.is(Keywords.kw_import) && Right.is(tok::less)) + return true; + // No space between import foo:bar but keep a space between import :bar; + if (Left.is(tok::identifier) && !Left.is(Keywords.kw_import) && + Right.is(TT_ModulePartitionColon)) + return false; + // No space between :bar; + if (Left.is(TT_ModulePartitionColon) && Right.is(tok::identifier)) + return false; + if (Left.is(tok::kw_operator)) return Right.is(tok::coloncolon); if (Right.is(tok::l_brace) && Right.is(BK_BracedInit) && Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -1250,6 +1250,21 @@ addUnwrappedLine(); return; } + if (Style.isCpp()) { + nextToken(); + while (FormatTok) { + if (FormatTok->is(tok::colon)) { + FormatTok->setType(TT_ModulePartitionColon); + } + if (FormatTok->is(tok::semi)) { + nextToken(); + break; + } + nextToken(); + } + addUnwrappedLine(); + return; + } } if (Style.isCpp() && FormatTok->isOneOf(Keywords.kw_signals, Keywords.kw_qsignals, Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -22397,6 +22397,28 @@ EXPECT_EQ(Code, format(Code, Style)); } +TEST_F(FormatTest, Cpp20ModulesSupport) { + FormatStyle Style = getLLVMStyle(); + + verifyFormat("export import foo:bar;", Style); + verifyFormat("export import :bar;", Style); + verifyFormat("export module foo:bar;", Style); + verifyFormat("export module foo;", Style); + + verifyFormat("import foo:bar;", Style); + verifyFormat("import :bar;", Style); + verifyFormat("import bar;", Style); + verifyFormat("import ;", Style); + + verifyFormat("module foo;", Style); + verifyFormat("module foo:bar;", Style); + + verifyFormat("export namespace hi {\n" + "const char *sayhi();\n" + "}", + Style); +} + } // namespace } // namespace format } // namespace clang