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