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 import dscanner.reports : SonarQubeGenericIssueDataReporter;
91 
92 bool first = true;
93 
94 private alias ASTAllocator = CAllocatorImpl!(
95 		AllocatorList!(n => Region!Mallocator(1024 * 128), Mallocator));
96 
97 immutable string defaultErrorFormat = "{filepath}({line}:{column})[{type}]: {message}";
98 
99 void messageFunctionFormat(string format, Message message, bool isError)
100 {
101 	auto s = format;
102 
103 	s = s.replace("{filepath}", message.fileName);
104 	s = s.replace("{line}", to!string(message.line));
105 	s = s.replace("{column}", to!string(message.column));
106 	s = s.replace("{type}", isError ? "error" : "warn");
107 	s = s.replace("{message}", message.message);
108     s = s.replace("{name}", message.checkName);
109 
110 	writefln("%s", s);
111 }
112 
113 void messageFunction(Message message, bool isError)
114 {
115 	messageFunctionFormat(defaultErrorFormat, message, isError);
116 }
117 
118 void messageFunctionJSON(string fileName, size_t line, size_t column, string message, bool)
119 {
120 	writeJSON(Message(fileName, line, column, "dscanner.syntax", message));
121 }
122 
123 void writeJSON(Message message)
124 {
125 	if (!first)
126 		writeln(",");
127 	else
128 		first = false;
129 	writeln("    {");
130 	writeln(`      "key": "`, message.key, `",`);
131 	if (message.checkName !is null)
132 	{
133 		writeln(`      "name": "`, message.checkName, `",`);
134 	}
135 	writeln(`      "fileName": "`, message.fileName.replace("\\", "\\\\").replace(`"`, `\"`), `",`);
136 	writeln(`      "line": `, message.line, `,`);
137 	writeln(`      "column": `, message.column, `,`);
138 	writeln(`      "message": "`, message.message.replace("\\", "\\\\").replace(`"`, `\"`), `"`);
139 	write("    }");
140 }
141 
142 bool syntaxCheck(string[] fileNames, string errorFormat, ref StringCache stringCache, ref ModuleCache moduleCache)
143 {
144 	StaticAnalysisConfig config = defaultStaticAnalysisConfig();
145 	return analyze(fileNames, config, errorFormat, stringCache, moduleCache, false);
146 }
147 
148 void generateReport(string[] fileNames, const StaticAnalysisConfig config,
149 		ref StringCache cache, ref ModuleCache moduleCache)
150 {
151 	writeln("{");
152 	writeln(`  "issues": [`);
153 	first = true;
154 	StatsCollector stats = new StatsCollector("");
155 	ulong lineOfCodeCount;
156 	foreach (fileName; fileNames)
157 	{
158 		auto code = readFile(fileName);
159 		// Skip files that could not be read and continue with the rest
160 		if (code.length == 0)
161 			continue;
162 		RollbackAllocator r;
163 		const(Token)[] tokens;
164 		const Module m = parseModule(fileName, code, &r, defaultErrorFormat, cache, true, tokens, &lineOfCodeCount);
165 		stats.visit(m);
166 		MessageSet results = analyze(fileName, m, config, moduleCache, tokens, true);
167 		foreach (result; results[])
168 		{
169 			writeJSON(result);
170 		}
171 	}
172 	writeln();
173 	writeln("  ],");
174 	writefln(`  "interfaceCount": %d,`, stats.interfaceCount);
175 	writefln(`  "classCount": %d,`, stats.classCount);
176 	writefln(`  "functionCount": %d,`, stats.functionCount);
177 	writefln(`  "templateCount": %d,`, stats.templateCount);
178 	writefln(`  "structCount": %d,`, stats.structCount);
179 	writefln(`  "statementCount": %d,`, stats.statementCount);
180 	writefln(`  "lineOfCodeCount": %d,`, lineOfCodeCount);
181 	writefln(`  "undocumentedPublicSymbols": %d`, stats.undocumentedPublicSymbols);
182 	writeln("}");
183 }
184 
185 void generateSonarQubeGenericIssueDataReport(string[] fileNames, const StaticAnalysisConfig config,
186 		ref StringCache cache, ref ModuleCache moduleCache)
187 {
188 	auto reporter = new SonarQubeGenericIssueDataReporter();
189 
190 	auto writeMessages = delegate void(string fileName, size_t line, size_t column, string message, bool isError){
191 		reporter.addMessage(Message(fileName, line, column, "dscanner.syntax", message), isError);
192 	};
193 
194 	foreach (fileName; fileNames)
195 	{
196 		auto code = readFile(fileName);
197 		// Skip files that could not be read and continue with the rest
198 		if (code.length == 0)
199 			continue;
200 		RollbackAllocator r;
201 		const(Token)[] tokens;
202 		const Module m = parseModule(fileName, code, &r, cache, tokens, writeMessages, null, null, null);
203 		MessageSet messageSet = analyze(fileName, m, config, moduleCache, tokens, true);
204 		reporter.addMessageSet(messageSet);
205 	}
206 
207 	writeln(reporter.getContent());
208 }
209 
210 /**
211  * For multiple files
212  *
213  * Returns: true if there were errors or if there were warnings and `staticAnalyze` was true.
214  */
215 bool analyze(string[] fileNames, const StaticAnalysisConfig config, string errorFormat,
216 		ref StringCache cache, ref ModuleCache moduleCache, bool staticAnalyze = true)
217 {
218 	bool hasErrors;
219 	foreach (fileName; fileNames)
220 	{
221 		auto code = readFile(fileName);
222 		// Skip files that could not be read and continue with the rest
223 		if (code.length == 0)
224 			continue;
225 		RollbackAllocator r;
226 		uint errorCount;
227 		uint warningCount;
228 		const(Token)[] tokens;
229 		const Module m = parseModule(fileName, code, &r, errorFormat, cache, false, tokens,
230 				null, &errorCount, &warningCount);
231 		assert(m);
232 		if (errorCount > 0 || (staticAnalyze && warningCount > 0))
233 			hasErrors = true;
234 		MessageSet results = analyze(fileName, m, config, moduleCache, tokens, staticAnalyze);
235 		if (results is null)
236 			continue;
237 		foreach (result; results[])
238 		{
239 			hasErrors = true;
240 			messageFunctionFormat(errorFormat, result, false);
241 		}
242 	}
243 	return hasErrors;
244 }
245 
246 const(Module) parseModule(string fileName, ubyte[] code, RollbackAllocator* p,
247 		ref StringCache cache, ref const(Token)[] tokens,
248 		MessageDelegate dlgMessage, ulong* linesOfCode = null,
249 		uint* errorCount = null, uint* warningCount = null)
250 {
251 	import dscanner.stats : isLineOfCode;
252 
253 	LexerConfig config;
254 	config.fileName = fileName;
255 	config.stringBehavior = StringBehavior.source;
256 	tokens = getTokensForParser(code, config, &cache);
257 	if (linesOfCode !is null)
258 		(*linesOfCode) += count!(a => isLineOfCode(a.type))(tokens);
259 
260 	return dparse.parser.parseModule(tokens, fileName, p, dlgMessage, errorCount, warningCount);
261 }
262 
263 const(Module) parseModule(string fileName, ubyte[] code, RollbackAllocator* p,
264 		string errorFormat, ref StringCache cache, bool report, ref const(Token)[] tokens,
265 		ulong* linesOfCode = null, uint* errorCount = null, uint* warningCount = null)
266 {
267 	auto writeMessages = delegate(string fileName, size_t line, size_t column, string message, bool isError){
268 		return messageFunctionFormat(errorFormat, Message(fileName, line, column, "dscanner.syntax", message), isError);
269 	};
270 
271 	return parseModule(fileName, code, p, cache, tokens,
272 		report ? toDelegate(&messageFunctionJSON) : writeMessages,
273 		linesOfCode, errorCount, warningCount);
274 }
275 
276 /**
277 Checks whether a module is part of a user-specified include/exclude list.
278 The user can specify a comma-separated list of filters, everyone needs to start with
279 either a '+' (inclusion) or '-' (exclusion).
280 If no includes are specified, all modules are included.
281 */
282 bool shouldRun(check : BaseAnalyzer)(string moduleName, const ref StaticAnalysisConfig config)
283 {
284 	enum string a = check.name;
285 
286 	if (mixin("config." ~ a) == Check.disabled)
287 		return false;
288 
289 	// By default, run the check
290 	if (!moduleName.length)
291 		return true;
292 
293 	auto filters = mixin("config.filters." ~ a);
294 
295 	// Check if there are filters are defined
296 	// filters starting with a comma are invalid
297 	if (filters.length == 0 || filters[0].length == 0)
298 		return true;
299 
300 	auto includers = filters.filter!(f => f[0] == '+').map!(f => f[1..$]);
301 	auto excluders = filters.filter!(f => f[0] == '-').map!(f => f[1..$]);
302 
303 	// exclusion has preference over inclusion
304 	if (!excluders.empty && excluders.any!(s => moduleName.canFind(s)))
305 		return false;
306 
307 	if (!includers.empty)
308 		return includers.any!(s => moduleName.canFind(s));
309 
310 	// by default: include all modules
311 	return true;
312 }
313 
314 ///
315 unittest
316 {
317 	bool test(string moduleName, string filters)
318 	{
319 		StaticAnalysisConfig config;
320 		// it doesn't matter which check we test here
321 		config.asm_style_check = Check.enabled;
322 		// this is done automatically by inifiled
323 		config.filters.asm_style_check = filters.split(",");
324 		return shouldRun!AsmStyleCheck(moduleName, config);
325 	}
326 
327 	// test inclusion
328 	assert(test("std.foo", "+std."));
329 	// partial matches are ok
330 	assert(test("std.foo", "+bar,+foo"));
331 	// full as well
332 	assert(test("std.foo", "+bar,+std.foo,+foo"));
333 	// mismatch
334 	assert(!test("std.foo", "+bar,+banana"));
335 
336 	// test exclusion
337 	assert(!test("std.foo", "-std."));
338 	assert(!test("std.foo", "-bar,-std.foo"));
339 	assert(!test("std.foo", "-bar,-foo"));
340 	// mismatch
341 	assert(test("std.foo", "-bar,-banana"));
342 
343 	// test combination (exclusion has precedence)
344 	assert(!test("std.foo", "+foo,-foo"));
345 	assert(test("std.foo", "+foo,-bar"));
346 	assert(test("std.bar.foo", "-barr,+bar"));
347 }
348 
349 MessageSet analyze(string fileName, const Module m, const StaticAnalysisConfig analysisConfig,
350 		ref ModuleCache moduleCache, const(Token)[] tokens, bool staticAnalyze = true)
351 {
352 	import dsymbol.symbol : DSymbol;
353 
354 	if (!staticAnalyze)
355 		return null;
356 
357 	auto symbolAllocator = scoped!ASTAllocator();
358 	version (unittest)
359 		enum ut = true;
360 	else
361 		enum ut = false;
362 
363 	string moduleName;
364 	if (m !is null && m.moduleDeclaration !is null &&
365 		  m.moduleDeclaration.moduleName !is null &&
366 		  m.moduleDeclaration.moduleName.identifiers !is null)
367 		moduleName = m.moduleDeclaration.moduleName.identifiers.map!(e => e.text).join(".");
368 
369 	auto first = scoped!FirstPass(m, internString(fileName), symbolAllocator,
370 			symbolAllocator, true, &moduleCache, null);
371 	first.run();
372 
373 	secondPass(first.rootSymbol, first.moduleScope, moduleCache);
374 	auto moduleScope = first.moduleScope;
375 	scope(exit) typeid(DSymbol).destroy(first.rootSymbol.acSymbol);
376 	scope(exit) typeid(SemanticSymbol).destroy(first.rootSymbol);
377 	scope(exit) typeid(Scope).destroy(first.moduleScope);
378 	BaseAnalyzer[] checks;
379 
380 	GC.disable;
381 
382 	if (moduleName.shouldRun!AsmStyleCheck(analysisConfig))
383 		checks ~= new AsmStyleCheck(fileName, moduleScope,
384 		analysisConfig.asm_style_check == Check.skipTests && !ut);
385 
386 	if (moduleName.shouldRun!BackwardsRangeCheck(analysisConfig))
387 		checks ~= new BackwardsRangeCheck(fileName, moduleScope,
388 		analysisConfig.backwards_range_check == Check.skipTests && !ut);
389 
390 	if (moduleName.shouldRun!BuiltinPropertyNameCheck(analysisConfig))
391 		checks ~= new BuiltinPropertyNameCheck(fileName, moduleScope,
392 		analysisConfig.builtin_property_names_check == Check.skipTests && !ut);
393 
394 	if (moduleName.shouldRun!CommaExpressionCheck(analysisConfig))
395 		checks ~= new CommaExpressionCheck(fileName, moduleScope,
396 		analysisConfig.comma_expression_check == Check.skipTests && !ut);
397 
398 	if (moduleName.shouldRun!ConstructorCheck(analysisConfig))
399 		checks ~= new ConstructorCheck(fileName, moduleScope,
400 		analysisConfig.constructor_check == Check.skipTests && !ut);
401 
402 	if (moduleName.shouldRun!UnmodifiedFinder(analysisConfig))
403 		checks ~= new UnmodifiedFinder(fileName, moduleScope,
404 		analysisConfig.could_be_immutable_check == Check.skipTests && !ut);
405 
406 	if (moduleName.shouldRun!DeleteCheck(analysisConfig))
407 		checks ~= new DeleteCheck(fileName, moduleScope,
408 		analysisConfig.delete_check == Check.skipTests && !ut);
409 
410 	if (moduleName.shouldRun!DuplicateAttributeCheck(analysisConfig))
411 		checks ~= new DuplicateAttributeCheck(fileName, moduleScope,
412 		analysisConfig.duplicate_attribute == Check.skipTests && !ut);
413 
414 	if (moduleName.shouldRun!EnumArrayLiteralCheck(analysisConfig))
415 		checks ~= new EnumArrayLiteralCheck(fileName, moduleScope,
416 		analysisConfig.enum_array_literal_check == Check.skipTests && !ut);
417 
418 	if (moduleName.shouldRun!PokemonExceptionCheck(analysisConfig))
419 		checks ~= new PokemonExceptionCheck(fileName, moduleScope,
420 		analysisConfig.exception_check == Check.skipTests && !ut);
421 
422 	if (moduleName.shouldRun!FloatOperatorCheck(analysisConfig))
423 		checks ~= new FloatOperatorCheck(fileName, moduleScope,
424 		analysisConfig.float_operator_check == Check.skipTests && !ut);
425 
426 	if (moduleName.shouldRun!FunctionAttributeCheck(analysisConfig))
427 		checks ~= new FunctionAttributeCheck(fileName, moduleScope,
428 		analysisConfig.function_attribute_check == Check.skipTests && !ut);
429 
430 	if (moduleName.shouldRun!IfElseSameCheck(analysisConfig))
431 		checks ~= new IfElseSameCheck(fileName, moduleScope,
432 		analysisConfig.if_else_same_check == Check.skipTests&& !ut);
433 
434 	if (moduleName.shouldRun!LabelVarNameCheck(analysisConfig))
435 		checks ~= new LabelVarNameCheck(fileName, moduleScope,
436 		analysisConfig.label_var_same_name_check == Check.skipTests && !ut);
437 
438 	if (moduleName.shouldRun!LengthSubtractionCheck(analysisConfig))
439 		checks ~= new LengthSubtractionCheck(fileName, moduleScope,
440 		analysisConfig.length_subtraction_check == Check.skipTests && !ut);
441 
442 	if (moduleName.shouldRun!LocalImportCheck(analysisConfig))
443 		checks ~= new LocalImportCheck(fileName, moduleScope,
444 		analysisConfig.local_import_check == Check.skipTests && !ut);
445 
446 	if (moduleName.shouldRun!LogicPrecedenceCheck(analysisConfig))
447 		checks ~= new LogicPrecedenceCheck(fileName, moduleScope,
448 		analysisConfig.logical_precedence_check == Check.skipTests && !ut);
449 
450 	if (moduleName.shouldRun!MismatchedArgumentCheck(analysisConfig))
451 		checks ~= new MismatchedArgumentCheck(fileName, moduleScope,
452 		analysisConfig.mismatched_args_check == Check.skipTests && !ut);
453 
454 	if (moduleName.shouldRun!NumberStyleCheck(analysisConfig))
455 		checks ~= new NumberStyleCheck(fileName, moduleScope,
456 		analysisConfig.number_style_check == Check.skipTests && !ut);
457 
458 	if (moduleName.shouldRun!ObjectConstCheck(analysisConfig))
459 		checks ~= new ObjectConstCheck(fileName, moduleScope,
460 		analysisConfig.object_const_check == Check.skipTests && !ut);
461 
462 	if (moduleName.shouldRun!OpEqualsWithoutToHashCheck(analysisConfig))
463 		checks ~= new OpEqualsWithoutToHashCheck(fileName, moduleScope,
464 		analysisConfig.opequals_tohash_check == Check.skipTests && !ut);
465 
466 	if (moduleName.shouldRun!RedundantParenCheck(analysisConfig))
467 		checks ~= new RedundantParenCheck(fileName, moduleScope,
468 		analysisConfig.redundant_parens_check == Check.skipTests && !ut);
469 
470 	if (moduleName.shouldRun!StyleChecker(analysisConfig))
471 		checks ~= new StyleChecker(fileName, moduleScope,
472 		analysisConfig.style_check == Check.skipTests && !ut);
473 
474 	if (moduleName.shouldRun!UndocumentedDeclarationCheck(analysisConfig))
475 		checks ~= new UndocumentedDeclarationCheck(fileName, moduleScope,
476 		analysisConfig.undocumented_declaration_check == Check.skipTests && !ut);
477 
478 	if (moduleName.shouldRun!UnusedLabelCheck(analysisConfig))
479 		checks ~= new UnusedLabelCheck(fileName, moduleScope,
480 		analysisConfig.unused_label_check == Check.skipTests && !ut);
481 
482 	if (moduleName.shouldRun!UnusedVariableCheck(analysisConfig))
483 		checks ~= new UnusedVariableCheck(fileName, moduleScope,
484 		analysisConfig.unused_variable_check == Check.skipTests && !ut);
485 
486 	if (moduleName.shouldRun!UnusedParameterCheck(analysisConfig))
487 		checks ~= new UnusedParameterCheck(fileName, moduleScope,
488 		analysisConfig.unused_parameter_check == Check.skipTests && !ut);
489 
490 	if (moduleName.shouldRun!LineLengthCheck(analysisConfig))
491 		checks ~= new LineLengthCheck(fileName, tokens,
492 		analysisConfig.long_line_check == Check.skipTests && !ut);
493 
494 	if (moduleName.shouldRun!AutoRefAssignmentCheck(analysisConfig))
495 		checks ~= new AutoRefAssignmentCheck(fileName,
496 		analysisConfig.auto_ref_assignment_check == Check.skipTests && !ut);
497 
498 	if (moduleName.shouldRun!IncorrectInfiniteRangeCheck(analysisConfig))
499 		checks ~= new IncorrectInfiniteRangeCheck(fileName,
500 		analysisConfig.incorrect_infinite_range_check == Check.skipTests && !ut);
501 
502 	if (moduleName.shouldRun!UselessAssertCheck(analysisConfig))
503 		checks ~= new UselessAssertCheck(fileName,
504 		analysisConfig.useless_assert_check == Check.skipTests && !ut);
505 
506 	if (moduleName.shouldRun!AliasSyntaxCheck(analysisConfig))
507 		checks ~= new AliasSyntaxCheck(fileName,
508 		analysisConfig.alias_syntax_check == Check.skipTests && !ut);
509 
510 	if (moduleName.shouldRun!StaticIfElse(analysisConfig))
511 		checks ~= new StaticIfElse(fileName,
512 		analysisConfig.static_if_else_check == Check.skipTests && !ut);
513 
514 	if (moduleName.shouldRun!LambdaReturnCheck(analysisConfig))
515 		checks ~= new LambdaReturnCheck(fileName,
516 		analysisConfig.lambda_return_check == Check.skipTests && !ut);
517 
518 	if (moduleName.shouldRun!AutoFunctionChecker(analysisConfig))
519 		checks ~= new AutoFunctionChecker(fileName,
520 		analysisConfig.auto_function_check == Check.skipTests && !ut);
521 
522 	if (moduleName.shouldRun!ImportSortednessCheck(analysisConfig))
523 		checks ~= new ImportSortednessCheck(fileName,
524 		analysisConfig.imports_sortedness == Check.skipTests && !ut);
525 
526 	if (moduleName.shouldRun!ExplicitlyAnnotatedUnittestCheck(analysisConfig))
527 		checks ~= new ExplicitlyAnnotatedUnittestCheck(fileName,
528 		analysisConfig.explicitly_annotated_unittests == Check.skipTests && !ut);
529 
530 	if (moduleName.shouldRun!ProperlyDocumentedPublicFunctions(analysisConfig))
531 		checks ~= new ProperlyDocumentedPublicFunctions(fileName,
532 		analysisConfig.properly_documented_public_functions == Check.skipTests && !ut);
533 
534 	if (moduleName.shouldRun!FinalAttributeChecker(analysisConfig))
535 		checks ~= new FinalAttributeChecker(fileName,
536 		analysisConfig.final_attribute_check == Check.skipTests && !ut);
537 
538 	if (moduleName.shouldRun!VcallCtorChecker(analysisConfig))
539 		checks ~= new VcallCtorChecker(fileName,
540 		analysisConfig.vcall_in_ctor == Check.skipTests && !ut);
541 
542 	if (moduleName.shouldRun!UselessInitializerChecker(analysisConfig))
543 		checks ~= new UselessInitializerChecker(fileName,
544 		analysisConfig.useless_initializer == Check.skipTests && !ut);
545 
546 	if (moduleName.shouldRun!AllManCheck(analysisConfig))
547 		checks ~= new AllManCheck(fileName, tokens,
548 		analysisConfig.allman_braces_check == Check.skipTests && !ut);
549 
550 	if (moduleName.shouldRun!RedundantAttributesCheck(analysisConfig))
551 		checks ~= new RedundantAttributesCheck(fileName, moduleScope,
552 		analysisConfig.redundant_attributes_check == Check.skipTests && !ut);
553 
554 	if (moduleName.shouldRun!HasPublicExampleCheck(analysisConfig))
555 		checks ~= new HasPublicExampleCheck(fileName, moduleScope,
556 		analysisConfig.has_public_example == Check.skipTests && !ut);
557 
558 	if (moduleName.shouldRun!AssertWithoutMessageCheck(analysisConfig))
559 		checks ~= new AssertWithoutMessageCheck(fileName, moduleScope,
560 		analysisConfig.assert_without_msg == Check.skipTests && !ut);
561 
562 	if (moduleName.shouldRun!IfConstraintsIndentCheck(analysisConfig))
563 		checks ~= new IfConstraintsIndentCheck(fileName, tokens,
564 		analysisConfig.if_constraints_indent == Check.skipTests && !ut);
565 
566 	if (moduleName.shouldRun!TrustTooMuchCheck(analysisConfig))
567 		checks ~= new TrustTooMuchCheck(fileName,
568 		analysisConfig.trust_too_much == Check.skipTests && !ut);
569 
570 	if (moduleName.shouldRun!RedundantStorageClassCheck(analysisConfig))
571 		checks ~= new RedundantStorageClassCheck(fileName,
572 		analysisConfig.redundant_storage_classes == Check.skipTests && !ut);
573 
574 	version (none)
575 		if (moduleName.shouldRun!IfStatementCheck(analysisConfig))
576 			checks ~= new IfStatementCheck(fileName, moduleScope,
577 			analysisConfig.redundant_if_check == Check.skipTests && !ut);
578 
579 	MessageSet set = new MessageSet;
580 	foreach (check; checks)
581 	{
582 		check.visit(m);
583 		foreach (message; check.messages)
584 			set.insert(message);
585 	}
586 
587 	GC.enable;
588 
589 	return set;
590 }
591