src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py

branch
eric7
changeset 11147
dee6e106b4d3
parent 11145
d328a7b74fd8
child 11148
15e30f0c76a8
equal deleted inserted replaced
11146:59e04f7003e9 11147:dee6e106b4d3
93 Class implementing a checker for miscellaneous checks. 93 Class implementing a checker for miscellaneous checks.
94 """ 94 """
95 95
96 Codes = [ 96 Codes = [
97 ## Coding line 97 ## Coding line
98 "M101", 98 "M-101",
99 "M102", 99 "M-102",
100 ## Copyright 100 ## Copyright
101 "M111", 101 "M-111",
102 "M112", 102 "M-112",
103 ## Shadowed Builtins 103 ## Shadowed Builtins
104 "M131", 104 "M-131",
105 "M132", 105 "M-132",
106 ## Comprehensions 106 ## Comprehensions
107 "M180", 107 "M-180",
108 "M181", 108 "M-181",
109 "M182", 109 "M-182",
110 "M183", 110 "M-183",
111 "M184", 111 "M-184",
112 "M185", 112 "M-185",
113 "M186", 113 "M-186",
114 "M188", 114 "M-188",
115 "M189", 115 "M-189",
116 "M189a", 116 "M-189a",
117 "M189b", 117 "M-189b",
118 "M190", 118 "M-190",
119 "M190a", 119 "M-190a",
120 "M190b", 120 "M-190b",
121 "M191", 121 "M-191",
122 "M193", 122 "M-193",
123 "M193a", 123 "M-193a",
124 "M193b", 124 "M-193b",
125 "M193c", 125 "M-193c",
126 "M194", 126 "M-194",
127 "M195", 127 "M-195",
128 "M196", 128 "M-196",
129 "M197", 129 "M-197",
130 "M198", 130 "M-198",
131 "M199", 131 "M-199",
132 "M200", 132 "M-200",
133 ## Dictionaries with sorted keys 133 ## Dictionaries with sorted keys
134 "M251", 134 "M-251",
135 ## Property 135 ## Property
136 "M260", 136 "M-260",
137 "M261", 137 "M-261",
138 "M262", 138 "M-262",
139 "M263", 139 "M-263",
140 "M264", 140 "M-264",
141 "M265", 141 "M-265",
142 "M266", 142 "M-266",
143 "M267", 143 "M-267",
144 ## Naive datetime usage 144 ## Naive datetime usage
145 "M301", 145 "M-301",
146 "M302", 146 "M-302",
147 "M303", 147 "M-303",
148 "M304", 148 "M-304",
149 "M305", 149 "M-305",
150 "M306", 150 "M-306",
151 "M307", 151 "M-307",
152 "M308", 152 "M-308",
153 "M311", 153 "M-311",
154 "M312", 154 "M-312",
155 "M313", 155 "M-313",
156 "M314", 156 "M-314",
157 "M315", 157 "M-315",
158 "M321", 158 "M-321",
159 ## sys.version and sys.version_info usage 159 ## sys.version and sys.version_info usage
160 "M401", 160 "M-401",
161 "M402", 161 "M-402",
162 "M403", 162 "M-403",
163 "M411", 163 "M-411",
164 "M412", 164 "M-412",
165 "M413", 165 "M-413",
166 "M414", 166 "M-414",
167 "M421", 167 "M-421",
168 "M422", 168 "M-422",
169 "M423", 169 "M-423",
170 ## Bugbear 170 ## Bugbear
171 "M501", 171 "M-501",
172 "M502", 172 "M-502",
173 "M503", 173 "M-503",
174 "M504", 174 "M-504",
175 "M505", 175 "M-505",
176 "M506", 176 "M-506",
177 "M507", 177 "M-507",
178 "M508", 178 "M-508",
179 "M509", 179 "M-509",
180 "M510", 180 "M-510",
181 "M511", 181 "M-511",
182 "M512", 182 "M-512",
183 "M513", 183 "M-513",
184 "M514", 184 "M-514",
185 "M515", 185 "M-515",
186 "M516", 186 "M-516",
187 "M517", 187 "M-517",
188 "M518", 188 "M-518",
189 "M519", 189 "M-519",
190 "M520", 190 "M-520",
191 "M521", 191 "M-521",
192 "M522", 192 "M-522",
193 "M523", 193 "M-523",
194 "M524", 194 "M-524",
195 "M525", 195 "M-525",
196 "M526", 196 "M-526",
197 "M527", 197 "M-527",
198 "M528", 198 "M-528",
199 "M529", 199 "M-529",
200 "M530", 200 "M-530",
201 "M531", 201 "M-531",
202 "M532", 202 "M-532",
203 "M533", 203 "M-533",
204 "M534", 204 "M-534",
205 "M535", 205 "M-535",
206 "M536", 206 "M-536",
207 "M537", 207 "M-537",
208 "M539", 208 "M-539",
209 "M540", 209 "M-540",
210 "M541", 210 "M-541",
211 ## Bugbear, opininonated 211 ## Bugbear, opininonated
212 "M569", 212 "M-569",
213 ## Bugbear++ 213 ## Bugbear++
214 "M581", 214 "M-581",
215 "M582", 215 "M-582",
216 ## Format Strings 216 ## Format Strings
217 "M601", 217 "M-601",
218 "M611", 218 "M-611",
219 "M612", 219 "M-612",
220 "M613", 220 "M-613",
221 "M621", 221 "M-621",
222 "M622", 222 "M-622",
223 "M623", 223 "M-623",
224 "M624", 224 "M-624",
225 "M625", 225 "M-625",
226 "M631", 226 "M-631",
227 "M632", 227 "M-632",
228 ## Future statements 228 ## Future statements
229 "M701", 229 "M-701",
230 "M702", 230 "M-702",
231 ## Gettext 231 ## Gettext
232 "M711", 232 "M-711",
233 ## print() statements 233 ## print() statements
234 "M801", 234 "M-801",
235 ## one element tuple 235 ## one element tuple
236 "M811", 236 "M-811",
237 ## return statements 237 ## return statements
238 "M831", 238 "M-831",
239 "M832", 239 "M-832",
240 "M833", 240 "M-833",
241 "M834", 241 "M-834",
242 ## line continuation 242 ## line continuation
243 "M841", 243 "M-841",
244 ## implicitly concatenated strings 244 ## implicitly concatenated strings
245 "M851", 245 "M-851",
246 "M852", 246 "M-852",
247 "M853", 247 "M-853",
248 ## commented code 248 ## commented code
249 "M891", 249 "M-891",
250 ## structural pattern matching 250 ## structural pattern matching
251 "M901", 251 "M-901",
252 "M902", 252 "M-902",
253 ] 253 ]
254 254
255 Formatter = Formatter() 255 Formatter = Formatter()
256 FormatFieldRegex = re.compile(r"^((?:\s|.)*?)(\..*|\[.*\])?$") 256 FormatFieldRegex = re.compile(r"^((?:\s|.)*?)(\..*|\[.*\])?$")
257 257
307 307
308 # collection of detected errors 308 # collection of detected errors
309 self.errors = [] 309 self.errors = []
310 310
311 checkersWithCodes = [ 311 checkersWithCodes = [
312 (self.__checkCoding, ("M101", "M102")), 312 (self.__checkCoding, ("M-101", "M-102")),
313 (self.__checkCopyright, ("M111", "M112")), 313 (self.__checkCopyright, ("M-111", "M-112")),
314 (self.__checkBuiltins, ("M131", "M132")), 314 (self.__checkBuiltins, ("M-131", "M-132")),
315 ( 315 (
316 self.__checkComprehensions, 316 self.__checkComprehensions,
317 ( 317 (
318 "M180", 318 "M-180",
319 "M181", 319 "M-181",
320 "M182", 320 "M-182",
321 "M183", 321 "M-183",
322 "M184", 322 "M-184",
323 "M185", 323 "M-185",
324 "M186", 324 "M-186",
325 "M188", 325 "M-188",
326 "M189", 326 "M-189",
327 "M189a", 327 "M-189a",
328 "M189b", 328 "M-189b",
329 "M190", 329 "M-190",
330 "M190a", 330 "M-190a",
331 "M190b", 331 "M-190b",
332 "M191", 332 "M-191",
333 "M193", 333 "M-193",
334 "M193a", 334 "M-193a",
335 "M193b", 335 "M-193b",
336 "M193c", 336 "M-193c",
337 "M194", 337 "M-194",
338 "M195", 338 "M-195",
339 "M196", 339 "M-196",
340 "M197", 340 "M-197",
341 "M198", 341 "M-198",
342 "M199", 342 "M-199",
343 "M200", 343 "M-200",
344 ), 344 ),
345 ), 345 ),
346 (self.__checkDictWithSortedKeys, ("M251",)), 346 (self.__checkDictWithSortedKeys, ("M-251",)),
347 ( 347 (
348 self.__checkProperties, 348 self.__checkProperties,
349 ("M260", "M261", "M262", "M263", "M264", "M265", "M266", "M267"), 349 ("M-260", "M-261", "M-262", "M-263", "M-264", "M-265", "M-266", "M-267"),
350 ), 350 ),
351 ( 351 (
352 self.__checkDateTime, 352 self.__checkDateTime,
353 ( 353 (
354 "M301", 354 "M-301",
355 "M302", 355 "M-302",
356 "M303", 356 "M-303",
357 "M304", 357 "M-304",
358 "M305", 358 "M-305",
359 "M306", 359 "M-306",
360 "M307", 360 "M-307",
361 "M308", 361 "M-308",
362 "M311", 362 "M-311",
363 "M312", 363 "M-312",
364 "M313", 364 "M-313",
365 "M314", 365 "M-314",
366 "M315", 366 "M-315",
367 "M321", 367 "M-321",
368 ), 368 ),
369 ), 369 ),
370 ( 370 (
371 self.__checkSysVersion, 371 self.__checkSysVersion,
372 ( 372 (
373 "M401", 373 "M-401",
374 "M402", 374 "M-402",
375 "M403", 375 "M-403",
376 "M411", 376 "M-411",
377 "M412", 377 "M-412",
378 "M413", 378 "M-413",
379 "M414", 379 "M-414",
380 "M421", 380 "M-421",
381 "M422", 381 "M-422",
382 "M423", 382 "M-423",
383 ), 383 ),
384 ), 384 ),
385 ( 385 (
386 self.__checkBugBear, 386 self.__checkBugBear,
387 ( 387 (
388 "M501", 388 "M-501",
389 "M502", 389 "M-502",
390 "M503", 390 "M-503",
391 "M504", 391 "M-504",
392 "M505", 392 "M-505",
393 "M506", 393 "M-506",
394 "M507", 394 "M-507",
395 "M508", 395 "M-508",
396 "M509", 396 "M-509",
397 "M510", 397 "M-510",
398 "M511", 398 "M-511",
399 "M512", 399 "M-512",
400 "M513", 400 "M-513",
401 "M514", 401 "M-514",
402 "M515", 402 "M-515",
403 "M516", 403 "M-516",
404 "M517", 404 "M-517",
405 "M518", 405 "M-518",
406 "M519", 406 "M-519",
407 "M520", 407 "M-520",
408 "M521", 408 "M-521",
409 "M522", 409 "M-522",
410 "M523", 410 "M-523",
411 "M524", 411 "M-524",
412 "M525", 412 "M-525",
413 "M526", 413 "M-526",
414 "M527", 414 "M-527",
415 "M528", 415 "M-528",
416 "M529", 416 "M-529",
417 "M530", 417 "M-530",
418 "M531", 418 "M-531",
419 "M532", 419 "M-532",
420 "M533", 420 "M-533",
421 "M534", 421 "M-534",
422 "M535", 422 "M-535",
423 "M536", 423 "M-536",
424 "M537", 424 "M-537",
425 "M539", 425 "M-539",
426 "M540", 426 "M-540",
427 "M541", 427 "M-541",
428 "M569", 428 "M-569",
429 "M581", 429 "M-581",
430 "M582", 430 "M-582",
431 ), 431 ),
432 ), 432 ),
433 (self.__checkPep3101, ("M601",)), 433 (self.__checkPep3101, ("M-601",)),
434 ( 434 (
435 self.__checkFormatString, 435 self.__checkFormatString,
436 ( 436 (
437 "M611", 437 "M-611",
438 "M612", 438 "M-612",
439 "M613", 439 "M-613",
440 "M621", 440 "M-621",
441 "M622", 441 "M-622",
442 "M623", 442 "M-623",
443 "M624", 443 "M-624",
444 "M625", 444 "M-625",
445 "M631", 445 "M-631",
446 "M632", 446 "M-632",
447 ), 447 ),
448 ), 448 ),
449 (self.__checkFuture, ("M701", "M702")), 449 (self.__checkFuture, ("M-701", "M-702")),
450 (self.__checkGettext, ("M711",)), 450 (self.__checkGettext, ("M-711",)),
451 (self.__checkPrintStatements, ("M801",)), 451 (self.__checkPrintStatements, ("M-801",)),
452 (self.__checkTuple, ("M811",)), 452 (self.__checkTuple, ("M-811",)),
453 (self.__checkReturn, ("M831", "M832", "M833", "M834")), 453 (self.__checkReturn, ("M-831", "M-832", "M-833", "M-834")),
454 (self.__checkLineContinuation, ("M841",)), 454 (self.__checkLineContinuation, ("M-841",)),
455 (self.__checkImplicitStringConcat, ("M851", "M852")), 455 (self.__checkImplicitStringConcat, ("M-851", "M-852")),
456 (self.__checkExplicitStringConcat, ("M853",)), 456 (self.__checkExplicitStringConcat, ("M-853",)),
457 (self.__checkCommentedCode, ("M891",)), 457 (self.__checkCommentedCode, ("M-891",)),
458 (self.__checkDefaultMatchCase, ("M901", "M902")), 458 (self.__checkDefaultMatchCase, ("M-901", "M-902")),
459 ] 459 ]
460 460
461 # the eradicate whitelist 461 # the eradicate whitelist
462 commentedCodeCheckerArgs = self.__args.get( 462 commentedCodeCheckerArgs = self.__args.get(
463 "CommentedCodeChecker", 463 "CommentedCodeChecker",
571 ).split(",") 571 ).split(",")
572 ] 572 ]
573 lineno, coding = self.__getCoding() 573 lineno, coding = self.__getCoding()
574 if coding: 574 if coding:
575 if coding.lower() not in encodings: 575 if coding.lower() not in encodings:
576 self.__error(lineno, 0, "M102", coding) 576 self.__error(lineno, 0, "M-102", coding)
577 else: 577 else:
578 self.__error(0, 0, "M101") 578 self.__error(0, 0, "M-101")
579 579
580 def __checkCopyright(self): 580 def __checkCopyright(self):
581 """ 581 """
582 Private method to check the presence of a copyright statement. 582 Private method to check the presence of a copyright statement.
583 """ 583 """
601 if len(topOfSource) < copyrightMinFileSize: 601 if len(topOfSource) < copyrightMinFileSize:
602 return 602 return
603 603
604 copyrightRe = re.compile(copyrightRegexStr.format(author=r".*"), re.IGNORECASE) 604 copyrightRe = re.compile(copyrightRegexStr.format(author=r".*"), re.IGNORECASE)
605 if not copyrightRe.search(topOfSource): 605 if not copyrightRe.search(topOfSource):
606 self.__error(0, 0, "M111") 606 self.__error(0, 0, "M-111")
607 return 607 return
608 608
609 if copyrightAuthor: 609 if copyrightAuthor:
610 copyrightAuthorRe = re.compile( 610 copyrightAuthorRe = re.compile(
611 copyrightRegexStr.format(author=copyrightAuthor), re.IGNORECASE 611 copyrightRegexStr.format(author=copyrightAuthor), re.IGNORECASE
612 ) 612 )
613 if not copyrightAuthorRe.search(topOfSource): 613 if not copyrightAuthorRe.search(topOfSource):
614 self.__error(0, 0, "M112") 614 self.__error(0, 0, "M-112")
615 615
616 def __checkCommentedCode(self): 616 def __checkCommentedCode(self):
617 """ 617 """
618 Private method to check for commented code. 618 Private method to check for commented code.
619 """ 619 """
627 MiscellaneousCheckerDefaultArgs["CommentedCodeChecker"]["Aggressive"], 627 MiscellaneousCheckerDefaultArgs["CommentedCodeChecker"]["Aggressive"],
628 ) 628 )
629 for markedLine in self.__eradicator.commented_out_code_line_numbers( 629 for markedLine in self.__eradicator.commented_out_code_line_numbers(
630 source, aggressive=aggressive 630 source, aggressive=aggressive
631 ): 631 ):
632 self.__error(markedLine - 1, 0, "M891") 632 self.__error(markedLine - 1, 0, "M-891")
633 633
634 def __checkLineContinuation(self): 634 def __checkLineContinuation(self):
635 """ 635 """
636 Private method to check line continuation using backslash. 636 Private method to check line continuation using backslash.
637 """ 637 """
650 for lineIndex, line in enumerate(stripped): 650 for lineIndex, line in enumerate(stripped):
651 strippedLine = line.strip() 651 strippedLine = line.strip()
652 if strippedLine.endswith("\\") and not strippedLine.startswith( 652 if strippedLine.endswith("\\") and not strippedLine.startswith(
653 ("assert", "with") 653 ("assert", "with")
654 ): 654 ):
655 self.__error(lineIndex, len(line), "M841") 655 self.__error(lineIndex, len(line), "M-841")
656 656
657 def __checkPrintStatements(self): 657 def __checkPrintStatements(self):
658 """ 658 """
659 Private method to check for print statements. 659 Private method to check for print statements.
660 """ 660 """
661 for node in ast.walk(self.__tree): 661 for node in ast.walk(self.__tree):
662 if ( 662 if (
663 isinstance(node, ast.Call) and getattr(node.func, "id", None) == "print" 663 isinstance(node, ast.Call) and getattr(node.func, "id", None) == "print"
664 ) or (hasattr(ast, "Print") and isinstance(node, ast.Print)): 664 ) or (hasattr(ast, "Print") and isinstance(node, ast.Print)):
665 self.__error(node.lineno - 1, node.col_offset, "M801") 665 self.__error(node.lineno - 1, node.col_offset, "M-801")
666 666
667 def __checkTuple(self): 667 def __checkTuple(self):
668 """ 668 """
669 Private method to check for one element tuples. 669 Private method to check for one element tuples.
670 """ 670 """
671 for node in ast.walk(self.__tree): 671 for node in ast.walk(self.__tree):
672 if isinstance(node, ast.Tuple) and len(node.elts) == 1: 672 if isinstance(node, ast.Tuple) and len(node.elts) == 1:
673 self.__error(node.lineno - 1, node.col_offset, "M811") 673 self.__error(node.lineno - 1, node.col_offset, "M-811")
674 674
675 def __checkFuture(self): 675 def __checkFuture(self):
676 """ 676 """
677 Private method to check the __future__ imports. 677 Private method to check the __future__ imports.
678 """ 678 """
706 if imports < expectedImports: 706 if imports < expectedImports:
707 if imports: 707 if imports:
708 self.__error( 708 self.__error(
709 node.lineno - 1, 709 node.lineno - 1,
710 node.col_offset, 710 node.col_offset,
711 "M701", 711 "M-701",
712 ", ".join(expectedImports), 712 ", ".join(expectedImports),
713 ", ".join(imports), 713 ", ".join(imports),
714 ) 714 )
715 else: 715 else:
716 self.__error( 716 self.__error(
717 node.lineno - 1, node.col_offset, "M702", ", ".join(expectedImports) 717 node.lineno - 1, node.col_offset, "M-702", ", ".join(expectedImports)
718 ) 718 )
719 719
720 def __checkPep3101(self): 720 def __checkPep3101(self):
721 """ 721 """
722 Private method to check for old style string formatting. 722 Private method to check for old style string formatting.
736 if pos >= lineLen: 736 if pos >= lineLen:
737 break 737 break
738 c = line[pos] 738 c = line[pos]
739 if c in "diouxXeEfFgGcrs": 739 if c in "diouxXeEfFgGcrs":
740 formatter += c 740 formatter += c
741 self.__error(lineno, formatPos, "M601", formatter) 741 self.__error(lineno, formatPos, "M-601", formatter)
742 742
743 def __checkFormatString(self): 743 def __checkFormatString(self):
744 """ 744 """
745 Private method to check string format strings. 745 Private method to check string format strings.
746 """ 746 """
759 except UnicodeDecodeError: 759 except UnicodeDecodeError:
760 continue 760 continue
761 fields, implicit, explicit = self.__getFields(text) 761 fields, implicit, explicit = self.__getFields(text)
762 if implicit: 762 if implicit:
763 if node in visitor.calls: 763 if node in visitor.calls:
764 self.__error(node.lineno - 1, node.col_offset, "M611") 764 self.__error(node.lineno - 1, node.col_offset, "M-611")
765 else: 765 else:
766 if node.is_docstring: 766 if node.is_docstring:
767 self.__error(node.lineno - 1, node.col_offset, "M612") 767 self.__error(node.lineno - 1, node.col_offset, "M-612")
768 else: 768 else:
769 self.__error(node.lineno - 1, node.col_offset, "M613") 769 self.__error(node.lineno - 1, node.col_offset, "M-613")
770 770
771 if node in visitor.calls: 771 if node in visitor.calls:
772 call, strArgs = visitor.calls[node] 772 call, strArgs = visitor.calls[node]
773 773
774 numbers = set() 774 numbers = set()
802 802
803 # if starargs or kwargs is not None, it can't count the 803 # if starargs or kwargs is not None, it can't count the
804 # parameters but at least check if the args are used 804 # parameters but at least check if the args are used
805 if hasKwArgs and not names: 805 if hasKwArgs and not names:
806 # No names but kwargs 806 # No names but kwargs
807 self.__error(call.lineno - 1, call.col_offset, "M623") 807 self.__error(call.lineno - 1, call.col_offset, "M-623")
808 if hasStarArgs and not numbers: 808 if hasStarArgs and not numbers:
809 # No numbers but args 809 # No numbers but args
810 self.__error(call.lineno - 1, call.col_offset, "M624") 810 self.__error(call.lineno - 1, call.col_offset, "M-624")
811 811
812 if not hasKwArgs and not hasStarArgs: 812 if not hasKwArgs and not hasStarArgs:
813 # can actually verify numbers and names 813 # can actually verify numbers and names
814 for number in sorted(numbers): 814 for number in sorted(numbers):
815 if number >= numArgs: 815 if number >= numArgs:
816 self.__error( 816 self.__error(
817 call.lineno - 1, call.col_offset, "M621", number 817 call.lineno - 1, call.col_offset, "M-621", number
818 ) 818 )
819 819
820 for name in sorted(names): 820 for name in sorted(names):
821 if name not in keywords: 821 if name not in keywords:
822 self.__error(call.lineno - 1, call.col_offset, "M622", name) 822 self.__error(call.lineno - 1, call.col_offset, "M-622", name)
823 823
824 for arg in range(numArgs): 824 for arg in range(numArgs):
825 if arg not in numbers: 825 if arg not in numbers:
826 self.__error(call.lineno - 1, call.col_offset, "M631", arg) 826 self.__error(call.lineno - 1, call.col_offset, "M-631", arg)
827 827
828 for keyword in keywords: 828 for keyword in keywords:
829 if keyword not in names: 829 if keyword not in names:
830 self.__error(call.lineno - 1, call.col_offset, "M632", keyword) 830 self.__error(call.lineno - 1, call.col_offset, "M-632", keyword)
831 831
832 if implicit and explicit: 832 if implicit and explicit:
833 self.__error(call.lineno - 1, call.col_offset, "M625") 833 self.__error(call.lineno - 1, call.col_offset, "M-625")
834 834
835 def __getFields(self, string): 835 def __getFields(self, string):
836 """ 836 """
837 Private method to extract the format field information. 837 Private method to extract the format field information.
838 838
889 and value.id in ignoreBuiltinAssignments[element.id] 889 and value.id in ignoreBuiltinAssignments[element.id]
890 ): 890 ):
891 # ignore compatibility assignments 891 # ignore compatibility assignments
892 continue 892 continue
893 self.__error( 893 self.__error(
894 element.lineno - 1, element.col_offset, "M131", element.id 894 element.lineno - 1, element.col_offset, "M-131", element.id
895 ) 895 )
896 elif isinstance(element, (ast.Tuple, ast.List)): 896 elif isinstance(element, (ast.Tuple, ast.List)):
897 for tupleElement in element.elts: 897 for tupleElement in element.elts:
898 if ( 898 if (
899 isinstance(tupleElement, ast.Name) 899 isinstance(tupleElement, ast.Name)
900 and tupleElement.id in self.__builtins 900 and tupleElement.id in self.__builtins
901 ): 901 ):
902 self.__error( 902 self.__error(
903 tupleElement.lineno - 1, 903 tupleElement.lineno - 1,
904 tupleElement.col_offset, 904 tupleElement.col_offset,
905 "M131", 905 "M-131",
906 tupleElement.id, 906 tupleElement.id,
907 ) 907 )
908 elif isinstance(node, ast.For): 908 elif isinstance(node, ast.For):
909 # for loop 909 # for loop
910 target = node.target 910 target = node.target
911 if isinstance(target, ast.Name) and target.id in self.__builtins: 911 if isinstance(target, ast.Name) and target.id in self.__builtins:
912 self.__error( 912 self.__error(
913 target.lineno - 1, target.col_offset, "M131", target.id 913 target.lineno - 1, target.col_offset, "M-131", target.id
914 ) 914 )
915 elif isinstance(target, (ast.Tuple, ast.List)): 915 elif isinstance(target, (ast.Tuple, ast.List)):
916 for element in target.elts: 916 for element in target.elts:
917 if ( 917 if (
918 isinstance(element, ast.Name) 918 isinstance(element, ast.Name)
919 and element.id in self.__builtins 919 and element.id in self.__builtins
920 ): 920 ):
921 self.__error( 921 self.__error(
922 element.lineno - 1, 922 element.lineno - 1,
923 element.col_offset, 923 element.col_offset,
924 "M131", 924 "M-131",
925 element.id, 925 element.id,
926 ) 926 )
927 elif any(isinstance(node, functionDef) for functionDef in functionDefs): 927 elif any(isinstance(node, functionDef) for functionDef in functionDefs):
928 # (asynchronous) function definition 928 # (asynchronous) function definition
929 for arg in node.args.args: 929 for arg in node.args.args:
930 if isinstance(arg, ast.arg) and arg.arg in self.__builtins: 930 if isinstance(arg, ast.arg) and arg.arg in self.__builtins:
931 self.__error(arg.lineno - 1, arg.col_offset, "M132", arg.arg) 931 self.__error(arg.lineno - 1, arg.col_offset, "M-132", arg.arg)
932 932
933 def __checkComprehensions(self): 933 def __checkComprehensions(self):
934 """ 934 """
935 Private method to check some comprehension related things. 935 Private method to check some comprehension related things.
936 936
954 numPositionalArgs == 1 954 numPositionalArgs == 1
955 and isinstance(node.args[0], ast.GeneratorExp) 955 and isinstance(node.args[0], ast.GeneratorExp)
956 and node.func.id in ("list", "set") 956 and node.func.id in ("list", "set")
957 ): 957 ):
958 errorCode = { 958 errorCode = {
959 "list": "M180", 959 "list": "M-180",
960 "set": "M181", 960 "set": "M-181",
961 }[node.func.id] 961 }[node.func.id]
962 self.__error(node.lineno - 1, node.col_offset, errorCode) 962 self.__error(node.lineno - 1, node.col_offset, errorCode)
963 963
964 elif ( 964 elif (
965 numPositionalArgs == 1 965 numPositionalArgs == 1
968 and isinstance(node.args[0], (ast.GeneratorExp, ast.ListComp)) 968 and isinstance(node.args[0], (ast.GeneratorExp, ast.ListComp))
969 and isinstance(node.args[0].elt, ast.Tuple) 969 and isinstance(node.args[0].elt, ast.Tuple)
970 and len(node.args[0].elt.elts) == 2 970 and len(node.args[0].elt.elts) == 2
971 ): 971 ):
972 if isinstance(node.args[0], ast.GeneratorExp): 972 if isinstance(node.args[0], ast.GeneratorExp):
973 errorCode = "M182" 973 errorCode = "M-182"
974 else: 974 else:
975 errorCode = "M184" 975 errorCode = "M-184"
976 self.__error(node.lineno - 1, node.col_offset, errorCode) 976 self.__error(node.lineno - 1, node.col_offset, errorCode)
977 977
978 elif ( 978 elif (
979 numPositionalArgs == 1 979 numPositionalArgs == 1
980 and isinstance(node.args[0], ast.ListComp) 980 and isinstance(node.args[0], ast.ListComp)
981 and node.func.id in ("list", "set", "any", "all") 981 and node.func.id in ("list", "set", "any", "all")
982 ): 982 ):
983 errorCode = { 983 errorCode = {
984 "list": "M191", 984 "list": "M-191",
985 "set": "M183", 985 "set": "M-183",
986 "any": "M199", 986 "any": "M-199",
987 "all": "M199", 987 "all": "M-199",
988 }[node.func.id] 988 }[node.func.id]
989 self.__error( 989 self.__error(
990 node.lineno - 1, node.col_offset, errorCode, node.func.id 990 node.lineno - 1, node.col_offset, errorCode, node.func.id
991 ) 991 )
992 992
995 and node.func.id == "tuple" 995 and node.func.id == "tuple"
996 or isinstance(node.args[0], ast.List) 996 or isinstance(node.args[0], ast.List)
997 and node.func.id == "list" 997 and node.func.id == "list"
998 ): 998 ):
999 errorCode = { 999 errorCode = {
1000 "tuple": "M189a", 1000 "tuple": "M-189a",
1001 "list": "M190a", 1001 "list": "M-190a",
1002 }[node.func.id] 1002 }[node.func.id]
1003 self.__error( 1003 self.__error(
1004 node.lineno - 1, 1004 node.lineno - 1,
1005 node.col_offset, 1005 node.col_offset,
1006 errorCode, 1006 errorCode,
1019 else: 1019 else:
1020 type_ = "dict comprehension" 1020 type_ = "dict comprehension"
1021 self.__error( 1021 self.__error(
1022 node.lineno - 1, 1022 node.lineno - 1,
1023 node.col_offset, 1023 node.col_offset,
1024 "M198", 1024 "M-198",
1025 type_, 1025 type_,
1026 ) 1026 )
1027 1027
1028 elif ( 1028 elif (
1029 numPositionalArgs == 1 1029 numPositionalArgs == 1
1038 ) 1038 )
1039 ) 1039 )
1040 ) 1040 )
1041 ): 1041 ):
1042 errorCode = { 1042 errorCode = {
1043 "tuple": "M189b", 1043 "tuple": "M-189b",
1044 "list": "M190b", 1044 "list": "M-190b",
1045 "set": "M185", 1045 "set": "M-185",
1046 "dict": "M186", 1046 "dict": "M-186",
1047 }[node.func.id] 1047 }[node.func.id]
1048 self.__error( 1048 self.__error(
1049 node.lineno - 1, 1049 node.lineno - 1,
1050 node.col_offset, 1050 node.col_offset,
1051 errorCode, 1051 errorCode,
1061 ) or ( 1061 ) or (
1062 numPositionalArgs == 0 1062 numPositionalArgs == 0
1063 and numKeywordArgs == 0 1063 and numKeywordArgs == 0
1064 and node.func.id in ("tuple", "list") 1064 and node.func.id in ("tuple", "list")
1065 ): 1065 ):
1066 self.__error(node.lineno - 1, node.col_offset, "M188", node.func.id) 1066 self.__error(node.lineno - 1, node.col_offset, "M-188", node.func.id)
1067 1067
1068 elif ( 1068 elif (
1069 node.func.id in {"list", "reversed"} 1069 node.func.id in {"list", "reversed"}
1070 and numPositionalArgs > 0 1070 and numPositionalArgs > 0
1071 and isinstance(node.args[0], ast.Call) 1071 and isinstance(node.args[0], ast.Call)
1085 1085
1086 if reverseFlagValue is None: 1086 if reverseFlagValue is None:
1087 self.__error( 1087 self.__error(
1088 node.lineno - 1, 1088 node.lineno - 1,
1089 node.col_offset, 1089 node.col_offset,
1090 "M193a", 1090 "M-193a",
1091 node.func.id, 1091 node.func.id,
1092 node.args[0].func.id, 1092 node.args[0].func.id,
1093 ) 1093 )
1094 else: 1094 else:
1095 self.__error( 1095 self.__error(
1096 node.lineno - 1, 1096 node.lineno - 1,
1097 node.col_offset, 1097 node.col_offset,
1098 "M193b", 1098 "M-193b",
1099 node.func.id, 1099 node.func.id,
1100 node.args[0].func.id, 1100 node.args[0].func.id,
1101 not reverseFlagValue, 1101 not reverseFlagValue,
1102 ) 1102 )
1103 1103
1104 else: 1104 else:
1105 self.__error( 1105 self.__error(
1106 node.lineno - 1, 1106 node.lineno - 1,
1107 node.col_offset, 1107 node.col_offset,
1108 "M193c", 1108 "M-193c",
1109 node.func.id, 1109 node.func.id,
1110 node.args[0].func.id, 1110 node.args[0].func.id,
1111 ) 1111 )
1112 1112
1113 elif ( 1113 elif (
1128 ) 1128 )
1129 ): 1129 ):
1130 self.__error( 1130 self.__error(
1131 node.lineno - 1, 1131 node.lineno - 1,
1132 node.col_offset, 1132 node.col_offset,
1133 "M194", 1133 "M-194",
1134 node.args[0].func.id, 1134 node.args[0].func.id,
1135 node.func.id, 1135 node.func.id,
1136 ) 1136 )
1137 1137
1138 elif ( 1138 elif (
1145 and isinstance(node.args[0].slice.step, ast.UnaryOp) 1145 and isinstance(node.args[0].slice.step, ast.UnaryOp)
1146 and isinstance(node.args[0].slice.step.op, ast.USub) 1146 and isinstance(node.args[0].slice.step.op, ast.USub)
1147 and isinstance(node.args[0].slice.step.operand, ast.Constant) 1147 and isinstance(node.args[0].slice.step.operand, ast.Constant)
1148 and node.args[0].slice.step.operand.n == 1 1148 and node.args[0].slice.step.operand.n == 1
1149 ): 1149 ):
1150 self.__error(node.lineno - 1, node.col_offset, "M195", node.func.id) 1150 self.__error(node.lineno - 1, node.col_offset, "M-195", node.func.id)
1151 1151
1152 elif ( 1152 elif (
1153 node.func.id == "map" 1153 node.func.id == "map"
1154 and node not in visitedMapCalls 1154 and node not in visitedMapCalls
1155 and len(node.args) == 2 1155 and len(node.args) == 2
1156 and isinstance(node.args[0], ast.Lambda) 1156 and isinstance(node.args[0], ast.Lambda)
1157 ): 1157 ):
1158 self.__error( 1158 self.__error(
1159 node.lineno - 1, node.col_offset, "M197", "generator expression" 1159 node.lineno - 1, node.col_offset, "M-197", "generator expression"
1160 ) 1160 )
1161 1161
1162 elif ( 1162 elif (
1163 node.func.id in ("list", "set", "dict") 1163 node.func.id in ("list", "set", "dict")
1164 and len(node.args) == 1 1164 and len(node.args) == 1
1184 rewriteable = False 1184 rewriteable = False
1185 1185
1186 if rewriteable: 1186 if rewriteable:
1187 comprehensionType = f"{node.func.id} comprehension" 1187 comprehensionType = f"{node.func.id} comprehension"
1188 self.__error( 1188 self.__error(
1189 node.lineno - 1, node.col_offset, "M197", comprehensionType 1189 node.lineno - 1, node.col_offset, "M-197", comprehensionType
1190 ) 1190 )
1191 1191
1192 elif isinstance(node, (ast.DictComp, ast.ListComp, ast.SetComp)) and ( 1192 elif isinstance(node, (ast.DictComp, ast.ListComp, ast.SetComp)) and (
1193 len(node.generators) == 1 1193 len(node.generators) == 1
1194 and not node.generators[0].ifs 1194 and not node.generators[0].ifs
1211 and node.generators[0].target.elts[1].id == node.value.id 1211 and node.generators[0].target.elts[1].id == node.value.id
1212 ): 1212 ):
1213 self.__error( 1213 self.__error(
1214 node.lineno - 1, 1214 node.lineno - 1,
1215 node.col_offset, 1215 node.col_offset,
1216 "M196", 1216 "M-196",
1217 compType[node.__class__], 1217 compType[node.__class__],
1218 ) 1218 )
1219 1219
1220 elif ( 1220 elif (
1221 isinstance(node, ast.DictComp) 1221 isinstance(node, ast.DictComp)
1225 and node.key.id == node.generators[0].target.id 1225 and node.key.id == node.generators[0].target.id
1226 ): 1226 ):
1227 self.__error( 1227 self.__error(
1228 node.lineno - 1, 1228 node.lineno - 1,
1229 node.col_offset, 1229 node.col_offset,
1230 "M200", 1230 "M-200",
1231 compType[node.__class__], 1231 compType[node.__class__],
1232 ) 1232 )
1233 1233
1234 def __dictShouldBeChecked(self, node): 1234 def __dictShouldBeChecked(self, node):
1235 """ 1235 """
1261 for key1, key2 in zip(node.keys, node.keys[1:]): 1261 for key1, key2 in zip(node.keys, node.keys[1:]):
1262 if key2.value < key1.value: 1262 if key2.value < key1.value:
1263 self.__error( 1263 self.__error(
1264 key2.lineno - 1, 1264 key2.lineno - 1,
1265 key2.col_offset, 1265 key2.col_offset,
1266 "M251", 1266 "M-251",
1267 key2.value, 1267 key2.value,
1268 key1.value, 1268 key1.value,
1269 ) 1269 )
1270 1270
1271 def __checkGettext(self): 1271 def __checkGettext(self):
1275 for node in ast.walk(self.__tree): 1275 for node in ast.walk(self.__tree):
1276 if isinstance(node, ast.ImportFrom) and any( 1276 if isinstance(node, ast.ImportFrom) and any(
1277 name.asname == "_" for name in node.names 1277 name.asname == "_" for name in node.names
1278 ): 1278 ):
1279 self.__error( 1279 self.__error(
1280 node.lineno - 1, node.col_offset, "M711", node.names[0].name 1280 node.lineno - 1, node.col_offset, "M-711", node.names[0].name
1281 ) 1281 )
1282 1282
1283 def __checkBugBear(self): 1283 def __checkBugBear(self):
1284 """ 1284 """
1285 Private method for bugbear checks. 1285 Private method for bugbear checks.
1351 properties.append(node.name) 1351 properties.append(node.name)
1352 if len(node.args.args) != 1: 1352 if len(node.args.args) != 1:
1353 self.__error( 1353 self.__error(
1354 node.lineno - 1, 1354 node.lineno - 1,
1355 node.col_offset, 1355 node.col_offset,
1356 "M260", 1356 "M-260",
1357 len(node.args.args), 1357 len(node.args.args),
1358 ) 1358 )
1359 1359
1360 if isinstance(decorator, ast.Attribute): 1360 if isinstance(decorator, ast.Attribute):
1361 # property setter method 1361 # property setter method
1364 if node.name != decorator.value.id: 1364 if node.name != decorator.value.id:
1365 if node.name in properties: 1365 if node.name in properties:
1366 self.__error( 1366 self.__error(
1367 node.lineno - 1, 1367 node.lineno - 1,
1368 node.col_offset, 1368 node.col_offset,
1369 "M265", 1369 "M-265",
1370 node.name, 1370 node.name,
1371 decorator.value.id, 1371 decorator.value.id,
1372 ) 1372 )
1373 else: 1373 else:
1374 self.__error( 1374 self.__error(
1375 node.lineno - 1, 1375 node.lineno - 1,
1376 node.col_offset, 1376 node.col_offset,
1377 "M263", 1377 "M-263",
1378 decorator.value.id, 1378 decorator.value.id,
1379 node.name, 1379 node.name,
1380 ) 1380 )
1381 if len(node.args.args) != 2: 1381 if len(node.args.args) != 2:
1382 self.__error( 1382 self.__error(
1383 node.lineno - 1, 1383 node.lineno - 1,
1384 node.col_offset, 1384 node.col_offset,
1385 "M261", 1385 "M-261",
1386 len(node.args.args), 1386 len(node.args.args),
1387 ) 1387 )
1388 1388
1389 # property deleter method 1389 # property deleter method
1390 if decorator.attr == "deleter": 1390 if decorator.attr == "deleter":
1392 if node.name != decorator.value.id: 1392 if node.name != decorator.value.id:
1393 if node.name in properties: 1393 if node.name in properties:
1394 self.__error( 1394 self.__error(
1395 node.lineno - 1, 1395 node.lineno - 1,
1396 node.col_offset, 1396 node.col_offset,
1397 "M266", 1397 "M-266",
1398 node.name, 1398 node.name,
1399 decorator.value.id, 1399 decorator.value.id,
1400 ) 1400 )
1401 else: 1401 else:
1402 self.__error( 1402 self.__error(
1403 node.lineno - 1, 1403 node.lineno - 1,
1404 node.col_offset, 1404 node.col_offset,
1405 "M264", 1405 "M-264",
1406 decorator.value.id, 1406 decorator.value.id,
1407 node.name, 1407 node.name,
1408 ) 1408 )
1409 if len(node.args.args) != 1: 1409 if len(node.args.args) != 1:
1410 self.__error( 1410 self.__error(
1411 node.lineno - 1, 1411 node.lineno - 1,
1412 node.col_offset, 1412 node.col_offset,
1413 "M262", 1413 "M-262",
1414 len(node.args.args), 1414 len(node.args.args),
1415 ) 1415 )
1416 1416
1417 if propertyCount > 1: 1417 if propertyCount > 1:
1418 self.__error(node.lineno - 1, node.col_offset, "M267", node.name) 1418 self.__error(node.lineno - 1, node.col_offset, "M-267", node.name)
1419 1419
1420 ####################################################################### 1420 #######################################################################
1421 ## The following methods check for implicitly concatenated strings. 1421 ## The following methods check for implicitly concatenated strings.
1422 ## 1422 ##
1423 ## These methods are adapted from: flake8-implicit-str-concat v0.5.0 1423 ## These methods are adapted from: flake8-implicit-str-concat v0.5.0
1487 ) 1487 )
1488 ) 1488 )
1489 for a, b in pairwise(tokensWithoutWhitespace): 1489 for a, b in pairwise(tokensWithoutWhitespace):
1490 if self.__isImplicitStringConcat(a, b): 1490 if self.__isImplicitStringConcat(a, b):
1491 self.__error( 1491 self.__error(
1492 a.end[0] - 1, a.end[1], "M851" if a.end[0] == b.start[0] else "M852" 1492 a.end[0] - 1, a.end[1], "M-851" if a.end[0] == b.start[0] else "M-852"
1493 ) 1493 )
1494 1494
1495 def __checkExplicitStringConcat(self): 1495 def __checkExplicitStringConcat(self):
1496 """ 1496 """
1497 Private method to check for explicitly concatenated strings. 1497 Private method to check for explicitly concatenated strings.
1504 AstUtilities.isBaseString(operand) 1504 AstUtilities.isBaseString(operand)
1505 or isinstance(operand, ast.JoinedStr) 1505 or isinstance(operand, ast.JoinedStr)
1506 for operand in (node.left, node.right) 1506 for operand in (node.left, node.right)
1507 ) 1507 )
1508 ): 1508 ):
1509 self.__error(node.lineno - 1, node.col_offset, "M853") 1509 self.__error(node.lineno - 1, node.col_offset, "M-853")
1510 1510
1511 ################################################################################# 1511 #################################################################################
1512 ## The following method checks default match cases. 1512 ## The following method checks default match cases.
1513 ################################################################################# 1513 #################################################################################
1514 1514
1944 ): 1944 ):
1945 good.remove(name) 1945 good.remove(name)
1946 if good != names: 1946 if good != names:
1947 desc = good[0] if len(good) == 1 else "({0})".format(", ".join(good)) 1947 desc = good[0] if len(good) == 1 else "({0})".format(", ".join(good))
1948 as_ = " as " + node.name if node.name is not None else "" 1948 as_ = " as " + node.name if node.name is not None else ""
1949 return (node, "M514", ", ".join(names), as_, desc, inTryStar) 1949 return (node, "M-514", ", ".join(names), as_, desc, inTryStar)
1950 1950
1951 return None 1951 return None
1952 1952
1953 def __walkList(self, nodes): 1953 def __walkList(self, nodes):
1954 """ 1954 """
2017 2017
2018 @param node reference to the node to be processed 2018 @param node reference to the node to be processed
2019 @type ast.Return 2019 @type ast.Return
2020 """ 2020 """
2021 if self.__inClassInit() and node.value is not None: 2021 if self.__inClassInit() and node.value is not None:
2022 self.violations.append((node, "M537")) 2022 self.violations.append((node, "M-537"))
2023 2023
2024 self.generic_visit(node) 2024 self.generic_visit(node)
2025 2025
2026 def visit_Yield(self, node): 2026 def visit_Yield(self, node):
2027 """ 2027 """
2029 2029
2030 @param node reference to the node to be processed 2030 @param node reference to the node to be processed
2031 @type ast.Yield 2031 @type ast.Yield
2032 """ 2032 """
2033 if self.__inClassInit(): 2033 if self.__inClassInit():
2034 self.violations.append((node, "M537")) 2034 self.violations.append((node, "M-537"))
2035 2035
2036 self.generic_visit(node) 2036 self.generic_visit(node)
2037 2037
2038 def visit_YieldFrom(self, node) -> None: 2038 def visit_YieldFrom(self, node) -> None:
2039 """ 2039 """
2041 2041
2042 @param node reference to the node to be processed 2042 @param node reference to the node to be processed
2043 @type ast.YieldFrom 2043 @type ast.YieldFrom
2044 """ 2044 """
2045 if self.__inClassInit(): 2045 if self.__inClassInit():
2046 self.violations.append((node, "M537")) 2046 self.violations.append((node, "M-537"))
2047 2047
2048 self.generic_visit(node) 2048 self.generic_visit(node)
2049 2049
2050 def visit(self, node): 2050 def visit(self, node):
2051 """ 2051 """
2092 self.__M540CaughtException = M540CaughtException(node.name, False) 2092 self.__M540CaughtException = M540CaughtException(node.name, False)
2093 2093
2094 names = self.__checkForM513_M514_M529_M530(node) 2094 names = self.__checkForM513_M514_M529_M530(node)
2095 2095
2096 if "BaseException" in names and not ExceptBaseExceptionVisitor(node).reRaised(): 2096 if "BaseException" in names and not ExceptBaseExceptionVisitor(node).reRaised():
2097 self.violations.append((node, "M536")) 2097 self.violations.append((node, "M-536"))
2098 2098
2099 self.generic_visit(node) 2099 self.generic_visit(node)
2100 2100
2101 if ( 2101 if (
2102 self.__M540CaughtException is not None 2102 self.__M540CaughtException is not None
2103 and self.__M540CaughtException.hasNote 2103 and self.__M540CaughtException.hasNote
2104 ): 2104 ):
2105 self.violations.append((node, "M540")) 2105 self.violations.append((node, "M-540"))
2106 self.__M540CaughtException = oldM540CaughtException 2106 self.__M540CaughtException = oldM540CaughtException
2107 2107
2108 def visit_UAdd(self, node): 2108 def visit_UAdd(self, node):
2109 """ 2109 """
2110 Public method to handle unary additions. 2110 Public method to handle unary additions.
2113 @type ast.UAdd 2113 @type ast.UAdd
2114 """ 2114 """
2115 trailingNodes = list(map(type, self.nodeWindow[-4:])) 2115 trailingNodes = list(map(type, self.nodeWindow[-4:]))
2116 if trailingNodes == [ast.UnaryOp, ast.UAdd, ast.UnaryOp, ast.UAdd]: 2116 if trailingNodes == [ast.UnaryOp, ast.UAdd, ast.UnaryOp, ast.UAdd]:
2117 originator = self.nodeWindow[-4] 2117 originator = self.nodeWindow[-4]
2118 self.violations.append((originator, "M502")) 2118 self.violations.append((originator, "M-502"))
2119 2119
2120 self.generic_visit(node) 2120 self.generic_visit(node)
2121 2121
2122 def visit_Call(self, node): 2122 def visit_Call(self, node):
2123 """ 2123 """
2141 and isinstance(args[0], ast.Attribute) 2141 and isinstance(args[0], ast.Attribute)
2142 and isinstance(args[0].value, ast.Name) 2142 and isinstance(args[0].value, ast.Name)
2143 and args[0].value.id == "self" 2143 and args[0].value.id == "self"
2144 and args[0].attr == "__class__" 2144 and args[0].attr == "__class__"
2145 ): 2145 ):
2146 self.violations.append((node, "M582")) 2146 self.violations.append((node, "M-582"))
2147 2147
2148 # bad getattr and setattr 2148 # bad getattr and setattr
2149 if ( 2149 if (
2150 node.func.id in ("getattr", "hasattr") 2150 node.func.id in ("getattr", "hasattr")
2151 and node.args[1].value == "__call__" 2151 and node.args[1].value == "__call__"
2152 ): 2152 ):
2153 self.violations.append((node, "M504")) 2153 self.violations.append((node, "M-504"))
2154 if ( 2154 if (
2155 node.func.id == "getattr" 2155 node.func.id == "getattr"
2156 and len(node.args) == 2 2156 and len(node.args) == 2
2157 and self.__isIdentifier(node.args[1]) 2157 and self.__isIdentifier(node.args[1])
2158 and iskeyword(AstUtilities.getValue(node.args[1])) 2158 and iskeyword(AstUtilities.getValue(node.args[1]))
2159 ): 2159 ):
2160 self.violations.append((node, "M509")) 2160 self.violations.append((node, "M-509"))
2161 elif ( 2161 elif (
2162 node.func.id == "setattr" 2162 node.func.id == "setattr"
2163 and len(node.args) == 3 2163 and len(node.args) == 3
2164 and self.__isIdentifier(node.args[1]) 2164 and self.__isIdentifier(node.args[1])
2165 and iskeyword(AstUtilities.getValue(node.args[1])) 2165 and iskeyword(AstUtilities.getValue(node.args[1]))
2166 ): 2166 ):
2167 self.violations.append((node, "M510")) 2167 self.violations.append((node, "M-510"))
2168 2168
2169 self.__checkForM526(node) 2169 self.__checkForM526(node)
2170 2170
2171 self.__checkForM528(node) 2171 self.__checkForM528(node)
2172 self.__checkForM534(node) 2172 self.__checkForM534(node)
2207 if ( 2207 if (
2208 isinstance(target, ast.Attribute) 2208 isinstance(target, ast.Attribute)
2209 and isinstance(target.value, ast.Name) 2209 and isinstance(target.value, ast.Name)
2210 and (target.value.id, target.attr) == ("os", "environ") 2210 and (target.value.id, target.attr) == ("os", "environ")
2211 ): 2211 ):
2212 self.violations.append((node, "M503")) 2212 self.violations.append((node, "M-503"))
2213 2213
2214 self.generic_visit(node) 2214 self.generic_visit(node)
2215 2215
2216 def visit_For(self, node): 2216 def visit_For(self, node):
2217 """ 2217 """
2307 """ 2307 """
2308 if ( 2308 if (
2309 AstUtilities.isNameConstant(node.test) 2309 AstUtilities.isNameConstant(node.test)
2310 and AstUtilities.getValue(node.test) is False 2310 and AstUtilities.getValue(node.test) is False
2311 ): 2311 ):
2312 self.violations.append((node, "M511")) 2312 self.violations.append((node, "M-511"))
2313 2313
2314 self.generic_visit(node) 2314 self.generic_visit(node)
2315 2315
2316 def visit_AsyncFunctionDef(self, node): 2316 def visit_AsyncFunctionDef(self, node):
2317 """ 2317 """
2421 """ 2421 """
2422 for value in node.values: 2422 for value in node.values:
2423 if isinstance(value, ast.FormattedValue): 2423 if isinstance(value, ast.FormattedValue):
2424 return 2424 return
2425 2425
2426 self.violations.append((node, "M581")) 2426 self.violations.append((node, "M-581"))
2427 2427
2428 def visit_AnnAssign(self, node): 2428 def visit_AnnAssign(self, node):
2429 """ 2429 """
2430 Public method to check annotated assign statements. 2430 Public method to check annotated assign statements.
2431 2431
2510 return # stripping just one character 2510 return # stripping just one character
2511 2511
2512 if len(value) == len(set(value)): 2512 if len(value) == len(set(value)):
2513 return # no characters appear more than once 2513 return # no characters appear more than once
2514 2514
2515 self.violations.append((node, "M505")) 2515 self.violations.append((node, "M-505"))
2516 2516
2517 def __checkForM506_M508(self, node): 2517 def __checkForM506_M508(self, node):
2518 """ 2518 """
2519 Private method to check the use of mutable literals, comprehensions and calls. 2519 Private method to check the use of mutable literals, comprehensions and calls.
2520 2520
2521 @param node reference to the node to be processed 2521 @param node reference to the node to be processed
2522 @type ast.AsyncFunctionDef or ast.FunctionDef 2522 @type ast.AsyncFunctionDef or ast.FunctionDef
2523 """ 2523 """
2524 visitor = FunctionDefDefaultsVisitor("M506", "M508") 2524 visitor = FunctionDefDefaultsVisitor("M-506", "M-508")
2525 visitor.visit(node.args.defaults + node.args.kw_defaults) 2525 visitor.visit(node.args.defaults + node.args.kw_defaults)
2526 self.violations.extend(visitor.errors) 2526 self.violations.extend(visitor.errors)
2527 2527
2528 def __checkForM507(self, node): 2528 def __checkForM507(self, node):
2529 """ 2529 """
2539 for expr in node.body: 2539 for expr in node.body:
2540 body.visit(expr) 2540 body.visit(expr)
2541 usedNames = set(body.getNames()) 2541 usedNames = set(body.getNames())
2542 for name in sorted(ctrlNames - usedNames): 2542 for name in sorted(ctrlNames - usedNames):
2543 n = targets.getNames()[name][0] 2543 n = targets.getNames()[name][0]
2544 self.violations.append((n, "M507", name)) 2544 self.violations.append((n, "M-507", name))
2545 2545
2546 def __checkForM512(self, node): 2546 def __checkForM512(self, node):
2547 """ 2547 """
2548 Private method to check for return/continue/break inside finally blocks. 2548 Private method to check for return/continue/break inside finally blocks.
2549 2549
2557 2557
2558 if isinstance(node, (ast.While, ast.For)): 2558 if isinstance(node, (ast.While, ast.For)):
2559 badNodeTypes = (ast.Return,) 2559 badNodeTypes = (ast.Return,)
2560 2560
2561 elif isinstance(node, badNodeTypes): 2561 elif isinstance(node, badNodeTypes):
2562 self.violations.append((node, "M512", self.__inTryStar)) 2562 self.violations.append((node, "M-512", self.__inTryStar))
2563 2563
2564 for child in ast.iter_child_nodes(node): 2564 for child in ast.iter_child_nodes(node):
2565 _loop(child, badNodeTypes) 2565 _loop(child, badNodeTypes)
2566 2566
2567 for child in node.finalbody: 2567 for child in node.finalbody:
2591 elif isinstance(handler, (ast.Call, ast.Starred)): 2591 elif isinstance(handler, (ast.Call, ast.Starred)):
2592 ignoredHandlers.append(handler) 2592 ignoredHandlers.append(handler)
2593 else: 2593 else:
2594 badHandlers.append(handler) 2594 badHandlers.append(handler)
2595 if badHandlers: 2595 if badHandlers:
2596 self.violations.append((node, "M530")) 2596 self.violations.append((node, "M-530"))
2597 if len(names) == 0 and not badHandlers and not ignoredHandlers: 2597 if len(names) == 0 and not badHandlers and not ignoredHandlers:
2598 self.violations.append((node, "M529", self.__inTryStar)) 2598 self.violations.append((node, "M-529", self.__inTryStar))
2599 elif ( 2599 elif (
2600 len(names) == 1 2600 len(names) == 1
2601 and not badHandlers 2601 and not badHandlers
2602 and not ignoredHandlers 2602 and not ignoredHandlers
2603 and isinstance(node.type, ast.Tuple) 2603 and isinstance(node.type, ast.Tuple)
2604 ): 2604 ):
2605 self.violations.append((node, "M513", *names, self.__inTryStar)) 2605 self.violations.append((node, "M-513", *names, self.__inTryStar))
2606 else: 2606 else:
2607 maybeError = self.__checkRedundantExcepthandlers( 2607 maybeError = self.__checkRedundantExcepthandlers(
2608 names, node, self.__inTryStar 2608 names, node, self.__inTryStar
2609 ) 2609 )
2610 if maybeError is not None: 2610 if maybeError is not None:
2617 2617
2618 @param node reference to the node to be processed 2618 @param node reference to the node to be processed
2619 @type ast.Compare 2619 @type ast.Compare
2620 """ 2620 """
2621 if isinstance(self.nodeStack[-2], ast.Expr): 2621 if isinstance(self.nodeStack[-2], ast.Expr):
2622 self.violations.append((node, "M515")) 2622 self.violations.append((node, "M-515"))
2623 2623
2624 def __checkForM516(self, node): 2624 def __checkForM516(self, node):
2625 """ 2625 """
2626 Private method to check for raising a literal instead of an exception. 2626 Private method to check for raising a literal instead of an exception.
2627 2627
2631 if ( 2631 if (
2632 AstUtilities.isNameConstant(node.exc) 2632 AstUtilities.isNameConstant(node.exc)
2633 or AstUtilities.isNumber(node.exc) 2633 or AstUtilities.isNumber(node.exc)
2634 or AstUtilities.isString(node.exc) 2634 or AstUtilities.isString(node.exc)
2635 ): 2635 ):
2636 self.violations.append((node, "M516")) 2636 self.violations.append((node, "M-516"))
2637 2637
2638 def __checkForM517(self, node): 2638 def __checkForM517(self, node):
2639 """ 2639 """
2640 Private method to check for use of the evil syntax 2640 Private method to check for use of the evil syntax
2641 'with assertRaises(Exception): or 'with pytest.raises(Exception):'. 2641 'with assertRaises(Exception): or 'with pytest.raises(Exception):'.
2671 and len(itemContext.args) == 1 2671 and len(itemContext.args) == 1
2672 and isinstance(itemContext.args[0], ast.Name) 2672 and isinstance(itemContext.args[0], ast.Name)
2673 and itemContext.args[0].id in ("Exception", "BaseException") 2673 and itemContext.args[0].id in ("Exception", "BaseException")
2674 and not item.optional_vars 2674 and not item.optional_vars
2675 ): 2675 ):
2676 self.violations.append((node, "M517")) 2676 self.violations.append((node, "M-517"))
2677 2677
2678 def __checkForM518(self, node): 2678 def __checkForM518(self, node):
2679 """ 2679 """
2680 Private method to check for useless expressions. 2680 Private method to check for useless expressions.
2681 2681
2696 (int, float, complex, bytes, bool), 2696 (int, float, complex, bytes, bool),
2697 ) 2697 )
2698 or node.value.value is None 2698 or node.value.value is None
2699 ) 2699 )
2700 ): 2700 ):
2701 self.violations.append((node, "M518", node.value.__class__.__name__)) 2701 self.violations.append((node, "M-518", node.value.__class__.__name__))
2702 2702
2703 def __checkForM519(self, node): 2703 def __checkForM519(self, node):
2704 """ 2704 """
2705 Private method to check for use of 'functools.lru_cache' or 'functools.cache'. 2705 Private method to check for use of 'functools.lru_cache' or 'functools.cache'.
2706 2706
2729 for idx, decorator in enumerate(resolvedDecorators): 2729 for idx, decorator in enumerate(resolvedDecorators):
2730 if decorator in {"classmethod", "staticmethod"}: 2730 if decorator in {"classmethod", "staticmethod"}:
2731 return 2731 return
2732 2732
2733 if decorator in caches: 2733 if decorator in caches:
2734 self.violations.append((node.decorator_list[idx], "M519")) 2734 self.violations.append((node.decorator_list[idx], "M-519"))
2735 return 2735 return
2736 2736
2737 def __checkForM520(self, node): 2737 def __checkForM520(self, node):
2738 """ 2738 """
2739 Private method to check for a loop that modifies its iterable. 2739 Private method to check for a loop that modifies its iterable.
2750 itersetNames = set(iterset.getNames()) 2750 itersetNames = set(iterset.getNames())
2751 2751
2752 for name in sorted(ctrlNames): 2752 for name in sorted(ctrlNames):
2753 if name in itersetNames: 2753 if name in itersetNames:
2754 n = targets.getNames()[name][0] 2754 n = targets.getNames()[name][0]
2755 self.violations.append((n, "M520")) 2755 self.violations.append((n, "M-520"))
2756 2756
2757 def __checkForM521(self, node): 2757 def __checkForM521(self, node):
2758 """ 2758 """
2759 Private method to check for use of an f-string as docstring. 2759 Private method to check for use of an f-string as docstring.
2760 2760
2764 if ( 2764 if (
2765 node.body 2765 node.body
2766 and isinstance(node.body[0], ast.Expr) 2766 and isinstance(node.body[0], ast.Expr)
2767 and isinstance(node.body[0].value, ast.JoinedStr) 2767 and isinstance(node.body[0].value, ast.JoinedStr)
2768 ): 2768 ):
2769 self.violations.append((node.body[0].value, "M521")) 2769 self.violations.append((node.body[0].value, "M-521"))
2770 2770
2771 def __checkForM522(self, node): 2771 def __checkForM522(self, node):
2772 """ 2772 """
2773 Private method to check for use of an f-string as docstring. 2773 Private method to check for use of an f-string as docstring.
2774 2774
2784 and itemContext.func.value.id == "contextlib" 2784 and itemContext.func.value.id == "contextlib"
2785 and hasattr(itemContext.func, "attr") 2785 and hasattr(itemContext.func, "attr")
2786 and itemContext.func.attr == "suppress" 2786 and itemContext.func.attr == "suppress"
2787 and len(itemContext.args) == 0 2787 and len(itemContext.args) == 0
2788 ): 2788 ):
2789 self.violations.append((node, "M522")) 2789 self.violations.append((node, "M-522"))
2790 2790
2791 def __checkForM523(self, loopNode): 2791 def __checkForM523(self, loopNode):
2792 """ 2792 """
2793 Private method to check that functions (including lambdas) do not use loop 2793 Private method to check that functions (including lambdas) do not use loop
2794 variables. 2794 variables.
2858 if suspiciousVariables: 2858 if suspiciousVariables:
2859 reassignedInLoop = set(self.__getAssignedNames(loopNode)) 2859 reassignedInLoop = set(self.__getAssignedNames(loopNode))
2860 2860
2861 for err in sorted(suspiciousVariables): 2861 for err in sorted(suspiciousVariables):
2862 if reassignedInLoop.issuperset(err[2]): 2862 if reassignedInLoop.issuperset(err[2]):
2863 self.violations.append((err[3], "M523", err[2])) 2863 self.violations.append((err[3], "M-523", err[2]))
2864 2864
2865 def __checkForM524_M527(self, node): 2865 def __checkForM524_M527(self, node):
2866 """ 2866 """
2867 Private method to check for inheritance from abstract classes in abc and lack of 2867 Private method to check for inheritance from abstract classes in abc and lack of
2868 any methods decorated with abstract*. 2868 any methods decorated with abstract*.
2939 if ( 2939 if (
2940 not hasAbstractDecorator 2940 not hasAbstractDecorator
2941 and emptyBody(stmt.body) 2941 and emptyBody(stmt.body)
2942 and not any(map(isOverload, stmt.decorator_list)) 2942 and not any(map(isOverload, stmt.decorator_list))
2943 ): 2943 ):
2944 self.violations.append((stmt, "M527", stmt.name)) 2944 self.violations.append((stmt, "M-527", stmt.name))
2945 2945
2946 if hasMethod and not hasAbstractMethod: 2946 if hasMethod and not hasAbstractMethod:
2947 self.violations.append((node, "M524", node.name)) 2947 self.violations.append((node, "M-524", node.name))
2948 2948
2949 def __checkForM525(self, node): 2949 def __checkForM525(self, node):
2950 """ 2950 """
2951 Private method to check for exceptions being handled multiple times. 2951 Private method to check for exceptions being handled multiple times.
2952 2952
2968 seen.extend(uniques) 2968 seen.extend(uniques)
2969 2969
2970 # sort to have a deterministic output 2970 # sort to have a deterministic output
2971 duplicates = sorted({x for x in seen if seen.count(x) > 1}) 2971 duplicates = sorted({x for x in seen if seen.count(x) > 1})
2972 for duplicate in duplicates: 2972 for duplicate in duplicates:
2973 self.violations.append((node, "M525", duplicate, self.__inTryStar)) 2973 self.violations.append((node, "M-525", duplicate, self.__inTryStar))
2974 2974
2975 def __checkForM526(self, node): 2975 def __checkForM526(self, node):
2976 """ 2976 """
2977 Private method to check for Star-arg unpacking after keyword argument. 2977 Private method to check for Star-arg unpacking after keyword argument.
2978 2978
2990 for starred in starreds: 2990 for starred in starreds:
2991 if (starred.lineno, starred.col_offset) > ( 2991 if (starred.lineno, starred.col_offset) > (
2992 firstKeyword.lineno, 2992 firstKeyword.lineno,
2993 firstKeyword.col_offset, 2993 firstKeyword.col_offset,
2994 ): 2994 ):
2995 self.violations.append((node, "M526")) 2995 self.violations.append((node, "M-526"))
2996 2996
2997 def __checkForM528(self, node): 2997 def __checkForM528(self, node):
2998 """ 2998 """
2999 Private method to check for warn without stacklevel. 2999 Private method to check for warn without stacklevel.
3000 3000
3009 and not any(kw.arg == "stacklevel" for kw in node.keywords) 3009 and not any(kw.arg == "stacklevel" for kw in node.keywords)
3010 and len(node.args) < 3 3010 and len(node.args) < 3
3011 and not any(isinstance(a, ast.Starred) for a in node.args) 3011 and not any(isinstance(a, ast.Starred) for a in node.args)
3012 and not any(kw.arg is None for kw in node.keywords) 3012 and not any(kw.arg is None for kw in node.keywords)
3013 ): 3013 ):
3014 self.violations.append((node, "M528")) 3014 self.violations.append((node, "M-528"))
3015 3015
3016 def __checkForM531(self, loopNode): 3016 def __checkForM531(self, loopNode):
3017 """ 3017 """
3018 Private method to check that 'itertools.groupby' isn't iterated over more than 3018 Private method to check that 'itertools.groupby' isn't iterated over more than
3019 once. 3019 once.
3049 for nestedNode in self.__walkList(node.body): 3049 for nestedNode in self.__walkList(node.body):
3050 if ( 3050 if (
3051 isinstance(nestedNode, ast.Name) 3051 isinstance(nestedNode, ast.Name)
3052 and nestedNode.id == groupName 3052 and nestedNode.id == groupName
3053 ): 3053 ):
3054 self.violations.append((nestedNode, "M531")) 3054 self.violations.append((nestedNode, "M-531"))
3055 3055
3056 # Handle multiple uses 3056 # Handle multiple uses
3057 if isinstance(node, ast.Name) and node.id == groupName: 3057 if isinstance(node, ast.Name) and node.id == groupName:
3058 numUsages += 1 3058 numUsages += 1
3059 if numUsages > 1: 3059 if numUsages > 1:
3060 self.violations.append((nestedNode, "M531")) 3060 self.violations.append((nestedNode, "M-531"))
3061 3061
3062 def __checkForM532(self, node): 3062 def __checkForM532(self, node):
3063 """ 3063 """
3064 Private method to check for possible unintentional typing annotation. 3064 Private method to check for possible unintentional typing annotation.
3065 3065
3076 isinstance(node.target, ast.Attribute) 3076 isinstance(node.target, ast.Attribute)
3077 and node.target.value.id != "self" 3077 and node.target.value.id != "self"
3078 ) 3078 )
3079 ) 3079 )
3080 ): 3080 ):
3081 self.violations.append((node, "M532")) 3081 self.violations.append((node, "M-532"))
3082 3082
3083 def __checkForM533(self, node): 3083 def __checkForM533(self, node):
3084 """ 3084 """
3085 Private method to check a set for duplicate items. 3085 Private method to check a set for duplicate items.
3086 3086
3090 seen = set() 3090 seen = set()
3091 for elt in node.elts: 3091 for elt in node.elts:
3092 if not isinstance(elt, ast.Constant): 3092 if not isinstance(elt, ast.Constant):
3093 continue 3093 continue
3094 if elt.value in seen: 3094 if elt.value in seen:
3095 self.violations.append((node, "M533", repr(elt.value))) 3095 self.violations.append((node, "M-533", repr(elt.value)))
3096 else: 3096 else:
3097 seen.add(elt.value) 3097 seen.add(elt.value)
3098 3098
3099 def __checkForM534(self, node): 3099 def __checkForM534(self, node):
3100 """ 3100 """
3111 return 3111 return
3112 3112
3113 def check(numArgs, paramName): 3113 def check(numArgs, paramName):
3114 if len(node.args) > numArgs: 3114 if len(node.args) > numArgs:
3115 arg = node.args[numArgs] 3115 arg = node.args[numArgs]
3116 self.violations.append((arg, "M534", func.attr, paramName)) 3116 self.violations.append((arg, "M-534", func.attr, paramName))
3117 3117
3118 if func.attr in ("sub", "subn"): 3118 if func.attr in ("sub", "subn"):
3119 check(3, "count") 3119 check(3, "count")
3120 elif func.attr == "split": 3120 elif func.attr == "split":
3121 check(2, "maxsplit") 3121 check(2, "maxsplit")
3129 3129
3130 @param node reference to the node to be processed 3130 @param node reference to the node to be processed
3131 @type ast.DictComp 3131 @type ast.DictComp
3132 """ 3132 """
3133 if isinstance(node.key, ast.Constant): 3133 if isinstance(node.key, ast.Constant):
3134 self.violations.append((node, "M535", node.key.value)) 3134 self.violations.append((node, "M-535", node.key.value))
3135 elif isinstance( 3135 elif isinstance(
3136 node.key, ast.Name 3136 node.key, ast.Name
3137 ) and node.key.id not in self.__getDictCompLoopAndNamedExprVarNames(node): 3137 ) and node.key.id not in self.__getDictCompLoopAndNamedExprVarNames(node):
3138 self.violations.append((node, "M535", node.key.id)) 3138 self.violations.append((node, "M-535", node.key.id))
3139 3139
3140 def __checkForM539(self, node): 3140 def __checkForM539(self, node):
3141 """ 3141 """
3142 Private method to check for correct ContextVar usage. 3142 Private method to check for correct ContextVar usage.
3143 3143
3160 if kw.arg == "default": 3160 if kw.arg == "default":
3161 break 3161 break
3162 else: 3162 else:
3163 return 3163 return
3164 3164
3165 visitor = FunctionDefDefaultsVisitor("M539", "M539") 3165 visitor = FunctionDefDefaultsVisitor("M-539", "M-539")
3166 visitor.visit(kw.value) 3166 visitor.visit(kw.value)
3167 self.violations.extend(visitor.errors) 3167 self.violations.extend(visitor.errors)
3168 3168
3169 def __checkForM540AddNote(self, node): 3169 def __checkForM540AddNote(self, node):
3170 """ 3170 """
3251 seen = set() 3251 seen = set()
3252 for index in keyIndices: 3252 for index in keyIndices:
3253 value = convertToValue(node.values[index]) 3253 value = convertToValue(node.values[index])
3254 if value in seen: 3254 if value in seen:
3255 keyNode = node.keys[index] 3255 keyNode = node.keys[index]
3256 self.violations.append((keyNode, "M541")) 3256 self.violations.append((keyNode, "M-541"))
3257 seen.add(value) 3257 seen.add(value)
3258 3258
3259 def __checkForM569(self, node): 3259 def __checkForM569(self, node):
3260 """ 3260 """
3261 Private method to check for changes to a loop's mutable iterable. 3261 Private method to check for changes to a loop's mutable iterable.
3270 else: 3270 else:
3271 return 3271 return
3272 checker = M569Checker(name, self) 3272 checker = M569Checker(name, self)
3273 checker.visit(node.body) 3273 checker.visit(node.body)
3274 for mutation in checker.mutations: 3274 for mutation in checker.mutations:
3275 self.violations.append((mutation, "M569")) 3275 self.violations.append((mutation, "M-569"))
3276 3276
3277 3277
3278 class M569Checker(ast.NodeVisitor): 3278 class M569Checker(ast.NodeVisitor):
3279 """ 3279 """
3280 Class traversing a 'for' loop body to check for modifications to a loop's 3280 Class traversing a 'for' loop body to check for modifications to a loop's
3943 """ 3943 """
3944 Private method to check for implicit return values. 3944 Private method to check for implicit return values.
3945 """ 3945 """
3946 for node in self.returns: 3946 for node in self.returns:
3947 if not node.value: 3947 if not node.value:
3948 self.violations.append((node, "M832")) 3948 self.violations.append((node, "M-832"))
3949 3949
3950 def __checkUnnecessaryReturnNone(self): 3950 def __checkUnnecessaryReturnNone(self):
3951 """ 3951 """
3952 Private method to check for an unnecessary 'return None' statement. 3952 Private method to check for an unnecessary 'return None' statement.
3953 """ 3953 """
3954 for node in self.returns: 3954 for node in self.returns:
3955 if self.__isNone(node.value): 3955 if self.__isNone(node.value):
3956 self.violations.append((node, "M831")) 3956 self.violations.append((node, "M-831"))
3957 3957
3958 def __checkImplicitReturn(self, node): 3958 def __checkImplicitReturn(self, node):
3959 """ 3959 """
3960 Private method to check for an implicit return statement. 3960 Private method to check for an implicit return statement.
3961 3961
3962 @param node reference to the node to check 3962 @param node reference to the node to check
3963 @type ast.AST 3963 @type ast.AST
3964 """ 3964 """
3965 if isinstance(node, ast.If): 3965 if isinstance(node, ast.If):
3966 if not node.body or not node.orelse: 3966 if not node.body or not node.orelse:
3967 self.violations.append((node, "M833")) 3967 self.violations.append((node, "M-833"))
3968 return 3968 return
3969 3969
3970 self.__checkImplicitReturn(node.body[-1]) 3970 self.__checkImplicitReturn(node.body[-1])
3971 self.__checkImplicitReturn(node.orelse[-1]) 3971 self.__checkImplicitReturn(node.orelse[-1])
3972 return 3972 return
3985 try: 3985 try:
3986 okNodes = (ast.Return, ast.Raise, ast.While, ast.Try) 3986 okNodes = (ast.Return, ast.Raise, ast.While, ast.Try)
3987 except AttributeError: 3987 except AttributeError:
3988 okNodes = (ast.Return, ast.Raise, ast.While) 3988 okNodes = (ast.Return, ast.Raise, ast.While)
3989 if not isinstance(node, okNodes): 3989 if not isinstance(node, okNodes):
3990 self.violations.append((node, "M833")) 3990 self.violations.append((node, "M-833"))
3991 3991
3992 def __checkUnnecessaryAssign(self, node): 3992 def __checkUnnecessaryAssign(self, node):
3993 """ 3993 """
3994 Private method to check for an unnecessary assign statement. 3994 Private method to check for an unnecessary assign statement.
3995 3995
4004 4004
4005 if varname not in self.assigns: 4005 if varname not in self.assigns:
4006 return 4006 return
4007 4007
4008 if varname not in self.refs: 4008 if varname not in self.refs:
4009 self.violations.append((node, "M834")) 4009 self.violations.append((node, "M-834"))
4010 return 4010 return
4011 4011
4012 if self.__hasRefsBeforeNextAssign(varname, returnLineno): 4012 if self.__hasRefsBeforeNextAssign(varname, returnLineno):
4013 return 4013 return
4014 4014
4015 self.violations.append((node, "M834")) 4015 self.violations.append((node, "M-834"))
4016 4016
4017 def __hasRefsBeforeNextAssign(self, varname, returnLineno): 4017 def __hasRefsBeforeNextAssign(self, varname, returnLineno):
4018 """ 4018 """
4019 Private method to check for references before a following assign 4019 Private method to check for references before a following assign
4020 statement. 4020 statement.
4124 AstUtilities.isNameConstant(tzinfoKeyword.value) 4124 AstUtilities.isNameConstant(tzinfoKeyword.value)
4125 and AstUtilities.getValue(tzinfoKeyword.value) is None 4125 and AstUtilities.getValue(tzinfoKeyword.value) is None
4126 ) 4126 )
4127 4127
4128 if not (isCase1 or isCase2): 4128 if not (isCase1 or isCase2):
4129 self.violations.append((node, "M301")) 4129 self.violations.append((node, "M-301"))
4130 4130
4131 elif node.func.attr == "time": 4131 elif node.func.attr == "time":
4132 # time(12, 10, 45, 0, datetime.timezone.utc) 4132 # time(12, 10, 45, 0, datetime.timezone.utc)
4133 isCase1 = len(node.args) >= 5 and not ( 4133 isCase1 = len(node.args) >= 5 and not (
4134 AstUtilities.isNameConstant(node.args[4]) 4134 AstUtilities.isNameConstant(node.args[4])
4141 AstUtilities.isNameConstant(tzinfoKeyword.value) 4141 AstUtilities.isNameConstant(tzinfoKeyword.value)
4142 and AstUtilities.getValue(tzinfoKeyword.value) is None 4142 and AstUtilities.getValue(tzinfoKeyword.value) is None
4143 ) 4143 )
4144 4144
4145 if not (isCase1 or isCase2): 4145 if not (isCase1 or isCase2):
4146 self.violations.append((node, "M321")) 4146 self.violations.append((node, "M-321"))
4147 4147
4148 elif node.func.attr == "date": 4148 elif node.func.attr == "date":
4149 self.violations.append((node, "M311")) 4149 self.violations.append((node, "M-311"))
4150 4150
4151 if isDateTimeClass or isDateTimeModuleAndClass: 4151 if isDateTimeClass or isDateTimeModuleAndClass:
4152 if node.func.attr == "today": 4152 if node.func.attr == "today":
4153 self.violations.append((node, "M302")) 4153 self.violations.append((node, "M-302"))
4154 4154
4155 elif node.func.attr == "utcnow": 4155 elif node.func.attr == "utcnow":
4156 self.violations.append((node, "M303")) 4156 self.violations.append((node, "M-303"))
4157 4157
4158 elif node.func.attr == "utcfromtimestamp": 4158 elif node.func.attr == "utcfromtimestamp":
4159 self.violations.append((node, "M304")) 4159 self.violations.append((node, "M-304"))
4160 4160
4161 elif node.func.attr in "now": 4161 elif node.func.attr in "now":
4162 # datetime.now(UTC) 4162 # datetime.now(UTC)
4163 isCase1 = ( 4163 isCase1 = (
4164 len(node.args) == 1 4164 len(node.args) == 1
4175 AstUtilities.isNameConstant(tzKeyword.value) 4175 AstUtilities.isNameConstant(tzKeyword.value)
4176 and AstUtilities.getValue(tzKeyword.value) is None 4176 and AstUtilities.getValue(tzKeyword.value) is None
4177 ) 4177 )
4178 4178
4179 if not (isCase1 or isCase2): 4179 if not (isCase1 or isCase2):
4180 self.violations.append((node, "M305")) 4180 self.violations.append((node, "M-305"))
4181 4181
4182 elif node.func.attr == "fromtimestamp": 4182 elif node.func.attr == "fromtimestamp":
4183 # datetime.fromtimestamp(1234, UTC) 4183 # datetime.fromtimestamp(1234, UTC)
4184 isCase1 = ( 4184 isCase1 = (
4185 len(node.args) == 2 4185 len(node.args) == 2
4196 AstUtilities.isNameConstant(tzKeyword.value) 4196 AstUtilities.isNameConstant(tzKeyword.value)
4197 and AstUtilities.getValue(tzKeyword.value) is None 4197 and AstUtilities.getValue(tzKeyword.value) is None
4198 ) 4198 )
4199 4199
4200 if not (isCase1 or isCase2): 4200 if not (isCase1 or isCase2):
4201 self.violations.append((node, "M306")) 4201 self.violations.append((node, "M-306"))
4202 4202
4203 elif node.func.attr == "strptime": 4203 elif node.func.attr == "strptime":
4204 # datetime.strptime(...).replace(tzinfo=UTC) 4204 # datetime.strptime(...).replace(tzinfo=UTC)
4205 parent = getattr(node, "_dtCheckerParent", None) 4205 parent = getattr(node, "_dtCheckerParent", None)
4206 pparent = getattr(parent, "_dtCheckerParent", None) 4206 pparent = getattr(parent, "_dtCheckerParent", None)
4214 AstUtilities.isNameConstant(tzinfoKeyword.value) 4214 AstUtilities.isNameConstant(tzinfoKeyword.value)
4215 and AstUtilities.getValue(tzinfoKeyword.value) is None 4215 and AstUtilities.getValue(tzinfoKeyword.value) is None
4216 ) 4216 )
4217 4217
4218 if not isCase1: 4218 if not isCase1:
4219 self.violations.append((node, "M307")) 4219 self.violations.append((node, "M-307"))
4220 4220
4221 elif node.func.attr == "fromordinal": 4221 elif node.func.attr == "fromordinal":
4222 self.violations.append((node, "M308")) 4222 self.violations.append((node, "M-308"))
4223 4223
4224 # date.something() 4224 # date.something()
4225 isDateClass = ( 4225 isDateClass = (
4226 isinstance(node.func, ast.Attribute) 4226 isinstance(node.func, ast.Attribute)
4227 and isinstance(node.func.value, ast.Name) 4227 and isinstance(node.func.value, ast.Name)
4237 and node.func.value.value.id == "datetime" 4237 and node.func.value.value.id == "datetime"
4238 ) 4238 )
4239 4239
4240 if isDateClass or isDateModuleAndClass: 4240 if isDateClass or isDateModuleAndClass:
4241 if node.func.attr == "today": 4241 if node.func.attr == "today":
4242 self.violations.append((node, "M312")) 4242 self.violations.append((node, "M-312"))
4243 4243
4244 elif node.func.attr == "fromtimestamp": 4244 elif node.func.attr == "fromtimestamp":
4245 self.violations.append((node, "M313")) 4245 self.violations.append((node, "M-313"))
4246 4246
4247 elif node.func.attr == "fromordinal": 4247 elif node.func.attr == "fromordinal":
4248 self.violations.append((node, "M314")) 4248 self.violations.append((node, "M-314"))
4249 4249
4250 elif node.func.attr == "fromisoformat": 4250 elif node.func.attr == "fromisoformat":
4251 self.violations.append((node, "M315")) 4251 self.violations.append((node, "M-315"))
4252 4252
4253 self.generic_visit(node) 4253 self.generic_visit(node)
4254 4254
4255 4255
4256 class SysVersionVisitor(ast.NodeVisitor): 4256 class SysVersionVisitor(ast.NodeVisitor):
4335 4335
4336 @param node reference to the node to be processed 4336 @param node reference to the node to be processed
4337 @type ast.Subscript 4337 @type ast.Subscript
4338 """ 4338 """
4339 if self.__isSysVersionUpperSlice(node, 1): 4339 if self.__isSysVersionUpperSlice(node, 1):
4340 self.violations.append((node.value, "M423")) 4340 self.violations.append((node.value, "M-423"))
4341 elif self.__isSysVersionUpperSlice(node, 3): 4341 elif self.__isSysVersionUpperSlice(node, 3):
4342 self.violations.append((node.value, "M401")) 4342 self.violations.append((node.value, "M-401"))
4343 elif ( 4343 elif (
4344 self.__isSys("version", node.value) 4344 self.__isSys("version", node.value)
4345 and isinstance(node.slice, ast.Index) 4345 and isinstance(node.slice, ast.Index)
4346 and AstUtilities.isNumber(node.slice.value) 4346 and AstUtilities.isNumber(node.slice.value)
4347 and AstUtilities.getValue(node.slice.value) == 2 4347 and AstUtilities.getValue(node.slice.value) == 2
4348 ): 4348 ):
4349 self.violations.append((node.value, "M402")) 4349 self.violations.append((node.value, "M-402"))
4350 elif ( 4350 elif (
4351 self.__isSys("version", node.value) 4351 self.__isSys("version", node.value)
4352 and isinstance(node.slice, ast.Index) 4352 and isinstance(node.slice, ast.Index)
4353 and AstUtilities.isNumber(node.slice.value) 4353 and AstUtilities.isNumber(node.slice.value)
4354 and AstUtilities.getValue(node.slice.value) == 0 4354 and AstUtilities.getValue(node.slice.value) == 0
4355 ): 4355 ):
4356 self.violations.append((node.value, "M421")) 4356 self.violations.append((node.value, "M-421"))
4357 4357
4358 self.generic_visit(node) 4358 self.generic_visit(node)
4359 4359
4360 def visit_Compare(self, node): 4360 def visit_Compare(self, node):
4361 """ 4361 """
4373 and len(node.ops) == 1 4373 and len(node.ops) == 1
4374 and isinstance(node.ops[0], ast.Eq) 4374 and isinstance(node.ops[0], ast.Eq)
4375 and AstUtilities.isNumber(node.comparators[0]) 4375 and AstUtilities.isNumber(node.comparators[0])
4376 and AstUtilities.getValue(node.comparators[0]) == 3 4376 and AstUtilities.getValue(node.comparators[0]) == 3
4377 ): 4377 ):
4378 self.violations.append((node.left, "M411")) 4378 self.violations.append((node.left, "M-411"))
4379 elif ( 4379 elif (
4380 self.__isSys("version", node.left) 4380 self.__isSys("version", node.left)
4381 and len(node.ops) == 1 4381 and len(node.ops) == 1
4382 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) 4382 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE))
4383 and AstUtilities.isString(node.comparators[0]) 4383 and AstUtilities.isString(node.comparators[0])
4384 ): 4384 ):
4385 if len(AstUtilities.getValue(node.comparators[0])) == 1: 4385 if len(AstUtilities.getValue(node.comparators[0])) == 1:
4386 errorCode = "M422" 4386 errorCode = "M-422"
4387 else: 4387 else:
4388 errorCode = "M403" 4388 errorCode = "M-403"
4389 self.violations.append((node.left, errorCode)) 4389 self.violations.append((node.left, errorCode))
4390 elif ( 4390 elif (
4391 isinstance(node.left, ast.Subscript) 4391 isinstance(node.left, ast.Subscript)
4392 and self.__isSys("version_info", node.left.value) 4392 and self.__isSys("version_info", node.left.value)
4393 and isinstance(node.left.slice, ast.Index) 4393 and isinstance(node.left.slice, ast.Index)
4395 and AstUtilities.getValue(node.left.slice.value) == 1 4395 and AstUtilities.getValue(node.left.slice.value) == 1
4396 and len(node.ops) == 1 4396 and len(node.ops) == 1
4397 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) 4397 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE))
4398 and AstUtilities.isNumber(node.comparators[0]) 4398 and AstUtilities.isNumber(node.comparators[0])
4399 ): 4399 ):
4400 self.violations.append((node, "M413")) 4400 self.violations.append((node, "M-413"))
4401 elif ( 4401 elif (
4402 isinstance(node.left, ast.Attribute) 4402 isinstance(node.left, ast.Attribute)
4403 and self.__isSys("version_info", node.left.value) 4403 and self.__isSys("version_info", node.left.value)
4404 and node.left.attr == "minor" 4404 and node.left.attr == "minor"
4405 and len(node.ops) == 1 4405 and len(node.ops) == 1
4406 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) 4406 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE))
4407 and AstUtilities.isNumber(node.comparators[0]) 4407 and AstUtilities.isNumber(node.comparators[0])
4408 ): 4408 ):
4409 self.violations.append((node, "M414")) 4409 self.violations.append((node, "M-414"))
4410 4410
4411 self.generic_visit(node) 4411 self.generic_visit(node)
4412 4412
4413 def visit_Attribute(self, node): 4413 def visit_Attribute(self, node):
4414 """ 4414 """
4420 if ( 4420 if (
4421 isinstance(node.value, ast.Name) 4421 isinstance(node.value, ast.Name)
4422 and node.value.id == "six" 4422 and node.value.id == "six"
4423 and node.attr == "PY3" 4423 and node.attr == "PY3"
4424 ): 4424 ):
4425 self.violations.append((node, "M412")) 4425 self.violations.append((node, "M-412"))
4426 4426
4427 self.generic_visit(node) 4427 self.generic_visit(node)
4428 4428
4429 def visit_Name(self, node): 4429 def visit_Name(self, node):
4430 """ 4430 """
4432 4432
4433 @param node reference to the node to be processed 4433 @param node reference to the node to be processed
4434 @type ast.Name 4434 @type ast.Name
4435 """ 4435 """
4436 if node.id == "PY3" and self.__fromImports.get(node.id) == "six": 4436 if node.id == "PY3" and self.__fromImports.get(node.id) == "six":
4437 self.violations.append((node, "M412")) 4437 self.violations.append((node, "M-412"))
4438 4438
4439 self.generic_visit(node) 4439 self.generic_visit(node)
4440 4440
4441 4441
4442 class DefaultMatchCaseVisitor(ast.NodeVisitor): 4442 class DefaultMatchCaseVisitor(ast.NodeVisitor):
4477 @ytype tyuple of (ast.AST, str) 4477 @ytype tyuple of (ast.AST, str)
4478 """ 4478 """
4479 for case in node.cases: 4479 for case in node.cases:
4480 if self.__emptyMatchDefault(case): 4480 if self.__emptyMatchDefault(case):
4481 if self.__lastStatementDoesNotRaise(case): 4481 if self.__lastStatementDoesNotRaise(case):
4482 yield self.__findBadNode(case), "M901" 4482 yield self.__findBadNode(case), "M-901"
4483 elif self.__returnPrecedesExceptionRaising(case): 4483 elif self.__returnPrecedesExceptionRaising(case):
4484 yield self.__findBadNode(case), "M902" 4484 yield self.__findBadNode(case), "M-902"
4485 4485
4486 def __emptyMatchDefault(self, case): 4486 def __emptyMatchDefault(self, case):
4487 """ 4487 """
4488 Private method to check for an empty default match case. 4488 Private method to check for an empty default match case.
4489 4489

eric ide

mercurial