Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -449,38 +449,38 @@ /// Different ways to break after the template declaration. enum BreakTemplateDeclarationsStyle { - /// Do not force break before declaration. - /// ``PenaltyBreakTemplateDeclaration`` is taken into account. - /// \code - /// template T foo() { - /// } - /// template T foo(int aaaaaaaaaaaaaaaaaaaaa, - /// int bbbbbbbbbbbbbbbbbbbbb) { - /// } - /// \endcode - BTDS_No, - /// Force break after template declaration only when the following - /// declaration spans multiple lines. - /// \code - /// template T foo() { - /// } - /// template - /// T foo(int aaaaaaaaaaaaaaaaaaaaa, - /// int bbbbbbbbbbbbbbbbbbbbb) { - /// } - /// \endcode - BTDS_MultiLine, - /// Always break after template declaration. - /// \code - /// template - /// T foo() { - /// } - /// template - /// T foo(int aaaaaaaaaaaaaaaaaaaaa, - /// int bbbbbbbbbbbbbbbbbbbbb) { - /// } - /// \endcode - BTDS_Yes + /// Do not force break before declaration. + /// ``PenaltyBreakTemplateDeclaration`` is taken into account. + /// \code + /// template T foo() { + /// } + /// template T foo(int aaaaaaaaaaaaaaaaaaaaa, + /// int bbbbbbbbbbbbbbbbbbbbb) { + /// } + /// \endcode + BTDS_No, + /// Force break after template declaration only when the following + /// declaration spans multiple lines. + /// \code + /// template T foo() { + /// } + /// template + /// T foo(int aaaaaaaaaaaaaaaaaaaaa, + /// int bbbbbbbbbbbbbbbbbbbbb) { + /// } + /// \endcode + BTDS_MultiLine, + /// Always break after template declaration. + /// \code + /// template + /// T foo() { + /// } + /// template + /// T foo(int aaaaaaaaaaaaaaaaaaaaa, + /// int bbbbbbbbbbbbbbbbbbbbb) { + /// } + /// \endcode + BTDS_Yes }; /// The template declaration breaking style to use. @@ -2089,6 +2089,31 @@ unsigned Line = 0; }; +enum class ExtraFormattingOptions { + None = 0x0, + KeepLineBreaksForNonEmptyLines = 0x1 +}; + +inline ExtraFormattingOptions operator|(ExtraFormattingOptions lhs, + ExtraFormattingOptions rhs) { + using T = std::underlying_type_t; + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +inline ExtraFormattingOptions operator&(ExtraFormattingOptions lhs, + ExtraFormattingOptions rhs) { + using T = std::underlying_type_t; + return static_cast(static_cast(lhs) & + static_cast(rhs)); +} + +inline bool alwaysKeepLineBreaks(ExtraFormattingOptions FormattingOptions) { + return (FormattingOptions & + ExtraFormattingOptions::KeepLineBreaksForNonEmptyLines) != + ExtraFormattingOptions::None; +} + /// Reformats the given \p Ranges in \p Code. /// /// Each range is extended on either end to its next bigger logic unit, i.e. @@ -2110,8 +2135,13 @@ /// a non-recoverable syntax error. tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, - StringRef FileName, - bool *IncompleteFormat); + StringRef FileName, bool *IncompleteFormat); + +tooling::Replacements reformat(const FormatStyle &Style, + ExtraFormattingOptions FormattingOptions, + StringRef Code, ArrayRef Ranges, + StringRef FileName = "", + FormattingAttemptStatus *Status = nullptr); /// Clean up any erroneous/redundant code in the given \p Ranges in \p /// Code. @@ -2222,6 +2252,6 @@ namespace std { template <> struct is_error_code_enum : std::true_type {}; -} +} // namespace std #endif // LLVM_CLANG_FORMAT_FORMAT_H Index: clang/lib/Format/ContinuationIndenter.cpp =================================================================== --- clang/lib/Format/ContinuationIndenter.cpp +++ clang/lib/Format/ContinuationIndenter.cpp @@ -1573,6 +1573,7 @@ std::pair Fixes = internal::reformat( RawStringStyle, RawText, {tooling::Range(0, RawText.size())}, FirstStartColumn, NextStartColumn, LastStartColumn, "", + ExtraFormattingOptions::None, /*Status=*/nullptr); auto NewCode = applyAllReplacements(RawText, Fixes.first); Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -605,8 +605,8 @@ return Style; FormatStyle Expanded = Style; Expanded.BraceWrapping = {false, false, false, false, false, false, - false, false, false, false, false, - false, false, true, true, true}; + false, false, false, false, false, false, + false, true, true, true}; switch (Style.BreakBeforeBraces) { case FormatStyle::BS_Linux: Expanded.BraceWrapping.AfterClass = true; @@ -683,8 +683,8 @@ LLVMStyle.BreakBeforeTernaryOperators = true; LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach; LLVMStyle.BraceWrapping = {false, false, false, false, false, false, - false, false, false, false, false, - false, false, true, true, true}; + false, false, false, false, false, false, + false, true, true, true}; LLVMStyle.BreakAfterJavaFieldAnnotations = false; LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon; LLVMStyle.BreakInheritanceList = FormatStyle::BILS_BeforeColon; @@ -1239,8 +1239,9 @@ class Formatter : public TokenAnalyzer { public: Formatter(const Environment &Env, const FormatStyle &Style, + ExtraFormattingOptions FormattingOptions, FormattingAttemptStatus *Status) - : TokenAnalyzer(Env, Style), Status(Status) {} + : TokenAnalyzer(Env, Style, FormattingOptions), Status(Status) {} std::pair analyze(TokenAnnotator &Annotator, @@ -1263,7 +1264,7 @@ unsigned Penalty = UnwrappedLineFormatter(&Indenter, &Whitespaces, Style, Tokens.getKeywords(), Env.getSourceManager(), - Status) + FormattingOptions, Status) .format(AnnotatedLines, /*DryRun=*/false, /*AdditionalIndent=*/0, /*FixBadIndentation=*/false, @@ -1769,8 +1770,8 @@ static void sortCppIncludes(const FormatStyle &Style, const SmallVectorImpl &Includes, ArrayRef Ranges, StringRef FileName, - StringRef Code, - tooling::Replacements &Replaces, unsigned *Cursor) { + StringRef Code, tooling::Replacements &Replaces, + unsigned *Cursor) { unsigned IncludesBeginOffset = Includes.front().Offset; unsigned IncludesEndOffset = Includes.back().Offset + Includes.back().Text.size(); @@ -2247,6 +2248,7 @@ reformat(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, unsigned FirstStartColumn, unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName, + ExtraFormattingOptions FormattingOptions, FormattingAttemptStatus *Status) { FormatStyle Expanded = expandPresets(Style); if (Expanded.DisableFormat) @@ -2280,7 +2282,7 @@ }); Passes.emplace_back([&](const Environment &Env) { - return Formatter(Env, Expanded, Status).process(); + return Formatter(Env, Expanded, FormattingOptions, Status).process(); }); auto Env = @@ -2317,7 +2319,21 @@ return internal::reformat(Style, Code, Ranges, /*FirstStartColumn=*/0, /*NextStartColumn=*/0, - /*LastStartColumn=*/0, FileName, Status) + /*LastStartColumn=*/0, FileName, + ExtraFormattingOptions::None, Status) + .first; +} + +tooling::Replacements reformat(const FormatStyle &Style, + ExtraFormattingOptions FormattingOptions, + StringRef Code, ArrayRef Ranges, + StringRef FileName, + FormattingAttemptStatus *Status) { + return internal::reformat(Style, Code, Ranges, + /*FirstStartColumn=*/0, + /*NextStartColumn=*/0, + /*LastStartColumn=*/0, FileName, FormattingOptions, + Status) .first; } Index: clang/lib/Format/FormatInternal.h =================================================================== --- clang/lib/Format/FormatInternal.h +++ clang/lib/Format/FormatInternal.h @@ -73,6 +73,7 @@ reformat(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, unsigned FirstStartColumn, unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName, + ExtraFormattingOptions FormattingOptions, FormattingAttemptStatus *Status); } // namespace internal Index: clang/lib/Format/TokenAnalyzer.h =================================================================== --- clang/lib/Format/TokenAnalyzer.h +++ clang/lib/Format/TokenAnalyzer.h @@ -79,7 +79,9 @@ class TokenAnalyzer : public UnwrappedLineConsumer { public: - TokenAnalyzer(const Environment &Env, const FormatStyle &Style); + TokenAnalyzer( + const Environment &Env, const FormatStyle &Style, + ExtraFormattingOptions FormattingOptions = ExtraFormattingOptions::None); std::pair process(); @@ -100,6 +102,7 @@ AffectedRangeManager AffectedRangeMgr; SmallVector, 2> UnwrappedLines; encoding::Encoding Encoding; + ExtraFormattingOptions FormattingOptions; }; } // end namespace format Index: clang/lib/Format/TokenAnalyzer.cpp =================================================================== --- clang/lib/Format/TokenAnalyzer.cpp +++ clang/lib/Format/TokenAnalyzer.cpp @@ -48,12 +48,14 @@ } } -TokenAnalyzer::TokenAnalyzer(const Environment &Env, const FormatStyle &Style) +TokenAnalyzer::TokenAnalyzer(const Environment &Env, const FormatStyle &Style, + ExtraFormattingOptions FormattingOptions) : Style(Style), Env(Env), AffectedRangeMgr(Env.getSourceManager(), Env.getCharRanges()), UnwrappedLines(1), Encoding(encoding::detectEncoding( - Env.getSourceManager().getBufferData(Env.getFileID()))) { + Env.getSourceManager().getBufferData(Env.getFileID()))), + FormattingOptions(FormattingOptions) { LLVM_DEBUG( llvm::dbgs() << "File encoding: " << (Encoding == encoding::Encoding_UTF8 ? "UTF8" : "unknown") @@ -68,7 +70,8 @@ Env.getFirstStartColumn(), Style, Encoding); UnwrappedLineParser Parser(Style, Tokens.getKeywords(), - Env.getFirstStartColumn(), Tokens.lex(), *this); + Env.getFirstStartColumn(), Tokens.lex(), *this, + FormattingOptions); Parser.parse(); assert(UnwrappedLines.rbegin()->empty()); unsigned Penalty = 0; Index: clang/lib/Format/UnwrappedLineFormatter.h =================================================================== --- clang/lib/Format/UnwrappedLineFormatter.h +++ clang/lib/Format/UnwrappedLineFormatter.h @@ -32,9 +32,11 @@ const FormatStyle &Style, const AdditionalKeywords &Keywords, const SourceManager &SourceMgr, + ExtraFormattingOptions FormattingOptions, FormattingAttemptStatus *Status) : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style), - Keywords(Keywords), SourceMgr(SourceMgr), Status(Status) {} + Keywords(Keywords), SourceMgr(SourceMgr), + FormattingOptions(FormattingOptions), Status(Status) {} /// Format the current block and return the penalty. unsigned format(const SmallVectorImpl &Lines, @@ -67,6 +69,7 @@ const FormatStyle &Style; const AdditionalKeywords &Keywords; const SourceManager &SourceMgr; + ExtraFormattingOptions FormattingOptions; FormattingAttemptStatus *Status; }; } // end namespace format Index: clang/lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- clang/lib/Format/UnwrappedLineFormatter.cpp +++ clang/lib/Format/UnwrappedLineFormatter.cpp @@ -682,11 +682,12 @@ /// Base class for classes that format one \c AnnotatedLine. class LineFormatter { public: - LineFormatter(ContinuationIndenter *Indenter, WhitespaceManager *Whitespaces, - const FormatStyle &Style, - UnwrappedLineFormatter *BlockFormatter) + LineFormatter( + ContinuationIndenter *Indenter, WhitespaceManager *Whitespaces, + const FormatStyle &Style, UnwrappedLineFormatter *BlockFormatter, + ExtraFormattingOptions FormattingOptions = ExtraFormattingOptions::None) : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style), - BlockFormatter(BlockFormatter) {} + BlockFormatter(BlockFormatter), FormattingOptions(FormattingOptions) {} virtual ~LineFormatter() {} /// Formats an \c AnnotatedLine and returns the penalty. @@ -726,7 +727,8 @@ // assert so that we can simply call this function for all tokens. return true; - if (NewLine) { + if (NewLine || (Previous.Children[0]->First->MustBreakBefore && + alwaysKeepLineBreaks(FormattingOptions))) { int AdditionalIndent = State.Stack.back().Indent - Previous.Children[0]->Level * Style.IndentWidth; @@ -776,6 +778,7 @@ WhitespaceManager *Whitespaces; const FormatStyle &Style; UnwrappedLineFormatter *BlockFormatter; + ExtraFormattingOptions FormattingOptions; }; /// Formatter that keeps the existing line breaks. @@ -784,8 +787,10 @@ NoColumnLimitLineFormatter(ContinuationIndenter *Indenter, WhitespaceManager *Whitespaces, const FormatStyle &Style, - UnwrappedLineFormatter *BlockFormatter) - : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter) {} + UnwrappedLineFormatter *BlockFormatter, + ExtraFormattingOptions FormattingOptions) + : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter, + FormattingOptions) {} /// Formats the line, simply keeping all of the input's line breaking /// decisions. @@ -821,7 +826,8 @@ LineState State = Indenter->getInitialState(FirstIndent, FirstStartColumn, &Line, DryRun); while (State.NextToken) { - formatChildren(State, /*Newline=*/false, DryRun, Penalty); + bool Newline = false; + formatChildren(State, Newline, DryRun, Penalty); Indenter->addTokenToState( State, /*Newline=*/State.NextToken->MustBreakBefore, DryRun); } @@ -1076,8 +1082,9 @@ !Style.JavaScriptWrapImports)) || (Style.isCSharp() && TheLine.InPPDirective); // don't split #regions in C# - if (Style.ColumnLimit == 0) - NoColumnLimitLineFormatter(Indenter, Whitespaces, Style, this) + if (Style.ColumnLimit == 0 || alwaysKeepLineBreaks(FormattingOptions)) + NoColumnLimitLineFormatter(Indenter, Whitespaces, Style, this, + FormattingOptions) .formatLine(TheLine, NextStartColumn + Indent, FirstLine ? FirstStartColumn : 0, DryRun); else if (FitsIntoOneLine) Index: clang/lib/Format/UnwrappedLineParser.h =================================================================== --- clang/lib/Format/UnwrappedLineParser.h +++ clang/lib/Format/UnwrappedLineParser.h @@ -77,7 +77,8 @@ UnwrappedLineParser(const FormatStyle &Style, const AdditionalKeywords &Keywords, unsigned FirstStartColumn, ArrayRef Tokens, - UnwrappedLineConsumer &Callback); + UnwrappedLineConsumer &Callback, + ExtraFormattingOptions FormattingOptions); void parse(); @@ -274,6 +275,8 @@ // does not start at the beginning of the file. unsigned FirstStartColumn; + ExtraFormattingOptions FormattingOptions; + friend class ScopedLineState; friend class CompoundStatementIndenter; }; Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -222,11 +222,10 @@ } // end anonymous namespace -UnwrappedLineParser::UnwrappedLineParser(const FormatStyle &Style, - const AdditionalKeywords &Keywords, - unsigned FirstStartColumn, - ArrayRef Tokens, - UnwrappedLineConsumer &Callback) +UnwrappedLineParser::UnwrappedLineParser( + const FormatStyle &Style, const AdditionalKeywords &Keywords, + unsigned FirstStartColumn, ArrayRef Tokens, + UnwrappedLineConsumer &Callback, ExtraFormattingOptions FormattingOptions) : Line(new UnwrappedLine), MustBreakBeforeNextToken(false), CurrentLines(&Lines), Style(Style), Keywords(Keywords), CommentPragmasRegex(Style.CommentPragmas), Tokens(nullptr), @@ -234,7 +233,8 @@ IncludeGuard(Style.IndentPPDirectives == FormatStyle::PPDIS_None ? IG_Rejected : IG_Inited), - IncludeGuardToken(nullptr), FirstStartColumn(FirstStartColumn) {} + IncludeGuardToken(nullptr), FirstStartColumn(FirstStartColumn), + FormattingOptions(FormattingOptions) {} void UnwrappedLineParser::reset() { PPBranchLevel = -1; @@ -2613,6 +2613,10 @@ else readTokenWithJavaScriptASI(); FormatTok->Previous = Previous; + + + if (FormatTok->NewlinesBefore && alwaysKeepLineBreaks(FormattingOptions)) + FormatTok->MustBreakBefore = true; } void UnwrappedLineParser::distributeComments( Index: clang/tools/clang-format/ClangFormat.cpp =================================================================== --- clang/tools/clang-format/ClangFormat.cpp +++ clang/tools/clang-format/ClangFormat.cpp @@ -51,13 +51,14 @@ "Can only be used with one input file."), cl::cat(ClangFormatCategory)); static cl::list -LineRanges("lines", cl::desc(": - format a range of\n" - "lines (both 1-based).\n" - "Multiple ranges can be formatted by specifying\n" - "several -lines arguments.\n" - "Can't be used with -offset and -length.\n" - "Can only be used with one input file."), - cl::cat(ClangFormatCategory)); + LineRanges("lines", + cl::desc(": - format a range of\n" + "lines (both 1-based).\n" + "Multiple ranges can be formatted by specifying\n" + "several -lines arguments.\n" + "Can't be used with -offset and -length.\n" + "Can only be used with one input file."), + cl::cat(ClangFormatCategory)); static cl::opt Style("style", cl::desc(clang::format::StyleOptionHelpDescription), cl::init(clang::format::DefaultFormatStyle), @@ -72,12 +73,12 @@ cl::init(clang::format::DefaultFallbackStyle), cl::cat(ClangFormatCategory)); -static cl::opt -AssumeFileName("assume-filename", - cl::desc("When reading from stdin, clang-format assumes this\n" - "filename to look for a style config file (with\n" - "-style=file) and to determine the language."), - cl::init(""), cl::cat(ClangFormatCategory)); +static cl::opt AssumeFileName( + "assume-filename", + cl::desc("When reading from stdin, clang-format assumes this\n" + "filename to look for a style config file (with\n" + "-style=file) and to determine the language."), + cl::init(""), cl::cat(ClangFormatCategory)); static cl::opt Inplace("i", cl::desc("Inplace edit s, if specified."), @@ -107,6 +108,11 @@ Verbose("verbose", cl::desc("If set, shows the list of processed files"), cl::cat(ClangFormatCategory)); +static cl::opt + KeepLineBreaks("keep-line-breaks", + cl::desc("If set, keeps all existing line breaks"), + cl::cat(ClangFormatCategory)); + static cl::list FileNames(cl::Positional, cl::desc("[ ...]"), cl::cat(ClangFormatCategory)); @@ -240,7 +246,9 @@ } // Returns true on error. -static bool format(StringRef FileName) { +static bool +format(StringRef FileName, + ExtraFormattingOptions ExtraOptions = ExtraFormattingOptions::None) { if (!OutputXML && Inplace && FileName == "-") { errs() << "error: cannot use -i when reading from stdin.\n"; return false; @@ -248,8 +256,8 @@ // On Windows, overwriting a file with an open file mapping doesn't work, // so read the whole file into memory when formatting in-place. ErrorOr> CodeOrErr = - !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName) : - MemoryBuffer::getFileOrSTDIN(FileName); + !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName) + : MemoryBuffer::getFileOrSTDIN(FileName); if (std::error_code EC = CodeOrErr.getError()) { errs() << EC.message() << "\n"; return true; @@ -263,20 +271,21 @@ // https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding // for more information. StringRef BufStr = Code->getBuffer(); - const char *InvalidBOM = llvm::StringSwitch(BufStr) - .StartsWith(llvm::StringLiteral::withInnerNUL("\x00\x00\xFE\xFF"), - "UTF-32 (BE)") - .StartsWith(llvm::StringLiteral::withInnerNUL("\xFF\xFE\x00\x00"), - "UTF-32 (LE)") - .StartsWith("\xFE\xFF", "UTF-16 (BE)") - .StartsWith("\xFF\xFE", "UTF-16 (LE)") - .StartsWith("\x2B\x2F\x76", "UTF-7") - .StartsWith("\xF7\x64\x4C", "UTF-1") - .StartsWith("\xDD\x73\x66\x73", "UTF-EBCDIC") - .StartsWith("\x0E\xFE\xFF", "SCSU") - .StartsWith("\xFB\xEE\x28", "BOCU-1") - .StartsWith("\x84\x31\x95\x33", "GB-18030") - .Default(nullptr); + const char *InvalidBOM = + llvm::StringSwitch(BufStr) + .StartsWith(llvm::StringLiteral::withInnerNUL("\x00\x00\xFE\xFF"), + "UTF-32 (BE)") + .StartsWith(llvm::StringLiteral::withInnerNUL("\xFF\xFE\x00\x00"), + "UTF-32 (LE)") + .StartsWith("\xFE\xFF", "UTF-16 (BE)") + .StartsWith("\xFF\xFE", "UTF-16 (LE)") + .StartsWith("\x2B\x2F\x76", "UTF-7") + .StartsWith("\xF7\x64\x4C", "UTF-1") + .StartsWith("\xDD\x73\x66\x73", "UTF-EBCDIC") + .StartsWith("\x0E\xFE\xFF", "SCSU") + .StartsWith("\xFB\xEE\x28", "BOCU-1") + .StartsWith("\x84\x31\x95\x33", "GB-18030") + .Default(nullptr); if (InvalidBOM) { errs() << "error: encoding with unsupported byte order mark \"" @@ -312,8 +321,9 @@ // Get new affected ranges after sorting `#includes`. Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); FormattingAttemptStatus Status; - Replacements FormatChanges = reformat(*FormatStyle, *ChangedCode, Ranges, - AssumedFileName, &Status); + Replacements FormatChanges = + reformat(*FormatStyle, ExtraOptions, *ChangedCode, Ranges, + AssumedFileName, &Status); Replaces = Replaces.merge(FormatChanges); if (OutputXML) { outs() << "\n Ranges(1, tooling::Range(0, Code.size())); FormattingAttemptStatus Status; tooling::Replacements Replaces = - reformat(Style, Code, Ranges, "", &Status); + reformat(Style, ExtraOptions, Code, Ranges, "", &Status); if (CheckComplete != SC_DoNotCheck) { bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete; EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete) @@ -395,6 +396,25 @@ Style)); } +TEST_F(FormatTest, KeepsLineBreaks) { + FormatStyle Style = getLLVMStyle(); + EXPECT_EQ("if (a\n" + " && b) {\n" + "}", + format("if (a\n" + " && b) {\n" + "}", + Style, SC_ExpectComplete, + ExtraFormattingOptions::KeepLineBreaksForNonEmptyLines)); + + EXPECT_EQ("[]() {\n" + " foo(); }", + format("[]() {\n" + "foo(); }", + Style, SC_ExpectComplete, + ExtraFormattingOptions::KeepLineBreaksForNonEmptyLines)); +} + TEST_F(FormatTest, RecognizesBinaryOperatorKeywords) { verifyFormat("x = (a) and (b);"); verifyFormat("x = (a) or (b);");