yardcheck 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.buildkite/hooks/pre-command +3 -0
- data/.buildkite/pipeline.yml +10 -0
- data/.gitmodules +3 -0
- data/.rubocop.yml +15 -13
- data/Gemfile +3 -2
- data/README.md +19 -4
- data/lib/yardcheck/const.rb +27 -1
- data/lib/yardcheck/documentation/method_object.rb +21 -3
- data/lib/yardcheck/method_call.rb +37 -10
- data/lib/yardcheck/method_tracer.rb +50 -11
- data/lib/yardcheck/observation.rb +32 -6
- data/lib/yardcheck/processed_source.rb +32 -0
- data/lib/yardcheck/runner.rb +6 -0
- data/lib/yardcheck/typedef/parser.rb +1 -1
- data/lib/yardcheck/typedef.rb +16 -2
- data/lib/yardcheck/version.rb +1 -1
- data/lib/yardcheck/violation.rb +38 -0
- data/lib/yardcheck.rb +2 -0
- data/spec/integration/yardcheck_spec.rb +9 -3
- data/spec/spec_helper.rb +5 -1
- data/spec/unit/yardcheck/const_spec.rb +14 -0
- data/spec/unit/yardcheck/documentation_spec.rb +17 -2
- data/spec/unit/yardcheck/method_tracer_spec.rb +4 -6
- data/spec/unit/yardcheck/processed_source_spec.rb +43 -0
- data/spec/unit/yardcheck/runner_spec.rb +19 -20
- data/test_app/Gemfile +1 -0
- data/test_app/Gemfile.lock +9 -4
- data/test_app/lib/test_app/ambiguous_raise.rb +33 -0
- data/test_app/lib/test_app/block_return.rb +40 -0
- data/test_app/lib/test_app/tracepoint_bug.rb +23 -0
- data/test_app/lib/test_app.rb +32 -1
- data/test_app/spec/test_app/ambiguous_raise_spec.rb +9 -0
- data/test_app/spec/test_app/block_return_spec.rb +9 -0
- data/test_app/spec/test_app/tracepoint_bug_spec.rb +7 -0
- data/test_app/spec/test_app_spec.rb +31 -13
- data/yardcheck.gemspec +1 -0
- metadata +27 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b594df9271ab2ef4f110b5fdaf97a44fc3e633a8
|
4
|
+
data.tar.gz: be08bcfad68850ed0adca3b1dba4e38562de2840
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5406813ffe5bda64d642a76cf9e6aab7c54fa0953e7bd693d5b98044048702cee8860c1af011fb48eb671a353d95b06e229b2996259180ac07e8ffb1cda68377
|
7
|
+
data.tar.gz: afc03fd46651422fbef24949eb940b175b4c6d62fb532c790b893482be060449d032c2202d02663c5f47f9dc2682069205da7cc5e403badaf66257ed78645bba
|
data/.gitmodules
ADDED
data/.rubocop.yml
CHANGED
@@ -13,7 +13,7 @@ AllCops:
|
|
13
13
|
# This ends up being too spammy
|
14
14
|
Style/Documentation:
|
15
15
|
Enabled: false
|
16
|
-
|
16
|
+
Layout/ExtraSpacing:
|
17
17
|
AllowForAlignment: true
|
18
18
|
Metrics/LineLength:
|
19
19
|
Max: 100
|
@@ -50,38 +50,38 @@ Style/Send:
|
|
50
50
|
Enabled: true
|
51
51
|
Style/AutoResourceCleanup:
|
52
52
|
Enabled: true
|
53
|
-
|
53
|
+
Layout/FirstArrayElementLineBreak:
|
54
54
|
Enabled: true
|
55
|
-
|
55
|
+
Layout/FirstHashElementLineBreak:
|
56
56
|
Enabled: true
|
57
|
-
|
57
|
+
Layout/FirstMethodArgumentLineBreak:
|
58
58
|
Enabled: true
|
59
|
-
|
59
|
+
Layout/FirstMethodParameterLineBreak:
|
60
60
|
Enabled: true
|
61
|
-
|
61
|
+
Layout/MultilineArrayBraceLayout:
|
62
62
|
Enabled: true
|
63
|
-
|
63
|
+
Layout/MultilineAssignmentLayout:
|
64
64
|
EnforcedStyle: new_line
|
65
65
|
Enabled: true
|
66
|
-
|
66
|
+
Layout/MultilineHashBraceLayout:
|
67
67
|
Enabled: true
|
68
|
-
|
68
|
+
Layout/MultilineMethodCallBraceLayout:
|
69
69
|
Enabled: true
|
70
|
-
|
70
|
+
Layout/MultilineMethodDefinitionBraceLayout:
|
71
71
|
Enabled: true
|
72
72
|
Style/OptionHash:
|
73
73
|
Enabled: true
|
74
74
|
Style/StringMethods:
|
75
75
|
Enabled: true
|
76
|
-
|
76
|
+
Layout/IndentArray:
|
77
77
|
EnforcedStyle: consistent
|
78
|
-
|
78
|
+
Layout/IndentHash:
|
79
79
|
EnforcedStyle: consistent
|
80
80
|
MultilineMethodCallIndentation:
|
81
81
|
EnforcedStyle: indented
|
82
82
|
Style/Alias:
|
83
83
|
EnforcedStyle: prefer_alias_method
|
84
|
-
|
84
|
+
Layout/AlignHash:
|
85
85
|
EnforcedColonStyle: table
|
86
86
|
Style/SignalException:
|
87
87
|
EnforcedStyle: semantic
|
@@ -212,3 +212,5 @@ Lint/DefEndAlignment:
|
|
212
212
|
# See explanation for `Lint/DefEndAlignment`
|
213
213
|
Lint/EndAlignment:
|
214
214
|
AutoCorrect: true
|
215
|
+
Style/MixinGrouping:
|
216
|
+
EnforcedStyle: grouped
|
data/Gemfile
CHANGED
@@ -11,9 +11,10 @@ group :test do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
group :lint do
|
14
|
-
gem '
|
14
|
+
gem 'rake' # sickill/rainbow#44
|
15
|
+
gem 'rubocop', '~> 0.49.1'
|
15
16
|
gem 'rubocop-devtools', git: 'https://github.com/backus/rubocop-devtools.git'
|
16
|
-
gem 'rubocop-rspec'
|
17
|
+
gem 'rubocop-rspec'
|
17
18
|
end
|
18
19
|
|
19
20
|
gem 'guard'
|
data/README.md
CHANGED
@@ -14,9 +14,9 @@ When you write documentation like this
|
|
14
14
|
# @param user [User]
|
15
15
|
#
|
16
16
|
# @return [true,false]
|
17
|
-
|
18
|
-
# ...
|
19
|
-
|
17
|
+
def valid?(user)
|
18
|
+
# ...
|
19
|
+
end
|
20
20
|
```
|
21
21
|
|
22
22
|
You are saying that you are always going to be passing in a `User` instance and the method will always returns `true` or `false`.
|
@@ -74,10 +74,25 @@ Yardcheck is doing some cool things here:
|
|
74
74
|
|
75
75
|
In this case I would update the documentation to be `@param [#call, nil] block`
|
76
76
|
|
77
|
-
|
77
|
+
## Is this ready?
|
78
78
|
|
79
79
|
Kind of.
|
80
80
|
|
81
81
|
It is not ready to be run in CI to check your documentation and it may never be since tracing method calls is fairly slow. We also sometimes mess up. For example, if another method raises an error then all of the methods that bubble up that error without rescuing it will be marked as returning `nil`. This seems like a limitation of ruby's `TracePoint` right now.
|
82
82
|
|
83
83
|
It is very helpful though. It will find a lot of cases where your documentation isn't quite right and the output is clear. Install it and give it a try.
|
84
|
+
|
85
|
+
## Install
|
86
|
+
|
87
|
+
You probably could have guessed this, but to install just run
|
88
|
+
|
89
|
+
```
|
90
|
+
$ gem install yardcheck
|
91
|
+
```
|
92
|
+
|
93
|
+
Or add this to your Gemfile
|
94
|
+
|
95
|
+
```
|
96
|
+
gem 'yardcheck'
|
97
|
+
```
|
98
|
+
|
data/lib/yardcheck/const.rb
CHANGED
@@ -4,14 +4,33 @@ module Yardcheck
|
|
4
4
|
class Const
|
5
5
|
include Concord::Public.new(:constant)
|
6
6
|
|
7
|
+
SPECIAL_CASES = [
|
8
|
+
Hash,
|
9
|
+
Array
|
10
|
+
].map { |const| [const.name, const] }.to_h
|
11
|
+
|
7
12
|
def self.resolve(constant_name, scope = Object)
|
8
|
-
|
13
|
+
if (const = direct_resolve(constant_name, scope))
|
14
|
+
const
|
15
|
+
else
|
16
|
+
resolve_parent(constant_name, scope)
|
17
|
+
end
|
18
|
+
end
|
9
19
|
|
20
|
+
def self.resolve_parent(constant_name, scope)
|
10
21
|
parent = parent_namespace(scope)
|
11
22
|
from_parent = resolve(constant_name, parent.constant) if parent.valid?
|
12
23
|
from_parent && from_parent.valid? ? from_parent : Invalid.new(scope, constant_name)
|
13
24
|
end
|
14
25
|
|
26
|
+
def self.direct_resolve(constant_name, scope)
|
27
|
+
if scope.equal?(Object) && constant_name.empty?
|
28
|
+
new(Object)
|
29
|
+
elsif scope.const_defined?(constant_name)
|
30
|
+
new(const_lookup(scope, constant_name))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
15
34
|
def self.parent_namespace(scope)
|
16
35
|
parent_name = scope.name.split('::').slice(0...-1).join('::')
|
17
36
|
|
@@ -22,6 +41,13 @@ module Yardcheck
|
|
22
41
|
end
|
23
42
|
end
|
24
43
|
|
44
|
+
def self.const_lookup(scope, name)
|
45
|
+
SPECIAL_CASES.fetch(name) do
|
46
|
+
scope.const_get(name) if scope.const_defined?(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
private_class_method :const_lookup
|
50
|
+
|
25
51
|
def valid?
|
26
52
|
true
|
27
53
|
end
|
@@ -23,6 +23,10 @@ module Yardcheck
|
|
23
23
|
return_typedef unless return_typedef&.invalid_const?
|
24
24
|
end
|
25
25
|
|
26
|
+
def raise_type
|
27
|
+
raise_typedef unless raise_typedef&.invalid_const?
|
28
|
+
end
|
29
|
+
|
26
30
|
def singleton?
|
27
31
|
scope.equal?(:class)
|
28
32
|
end
|
@@ -58,15 +62,29 @@ module Yardcheck
|
|
58
62
|
[*param_warnings, *return_warning].map { |warning| Warning.new(self, warning) }
|
59
63
|
end
|
60
64
|
|
65
|
+
def predicate_method?
|
66
|
+
selector.to_s.end_with?('?')
|
67
|
+
end
|
68
|
+
|
69
|
+
def processed_source
|
70
|
+
ProcessedSource.new(yardoc.source)
|
71
|
+
end
|
72
|
+
memoize :processed_source
|
73
|
+
|
61
74
|
private
|
62
75
|
|
63
76
|
def return_typedef
|
64
|
-
|
77
|
+
aggregate_tags(:return)
|
65
78
|
end
|
66
79
|
memoize :return_typedef
|
67
80
|
|
68
|
-
def
|
69
|
-
|
81
|
+
def raise_typedef
|
82
|
+
aggregate_tags(:raise)
|
83
|
+
end
|
84
|
+
memoize :raise_typedef
|
85
|
+
|
86
|
+
def aggregate_tags(name)
|
87
|
+
tags(name).map(&method(:typedefs)).reduce(:+)
|
70
88
|
end
|
71
89
|
|
72
90
|
def param_typedefs
|
@@ -2,25 +2,21 @@
|
|
2
2
|
|
3
3
|
module Yardcheck
|
4
4
|
class MethodCall
|
5
|
-
include Anima.new(
|
5
|
+
include AbstractType, Anima.new(
|
6
6
|
:scope,
|
7
7
|
:selector,
|
8
8
|
:namespace,
|
9
9
|
:params,
|
10
|
-
:example_location
|
11
|
-
:return_value,
|
12
|
-
:error_raised
|
10
|
+
:example_location
|
13
11
|
)
|
14
12
|
|
15
|
-
def self.process(params:,
|
13
|
+
def self.process(params:, **attributes)
|
16
14
|
params =
|
17
15
|
params.map do |key, value|
|
18
16
|
[key, TestValue.process(value)]
|
19
17
|
end.to_h
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
new(params: params, return_value: return_value, **attributes)
|
19
|
+
new(params: params, **attributes)
|
24
20
|
end
|
25
21
|
|
26
22
|
def method_identifier
|
@@ -31,8 +27,39 @@ module Yardcheck
|
|
31
27
|
selector == :initialize && scope == :instance
|
32
28
|
end
|
33
29
|
|
34
|
-
def
|
35
|
-
|
30
|
+
def raise?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def return?
|
35
|
+
false
|
36
36
|
end
|
37
|
+
|
38
|
+
class Return < self
|
39
|
+
include anima.add(:return_value)
|
40
|
+
|
41
|
+
def self.process(return_value:, **kwargs)
|
42
|
+
super(return_value: TestValue.process(return_value), **kwargs)
|
43
|
+
end
|
44
|
+
|
45
|
+
def return?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
end # Return
|
49
|
+
|
50
|
+
class Raise < self
|
51
|
+
include anima.add(:exception)
|
52
|
+
|
53
|
+
def self.process(exception:, **kwargs)
|
54
|
+
super(exception: TestValue.process(exception), **kwargs)
|
55
|
+
end
|
56
|
+
|
57
|
+
def raise?
|
58
|
+
true
|
59
|
+
end
|
60
|
+
end # Raise
|
61
|
+
|
62
|
+
class Jump < self
|
63
|
+
end # Jump
|
37
64
|
end # MethodCall
|
38
65
|
end # Yardcheck
|
@@ -6,6 +6,17 @@ module Yardcheck
|
|
6
6
|
|
7
7
|
def initialize(namespace)
|
8
8
|
super(namespace, [], [])
|
9
|
+
|
10
|
+
# When an exception is raised it isn't clear when it has been eventually rescued.
|
11
|
+
# We set `@current_exception` to `true` when we have observed an exception
|
12
|
+
# and have not seen a non `nil` return.
|
13
|
+
@current_exception = nil
|
14
|
+
|
15
|
+
# A block passed from one method down to another method can trigger a return of the
|
16
|
+
# originating method. This triggers a waterfall of `nil` returns because each method
|
17
|
+
# shortcircuits. Similar to `current_exception` we need to also track jumps triggered
|
18
|
+
# by block returns
|
19
|
+
@block_jump = false
|
9
20
|
end
|
10
21
|
|
11
22
|
def trace(&block)
|
@@ -18,8 +29,10 @@ module Yardcheck
|
|
18
29
|
|
19
30
|
private
|
20
31
|
|
32
|
+
attr_reader :current_exception, :block_jump
|
33
|
+
|
21
34
|
def tracer
|
22
|
-
TracePoint.new(:call, :return, :raise) do |event|
|
35
|
+
TracePoint.new(:call, :return, :raise, :b_return) do |event|
|
23
36
|
tracer.disable do
|
24
37
|
process(event) if target?(event.defined_class)
|
25
38
|
end
|
@@ -29,13 +42,18 @@ module Yardcheck
|
|
29
42
|
|
30
43
|
def process(trace_event)
|
31
44
|
case trace_event.event
|
32
|
-
when :call
|
33
|
-
when :return
|
34
|
-
when :raise
|
45
|
+
when :call then process_call(trace_event)
|
46
|
+
when :return then process_return(trace_event)
|
47
|
+
when :raise then process_raise(trace_event)
|
48
|
+
when :b_return then process_block_return
|
35
49
|
end
|
36
50
|
end
|
37
51
|
|
38
52
|
def process_call(trace_event)
|
53
|
+
# If we observe a method call then we are certainly no longer inside of a
|
54
|
+
# bubbling up of early returns
|
55
|
+
@block_jump = false
|
56
|
+
|
39
57
|
parameter_names =
|
40
58
|
trace_event
|
41
59
|
.defined_class
|
@@ -54,13 +72,35 @@ module Yardcheck
|
|
54
72
|
end
|
55
73
|
|
56
74
|
def process_return(trace_event)
|
57
|
-
|
58
|
-
|
59
|
-
|
75
|
+
return_value = trace_event.return_value
|
76
|
+
|
77
|
+
# If we observe a non `nil` return value then it is no longer possible that we are observing
|
78
|
+
# "fake returns" caused by either an exception being raised or a block being executed
|
79
|
+
# that invoked `return` and caused other methods to return early
|
80
|
+
unless nil.equal?(return_value)
|
81
|
+
@current_exception = nil
|
82
|
+
@block_jump = false
|
83
|
+
end
|
84
|
+
|
85
|
+
frame = call_stack.pop
|
86
|
+
method_call =
|
87
|
+
if @current_exception
|
88
|
+
MethodCall::Raise.process(frame.merge(exception: @current_exception))
|
89
|
+
elsif @block_jump
|
90
|
+
MethodCall::Jump.process(frame)
|
91
|
+
else
|
92
|
+
MethodCall::Return.process(frame.merge(return_value: return_value))
|
93
|
+
end
|
94
|
+
|
95
|
+
seen << method_call
|
96
|
+
end
|
97
|
+
|
98
|
+
def process_raise(trace_event)
|
99
|
+
@current_exception = trace_event.raised_exception
|
60
100
|
end
|
61
101
|
|
62
|
-
def
|
63
|
-
|
102
|
+
def process_block_return
|
103
|
+
@block_jump = true
|
64
104
|
end
|
65
105
|
|
66
106
|
def event_details(event)
|
@@ -68,8 +108,7 @@ module Yardcheck
|
|
68
108
|
scope: event.defined_class.__send__(:singleton_class?) ? :class : :instance,
|
69
109
|
selector: event.method_id,
|
70
110
|
namespace: event.defined_class,
|
71
|
-
example_location: RSpec.current_example.location
|
72
|
-
error_raised: false
|
111
|
+
example_location: RSpec.current_example.location
|
73
112
|
}
|
74
113
|
end
|
75
114
|
|
@@ -5,7 +5,7 @@ module Yardcheck
|
|
5
5
|
include Concord.new(:documentation, :event)
|
6
6
|
|
7
7
|
def violations
|
8
|
-
param_violations + return_violations
|
8
|
+
param_violations + return_violations + raise_violations
|
9
9
|
end
|
10
10
|
|
11
11
|
def source_code
|
@@ -36,10 +36,18 @@ module Yardcheck
|
|
36
36
|
documentation.return_type
|
37
37
|
end
|
38
38
|
|
39
|
+
def documented_raise_type
|
40
|
+
documentation.raise_type
|
41
|
+
end
|
42
|
+
|
39
43
|
def actual_return_type
|
40
44
|
event.return_value.type
|
41
45
|
end
|
42
46
|
|
47
|
+
def actual_raise_type
|
48
|
+
event.exception.type
|
49
|
+
end
|
50
|
+
|
43
51
|
def documentation_warnings
|
44
52
|
documentation.warnings
|
45
53
|
end
|
@@ -59,14 +67,32 @@ module Yardcheck
|
|
59
67
|
end
|
60
68
|
|
61
69
|
def return_violations
|
62
|
-
|
70
|
+
invalid_return? ? [Violation::Return.new(self)] : []
|
71
|
+
end
|
72
|
+
|
73
|
+
def raise_violations
|
74
|
+
invalid_raise? ? [Violation::Raise.new(self)] : []
|
63
75
|
end
|
64
76
|
|
65
|
-
def
|
66
|
-
documentation.
|
77
|
+
def invalid_raise?
|
78
|
+
documentation.raise_type &&
|
79
|
+
event.raise? &&
|
80
|
+
!documentation.raise_type.match?(event.exception)
|
81
|
+
end
|
82
|
+
|
83
|
+
def invalid_return?
|
84
|
+
documentation.return_type &&
|
85
|
+
event.return? &&
|
67
86
|
!documentation.return_type.match?(event.return_value) &&
|
68
|
-
!event.
|
69
|
-
!
|
87
|
+
!event.initialize? &&
|
88
|
+
!documentation.predicate_method? &&
|
89
|
+
!possible_tracepoint_bug?
|
90
|
+
end
|
91
|
+
|
92
|
+
def possible_tracepoint_bug?
|
93
|
+
return false unless event.return_value.is?(nil)
|
94
|
+
|
95
|
+
documentation.processed_source.tracepoint_bug_candidate?
|
70
96
|
end
|
71
97
|
end # Observation
|
72
98
|
end # Yardcheck
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yardcheck
|
4
|
+
class ProcessedSource
|
5
|
+
include Concord.new(:raw_source), Adamantium::Flat
|
6
|
+
|
7
|
+
# @see https://bugs.ruby-lang.org/issues/13369
|
8
|
+
def tracepoint_bug_candidate?
|
9
|
+
resbodies = find_resbodies
|
10
|
+
resbodies.any? do |node|
|
11
|
+
find_type(node, :return).any?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def find_resbodies(node = ast)
|
18
|
+
find_type(node, :resbody)
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_type(node, type)
|
22
|
+
return nil unless node.is_a?(Parser::AST::Node)
|
23
|
+
return [node] if node.type == type
|
24
|
+
|
25
|
+
node.children.map { |child| find_type(child, type) }.flatten.compact
|
26
|
+
end
|
27
|
+
|
28
|
+
def ast
|
29
|
+
Parser::CurrentRuby.parse(raw_source)
|
30
|
+
end
|
31
|
+
end # ProcessedSource
|
32
|
+
end # Yardcheck
|
data/lib/yardcheck/runner.rb
CHANGED
data/lib/yardcheck/typedef.rb
CHANGED
@@ -5,7 +5,9 @@ module Yardcheck
|
|
5
5
|
include Concord.new(:types)
|
6
6
|
|
7
7
|
def self.parse(types)
|
8
|
-
if types.
|
8
|
+
if types.empty?
|
9
|
+
Unspecified.new
|
10
|
+
elsif types.include?(:undefined)
|
9
11
|
fail 'Cannot combined [undefined] with other types' unless types.one?
|
10
12
|
Undefined.new
|
11
13
|
elsif types.grep(Parser::Invalid).any?
|
@@ -33,6 +35,18 @@ module Yardcheck
|
|
33
35
|
types.any?(&:invalid_const?)
|
34
36
|
end
|
35
37
|
|
38
|
+
class Unspecified < self
|
39
|
+
include Concord.new
|
40
|
+
|
41
|
+
def signature
|
42
|
+
'(Unspecified type)'
|
43
|
+
end
|
44
|
+
|
45
|
+
def invalid_const?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
end # Unspecified
|
49
|
+
|
36
50
|
class Literal < self
|
37
51
|
include Concord.new(:const)
|
38
52
|
|
@@ -117,6 +131,6 @@ module Yardcheck
|
|
117
131
|
def invalid_const?
|
118
132
|
true
|
119
133
|
end
|
120
|
-
end
|
134
|
+
end # Invalid
|
121
135
|
end # Typedef
|
122
136
|
end # Yardcheck
|
data/lib/yardcheck/version.rb
CHANGED
data/lib/yardcheck/violation.rb
CHANGED
@@ -152,5 +152,43 @@ module Yardcheck
|
|
152
152
|
observation.documented_param(name)
|
153
153
|
end
|
154
154
|
end # Param
|
155
|
+
|
156
|
+
class Raise < self
|
157
|
+
include Equalizer.new(:observation)
|
158
|
+
|
159
|
+
FORMAT =
|
160
|
+
"Expected #{blue('%<shorthand>s')} to raise " \
|
161
|
+
"#{yellow('%<signature>s')} but observed " \
|
162
|
+
"#{red('%<observed_type>s')}"
|
163
|
+
|
164
|
+
def initialize(observation, test_locations = [observation.test_location])
|
165
|
+
super
|
166
|
+
end
|
167
|
+
|
168
|
+
def explanation
|
169
|
+
format(
|
170
|
+
FORMAT,
|
171
|
+
shorthand: shorthand,
|
172
|
+
signature: signature,
|
173
|
+
observed_type: observed_type
|
174
|
+
)
|
175
|
+
end
|
176
|
+
|
177
|
+
protected
|
178
|
+
|
179
|
+
def observed_type
|
180
|
+
observation.actual_raise_type
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def combine_requirements
|
186
|
+
%i[shorthand signature observed_type]
|
187
|
+
end
|
188
|
+
|
189
|
+
def expected_type
|
190
|
+
observation.documented_raise_type
|
191
|
+
end
|
192
|
+
end # Raise
|
155
193
|
end # Violation
|
156
194
|
end # Yardcheck
|
data/lib/yardcheck.rb
CHANGED
@@ -5,6 +5,7 @@ require 'concord'
|
|
5
5
|
require 'yard'
|
6
6
|
require 'rspec'
|
7
7
|
require 'coderay'
|
8
|
+
require 'parser/current'
|
8
9
|
|
9
10
|
require 'yardcheck/version'
|
10
11
|
require 'yardcheck/runner'
|
@@ -24,3 +25,4 @@ require 'yardcheck/color'
|
|
24
25
|
require 'yardcheck/violation'
|
25
26
|
require 'yardcheck/warning'
|
26
27
|
require 'yardcheck/source_lines'
|
28
|
+
require 'yardcheck/processed_source'
|
@@ -33,9 +33,11 @@ RSpec.describe 'test app integration' do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'generates a warning for invalid constant' do
|
36
|
-
expect_report('WARNING: Unabled to resolve "What" for lib/test_app.rb:
|
37
|
-
expect_report('WARNING: Unabled to resolve "Wow" for lib/test_app.rb:
|
38
|
-
expect_report('WARNING: Unabled to resolve
|
36
|
+
expect_report('WARNING: Unabled to resolve "What" for lib/test_app.rb:43')
|
37
|
+
expect_report('WARNING: Unabled to resolve "Wow" for lib/test_app.rb:43')
|
38
|
+
expect_report('WARNING: Unabled to resolve (Unspecified type) for lib/test_app.rb:58')
|
39
|
+
expect_report('WARNING: Unabled to resolve (Unspecified type) for lib/test_app.rb:58')
|
40
|
+
expect_report('WARNING: Unabled to resolve :foo for lib/test_app.rb:122')
|
39
41
|
end
|
40
42
|
|
41
43
|
it 'reports expectations' do
|
@@ -50,6 +52,10 @@ RSpec.describe 'test app integration' do
|
|
50
52
|
'Expected TestApp::Namespace#improperly_tested_with_instance_double ' \
|
51
53
|
'to receive String for value but observed Integer'
|
52
54
|
)
|
55
|
+
expect_report(
|
56
|
+
'Expected TestApp::Namespace#invalid_raise_documentation to raise ' \
|
57
|
+
'TestApp::Namespace::AppError but observed KeyError'
|
58
|
+
)
|
53
59
|
matches = report.scan(/^Expected .+ to return .+ but observed .+$/)
|
54
60
|
expect(matches.size).to be(3)
|
55
61
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,7 +4,11 @@ require 'pathname'
|
|
4
4
|
require 'bundler'
|
5
5
|
require 'timeout'
|
6
6
|
|
7
|
-
Bundler.with_clean_env
|
7
|
+
Bundler.with_clean_env do
|
8
|
+
Dir.chdir('test_app') do
|
9
|
+
YARD::CLI::Yardoc.run(*%w[--no-cache --no-output --no-stats --quiet --no-progress .])
|
10
|
+
end
|
11
|
+
end
|
8
12
|
|
9
13
|
begin
|
10
14
|
require 'mutest'
|