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