reek 4.6.2 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 621c25e4d095d1164a97aea57967dcf79f1961ec
4
- data.tar.gz: 4e9dbb2eee9bec1ac8616811b1895631206cd394
3
+ metadata.gz: 1ec8e3f40eb231654c58d4025080c703268e7322
4
+ data.tar.gz: a018b00cdba03a5da356e1c05e32bd0bc04ba569
5
5
  SHA512:
6
- metadata.gz: d5c4bf0e5c8f2b344b5bec3c386599e1a2d2eadc6e70c484146d771190fcc6fba24986a7747654962caaac5f27c625a49d2f11a5f9f0c8f375b34b309945178b
7
- data.tar.gz: a98dab6abcd0ffdc95695ba0883ca078ae9fa3b50d0493bf1d89f3677947d7056d5a3e7529eb9611ad5a50d1ab1efb682f2611247cd0d7225e3d4fcc12396ccb
6
+ metadata.gz: 78c20b5e99a3c4f804c530da98b020d634b289ea575f15339e3978088f207112a022345695b7ba45fcaa5edfbac74bcba38409e99ffc6e42d0e46f2add060fdf
7
+ data.tar.gz: 1bd64f7d805994fb356887cb638e900669c2308d2bc86fed37ad36709df4314dbe4dcfc616240c8790bf1d61ce5e3c1b58fd7b07e5bed385d0ced79cfad19cdf
@@ -8,6 +8,30 @@ AllCops:
8
8
  - 'vendor/**/*'
9
9
  TargetRubyVersion: 2.1
10
10
 
11
+ # Place . on the previous line
12
+ Layout/DotPosition:
13
+ EnforcedStyle: trailing
14
+
15
+ # Require empty lines between defs, except for one-line defs
16
+ Layout/EmptyLineBetweenDefs:
17
+ AllowAdjacentOneLineDefs: true
18
+
19
+ # Use active_support's strip_heredoc to indent heredocs
20
+ Layout/IndentHeredoc:
21
+ EnforcedStyle: active_support
22
+
23
+ # Always put the closing brace on the last line
24
+ Layout/MultilineMethodCallBraceLayout:
25
+ EnforcedStyle: same_line
26
+
27
+ # Indent one level for follow-up lines
28
+ Layout/MultilineMethodCallIndentation:
29
+ EnforcedStyle: indented
30
+
31
+ # Indent one level for follow-up lines
32
+ Layout/MultilineOperationIndentation:
33
+ EnforcedStyle: indented
34
+
11
35
  # Assume the programmer knows how bracketed block syntax works
12
36
  Lint/AmbiguousBlockAssociation:
13
37
  Enabled: false
@@ -95,14 +119,6 @@ Style/Documentation:
95
119
  - 'lib/reek/ast/sexp_extensions/super.rb'
96
120
  - 'lib/reek/ast/sexp_extensions/variables.rb'
97
121
 
98
- # Place . on the previous line
99
- Style/DotPosition:
100
- EnforcedStyle: trailing
101
-
102
- # Require empty lines between defs, except for one-line defs
103
- Style/EmptyLineBetweenDefs:
104
- AllowAdjacentOneLineDefs: true
105
-
106
122
  # Require comment for files in lib and bin
107
123
  Style/FrozenStringLiteralComment:
108
124
  Include:
@@ -110,26 +126,10 @@ Style/FrozenStringLiteralComment:
110
126
  - 'lib/**/*'
111
127
  EnforcedStyle: always
112
128
 
113
- # Use active_support's strip_heredoc to indent heredocs
114
- Style/IndentHeredoc:
115
- EnforcedStyle: active_support
116
-
117
129
  # Allow multiline block chains
118
130
  Style/MultilineBlockChain:
119
131
  Enabled: false
120
132
 
121
- # Always put the closing brace on the last line
122
- Style/MultilineMethodCallBraceLayout:
123
- EnforcedStyle: same_line
124
-
125
- # Indent one level for follow-up lines
126
- Style/MultilineMethodCallIndentation:
127
- EnforcedStyle: indented
128
-
129
- # Indent one level for follow-up lines
130
- Style/MultilineOperationIndentation:
131
- EnforcedStyle: indented
132
-
133
133
  # There's nothing wrong with parallel assignment
134
134
  Style/ParallelAssignment:
135
135
  Enabled: false
@@ -1,5 +1,9 @@
1
1
  # Change log
2
2
 
3
+ ## 4.7.0 (2017-05-31)
4
+
5
+ * (pocke) Introduce Syntax smell detector
6
+
3
7
  ## 4.6.2 (2017-04-27)
4
8
 
5
9
  * (pocke) Prevent from breaking on a string with escape sequence incompatible with UTF-8
data/Gemfile CHANGED
@@ -16,7 +16,7 @@ group :development do
16
16
  gem 'yard', '~> 0.9.5'
