Skip to content

Commit ab0dda4

Browse files
committed
Static analysis
PullRequest: truffleruby/561
2 parents 903923e + e5b1ed1 commit ab0dda4

File tree

22 files changed

+551
-477
lines changed

22 files changed

+551
-477
lines changed

.reek.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
detectors:
2+
UncommunicativeVariableName:
3+
enabled: false # we're happy with short variable names
4+
UncommunicativeParameterName:
5+
enabled: false # we're happy with short parameter names
6+
LongParameterList:
7+
enabled: false # we're often meeting an existing Ruby API with fixed parameters
8+
UncommunicativeModuleName:
9+
enabled: false # we're often meeting an existing Ruby API with fixed module names
10+
UncommunicativeMethodName:
11+
enabled: false # we're often meeting an existing Ruby API with fixed method names
12+
BooleanParameter:
13+
enabled: false # we're often meeting an existing Ruby API with boolean parameters
14+
TooManyStatements:
15+
enabled: false # yes we often write longer methods
16+
FeatureEnvy:
17+
enabled: false # we don't want to add new public methods to Ruby API classes to better encapsulate behaviour
18+
UnusedParameters:
19+
enabled: false # primitives mean that the use of parameters can be hidden
20+
NilCheck:
21+
enabled: false # our code is often too low level to factor this away
22+
ManualDispatch:
23+
enabled: false # manual dispatch is often part of Ruby internal semantics
24+
IrresponsibleModule:
25+
enabled: false # we don't comment all modules because we're implementing an existing interface so they do not need a raison d'être
26+
ControlParameter:
27+
enabled: false # we're often meeting an existing Ruby API with control parameters
28+
DataClump:
29+
enabled: false # we're often meeting an existing Ruby API with 'data clumps'
30+
TooManyMethods:
31+
enabled: false # we're often meeting an existing Ruby API with an existing set of methods
32+
TooManyConstants:
33+
enabled: false # we're often meeting an existing Ruby API with an existing set of constants
34+
UtilityFunction:
35+
enabled: false # we're often meeting an existing Ruby API with utility functions
36+
MissingSafeMethod:
37+
enabled: false # we're often meeting an existing Ruby API without safe equivalents, or the safe equivalent is implemented in Java
38+
Attribute:
39+
enabled: false # we're often meeting an existing Ruby API with writable attributes
40+
InstanceVariableAssumption:
41+
enabled: false # we often set instance variables lazily
42+
NestedIterators:
43+
enabled: false # yes we often write nested iterators
44+
TooManyInstanceVariables:
45+
enabled: false # yes we sometimes use a lot of instance variables
46+
RepeatedConditional:
47+
enabled: false # we don't want to add new public methods to Ruby API classes to better encapsulate behaviour
48+
DuplicateMethodCall:
49+
enabled: false # needs investigation - sometimes legitimate due to Ruby semantics, but lots of failures

.rubocop.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ AllCops:
1111

1212
# Type 'Style' (166):
1313
# Supports --auto-correct
14-
Layout/TrailingBlankLines:
14+
Style/TrailingBlankLines:
1515
Description: Checks trailing blank lines and final newline.
1616
StyleGuide: "#newline-eof"
1717
Enabled: true
@@ -21,7 +21,7 @@ Layout/TrailingBlankLines:
2121
- final_blank_line
2222

2323
# Supports --auto-correct
24-
Layout/TrailingWhitespace:
24+
Style/TrailingWhitespace:
2525
Description: Avoid trailing whitespace.
2626
StyleGuide: "#no-trailing-whitespace"
2727
Enabled: true

