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; 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 final class AllManCheck : BaseAnalyzer 29 { 30 mixin AnalyzerInfo!"allman_braces_check"; 31 32 /// 33 this(string fileName, const(Token)[] tokens, bool skipTests = false) 34 { 35 super(fileName, null, skipTests); 36 foreach (i; 1 .. tokens.length - 1) 37 { 38 const curLine = tokens[i].line; 39 const prevTokenLine = tokens[i-1].line; 40 if (tokens[i].type == tok!"{" && curLine == prevTokenLine) 41 { 42 // ignore struct initialization 43 if (tokens[i-1].type == tok!"=") 44 continue; 45 // ignore duplicate braces 46 if (tokens[i-1].type == tok!"{" && tokens[i - 2].line != curLine) 47 continue; 48 // ignore inline { } braces 49 if (curLine != tokens[i + 1].line) 50 addErrorMessage(tokens[i].line, tokens[i].column, KEY, MESSAGE); 51 } 52 if (tokens[i].type == tok!"}" && curLine == prevTokenLine) 53 { 54 // ignore duplicate braces 55 if (tokens[i-1].type == tok!"}" && tokens[i - 2].line != curLine) 56 continue; 57 // ignore inline { } braces 58 if (!tokens[0 .. i].retro.until!(t => t.line != curLine).canFind!(t => t.type == tok!"{")) 59 addErrorMessage(tokens[i].line, tokens[i].column, KEY, MESSAGE); 60 } 61 } 62 } 63 64 enum string KEY = "dscanner.style.allman"; 65 enum string MESSAGE = "Braces should be on their own line"; 66 } 67 68 unittest 69 { 70 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 71 import dscanner.analysis.helpers : assertAnalyzerWarnings; 72 import std.format : format; 73 import std.stdio : stderr; 74 75 StaticAnalysisConfig sac = disabledConfig(); 76 sac.allman_braces_check = Check.enabled; 77 78 // check common allman style violation 79 assertAnalyzerWarnings(q{ 80 void testAllman() 81 { 82 while (true) { // [warn]: %s 83 auto f = 1; 84 } 85 86 do { // [warn]: %s 87 auto f = 1; 88 } while (true); 89 90 // inline braces are OK 91 while (true) { auto f = 1; } 92 93 if (true) { // [warn]: %s 94 auto f = 1; 95 } 96 if (true) 97 { 98 auto f = 1; } // [warn]: %s 99 if (true) { auto f = 1; } 100 foreach (r; [1]) { // [warn]: %s 101 } 102 foreach (r; [1]) { } 103 foreach_reverse (r; [1]) { // [warn]: %s 104 } 105 foreach_reverse (r; [1]) { } 106 for (int i = 0; i < 10; i++) { // [warn]: %s 107 } 108 for (int i = 0; i < 10; i++) { } 109 110 // nested check 111 while (true) { // [warn]: %s 112 while (true) { // [warn]: %s 113 auto f = 1; 114 } 115 } 116 } 117 }}.format( 118 AllManCheck.MESSAGE, 119 AllManCheck.MESSAGE, 120 AllManCheck.MESSAGE, 121 AllManCheck.MESSAGE, 122 AllManCheck.MESSAGE, 123 AllManCheck.MESSAGE, 124 AllManCheck.MESSAGE, 125 AllManCheck.MESSAGE, 126 AllManCheck.MESSAGE, 127 ), sac); 128 129 // check struct initialization 130 assertAnalyzerWarnings(q{ 131 unittest 132 { 133 struct Foo { int a; } 134 Foo foo = { 135 a: 1; 136 }; 137 } 138 }, sac); 139 140 // allow duplicate braces 141 assertAnalyzerWarnings(q{ 142 unittest 143 {{ 144 }} 145 }, sac); 146 147 148 stderr.writeln("Unittest for Allman passed."); 149 }