improve_your_code 1.0.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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +12 -0
  4. data/README.md +0 -0
  5. data/Rakefile +4 -0
  6. data/bin/improve_your_code +6 -0
  7. data/improve_your_code.gemspec +22 -0
  8. data/lib/improve_your_code/ast/ast_node_class_map.rb +39 -0
  9. data/lib/improve_your_code/ast/builder.rb +11 -0
  10. data/lib/improve_your_code/ast/node.rb +108 -0
  11. data/lib/improve_your_code/ast/object_refs.rb +32 -0
  12. data/lib/improve_your_code/ast/reference_collector.rb +29 -0
  13. data/lib/improve_your_code/ast/sexp_extensions.rb +15 -0
  14. data/lib/improve_your_code/ast/sexp_extensions/arguments.rb +89 -0
  15. data/lib/improve_your_code/ast/sexp_extensions/block.rb +38 -0
  16. data/lib/improve_your_code/ast/sexp_extensions/constant.rb +13 -0
  17. data/lib/improve_your_code/ast/sexp_extensions/if.rb +18 -0
  18. data/lib/improve_your_code/ast/sexp_extensions/methods.rb +81 -0
  19. data/lib/improve_your_code/ast/sexp_extensions/module.rb +64 -0
  20. data/lib/improve_your_code/ast/sexp_extensions/nested_assignables.rb +17 -0
  21. data/lib/improve_your_code/ast/sexp_extensions/self.rb +13 -0
  22. data/lib/improve_your_code/ast/sexp_extensions/send.rb +55 -0
  23. data/lib/improve_your_code/ast/sexp_extensions/symbols.rb +14 -0
  24. data/lib/improve_your_code/ast/sexp_extensions/variables.rb +45 -0
  25. data/lib/improve_your_code/cli/application.rb +32 -0
  26. data/lib/improve_your_code/cli/command/report_command.rb +39 -0
  27. data/lib/improve_your_code/cli/silencer.rb +24 -0
  28. data/lib/improve_your_code/code_comment.rb +52 -0
  29. data/lib/improve_your_code/context/attribute_context.rb +33 -0
  30. data/lib/improve_your_code/context/code_context.rb +121 -0
  31. data/lib/improve_your_code/context/method_context.rb +79 -0
  32. data/lib/improve_your_code/context/module_context.rb +77 -0
  33. data/lib/improve_your_code/context/root_context.rb +22 -0
  34. data/lib/improve_your_code/context/send_context.rb +16 -0
  35. data/lib/improve_your_code/context/singleton_attribute_context.rb +13 -0
  36. data/lib/improve_your_code/context/singleton_method_context.rb +29 -0
  37. data/lib/improve_your_code/context/statement_counter.rb +27 -0
  38. data/lib/improve_your_code/context/visibility_tracker.rb +52 -0
  39. data/lib/improve_your_code/context_builder.rb +121 -0
  40. data/lib/improve_your_code/detector_repository.rb +32 -0
  41. data/lib/improve_your_code/examiner.rb +48 -0
  42. data/lib/improve_your_code/report/formatter.rb +25 -0
  43. data/lib/improve_your_code/report/formatter/heading_formatter.rb +33 -0
  44. data/lib/improve_your_code/report/formatter/progress_formatter.rb +25 -0
  45. data/lib/improve_your_code/report/formatter/simple_warning_formatter.rb +13 -0
  46. data/lib/improve_your_code/report/text_report.rb +76 -0
  47. data/lib/improve_your_code/smell_configuration.rb +48 -0
  48. data/lib/improve_your_code/smell_detectors.rb +12 -0
  49. data/lib/improve_your_code/smell_detectors/base_detector.rb +114 -0
  50. data/lib/improve_your_code/smell_detectors/long_parameter_list.rb +44 -0
  51. data/lib/improve_your_code/smell_detectors/too_many_constants.rb +54 -0
  52. data/lib/improve_your_code/smell_detectors/too_many_instance_variables.rb +48 -0
  53. data/lib/improve_your_code/smell_detectors/too_many_methods.rb +47 -0
  54. data/lib/improve_your_code/smell_detectors/too_many_statements.rb +43 -0
  55. data/lib/improve_your_code/smell_detectors/uncommunicative_method_name.rb +58 -0
  56. data/lib/improve_your_code/smell_detectors/uncommunicative_module_name.rb +71 -0
  57. data/lib/improve_your_code/smell_detectors/uncommunicative_variable_name.rb +129 -0
  58. data/lib/improve_your_code/smell_detectors/unused_parameters.rb +24 -0
  59. data/lib/improve_your_code/smell_detectors/unused_private_method.rb +69 -0
  60. data/lib/improve_your_code/smell_warning.rb +70 -0
  61. data/lib/improve_your_code/source/source_code.rb +84 -0
  62. data/lib/improve_your_code/source/source_locator.rb +38 -0
  63. data/lib/improve_your_code/tree_dresser.rb +74 -0
  64. data/lib/improve_your_code/version.rb +7 -0
  65. metadata +141 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3a81cdf7aa2de07a1fa7de9913869dd0d13c9d2b