doc/contributor/static-analysis.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Static Analysis of TruffleRuby
2+
3+
We apply static analysis to the production code that we maintain -
4+
`lib/truffle`, `lib/cext`, `src/annotations`, `src/launcher`, `src/main`,
5+
`src/services` (some parts of static analysis are also applied to other files,
6+
but these are the key ones).
7+
8+
## C
9+
10+
The [Clang Static Analyzer](https://clang-analyzer.llvm.org) finds bugs in C
11+
code. We occasionally run this tool locally but only take its output as a
12+
suggestion. We use a default configuration.
13+
14+
```
15+
$ scan-build --use-analyzer `which clang` -analyze-headers clang -c --std=c99 -Ilib/cext/include src/main/c/cext/ruby.c src/main/c/truffleposix/truffleposix.c
16+
$ scan-view ...as instructed by scan-build...
17+
```
18+
19+
## Java
20+
21+
### DSL usage
22+
23+
We have a tool to check that some use of our internal annotations and the
24+
Truffle DSL are correct. Passing this is enforced in our CI gate.
25+
26+
```
27+
$ jt check_dsl_usage
28+
```
29+
30+
### CheckStyle
31+
32+
[CheckStyle](http://checkstyle.sourceforge.net) enforces a Java style guide.
33+
Passing CheckStyle is enforced in our CI gate.
34+
35+
```
36+
$ mx checkstyle
37+
```
38+
39+
### FindBugs
40+
41+
[FindBugs](http://findbugs.sourceforge.net) looks for potential Java
42+
programming errors. We run it with the default Graal project configuration.
43+
Passing FindBugs is enforced in our CI gate.
44+
45+
```
46+
$ mx findbugs
47+
```
48+
49+
## Ruby
50+
51+
### Rubocop
52+
53+
[Rubocop](https://github.com/rubocop-hq/rubocop) enforces a Ruby style guide.
54+
It's configured in `.rubocop.yml`, and can be run locally as `jt rubocop`.
55+
Passing Rubocop is enforced in our CI gate.
56+
57+
```
58+
$ jt rubocop
59+
```
60+
61+
### Fasterer
62+
63+
[Fasterer](https://github.com/DamirSvrtan/fasterer) looks for potential
64+
performance improvements. We occasionally run this tool locally but only take
65+
its output as a suggestion. We use a default configuration.
66+
67+
```
68+
$ gem install fasterer
69+
$ fasterer lib/truffle lib/cext src/main
70+
```
71+
72+
### Reek
73+
74+
[Reek](https://github.com/troessner/reek) looks for patterns of Ruby code
75+
which could be improved - generally around making it simpler and more clear.
76+
We occasionally run this tool locally but only take its output as a
77+
suggestion. We disable a lot of the defaults in `.reek.yml`, either because
78+
we're implementing a set API, because we're doing something low-level or
79+
outside normal Ruby semantics, or for performance reasons.
80+
81+
```
82+
$ gem install reek
83+
$ reek lib/truffle lib/cext src/main
84+
```
85+
86+
### Flog
87+
88+
[Flog](http://ruby.sadi.st/Flog.html) lists methods by complexity. You can
89+
check that your methods do not appear near the top of this list. We
90+
occasionally run this tool locally but only take its output as a suggestion.
91+
92+
```
93+
$ gem install flog
94+
$ flog -m -t 10 lib/truffle lib/cext src/main
95+
```
96+
97+
### Flay
98+
99+
[Flay](http://ruby.sadi.st/Flay.html) finds similar or identical code, which
100+
could potentially be factored out. We occasionally run this tool locally but
101+
only take its output as a suggestion.
102+
103+
```
104+
$ gem install flay
105+
$ flay lib/truffle lib/cext src/main
106+
```
107+
108+
### Tools we don't use
109+
110+
* [Brakeman](https://github.com/presidentbeef/brakeman) appears to only be
111+
applicable to Rails applications.

lib/truffle/bigdecimal.rb

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -101,42 +101,36 @@ def coerce(other)
101101
[BigDecimal(other, 20), self]
102102
end
103103

104-
# TODO (pitr 28-may-2015): compare with pure Java versions
105104
def >(other)
106-
unless comp = (self <=> other)
107-
return false if nan? || (BigDecimal === other && other.nan?)
108-
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
109-
end
110-
111-
comp > 0
105+
comp_helper(other, :>)
112106
end
113107

114108
def >=(other)
115-
unless comp = (self <=> other)
116-
return false if nan? || (BigDecimal === other && other.nan?)
117-
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
118-
end
119-
120-
comp >= 0
109+
comp_helper(other, :>=)
121110
end
122111

123112
def <(other)
124-
unless comp = (self <=> other)
125-
return false if nan? || (BigDecimal === other && other.nan?)
126-
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
127-
end
128-
129-
comp < 0
113+
comp_helper(other, :<)
130114
end
131115

132116
def <=(other)
133-
unless comp = (self <=> other)
134-
return false if nan? || (BigDecimal === other && other.nan?)
135-
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
136-
end
117+
comp_helper(other, :<=)
118+
end
137119

138-
comp <= 0
120+
def comp_helper(other, op)
121+
comp = (self <=> other)
122+
if comp
123+
# cmp >= 0, comp < 0, etc
124+
comp.send(op, 0)
125+
else
126+
if nan? || (BigDecimal === other && other.nan?)
127+
return false
128+
else
129+
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
130+
end
131+
end
139132
end
133+
private :comp_helper
140134

141135
def nonzero?
142136
zero? ? nil : self

lib/truffle/date.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,7 @@ class Infinity < Numeric # :nodoc:
258258

259259
def initialize(d=1) @d = d <=> 0 end
260260

261-
def d() @d end
262-
261+
attr_reader :d
263262
protected :d
264263

265264
def zero? () false end
@@ -1144,7 +1143,7 @@ def initialize(ajd=0, of=0, sg=ITALY)
11441143
end
11451144

11461145
# Get the date as an Astronomical Julian Day Number.
1147-
def ajd() @ajd end
1146+
attr_reader :ajd
11481147

11491148
# Get the date as an Astronomical Modified Julian Day Number.
11501149
def amjd() ajd_to_amjd(@ajd) end

lib/truffle/date/delta.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class << self; alias_method :mins, :minutes end
175175
class << self; alias_method :secs, :seconds end
176176

177177
def self.parse(str)
178-
d = begin (@@pa ||= Parser.new).parse(str)
178+
d = begin (@pa ||= Parser.new).parse(str)
179179
rescue Racc::ParseError
180180
raise ArgumentError, 'syntax error'
181181
end
@@ -187,7 +187,7 @@ def self.diff(d1, d2) new(d1.ajd - d2.ajd) end
187187
class << self
188188

189189
def once(*ids) # :nodoc: -- restricted
190-
for id in ids
190+
ids.each do |id|
191191
module_eval <<-"end;"
192192
alias_method :__#{id.object_id}__, :#{id}
193193
private :__#{id.object_id}__
@@ -206,8 +206,7 @@ def dhms() self.class.delta_to_dhms(@delta) end
206206

207207
once :dhms
208208

209-
def delta() @delta end
210-
209+
attr_reader :delta
211210
protected :delta
212211

213212
def years() dhms[0] end

lib/truffle/rbconfig-for-mkmf.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,5 +109,5 @@
109109
mkconfig['TRY_LINK'] = "#{cc} -o conftest $(INCFLAGS) $(CPPFLAGS) #{cflags_for_try_link} #{cext_dir}/ruby.o #{cext_dir}/sulongmock.o $(src) $(LIBPATH) $(LDFLAGS) $(ARCH_FLAG) $(LOCAL_LIBS) $(LIBS)"
110110

111111
%w[COMPILE_C COMPILE_CXX TRY_LINK].each do |key|
112-
expanded[key] = mkconfig[key].gsub(/\$\((\w+)\)/) { expanded.fetch($1, $&) }
112+
expanded[key] = mkconfig[key].gsub(/\$\((\w+)\)/) { expanded.fetch($1) { $& } }
113113
end

lib/truffle/rubygems/extra_executables_installer.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ def remove_hooks(install_hook, uninstall_hook)
1818

1919
private
2020

21-
def with_bin_dir(obj, new_bin_dir, &block)
21+
def with_bin_dir(obj, new_bin_dir)
2222
old_bin_dir = obj.instance_variable_get :@bin_dir
2323
obj.instance_variable_set :@bin_dir, new_bin_dir
2424
begin
25-
block.call
25+
yield
2626
ensure
2727
obj.instance_variable_set :@bin_dir, old_bin_dir
2828
end

0 commit comments

Comments
 (0)