Skip to content

Commit b43c8f4

Browse files
Robert SchadekWebFreak001
authored andcommitted
Always Check Curly
Check that if|else|for|foreach|while|do|try|catch are always followed by a BlockStatement aka. { } closer can not get the test to work try to get the AutoFix in place maybe a fix nicer messages some formatting more tinkering still nothing autofix work now AutoFix name message to message_postfix
1 parent fc1699b commit b43c8f4

3 files changed

Lines changed: 235 additions & 0 deletions

File tree

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
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.always_curly;
6+
7+
import dparse.lexer;
8+
import dparse.ast;
9+
import dscanner.analysis.base;
10+
import dsymbol.scope_ : Scope;
11+
12+
import std.array : back, front;
13+
import std.algorithm;
14+
import std.range;
15+
import std.stdio;
16+
17+
final class AlwaysCurlyCheck : BaseAnalyzer
18+
{
19+
mixin AnalyzerInfo!"always_curly_check";
20+
21+
alias visit = BaseAnalyzer.visit;
22+
23+
///
24+
this(string fileName, const(Token)[] tokens, bool skipTests = false)
25+
{
26+
super(fileName, null, skipTests);
27+
}
28+
29+
void test(L, B)(L loc, B s, string stmtKind)
30+
{
31+
if (!is(s == BlockStatement))
32+
{
33+
if (!s.tokens.empty)
34+
{
35+
AutoFix af = AutoFix.insertionBefore(s.tokens.front, " { ")
36+
.concat(AutoFix.insertionAfter(s.tokens.back, " } "));
37+
af.name = "Wrap in braces";
38+
39+
addErrorMessage(loc, KEY, stmtKind ~ MESSAGE_POSTFIX, [af]);
40+
}
41+
else
42+
{
43+
addErrorMessage(loc, KEY, stmtKind ~ MESSAGE_POSTFIX);
44+
}
45+
}
46+
}
47+
48+
override void visit(const(IfStatement) stmt)
49+
{
50+
auto s = stmt.thenStatement.statement;
51+
this.test(stmt.thenStatement, s, "if");
52+
if (stmt.elseStatement !is null)
53+
{
54+
auto e = stmt.elseStatement.statement;
55+
this.test(stmt.elseStatement, e, "else");
56+
}
57+
}
58+
59+
override void visit(const(ForStatement) stmt)
60+
{
61+
auto s = stmt.declarationOrStatement;
62+
if (s.statement !is null)
63+
{
64+
this.test(s, s, "for");
65+
}
66+
}
67+
68+
override void visit(const(ForeachStatement) stmt)
69+
{
70+
auto s = stmt.declarationOrStatement;
71+
if (s.statement !is null)
72+
{
73+
this.test(s, s, "foreach");
74+
}
75+
}
76+
77+
override void visit(const(TryStatement) stmt)
78+
{
79+
auto s = stmt.declarationOrStatement;
80+
if (s.statement !is null)
81+
{
82+
this.test(s, s, "try");
83+
}
84+
85+
if (stmt.catches !is null)
86+
{
87+
foreach (const(Catch) ct; stmt.catches.catches)
88+
{
89+
this.test(ct, ct.declarationOrStatement, "catch");
90+
}
91+
if (stmt.catches.lastCatch !is null)
92+
{
93+
auto sncnd = stmt.catches.lastCatch.statementNoCaseNoDefault;
94+
if (sncnd !is null)
95+
{
96+
this.test(stmt.catches.lastCatch, sncnd, "finally");
97+
}
98+
}
99+
}
100+
}
101+
102+
override void visit(const(WhileStatement) stmt)
103+
{
104+
auto s = stmt.declarationOrStatement;
105+
if (s.statement !is null)
106+
{
107+
this.test(s, s, "while");
108+
}
109+
}
110+
111+
override void visit(const(DoStatement) stmt)
112+
{
113+
auto s = stmt.statementNoCaseNoDefault;
114+
if (s !is null)
115+
{
116+
this.test(s, s, "do");
117+
}
118+
}
119+
120+
enum string KEY = "dscanner.style.always_curly";
121+
enum string MESSAGE_POSTFIX = " must be follow by a BlockStatement aka. { }";
122+
}
123+
124+
unittest
125+
{
126+
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
127+
import dscanner.analysis.helpers : assertAnalyzerWarnings, assertAutoFix;
128+
import std.stdio : stderr;
129+
130+
StaticAnalysisConfig sac = disabledConfig();
131+
sac.always_curly_check = Check.enabled;
132+
133+
assertAnalyzerWarnings(q{
134+
void testIf()
135+
{
136+
if(true) return; // [warn]: if must be follow by a BlockStatement aka. { }
137+
}
138+
}, sac);
139+
140+
assertAnalyzerWarnings(q{
141+
void testIf()
142+
{
143+
if(true) return; /+
144+
^^^^^^^ [warn]: if must be follow by a BlockStatement aka. { } +/
145+
}
146+
}, sac);
147+
148+
assertAnalyzerWarnings(q{
149+
void testIf()
150+
{
151+
for(int i = 0; i < 10; ++i) return; // [warn]: for must be follow by a BlockStatement aka. { }
152+
}
153+
}, sac);
154+
155+
assertAnalyzerWarnings(q{
156+
void testIf()
157+
{
158+
foreach(it; 0 .. 10) return; // [warn]: foreach must be follow by a BlockStatement aka. { }
159+
}
160+
}, sac);
161+
162+
assertAnalyzerWarnings(q{
163+
void testIf()
164+
{
165+
while(true) return; // [warn]: while must be follow by a BlockStatement aka. { }
166+
}
167+
}, sac);
168+
169+
assertAnalyzerWarnings(q{
170+
void testIf()
171+
{
172+
do return; while(true); return; // [warn]: do must be follow by a BlockStatement aka. { }
173+
}
174+
}, sac);
175+
}
176+
177+
unittest {
178+
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
179+
import dscanner.analysis.helpers : assertAnalyzerWarnings, assertAutoFix;
180+
import std.stdio : stderr;
181+
182+
StaticAnalysisConfig sac = disabledConfig();
183+
sac.always_curly_check = Check.enabled;
184+
185+
assertAutoFix(q{
186+
void test() {
187+
if(true) return; // fix:0
188+
}
189+
}c, q{
190+
void test() {
191+
if(true) { return; } // fix:0
192+
}
193+
}c, sac);
194+
195+
assertAutoFix(q{
196+
void test() {
197+
foreach(_; 0 .. 10 ) return; // fix:0
198+
}
199+
}c, q{
200+
void test() {
201+
foreach(_; 0 .. 10 ) { return; } // fix:0
202+
}
203+
}c, sac);
204+
205+
assertAutoFix(q{
206+
void test() {
207+
for(int i = 0; i < 10; ++i) return; // fix:0
208+
}
209+
}c, q{
210+
void test() {
211+
for(int i = 0; i < 10; ++i) { return; } // fix:0
212+
}
213+
}c, sac);
214+
215+
assertAutoFix(q{
216+
void test() {
217+
do return; while(true) // fix:0
218+
}
219+
}c, q{
220+
void test() {
221+
do { return; } while(true) // fix:0
222+
}
223+
}c, sac);
224+
225+
226+
stderr.writeln("Unittest for AlwaysCurly passed.");
227+
}

