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 }