Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -3184,6 +3184,9 @@ * ``LK_None`` (in configuration: ``None``) Do not use. + * ``LK_Carbon`` (in configuration: ``Carbon``) + Should be used for Carbon + * ``LK_Cpp`` (in configuration: ``Cpp``) Should be used for C, C++. Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -2587,6 +2587,8 @@ enum LanguageKind : int8_t { /// Do not use. LK_None, + /// Should be used for Carbon + LK_Carbon, /// Should be used for C, C++. LK_Cpp, /// Should be used for C#. @@ -2614,6 +2616,7 @@ }; bool isCpp() const { return Language == LK_Cpp || Language == LK_ObjC; } bool isCSharp() const { return Language == LK_CSharp; } + bool isCarbon() const { return Language == LK_Carbon; } bool isJson() const { return Language == LK_Json; } bool isJavaScript() const { return Language == LK_JavaScript; } bool isVerilog() const { return Language == LK_Verilog; } @@ -4292,6 +4295,8 @@ switch (Language) { case FormatStyle::LK_Cpp: return "C++"; + case FormatStyle::LK_Carbon: + return "Carbon"; case FormatStyle::LK_CSharp: return "CSharp"; case FormatStyle::LK_ObjC: Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -60,14 +60,15 @@ template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::LanguageKind &Value) { IO.enumCase(Value, "Cpp", FormatStyle::LK_Cpp); + IO.enumCase(Value, "Carbon", FormatStyle::LK_Carbon); + IO.enumCase(Value, "CSharp", FormatStyle::LK_CSharp); IO.enumCase(Value, "Java", FormatStyle::LK_Java); IO.enumCase(Value, "JavaScript", FormatStyle::LK_JavaScript); + IO.enumCase(Value, "Json", FormatStyle::LK_Json); IO.enumCase(Value, "ObjC", FormatStyle::LK_ObjC); IO.enumCase(Value, "Proto", FormatStyle::LK_Proto); IO.enumCase(Value, "TableGen", FormatStyle::LK_TableGen); IO.enumCase(Value, "TextProto", FormatStyle::LK_TextProto); - IO.enumCase(Value, "CSharp", FormatStyle::LK_CSharp); - IO.enumCase(Value, "Json", FormatStyle::LK_Json); } }; @@ -1501,6 +1502,9 @@ GoogleStyle.BreakStringLiterals = false; GoogleStyle.ColumnLimit = 100; GoogleStyle.NamespaceIndentation = FormatStyle::NI_All; + } else if (Language == FormatStyle::LK_Carbon) { + GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty; + GoogleStyle.MaxEmptyLinesToKeep = 1; } return GoogleStyle; @@ -3487,6 +3491,8 @@ return FormatStyle::LK_TableGen; if (FileName.endswith_insensitive(".cs")) return FormatStyle::LK_CSharp; + if (FileName.endswith_insensitive(".carbon")) + return FormatStyle::LK_Carbon; if (FileName.endswith_insensitive(".json")) return FormatStyle::LK_Json; if (FileName.endswith_insensitive(".sv") || Index: clang/lib/Format/FormatToken.h =================================================================== --- clang/lib/Format/FormatToken.h +++ clang/lib/Format/FormatToken.h @@ -1143,6 +1143,10 @@ kw_with = &IdentTable.get("with"); kw_wor = &IdentTable.get("wor"); + // Carbon keywords + kw_me = &IdentTable.get("me"); + kw_fn = &IdentTable.get("fn"); + // Symbols that are treated as keywords. kw_verilogHash = &IdentTable.get("#"); kw_verilogHashHash = &IdentTable.get("##"); @@ -1172,6 +1176,9 @@ // Keywords from the Java section. kw_abstract, kw_extends, kw_implements, kw_instanceof, kw_interface}); + CarbonExtraKeywords = std::unordered_set( + {kw_me, kw_package, kw_fn, kw_abstract}); + // Some keywords are not included here because they don't need special // treatment like `showcancelled` or they should be treated as identifiers // like `int` and `logic`. @@ -1406,6 +1413,10 @@ IdentifierInfo *kw_when; IdentifierInfo *kw_where; + // Carbon keywords + IdentifierInfo *kw_me; + IdentifierInfo *kw_fn; + // Verilog keywords IdentifierInfo *kw_always; IdentifierInfo *kw_always_comb; @@ -1800,6 +1811,9 @@ /// The Verilog keywords beyond the C++ keyword set. std::unordered_set VerilogExtraKeywords; + + /// The Carbon keywords beyond the C++ keyword set + std::unordered_set CarbonExtraKeywords; }; } // namespace format Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -1018,7 +1018,10 @@ ++Contexts.back().FirstObjCSelectorName->ObjCSelectorNameParts; } } else if (Contexts.back().ColonIsForRangeExpr) { - Tok->setType(TT_RangeBasedForLoopColon); + if (Style.isCarbon()) + Tok->setType(TT_JsTypeColon); + else + Tok->setType(TT_RangeBasedForLoopColon); } else if (CurrentToken && CurrentToken->is(tok::numeric_constant)) { Tok->setType(TT_BitFieldColon); } else if (Contexts.size() == 1 && @@ -1038,7 +1041,10 @@ if (PrevPrev && PrevPrev->isOneOf(tok::r_paren, tok::kw_noexcept)) Tok->setType(TT_CtorInitializerColon); } else { - Tok->setType(TT_InheritanceColon); + if (Style.isCarbon()) + Tok->setType(TT_JsTypeColon); + else + Tok->setType(TT_InheritanceColon); } } else if (canBeObjCSelectorComponent(*Tok->Previous) && Tok->Next && (Tok->Next->isOneOf(tok::r_paren, tok::comma) || @@ -1048,7 +1054,10 @@ // the colon are passed as macro arguments. Tok->setType(TT_ObjCMethodExpr); } else if (Contexts.back().ContextKind == tok::l_paren) { - Tok->setType(TT_InlineASMColon); + if (Style.isCarbon()) + Tok->setType(TT_JsTypeColon); + else + Tok->setType(TT_InlineASMColon); } break; case tok::pipe: @@ -1842,6 +1851,13 @@ } } + if (Style.isCarbon() && Line.MustBeDeclaration) { + if (Current.is(tok::arrow) && Current.Next->Next && + Current.Next->Next->is(TT_FunctionLBrace)) { + Current.setType(TT_TrailingReturnArrow); + } + } + // Line.MightBeFunctionDecl can only be true after the parentheses of a // function declaration have been found. In this case, 'Current' is a // trailing token of this declaration and thus cannot be a name. @@ -3908,6 +3924,9 @@ Right.is(tok::l_paren)) { return true; } + } else if (Style.isCarbon()) { + if (Right.is(TT_JsTypeColon)) + return false; } else if (Style.isJavaScript()) { if (Left.is(TT_FatArrow)) return true; Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -3667,9 +3667,9 @@ // parseRecord falls through and does not yet add an unwrapped line as a // record declaration or definition can start a structural element. parseRecord(); - // This does not apply to Java, JavaScript and C#. + // This does not apply to Java, JavaScript and C# or Carbon if (Style.Language == FormatStyle::LK_Java || Style.isJavaScript() || - Style.isCSharp()) { + Style.isCSharp() || Style.isCarbon()) { if (FormatTok->is(tok::semi)) nextToken(); addUnwrappedLine(); Index: clang/tools/clang-format/ClangFormat.cpp =================================================================== --- clang/tools/clang-format/ClangFormat.cpp +++ clang/tools/clang-format/ClangFormat.cpp @@ -582,7 +582,8 @@ cl::SetVersionPrinter(PrintVersion); cl::ParseCommandLineOptions( argc, argv, - "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# " + "A tool to format " + "C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C#/Carbon " "code.\n\n" "If no arguments are specified, it formats the code from standard input\n" "and writes the result to the standard output.\n" Index: clang/unittests/Format/CMakeLists.txt =================================================================== --- clang/unittests/Format/CMakeLists.txt +++ clang/unittests/Format/CMakeLists.txt @@ -6,6 +6,7 @@ CleanupTest.cpp DefinitionBlockSeparatorTest.cpp FormatTest.cpp + FormatTestCarbon.cpp FormatTestComments.cpp FormatTestCSharp.cpp FormatTestJS.cpp Index: clang/unittests/Format/FormatTestCarbon.cpp =================================================================== --- /dev/null +++ clang/unittests/Format/FormatTestCarbon.cpp @@ -0,0 +1,98 @@ +//===- unittest/Format/FormatTestCarbon.cpp - Formatting tests for Carbon -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FormatTestUtils.h" +#include "clang/Format/Format.h" +#include "llvm/Support/Debug.h" +#include "gtest/gtest.h" + +#define DEBUG_TYPE "format-test-json" + +namespace clang { +namespace format { + +class FormatTestCarbon : public ::testing::Test { +protected: + static std::string format(llvm::StringRef Code, unsigned Offset, + unsigned Length, const FormatStyle &Style) { + LLVM_DEBUG(llvm::errs() << "---\n"); + LLVM_DEBUG(llvm::errs() << Code << "\n\n"); + std::vector Ranges(1, tooling::Range(Offset, Length)); + tooling::Replacements Replaces = reformat(Style, Code, Ranges); + auto Result = applyAllReplacements(Code, Replaces); + EXPECT_TRUE(static_cast(Result)); + LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); + return *Result; + } + + static std::string + format(llvm::StringRef Code, + const FormatStyle &Style = getGoogleStyle(FormatStyle::LK_Carbon)) { + return format(Code, 0, Code.size(), Style); + } + + static FormatStyle getStyleWithColumns(unsigned ColumnLimit) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Carbon); + Style.ColumnLimit = ColumnLimit; + return Style; + } + + static void verifyFormatStable(llvm::StringRef Code, + const FormatStyle &Style) { + EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable"; + } + + static void verifyFormat( + llvm::StringRef Code, + const FormatStyle &Style = getGoogleStyle(FormatStyle::LK_Carbon)) { + verifyFormatStable(Code, Style); + EXPECT_EQ(Code.str(), format(test::messUp(Code), Style)); + } +}; + +TEST_F(FormatTestCarbon, CarbonBasicFunctions) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Carbon); + verifyFormat("fn Sum(var a: i32, var b: i32) -> i32 {\n" + " return a + b;\n" + "}", + Style); + verifyFormat("// Empty or void return type.\n" + "fn PrintCount(var count: i32) {\n" + " Print(\" The count is { 0 } \", count);\n" + "}\n", + Style); +} + +TEST_F(FormatTestCarbon, CarbonVariable) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Carbon); + + verifyFormat("var name: auto = \"Carbon is an open-source programming\";", + Style); + verifyFormat("let id: i32 = 57;", Style); + verifyFormat("var total_articles_published: i32 = 4500;", Style); +} + +TEST_F(FormatTestCarbon, CarbonVariableInLoop) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Carbon); + + verifyFormat("for (var name: String in names) {", Style); +} + +TEST_F(FormatTestCarbon, CarbonVariableBreakAfterClass) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Carbon); + + verifyFormat("class Sum {\n" + " var a: i32;\n" + "}\n" + "fn Main() -> i32 {\n" + " var p1: Sum = {.a = 5};\n" + "}", Style); +} + +} // namespace format +} // end namespace clang Index: clang/unittests/Format/TokenAnnotatorTest.cpp =================================================================== --- clang/unittests/Format/TokenAnnotatorTest.cpp +++ clang/unittests/Format/TokenAnnotatorTest.cpp @@ -1045,6 +1045,31 @@ EXPECT_TOKEN(Tokens[9], tok::colon, TT_GotoLabelColon); } +TEST_F(TokenAnnotatorTest, UnderstandsCarbonReturnType) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Carbon); + + auto Tokens = annotate("fn Sum(var a: i32, var b: i32) -> i32 {", Style); + ASSERT_EQ(Tokens.size(), 17u) << Tokens; + EXPECT_TOKEN(Tokens[13], tok::arrow, TT_TrailingReturnArrow); + EXPECT_TOKEN(Tokens[15], tok::l_brace, TT_FunctionLBrace); +} + +TEST_F(TokenAnnotatorTest, UnderstandsCarbonTypeOnColon) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Carbon); + + auto Tokens = annotate("let id: i32 = 57;", Style); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::colon, TT_JsTypeColon); +} + +TEST_F(TokenAnnotatorTest, UnderstandsCarbonTypeOnColonForLoop) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Carbon); + + auto Tokens = annotate("for (var name: String in names) {", Style); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[4], tok::colon, TT_JsTypeColon); +} + } // namespace } // namespace format } // namespace clang