4
+ data.tar.gz: 9e90b1d298fb90594093b3e2ed0486e99f248d15
5
+ SHA512:
6
+ metadata.gz: d163763a9847ea7a6a3420a0dd7cb7ef23704fb98b0590706cc8987ae634a71d070f4715027163240b8830e34a9dec1093970c6214c93efaed949331cb462e2b
7
+ data.tar.gz: f9f073e2c4a4d112a45e4ae720cd8f6990d4110fab0b443d0ac45ed06d3cbd3ed282a39d95b4db019a2e570e003a7ecbca99174a15f28cfcb028f63eff32b93f
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ pkg
2
+ spec/output
3
+ tmp
4
+ doc
5
+ coverage
6
+ .yardoc
7
+ .bundle
8
+ .rspec
9
+ .rbx
10
+ Gemfile.lock
11
+ *.swp
12
+ tags
13
+ .pryrc
14
+ .tags*
15
+ .DS_Store
16
+ .idea/
17
+ .rbenv-gemsets
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ ruby RUBY_VERSION
8
+
9
+ group :development do
10
+ gem 'activesupport', '>= 4.2'
11
+ gem 'rake', '~> 12.0'
12
+ end
data/README.md ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/clean'
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/improve_your_code/cli/application'
5
+
6
+ exit ImproveYourCode::CLI::Application.new.execute
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.join(File.dirname(__FILE__), 'lib/improve_your_code/version')
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'improve_your_code'
7
+ s.version = ImproveYourCode::Version::STRING
8
+
9
+ s.authors = ['Ilya Umanets']
10
+ s.default_executable = 'improve_your_code'
11
+ s.description =
12
+ 'ImproveYourCode is a tool that examines Ruby classes, modules and methods and reports ' \
13
+ 'any code smells it finds.'
14
+
15
+ s.files = `git ls-files -z`.split("\0")
16
+ s.executables = s.files.grep(%r{^bin/}).map { |path| File.basename(path) }
17
+ s.required_ruby_version = '>= 2.1.0'
18
+ s.summary = 'Code smell detector for Ruby'
19
+
20
+ s.add_runtime_dependency 'parser', '< 2.5', '>= 2.4.0.0'
21
+ s.add_runtime_dependency 'rainbow', '~> 2.0'
22
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'node'
4
+ require_relative 'sexp_extensions'
5
+
6
+ module ImproveYourCode
7
+ module AST
8
+ class ASTNodeClassMap
9
+ def initialize
10
+ @klass_map = {}
11
+ end
12
+
13
+ def klass_for(type)
14
+ klass_map[type] ||= Class.new(Node).tap do |klass|
15
+ extension = extension_map[type]
16
+
17
+ klass.send :include, extension if extension
18
+ end
19
+ end
20
+
21
+ def extension_map
22
+ @extension_map ||=
23
+ begin
24
+ assoc = SexpExtensions.constants.map do |const|
25
+ [
26
+ const.to_s.sub(/Node$/, '').downcase.to_sym,
27
+ SexpExtensions.const_get(const)
28
+ ]
29
+ end
30
+ Hash[assoc]
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :klass_map
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ class Builder < ::Parser::Builders::Default
6
+ def string_value(token)
7
+ value(token)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../cli/silencer'
4
+
5
+ ImproveYourCode::CLI::Silencer.silently do
6
+ require 'parser'
7
+ end
8
+
9
+ module ImproveYourCode
10
+ module AST
11
+ class Node < ::Parser::AST::Node
12
+ def initialize(type, children = [], options = {})
13
+ @comments = options.fetch(:comments, [])
14
+ super
15
+ end
16
+
17
+ def full_comment
18
+ comments.map(&:text).join("\n")
19
+ end
20
+
21
+ def leading_comment
22
+ line = location.line
23
+ comment_lines = comments.select do |comment|
24
+ comment.location.line < line
25
+ end
26
+ comment_lines.map(&:text).join("\n")
27
+ end
28
+
29
+ def line
30
+ loc&.line
31
+ end
32
+
33
+ def each_node(target_type, ignoring = [], &blk)
34
+ if block_given?
35
+ look_for_type(target_type, ignoring, &blk)
36
+ else
37
+ result = []
38
+ look_for_type(target_type, ignoring) { |exp| result << exp }
39
+ result
40
+ end
41
+ end
42
+
43
+ def find_nodes(target_types, ignoring = [])
44
+ result = []
45
+ look_for_types(target_types, ignoring) { |exp| result << exp }
46
+ result
47
+ end
48
+
49
+ def contains_nested_node?(target_type)
50
+ look_for_type(target_type) { |_elem| return true }
51
+ false
52
+ end
53
+
54
+ def format_to_ruby
55
+ if location
56
+ lines = location.expression.source.split("\n").map(&:strip)
57
+ case lines.length
58
+ when 1 then lines.first
59
+ when 2 then lines.join('; ')
60
+ else [lines.first, lines.last].join(' ... ')
61
+ end
62
+ else
63
+ to_s
64
+ end
65
+ end
66
+
67
+ def length
68
+ 1
69
+ end
70
+
71
+ def statements
72
+ [self]
73
+ end
74
+
75
+ def source
76
+ loc.expression.source_buffer.name
77
+ end
78
+
79
+ protected
80
+
81
+ def look_for_type(target_type, ignoring = [], &blk)
82
+ each_sexp do |elem|
83
+ elem.look_for_type(target_type, ignoring, &blk) unless ignoring.include?(elem.type)
84
+ end
85
+ yield self if type == target_type
86
+ end
87
+
88
+ def look_for_types(target_types, ignoring = [], &blk)
89
+ return if ignoring.include?(type)
90
+ if target_types.include? type
91
+ yield self
92
+ else
93
+ each_sexp do |elem|
94
+ elem.look_for_types(target_types, ignoring, &blk)
95
+ end
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ attr_reader :comments
102
+
103
+ def each_sexp
104
+ children.each { |elem| yield elem if elem.is_a? ::Parser::AST::Node }
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ class ObjectRefs
6
+ def initialize
7
+ @refs = Hash.new { |refs, name| refs[name] = [] }
8
+ end
9
+
10
+ def record_reference(name:, line: nil)
11
+ refs[name] << line
12
+ end
13
+
14
+ def most_popular
15
+ max = refs.values.map(&:size).max
16
+ refs.select { |_name, refs| refs.size == max }
17
+ end
18
+
19
+ def references_to(name)
20
+ refs[name]
21
+ end
22
+
23
+ def self_is_max?
24
+ refs.empty? || most_popular.keys.include?(:self)
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :refs
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ class ReferenceCollector
6
+ def initialize(ast)
7
+ @ast = ast
8
+ end
9
+
10
+ def num_refs_to_self
11
+ (explicit_self_calls + implicit_self_calls).size
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :ast
17
+
18
+ def explicit_self_calls
19
+ %i[self super zsuper ivar ivasgn].flat_map do |node_type|
20
+ ast.each_node(node_type)
21
+ end
22
+ end
23
+
24
+ def implicit_self_calls
25
+ ast.each_node(:send).reject(&:receiver)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'reference_collector'
4
+
5
+ require_relative 'sexp_extensions/arguments'
6
+ require_relative 'sexp_extensions/block'
7
+ require_relative 'sexp_extensions/constant'
8
+ require_relative 'sexp_extensions/if'
9
+ require_relative 'sexp_extensions/methods'
10
+ require_relative 'sexp_extensions/module'
11
+ require_relative 'sexp_extensions/nested_assignables'
12
+ require_relative 'sexp_extensions/self'
13
+ require_relative 'sexp_extensions/send'
14
+ require_relative 'sexp_extensions/symbols'
15
+ require_relative 'sexp_extensions/variables'
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ module ArgNodeBase
7
+ def name
8
+ children.first
9
+ end
10
+
11
+ def marked_unused?
12
+ plain_name.start_with?('_')
13
+ end
14
+
15
+ def plain_name
16
+ name.to_s
17
+ end
18
+
19
+ def block?
20
+ false
21
+ end
22
+
23
+ def optional_argument?
24
+ false
25
+ end
26
+
27
+ def anonymous_splat?
28
+ false
29
+ end
30
+
31
+ def components
32
+ [self]
33
+ end
34
+ end
35
+
36
+ module ArgNode
37
+ include ArgNodeBase
38
+ end
39
+
40
+ module KwargNode
41
+ include ArgNodeBase
42
+ end
43
+
44
+ module OptargNode
45
+ include ArgNodeBase
46
+
47
+ def optional_argument?
48
+ true
49
+ end
50
+ end
51
+
52
+ module KwoptargNode
53
+ include ArgNodeBase
54
+
55
+ def optional_argument?
56
+ true
57
+ end
58
+ end
59
+
60
+ module BlockargNode
61
+ include ArgNodeBase
62
+
63
+ def block?
64
+ true
65
+ end
66
+ end
67
+
68
+ module RestargNode
69
+ include ArgNodeBase
70
+
71
+ def anonymous_splat?
72
+ !name
73
+ end
74
+ end
75
+
76
+ module KwrestargNode
77
+ include ArgNodeBase
78
+
79
+ def anonymous_splat?
80
+ !name
81
+ end
82
+ end
83
+
84
+ module ShadowargNode
85
+ include ArgNodeBase
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ # Utility methods for :block nodes.
7
+ module BlockNode
8
+ def call
9
+ children.first
10
+ end
11
+
12
+ def args
13
+ children[1]
14
+ end
15
+
16
+ def block
17
+ children[2]
18
+ end
19
+
20
+ def parameters
21
+ children[1] || []
22
+ end
23
+
24
+ def parameter_names
25
+ parameters.children
26
+ end
27
+
28
+ def simple_name
29
+ :block
30
+ end
31
+
32
+ def without_block_arguments?
33
+ args.components.empty?
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end