diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Format/Format.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Format/Format.cpp | 1192 |
1 files changed, 917 insertions, 275 deletions
diff --git a/contrib/llvm/tools/clang/lib/Format/Format.cpp b/contrib/llvm/tools/clang/lib/Format/Format.cpp index 01c122e..58dd5604 100644 --- a/contrib/llvm/tools/clang/lib/Format/Format.cpp +++ b/contrib/llvm/tools/clang/lib/Format/Format.cpp @@ -13,84 +13,131 @@ /// //===----------------------------------------------------------------------===// -#define DEBUG_TYPE "format-formatter" - #include "ContinuationIndenter.h" #include "TokenAnnotator.h" #include "UnwrappedLineParser.h" #include "WhitespaceManager.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Format/Format.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Debug.h" -#include "llvm/Support/YAMLTraits.h" #include "llvm/Support/Path.h" +#include "llvm/Support/YAMLTraits.h" #include <queue> #include <string> +#define DEBUG_TYPE "format-formatter" + +using clang::format::FormatStyle; + +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(std::string) + namespace llvm { namespace yaml { -template <> -struct ScalarEnumerationTraits<clang::format::FormatStyle::LanguageStandard> { - static void enumeration(IO &IO, - clang::format::FormatStyle::LanguageStandard &Value) { - IO.enumCase(Value, "Cpp03", clang::format::FormatStyle::LS_Cpp03); - IO.enumCase(Value, "C++03", clang::format::FormatStyle::LS_Cpp03); - IO.enumCase(Value, "Cpp11", clang::format::FormatStyle::LS_Cpp11); - IO.enumCase(Value, "C++11", clang::format::FormatStyle::LS_Cpp11); - IO.enumCase(Value, "Auto", clang::format::FormatStyle::LS_Auto); +template <> struct ScalarEnumerationTraits<FormatStyle::LanguageKind> { + static void enumeration(IO &IO, FormatStyle::LanguageKind &Value) { + IO.enumCase(Value, "Cpp", FormatStyle::LK_Cpp); + IO.enumCase(Value, "JavaScript", FormatStyle::LK_JavaScript); + IO.enumCase(Value, "Proto", FormatStyle::LK_Proto); + } +}; + +template <> struct ScalarEnumerationTraits<FormatStyle::LanguageStandard> { + static void enumeration(IO &IO, FormatStyle::LanguageStandard &Value) { + IO.enumCase(Value, "Cpp03", FormatStyle::LS_Cpp03); + IO.enumCase(Value, "C++03", FormatStyle::LS_Cpp03); + IO.enumCase(Value, "Cpp11", FormatStyle::LS_Cpp11); + IO.enumCase(Value, "C++11", FormatStyle::LS_Cpp11); + IO.enumCase(Value, "Auto", FormatStyle::LS_Auto); + } +}; + +template <> struct ScalarEnumerationTraits<FormatStyle::UseTabStyle> { + static void enumeration(IO &IO, FormatStyle::UseTabStyle &Value) { + IO.enumCase(Value, "Never", FormatStyle::UT_Never); + IO.enumCase(Value, "false", FormatStyle::UT_Never); + IO.enumCase(Value, "Always", FormatStyle::UT_Always); + IO.enumCase(Value, "true", FormatStyle::UT_Always); + IO.enumCase(Value, "ForIndentation", FormatStyle::UT_ForIndentation); + } +}; + +template <> struct ScalarEnumerationTraits<FormatStyle::ShortFunctionStyle> { + static void enumeration(IO &IO, FormatStyle::ShortFunctionStyle &Value) { + IO.enumCase(Value, "None", FormatStyle::SFS_None); + IO.enumCase(Value, "false", FormatStyle::SFS_None); + IO.enumCase(Value, "All", FormatStyle::SFS_All); + IO.enumCase(Value, "true", FormatStyle::SFS_All); + IO.enumCase(Value, "Inline", FormatStyle::SFS_Inline); + } +}; + +template <> struct ScalarEnumerationTraits<FormatStyle::BraceBreakingStyle> { + static void enumeration(IO &IO, FormatStyle::BraceBreakingStyle &Value) { + IO.enumCase(Value, "Attach", FormatStyle::BS_Attach); + IO.enumCase(Value, "Linux", FormatStyle::BS_Linux); + IO.enumCase(Value, "Stroustrup", FormatStyle::BS_Stroustrup); + IO.enumCase(Value, "Allman", FormatStyle::BS_Allman); + IO.enumCase(Value, "GNU", FormatStyle::BS_GNU); } }; template <> -struct ScalarEnumerationTraits<clang::format::FormatStyle::UseTabStyle> { +struct ScalarEnumerationTraits<FormatStyle::NamespaceIndentationKind> { static void enumeration(IO &IO, - clang::format::FormatStyle::UseTabStyle &Value) { - IO.enumCase(Value, "Never", clang::format::FormatStyle::UT_Never); - IO.enumCase(Value, "false", clang::format::FormatStyle::UT_Never); - IO.enumCase(Value, "Always", clang::format::FormatStyle::UT_Always); - IO.enumCase(Value, "true", clang::format::FormatStyle::UT_Always); - IO.enumCase(Value, "ForIndentation", - clang::format::FormatStyle::UT_ForIndentation); + FormatStyle::NamespaceIndentationKind &Value) { + IO.enumCase(Value, "None", FormatStyle::NI_None); + IO.enumCase(Value, "Inner", FormatStyle::NI_Inner); + IO.enumCase(Value, "All", FormatStyle::NI_All); } }; template <> -struct ScalarEnumerationTraits<clang::format::FormatStyle::BraceBreakingStyle> { - static void - enumeration(IO &IO, clang::format::FormatStyle::BraceBreakingStyle &Value) { - IO.enumCase(Value, "Attach", clang::format::FormatStyle::BS_Attach); - IO.enumCase(Value, "Linux", clang::format::FormatStyle::BS_Linux); - IO.enumCase(Value, "Stroustrup", clang::format::FormatStyle::BS_Stroustrup); - IO.enumCase(Value, "Allman", clang::format::FormatStyle::BS_Allman); +struct ScalarEnumerationTraits<FormatStyle::PointerAlignmentStyle> { + static void enumeration(IO &IO, + FormatStyle::PointerAlignmentStyle &Value) { + IO.enumCase(Value, "Middle", FormatStyle::PAS_Middle); + IO.enumCase(Value, "Left", FormatStyle::PAS_Left); + IO.enumCase(Value, "Right", FormatStyle::PAS_Right); + + // For backward compatibility. + IO.enumCase(Value, "true", FormatStyle::PAS_Left); + IO.enumCase(Value, "false", FormatStyle::PAS_Right); } }; template <> -struct ScalarEnumerationTraits< - clang::format::FormatStyle::NamespaceIndentationKind> { - static void - enumeration(IO &IO, - clang::format::FormatStyle::NamespaceIndentationKind &Value) { - IO.enumCase(Value, "None", clang::format::FormatStyle::NI_None); - IO.enumCase(Value, "Inner", clang::format::FormatStyle::NI_Inner); - IO.enumCase(Value, "All", clang::format::FormatStyle::NI_All); +struct ScalarEnumerationTraits<FormatStyle::SpaceBeforeParensOptions> { + static void enumeration(IO &IO, + FormatStyle::SpaceBeforeParensOptions &Value) { + IO.enumCase(Value, "Never", FormatStyle::SBPO_Never); + IO.enumCase(Value, "ControlStatements", + FormatStyle::SBPO_ControlStatements); + IO.enumCase(Value, "Always", FormatStyle::SBPO_Always); + + // For backward compatibility. + IO.enumCase(Value, "false", FormatStyle::SBPO_Never); + IO.enumCase(Value, "true", FormatStyle::SBPO_ControlStatements); } }; -template <> struct MappingTraits<clang::format::FormatStyle> { - static void mapping(llvm::yaml::IO &IO, clang::format::FormatStyle &Style) { +template <> struct MappingTraits<FormatStyle> { + static void mapping(IO &IO, FormatStyle &Style) { + // When reading, read the language first, we need it for getPredefinedStyle. + IO.mapOptional("Language", Style.Language); + if (IO.outputting()) { StringRef StylesArray[] = { "LLVM", "Google", "Chromium", - "Mozilla", "WebKit" }; + "Mozilla", "WebKit", "GNU" }; ArrayRef<StringRef> Styles(StylesArray); for (size_t i = 0, e = Styles.size(); i < e; ++i) { StringRef StyleName(Styles[i]); - clang::format::FormatStyle PredefinedStyle; - if (clang::format::getPredefinedStyle(StyleName, &PredefinedStyle) && + FormatStyle PredefinedStyle; + if (getPredefinedStyle(StyleName, Style.Language, &PredefinedStyle) && Style == PredefinedStyle) { IO.mapOptional("# BasedOnStyle", StyleName); break; @@ -99,11 +146,16 @@ template <> struct MappingTraits<clang::format::FormatStyle> { } else { StringRef BasedOnStyle; IO.mapOptional("BasedOnStyle", BasedOnStyle); - if (!BasedOnStyle.empty()) - if (!clang::format::getPredefinedStyle(BasedOnStyle, &Style)) { + if (!BasedOnStyle.empty()) { + FormatStyle::LanguageKind OldLanguage = Style.Language; + FormatStyle::LanguageKind Language = + ((FormatStyle *)IO.getContext())->Language; + if (!getPredefinedStyle(BasedOnStyle, Language, &Style)) { IO.setError(Twine("Unknown value for BasedOnStyle: ", BasedOnStyle)); return; } + Style.Language = OldLanguage; + } } IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset); @@ -113,10 +165,14 @@ template <> struct MappingTraits<clang::format::FormatStyle> { IO.mapOptional("AlignTrailingComments", Style.AlignTrailingComments); IO.mapOptional("AllowAllParametersOfDeclarationOnNextLine", Style.AllowAllParametersOfDeclarationOnNextLine); + IO.mapOptional("AllowShortBlocksOnASingleLine", + Style.AllowShortBlocksOnASingleLine); IO.mapOptional("AllowShortIfStatementsOnASingleLine", Style.AllowShortIfStatementsOnASingleLine); IO.mapOptional("AllowShortLoopsOnASingleLine", Style.AllowShortLoopsOnASingleLine); + IO.mapOptional("AllowShortFunctionsOnASingleLine", + Style.AllowShortFunctionsOnASingleLine); IO.mapOptional("AlwaysBreakTemplateDeclarations", Style.AlwaysBreakTemplateDeclarations); IO.mapOptional("AlwaysBreakBeforeMultilineStrings", @@ -131,12 +187,19 @@ template <> struct MappingTraits<clang::format::FormatStyle> { IO.mapOptional("ColumnLimit", Style.ColumnLimit); IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine", Style.ConstructorInitializerAllOnOneLineOrOnePerLine); - IO.mapOptional("DerivePointerBinding", Style.DerivePointerBinding); + IO.mapOptional("DerivePointerAlignment", Style.DerivePointerAlignment); IO.mapOptional("ExperimentalAutoDetectBinPacking", Style.ExperimentalAutoDetectBinPacking); IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels); + IO.mapOptional("IndentWrappedFunctionNames", + Style.IndentWrappedFunctionNames); + IO.mapOptional("IndentFunctionDeclarationAfterType", + Style.IndentWrappedFunctionNames); IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep); + IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks", + Style.KeepEmptyLinesAtTheStartOfBlocks); IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation); + IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty); IO.mapOptional("ObjCSpaceBeforeProtocolList", Style.ObjCSpaceBeforeProtocolList); IO.mapOptional("PenaltyBreakBeforeFirstCallParameter", @@ -148,7 +211,7 @@ template <> struct MappingTraits<clang::format::FormatStyle> { IO.mapOptional("PenaltyExcessCharacter", Style.PenaltyExcessCharacter); IO.mapOptional("PenaltyReturnTypeOnItsOwnLine", Style.PenaltyReturnTypeOnItsOwnLine); - IO.mapOptional("PointerBindsToType", Style.PointerBindsToType); + IO.mapOptional("PointerAlignment", Style.PointerAlignment); IO.mapOptional("SpacesBeforeTrailingComments", Style.SpacesBeforeTrailingComments); IO.mapOptional("Cpp11BracedListStyle", Style.Cpp11BracedListStyle); @@ -157,18 +220,54 @@ template <> struct MappingTraits<clang::format::FormatStyle> { IO.mapOptional("TabWidth", Style.TabWidth); IO.mapOptional("UseTab", Style.UseTab); IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces); - IO.mapOptional("IndentFunctionDeclarationAfterType", - Style.IndentFunctionDeclarationAfterType); IO.mapOptional("SpacesInParentheses", Style.SpacesInParentheses); IO.mapOptional("SpacesInAngles", Style.SpacesInAngles); IO.mapOptional("SpaceInEmptyParentheses", Style.SpaceInEmptyParentheses); IO.mapOptional("SpacesInCStyleCastParentheses", Style.SpacesInCStyleCastParentheses); - IO.mapOptional("SpaceAfterControlStatementKeyword", - Style.SpaceAfterControlStatementKeyword); + IO.mapOptional("SpacesInContainerLiterals", + Style.SpacesInContainerLiterals); IO.mapOptional("SpaceBeforeAssignmentOperators", Style.SpaceBeforeAssignmentOperators); IO.mapOptional("ContinuationIndentWidth", Style.ContinuationIndentWidth); + IO.mapOptional("CommentPragmas", Style.CommentPragmas); + IO.mapOptional("ForEachMacros", Style.ForEachMacros); + + // For backward compatibility. + if (!IO.outputting()) { + IO.mapOptional("SpaceAfterControlStatementKeyword", + Style.SpaceBeforeParens); + IO.mapOptional("PointerBindsToType", Style.PointerAlignment); + IO.mapOptional("DerivePointerBinding", Style.DerivePointerAlignment); + } + IO.mapOptional("SpaceBeforeParens", Style.SpaceBeforeParens); + IO.mapOptional("DisableFormat", Style.DisableFormat); + } +}; + +// Allows to read vector<FormatStyle> while keeping default values. +// IO.getContext() should contain a pointer to the FormatStyle structure, that +// will be used to get default values for missing keys. +// If the first element has no Language specified, it will be treated as the +// default one for the following elements. +template <> struct DocumentListTraits<std::vector<FormatStyle> > { + static size_t size(IO &IO, std::vector<FormatStyle> &Seq) { + return Seq.size(); + } + static FormatStyle &element(IO &IO, std::vector<FormatStyle> &Seq, + size_t Index) { + if (Index >= Seq.size()) { + assert(Index == Seq.size()); + FormatStyle Template; + if (Seq.size() > 0 && Seq[0].Language == FormatStyle::LK_None) { + Template = Seq[0]; + } else { + Template = *((const FormatStyle *)IO.getContext()); + Template.Language = FormatStyle::LK_None; + } + Seq.resize(Index + 1, Template); + } + return Seq[Index]; } }; } @@ -177,19 +276,39 @@ template <> struct MappingTraits<clang::format::FormatStyle> { namespace clang { namespace format { -void setDefaultPenalties(FormatStyle &Style) { - Style.PenaltyBreakComment = 60; - Style.PenaltyBreakFirstLessLess = 120; - Style.PenaltyBreakString = 1000; - Style.PenaltyExcessCharacter = 1000000; +const std::error_category &getParseCategory() { + static ParseErrorCategory C; + return C; +} +std::error_code make_error_code(ParseError e) { + return std::error_code(static_cast<int>(e), getParseCategory()); +} + +const char *ParseErrorCategory::name() const LLVM_NOEXCEPT { + return "clang-format.parse_error"; +} + +std::string ParseErrorCategory::message(int EV) const { + switch (static_cast<ParseError>(EV)) { + case ParseError::Success: + return "Success"; + case ParseError::Error: + return "Invalid argument"; + case ParseError::Unsuitable: + return "Unsuitable"; + } + llvm_unreachable("unexpected parse error"); } FormatStyle getLLVMStyle() { FormatStyle LLVMStyle; + LLVMStyle.Language = FormatStyle::LK_Cpp; LLVMStyle.AccessModifierOffset = -2; LLVMStyle.AlignEscapedNewlinesLeft = false; LLVMStyle.AlignTrailingComments = true; LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true; + LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; + LLVMStyle.AllowShortBlocksOnASingleLine = false; LLVMStyle.AllowShortIfStatementsOnASingleLine = false; LLVMStyle.AllowShortLoopsOnASingleLine = false; LLVMStyle.AlwaysBreakBeforeMultilineStrings = false; @@ -200,91 +319,92 @@ FormatStyle getLLVMStyle() { LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach; LLVMStyle.BreakConstructorInitializersBeforeComma = false; LLVMStyle.ColumnLimit = 80; + LLVMStyle.CommentPragmas = "^ IWYU pragma:"; LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false; LLVMStyle.ConstructorInitializerIndentWidth = 4; - LLVMStyle.Cpp11BracedListStyle = false; - LLVMStyle.DerivePointerBinding = false; + LLVMStyle.ContinuationIndentWidth = 4; + LLVMStyle.Cpp11BracedListStyle = true; + LLVMStyle.DerivePointerAlignment = false; LLVMStyle.ExperimentalAutoDetectBinPacking = false; + LLVMStyle.ForEachMacros.push_back("foreach"); + LLVMStyle.ForEachMacros.push_back("Q_FOREACH"); + LLVMStyle.ForEachMacros.push_back("BOOST_FOREACH"); LLVMStyle.IndentCaseLabels = false; - LLVMStyle.IndentFunctionDeclarationAfterType = false; + LLVMStyle.IndentWrappedFunctionNames = false; LLVMStyle.IndentWidth = 2; LLVMStyle.TabWidth = 8; LLVMStyle.MaxEmptyLinesToKeep = 1; + LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true; LLVMStyle.NamespaceIndentation = FormatStyle::NI_None; + LLVMStyle.ObjCSpaceAfterProperty = false; LLVMStyle.ObjCSpaceBeforeProtocolList = true; - LLVMStyle.PointerBindsToType = false; + LLVMStyle.PointerAlignment = FormatStyle::PAS_Right; LLVMStyle.SpacesBeforeTrailingComments = 1; - LLVMStyle.Standard = FormatStyle::LS_Cpp03; + LLVMStyle.Standard = FormatStyle::LS_Cpp11; LLVMStyle.UseTab = FormatStyle::UT_Never; LLVMStyle.SpacesInParentheses = false; LLVMStyle.SpaceInEmptyParentheses = false; + LLVMStyle.SpacesInContainerLiterals = true; LLVMStyle.SpacesInCStyleCastParentheses = false; - LLVMStyle.SpaceAfterControlStatementKeyword = true; + LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; LLVMStyle.SpaceBeforeAssignmentOperators = true; - LLVMStyle.ContinuationIndentWidth = 4; LLVMStyle.SpacesInAngles = false; - setDefaultPenalties(LLVMStyle); + LLVMStyle.PenaltyBreakComment = 300; + LLVMStyle.PenaltyBreakFirstLessLess = 120; + LLVMStyle.PenaltyBreakString = 1000; + LLVMStyle.PenaltyExcessCharacter = 1000000; LLVMStyle.PenaltyReturnTypeOnItsOwnLine = 60; LLVMStyle.PenaltyBreakBeforeFirstCallParameter = 19; + LLVMStyle.DisableFormat = false; + return LLVMStyle; } -FormatStyle getGoogleStyle() { - FormatStyle GoogleStyle; +FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { + FormatStyle GoogleStyle = getLLVMStyle(); + GoogleStyle.Language = Language; + GoogleStyle.AccessModifierOffset = -1; GoogleStyle.AlignEscapedNewlinesLeft = true; - GoogleStyle.AlignTrailingComments = true; - GoogleStyle.AllowAllParametersOfDeclarationOnNextLine = true; GoogleStyle.AllowShortIfStatementsOnASingleLine = true; GoogleStyle.AllowShortLoopsOnASingleLine = true; GoogleStyle.AlwaysBreakBeforeMultilineStrings = true; GoogleStyle.AlwaysBreakTemplateDeclarations = true; - GoogleStyle.BinPackParameters = true; - GoogleStyle.BreakBeforeBinaryOperators = false; - GoogleStyle.BreakBeforeTernaryOperators = true; - GoogleStyle.BreakBeforeBraces = FormatStyle::BS_Attach; - GoogleStyle.BreakConstructorInitializersBeforeComma = false; - GoogleStyle.ColumnLimit = 80; GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; - GoogleStyle.ConstructorInitializerIndentWidth = 4; - GoogleStyle.Cpp11BracedListStyle = true; - GoogleStyle.DerivePointerBinding = true; - GoogleStyle.ExperimentalAutoDetectBinPacking = false; + GoogleStyle.DerivePointerAlignment = true; GoogleStyle.IndentCaseLabels = true; - GoogleStyle.IndentFunctionDeclarationAfterType = true; - GoogleStyle.IndentWidth = 2; - GoogleStyle.TabWidth = 8; - GoogleStyle.MaxEmptyLinesToKeep = 1; - GoogleStyle.NamespaceIndentation = FormatStyle::NI_None; + GoogleStyle.KeepEmptyLinesAtTheStartOfBlocks = false; + GoogleStyle.ObjCSpaceAfterProperty = false; GoogleStyle.ObjCSpaceBeforeProtocolList = false; - GoogleStyle.PointerBindsToType = true; + GoogleStyle.PointerAlignment = FormatStyle::PAS_Left; GoogleStyle.SpacesBeforeTrailingComments = 2; GoogleStyle.Standard = FormatStyle::LS_Auto; - GoogleStyle.UseTab = FormatStyle::UT_Never; - GoogleStyle.SpacesInParentheses = false; - GoogleStyle.SpaceInEmptyParentheses = false; - GoogleStyle.SpacesInCStyleCastParentheses = false; - GoogleStyle.SpaceAfterControlStatementKeyword = true; - GoogleStyle.SpaceBeforeAssignmentOperators = true; - GoogleStyle.ContinuationIndentWidth = 4; - GoogleStyle.SpacesInAngles = false; - - setDefaultPenalties(GoogleStyle); + GoogleStyle.PenaltyReturnTypeOnItsOwnLine = 200; GoogleStyle.PenaltyBreakBeforeFirstCallParameter = 1; + if (Language == FormatStyle::LK_JavaScript) { + GoogleStyle.BreakBeforeTernaryOperators = false; + GoogleStyle.MaxEmptyLinesToKeep = 3; + GoogleStyle.SpacesInContainerLiterals = false; + } else if (Language == FormatStyle::LK_Proto) { + GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; + GoogleStyle.SpacesInContainerLiterals = false; + } + return GoogleStyle; } -FormatStyle getChromiumStyle() { - FormatStyle ChromiumStyle = getGoogleStyle(); +FormatStyle getChromiumStyle(FormatStyle::LanguageKind Language) { + FormatStyle ChromiumStyle = getGoogleStyle(Language); ChromiumStyle.AllowAllParametersOfDeclarationOnNextLine = false; + ChromiumStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Inline; ChromiumStyle.AllowShortIfStatementsOnASingleLine = false; ChromiumStyle.AllowShortLoopsOnASingleLine = false; ChromiumStyle.BinPackParameters = false; - ChromiumStyle.DerivePointerBinding = false; + ChromiumStyle.DerivePointerAlignment = false; ChromiumStyle.Standard = FormatStyle::LS_Cpp03; return ChromiumStyle; } @@ -292,12 +412,15 @@ FormatStyle getChromiumStyle() { FormatStyle getMozillaStyle() { FormatStyle MozillaStyle = getLLVMStyle(); MozillaStyle.AllowAllParametersOfDeclarationOnNextLine = false; + MozillaStyle.Cpp11BracedListStyle = false; MozillaStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; - MozillaStyle.DerivePointerBinding = true; + MozillaStyle.DerivePointerAlignment = true; MozillaStyle.IndentCaseLabels = true; + MozillaStyle.ObjCSpaceAfterProperty = true; MozillaStyle.ObjCSpaceBeforeProtocolList = false; MozillaStyle.PenaltyReturnTypeOnItsOwnLine = 200; - MozillaStyle.PointerBindsToType = true; + MozillaStyle.PointerAlignment = FormatStyle::PAS_Left; + MozillaStyle.Standard = FormatStyle::LS_Cpp03; return MozillaStyle; } @@ -308,36 +431,102 @@ FormatStyle getWebKitStyle() { Style.BreakBeforeBinaryOperators = true; Style.BreakBeforeBraces = FormatStyle::BS_Stroustrup; Style.BreakConstructorInitializersBeforeComma = true; + Style.Cpp11BracedListStyle = false; Style.ColumnLimit = 0; Style.IndentWidth = 4; Style.NamespaceIndentation = FormatStyle::NI_Inner; - Style.PointerBindsToType = true; + Style.ObjCSpaceAfterProperty = true; + Style.PointerAlignment = FormatStyle::PAS_Left; + Style.Standard = FormatStyle::LS_Cpp03; + return Style; +} + +FormatStyle getGNUStyle() { + FormatStyle Style = getLLVMStyle(); + Style.BreakBeforeBinaryOperators = true; + Style.BreakBeforeBraces = FormatStyle::BS_GNU; + Style.BreakBeforeTernaryOperators = true; + Style.Cpp11BracedListStyle = false; + Style.ColumnLimit = 79; + Style.SpaceBeforeParens = FormatStyle::SBPO_Always; + Style.Standard = FormatStyle::LS_Cpp03; return Style; } -bool getPredefinedStyle(StringRef Name, FormatStyle *Style) { - if (Name.equals_lower("llvm")) +FormatStyle getNoStyle() { + FormatStyle NoStyle = getLLVMStyle(); + NoStyle.DisableFormat = true; + return NoStyle; +} + +bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, + FormatStyle *Style) { + if (Name.equals_lower("llvm")) { *Style = getLLVMStyle(); - else if (Name.equals_lower("chromium")) - *Style = getChromiumStyle(); - else if (Name.equals_lower("mozilla")) + } else if (Name.equals_lower("chromium")) { + *Style = getChromiumStyle(Language); + } else if (Name.equals_lower("mozilla")) { *Style = getMozillaStyle(); - else if (Name.equals_lower("google")) - *Style = getGoogleStyle(); - else if (Name.equals_lower("webkit")) + } else if (Name.equals_lower("google")) { + *Style = getGoogleStyle(Language); + } else if (Name.equals_lower("webkit")) { *Style = getWebKitStyle(); - else + } else if (Name.equals_lower("gnu")) { + *Style = getGNUStyle(); + } else if (Name.equals_lower("none")) { + *Style = getNoStyle(); + } else { return false; + } + Style->Language = Language; return true; } -llvm::error_code parseConfiguration(StringRef Text, FormatStyle *Style) { +std::error_code parseConfiguration(StringRef Text, FormatStyle *Style) { + assert(Style); + FormatStyle::LanguageKind Language = Style->Language; + assert(Language != FormatStyle::LK_None); if (Text.trim().empty()) - return llvm::make_error_code(llvm::errc::invalid_argument); + return make_error_code(ParseError::Error); + + std::vector<FormatStyle> Styles; llvm::yaml::Input Input(Text); - Input >> *Style; - return Input.error(); + // DocumentListTraits<vector<FormatStyle>> uses the context to get default + // values for the fields, keys for which are missing from the configuration. + // Mapping also uses the context to get the language to find the correct + // base style. + Input.setContext(Style); + Input >> Styles; + if (Input.error()) + return Input.error(); + + for (unsigned i = 0; i < Styles.size(); ++i) { + // Ensures that only the first configuration can skip the Language option. + if (Styles[i].Language == FormatStyle::LK_None && i != 0) + return make_error_code(ParseError::Error); + // Ensure that each language is configured at most once. + for (unsigned j = 0; j < i; ++j) { + if (Styles[i].Language == Styles[j].Language) { + DEBUG(llvm::dbgs() + << "Duplicate languages in the config file on positions " << j + << " and " << i << "\n"); + return make_error_code(ParseError::Error); + } + } + } + // Look for a suitable configuration starting from the end, so we can + // find the configuration for the specific language first, and the default + // configuration (which can only be at slot 0) after it. + for (int i = Styles.size() - 1; i >= 0; --i) { + if (Styles[i].Language == Language || + Styles[i].Language == FormatStyle::LK_None) { + *Style = Styles[i]; + Style->Language = Language; + return make_error_code(ParseError::Success); + } + } + return make_error_code(ParseError::Unsuitable); } std::string configurationAsText(const FormatStyle &Style) { @@ -362,7 +551,7 @@ public: void format(unsigned FirstIndent, const AnnotatedLine *Line) { LineState State = Indenter->getInitialState(FirstIndent, Line, /*DryRun=*/false); - while (State.NextToken != NULL) { + while (State.NextToken) { bool Newline = Indenter->mustBreak(State) || (Indenter->canBreak(State) && State.NextToken->NewlinesBefore > 0); @@ -381,14 +570,14 @@ public: /// \brief Calculates how many lines can be merged into 1 starting at \p I. unsigned tryFitMultipleLinesInOne(unsigned Indent, - SmallVectorImpl<AnnotatedLine *>::const_iterator &I, + SmallVectorImpl<AnnotatedLine *>::const_iterator I, SmallVectorImpl<AnnotatedLine *>::const_iterator E) { // We can never merge stuff if there are trailing line comments. - AnnotatedLine *TheLine = *I; + const AnnotatedLine *TheLine = *I; if (TheLine->Last->Type == TT_LineComment) return 0; - if (Indent > Style.ColumnLimit) + if (Style.ColumnLimit > 0 && Indent > Style.ColumnLimit) return 0; unsigned Limit = @@ -399,19 +588,54 @@ public: ? 0 : Limit - TheLine->Last->TotalLength; - if (I + 1 == E || I[1]->Type == LT_Invalid) + if (I + 1 == E || I[1]->Type == LT_Invalid || I[1]->First->MustBreakBefore) return 0; + // FIXME: TheLine->Level != 0 might or might not be the right check to do. + // If necessary, change to something smarter. + bool MergeShortFunctions = + Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_All || + (Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Inline && + TheLine->Level != 0); + + if (TheLine->Last->Type == TT_FunctionLBrace && + TheLine->First != TheLine->Last) { + return MergeShortFunctions ? tryMergeSimpleBlock(I, E, Limit) : 0; + } if (TheLine->Last->is(tok::l_brace)) { - return tryMergeSimpleBlock(I, E, Limit); - } else if (Style.AllowShortIfStatementsOnASingleLine && - TheLine->First->is(tok::kw_if)) { - return tryMergeSimpleControlStatement(I, E, Limit); - } else if (Style.AllowShortLoopsOnASingleLine && - TheLine->First->isOneOf(tok::kw_for, tok::kw_while)) { - return tryMergeSimpleControlStatement(I, E, Limit); - } else if (TheLine->InPPDirective && (TheLine->First->HasUnescapedNewline || - TheLine->First->IsFirst)) { + return Style.BreakBeforeBraces == FormatStyle::BS_Attach + ? tryMergeSimpleBlock(I, E, Limit) + : 0; + } + if (I[1]->First->Type == TT_FunctionLBrace && + Style.BreakBeforeBraces != FormatStyle::BS_Attach) { + // Check for Limit <= 2 to account for the " {". + if (Limit <= 2 || (Style.ColumnLimit == 0 && containsMustBreak(TheLine))) + return 0; + Limit -= 2; + + unsigned MergedLines = 0; + if (MergeShortFunctions) { + MergedLines = tryMergeSimpleBlock(I + 1, E, Limit); + // If we managed to merge the block, count the function header, which is + // on a separate line. + if (MergedLines > 0) + ++MergedLines; + } + return MergedLines; + } + if (TheLine->First->is(tok::kw_if)) { + return Style.AllowShortIfStatementsOnASingleLine + ? tryMergeSimpleControlStatement(I, E, Limit) + : 0; + } + if (TheLine->First->isOneOf(tok::kw_for, tok::kw_while)) { + return Style.AllowShortLoopsOnASingleLine + ? tryMergeSimpleControlStatement(I, E, Limit) + : 0; + } + if (TheLine->InPPDirective && + (TheLine->First->HasUnescapedNewline || TheLine->First->IsFirst)) { return tryMergeSimplePPDirective(I, E, Limit); } return 0; @@ -419,7 +643,7 @@ public: private: unsigned - tryMergeSimplePPDirective(SmallVectorImpl<AnnotatedLine *>::const_iterator &I, + tryMergeSimplePPDirective(SmallVectorImpl<AnnotatedLine *>::const_iterator I, SmallVectorImpl<AnnotatedLine *>::const_iterator E, unsigned Limit) { if (Limit == 0) @@ -434,23 +658,25 @@ private: } unsigned tryMergeSimpleControlStatement( - SmallVectorImpl<AnnotatedLine *>::const_iterator &I, + SmallVectorImpl<AnnotatedLine *>::const_iterator I, SmallVectorImpl<AnnotatedLine *>::const_iterator E, unsigned Limit) { if (Limit == 0) return 0; - if (Style.BreakBeforeBraces == FormatStyle::BS_Allman && - I[1]->First->is(tok::l_brace)) + if ((Style.BreakBeforeBraces == FormatStyle::BS_Allman || + Style.BreakBeforeBraces == FormatStyle::BS_GNU) && + (I[1]->First->is(tok::l_brace) && !Style.AllowShortBlocksOnASingleLine)) return 0; if (I[1]->InPPDirective != (*I)->InPPDirective || (I[1]->InPPDirective && I[1]->First->HasUnescapedNewline)) return 0; + Limit = limitConsideringMacros(I + 1, E, Limit); AnnotatedLine &Line = **I; if (Line.Last->isNot(tok::r_paren)) return 0; if (1 + I[1]->Last->TotalLength > Limit) return 0; if (I[1]->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for, - tok::kw_while) || + tok::kw_while) || I[1]->First->Type == TT_LineComment) return 0; // Only inline simple if's (no nested if or else). @@ -461,54 +687,69 @@ private: } unsigned - tryMergeSimpleBlock(SmallVectorImpl<AnnotatedLine *>::const_iterator &I, + tryMergeSimpleBlock(SmallVectorImpl<AnnotatedLine *>::const_iterator I, SmallVectorImpl<AnnotatedLine *>::const_iterator E, unsigned Limit) { - // No merging if the brace already is on the next line. - if (Style.BreakBeforeBraces != FormatStyle::BS_Attach) + AnnotatedLine &Line = **I; + + // Don't merge ObjC @ keywords and methods. + if (Line.First->isOneOf(tok::at, tok::minus, tok::plus)) return 0; - // First, check that the current line allows merging. This is the case if - // we're not in a control flow statement and the last token is an opening - // brace. - AnnotatedLine &Line = **I; - if (Line.First->isOneOf(tok::kw_if, tok::kw_while, tok::kw_do, tok::r_brace, - tok::kw_else, tok::kw_try, tok::kw_catch, - tok::kw_for, - // This gets rid of all ObjC @ keywords and methods. - tok::at, tok::minus, tok::plus)) + // Check that the current line allows merging. This depends on whether we + // are in a control flow statements as well as several style flags. + if (Line.First->isOneOf(tok::kw_else, tok::kw_case)) return 0; + if (Line.First->isOneOf(tok::kw_if, tok::kw_while, tok::kw_do, tok::kw_try, + tok::kw_catch, tok::kw_for, tok::r_brace)) { + if (!Style.AllowShortBlocksOnASingleLine) + return 0; + if (!Style.AllowShortIfStatementsOnASingleLine && + Line.First->is(tok::kw_if)) + return 0; + if (!Style.AllowShortLoopsOnASingleLine && + Line.First->isOneOf(tok::kw_while, tok::kw_do, tok::kw_for)) + return 0; + // FIXME: Consider an option to allow short exception handling clauses on + // a single line. + if (Line.First->isOneOf(tok::kw_try, tok::kw_catch)) + return 0; + } FormatToken *Tok = I[1]->First; if (Tok->is(tok::r_brace) && !Tok->MustBreakBefore && - (Tok->getNextNonComment() == NULL || + (Tok->getNextNonComment() == nullptr || Tok->getNextNonComment()->is(tok::semi))) { // We merge empty blocks even if the line exceeds the column limit. Tok->SpacesRequiredBefore = 0; Tok->CanBreakBefore = true; return 1; } else if (Limit != 0 && Line.First->isNot(tok::kw_namespace)) { + // We don't merge short records. + if (Line.First->isOneOf(tok::kw_class, tok::kw_union, tok::kw_struct)) + return 0; + // Check that we still have three lines and they fit into the limit. if (I + 2 == E || I[2]->Type == LT_Invalid) return 0; + Limit = limitConsideringMacros(I + 2, E, Limit); if (!nextTwoLinesFitInto(I, Limit)) return 0; // Second, check that the next line does not contain any braces - if it // does, readability declines when putting it into a single line. - if (I[1]->Last->Type == TT_LineComment || Tok->MustBreakBefore) + if (I[1]->Last->Type == TT_LineComment) return 0; do { - if (Tok->isOneOf(tok::l_brace, tok::r_brace)) + if (Tok->is(tok::l_brace) && Tok->BlockKind != BK_BracedInit) return 0; Tok = Tok->Next; - } while (Tok != NULL); + } while (Tok); - // Last, check that the third line contains a single closing brace. + // Last, check that the third line starts with a closing brace. Tok = I[2]->First; - if (Tok->getNextNonComment() != NULL || Tok->isNot(tok::r_brace) || - Tok->MustBreakBefore) + if (Tok->isNot(tok::r_brace)) return 0; return 2; @@ -516,34 +757,60 @@ private: return 0; } + /// Returns the modified column limit for \p I if it is inside a macro and + /// needs a trailing '\'. + unsigned + limitConsideringMacros(SmallVectorImpl<AnnotatedLine *>::const_iterator I, + SmallVectorImpl<AnnotatedLine *>::const_iterator E, + unsigned Limit) { + if (I[0]->InPPDirective && I + 1 != E && + !I[1]->First->HasUnescapedNewline && !I[1]->First->is(tok::eof)) { + return Limit < 2 ? 0 : Limit - 2; + } + return Limit; + } + bool nextTwoLinesFitInto(SmallVectorImpl<AnnotatedLine *>::const_iterator I, unsigned Limit) { + if (I[1]->First->MustBreakBefore || I[2]->First->MustBreakBefore) + return false; return 1 + I[1]->Last->TotalLength + 1 + I[2]->Last->TotalLength <= Limit; } + bool containsMustBreak(const AnnotatedLine *Line) { + for (const FormatToken *Tok = Line->First; Tok; Tok = Tok->Next) { + if (Tok->MustBreakBefore) + return true; + } + return false; + } + const FormatStyle &Style; }; class UnwrappedLineFormatter { public: - UnwrappedLineFormatter(SourceManager &SourceMgr, - SmallVectorImpl<CharSourceRange> &Ranges, - ContinuationIndenter *Indenter, + UnwrappedLineFormatter(ContinuationIndenter *Indenter, WhitespaceManager *Whitespaces, const FormatStyle &Style) - : SourceMgr(SourceMgr), Ranges(Ranges), Indenter(Indenter), - Whitespaces(Whitespaces), Style(Style), Joiner(Style) {} + : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style), + Joiner(Style) {} unsigned format(const SmallVectorImpl<AnnotatedLine *> &Lines, bool DryRun, - int AdditionalIndent = 0) { + int AdditionalIndent = 0, bool FixBadIndentation = false) { + // Try to look up already computed penalty in DryRun-mode. + std::pair<const SmallVectorImpl<AnnotatedLine *> *, unsigned> CacheKey( + &Lines, AdditionalIndent); + auto CacheIt = PenaltyCache.find(CacheKey); + if (DryRun && CacheIt != PenaltyCache.end()) + return CacheIt->second; + assert(!Lines.empty()); unsigned Penalty = 0; std::vector<int> IndentForLevel; for (unsigned i = 0, e = Lines[0]->Level; i != e; ++i) IndentForLevel.push_back(Style.IndentWidth * i + AdditionalIndent); - bool PreviousLineWasTouched = false; - const AnnotatedLine *PreviousLine = NULL; - bool FormatPPDirective = false; + const AnnotatedLine *PreviousLine = nullptr; for (SmallVectorImpl<AnnotatedLine *>::const_iterator I = Lines.begin(), E = Lines.end(); I != E; ++I) { @@ -551,21 +818,30 @@ public: const FormatToken *FirstTok = TheLine.First; int Offset = getIndentOffset(*FirstTok); - // Check whether this line is part of a formatted preprocessor directive. - if (FirstTok->HasUnescapedNewline) - FormatPPDirective = false; - if (!FormatPPDirective && TheLine.InPPDirective && - (touchesLine(TheLine) || touchesPPDirective(I + 1, E))) - FormatPPDirective = true; - // Determine indent and try to merge multiple unwrapped lines. - while (IndentForLevel.size() <= TheLine.Level) - IndentForLevel.push_back(-1); - IndentForLevel.resize(TheLine.Level + 1); - unsigned Indent = getIndent(IndentForLevel, TheLine.Level); + unsigned Indent; + if (TheLine.InPPDirective) { + Indent = TheLine.Level * Style.IndentWidth; + } else { + while (IndentForLevel.size() <= TheLine.Level) + IndentForLevel.push_back(-1); + IndentForLevel.resize(TheLine.Level + 1); + Indent = getIndent(IndentForLevel, TheLine.Level); + } + unsigned LevelIndent = Indent; if (static_cast<int>(Indent) + Offset >= 0) Indent += Offset; + + // Merge multiple lines if possible. unsigned MergedLines = Joiner.tryFitMultipleLinesInOne(Indent, I, E); + if (MergedLines > 0 && Style.ColumnLimit == 0) { + // Disallow line merging if there is a break at the start of one of the + // input lines. + for (unsigned i = 0; i < MergedLines; ++i) { + if (I[i + 1]->First->NewlinesBefore > 0) + MergedLines = 0; + } + } if (!DryRun) { for (unsigned i = 0; i < MergedLines; ++i) { join(*I[i], *I[i + 1]); @@ -573,18 +849,18 @@ public: } I += MergedLines; - bool WasMoved = PreviousLineWasTouched && FirstTok->NewlinesBefore == 0; + bool FixIndentation = + FixBadIndentation && (LevelIndent != FirstTok->OriginalColumn); if (TheLine.First->is(tok::eof)) { - if (PreviousLineWasTouched && !DryRun) { + if (PreviousLine && PreviousLine->Affected && !DryRun) { + // Remove the file's trailing whitespace. unsigned Newlines = std::min(FirstTok->NewlinesBefore, 1u); Whitespaces->replaceWhitespace(*TheLine.First, Newlines, /*IndentLevel=*/0, /*Spaces=*/0, /*TargetColumn=*/0); } } else if (TheLine.Type != LT_Invalid && - (WasMoved || FormatPPDirective || touchesLine(TheLine))) { - unsigned LevelIndent = - getIndent(IndentForLevel, TheLine.Level); + (TheLine.Affected || FixIndentation)) { if (FirstTok->WhitespaceRange.isValid()) { if (!DryRun) formatFirstToken(*TheLine.First, PreviousLine, TheLine.Level, @@ -603,9 +879,12 @@ public: if (TheLine.Last->TotalLength + Indent <= ColumnLimit) { LineState State = Indenter->getInitialState(Indent, &TheLine, DryRun); - while (State.NextToken != NULL) + while (State.NextToken) { + formatChildren(State, /*Newline=*/false, /*DryRun=*/false, Penalty); Indenter->addTokenToState(State, /*Newline=*/false, DryRun); + } } else if (Style.ColumnLimit == 0) { + // FIXME: Implement nested blocks for ColumnLimit = 0. NoColumnLimitFormatter Formatter(Indenter); if (!DryRun) Formatter.format(Indent, &TheLine); @@ -613,19 +892,21 @@ public: Penalty += format(TheLine, Indent, DryRun); } - IndentForLevel[TheLine.Level] = LevelIndent; - PreviousLineWasTouched = true; + if (!TheLine.InPPDirective) + IndentForLevel[TheLine.Level] = LevelIndent; + } else if (TheLine.ChildrenAffected) { + format(TheLine.Children, DryRun); } else { // Format the first token if necessary, and notify the WhitespaceManager // about the unchanged whitespace. - for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) { + for (FormatToken *Tok = TheLine.First; Tok; Tok = Tok->Next) { if (Tok == TheLine.First && (Tok->NewlinesBefore > 0 || Tok->IsFirst)) { unsigned LevelIndent = Tok->OriginalColumn; if (!DryRun) { - // Remove trailing whitespace of the previous line if it was - // touched. - if (PreviousLineWasTouched || touchesEmptyLineBefore(TheLine)) { + // Remove trailing whitespace of the previous line. + if ((PreviousLine && PreviousLine->Affected) || + TheLine.LeadingEmptyLinesAffected) { formatFirstToken(*Tok, PreviousLine, TheLine.Level, LevelIndent, TheLine.InPPDirective); } else { @@ -635,24 +916,21 @@ public: if (static_cast<int>(LevelIndent) - Offset >= 0) LevelIndent -= Offset; - if (Tok->isNot(tok::comment)) + if (Tok->isNot(tok::comment) && !TheLine.InPPDirective) IndentForLevel[TheLine.Level] = LevelIndent; } else if (!DryRun) { Whitespaces->addUntouchableToken(*Tok, TheLine.InPPDirective); } } - // If we did not reformat this unwrapped line, the column at the end of - // the last token is unchanged - thus, we can calculate the end of the - // last token. - PreviousLineWasTouched = false; } if (!DryRun) { - for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) { + for (FormatToken *Tok = TheLine.First; Tok; Tok = Tok->Next) { Tok->Finalized = true; } } PreviousLine = *I; } + PenaltyCache[CacheKey] = Penalty; return Penalty; } @@ -722,6 +1000,14 @@ private: Newlines = std::min(Newlines, 1u); if (Newlines == 0 && !RootToken.IsFirst) Newlines = 1; + if (RootToken.IsFirst && !RootToken.HasUnescapedNewline) + Newlines = 0; + + // Remove empty lines after "{". + if (!Style.KeepEmptyLinesAtTheStartOfBlocks && PreviousLine && + PreviousLine->Last->is(tok::l_brace) && + PreviousLine->First->isNot(tok::kw_namespace)) + Newlines = 1; // Insert extra new line before access specifiers. if (PreviousLine && PreviousLine->Last->isOneOf(tok::semi, tok::r_brace) && @@ -732,9 +1018,9 @@ private: if (PreviousLine && PreviousLine->First->isAccessSpecifier()) Newlines = std::min(1u, Newlines); - Whitespaces->replaceWhitespace( - RootToken, Newlines, IndentLevel, Indent, Indent, - InPPDirective && !RootToken.HasUnescapedNewline); + Whitespaces->replaceWhitespace(RootToken, Newlines, IndentLevel, Indent, + Indent, InPPDirective && + !RootToken.HasUnescapedNewline); } /// \brief Get the indent of \p Level from \p IndentForLevel. @@ -753,6 +1039,8 @@ private: void join(AnnotatedLine &A, const AnnotatedLine &B) { assert(!A.Last->Next); assert(!B.First->Previous); + if (B.Affected) + A.Affected = true; A.Last->Next = B.First; B.First->Previous = A.Last; B.First->CanBreakBefore = true; @@ -768,47 +1056,11 @@ private: return Style.ColumnLimit - (InPPDirective ? 2 : 0); } - bool touchesRanges(const CharSourceRange &Range) { - for (SmallVectorImpl<CharSourceRange>::const_iterator I = Ranges.begin(), - E = Ranges.end(); - I != E; ++I) { - if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), I->getBegin()) && - !SourceMgr.isBeforeInTranslationUnit(I->getEnd(), Range.getBegin())) - return true; - } - return false; - } - - bool touchesLine(const AnnotatedLine &TheLine) { - const FormatToken *First = TheLine.First; - const FormatToken *Last = TheLine.Last; - CharSourceRange LineRange = CharSourceRange::getCharRange( - First->WhitespaceRange.getBegin().getLocWithOffset( - First->LastNewlineOffset), - Last->getStartOfNonWhitespace().getLocWithOffset( - Last->TokenText.size() - 1)); - return touchesRanges(LineRange); - } - - bool touchesPPDirective(SmallVectorImpl<AnnotatedLine *>::const_iterator I, - SmallVectorImpl<AnnotatedLine *>::const_iterator E) { - for (; I != E; ++I) { - if ((*I)->First->HasUnescapedNewline) - return false; - if (touchesLine(**I)) - return true; + struct CompareLineStatePointers { + bool operator()(LineState *obj1, LineState *obj2) const { + return *obj1 < *obj2; } - return false; - } - - bool touchesEmptyLineBefore(const AnnotatedLine &TheLine) { - const FormatToken *First = TheLine.First; - CharSourceRange LineRange = CharSourceRange::getCharRange( - First->WhitespaceRange.getBegin(), - First->WhitespaceRange.getBegin().getLocWithOffset( - First->LastNewlineOffset)); - return touchesRanges(LineRange); - } + }; /// \brief Analyze the entire solution space starting from \p InitialState. /// @@ -819,7 +1071,7 @@ private: /// /// If \p DryRun is \c false, directly applies the changes. unsigned analyzeSolutionSpace(LineState &InitialState, bool DryRun = false) { - std::set<LineState> Seen; + std::set<LineState *, CompareLineStatePointers> Seen; // Increasing count of \c StateNode items we have created. This is used to // create a deterministic order independent of the container. @@ -828,7 +1080,7 @@ private: // Insert start element into queue. StateNode *Node = - new (Allocator.Allocate()) StateNode(InitialState, false, NULL); + new (Allocator.Allocate()) StateNode(InitialState, false, nullptr); Queue.push(QueueItem(OrderedPenalty(0, Count), Node)); ++Count; @@ -838,7 +1090,7 @@ private: while (!Queue.empty()) { Penalty = Queue.top().first.first; StateNode *Node = Queue.top().second; - if (Node->State.NextToken == NULL) { + if (!Node->State.NextToken) { DEBUG(llvm::dbgs() << "\n---\nPenalty for line: " << Penalty << "\n"); break; } @@ -849,7 +1101,7 @@ private: if (Count > 10000) Node->State.IgnoreStackForComparison = true; - if (!Seen.insert(Node->State).second) + if (!Seen.insert(&Node->State).second) // State already examined with lower penalty. continue; @@ -953,20 +1205,38 @@ private: return true; if (NewLine) { - int AdditionalIndent = State.Stack.back().Indent - - Previous.Children[0]->Level * Style.IndentWidth; - Penalty += format(Previous.Children, DryRun, AdditionalIndent); + int AdditionalIndent = + State.FirstIndent - State.Line->Level * Style.IndentWidth; + if (State.Stack.size() < 2 || + !State.Stack[State.Stack.size() - 2].JSFunctionInlined) { + AdditionalIndent = State.Stack.back().Indent - + Previous.Children[0]->Level * Style.IndentWidth; + } + + Penalty += format(Previous.Children, DryRun, AdditionalIndent, + /*FixBadIndentation=*/true); return true; } // Cannot merge multiple statements into a single line. if (Previous.Children.size() > 1) - return false; + return false; + + // Cannot merge into one line if this line ends on a comment. + if (Previous.is(tok::comment)) + return false; // We can't put the closing "}" on a line with a trailing comment. if (Previous.Children[0]->Last->isTrailingComment()) return false; + // If the child line exceeds the column limit, we wouldn't want to merge it. + // We add +2 for the trailing " }". + if (Style.ColumnLimit > 0 && + Previous.Children[0]->Last->TotalLength + State.Column + 2 > + Style.ColumnLimit) + return false; + if (!DryRun) { Whitespaces->replaceWhitespace( *Previous.Children[0]->First, @@ -979,31 +1249,43 @@ private: return true; } - SourceManager &SourceMgr; - SmallVectorImpl<CharSourceRange> &Ranges; ContinuationIndenter *Indenter; WhitespaceManager *Whitespaces; FormatStyle Style; LineJoiner Joiner; llvm::SpecificBumpPtrAllocator<StateNode> Allocator; + + // Cache to store the penalty of formatting a vector of AnnotatedLines + // starting from a specific additional offset. Improves performance if there + // are many nested blocks. + std::map<std::pair<const SmallVectorImpl<AnnotatedLine *> *, unsigned>, + unsigned> PenaltyCache; }; class FormatTokenLexer { public: FormatTokenLexer(Lexer &Lex, SourceManager &SourceMgr, FormatStyle &Style, encoding::Encoding Encoding) - : FormatTok(NULL), IsFirstToken(true), GreaterStashed(false), Column(0), - TrailingWhitespace(0), Lex(Lex), SourceMgr(SourceMgr), Style(Style), - IdentTable(getFormattingLangOpts()), Encoding(Encoding) { + : FormatTok(nullptr), IsFirstToken(true), GreaterStashed(false), + Column(0), TrailingWhitespace(0), Lex(Lex), SourceMgr(SourceMgr), + Style(Style), IdentTable(getFormattingLangOpts()), Encoding(Encoding), + FirstInLineIndex(0) { Lex.SetKeepWhitespaceMode(true); + + for (const std::string &ForEachMacro : Style.ForEachMacros) + ForEachMacros.push_back(&IdentTable.get(ForEachMacro)); + std::sort(ForEachMacros.begin(), ForEachMacros.end()); } ArrayRef<FormatToken *> lex() { assert(Tokens.empty()); + assert(FirstInLineIndex == 0); do { Tokens.push_back(getNextToken()); - maybeJoinPreviousTokens(); + tryMergePreviousTokens(); + if (Tokens.back()->NewlinesBefore > 0) + FirstInLineIndex = Tokens.size() - 1; } while (Tokens.back()->Tok.isNot(tok::eof)); return Tokens; } @@ -1011,23 +1293,125 @@ public: IdentifierTable &getIdentTable() { return IdentTable; } private: - void maybeJoinPreviousTokens() { - if (Tokens.size() < 4) + void tryMergePreviousTokens() { + if (tryMerge_TMacro()) return; + if (tryMergeConflictMarkers()) + return; + + if (Style.Language == FormatStyle::LK_JavaScript) { + if (tryMergeEscapeSequence()) + return; + if (tryMergeJSRegexLiteral()) + return; + + static tok::TokenKind JSIdentity[] = { tok::equalequal, tok::equal }; + static tok::TokenKind JSNotIdentity[] = { tok::exclaimequal, tok::equal }; + static tok::TokenKind JSShiftEqual[] = { tok::greater, tok::greater, + tok::greaterequal }; + static tok::TokenKind JSRightArrow[] = { tok::equal, tok::greater }; + // FIXME: We probably need to change token type to mimic operator with the + // correct priority. + if (tryMergeTokens(JSIdentity)) + return; + if (tryMergeTokens(JSNotIdentity)) + return; + if (tryMergeTokens(JSShiftEqual)) + return; + if (tryMergeTokens(JSRightArrow)) + return; + } + } + + bool tryMergeTokens(ArrayRef<tok::TokenKind> Kinds) { + if (Tokens.size() < Kinds.size()) + return false; + + SmallVectorImpl<FormatToken *>::const_iterator First = + Tokens.end() - Kinds.size(); + if (!First[0]->is(Kinds[0])) + return false; + unsigned AddLength = 0; + for (unsigned i = 1; i < Kinds.size(); ++i) { + if (!First[i]->is(Kinds[i]) || First[i]->WhitespaceRange.getBegin() != + First[i]->WhitespaceRange.getEnd()) + return false; + AddLength += First[i]->TokenText.size(); + } + Tokens.resize(Tokens.size() - Kinds.size() + 1); + First[0]->TokenText = StringRef(First[0]->TokenText.data(), + First[0]->TokenText.size() + AddLength); + First[0]->ColumnWidth += AddLength; + return true; + } + + // Tries to merge an escape sequence, i.e. a "\\" and the following + // character. Use e.g. inside JavaScript regex literals. + bool tryMergeEscapeSequence() { + if (Tokens.size() < 2) + return false; + FormatToken *Previous = Tokens[Tokens.size() - 2]; + if (Previous->isNot(tok::unknown) || Previous->TokenText != "\\" || + Tokens.back()->NewlinesBefore != 0) + return false; + Previous->ColumnWidth += Tokens.back()->ColumnWidth; + StringRef Text = Previous->TokenText; + Previous->TokenText = + StringRef(Text.data(), Text.size() + Tokens.back()->TokenText.size()); + Tokens.resize(Tokens.size() - 1); + return true; + } + + // Try to determine whether the current token ends a JavaScript regex literal. + // We heuristically assume that this is a regex literal if we find two + // unescaped slashes on a line and the token before the first slash is one of + // "(;,{}![:?", a binary operator or 'return', as those cannot be followed by + // a division. + bool tryMergeJSRegexLiteral() { + if (Tokens.size() < 2 || Tokens.back()->isNot(tok::slash) || + (Tokens[Tokens.size() - 2]->is(tok::unknown) && + Tokens[Tokens.size() - 2]->TokenText == "\\")) + return false; + unsigned TokenCount = 0; + unsigned LastColumn = Tokens.back()->OriginalColumn; + for (auto I = Tokens.rbegin() + 1, E = Tokens.rend(); I != E; ++I) { + ++TokenCount; + if (I[0]->is(tok::slash) && I + 1 != E && + (I[1]->isOneOf(tok::l_paren, tok::semi, tok::l_brace, tok::r_brace, + tok::exclaim, tok::l_square, tok::colon, tok::comma, + tok::question, tok::kw_return) || + I[1]->isBinaryOperator())) { + Tokens.resize(Tokens.size() - TokenCount); + Tokens.back()->Tok.setKind(tok::unknown); + Tokens.back()->Type = TT_RegexLiteral; + Tokens.back()->ColumnWidth += LastColumn - I[0]->OriginalColumn; + return true; + } + + // There can't be a newline inside a regex literal. + if (I[0]->NewlinesBefore > 0) + return false; + } + return false; + } + + bool tryMerge_TMacro() { + if (Tokens.size() < 4) + return false; FormatToken *Last = Tokens.back(); if (!Last->is(tok::r_paren)) - return; + return false; FormatToken *String = Tokens[Tokens.size() - 2]; if (!String->is(tok::string_literal) || String->IsMultiline) - return; + return false; if (!Tokens[Tokens.size() - 3]->is(tok::l_paren)) - return; + return false; FormatToken *Macro = Tokens[Tokens.size() - 4]; if (Macro->TokenText != "_T") - return; + return false; const char *Start = Macro->TokenText.data(); const char *End = Last->TokenText.data() + Last->TokenText.size(); @@ -1043,6 +1427,69 @@ private: Tokens.pop_back(); Tokens.pop_back(); Tokens.back() = String; + return true; + } + + bool tryMergeConflictMarkers() { + if (Tokens.back()->NewlinesBefore == 0 && Tokens.back()->isNot(tok::eof)) + return false; + + // Conflict lines look like: + // <marker> <text from the vcs> + // For example: + // >>>>>>> /file/in/file/system at revision 1234 + // + // We merge all tokens in a line that starts with a conflict marker + // into a single token with a special token type that the unwrapped line + // parser will use to correctly rebuild the underlying code. + + FileID ID; + // Get the position of the first token in the line. + unsigned FirstInLineOffset; + std::tie(ID, FirstInLineOffset) = SourceMgr.getDecomposedLoc( + Tokens[FirstInLineIndex]->getStartOfNonWhitespace()); + StringRef Buffer = SourceMgr.getBuffer(ID)->getBuffer(); + // Calculate the offset of the start of the current line. + auto LineOffset = Buffer.rfind('\n', FirstInLineOffset); + if (LineOffset == StringRef::npos) { + LineOffset = 0; + } else { + ++LineOffset; + } + + auto FirstSpace = Buffer.find_first_of(" \n", LineOffset); + StringRef LineStart; + if (FirstSpace == StringRef::npos) { + LineStart = Buffer.substr(LineOffset); + } else { + LineStart = Buffer.substr(LineOffset, FirstSpace - LineOffset); + } + + TokenType Type = TT_Unknown; + if (LineStart == "<<<<<<<" || LineStart == ">>>>") { + Type = TT_ConflictStart; + } else if (LineStart == "|||||||" || LineStart == "=======" || + LineStart == "====") { + Type = TT_ConflictAlternative; + } else if (LineStart == ">>>>>>>" || LineStart == "<<<<") { + Type = TT_ConflictEnd; + } + + if (Type != TT_Unknown) { + FormatToken *Next = Tokens.back(); + + Tokens.resize(FirstInLineIndex + 1); + // We do not need to build a complete token here, as we will skip it + // during parsing anyway (as we must not touch whitespace around conflict + // markers). + Tokens.back()->Type = Type; + Tokens.back()->Tok.setKind(tok::kw___unknown_anytype); + + Tokens.push_back(Next); + return true; + } + + return false; } FormatToken *getNextToken() { @@ -1122,7 +1569,7 @@ private: // FIXME: Add a more explicit test. while (FormatTok->TokenText.size() > 1 && FormatTok->TokenText[0] == '\\' && FormatTok->TokenText[1] == '\n') { - // FIXME: ++FormatTok->NewlinesBefore is missing... + ++FormatTok->NewlinesBefore; WhitespaceLength += 2; Column = 0; FormatTok->TokenText = FormatTok->TokenText.substr(2); @@ -1174,6 +1621,10 @@ private: Column = FormatTok->LastLineColumnWidth; } + FormatTok->IsForEachMacro = + std::binary_search(ForEachMacros.begin(), ForEachMacros.end(), + FormatTok->Tok.getIdentifierInfo()); + return FormatTok; } @@ -1188,7 +1639,10 @@ private: IdentifierTable IdentTable; encoding::Encoding Encoding; llvm::SpecificBumpPtrAllocator<FormatToken> Allocator; + // Index (in 'Tokens') of the last token that starts a new line. + unsigned FirstInLineIndex; SmallVector<FormatToken *, 16> Tokens; + SmallVector<IdentifierInfo *, 8> ForEachMacros; void readRawToken(FormatToken &Tok) { Lex.LexFromRawLexer(Tok.Tok); @@ -1196,14 +1650,31 @@ private: Tok.Tok.getLength()); // For formatting, treat unterminated string literals like normal string // literals. - if (Tok.is(tok::unknown) && !Tok.TokenText.empty() && - Tok.TokenText[0] == '"') { - Tok.Tok.setKind(tok::string_literal); - Tok.IsUnterminatedLiteral = true; + if (Tok.is(tok::unknown)) { + if (!Tok.TokenText.empty() && Tok.TokenText[0] == '"') { + Tok.Tok.setKind(tok::string_literal); + Tok.IsUnterminatedLiteral = true; + } else if (Style.Language == FormatStyle::LK_JavaScript && + Tok.TokenText == "''") { + Tok.Tok.setKind(tok::char_constant); + } } } }; +static StringRef getLanguageName(FormatStyle::LanguageKind Language) { + switch (Language) { + case FormatStyle::LK_Cpp: + return "C++"; + case FormatStyle::LK_JavaScript: + return "JavaScript"; + case FormatStyle::LK_Proto: + return "Proto"; + default: + return "Unknown"; + } +} + class Formatter : public UnwrappedLineConsumer { public: Formatter(const FormatStyle &Style, Lexer &Lex, SourceManager &SourceMgr, @@ -1216,6 +1687,8 @@ public: << (Encoding == encoding::Encoding_UTF8 ? "UTF8" : "unknown") << "\n"); + DEBUG(llvm::dbgs() << "Language: " << getLanguageName(Style.Language) + << "\n"); } tooling::Replacements format() { @@ -1261,17 +1734,151 @@ public: for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) { Annotator.calculateFormattingInformation(*AnnotatedLines[i]); } + computeAffectedLines(AnnotatedLines.begin(), AnnotatedLines.end()); Annotator.setCommentLineLevels(AnnotatedLines); ContinuationIndenter Indenter(Style, SourceMgr, Whitespaces, Encoding, BinPackInconclusiveFunctions); - UnwrappedLineFormatter Formatter(SourceMgr, Ranges, &Indenter, &Whitespaces, - Style); + UnwrappedLineFormatter Formatter(&Indenter, &Whitespaces, Style); Formatter.format(AnnotatedLines, /*DryRun=*/false); return Whitespaces.generateReplacements(); } private: + // Determines which lines are affected by the SourceRanges given as input. + // Returns \c true if at least one line between I and E or one of their + // children is affected. + bool computeAffectedLines(SmallVectorImpl<AnnotatedLine *>::iterator I, + SmallVectorImpl<AnnotatedLine *>::iterator E) { + bool SomeLineAffected = false; + const AnnotatedLine *PreviousLine = nullptr; + while (I != E) { + AnnotatedLine *Line = *I; + Line->LeadingEmptyLinesAffected = affectsLeadingEmptyLines(*Line->First); + + // If a line is part of a preprocessor directive, it needs to be formatted + // if any token within the directive is affected. + if (Line->InPPDirective) { + FormatToken *Last = Line->Last; + SmallVectorImpl<AnnotatedLine *>::iterator PPEnd = I + 1; + while (PPEnd != E && !(*PPEnd)->First->HasUnescapedNewline) { + Last = (*PPEnd)->Last; + ++PPEnd; + } + + if (affectsTokenRange(*Line->First, *Last, + /*IncludeLeadingNewlines=*/false)) { + SomeLineAffected = true; + markAllAsAffected(I, PPEnd); + } + I = PPEnd; + continue; + } + + if (nonPPLineAffected(Line, PreviousLine)) + SomeLineAffected = true; + + PreviousLine = Line; + ++I; + } + return SomeLineAffected; + } + + // Determines whether 'Line' is affected by the SourceRanges given as input. + // Returns \c true if line or one if its children is affected. + bool nonPPLineAffected(AnnotatedLine *Line, + const AnnotatedLine *PreviousLine) { + bool SomeLineAffected = false; + Line->ChildrenAffected = + computeAffectedLines(Line->Children.begin(), Line->Children.end()); + if (Line->ChildrenAffected) + SomeLineAffected = true; + + // Stores whether one of the line's tokens is directly affected. + bool SomeTokenAffected = false; + // Stores whether we need to look at the leading newlines of the next token + // in order to determine whether it was affected. + bool IncludeLeadingNewlines = false; + + // Stores whether the first child line of any of this line's tokens is + // affected. + bool SomeFirstChildAffected = false; + + for (FormatToken *Tok = Line->First; Tok; Tok = Tok->Next) { + // Determine whether 'Tok' was affected. + if (affectsTokenRange(*Tok, *Tok, IncludeLeadingNewlines)) + SomeTokenAffected = true; + + // Determine whether the first child of 'Tok' was affected. + if (!Tok->Children.empty() && Tok->Children.front()->Affected) + SomeFirstChildAffected = true; + + IncludeLeadingNewlines = Tok->Children.empty(); + } + + // Was this line moved, i.e. has it previously been on the same line as an + // affected line? + bool LineMoved = PreviousLine && PreviousLine->Affected && + Line->First->NewlinesBefore == 0; + + bool IsContinuedComment = + Line->First->is(tok::comment) && Line->First->Next == nullptr && + Line->First->NewlinesBefore < 2 && PreviousLine && + PreviousLine->Affected && PreviousLine->Last->is(tok::comment); + + if (SomeTokenAffected || SomeFirstChildAffected || LineMoved || + IsContinuedComment) { + Line->Affected = true; + SomeLineAffected = true; + } + return SomeLineAffected; + } + + // Marks all lines between I and E as well as all their children as affected. + void markAllAsAffected(SmallVectorImpl<AnnotatedLine *>::iterator I, + SmallVectorImpl<AnnotatedLine *>::iterator E) { + while (I != E) { + (*I)->Affected = true; + markAllAsAffected((*I)->Children.begin(), (*I)->Children.end()); + ++I; + } + } + + // Returns true if the range from 'First' to 'Last' intersects with one of the + // input ranges. + bool affectsTokenRange(const FormatToken &First, const FormatToken &Last, + bool IncludeLeadingNewlines) { + SourceLocation Start = First.WhitespaceRange.getBegin(); + if (!IncludeLeadingNewlines) + Start = Start.getLocWithOffset(First.LastNewlineOffset); + SourceLocation End = Last.getStartOfNonWhitespace(); + if (Last.TokenText.size() > 0) + End = End.getLocWithOffset(Last.TokenText.size() - 1); + CharSourceRange Range = CharSourceRange::getCharRange(Start, End); + return affectsCharSourceRange(Range); + } + + // Returns true if one of the input ranges intersect the leading empty lines + // before 'Tok'. + bool affectsLeadingEmptyLines(const FormatToken &Tok) { + CharSourceRange EmptyLineRange = CharSourceRange::getCharRange( + Tok.WhitespaceRange.getBegin(), + Tok.WhitespaceRange.getBegin().getLocWithOffset(Tok.LastNewlineOffset)); + return affectsCharSourceRange(EmptyLineRange); + } + + // Returns true if 'Range' intersects with one of the input ranges. + bool affectsCharSourceRange(const CharSourceRange &Range) { + for (SmallVectorImpl<CharSourceRange>::const_iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) { + if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), I->getBegin()) && + !SourceMgr.isBeforeInTranslationUnit(I->getEnd(), Range.getBegin())) + return true; + } + return false; + } + static bool inputUsesCRLF(StringRef Text) { return Text.count('\r') * 2 > Text.count('\n'); } @@ -1316,11 +1923,11 @@ private: Tok = Tok->Next; } } - if (Style.DerivePointerBinding) { + if (Style.DerivePointerAlignment) { if (CountBoundToType > CountBoundToVariable) - Style.PointerBindsToType = true; + Style.PointerAlignment = FormatStyle::PAS_Left; else if (CountBoundToType < CountBoundToVariable) - Style.PointerBindsToType = false; + Style.PointerAlignment = FormatStyle::PAS_Right; } if (Style.Standard == FormatStyle::LS_Auto) { Style.Standard = HasCpp03IncompatibleFormat ? FormatStyle::LS_Cpp11 @@ -1330,12 +1937,12 @@ private: HasBinPackedFunction || !HasOnePerLineFunction; } - virtual void consumeUnwrappedLine(const UnwrappedLine &TheLine) { + void consumeUnwrappedLine(const UnwrappedLine &TheLine) override { assert(!UnwrappedLines.empty()); UnwrappedLines.back().push_back(TheLine); } - virtual void finishRun() { + void finishRun() override { UnwrappedLines.push_back(SmallVector<UnwrappedLine, 16>()); } @@ -1355,6 +1962,11 @@ private: tooling::Replacements reformat(const FormatStyle &Style, Lexer &Lex, SourceManager &SourceMgr, std::vector<CharSourceRange> Ranges) { + if (Style.DisableFormat) { + tooling::Replacements EmptyResult; + return EmptyResult; + } + Formatter formatter(Style, Lex, SourceMgr, Ranges); return formatter.format(); } @@ -1389,7 +2001,9 @@ LangOptions getFormattingLangOpts(FormatStyle::LanguageStandard Standard) { LangOptions LangOpts; LangOpts.CPlusPlus = 1; LangOpts.CPlusPlus11 = Standard == FormatStyle::LS_Cpp03 ? 0 : 1; + LangOpts.CPlusPlus1y = Standard == FormatStyle::LS_Cpp03 ? 0 : 1; LangOpts.LineComment = 1; + LangOpts.CXXOperatorNames = 1; LangOpts.Bool = 1; LangOpts.ObjC1 = 1; LangOpts.ObjC2 = 1; @@ -1407,15 +2021,29 @@ const char *StyleOptionHelpDescription = "parameters, e.g.:\n" " -style=\"{BasedOnStyle: llvm, IndentWidth: 8}\""; -FormatStyle getStyle(StringRef StyleName, StringRef FileName) { - // Fallback style in case the rest of this function can't determine a style. - StringRef FallbackStyle = "LLVM"; - FormatStyle Style; - getPredefinedStyle(FallbackStyle, &Style); +static FormatStyle::LanguageKind getLanguageByFileName(StringRef FileName) { + if (FileName.endswith_lower(".js")) { + return FormatStyle::LK_JavaScript; + } else if (FileName.endswith_lower(".proto") || + FileName.endswith_lower(".protodevel")) { + return FormatStyle::LK_Proto; + } + return FormatStyle::LK_Cpp; +} + +FormatStyle getStyle(StringRef StyleName, StringRef FileName, + StringRef FallbackStyle) { + FormatStyle Style = getLLVMStyle(); + Style.Language = getLanguageByFileName(FileName); + if (!getPredefinedStyle(FallbackStyle, Style.Language, &Style)) { + llvm::errs() << "Invalid fallback style \"" << FallbackStyle + << "\" using LLVM style\n"; + return Style; + } if (StyleName.startswith("{")) { // Parse YAML/JSON style from the command line. - if (llvm::error_code ec = parseConfiguration(StyleName, &Style)) { + if (std::error_code ec = parseConfiguration(StyleName, &Style)) { llvm::errs() << "Error parsing -style: " << ec.message() << ", using " << FallbackStyle << " style\n"; } @@ -1423,12 +2051,14 @@ FormatStyle getStyle(StringRef StyleName, StringRef FileName) { } if (!StyleName.equals_lower("file")) { - if (!getPredefinedStyle(StyleName, &Style)) + if (!getPredefinedStyle(StyleName, Style.Language, &Style)) llvm::errs() << "Invalid value for -style, using " << FallbackStyle << " style\n"; return Style; } + // Look for .clang-format/_clang-format file in the file's parent directories. + SmallString<128> UnsuitableConfigFiles; SmallString<128> Path(FileName); llvm::sys::fs::make_absolute(Path); for (StringRef Directory = Path; !Directory.empty(); @@ -1453,16 +2083,23 @@ FormatStyle getStyle(StringRef StyleName, StringRef FileName) { } if (IsFile) { - OwningPtr<llvm::MemoryBuffer> Text; - if (llvm::error_code ec = - llvm::MemoryBuffer::getFile(ConfigFile.c_str(), Text)) { - llvm::errs() << ec.message() << "\n"; - continue; + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text = + llvm::MemoryBuffer::getFile(ConfigFile.c_str()); + if (std::error_code EC = Text.getError()) { + llvm::errs() << EC.message() << "\n"; + break; } - if (llvm::error_code ec = parseConfiguration(Text->getBuffer(), &Style)) { + if (std::error_code ec = + parseConfiguration(Text.get()->getBuffer(), &Style)) { + if (ec == ParseError::Unsuitable) { + if (!UnsuitableConfigFiles.empty()) + UnsuitableConfigFiles.append(", "); + UnsuitableConfigFiles.append(ConfigFile); + continue; + } llvm::errs() << "Error reading " << ConfigFile << ": " << ec.message() << "\n"; - continue; + break; } DEBUG(llvm::dbgs() << "Using configuration file " << ConfigFile << "\n"); return Style; @@ -1470,6 +2107,11 @@ FormatStyle getStyle(StringRef StyleName, StringRef FileName) { } llvm::errs() << "Can't find usable .clang-format, using " << FallbackStyle << " style\n"; + if (!UnsuitableConfigFiles.empty()) { + llvm::errs() << "Configuration file(s) do(es) not support " + << getLanguageName(Style.Language) << ": " + << UnsuitableConfigFiles << "\n"; + } return Style; } |