From 1b3fca1add151e64f2fe6fcaf48615941388af56 Mon Sep 17 00:00:00 2001 From: Robert burner Schadek Date: Tue, 17 Feb 2026 16:55:32 +0100 Subject: [PATCH] Add --force_curly_braces option to insert braces around single-statement bodies. When enabled, the preprocessor inserts synthetic `{` and `}` tokens around braceless bodies for all block header constructs: if, else, for, foreach, while, do, scope, synchronized, with, catch, debug, version, finally, and their combinations (static if, static foreach, etc.). Implementation uses token stream preprocessing so the formatter's existing brace-handling logic handles style and indentation naturally. Synthetic tokens use size_t.max-based indices to avoid collisions with AST info lookups. Handles compound constructs correctly: - try/catch/finally chains as single statements - Nested braceless blocks (each level gets its own braces) - Mixed braced/braceless siblings (only braceless branches get braces) - else if/else version/else static if chains (no double-wrapping) - debug/version with and without parens - Respects // dfmt off / // dfmt on regions --- src/dfmt/config.d | 3 + src/dfmt/formatter.d | 526 ++++++++++++++++++++++++++ src/dfmt/main.d | 7 +- tests/allman/force_curly_braces.d.ref | 292 ++++++++++++++ tests/force_curly_braces.args | 1 + tests/force_curly_braces.d | 186 +++++++++ tests/knr/force_curly_braces.d.ref | 237 ++++++++++++ tests/otbs/force_curly_braces.d.ref | 213 +++++++++++ 8 files changed, 1464 insertions(+), 1 deletion(-) create mode 100644 tests/allman/force_curly_braces.d.ref create mode 100644 tests/force_curly_braces.args create mode 100644 tests/force_curly_braces.d create mode 100644 tests/knr/force_curly_braces.d.ref create mode 100644 tests/otbs/force_curly_braces.d.ref diff --git a/src/dfmt/config.d b/src/dfmt/config.d index 6b41d4a..e2eabcf 100644 --- a/src/dfmt/config.d +++ b/src/dfmt/config.d @@ -69,6 +69,8 @@ struct Config OptionalBoolean dfmt_space_after_statement_keyword; /// OptionalBoolean dfmt_space_before_named_arg_colon; + /// + OptionalBoolean dfmt_force_curly_braces; mixin StandardEditorConfigFields; @@ -101,6 +103,7 @@ struct Config dfmt_single_indent = OptionalBoolean.f; dfmt_reflow_property_chains = OptionalBoolean.t; dfmt_space_before_named_arg_colon = OptionalBoolean.f; + dfmt_force_curly_braces = OptionalBoolean.f; } /** diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index 2545edb..1fe8941 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -51,6 +51,11 @@ bool format(OutputRange)(string source_desc, ubyte[] buffer, OutputRange output, auto tokens = app.data; if (!tokenRange.messages.empty) return false; + + import dfmt.editorconfig : OptionalBoolean; + if (formatterConfig.dfmt_force_curly_braces == OptionalBoolean.t) + tokens = insertForcedBraces(tokens); + auto depths = generateDepthInfo(tokens); auto tokenFormatter = TokenFormatter!OutputRange(buffer, tokens, depths, output, &astInformation, formatterConfig); @@ -2369,6 +2374,527 @@ const pure @safe @nogc: } } +/** + * Insert `{` and `}` tokens around single-statement bodies for block headers + * (if, else, for, foreach, while, do, etc.) when force_curly_braces is enabled. + * + * This preprocessing step runs on the token array before the formatter, so the + * formatter's existing brace-handling logic takes care of style and indentation. + */ +Token[] insertForcedBraces(const(Token)[] tokens) +{ + import std.array : appender, Appender; + + // Collect insertion points: (index, true=open/false=close) + // We'll insert `{` before the token at `index`, or `}` after the token at `index-1`. + struct Insertion + { + size_t position; // insert BEFORE this token index + bool isOpen; // true = `{`, false = `}` + } + + Appender!(Insertion[]) insertions; + + // Helper: is this token type a block header keyword that takes parens? + static bool isParenBlockHeader(IdType t) pure nothrow @safe @nogc + { + return t == tok!"if" || t == tok!"for" || t == tok!"foreach" + || t == tok!"foreach_reverse" || t == tok!"while" + || t == tok!"catch" || t == tok!"with" + || t == tok!"synchronized" || t == tok!"scope" + || t == tok!"version"; + } + + // Skip past comments at position i, return new position + size_t skipComments(size_t i) nothrow @safe @nogc + { + while (i < tokens.length && tokens[i].type == tok!"comment") + i++; + return i; + } + + // Skip past matched parentheses starting at open paren at position i. + // Returns position AFTER the closing paren, or tokens.length on error. + size_t skipParens(size_t i) nothrow @safe @nogc + { + if (i >= tokens.length || tokens[i].type != tok!"(") + return i; + int depth = 1; + i++; + while (i < tokens.length && depth > 0) + { + if (tokens[i].type == tok!"(") + depth++; + else if (tokens[i].type == tok!")") + depth--; + i++; + } + return i; + } + + // Find the end of a single statement starting at position `i`. + // Returns the position AFTER the statement (i.e., after the `;` or closing `}`). + // For block headers, this recurses to find the full construct including else. + size_t findStatementEnd(size_t i) + { + i = skipComments(i); + if (i >= tokens.length) + return i; + + auto t = tokens[i].type; + + // If the statement starts with '{', find matching '}' + if (t == tok!"{") + { + int depth = 1; + i++; + while (i < tokens.length && depth > 0) + { + if (tokens[i].type == tok!"{") + depth++; + else if (tokens[i].type == tok!"}") + depth--; + i++; + } + return i; + } + + // If the statement is a block header with parens (if, for, while, etc.) + if (isParenBlockHeader(t)) + { + i++; // skip keyword + i = skipComments(i); + if (i < tokens.length && tokens[i].type == tok!"(") + i = skipParens(i); + i = skipComments(i); + // Now find the body + size_t bodyEnd = findStatementEnd(i); + + // Special case for `if`: check for trailing `else` + if (t == tok!"if") + { + size_t afterBody = skipComments(bodyEnd); + if (afterBody < tokens.length && tokens[afterBody].type == tok!"else") + { + afterBody++; // skip `else` + afterBody = skipComments(afterBody); + bodyEnd = findStatementEnd(afterBody); + } + } + return bodyEnd; + } + + // `debug` can appear with or without parens + if (t == tok!"debug") + { + i++; // skip `debug` + i = skipComments(i); + if (i < tokens.length && tokens[i].type == tok!"(") + i = skipParens(i); + i = skipComments(i); + size_t bodyEnd = findStatementEnd(i); + // Check for trailing else + size_t afterBody = skipComments(bodyEnd); + if (afterBody < tokens.length && tokens[afterBody].type == tok!"else") + { + afterBody++; + afterBody = skipComments(afterBody); + bodyEnd = findStatementEnd(afterBody); + } + return bodyEnd; + } + + // `do` ... `while` construct + if (t == tok!"do") + { + i++; // skip `do` + i = skipComments(i); + size_t bodyEnd = findStatementEnd(i); + bodyEnd = skipComments(bodyEnd); + // Expect `while` + if (bodyEnd < tokens.length && tokens[bodyEnd].type == tok!"while") + { + bodyEnd++; // skip `while` + bodyEnd = skipComments(bodyEnd); + if (bodyEnd < tokens.length && tokens[bodyEnd].type == tok!"(") + bodyEnd = skipParens(bodyEnd); + // skip trailing `;` + bodyEnd = skipComments(bodyEnd); + if (bodyEnd < tokens.length && tokens[bodyEnd].type == tok!";") + bodyEnd++; + } + return bodyEnd; + } + + // `try` ... `catch` ... `finally` compound + if (t == tok!"try") + { + i++; // skip `try` + i = skipComments(i); + size_t bodyEnd = findStatementEnd(i); // try body + // Check for catch/finally chains + while (true) + { + size_t next = skipComments(bodyEnd); + if (next < tokens.length && tokens[next].type == tok!"catch") + { + next++; // skip catch + next = skipComments(next); + if (next < tokens.length && tokens[next].type == tok!"(") + next = skipParens(next); + next = skipComments(next); + bodyEnd = findStatementEnd(next); // catch body + } + else if (next < tokens.length && tokens[next].type == tok!"finally") + { + next++; // skip finally + next = skipComments(next); + bodyEnd = findStatementEnd(next); // finally body + break; // finally is always last + } + else + break; + } + return bodyEnd; + } + + // `finally` — just a body, no parens + if (t == tok!"finally") + { + i++; // skip `finally` + i = skipComments(i); + return findStatementEnd(i); + } + + // `else` (standalone, not `else if`) + if (t == tok!"else") + { + i++; // skip `else` + i = skipComments(i); + return findStatementEnd(i); + } + + // Regular statement: scan to `;` at depth 0 + int depth = 0; + while (i < tokens.length) + { + if (tokens[i].type == tok!"(" || tokens[i].type == tok!"[" + || tokens[i].type == tok!"{") + depth++; + else if (tokens[i].type == tok!")" || tokens[i].type == tok!"]" + || tokens[i].type == tok!"}") + { + depth--; + if (depth < 0) + break; // unmatched close - stop + } + else if (tokens[i].type == tok!";" && depth == 0) + { + i++; // include the semicolon + return i; + } + i++; + } + return i; + } + + // Find the end of just the "then" part of an if statement (not including else). + // This is needed because findStatementEnd for if includes the else. + size_t findThenBodyEnd(size_t i) + { + i = skipComments(i); + if (i >= tokens.length) + return i; + + auto t = tokens[i].type; + + // Braced body + if (t == tok!"{") + { + int depth = 1; + i++; + while (i < tokens.length && depth > 0) + { + if (tokens[i].type == tok!"{") + depth++; + else if (tokens[i].type == tok!"}") + depth--; + i++; + } + return i; + } + + // If the then-body is itself a block header, we need the full construct + // but NOT its else (since that else belongs to the outer if) + // Actually, for nested if: `if (a) if (b) x; else y; else z;` + // the first else belongs to the inner if. We need to include it. + // So we use findStatementEnd which handles if+else as a unit. + if (isParenBlockHeader(t) || t == tok!"debug" || t == tok!"do" || t == tok!"else" + || t == tok!"try" || t == tok!"finally") + { + return findStatementEnd(i); + } + + // Regular statement + int depth = 0; + while (i < tokens.length) + { + if (tokens[i].type == tok!"(" || tokens[i].type == tok!"[" + || tokens[i].type == tok!"{") + depth++; + else if (tokens[i].type == tok!")" || tokens[i].type == tok!"]" + || tokens[i].type == tok!"}") + { + depth--; + if (depth < 0) + break; + } + else if (tokens[i].type == tok!";" && depth == 0) + { + i++; + return i; + } + i++; + } + return i; + } + + // Process a block header body at position `i` (after the condition/parens). + // If the body is not braced, record insertion points for `{` and `}`. + // Only wraps the immediate body, not any trailing `else` — the main loop + // handles `else` and inner block headers separately. + void processBody(size_t i, IdType headerType) + { + size_t bodyStart = skipComments(i); + if (bodyStart >= tokens.length) + return; + + // Already braced or empty statement - no insertion needed + if (tokens[bodyStart].type == tok!"{" || tokens[bodyStart].type == tok!";") + return; + + // For certain keywords after function declarations, don't insert braces + if (tokens[bodyStart].type == tok!"in" || tokens[bodyStart].type == tok!"out" + || tokens[bodyStart].type == tok!"do") + return; + if (bodyStart < tokens.length && tokens[bodyStart].text == "body") + return; + + // For `if`/`debug`/`version` with `else`: wrap only the "then" body. + // The main loop will handle `else` when it encounters it. + if (headerType == tok!"if" || headerType == tok!"debug" + || headerType == tok!"version") + { + size_t thenEnd = findThenBodyEnd(bodyStart); + insertions ~= Insertion(bodyStart, true); + insertions ~= Insertion(thenEnd, false); + return; + } + + // For non-if block headers (for, foreach, while, etc.) + size_t stmtEnd = findStatementEnd(bodyStart); + insertions ~= Insertion(bodyStart, true); + insertions ~= Insertion(stmtEnd, false); + } + + // Helper: extract the meaningful text from a comment token (strip // or /* */) + static string getDfmtCommentText(string commentText) pure @safe + { + import std.string : strip; + + if (commentText.length >= 2 && commentText[0 .. 2] == "//") + return commentText[2 .. $].strip(); + else if (commentText.length > 3) + return commentText[2 .. $ - 2].strip(); + else if (commentText.length >= 2) + return commentText[2 .. $].strip(); + return commentText; + } + + // Main scan: walk through tokens and look for block headers. + // We process every token position; processBody only records insertions, + // it does NOT cause the main loop to skip tokens, so inner block headers + // (like nested if, foreach inside if body, etc.) are also processed. + size_t i = 0; + bool dfmtOff = false; + while (i < tokens.length) + { + auto t = tokens[i].type; + + // Track `// dfmt off` / `// dfmt on` comment regions. + // When dfmtOff is true, skip all block header processing. + if (t == tok!"comment") + { + auto ct = getDfmtCommentText(tokens[i].text); + if (ct == "dfmt off") + dfmtOff = true; + else if (ct == "dfmt on") + dfmtOff = false; + i++; + continue; + } + + if (dfmtOff) + { + i++; + continue; + } + + // Block header with parens + if (isParenBlockHeader(t)) + { + size_t afterKw = i + 1; + afterKw = skipComments(afterKw); + if (afterKw < tokens.length && tokens[afterKw].type == tok!"(") + { + size_t afterParens = skipParens(afterKw); + processBody(afterParens, t); + } + else if (t == tok!"synchronized") + { + // synchronized without parens: `synchronized stmt;` + processBody(afterKw, t); + } + i++; + continue; + } + + // `debug` keyword — can appear with or without parens + if (t == tok!"debug") + { + size_t afterKw = i + 1; + afterKw = skipComments(afterKw); + if (afterKw < tokens.length && tokens[afterKw].type == tok!"(") + { + size_t afterParens = skipParens(afterKw); + processBody(afterParens, t); + } + else + { + // debug without parens: `debug stmt;` + processBody(afterKw, t); + } + i++; + continue; + } + + // `do` keyword (not `do` in function body contracts) + if (t == tok!"do") + { + size_t bodyStart = skipComments(i + 1); + if (bodyStart < tokens.length && tokens[bodyStart].type != tok!"{" + && tokens[bodyStart].type != tok!";") + { + // Find end of do body (just the single statement, not the while) + size_t stmtEnd = findThenBodyEnd(bodyStart); + insertions ~= Insertion(bodyStart, true); + insertions ~= Insertion(stmtEnd, false); + } + i++; + continue; + } + + // `finally` keyword — never takes parens + if (t == tok!"finally") + { + size_t bodyStart = skipComments(i + 1); + if (bodyStart < tokens.length && tokens[bodyStart].type != tok!"{" + && tokens[bodyStart].type != tok!";") + { + size_t stmtEnd = findStatementEnd(bodyStart); + insertions ~= Insertion(bodyStart, true); + insertions ~= Insertion(stmtEnd, false); + } + i++; + continue; + } + + // `else` keyword + if (t == tok!"else") + { + size_t afterElse = skipComments(i + 1); + if (afterElse < tokens.length) + { + auto nextT = tokens[afterElse].type; + // else if / else version / else static if — skip, let inner handle it + if (nextT == tok!"if" || nextT == tok!"version" + || (nextT == tok!"static" && afterElse + 1 < tokens.length + && (tokens[afterElse + 1].type == tok!"if" + || tokens[afterElse + 1].type == tok!"foreach" + || tokens[afterElse + 1].type == tok!"foreach_reverse"))) + { + i++; + continue; + } + // else with braced body - skip + if (nextT == tok!"{") + { + i++; + continue; + } + // else with braceless body - add braces + size_t elseBodyEnd = findStatementEnd(afterElse); + insertions ~= Insertion(afterElse, true); + insertions ~= Insertion(elseBodyEnd, false); + } + i++; + continue; + } + + i++; + } + + // If no insertions needed, return the original array + if (insertions.data.length == 0) + return cast(Token[]) tokens; + + // Sort insertions by position (stable sort: opens before closes at same position) + import std.algorithm : sort; + auto ins = insertions.data; + sort!((a, b) { + if (a.position != b.position) + return a.position < b.position; + // At the same position, open brace comes before close brace + // (shouldn't normally happen, but handle gracefully) + return a.isOpen && !b.isOpen; + })(ins); + + // Build the new token array + auto result = appender!(Token[])(); + result.reserve(tokens.length + ins.length); + + size_t insIdx = 0; + for (size_t ti = 0; ti <= tokens.length; ti++) + { + // Insert any synthetic tokens at this position + while (insIdx < ins.length && ins[insIdx].position == ti) + { + Token synth; + synth.type = ins[insIdx].isOpen ? tok!"{" : tok!"}"; + // Use size_t.max minus a counter for unique synthetic indices + // that won't match any AST info lookups + synth.index = size_t.max - insIdx; + // Copy line/column from the nearest real token for reasonable error messages + if (ti < tokens.length) + { + synth.line = tokens[ti].line; + synth.column = tokens[ti].column; + } + else if (tokens.length > 0) + { + synth.line = tokens[$ - 1].line; + synth.column = tokens[$ - 1].column; + } + result ~= synth; + insIdx++; + } + if (ti < tokens.length) + result ~= tokens[ti]; + } + + return result.data; +} + bool canFindIndex(const size_t[] items, size_t index, size_t* pos = null) pure @safe @nogc { import std.range : assumeSorted; diff --git a/src/dfmt/main.d b/src/dfmt/main.d index c5a5577..027f332 100644 --- a/src/dfmt/main.d +++ b/src/dfmt/main.d @@ -107,6 +107,9 @@ else case "space_after_keywords": optConfig.dfmt_space_after_keywords = optVal; break; + case "force_curly_braces": + optConfig.dfmt_force_curly_braces = optVal; + break; default: assert(false, "Invalid command-line switch"); } @@ -141,7 +144,8 @@ else "template_constraint_style", &optConfig.dfmt_template_constraint_style, "keep_line_breaks", &handleBooleans, "single_indent", &handleBooleans, - "reflow_property_chains", &handleBooleans); + "reflow_property_chains", &handleBooleans, + "force_curly_braces", &handleBooleans); // dfmt on } catch (GetOptException e) @@ -356,6 +360,7 @@ Formatting Options: --space_before_named_arg_colon --single_indent --reflow_property_chains + --force_curly_braces `, optionsToString!(typeof(Config.dfmt_template_constraint_style))); } diff --git a/tests/allman/force_curly_braces.d.ref b/tests/allman/force_curly_braces.d.ref new file mode 100644 index 0000000..781b992 --- /dev/null +++ b/tests/allman/force_curly_braces.d.ref @@ -0,0 +1,292 @@ +// Test: force_curly_braces option +// Tests various braceless constructs that should get braces added + +// 1. Simple if without braces +int test1(int x) +{ + if (x > 0) + { + return 1; + } + return 0; +} + +// 2. if/else without braces +int test2(int x) +{ + if (x > 0) + { + return 1; + } + else + { + return 0; + } +} + +// 3. Nested if without braces +void test3(int a, int b) +{ + if (a) + { + if (b) + { + doSomething(); + } + } +} + +// 4. if/else if/else chain +int test4(int x) +{ + if (x > 0) + { + return 1; + } + else if (x < 0) + { + return -1; + } + else + { + return 0; + } +} + +// 5. for loop without braces +void test5() +{ + for (int i = 0; i < 10; i++) + { + doSomething(); + } +} + +// 6. foreach without braces +void test6(int[] arr) +{ + foreach (x; arr) + { + doSomething(); + } +} + +// 7. while without braces +void test7() +{ + while (condition()) + { + doSomething(); + } +} + +// 8. Mixed: if braced, else not +int test8(int x) +{ + if (x > 0) + { + return 1; + } + else + { + return 0; + } +} + +// 9. Mixed: if not braced, else braced +int test9(int x) +{ + if (x > 0) + { + return 1; + } + else + { + return 0; + } +} + +// 10. Already fully braced (should be unchanged) +int test10(int x) +{ + if (x > 0) + { + return 1; + } + else + { + return 0; + } +} + +// 11. Nested mixed: if with for body +void test11(int[] arr) +{ + if (arr.length > 0) + { + foreach (x; arr) + { + doSomething(); + } + } +} + +// 12. while with if body +void test12() +{ + while (condition()) + { + if (check()) + { + doSomething(); + } + } +} + +// 13. do-while without braces +void test13() +{ + do + { + doSomething(); + } + while (condition()); +} + +// 14. scope(exit) without braces +void test14() +{ + scope (exit) + { + cleanup(); + } + doWork(); +} + +// 15. synchronized without braces +void test15() +{ + synchronized + { + doSomething(); + } +} + +// 16. debug without parens +void test16() +{ + debug + { + doSomething(); + } +} + +// 17. version/else +void test17() +{ + version (Windows) + { + doWindows(); + } + else + { + doPosix(); + } +} + +// 18. with statement +void test18() +{ + with (someObj) + { + doSomething(); + } +} + +// 19. catch without braces +void test19() +{ + try + { + doSomething(); + } + catch (Exception e) + { + handleError(); + } +} + +// 20. debug with else +void test20() +{ + debug + { + doDebug(); + } + else + { + doRelease(); + } +} + +// 21. finally without braces +void test21() +{ + try + { + doSomething(); + } + catch (Exception e) + { + handleError(); + } + finally + { + cleanup(); + } +} + +// 22. try/catch/finally all braceless as body of if +void test22(bool flag) +{ + if (flag) + { + try + { + doSomething(); + } + catch (Exception e) + { + handleError(); + } + } +} + +// 23. dfmt off region — braceless code should NOT get braces +void test23() +{ + // dfmt off + if (x) + doSomething(); + // dfmt on + if (y) + { + doSomethingElse(); + } +} + +// 24. else debug without parens +void test24() +{ + version (Windows) + { + doWindows(); + } + else + { + debug + { + doDebugPosix(); + } + } +} diff --git a/tests/force_curly_braces.args b/tests/force_curly_braces.args new file mode 100644 index 0000000..b7491d7 --- /dev/null +++ b/tests/force_curly_braces.args @@ -0,0 +1 @@ +--force_curly_braces=true diff --git a/tests/force_curly_braces.d b/tests/force_curly_braces.d new file mode 100644 index 0000000..796eb23 --- /dev/null +++ b/tests/force_curly_braces.d @@ -0,0 +1,186 @@ +// Test: force_curly_braces option +// Tests various braceless constructs that should get braces added + +// 1. Simple if without braces +int test1(int x) { + if (x > 0) + return 1; + return 0; +} + +// 2. if/else without braces +int test2(int x) { + if (x > 0) + return 1; + else + return 0; +} + +// 3. Nested if without braces +void test3(int a, int b) { + if (a) + if (b) + doSomething(); +} + +// 4. if/else if/else chain +int test4(int x) { + if (x > 0) + return 1; + else if (x < 0) + return -1; + else + return 0; +} + +// 5. for loop without braces +void test5() { + for (int i = 0; i < 10; i++) + doSomething(); +} + +// 6. foreach without braces +void test6(int[] arr) { + foreach (x; arr) + doSomething(); +} + +// 7. while without braces +void test7() { + while (condition()) + doSomething(); +} + +// 8. Mixed: if braced, else not +int test8(int x) { + if (x > 0) { + return 1; + } else + return 0; +} + +// 9. Mixed: if not braced, else braced +int test9(int x) { + if (x > 0) + return 1; + else { + return 0; + } +} + +// 10. Already fully braced (should be unchanged) +int test10(int x) { + if (x > 0) { + return 1; + } else { + return 0; + } +} + +// 11. Nested mixed: if with for body +void test11(int[] arr) { + if (arr.length > 0) + foreach (x; arr) + doSomething(); +} + +// 12. while with if body +void test12() { + while (condition()) + if (check()) + doSomething(); +} + +// 13. do-while without braces +void test13() { + do + doSomething(); + while (condition()); +} + +// 14. scope(exit) without braces +void test14() { + scope(exit) + cleanup(); + doWork(); +} + +// 15. synchronized without braces +void test15() { + synchronized + doSomething(); +} + +// 16. debug without parens +void test16() { + debug + doSomething(); +} + +// 17. version/else +void test17() { + version(Windows) + doWindows(); + else + doPosix(); +} + +// 18. with statement +void test18() { + with (someObj) + doSomething(); +} + +// 19. catch without braces +void test19() { + try { + doSomething(); + } catch (Exception e) + handleError(); +} + +// 20. debug with else +void test20() { + debug + doDebug(); + else + doRelease(); +} + +// 21. finally without braces +void test21() { + try { + doSomething(); + } catch (Exception e) { + handleError(); + } finally + cleanup(); +} + +// 22. try/catch/finally all braceless as body of if +void test22(bool flag) { + if (flag) + try { + doSomething(); + } catch (Exception e) + handleError(); +} + +// 23. dfmt off region — braceless code should NOT get braces +void test23() { + // dfmt off + if (x) + doSomething(); + // dfmt on + if (y) + doSomethingElse(); +} + +// 24. else debug without parens +void test24() { + version(Windows) + doWindows(); + else + debug + doDebugPosix(); +} diff --git a/tests/knr/force_curly_braces.d.ref b/tests/knr/force_curly_braces.d.ref new file mode 100644 index 0000000..45e55ad --- /dev/null +++ b/tests/knr/force_curly_braces.d.ref @@ -0,0 +1,237 @@ +// Test: force_curly_braces option +// Tests various braceless constructs that should get braces added + +// 1. Simple if without braces +int test1(int x) +{ + if (x > 0) { + return 1; + } + return 0; +} + +// 2. if/else without braces +int test2(int x) +{ + if (x > 0) { + return 1; + } else { + return 0; + } +} + +// 3. Nested if without braces +void test3(int a, int b) +{ + if (a) { + if (b) { + doSomething(); + } + } +} + +// 4. if/else if/else chain +int test4(int x) +{ + if (x > 0) { + return 1; + } else if (x < 0) { + return -1; + } else { + return 0; + } +} + +// 5. for loop without braces +void test5() +{ + for (int i = 0; i < 10; i++) { + doSomething(); + } +} + +// 6. foreach without braces +void test6(int[] arr) +{ + foreach (x; arr) { + doSomething(); + } +} + +// 7. while without braces +void test7() +{ + while (condition()) { + doSomething(); + } +} + +// 8. Mixed: if braced, else not +int test8(int x) +{ + if (x > 0) { + return 1; + } else { + return 0; + } +} + +// 9. Mixed: if not braced, else braced +int test9(int x) +{ + if (x > 0) { + return 1; + } else { + return 0; + } +} + +// 10. Already fully braced (should be unchanged) +int test10(int x) +{ + if (x > 0) { + return 1; + } else { + return 0; + } +} + +// 11. Nested mixed: if with for body +void test11(int[] arr) +{ + if (arr.length > 0) { + foreach (x; arr) { + doSomething(); + } + } +} + +// 12. while with if body +void test12() +{ + while (condition()) { + if (check()) { + doSomething(); + } + } +} + +// 13. do-while without braces +void test13() +{ + do { + doSomething(); + } + while (condition()); +} + +// 14. scope(exit) without braces +void test14() +{ + scope (exit) { + cleanup(); + } + doWork(); +} + +// 15. synchronized without braces +void test15() +{ + synchronized { + doSomething(); + } +} + +// 16. debug without parens +void test16() +{ + debug { + doSomething(); + } +} + +// 17. version/else +void test17() +{ + version (Windows) { + doWindows(); + } else { + doPosix(); + } +} + +// 18. with statement +void test18() +{ + with (someObj) { + doSomething(); + } +} + +// 19. catch without braces +void test19() +{ + try { + doSomething(); + } catch (Exception e) { + handleError(); + } +} + +// 20. debug with else +void test20() +{ + debug { + doDebug(); + } else { + doRelease(); + } +} + +// 21. finally without braces +void test21() +{ + try { + doSomething(); + } catch (Exception e) { + handleError(); + } finally { + cleanup(); + } +} + +// 22. try/catch/finally all braceless as body of if +void test22(bool flag) +{ + if (flag) { + try { + doSomething(); + } catch (Exception e) { + handleError(); + } + } +} + +// 23. dfmt off region — braceless code should NOT get braces +void test23() +{ + // dfmt off + if (x) + doSomething(); + // dfmt on + if (y) { + doSomethingElse(); + } +} + +// 24. else debug without parens +void test24() +{ + version (Windows) { + doWindows(); + } else { + debug { + doDebugPosix(); + } + } +} diff --git a/tests/otbs/force_curly_braces.d.ref b/tests/otbs/force_curly_braces.d.ref new file mode 100644 index 0000000..927b05d --- /dev/null +++ b/tests/otbs/force_curly_braces.d.ref @@ -0,0 +1,213 @@ +// Test: force_curly_braces option +// Tests various braceless constructs that should get braces added + +// 1. Simple if without braces +int test1(int x) { + if (x > 0) { + return 1; + } + return 0; +} + +// 2. if/else without braces +int test2(int x) { + if (x > 0) { + return 1; + } else { + return 0; + } +} + +// 3. Nested if without braces +void test3(int a, int b) { + if (a) { + if (b) { + doSomething(); + } + } +} + +// 4. if/else if/else chain +int test4(int x) { + if (x > 0) { + return 1; + } else if (x < 0) { + return -1; + } else { + return 0; + } +} + +// 5. for loop without braces +void test5() { + for (int i = 0; i < 10; i++) { + doSomething(); + } +} + +// 6. foreach without braces +void test6(int[] arr) { + foreach (x; arr) { + doSomething(); + } +} + +// 7. while without braces +void test7() { + while (condition()) { + doSomething(); + } +} + +// 8. Mixed: if braced, else not +int test8(int x) { + if (x > 0) { + return 1; + } else { + return 0; + } +} + +// 9. Mixed: if not braced, else braced +int test9(int x) { + if (x > 0) { + return 1; + } else { + return 0; + } +} + +// 10. Already fully braced (should be unchanged) +int test10(int x) { + if (x > 0) { + return 1; + } else { + return 0; + } +} + +// 11. Nested mixed: if with for body +void test11(int[] arr) { + if (arr.length > 0) { + foreach (x; arr) { + doSomething(); + } + } +} + +// 12. while with if body +void test12() { + while (condition()) { + if (check()) { + doSomething(); + } + } +} + +// 13. do-while without braces +void test13() { + do { + doSomething(); + } + while (condition()); +} + +// 14. scope(exit) without braces +void test14() { + scope (exit) { + cleanup(); + } + doWork(); +} + +// 15. synchronized without braces +void test15() { + synchronized { + doSomething(); + } +} + +// 16. debug without parens +void test16() { + debug { + doSomething(); + } +} + +// 17. version/else +void test17() { + version (Windows) { + doWindows(); + } else { + doPosix(); + } +} + +// 18. with statement +void test18() { + with (someObj) { + doSomething(); + } +} + +// 19. catch without braces +void test19() { + try { + doSomething(); + } catch (Exception e) { + handleError(); + } +} + +// 20. debug with else +void test20() { + debug { + doDebug(); + } else { + doRelease(); + } +} + +// 21. finally without braces +void test21() { + try { + doSomething(); + } catch (Exception e) { + handleError(); + } finally { + cleanup(); + } +} + +// 22. try/catch/finally all braceless as body of if +void test22(bool flag) { + if (flag) { + try { + doSomething(); + } catch (Exception e) { + handleError(); + } + } +} + +// 23. dfmt off region — braceless code should NOT get braces +void test23() { + // dfmt off + if (x) + doSomething(); + // dfmt on + if (y) { + doSomethingElse(); + } +} + +// 24. else debug without parens +void test24() { + version (Windows) { + doWindows(); + } else { + debug { + doDebugPosix(); + } + } +}