@@ -11,7 +11,7 @@ import dparse.ast;
1111import dparse.lexer;
1212
1313import std.stdio ;
14- import std.algorithm.searching : any ;
14+ import std.algorithm : map, filter ;
1515
1616/**
1717 * Checks for auto functions without return statement.
@@ -26,7 +26,8 @@ final class AutoFunctionChecker : BaseAnalyzer
2626private :
2727
2828 enum string KEY = " dscanner.suspicious.missing_return" ;
29- enum string MESSAGE = " Auto function without return statement, prefer an explicit void" ;
29+ enum string MESSAGE = " Auto function without return statement, prefer replacing auto with void" ;
30+ enum string MESSAGE_INSERT = " Auto function without return statement, prefer inserting void to be explicit" ;
3031
3132 bool [] _returns;
3233 size_t _mixinDepth;
@@ -44,19 +45,41 @@ public:
4445 super (fileName, null , skipTests);
4546 }
4647
48+ package static const (Token )[] findAutoReturnType(const (FunctionDeclaration) decl)
49+ {
50+ auto autoFunTokens = decl.storageClasses
51+ .map! (a => a.token.type == tok! " auto"
52+ ? [a.token]
53+ : a.atAttribute
54+ ? a.atAttribute.tokens
55+ : null )
56+ .filter! (a => a.length > 0 );
57+ return autoFunTokens.empty ? null : autoFunTokens.front;
58+ }
59+
4760 override void visit (const (FunctionDeclaration) decl)
4861 {
4962 _returns.length += 1 ;
5063 scope (exit) _returns.length -= 1 ;
5164 _returns[$- 1 ] = false ;
5265
53- const bool autoFun = decl.storageClasses
54- .any ! (a => a.token.type == tok ! " auto " || a.atAttribute ! is null ) ;
66+ auto autoTokens = findAutoReturnType( decl);
67+ bool isAtAttribute = autoTokens.length > 1 ;
5568
5669 decl.accept(this );
5770
58- if (decl.functionBody.specifiedFunctionBody && autoFun && ! _returns[$- 1 ])
59- addErrorMessage(decl.name.line, decl.name.column, KEY , MESSAGE );
71+ if (decl.functionBody.specifiedFunctionBody && autoTokens.length && ! _returns[$- 1 ])
72+ {
73+ if (isAtAttribute)
74+ {
75+ // highlight on the whitespace between attribute and function name
76+ auto tok = autoTokens[$ - 1 ];
77+ auto whitespace = tok.column + (tok.text.length ? tok.text.length : str(tok.type).length);
78+ addErrorMessage(tok.line, whitespace, whitespace + 1 , KEY , MESSAGE_INSERT );
79+ }
80+ else
81+ addErrorMessage(autoTokens, KEY , MESSAGE );
82+ }
6083 }
6184
6285 override void visit (const (ReturnStatement) rst)
@@ -165,9 +188,12 @@ unittest
165188 StaticAnalysisConfig sac = disabledConfig();
166189 sac.auto_function_check = Check.enabled;
167190 assertAnalyzerWarnings(q{
168- auto ref doStuff (){} // [warn]: %s
169- auto doStuff (){} // [warn]: %s
170- int doStuff (){auto doStuff(){}} // [warn]: %s
191+ auto ref doStuff (){} /+
192+ ^^^^ [warn]: %s +/
193+ auto doStuff (){} /+
194+ ^^^^ [warn]: %s +/
195+ int doStuff (){auto doStuff(){}} /+
196+ ^^^^ [warn]: %s +/
171197 auto doStuff (){return 0 ;}
172198 int doStuff (){/* error but not the aim*/ }
173199 }c.format(
@@ -177,55 +203,63 @@ unittest
177203 ), sac);
178204
179205 assertAnalyzerWarnings(q{
180- auto doStuff (){assert (true );} // [warn]: %s
206+ auto doStuff (){assert (true );} /+
207+ ^^^^ [warn]: %s +/
181208 auto doStuff (){assert (false );}
182209 }c.format(
183210 AutoFunctionChecker.MESSAGE ,
184211 ), sac);
185212
186213 assertAnalyzerWarnings(q{
187- auto doStuff (){assert (1 );} // [warn]: %s
214+ auto doStuff (){assert (1 );} /+
215+ ^^^^ [warn]: %s +/
188216 auto doStuff (){assert (0 );}
189217 }c.format(
190218 AutoFunctionChecker.MESSAGE ,
191219 ), sac);
192220
193221 assertAnalyzerWarnings(q{
194- auto doStuff (){mixin (" 0+0" );} // [warn]: %s
222+ auto doStuff (){mixin (" 0+0" );} /+
223+ ^^^^ [warn]: %s +/
195224 auto doStuff (){mixin (" return 0;" );}
196225 }c.format(
197226 AutoFunctionChecker.MESSAGE ,
198227 ), sac);
199228
200229 assertAnalyzerWarnings(q{
201- auto doStuff (){mixin (" 0+0" );} // [warn]: %s
230+ auto doStuff (){mixin (" 0+0" );} /+
231+ ^^^^ [warn]: %s +/
202232 auto doStuff (){mixin (" static if (true)" ~ " return " ~ 0. stringof ~ " ;" );}
203233 }c.format(
204234 AutoFunctionChecker.MESSAGE ,
205235 ), sac);
206236
207237 assertAnalyzerWarnings(q{
208- auto doStuff (){} // [warn]: %s
238+ auto doStuff (){} /+
239+ ^^^^ [warn]: %s +/
209240 extern (C ) auto doStuff();
210241 }c.format(
211242 AutoFunctionChecker.MESSAGE ,
212243 ), sac);
213244
214245 assertAnalyzerWarnings(q{
215- auto doStuff (){} // [warn]: %s
246+ auto doStuff (){} /+
247+ ^^^^ [warn]: %s +/
216248 @disable auto doStuff();
217249 }c.format(
218250 AutoFunctionChecker.MESSAGE ,
219251 ), sac);
220252
221253 assertAnalyzerWarnings(q{
222- @property doStuff(){} // [warn]: %s
223- @safe doStuff(){} // [warn]: %s
254+ @property doStuff(){} /+
255+ ^ [warn]: %s +/
256+ @safe doStuff(){} /+
257+ ^ [warn]: %s +/
224258 @disable doStuff();
225259 @safe void doStuff();
226260 }c.format(
227- AutoFunctionChecker.MESSAGE ,
228- AutoFunctionChecker.MESSAGE ,
261+ AutoFunctionChecker.MESSAGE_INSERT ,
262+ AutoFunctionChecker.MESSAGE_INSERT ,
229263 ), sac);
230264
231265 assertAnalyzerWarnings(q{
0 commit comments