1 //          Copyright Brian Schott (Hackerpilot) 2014.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module dscanner.analysis.run;
7 
8 import core.memory : GC;
9 
10 import std.stdio;
11 import std.array;
12 import std.conv;
13 import std.algorithm;
14 import std.range;
15 import std.array;
16 import std.functional : toDelegate;
17 import dparse.lexer;
18 import dparse.parser;
19 import dparse.ast;
20 import dparse.rollback_allocator;
21 import std.typecons : scoped;
22 
23 import stdx.allocator : CAllocatorImpl;
24 import stdx.allocator.mallocator : Mallocator;
25 import stdx.allocator.building_blocks.region : Region;
26 import stdx.allocator.building_blocks.allocator_list : AllocatorList;
27 
28 import dscanner.analysis.config;
29 import dscanner.analysis.base;
30 import dscanner.analysis.style;
31 import dscanner.analysis.enumarrayliteral;
32 import dscanner.analysis.pokemon;
33 import dscanner.analysis.del;
34 import dscanner.analysis.fish;
35 import dscanner.analysis.numbers;
36 import dscanner.analysis.objectconst;
37 import dscanner.analysis.range;
38 import dscanner.analysis.ifelsesame;
39 import dscanner.analysis.constructors;
40 import dscanner.analysis.unused_variable;
41 import dscanner.analysis.unused_label;
42 import dscanner.analysis.unused_parameter;
43 import dscanner.analysis.duplicate_attribute;
44 import dscanner.analysis.opequals_without_tohash;
45 import dscanner.analysis.length_subtraction;
46 import dscanner.analysis.builtin_property_names;
47 import dscanner.analysis.asm_style;
48 import dscanner.analysis.logic_precedence;
49 import dscanner.analysis.stats_collector;
50 import dscanner.analysis.undocumented;
51 import dscanner.analysis.comma_expression;
52 import dscanner.analysis.function_attributes;
53 import dscanner.analysis.local_imports;
54 import dscanner.analysis.unmodified;
55 import dscanner.analysis.if_statements;
56 import dscanner.analysis.redundant_parens;
57 import dscanner.analysis.mismatched_args;
58 import dscanner.analysis.label_var_same_name_check;
59 import dscanner.analysis.line_length;
60 import dscanner.analysis.auto_ref_assignment;
61 import dscanner.analysis.incorrect_infinite_range;
62 import dscanner.analysis.useless_assert;
63 import dscanner.analysis.alias_syntax_check;
64 import dscanner.analysis.static_if_else;
65 import dscanner.analysis.lambda_return_check;
66 import dscanner.analysis.auto_function;
67 import dscanner.analysis.imports_sortedness;
68 import dscanner.analysis.explicitly_annotated_unittests;
69 import dscanner.analysis.properly_documented_public_functions;
70 import dscanner.analysis.final_attribute;
71 import dscanner.analysis.vcall_in_ctor;
72 import dscanner.analysis.useless_initializer;
73 import dscanner.analysis.allman;
74 import dscanner.analysis.redundant_attributes;
75 import dscanner.analysis.has_public_example;
76 import dscanner.analysis.assert_without_msg;
77 import dscanner.analysis.if_constraints_indent;
78 import dscanner.analysis.trust_too_much;
79 import dscanner.analysis.redundant_storage_class;
80 
81 import dsymbol.string_interning : internString;
82 import dsymbol.scope_;
83 import dsymbol.semantic;
84 import dsymbol.conversion;
85 import dsymbol.conversion.first;
86 import dsymbol.conversion.second;
87 import dsymbol.modulecache : ModuleCache;
88 
89 import dscanner.utils;
90 
91 bool first = true;
92 
93 private alias ASTAllocator = CAllocatorImpl!(
94 		AllocatorList!(n => Region!Mallocator(1024 * 128), Mallocator));
95 
96 immutable string defaultErrorFormat = "{filepath}({line}:{column})[{type}]: {message}";
97 
98 void messageFunctionFormat(string format, Message message, bool isError)
99 {
100 	auto s = format;
101 
102 	s = s.replace("{filepath}", message.fileName);
103 	s = s.replace("{line}", to!string(message.line));
104 	s = s.replace("{column}", to!string(message.column));
105 	s = s.replace("{type}", isError ? "error" : "warn");
106 	s = s.replace("{message}", message.message);
107 
108 	writefln("%s", s);
109 }
110 
111 void messageFunction(Message message, bool isError)
112 {
113 	messageFunctionFormat(defaultErrorFormat, message, isError);
114 }
115 
116 void messageFunctionJSON(string fileName, size_t line, size_t column, string message, bool)
117 {
118 	writeJSON(Message(fileName, line, column, "dscanner.syntax", message));
119 }
120 
121 void writeJSON(Message message)
122 {
123 	if (!first)
124 		writeln(",");
125 	else
126 		first = false;
127 	writeln("    {");
128 	writeln(`      "key": "`, message.key, `",`);
129 	if (message.checkName !is null)
130 	{
131 		writeln(`      "name": "`, message.checkName, `",`);
132 	}
133 	writeln(`      "fileName": "`, message.fileName.replace("\\", "\\\\").replace(`"`, `\"`), `",`);
134 	writeln(`      "line": `, message.line, `,`);
135 	writeln(`      "column": `, message.column, `,`);
136 	writeln(`      "message": "`, message.message.replace("\\", "\\\\").replace(`"`, `\"`), `"`);
137 	write("    }");
138 }
139 
140 bool syntaxCheck(string[] fileNames, string errorFormat, ref StringCache stringCache, ref ModuleCache moduleCache)
141 {
142 	StaticAnalysisConfig config = defaultStaticAnalysisConfig();
143 	return analyze(fileNames, config, errorFormat, stringCache, moduleCache, false);
144 }
145 
146 void generateReport(string[] fileNames, const StaticAnalysisConfig config,
147 		ref StringCache cache, ref ModuleCache moduleCache)
148 {
149 	writeln("{");
150 	writeln(`  "issues": [`);
151 	first = true;
152 	StatsCollector stats = new StatsCollector("");
153 	ulong lineOfCodeCount;
154 	foreach (fileName; fileNames)
155 	{
156 		auto code = readFile(fileName);
157 		// Skip files that could not be read and continue with the rest
158 		if (code.length == 0)
159 			continue;
160 		RollbackAllocator r;
161 		const(Token)[] tokens;
162 		const Module m = parseModule(fileName, code, &r, defaultErrorFormat, cache, true, tokens, &lineOfCodeCount);
163 		stats.visit(m);
164 		MessageSet results = analyze(fileName, m, config, moduleCache, tokens, true);
165 		foreach (result; results[])
166 		{
167 			writeJSON(result);
168 		}
169 	}
170 	writeln();
171 	writeln("  ],");
172 	writefln(`  "interfaceCount": %d,`, stats.interfaceCount);
173 	writefln(`  "classCount": %d,`, stats.classCount);
174 	writefln(`  "functionCount": %d,`, stats.functionCount);
175 	writefln(`  "templateCount": %d,`, stats.templateCount);
176 	writefln(`  "structCount": %d,`, stats.structCount);
177 	writefln(`  "statementCount": %d,`, stats.statementCount);
178 	writefln(`  "lineOfCodeCount": %d,`, lineOfCodeCount);
179 	writefln(`  "undocumentedPublicSymbols": %d`, stats.undocumentedPublicSymbols);
180 	writeln("}");
181 }
182 
183 /**
184  * For multiple files
185  *
186  * Returns: true if there were errors or if there were warnings and `staticAnalyze` was true.
187  */
188 bool analyze(string[] fileNames, const StaticAnalysisConfig config, string errorFormat,
189 		ref StringCache cache, ref ModuleCache moduleCache, bool staticAnalyze = true)
190 {
191 	bool hasErrors;
192 	foreach (fileName; fileNames)
193 	{
194 		auto code = readFile(fileName);
195 		// Skip files that could not be read and continue with the rest
196 		if (code.length == 0)
197 			continue;
198 		RollbackAllocator r;
199 		uint errorCount;
200 		uint warningCount;
201 		const(Token)[] tokens;
202 		const Module m = parseModule(fileName, code, &r, errorFormat, cache, false, tokens,
203 				null, &errorCount, &warningCount);
204 		assert(m);
205 		if (errorCount > 0 || (staticAnalyze && warningCount > 0))
206 			hasErrors = true;
207 		MessageSet results = analyze(fileName, m, config, moduleCache, tokens, staticAnalyze);
208 		if (results is null)
209 			continue;
210 		foreach (result; results[])
211 		{
212 			hasErrors = true;
213 			messageFunctionFormat(errorFormat, result, false);
214 		}
215 	}
216 	return hasErrors;
217 }
218 
219 const(Module) parseModule(string fileName, ubyte[] code, RollbackAllocator* p,
220 		string errorFormat, ref StringCache cache, bool report, ref const(Token)[] tokens,
221 		ulong* linesOfCode = null, uint* errorCount = null, uint* warningCount = null)
222 {
223 	import dscanner.stats : isLineOfCode;
224 
225 	auto writeMessages = delegate(string fileName, size_t line, size_t column, string message, bool isError){
226 		return messageFunctionFormat(errorFormat, Message(fileName, line, column, message), isError);
227 	};
228 
229 	LexerConfig config;
230 	config.fileName = fileName;
231 	config.stringBehavior = StringBehavior.source;
232 	tokens = getTokensForParser(code, config, &cache);
233 	if (linesOfCode !is null)
234 		(*linesOfCode) += count!(a => isLineOfCode(a.type))(tokens);
235 	return dparse.parser.parseModule(tokens, fileName, p,
236 		report ? toDelegate(&messageFunctionJSON) : writeMessages,
237 		errorCount, warningCount);
238 }
239 
240 /**
241 Checks whether a module is part of a user-specified include/exclude list.
242 The user can specify a comma-separated list of filters, everyone needs to start with
243 either a '+' (inclusion) or '-' (exclusion).
244 If no includes are specified, all modules are included.
245 */
246 bool shouldRun(check : BaseAnalyzer)(string moduleName, const ref StaticAnalysisConfig config)
247 {
248 	enum string a = check.name;
249 
250 	if (mixin("config." ~ a) == Check.disabled)
251 		return false;
252 
253 	// By default, run the check
254 	if (!moduleName.length)
255 		return true;
256 
257 	auto filters = mixin("config.filters." ~ a);
258 
259 	// Check if there are filters are defined
260 	// filters starting with a comma are invalid
261 	if (filters.length == 0 || filters[0].length == 0)
262 		return true;
263 
264 	auto includers = filters.filter!(f => f[0] == '+').map!(f => f[1..$]);
265 	auto excluders = filters.filter!(f => f[0] == '-').map!(f => f[1..$]);
266 
267 	// exclusion has preference over inclusion
268 	if (!excluders.empty && excluders.any!(s => moduleName.canFind(s)))
269 		return false;
270 
271 	if (!includers.empty)
272 		return includers.any!(s => moduleName.canFind(s));
273 
274 	// by default: include all modules
275 	return true;
276 }
277 
278 ///
279 unittest
280 {
281 	bool test(string moduleName, string filters)
282 	{
283 		StaticAnalysisConfig config;
284 		// it doesn't matter which check we test here
285 		config.asm_style_check = Check.enabled;
286 		// this is done automatically by inifiled
287 		config.filters.asm_style_check = filters.split(",");
288 		return shouldRun!AsmStyleCheck(moduleName, config);
289 	}
290 
291 	// test inclusion
292 	assert(test("std.foo", "+std."));
293 	// partial matches are ok
294 	assert(test("std.foo", "+bar,+foo"));
295 	// full as well
296 	assert(test("std.foo", "+bar,+std.foo,+foo"));
297 	// mismatch
298 	assert(!test("std.foo", "+bar,+banana"));
299 
300 	// test exclusion
301 	assert(!test("std.foo", "-std."));
302 	assert(!test("std.foo", "-bar,-std.foo"));
303 	assert(!test("std.foo", "-bar,-foo"));
304 	// mismatch
305 	assert(test("std.foo", "-bar,-banana"));
306 
307 	// test combination (exclusion has precedence)
308 	assert(!test("std.foo", "+foo,-foo"));
309 	assert(test("std.foo", "+foo,-bar"));
310 	assert(test("std.bar.foo", "-barr,+bar"));
311 }
312 
313 MessageSet analyze(string fileName, const Module m, const StaticAnalysisConfig analysisConfig,
314 		ref ModuleCache moduleCache, const(Token)[] tokens, bool staticAnalyze = true)
315 {
316 	import dsymbol.symbol : DSymbol;
317 
318 	if (!staticAnalyze)
319 		return null;
320 
321 	auto symbolAllocator = scoped!ASTAllocator();
322 	version (unittest)
323 		enum ut = true;
324 	else
325 		enum ut = false;
326 
327 	string moduleName;
328 	if (m !is null && m.moduleDeclaration !is null &&
329 		  m.moduleDeclaration.moduleName !is null &&
330 		  m.moduleDeclaration.moduleName.identifiers !is null)
331 		moduleName = m.moduleDeclaration.moduleName.identifiers.map!(e => e.text).join(".");
332 
333 	auto first = scoped!FirstPass(m, internString(fileName), symbolAllocator,
334 			symbolAllocator, true, &moduleCache, null);
335 	first.run();
336 
337 	secondPass(first.rootSymbol, first.moduleScope, moduleCache);
338 	auto moduleScope = first.moduleScope;
339 	scope(exit) typeid(DSymbol).destroy(first.rootSymbol.acSymbol);
340 	scope(exit) typeid(SemanticSymbol).destroy(first.rootSymbol);
341 	scope(exit) typeid(Scope).destroy(first.moduleScope);
342 	BaseAnalyzer[] checks;
343 
344 	GC.disable;
345 
346 	if (moduleName.shouldRun!AsmStyleCheck(analysisConfig))
347 		checks ~= new AsmStyleCheck(fileName, moduleScope,
348 		analysisConfig.asm_style_check == Check.skipTests && !ut);
349 
350 	if (moduleName.shouldRun!BackwardsRangeCheck(analysisConfig))
351 		checks ~= new BackwardsRangeCheck(fileName, moduleScope,
352 		analysisConfig.backwards_range_check == Check.skipTests && !ut);
353 
354 	if (moduleName.shouldRun!BuiltinPropertyNameCheck(analysisConfig))
355 		checks ~= new BuiltinPropertyNameCheck(fileName, moduleScope,
356 		analysisConfig.builtin_property_names_check == Check.skipTests && !ut);
357 
358 	if (moduleName.shouldRun!CommaExpressionCheck(analysisConfig))
359 		checks ~= new CommaExpressionCheck(fileName, moduleScope,
360 		analysisConfig.comma_expression_check == Check.skipTests && !ut);
361 
362 	if (moduleName.shouldRun!ConstructorCheck(analysisConfig))
363 		checks ~= new ConstructorCheck(fileName, moduleScope,
364 		analysisConfig.constructor_check == Check.skipTests && !ut);
365 
366 	if (moduleName.shouldRun!UnmodifiedFinder(analysisConfig))
367 		checks ~= new UnmodifiedFinder(fileName, moduleScope,
368 		analysisConfig.could_be_immutable_check == Check.skipTests && !ut);
369 
370 	if (moduleName.shouldRun!DeleteCheck(analysisConfig))
371 		checks ~= new DeleteCheck(fileName, moduleScope,
372 		analysisConfig.delete_check == Check.skipTests && !ut);
373 
374 	if (moduleName.shouldRun!DuplicateAttributeCheck(analysisConfig))
375 		checks ~= new DuplicateAttributeCheck(fileName, moduleScope,
376 		analysisConfig.duplicate_attribute == Check.skipTests && !ut);
377 
378 	if (moduleName.shouldRun!EnumArrayLiteralCheck(analysisConfig))
379 		checks ~= new EnumArrayLiteralCheck(fileName, moduleScope,
380 		analysisConfig.enum_array_literal_check == Check.skipTests && !ut);
381 
382 	if (moduleName.shouldRun!PokemonExceptionCheck(analysisConfig))
383 		checks ~= new PokemonExceptionCheck(fileName, moduleScope,
384 		analysisConfig.exception_check == Check.skipTests && !ut);
385 
386 	if (moduleName.shouldRun!FloatOperatorCheck(analysisConfig))
387 		checks ~= new FloatOperatorCheck(fileName, moduleScope,
388 		analysisConfig.float_operator_check == Check.skipTests && !ut);
389 
390 	if (moduleName.shouldRun!FunctionAttributeCheck(analysisConfig))
391 		checks ~= new FunctionAttributeCheck(fileName, moduleScope,
392 		analysisConfig.function_attribute_check == Check.skipTests && !ut);
393 
394 	if (moduleName.shouldRun!IfElseSameCheck(analysisConfig))
395 		checks ~= new IfElseSameCheck(fileName, moduleScope,
396 		analysisConfig.if_else_same_check == Check.skipTests&& !ut);
397 
398 	if (moduleName.shouldRun!LabelVarNameCheck(analysisConfig))
399 		checks ~= new LabelVarNameCheck(fileName, moduleScope,
400 		analysisConfig.label_var_same_name_check == Check.skipTests && !ut);
401 
402 	if (moduleName.shouldRun!LengthSubtractionCheck(analysisConfig))
403 		checks ~= new LengthSubtractionCheck(fileName, moduleScope,
404 		analysisConfig.length_subtraction_check == Check.skipTests && !ut);
405 
406 	if (moduleName.shouldRun!LocalImportCheck(analysisConfig))
407 		checks ~= new LocalImportCheck(fileName, moduleScope,
408 		analysisConfig.local_import_check == Check.skipTests && !ut);
409 
410 	if (moduleName.shouldRun!LogicPrecedenceCheck(analysisConfig))
411 		checks ~= new LogicPrecedenceCheck(fileName, moduleScope,
412 		analysisConfig.logical_precedence_check == Check.skipTests && !ut);
413 
414 	if (moduleName.shouldRun!MismatchedArgumentCheck(analysisConfig))
415 		checks ~= new MismatchedArgumentCheck(fileName, moduleScope,
416 		analysisConfig.mismatched_args_check == Check.skipTests && !ut);
417 
418 	if (moduleName.shouldRun!NumberStyleCheck(analysisConfig))
419 		checks ~= new NumberStyleCheck(fileName, moduleScope,
420 		analysisConfig.number_style_check == Check.skipTests && !ut);
421 
422 	if (moduleName.shouldRun!ObjectConstCheck(analysisConfig))
423 		checks ~= new ObjectConstCheck(fileName, moduleScope,
424 		analysisConfig.object_const_check == Check.skipTests && !ut);
425 
426 	if (moduleName.shouldRun!OpEqualsWithoutToHashCheck(analysisConfig))
427 		checks ~= new OpEqualsWithoutToHashCheck(fileName, moduleScope,
428 		analysisConfig.opequals_tohash_check == Check.skipTests && !ut);
429 
430 	if (moduleName.shouldRun!RedundantParenCheck(analysisConfig))
431 		checks ~= new RedundantParenCheck(fileName, moduleScope,
432 		analysisConfig.redundant_parens_check == Check.skipTests && !ut);
433 
434 	if (moduleName.shouldRun!StyleChecker(analysisConfig))
435 		checks ~= new StyleChecker(fileName, moduleScope,
436 		analysisConfig.style_check == Check.skipTests && !ut);
437 
438 	if (moduleName.shouldRun!UndocumentedDeclarationCheck(analysisConfig))
439 		checks ~= new UndocumentedDeclarationCheck(fileName, moduleScope,
440 		analysisConfig.undocumented_declaration_check == Check.skipTests && !ut);
441 
442 	if (moduleName.shouldRun!UnusedLabelCheck(analysisConfig))
443 		checks ~= new UnusedLabelCheck(fileName, moduleScope,
444 		analysisConfig.unused_label_check == Check.skipTests && !ut);
445 
446 	if (moduleName.shouldRun!UnusedVariableCheck(analysisConfig))
447 		checks ~= new UnusedVariableCheck(fileName, moduleScope,
448 		analysisConfig.unused_variable_check == Check.skipTests && !ut);
449 
450 	if (moduleName.shouldRun!UnusedParameterCheck(analysisConfig))
451 		checks ~= new UnusedParameterCheck(fileName, moduleScope,
452 		analysisConfig.unused_parameter_check == Check.skipTests && !ut);
453 
454 	if (moduleName.shouldRun!LineLengthCheck(analysisConfig))
455 		checks ~= new LineLengthCheck(fileName, tokens,
456 		analysisConfig.long_line_check == Check.skipTests && !ut);
457 
458 	if (moduleName.shouldRun!AutoRefAssignmentCheck(analysisConfig))
459 		checks ~= new AutoRefAssignmentCheck(fileName,
460 		analysisConfig.auto_ref_assignment_check == Check.skipTests && !ut);
461 
462 	if (moduleName.shouldRun!IncorrectInfiniteRangeCheck(analysisConfig))
463 		checks ~= new IncorrectInfiniteRangeCheck(fileName,
464 		analysisConfig.incorrect_infinite_range_check == Check.skipTests && !ut);
465 
466 	if (moduleName.shouldRun!UselessAssertCheck(analysisConfig))
467 		checks ~= new UselessAssertCheck(fileName,
468 		analysisConfig.useless_assert_check == Check.skipTests && !ut);
469 
470 	if (moduleName.shouldRun!AliasSyntaxCheck(analysisConfig))
471 		checks ~= new AliasSyntaxCheck(fileName,
472 		analysisConfig.alias_syntax_check == Check.skipTests && !ut);
473 
474 	if (moduleName.shouldRun!StaticIfElse(analysisConfig))
475 		checks ~= new StaticIfElse(fileName,
476 		analysisConfig.static_if_else_check == Check.skipTests && !ut);
477 
478 	if (moduleName.shouldRun!LambdaReturnCheck(analysisConfig))
479 		checks ~= new LambdaReturnCheck(fileName,
480 		analysisConfig.lambda_return_check == Check.skipTests && !ut);
481 
482 	if (moduleName.shouldRun!AutoFunctionChecker(analysisConfig))
483 		checks ~= new AutoFunctionChecker(fileName,
484 		analysisConfig.auto_function_check == Check.skipTests && !ut);
485 
486 	if (moduleName.shouldRun!ImportSortednessCheck(analysisConfig))
487 		checks ~= new ImportSortednessCheck(fileName,
488 		analysisConfig.imports_sortedness == Check.skipTests && !ut);
489 
490 	if (moduleName.shouldRun!ExplicitlyAnnotatedUnittestCheck(analysisConfig))
491 		checks ~= new ExplicitlyAnnotatedUnittestCheck(fileName,
492 		analysisConfig.explicitly_annotated_unittests == Check.skipTests && !ut);
493 
494 	if (moduleName.shouldRun!ProperlyDocumentedPublicFunctions(analysisConfig))
495 		checks ~= new ProperlyDocumentedPublicFunctions(fileName,
496 		analysisConfig.properly_documented_public_functions == Check.skipTests && !ut);
497 
498 	if (moduleName.shouldRun!FinalAttributeChecker(analysisConfig))
499 		checks ~= new FinalAttributeChecker(fileName,
500 		analysisConfig.final_attribute_check == Check.skipTests && !ut);
501 
502 	if (moduleName.shouldRun!VcallCtorChecker(analysisConfig))
503 		checks ~= new VcallCtorChecker(fileName,
504 		analysisConfig.vcall_in_ctor == Check.skipTests && !ut);
505 
506 	if (moduleName.shouldRun!UselessInitializerChecker(analysisConfig))
507 		checks ~= new UselessInitializerChecker(fileName,
508 		analysisConfig.useless_initializer == Check.skipTests && !ut);
509 
510 	if (moduleName.shouldRun!AllManCheck(analysisConfig))
511 		checks ~= new AllManCheck(fileName, tokens,
512 		analysisConfig.allman_braces_check == Check.skipTests && !ut);
513 
514 	if (moduleName.shouldRun!RedundantAttributesCheck(analysisConfig))
515 		checks ~= new RedundantAttributesCheck(fileName, moduleScope,
516 		analysisConfig.redundant_attributes_check == Check.skipTests && !ut);
517 
518 	if (moduleName.shouldRun!HasPublicExampleCheck(analysisConfig))
519 		checks ~= new HasPublicExampleCheck(fileName, moduleScope,
520 		analysisConfig.has_public_example == Check.skipTests && !ut);
521 
522 	if (moduleName.shouldRun!AssertWithoutMessageCheck(analysisConfig))
523 		checks ~= new AssertWithoutMessageCheck(fileName, moduleScope,
524 		analysisConfig.assert_without_msg == Check.skipTests && !ut);
525 
526 	if (moduleName.shouldRun!IfConstraintsIndentCheck(analysisConfig))
527 		checks ~= new IfConstraintsIndentCheck(fileName, tokens,
528 		analysisConfig.if_constraints_indent == Check.skipTests && !ut);
529 
530 	if (moduleName.shouldRun!TrustTooMuchCheck(analysisConfig))
531 		checks ~= new TrustTooMuchCheck(fileName,
532 		analysisConfig.trust_too_much == Check.skipTests && !ut);
533 
534 	if (moduleName.shouldRun!RedundantStorageClassCheck(analysisConfig))
535 		checks ~= new RedundantStorageClassCheck(fileName,
536 		analysisConfig.redundant_storage_classes == Check.skipTests && !ut);
537 
538 	version (none)
539 		if (moduleName.shouldRun!IfStatementCheck(analysisConfig))
540 			checks ~= new IfStatementCheck(fileName, moduleScope,
541 			analysisConfig.redundant_if_check == Check.skipTests && !ut);
542 
543 	MessageSet set = new MessageSet;
544 	foreach (check; checks)
545 	{
546 		check.visit(m);
547 		foreach (message; check.messages)
548 			set.insert(message);
549 	}
550 
551 	GC.enable;
552 
553 	return set;
554 }
555