Jeroen Engels
@jfmengels
Elm Radio
// linter-disable rule
// linter-disable rule
// linter-disable rule
// linter-disable rule
// linter-disable rule
// linter-disable rule
// linter-disable rule
// linter-disable rule
| False positive | False negative | |
|---|---|---|
| Linter / compiler | Unnecessary blocking | Missed problem |
| False positive | False negative | |
|---|---|---|
| Linter / compiler | Unnecessary blocking | Missed problem |
| Code optimizer | Optimization bug, potential crash | Missed opportunity |
| False positive | |
|---|---|
| Linter / compiler | Unnecessary blocking |
array.map(function(item) {
console.log(item);
});const array = new NotReallyAnArray();(array-callback-return) Array.prototype.map() expects a return value from function
FpJmybnS3yy8Ny#1kF4D6X?zgoN&TCN5H5CrM9kr
MrAfxo5L6F?KQ583fFcpp$n5z$3T#!g6e6tsjGEr
gNix&i69gQneYe#8Go@gkHSfaDd!@&XY@qHG!kL$
BftTcRJC88z!3Szqq&NChsx7D9C4LJLE&hftKksA
EoTFoxJ$nSis79zbEQGM?5ff7QCsnjoxj7pspGBo
rN6rXdxngDeDm@tlGQasFAC@g5JQ?jBHAEqK9&g!
#5?GiBXhSrfYXX@jC8SGooamhdn&Ag5Cm8$nFGj4(a + b) / 2Integer 2Binary expression
using "+"Reference to "a"Binary expression
using "/"Reference to "b"Parenthesizeda / 0Integer 0Reference to "a"Binary expression
using "/"Rule: No division by 0
If I see a binary expression using "/" where on the right side there's an integer 0 , then report a problem.
someVariable + 1const a = 1; const a = 2;fn(tooFewOrTooManyArguments)import "unknown-module"*But ignorable
92% (56/61) of the recommended rules
87% (228/263) of all the rules
someVariable + 1array.map(function(item) {
console.log(item);
});Array.map (\item -> {- ... -}) arrayExplicit call to the target function
Guaranteed to be an Array
someFunction argument =
let
a = func1(argument)
b = func2(argument)
c = func3(a, b)
in
func4(c)someFunction argument =
let
a = func1(argument)
b = func2(argument)
c = func3(a, b)
in
func4(c)func1 argument =
func5(
GLOBAL_VAR++,
argument
)
func2 argument =
GLOBAL_VAR + argument???
???
References indicate explicit dependencies on values
Side-effects create hidden dependencies on operations
someFunction n =
if needToCompute(n) then
+ let
+ value =
+ expensiveComputation(n)
+ in
use(value)
else
0someFunction n =
- let
- value =
- expensiveComputation(n)
- in
if needToCompute(n) then
use(value)
else
0someFunction n =
let
value =
expensiveComputation(n)
in
if needToCompute(n) then
use(value)
else
0someFunction n =
if f(n) == f(n) then
1
else
0someFunction n =
if True then
1
else
0someFunction n =
1sayHello name =
let
neverUsed = toUpperCase(name)
in
"Hello " ++ format(name)sayHello name =
let
- neverUsed = toUpperCase(name)
+ toUpperCase(name)
in
"Hello " ++ format(name)sayHello name =
- let
- neverUsed = toUpperCase(name)
- in
"Hello " ++ format(name)sayHello name =
let
- neverUsed = toUpperCase(name)
+ toUpperCase(name)
in
"Hello " ++ format(name)Dynamic references
fnName = "unusedFn"
global[fnName](10)unusedFn n =
n + 1Post-analysis
code manipulation
-- Injected at
-- compilation time
unusedFn(10)Arbitrary code execution
eval("unusedFn(10)")
https://slides.com/jfmengels/static-analysis-tools-love-pure-fp
Jeroen Engels
@jfmengels
Elm Radio
someFunction arg =
let
array = []
if someCondition(arg) then
array.push(arg)
else if otherCond(arg) then
array.push(0)
else
array.push(1)
in
arraysomeFunction arg =
if someCondition(arg) then
[ arg ]
else if otherCond(arg) then
[ 0 ]
else
[ 1 ]