rubocop-definition_validator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 323773610745ff80ea1a50e0f36d01372f82c121
4
+ data.tar.gz: d870c1d0acfe7c9ea7ddd79dac8981140ee1b30c
5
+ SHA512:
6
+ metadata.gz: 445590d63bd3e6dbb99dba525a3e6615679d0d92de6f1a5b8e694a0e568f93202ee8b9472aad63301d32882b9382f81bd4c74f410207239f581e0001510ee73b
7
+ data.tar.gz: 48c8d95450b59208818449954b8182c5d029558ae3a5f68d6a042370f72e58fd80876ed0b2edb03789cfad9bbf5dfdb9636669b86bf1735824cfdaa49189a841
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "vendor/rubocop"]
2
+ path = vendor/rubocop
3
+ url = git@github.com:bbatsov/rubocop.git
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,467 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'vendor/**/*'
4
+ DisplayCopNames: true
5
+ TargetRubyVersion: 2.3
6
+
7
+ Style/AccessModifierIndentation:
8
+ Enabled: false
9
+
10
+ Style/AccessorMethodName:
11
+ Enabled: false
12
+
13
+ Style/Alias:
14
+ Enabled: false
15
+
16
+ Style/AlignArray:
17
+ Enabled: false
18
+
19
+ Style/AlignHash:
20
+ Enabled: false
21
+
22
+ Style/AlignParameters:
23
+ Enabled: false
24
+
25
+ Style/AndOr:
26
+ Enabled: false
27
+
28
+ Style/ArrayJoin:
29
+ Enabled: false
30
+
31
+ Style/AsciiComments:
32
+ Enabled: false
33
+
34
+ Style/AsciiIdentifiers:
35
+ Enabled: false
36
+
37
+ Style/Attr:
38
+ Enabled: false
39
+
40
+ Style/BeginBlock:
41
+ Enabled: false
42
+
43
+ Style/BarePercentLiterals:
44
+ Enabled: false
45
+
46
+ Style/BlockComments:
47
+ Enabled: false
48
+
49
+ Style/BlockEndNewline:
50
+ Enabled: false
51
+
52
+ Style/BlockDelimiters:
53
+ Enabled: false
54
+
55
+ Style/BracesAroundHashParameters:
56
+ Enabled: false
57
+
58
+ Style/CaseEquality:
59
+ Enabled: true
60
+
61
+ Style/CaseIndentation:
62
+ Enabled: false
63
+
64
+ Style/CharacterLiteral:
65
+ Enabled: false
66
+
67
+ Style/ClassAndModuleCamelCase:
68
+ Enabled: false
69
+
70
+ Style/ClassAndModuleChildren:
71
+ Enabled: false
72
+
73
+ Style/ClassCheck:
74
+ Enabled: false
75
+
76
+ Style/ClassMethods:
77
+ Enabled: false
78
+
79
+ Style/ClassVars:
80
+ Enabled: false
81
+
82
+ Style/ColonMethodCall:
83
+ Enabled: false
84
+
85
+ Style/CommentAnnotation:
86
+ Enabled: false
87
+
88
+ Style/CommentIndentation:
89
+ Enabled: false
90
+
91
+ Style/ConstantName:
92
+ Enabled: false
93
+
94
+ Style/DefWithParentheses:
95
+ Enabled: false
96
+
97
+ Style/DeprecatedHashMethods:
98
+ Enabled: false
99
+
100
+ Style/Documentation:
101
+ Enabled: false
102
+
103
+ Style/DotPosition:
104
+ Enabled: false
105
+
106
+ Style/DoubleNegation:
107
+ Enabled: false
108
+
109
+ Style/EachWithObject:
110
+ Enabled: false
111
+
112
+ Style/ElseAlignment:
113
+ Enabled: false
114
+
115
+ Style/EmptyElse:
116
+ Enabled: false
117
+
118
+ Style/EmptyLineBetweenDefs:
119
+ Enabled: false
120
+
121
+ Style/EmptyLines:
122
+ Enabled: false
123
+
124
+ Style/EmptyLinesAroundAccessModifier:
125
+ Enabled: false
126
+
127
+ Style/EmptyLinesAroundBlockBody:
128
+ Enabled: false
129
+
130
+ Style/EmptyLinesAroundClassBody:
131
+ Enabled: false
132
+
133
+ Style/EmptyLinesAroundModuleBody:
134
+ Enabled: false
135
+
136
+ Style/EmptyLinesAroundMethodBody:
137
+ Enabled: false
138
+
139
+ Style/EmptyLiteral:
140
+ Enabled: false
141
+
142
+ Style/EndBlock:
143
+ Enabled: false
144
+
145
+ Style/EndOfLine:
146
+ Enabled: false
147
+
148
+ Style/EvenOdd:
149
+ Enabled: false
150
+
151
+ Style/FileName:
152
+ Enabled: false
153
+
154
+ Style/FirstParameterIndentation:
155
+ Enabled: false
156
+
157
+ Style/FlipFlop:
158
+ Enabled: false
159
+
160
+ Style/For:
161
+ Enabled: false
162
+
163
+ Style/FormatString:
164
+ Enabled: false
165
+
166
+ Style/FrozenStringLiteralComment:
167
+ Enabled: false
168
+
169
+ Style/GlobalVars:
170
+ Enabled: true
171
+
172
+ Style/GuardClause:
173
+ Enabled: false
174
+
175
+ Style/HashSyntax:
176
+ Enabled: false
177
+
178
+ Style/IfUnlessModifier:
179
+ Enabled: false
180
+
181
+ Style/IfWithSemicolon:
182
+ Enabled: false
183
+
184
+ Style/IndentationConsistency:
185
+ Enabled: false
186
+
187
+ Style/IndentationWidth:
188
+ Enabled: false
189
+
190
+ Style/IndentArray:
191
+ Enabled: false
192
+
193
+ Style/IndentHash:
194
+ Enabled: false
195
+
196
+ Style/InfiniteLoop:
197
+ Enabled: false
198
+
199
+ Style/Lambda:
200
+ Enabled: false
201
+
202
+ Style/LambdaCall:
203
+ Enabled: false
204
+
205
+ Style/LeadingCommentSpace:
206
+ Enabled: false
207
+
208
+ Style/LineEndConcatenation:
209
+ Enabled: false
210
+
211
+ Style/MethodCallParentheses:
212
+ Enabled: false
213
+
214
+ Style/MethodDefParentheses:
215
+ Enabled: false
216
+
217
+ Style/MethodName:
218
+ Enabled: false
219
+
220
+ Style/ModuleFunction:
221
+ Enabled: false
222
+
223
+ Style/MultilineBlockChain:
224
+ Enabled: false
225
+
226
+ Style/MultilineBlockLayout:
227
+ Enabled: false
228
+
229
+ Style/MultilineIfThen:
230
+ Enabled: false
231
+
232
+ Style/MultilineOperationIndentation:
233
+ Enabled: false
234
+
235
+ Style/MultilineTernaryOperator:
236
+ Enabled: false
237
+
238
+ Style/NegatedIf:
239
+ Enabled: false
240
+
241
+ Style/NegatedWhile:
242
+ Enabled: false
243
+
244
+ Style/NestedTernaryOperator:
245
+ Enabled: false
246
+
247
+ Style/Next:
248
+ Enabled: false
249
+
250
+ Style/NilComparison:
251
+ Enabled: false
252
+
253
+ Style/NonNilCheck:
254
+ Enabled: false
255
+
256
+ Style/Not:
257
+ Enabled: false
258
+
259
+ Style/NumericLiterals:
260
+ Enabled: false
261
+
262
+ Style/OneLineConditional:
263
+ Enabled: false
264
+
265
+ Style/OpMethod:
266
+ Enabled: false
267
+
268
+ Style/ParenthesesAroundCondition:
269
+ Enabled: false
270
+
271
+ Style/PercentLiteralDelimiters:
272
+ Enabled: false
273
+
274
+ Style/PercentQLiterals:
275
+ Enabled: false
276
+
277
+ Style/PerlBackrefs:
278
+ Enabled: false
279
+
280
+ Style/PredicateName:
281
+ Enabled: false
282
+
283
+ Style/Proc:
284
+ Enabled: false
285
+
286
+ Style/RaiseArgs:
287
+ Enabled: false
288
+
289
+ Style/RedundantBegin:
290
+ Enabled: false
291
+
292
+ Style/RedundantException:
293
+ Enabled: false
294
+
295
+ Style/RedundantReturn:
296
+ Enabled: false
297
+
298
+ Style/RedundantSelf:
299
+ Enabled: false
300
+
301
+ Style/RegexpLiteral:
302
+ Enabled: false
303
+
304
+ Style/RescueModifier:
305
+ Enabled: false
306
+
307
+ Style/SelfAssignment:
308
+ Enabled: false
309
+
310
+ Style/Semicolon:
311
+ Enabled: false
312
+
313
+ Style/SignalException:
314
+ Enabled: false
315
+
316
+ Style/SingleLineBlockParams:
317
+ Enabled: false
318
+
319
+ Style/SingleLineMethods:
320
+ Enabled: false
321
+
322
+ Style/SpaceBeforeFirstArg:
323
+ Enabled: false
324
+
325
+ Style/SpaceAfterColon:
326
+ Enabled: false
327
+
328
+ Style/SpaceAfterComma:
329
+ Enabled: false
330
+
331
+ Style/SpaceAroundKeyword:
332
+ Enabled: false
333
+
334
+ Style/SpaceAfterMethodName:
335
+ Enabled: false
336
+
337
+ Style/SpaceAfterNot:
338
+ Enabled: false
339
+
340
+ Style/SpaceAfterSemicolon:
341
+ Enabled: false
342
+
343
+ Style/SpaceBeforeBlockBraces:
344
+ Enabled: false
345
+
346
+ Style/SpaceBeforeComma:
347
+ Enabled: false
348
+
349
+ Style/SpaceBeforeComment:
350
+ Enabled: false
351
+
352
+ Style/SpaceBeforeSemicolon:
353
+ Enabled: false
354
+
355
+ Style/SpaceInsideBlockBraces:
356
+ Enabled: false
357
+
358
+ Style/SpaceAroundBlockParameters:
359
+ Enabled: false
360
+
361
+ Style/SpaceAroundEqualsInParameterDefault:
362
+ Enabled: false
363
+
364
+ Style/SpaceAroundOperators:
365
+ Enabled: false
366
+
367
+ Style/SpaceInsideBrackets:
368
+ Enabled: false
369
+
370
+ Style/SpaceInsideHashLiteralBraces:
371
+ Enabled: false
372
+
373
+ Style/SpaceInsideParens:
374
+ Enabled: false
375
+
376
+ Style/SpaceInsideRangeLiteral:
377
+ Enabled: false
378
+
379
+ Style/SpecialGlobalVars:
380
+ Enabled: false
381
+
382
+ Style/StringLiterals:
383
+ Enabled: false
384
+
385
+ Style/StringLiteralsInInterpolation:
386
+ Enabled: false
387
+
388
+ Style/StructInheritance:
389
+ Enabled: false
390
+
391
+ Style/SymbolProc:
392
+ Enabled: false
393
+
394
+ Style/Tab:
395
+ Enabled: false
396
+
397
+ Style/TrailingBlankLines:
398
+ Enabled: false
399
+
400
+ Style/TrailingCommaInLiteral:
401
+ Enabled: false
402
+
403
+ Style/TrailingWhitespace:
404
+ Enabled: false
405
+
406
+ Style/TrivialAccessors:
407
+ Enabled: false
408
+
409
+ Style/UnlessElse:
410
+ Enabled: false
411
+
412
+ Style/UnneededCapitalW:
413
+ Enabled: false
414
+
415
+ Style/UnneededPercentQ:
416
+ Enabled: false
417
+
418
+ Style/UnneededPercentX:
419
+ Enabled: false
420
+
421
+ Style/VariableInterpolation:
422
+ Enabled: false
423
+
424
+ Style/VariableName:
425
+ Enabled: false
426
+
427
+ Style/WhenThen:
428
+ Enabled: false
429
+
430
+ Style/WhileUntilDo:
431
+ Enabled: false
432
+
433
+ Style/WhileUntilModifier:
434
+ Enabled: false
435
+
436
+ Style/WordArray:
437
+ Enabled: false
438
+
439
+ Lint/DeprecatedClassMethods:
440
+ Enabled: false
441
+
442
+ Lint/StringConversionInInterpolation:
443
+ Enabled: false
444
+
445
+ Metrics/AbcSize:
446
+ Enabled: false
447
+
448
+ Metrics/BlockNesting:
449
+ Enabled: false
450
+
451
+ Metrics/ClassLength:
452
+ Enabled: false
453
+
454
+ Metrics/CyclomaticComplexity:
455
+ Enabled: false
456
+
457
+ Metrics/LineLength:
458
+ Enabled: false
459
+
460
+ Metrics/MethodLength:
461
+ Enabled: false
462
+
463
+ Metrics/ParameterLists:
464
+ Enabled: false
465
+
466
+ Metrics/PerceivedComplexity:
467
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,54 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ guard :bundler do
19
+ require 'guard/bundler'
20
+ require 'guard/bundler/verify'
21
+ helper = Guard::Bundler::Verify.new
22
+
23
+ files = ['Gemfile']
24
+ files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) }
25
+
26
+ # Assume files are symlinked from somewhere
27
+ files.each { |file| watch(helper.real_path(file)) }
28
+ end
29
+
30
+ # Note: The cmd option is now required due to the increasing number of ways
31
+ # rspec may be run, below are examples of the most common uses.
32
+ # * bundler: 'bundle exec rspec'
33
+ # * bundler binstubs: 'bin/rspec'
34
+ # * spring: 'bin/rspec' (This will use spring if running and you have
35
+ # installed the spring binstubs per the docs)
36
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
37
+ # * 'just' rspec: 'rspec'
38
+
39
+ guard :rspec, cmd: "bundle exec rspec" do
40
+ require "guard/rspec/dsl"
41
+ dsl = Guard::RSpec::Dsl.new(self)
42
+
43
+ # Feel free to open issues for suggestions and improvements
44
+
45
+ # RSpec files
46
+ rspec = dsl.rspec
47
+ watch(rspec.spec_helper) { rspec.spec_dir }
48
+ watch(rspec.spec_support) { rspec.spec_dir }
49
+ watch(rspec.spec_files)
50
+
51
+ # Ruby files
52
+ ruby = dsl.ruby
53
+ dsl.watch_spec_files_for(ruby.lib_files)
54
+ end
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Rubocop::DefinitionValidator
2
+
3
+ This tool detects omission of modification of callers when the name of a method, number of arguments, or the name of a keyword argument is modified.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'rubocop-definition_validator'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install rubocop-definition_validator
20
+
21
+ ## Usage
22
+
23
+ ```sh
24
+ $ git diff TARGET_COMMIT > ./.rubocop-definition_validator.diff
25
+ $ rubocop -r rubocop/definition_validator
26
+ ```
27
+
28
+ ## Development
29
+
30
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
31
+
32
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
33
+
34
+ ## Contributing
35
+
36
+ Bug reports and pull requests are welcome on GitHub at https://github.com/actcat/rubocop-definition_validator.
37
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rubocop/diff"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ Lint/DefinitionValidator:
2
+ Description: 'TODO'
3
+ Enabled: true
4
+ Min: 6
@@ -0,0 +1,31 @@
1
+ module RuboCop
2
+ module Cop
3
+ module Lint
4
+ class DefinitionValidator < Cop
5
+ def on_send(node)
6
+ name = node.method_name.to_s
7
+ args = node.method_args
8
+ min = cop_config['Min']
9
+
10
+ msg = nil
11
+ Rubocop::DefinitionValidator::ChangeDetector.changed_methods.each do |m|
12
+ old, new = m[:removed], m[:added]
13
+
14
+ next if old.name.size <= min
15
+
16
+ old_callable, _ = old.callable?(name, args)
17
+ next unless old_callable
18
+
19
+ new_callable, reason = new.callable?(name, args)
20
+ next if new_callable
21
+
22
+ msg = reason.respond_to?(:call) ? reason.(old, new) : reason
23
+ end
24
+
25
+ return unless msg
26
+ add_offense(node, :expression, msg)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ require 'git_diff_parser'
2
+
3
+ require 'rubocop'
4
+ require "rubocop/definition_validator/version"
5
+ require 'rubocop/definition_validator/inject'
6
+
7
+ RuboCop::DefinitionValidator::Inject.defaults!
8
+
9
+
10
+ require 'rubocop/definition_validator/line'
11
+ require 'rubocop/definition_validator/patch'
12
+ require 'rubocop/definition_validator/reason'
13
+ require 'rubocop/definition_validator/method'
14
+ require 'rubocop/definition_validator/change_detector'
15
+
16
+ require 'rubocop/cop/lint/definition_validator'
@@ -0,0 +1,34 @@
1
+ module Rubocop::DefinitionValidator::ChangeDetector
2
+ class << self
3
+ attr_reader :changed_methods
4
+
5
+ def init(diff_path)
6
+ unless File.exist?(diff_path)
7
+ @changed_methods = []
8
+ return
9
+ end
10
+
11
+ diff = File.read(diff_path)
12
+ parsed = GitDiffParser::Patches.parse(diff)
13
+ patches = parsed.map{|orig_patch| patch = Rubocop::DefinitionValidator::Patch.new(orig_patch)}
14
+ # [
15
+ # {added: Method, removed: Method}
16
+ # ]
17
+ @changed_methods = patches
18
+ .map{|patch| patch.changed_method_codes}
19
+ .flatten
20
+ .map{|code|
21
+ code.map{|k, v|
22
+ begin
23
+ [k, Rubocop::DefinitionValidator::Method.new(v.body)]
24
+ rescue Rubocop::DefinitionValidator::Method::InvalidAST
25
+ nil
26
+ end
27
+ }.compact.to_h
28
+ }
29
+ end
30
+ end
31
+ end
32
+
33
+ # XXX: 暫定的に .rubocop-definition_validator.diff からdiffを読む
34
+ Rubocop::DefinitionValidator::ChangeDetector.init('./.rubocop-definition_validator.diff')
@@ -0,0 +1,23 @@
1
+ # This file is copied from rubocop-rspec
2
+ require 'yaml'
3
+
4
+ module RuboCop
5
+ module DefinitionValidator
6
+ # Because RuboCop doesn't yet support plugins, we have to monkey patch in a
7
+ # bit of our configuration.
8
+ module Inject
9
+ DEFAULT_FILE = File.expand_path(
10
+ '../../../../config/default.yml', __FILE__
11
+ )
12
+
13
+ def self.defaults!
14
+ path = File.absolute_path(DEFAULT_FILE)
15
+ hash = ConfigLoader.send(:load_yaml_configuration, path)
16
+ config = Config.new(hash, path)
17
+ puts "configuration from #{DEFAULT_FILE}" if ConfigLoader.debug?
18
+ config = ConfigLoader.merge_with_default(config, path)
19
+ ConfigLoader.instance_variable_set(:@default_configuration, config)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ class Rubocop::DefinitionValidator::Line < GitDiffParser::Line
2
+ attr_reader :content
3
+
4
+ # trim `+` or `-` prefix
5
+ def body
6
+ content[1..-1]
7
+ end
8
+
9
+ # @return [String] + or -
10
+ def type
11
+ content[0]
12
+ end
13
+ end
@@ -0,0 +1,191 @@
1
+ require 'ripper'
2
+
3
+ module Rubocop::DefinitionValidator
4
+ class Method
5
+ class InvalidAST < ArgumentError; end
6
+
7
+ attr_reader :name
8
+
9
+ # @param [String] definition
10
+ #
11
+ # Definition Example:
12
+ # def f(a, b, c, m = 1, n = 1, *rest, x, y, z, k: 1, **kwrest, &blk)
13
+ #
14
+ # AST Example:
15
+ # [:program,
16
+ # [[:def,
17
+ # [:@ident, "f", [1, 4]],
18
+ # [:paren,
19
+ # [:params,
20
+ # [[:@ident, "a", [1, 6]], [:@ident, "b", [1, 9]], [:@ident, "c", [1, 12]]],
21
+ # [[[:@ident, "m", [1, 15]], [:@int, "1", [1, 19]]], [[:@ident, "n", [1, 22]], [:@int, "1", [1, 26]]]],
22
+ # [:rest_param, [:@ident, "rest", [1, 30]]],
23
+ # [[:@ident, "x", [1, 36]], [:@ident, "y", [1, 39]], [:@ident, "z", [1, 42]]],
24
+ # [[[:@label, "k:", [1, 45]], [:@int, "1", [1, 48]]]],
25
+ # [:@ident, "kwrest", [1, 53]],
26
+ # [:blockarg, [:@ident, "blk", [1, 62]]]]],
27
+ # [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
28
+ #
29
+ def initialize(definition)
30
+ code = "#{definition}; end"
31
+ ast = Ripper.sexp(code)
32
+
33
+ begin
34
+ params = ast[1][0][2]
35
+ name = ast[1][0][1][1]
36
+ rescue NoMethodError => ex
37
+ raise InvalidAST, "Can't parse AST. \nAST: #{ast}\nError: #{ex}"
38
+ end
39
+
40
+ if params[0] == :paren
41
+ params = params[1]
42
+ end
43
+
44
+ @params = params[1..-1]
45
+ @name = name
46
+ end
47
+
48
+ # @param [Array<RuboCop::Node>] args
49
+ # @return [Boolean] callable
50
+ # @return [Proc] cause
51
+ def callable?(name, args)
52
+ return false, Reason.method_name unless name == @name
53
+
54
+ args = args.dup
55
+
56
+ # 通常の引数分shift
57
+ normal_params_size = (normal_params || []).size
58
+ received_normal_params_size = args.shift(normal_params_size).size
59
+ unless received_normal_params_size == normal_params_size
60
+ return false, Reason.not_enough_arguments(
61
+ received_normal_params_size,
62
+ normal_params_size,
63
+ :normal_params,
64
+ )
65
+ end
66
+
67
+ if has_required_keyword_params?
68
+ decide_with_required_keyword_params(args)
69
+ elsif has_keyword_params?
70
+ decide_with_keyword_params(args)
71
+ else
72
+ decide_rest_args(args)
73
+ end
74
+ end
75
+
76
+
77
+ %i[
78
+ normal_params
79
+ default_value_params
80
+ rest_params
81
+ normal_params_after_rest
82
+ keyword_params
83
+ keyword_rest_params
84
+ ].each.with_index do |name, idx|
85
+ eval <<-CODE
86
+ def #{name}
87
+ @params[#{idx}]
88
+ end
89
+ CODE
90
+ end
91
+
92
+
93
+ private
94
+
95
+ # decide default_value_params, rest_params, normal_params_after_rest
96
+ # @param [Array<RuboCop::Node>] args
97
+ # @return [Boolean]
98
+ def decide_rest_args(args)
99
+ normal_params_after_rest_size = (normal_params_after_rest || []).size
100
+ given_normal_params_after_rest_size = args.pop(normal_params_after_rest_size).size
101
+ unless given_normal_params_after_rest_size == normal_params_after_rest_size
102
+ return false, Reason.not_enough_arguments(
103
+ given_normal_params_after_rest_size,
104
+ normal_params_after_rest_size,
105
+ :normal_params_after_rest
106
+ )
107
+ end
108
+
109
+ # rest引数があれば全て呑み込むためtrue
110
+ return true if rest_params
111
+ # デフォルト値付き引数の数だけは呑み込める
112
+ if default_value_params
113
+ return true if args.size <= default_value_params.size
114
+ # XXX: optimize message.
115
+ return false, "Too many argument"
116
+ end
117
+ return true if args.empty?
118
+ return false, "Too many argument"
119
+ end
120
+
121
+ def decide_with_required_keyword_params(args)
122
+ kwparam = args.pop(1)
123
+ usable, reason = usable_as_keyword_param?(kwparam[0])
124
+ return false, reason unless usable
125
+
126
+ return decide_rest_args(args)
127
+ end
128
+
129
+ def decide_with_keyword_params(args)
130
+ ok, _ = decide_rest_args(args.dup)
131
+ return true if ok
132
+
133
+ return decide_with_required_keyword_params(args)
134
+ end
135
+
136
+
137
+ def has_keyword_params?
138
+ !!((keyword_params && !keyword_params.empty?) ||
139
+ keyword_rest_params)
140
+ end
141
+
142
+ def has_required_keyword_params?
143
+ (keyword_params && !keyword_params.empty?) &&
144
+ keyword_params.any?{|p| p[1] == false}
145
+ end
146
+
147
+
148
+ # @param [RuboCop::Node] arg
149
+ def usable_as_keyword_param?(arg)
150
+ # should be hash
151
+ return false, "Keyword params is required." unless arg
152
+ return false, "Keyword params should be hash. But got #{arg.loc.expression.source}" unless arg.hash_type? || !arg.literal?
153
+
154
+ # should have specified keyword
155
+ return true unless arg.hash_type?
156
+
157
+ required_keyword_names = keyword_params
158
+ .select{|x| x[1] == false}
159
+ .map{|x| x[0][1]}
160
+ .map{|x| x.chop.to_sym}
161
+
162
+ received_keyword_names = arg
163
+ .children
164
+ .map(&:children)
165
+ .map{|x| x.first}
166
+ .map(&:children)
167
+ .map{|x| x.first}
168
+
169
+ not_fould_requireds = required_keyword_names.select do |name|
170
+ not received_keyword_names.include?(name)
171
+ end
172
+ unless not_fould_requireds.empty?
173
+ return false, "The following keyword parameters are required. But not received. #{not_fould_requireds.join(', ')}"
174
+ end
175
+
176
+ return true if keyword_rest_params
177
+
178
+ allowed_keyword_names = keyword_params
179
+ .map{|x| x[0][1]}
180
+ .map{|x| x.chop.to_sym}
181
+
182
+ unexpected_keywords = received_keyword_names.select do |name|
183
+ not allowed_keyword_names.include?(name)
184
+ end
185
+ unless unexpected_keywords.empty?
186
+ return false, "The following keyword parameters are not expected. But received. #{unexpected_keywords}"
187
+ end
188
+ return true
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,63 @@
1
+ #
2
+ # This class override `changed_lines` method.
3
+ # The original method retuns only added lines.
4
+ # However we want added and removed lines.
5
+ #
6
+ class Rubocop::DefinitionValidator::Patch < GitDiffParser::Patch
7
+ ADDED_LINE = -> (line) { line.start_with?('+') && line !~ /^\+\+\+/ }
8
+ REMOVED_LINE = -> (line) { line.start_with?('-') && line !~ /^\-\-\-/ }
9
+
10
+ def initialize(original_patch)
11
+ @body = original_patch.body
12
+ @file = original_patch.file
13
+ @secure_hash = original_patch.secure_hash
14
+ end
15
+
16
+ # @return [Array<Line>] changed lines
17
+ def changed_lines
18
+ line_number = 0
19
+ removed_line_offset = 0
20
+
21
+ lines.each_with_index.inject([]) do |lines, (content, patch_position)|
22
+ case content
23
+ when RANGE_INFORMATION_LINE
24
+ line_number = Regexp.last_match[:line_number].to_i
25
+ removed_line_offset = 0
26
+ when ADDED_LINE
27
+ line = Rubocop::DefinitionValidator::Line.new(
28
+ content: content,
29
+ number: line_number,
30
+ patch_position: patch_position
31
+ )
32
+ lines << line
33
+ line_number += 1
34
+ removed_line_offset = 0
35
+ when REMOVED_LINE
36
+ line = Rubocop::DefinitionValidator::Line.new(
37
+ content: content,
38
+ number: line_number + removed_line_offset,
39
+ patch_position: patch_position
40
+ )
41
+ lines << line
42
+ removed_line_offset +=1
43
+ when NOT_REMOVED_LINE
44
+ line_number += 1
45
+ removed_line_offset = 0
46
+ end
47
+
48
+ lines
49
+ end
50
+ end
51
+
52
+ def changed_method_codes
53
+ lines = changed_lines
54
+ lines
55
+ .group_by{|l| l.number}
56
+ .values
57
+ .select{|l| l.size == 2}
58
+ .select{|l| t = l.map(&:type); t.include?('-') && t.include?('+')}
59
+ .select{|l| l.all?{|x| x.content =~ /def\s+\w+/}}
60
+ .map{|l| l.sort_by(&:type)}
61
+ .map{|l| {added: l.first, removed: l.last}}
62
+ end
63
+ end
@@ -0,0 +1,20 @@
1
+ module Rubocop::DefinitionValidator::Reason
2
+ class << self
3
+ # @return [Proc]
4
+ def method_name
5
+ -> (old, new) { "#{old.name} is undefined. Did you mean? #{new.name}" }
6
+ end
7
+
8
+ # @param [Integer] given given args size.
9
+ # @param [Integer] expected expected args size.
10
+ # @param [Symbol] kind normal_params or normal_params_after_rest
11
+ # @return [Proc]
12
+ def not_enough_arguments(given, expected, kind)
13
+ n = expected - given
14
+ -> (_old, new) {
15
+ not_enough_arg_names = new.__send__(kind).dup.pop(n).map{|x| x[1]}
16
+ "Not enough arguments. Did you forget the following arguments? [#{not_enough_arg_names.join(', ')}]"
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ module Rubocop
2
+ module DefinitionValidator
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rubocop/definition_validator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rubocop-definition_validator"
8
+ spec.version = Rubocop::DefinitionValidator::VERSION
9
+ spec.authors = ["Masataka Kuwabara"]
10
+ spec.email = ["p.ck.t22@gmail.com"]
11
+
12
+ spec.summary = %q{This tool detects omission of modification of callers when the name of a method, number of arguments, or the name of a keyword argument is modified.}
13
+ spec.description = %q{This tool detects omission of modification of callers when the name of a method, number of arguments, or the name of a keyword argument is modified.}
14
+ spec.homepage = "https://github.com/actcat/rubocop-definition_validator"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+ # TODO: License
21
+
22
+ spec.add_runtime_dependency 'rubocop', '>= 0.31.0'
23
+ spec.add_runtime_dependency 'git_diff_parser', '>= 2.2.0'
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.11"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+
28
+ spec.add_development_dependency "rspec", "~> 3.4.0"
29
+ spec.add_development_dependency 'guard', '~> 2.13.0'
30
+ spec.add_development_dependency 'guard-rspec', '~> 4.6.5'
31
+ spec.add_development_dependency 'guard-bundler', '~> 2.1.0'
32
+ spec.add_development_dependency 'pry', '~> 0.10.3'
33
+ spec.add_development_dependency 'rspec-power_assert', '~> 0.3.0'
34
+ spec.add_development_dependency 'pry_testcase', '~> 0.3.0'
35
+ end
metadata ADDED
@@ -0,0 +1,220 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubocop-definition_validator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Masataka Kuwabara
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-06-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubocop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.31.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.31.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: git_diff_parser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.11'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.4.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.4.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.13.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.13.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 4.6.5
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 4.6.5
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard-bundler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 2.1.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 2.1.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.10.3
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.10.3
139
+ - !ruby/object:Gem::Dependency
140
+ name: rspec-power_assert
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.3.0
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 0.3.0
153
+ - !ruby/object:Gem::Dependency
154
+ name: pry_testcase
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 0.3.0
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 0.3.0
167
+ description: This tool detects omission of modification of callers when the name of
168
+ a method, number of arguments, or the name of a keyword argument is modified.
169
+ email:
170
+ - p.ck.t22@gmail.com
171
+ executables: []
172
+ extensions: []
173
+ extra_rdoc_files: []
174
+ files:
175
+ - ".gitignore"
176
+ - ".gitmodules"
177
+ - ".rspec"
178
+ - ".rubocop.yml"
179
+ - Gemfile
180
+ - Guardfile
181
+ - README.md
182
+ - Rakefile
183
+ - bin/console
184
+ - bin/setup
185
+ - config/default.yml
186
+ - lib/rubocop/cop/lint/definition_validator.rb
187
+ - lib/rubocop/definition_validator.rb
188
+ - lib/rubocop/definition_validator/change_detector.rb
189
+ - lib/rubocop/definition_validator/inject.rb
190
+ - lib/rubocop/definition_validator/line.rb
191
+ - lib/rubocop/definition_validator/method.rb
192
+ - lib/rubocop/definition_validator/patch.rb
193
+ - lib/rubocop/definition_validator/reason.rb
194
+ - lib/rubocop/definition_validator/version.rb
195
+ - rubocop-definition_validator.gemspec
196
+ homepage: https://github.com/actcat/rubocop-definition_validator
197
+ licenses: []
198
+ metadata: {}
199
+ post_install_message:
200
+ rdoc_options: []
201
+ require_paths:
202
+ - lib
203
+ required_ruby_version: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
208
+ required_rubygems_version: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
213
+ requirements: []
214
+ rubyforge_project:
215
+ rubygems_version: 2.5.1
216
+ signing_key:
217
+ specification_version: 4
218
+ summary: This tool detects omission of modification of callers when the name of a
219
+ method, number of arguments, or the name of a keyword argument is modified.
220
+ test_files: []