src/dscanner/analysis/config.d

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ struct StaticAnalysisConfig
167167
@INI("Check for auto function without return statement")
168168
string auto_function_check = Check.disabled;
169169

170+
@INI("Check that if|else|for|foreach|while|do|try|catch are always followed by a BlockStatement { }")
171+
string always_curly_check = Check.disabled;
172+
170173
@INI("Check for sortedness of imports")
171174
string imports_sortedness = Check.disabled;
172175

src/dscanner/analysis/run.d

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import dscanner.analysis.final_attribute;
7373
import dscanner.analysis.vcall_in_ctor;
7474
import dscanner.analysis.useless_initializer;
7575
import dscanner.analysis.allman;
76+
import dscanner.analysis.always_curly;
7677
import dscanner.analysis.redundant_attributes;
7778
import dscanner.analysis.has_public_example;
7879
import dscanner.analysis.assert_without_msg;
@@ -917,6 +918,10 @@ private BaseAnalyzer[] getAnalyzersForModuleAndConfig(string fileName,
917918
checks ~= new AllManCheck(fileName, tokens,
918919
analysisConfig.allman_braces_check == Check.skipTests && !ut);
919920

921+
if (moduleName.shouldRun!AlwaysCurlyCheck(analysisConfig))
922+
checks ~= new AlwaysCurlyCheck(fileName, tokens,
923+
analysisConfig.always_curly_check == Check.skipTests && !ut);
924+
920925
if (moduleName.shouldRun!RedundantAttributesCheck(analysisConfig))
921926
checks ~= new RedundantAttributesCheck(fileName, moduleScope,
922927
analysisConfig.redundant_attributes_check == Check.skipTests && !ut);

0 commit comments

Comments
 (0)