17
17
 
18
18
  if RUBY_VERSION >= '2.3'
19
- gem 'rubocop', '~> 0.48.1'
19
+ gem 'rubocop', '~> 0.49.1'
20
20
  gem 'rubocop-rspec', '~> 1.15.0'
21
21
  end
22
22
 
@@ -66,6 +66,9 @@ RepeatedConditional:
66
66
  SubclassedFromCoreClass:
67
67
  enabled: true
68
68
  exclude: []
69
+ Syntax:
70
+ enabled: true
71
+ exclude: []
69
72
  TooManyConstants:
70
73
  enabled: true
71
74
  exclude: []
@@ -82,7 +82,7 @@ ruby.rb -- 1 warning:
82
82
  If you want to suppress this warning you can do this via source comment like this:
83
83
 
84
84
  ```Ruby
85
- # :reek:PrimaDonnaMethod: { exclude: [ bravo! ] }
85
+ # :reek:PrimaDonnaMethod { exclude: [ bravo! ] }
86
86
  class Alfa
87
87
  def bravo!
88
88
  end
@@ -34,6 +34,10 @@ Feature: Reek reads from $stdin when no files are given
34
34
 
35
35
  Scenario: syntax error causes the source to be ignored
36
36
  When I pass "= invalid syntax =" to reek
37
- Then it reports a parsing error
38
- And the exit status indicates an error
39
- And it reports nothing
37
+ Then the exit status indicates smells
38
+ And it reports:
39
+ """
40
+ STDIN -- 2 warnings:
41
+ [2]:Syntax: This file has unexpected token $end [https://github.com/troessner/reek/blob/master/docs/Syntax.md]
42
+ [1]:Syntax: This file has unexpected token tEQL [https://github.com/troessner/reek/blob/master/docs/Syntax.md]
43
+ """
@@ -97,12 +97,15 @@ module Reek
97
97
  #
98
98
  # :reek:TooManyStatements { max_statements: 8 }
99
99
  def run
100
- return [] unless syntax_tree
101
- begin
102
- examine_tree
103
- rescue Errors::BaseError => exception
104
- raise unless @error_handler.handle exception
105
- []
100
+ if source.valid_syntax? && syntax_tree
101
+ begin
102
+ examine_tree
103
+ rescue Errors::BaseError => exception
104
+ raise unless @error_handler.handle exception
105
+ []
106
+ end
107
+ else
108
+ SmellDetectors::Syntax.smells_from_source(source)
106
109
  end
107
110
  end
108
111
 
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env ruby
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'rake'
@@ -860,3 +860,7 @@ SubclassedFromCoreClass:
860
860
  you are gonna have a bad time.
861
861
 
862
862
  Source: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
863
+ Syntax:
864
+ remediation_points: 250_000
865
+ content: |
866
+ Check syntax errors.
@@ -18,6 +18,7 @@ require_relative 'smell_detectors/nil_check'
18
18
  require_relative 'smell_detectors/prima_donna_method'
19
19
  require_relative 'smell_detectors/repeated_conditional'
20
20
  require_relative 'smell_detectors/subclassed_from_core_class'
21
+ require_relative 'smell_detectors/syntax'
21
22
  require_relative 'smell_detectors/too_many_instance_variables'
22
23
  require_relative 'smell_detectors/too_many_constants'
23
24
  require_relative 'smell_detectors/too_many_methods'
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_detector'
4
+
5
+ module Reek
6
+ module SmellDetectors
7
+ # Check syntax errors.
8
+ # Note: this detector is called by examiner directly unlike other detectors.
9
+ class Syntax < BaseDetector
10
+ DummyContext = Struct.new(:exp, :full_name).new(
11
+ Struct.new('Exp', :source).new(nil),
12
+ 'This file')
13
+
14
+ def self.contexts
15
+ []
16
+ end
17
+
18
+ def self.smells_from_source(source)
19
+ new.smells_from_source(source)
20
+ end
21
+
22
+ # :reek:FeatureEnvy
23
+ def smells_from_source(source)
24
+ source.diagnostics.map do |diagnostic|
25
+ smell_warning(
26
+ context: DummyContext,
27
+ lines: [diagnostic.location.line],
28
+ message: "has #{diagnostic.message}")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -7,7 +7,6 @@ end
7
7
  require_relative '../tree_dresser'
8
8
  require_relative '../ast/node'
9
9
  require_relative '../ast/builder'
10
- require_relative '../errors/parse_error'
11
10
 
12
11
  # Opt in to new way of representing lambdas
13
12
  Parser::Builders::Default.emit_lambda = true
@@ -21,7 +20,7 @@ module Reek
21
20
  IO_IDENTIFIER = 'STDIN'.freeze
22
21
  STRING_IDENTIFIER = 'string'.freeze
23
22
 
24
- attr_reader :origin
23
+ attr_reader :origin, :diagnostics, :syntax_tree
25
24
 
26
25
  # Initializer.
27
26
  #
@@ -29,9 +28,9 @@ module Reek
29
28
  # origin - 'STDIN', 'string' or a filepath as String
30
29
  # parser - the parser to use for generating AST's out of the given source
31
30
  def initialize(code:, origin:, parser: default_parser)
32
- @source = code
33
31
  @origin = origin
34
- @parser = parser
32
+ @diagnostics = []
33
+ @syntax_tree = parse(parser, code)
35
34
  end
36
35
 
37
36
  # Initializes an instance of SourceCode given a source.
@@ -53,13 +52,22 @@ module Reek
53
52
  end
54
53
  end
55
54
 
55
+ # @return [true|false] Returns true if parsed file does not have any syntax errors.
56
+ def valid_syntax?
57
+ @diagnostics.none? { |diagnostic| [:error, :fatal].include?(diagnostic.level) }
58
+ end
59
+
60
+ private
61
+
62
+ attr_reader :source
63
+
56
64
  # Parses the given source into an AST and associates the source code comments with it.
57
65
  # This AST is then traversed by a TreeDresser which adorns the nodes in the AST
58
66
  # with our SexpExtensions.
59
67
  # Finally this AST is returned where each node is an anonymous subclass of Reek::AST::Node
60
68
  #
61
- # Important to note is that Reek will not fail on unparseable files but rather print out
62
- # a warning and then just continue.
69
+ # Important to note is that Reek will not fail on unparseable files but rather register a
70
+ # parse error to @diagnostics and then just continue.
63
71
  #
64
72
  # Given this @source:
65
73
  #
@@ -82,36 +90,34 @@ module Reek
82
90
  # where each node is possibly adorned with our SexpExtensions (see ast/ast_node_class_map
83
91
  # and ast/sexp_extensions for details).
84
92
  #
85
- # @return [Anonymous subclass of Reek::AST::Node] the AST presentation
86
- # for the given source
87
- # :reek:TooManyStatements: { max_statements: 7 }
88
- def syntax_tree
89
- @syntax_tree ||=
90
- begin
91
- buffer = Parser::Source::Buffer.new(origin, 1)
92
- buffer.source = source
93
- begin
94
- ast, comments = parser.parse_with_comments(buffer)
95
- rescue Racc::ParseError, Parser::SyntaxError => error
96
- raise Errors::ParseError, origin: origin, original_exception: error
97
- end
93
+ # @param parser [Parser::Ruby24]
94
+ # @param source [String] - Ruby code
95
+ # @return [Anonymous subclass of Reek::AST::Node] the AST presentation
96
+ # for the given source
97
+ def parse(parser, source)
98
+ buffer = Parser::Source::Buffer.new(origin, 1)
99
+ buffer.source = source
100
+ begin
101
+ ast, comments = parser.parse_with_comments(buffer)
102
+ rescue Parser::SyntaxError # rubocop:disable Lint/HandleExceptions
103
+ # All errors are in diagnostics. No need to handle exception.
104
+ end
98
105
 
99
- # See https://whitequark.github.io/parser/Parser/Source/Comment/Associator.html
100
- comment_map = Parser::Source::Comment.associate(ast, comments) if ast
101
- TreeDresser.new.dress(ast, comment_map)
102
- end
106
+ # See https://whitequark.github.io/parser/Parser/Source/Comment/Associator.html
107
+ comment_map = Parser::Source::Comment.associate(ast, comments) if ast
108
+ TreeDresser.new.dress(ast, comment_map)
103
109
  end
104
110
 
105
- private
106
-
107
- attr_reader :parser, :source
108
-
109
- # :reek:UtilityFunction
111
+ # :reek:TooManyStatements: { max_statements: 6 }
112
+ # :reek:FeatureEnvy
110
113
  def default_parser
111
114
  Parser::Ruby24.new(AST::Builder.new).tap do |parser|
112
115
  diagnostics = parser.diagnostics
113
- diagnostics.all_errors_are_fatal = true
114
- diagnostics.ignore_warnings = true
116
+ diagnostics.all_errors_are_fatal = false
117
+ diagnostics.ignore_warnings = false
118
+ diagnostics.consumer = lambda do |diagnostic|
119
+ @diagnostics << diagnostic
120
+ end
115
121
  end
116
122
  end
117
123
  end
@@ -8,6 +8,6 @@ module Reek
8
8
  # @public
9
9
  module Version
10
10
  # @public
11
- STRING = '4.6.2'.freeze
11
+ STRING = '4.7.0'.freeze
12
12
  end
13
13
  end
@@ -34,50 +34,31 @@ RSpec.describe Reek::Source::SourceCode do
34
34
 
35
35
  context 'when the parser fails' do
36
36
  let(:source_name) { 'Test source' }
37
- let(:error_message) { 'Error message' }
38
- let(:parser) { instance_double('Parser::Ruby24') }
39
- let(:src) { described_class.new(code: '', origin: source_name, parser: parser) }
40
-
41
- shared_examples_for 'handling and recording the error' do
42
- it 'raises an informative error' do
43
- expect { src.syntax_tree }.
44
- to raise_error(/#{source_name}: #{error_class.name}: #{error_message}/)
45
- end
46
- end
37
+ let(:src) { described_class.new(code: code, origin: source_name, **parser) }
47
38
 
48
39
  context 'with a Parser::SyntaxError' do
49
- let(:error_class) { Parser::SyntaxError }
50
- let(:diagnostic) { instance_double('Parser::Diagnostic', message: error_message) }
51
-
52
- before do
53
- allow(parser).to receive(:parse_with_comments).
54
- and_raise error_class.new(diagnostic)
55
- end
40
+ let(:code) { '== Invalid Syntax ==' }
41
+ let(:parser) { {} }
56
42
 
57
- it_behaves_like 'handling and recording the error'
58
- end
59
-
60
- context 'with a Racc::ParseError' do
61
- let(:error_class) { Racc::ParseError }
62
-
63
- before do
64
- allow(parser).to receive(:parse_with_comments).
65
- and_raise(error_class.new(error_message))
43
+ it 'adds a diagnostic' do
44
+ expect(src.diagnostics.size).to eq 2
66
45
  end
67
-
68
- it_behaves_like 'handling and recording the error'
69
46
  end
70
47
 
71
48
  context 'with a generic error' do
49
+ let(:code) { '' }
72
50
  let(:error_class) { RuntimeError }
73
-
74
- before do
75
- allow(parser).to receive(:parse_with_comments).
76
- and_raise(error_class.new(error_message))
51
+ let(:error_message) { 'An error' }
52
+ let(:parser) do
53
+ parser = instance_double('Parser::Ruby24')
54
+ allow(parser).to receive(:parse_with_comments).and_raise(error_class, error_message)
55
+ {
56
+ parser: parser
57
+ }
77
58
  end
78
59
 
79
60
  it 'raises the error' do
80
- expect { src.syntax_tree }.to raise_error error_class
61
+ expect { src }.to raise_error error_class, error_message
81
62
  end
82
63
  end
83
64
  end
@@ -1,7 +1,6 @@
1
1
  require 'pathname'
2
2
  require_relative '../../spec_helper'
3
3
  require_lib 'reek/spec'
4
- require 'active_support/core_ext/string/strip'
5
4
  require 'active_support/core_ext/hash/except'
6
5
 
7
6
  RSpec.describe Reek::Spec::ShouldReekOf do
@@ -1,5 +1,6 @@
1
1
  require 'pathname'
2
2
  require 'timeout'
3
+ require 'active_support/core_ext/string/strip'
3
4
  require_relative '../lib/reek'
4
5
  require_relative '../lib/reek/spec'
5
6
  require_relative '../lib/reek/ast/ast_node_class_map'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reek
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.6.2
4
+ version: 4.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Rutherford
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2017-04-27 00:00:00.000000000 Z
14
+ date: 2017-05-31 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: codeclimate-engine-rb
@@ -230,7 +230,6 @@ files:
230
230
  - lib/reek/errors/base_error.rb
231
231
  - lib/reek/errors/garbage_detector_configuration_in_comment_error.rb
232
232
  - lib/reek/errors/incomprehensible_source_error.rb
233
- - lib/reek/errors/parse_error.rb
234
233
  - lib/reek/examiner.rb
235
234
  - lib/reek/logging_error_handler.rb
236
235
  - lib/reek/rake/task.rb
@@ -275,6 +274,7 @@ files:
275
274
  - lib/reek/smell_detectors/prima_donna_method.rb
276
275
  - lib/reek/smell_detectors/repeated_conditional.rb
277
276
  - lib/reek/smell_detectors/subclassed_from_core_class.rb
277
+ - lib/reek/smell_detectors/syntax.rb
278
278
  - lib/reek/smell_detectors/too_many_constants.rb
279
279
  - lib/reek/smell_detectors/too_many_instance_variables.rb
280
280
  - lib/reek/smell_detectors/too_many_methods.rb
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base_error'
4
-
5
- module Reek
6
- module Errors
7
- # Gets raised when Reek is unable to process the source
8
- class ParseError < BaseError
9
- MESSAGE_TEMPLATE = '%s: %s: %s'.freeze
10
-
11
- def initialize(origin:, original_exception:)
12
- message = format(MESSAGE_TEMPLATE,
13
- origin,
14
- original_exception.class.name,
15
- original_exception.message)
16
- super message
17
- end
18
- end
19
- end
20
- end