1- @enum (LintCodes,
2- MissingRef,
3- IncorrectCallArgs,
4- IncorrectIterSpec,
5- NothingEquality,
6- NothingNotEq,
7- ConstIfCondition,
8- EqInIfConditional,
9- PointlessOR,
10- PointlessAND,
11- UnusedBinding,
12- InvalidTypeDeclaration,
13- UnusedTypeParameter,
14- IncludeLoop,
15- MissingFile,
16- InvalidModuleName,
17- TypePiracy,
18- UnusedFunctionArgument,
19- CannotDeclareConst,
20- InvalidRedefofConst,
21- NotEqDef,
22- KwDefaultMismatch,
23- InappropriateUseOfLiteral,
24- ShouldBeInALoop,
25- TypeDeclOnGlobalVariable,
26- UnsupportedConstLocalVariable,
27- UnassignedKeywordArgument,
28- CannotDefineFuncAlreadyHasValue,
29- DuplicateFuncArgName,
30- IncludePathContainsNULL)
31-
32-
33-
34- const LintCodeDescriptions = Dict {LintCodes,String} (IncorrectCallArgs => " Possible method call error." ,
1+ @enum (
2+ LintCodes,
3+
4+ MissingRef,
5+ IncorrectCallArgs,
6+ IncorrectIterSpec,
7+ NothingEquality,
8+ NothingNotEq,
9+ ConstIfCondition,
10+ EqInIfConditional,
11+ PointlessOR,
12+ PointlessAND,
13+ UnusedBinding,
14+ InvalidTypeDeclaration,
15+ UnusedTypeParameter,
16+ IncludeLoop,
17+ MissingFile,
18+ InvalidModuleName,
19+ TypePiracy,
20+ UnusedFunctionArgument,
21+ CannotDeclareConst,
22+ InvalidRedefofConst,
23+ NotEqDef,
24+ KwDefaultMismatch,
25+ InappropriateUseOfLiteral,
26+ ShouldBeInALoop,
27+ TypeDeclOnGlobalVariable,
28+ UnsupportedConstLocalVariable,
29+ UnassignedKeywordArgument,
30+ CannotDefineFuncAlreadyHasValue,
31+ DuplicateFuncArgName,
32+ IncludePathContainsNULL,
33+ IndexFromLength
34+ )
35+
36+ const LintCodeDescriptions = Dict {LintCodes,String} (
37+ IncorrectCallArgs => " Possible method call error." ,
3538 IncorrectIterSpec => " A loop iterator has been used that will likely error." ,
3639 NothingEquality => " Compare against `nothing` using `===`" ,
3740 NothingNotEq => " Compare against `nothing` using `!==`" ,
@@ -58,8 +61,9 @@ const LintCodeDescriptions = Dict{LintCodes,String}(IncorrectCallArgs => "Possib
5861 UnassignedKeywordArgument => " Keyword argument not assigned." ,
5962 CannotDefineFuncAlreadyHasValue => " Cannot define function ; it already has a value." ,
6063 DuplicateFuncArgName => " Function argument name not unique." ,
61- IncludePathContainsNULL => " Cannot include file, path cotains NULL characters."
62- )
64+ IncludePathContainsNULL => " Cannot include file, path contains NULL characters." ,
65+ IndexFromLength => " Indexing with indices obtained from `length`, `size` etc is discouraged. Use `eachindex` or `axes` instead."
66+ )
6367
6468haserror (m:: Meta ) = m. error != = nothing
6569haserror (x:: EXPR ) = hasmeta (x) && haserror (x. meta)
@@ -343,22 +347,75 @@ isdocumented(x::EXPR) = parentof(x) isa EXPR && CSTParser.ismacrocall(parentof(x
343347
344348function check_loop_iter (x:: EXPR , env:: ExternalEnv )
345349 if headof (x) === :for
346- if length (x. args) > 0 && CSTParser. is_range (x. args[1 ])
347- rng = rhs_of_iterator (x. args[1 ])
348- if headof (rng) === :FLOAT || headof (rng) === :INTEGER || (iscall (rng) && refof (rng. args[1 ]) === getsymbols (env)[:Base ][:length ])
349- seterror! (x. args[1 ], IncorrectIterSpec)
350+ if length (x. args) > 1
351+ body = x. args[2 ]
352+ if headof (x. args[1 ]) === :block && x. args[1 ]. args != = nothing
353+ for arg in x. args[1 ]. args
354+ check_incorrect_iter_spec (arg, body, env)
355+ end
356+ else
357+ check_incorrect_iter_spec (x. args[1 ], body, env)
350358 end
351359 end
352360 elseif headof (x) === :generator
361+ body = x. args[1 ]
353362 for i = 2 : length (x. args)
354- if CSTParser. is_range (x. args[i])
355- rng = rhs_of_iterator (x. args[i])
356- if headof (rng) === :FLOAT || headof (rng) === :INTEGER || (iscall (rng) && refof (rng. args[1 ]) === getsymbols (env)[:Base ][:length ])
357- seterror! (x. args[i], IncorrectIterSpec)
363+ check_incorrect_iter_spec (x. args[i], body, env)
364+ end
365+ end
366+ end
367+
368+ function check_incorrect_iter_spec (x, body, env)
369+ if x. args != = nothing && CSTParser. is_range (x)
370+ rng = rhs_of_iterator (x)
371+
372+ if headof (rng) === :FLOAT || headof (rng) === :INTEGER || (iscall (rng) && refof (rng. args[1 ]) === getsymbols (env)[:Base ][:length ])
373+ seterror! (x, IncorrectIterSpec)
374+ elseif iscall (rng) && valof (rng. args[1 ]) == " :" &&
375+ length (rng. args) === 3 &&
376+ headof (rng. args[2 ]) === :INTEGER &&
377+ iscall (rng. args[3 ]) &&
378+ length (rng. args[3 ]. args) > 1 && (
379+ refof (rng. args[3 ]. args[1 ]) === getsymbols (env)[:Base ][:length ] ||
380+ refof (rng. args[3 ]. args[1 ]) === getsymbols (env)[:Base ][:size ]
381+ )
382+ if length (x. args) >= 1
383+ lhs = x. args[1 ]
384+ arr = rng. args[3 ]. args[2 ]
385+ b = refof (arr)
386+
387+ # 1:length(arr) indexing is ok for Vector and Array specifically
388+ if b isa Binding && (CoreTypes. isarray (b. type) || CoreTypes. isvector (b. type))
389+ return
390+ end
391+ if ! all_underscore (valof (lhs))
392+ if check_is_used_in_getindex (body, lhs, arr)
393+ seterror! (x, IndexFromLength)
394+ end
395+ end
396+ end
397+ end
398+ end
399+ end
400+
401+ function check_is_used_in_getindex (expr, lhs, arr)
402+ if headof (expr) === :ref && expr. args != = nothing && length (expr. args) > 1
403+ this_arr = expr. args[1 ]
404+ if hasref (this_arr) && hasref (arr) && refof (this_arr) == refof (arr)
405+ for index_arg in expr. args[2 : end ]
406+ if hasref (index_arg) && hasref (lhs) && refof (index_arg) == refof (lhs)
407+ seterror! (expr, IndexFromLength)
408+ return true
358409 end
359410 end
360411 end
361412 end
413+ if expr. args != = nothing
414+ for arg in expr. args
415+ check_is_used_in_getindex (arg, lhs, arr) && return true
416+ end
417+ end
418+ return false
362419end
363420
364421function check_nothing_equality (x:: EXPR , env:: ExternalEnv )
0 commit comments