1 // Distributed under the Boost Software License, Version 1.0. 2 // (See accompanying file LICENSE_1_0.txt or copy at 3 // http://www.boost.org/LICENSE_1_0.txt) 4 5 module dscanner.analysis.allman; 6 7 import dparse.lexer; 8 import dparse.ast; 9 import dscanner.analysis.base : BaseAnalyzer; 10 import dsymbol.scope_ : Scope; 11 12 import std.algorithm; 13 import std.range; 14 15 /** 16 Checks for the allman style (braces should be on their own line) 17 ------------ 18 if (param < 0) { 19 } 20 ------------ 21 should be 22 ------------ 23 if (param < 0) 24 { 25 } 26 ------------ 27 */ 28 class AllManCheck : BaseAnalyzer 29 { 30 /// 31 this(string fileName, const(Token)[] tokens, bool skipTests = false) 32 { 33 super(fileName, null, skipTests); 34 foreach (i; 1 .. tokens.length - 1) 35 { 36 const curLine = tokens[i].line; 37 const prevTokenLine = tokens[i-1].line; 38 if (tokens[i].type == tok!"{" && curLine == prevTokenLine) 39 { 40 // ignore struct initialization 41 if (tokens[i-1].type == tok!"=") 42 continue; 43 // ignore duplicate braces 44 if (tokens[i-1].type == tok!"{" && tokens[i - 2].line != curLine) 45 continue; 46 // ignore inline { } braces 47 if (curLine != tokens[i + 1].line) 48 addErrorMessage(tokens[i].line, tokens[i].column, KEY, MESSAGE); 49 } 50 if (tokens[i].type == tok!"}" && curLine == prevTokenLine) 51 { 52 // ignore duplicate braces 53 if (tokens[i-1].type == tok!"}" && tokens[i - 2].line != curLine) 54 continue; 55 // ignore inline { } braces 56 if (!tokens[0 .. i].retro.until!(t => t.line != curLine).canFind!(t => t.type == tok!"{")) 57 addErrorMessage(tokens[i].line, tokens[i].column, KEY, MESSAGE); 58 } 59 } 60 } 61 62 enum string KEY = "dscanner.style.allman"; 63 enum string MESSAGE = "Braces should be on their own line"; 64 } 65 66 unittest 67 { 68 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 69 import dscanner.analysis.helpers : assertAnalyzerWarnings; 70 import std.format : format; 71 import std.stdio : stderr; 72 73 StaticAnalysisConfig sac = disabledConfig(); 74 sac.allman_braces_check = Check.enabled; 75 76 // check common allman style violation 77 assertAnalyzerWarnings(q{ 78 void testAllman() 79 { 80 while (true) { // [warn]: %s 81 auto f = 1; 82 } 83 84 do { // [warn]: %s 85 auto f = 1; 86 } while (true); 87 88 // inline braces are OK 89 while (true) { auto f = 1; } 90 91 if (true) { // [warn]: %s 92 auto f = 1; 93 } 94 if (true) 95 { 96 auto f = 1; } // [warn]: %s 97 if (true) { auto f = 1; } 98 foreach (r; [1]) { // [warn]: %s 99 } 100 foreach (r; [1]) { } 101 foreach_reverse (r; [1]) { // [warn]: %s 102 } 103 foreach_reverse (r; [1]) { } 104 for (int i = 0; i < 10; i++) { // [warn]: %s 105 } 106 for (int i = 0; i < 10; i++) { } 107 108 // nested check 109 while (true) { // [warn]: %s 110 while (true) { // [warn]: %s 111 auto f = 1; 112 } 113 } 114 } 115 }c.format( 116 AllManCheck.MESSAGE, 117 AllManCheck.MESSAGE, 118 AllManCheck.MESSAGE, 119 AllManCheck.MESSAGE, 120 AllManCheck.MESSAGE, 121 AllManCheck.MESSAGE, 122 AllManCheck.MESSAGE, 123 AllManCheck.MESSAGE, 124 AllManCheck.MESSAGE, 125 ), sac); 126 127 // check struct initialization 128 assertAnalyzerWarnings(q{ 129 unittest 130 { 131 struct Foo { int a; } 132 Foo foo = { 133 a: 1; 134 }; 135 } 136 }, sac); 137 138 // allow duplicate braces 139 assertAnalyzerWarnings(q{ 140 unittest 141 {{ 142 }} 143 }, sac); 144 145 146 stderr.writeln("Unittest for Allman passed."); 147 }