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." ,
35
38
IncorrectIterSpec => " A loop iterator has been used that will likely error." ,
36
39
NothingEquality => " Compare against `nothing` using `===`" ,
37
40
NothingNotEq => " Compare against `nothing` using `!==`" ,
@@ -58,8 +61,9 @@ const LintCodeDescriptions = Dict{LintCodes,String}(IncorrectCallArgs => "Possib
58
61
UnassignedKeywordArgument => " Keyword argument not assigned." ,
59
62
CannotDefineFuncAlreadyHasValue => " Cannot define function ; it already has a value." ,
60
63
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
+ )
63
67
64
68
haserror (m:: Meta ) = m. error != = nothing
65
69
haserror (x:: EXPR ) = hasmeta (x) && haserror (x. meta)
@@ -343,22 +347,75 @@ isdocumented(x::EXPR) = parentof(x) isa EXPR && CSTParser.ismacrocall(parentof(x
343
347
344
348
function check_loop_iter (x:: EXPR , env:: ExternalEnv )
345
349
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)
350
358
end
351
359
end
352
360
elseif headof (x) === :generator
361
+ body = x. args[1 ]
353
362
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
358
409
end
359
410
end
360
411
end
361
412
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
362
419
end
363
420
364
421
function check_nothing_equality (x:: EXPR , env:: ExternalEnv )
0 commit comments