reek 4.6.2 → 4.7.0

Sign up to get free protection for your applications and to get access to all the features.
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