reek 4.7.3 → 4.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -12
  3. data/.rubocop.yml +1 -0
  4. data/.travis.yml +4 -2
  5. data/CHANGELOG.md +10 -0
  6. data/Gemfile +3 -3
  7. data/README.md +19 -4
  8. data/lib/reek/ast/node.rb +37 -49
  9. data/lib/reek/ast/reference_collector.rb +2 -4
  10. data/lib/reek/ast/sexp_extensions/case.rb +1 -1
  11. data/lib/reek/ast/sexp_extensions/if.rb +8 -1
  12. data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
  13. data/lib/reek/ast/sexp_extensions/methods.rb +3 -5
  14. data/lib/reek/configuration/configuration_file_finder.rb +3 -3
  15. data/lib/reek/context/code_context.rb +4 -7
  16. data/lib/reek/context/method_context.rb +5 -10
  17. data/lib/reek/context/module_context.rb +3 -3
  18. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +9 -9
  19. data/lib/reek/errors/bad_detector_in_comment_error.rb +7 -7
  20. data/lib/reek/errors/base_error.rb +3 -0
  21. data/lib/reek/errors/encoding_error.rb +16 -11
  22. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +7 -7
  23. data/lib/reek/errors/incomprehensible_source_error.rb +20 -22
  24. data/lib/reek/examiner.rb +18 -14
  25. data/lib/reek/logging_error_handler.rb +7 -5
  26. data/lib/reek/smell_detectors/class_variable.rb +3 -10
  27. data/lib/reek/smell_detectors/duplicate_method_call.rb +1 -1
  28. data/lib/reek/smell_detectors/instance_variable_assumption.rb +1 -9
  29. data/lib/reek/smell_detectors/manual_dispatch.rb +1 -1
  30. data/lib/reek/smell_detectors/module_initialize.rb +1 -1
  31. data/lib/reek/smell_detectors/nested_iterators.rb +2 -1
  32. data/lib/reek/smell_detectors/too_many_constants.rb +1 -1
  33. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +2 -2
  34. data/lib/reek/smell_detectors/utility_function.rb +1 -1
  35. data/lib/reek/source/source_code.rb +9 -23
  36. data/lib/reek/version.rb +1 -1
  37. data/reek.gemspec +2 -2
  38. data/spec/factories/factories.rb +2 -13
  39. data/spec/reek/ast/node_spec.rb +98 -5
  40. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  41. data/spec/reek/ast/sexp_extensions_spec.rb +2 -2
  42. data/spec/reek/cli/application_spec.rb +39 -41
  43. data/spec/reek/cli/command/todo_list_command_spec.rb +2 -2
  44. data/spec/reek/code_comment_spec.rb +32 -32
  45. data/spec/reek/configuration/app_configuration_spec.rb +3 -3
  46. data/spec/reek/configuration/configuration_file_finder_spec.rb +1 -1
  47. data/spec/reek/configuration/directory_directives_spec.rb +3 -3
  48. data/spec/reek/configuration/excluded_paths_spec.rb +1 -1
  49. data/spec/reek/context/code_context_spec.rb +59 -95
  50. data/spec/reek/context/ghost_context_spec.rb +1 -1
  51. data/spec/reek/context/root_context_spec.rb +1 -1
  52. data/spec/reek/errors/base_error_spec.rb +13 -0
  53. data/spec/reek/examiner_spec.rb +72 -29
  54. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -80
  55. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  56. data/spec/reek/report/xml_report_spec.rb +2 -2
  57. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
  58. data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
  59. data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
  60. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
  61. data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
  62. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  63. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +3 -3
  64. data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
  65. data/spec/reek/smell_detectors/unused_private_method_spec.rb +9 -9
  66. data/spec/reek/smell_detectors/utility_function_spec.rb +5 -5
  67. data/spec/reek/smell_warning_spec.rb +8 -8
  68. data/spec/reek/source/source_code_spec.rb +5 -25
  69. data/spec/reek/source/source_locator_spec.rb +6 -6
  70. data/spec/reek/spec/should_reek_of_spec.rb +7 -7
  71. data/spec/reek/spec/should_reek_spec.rb +2 -2
  72. data/spec/reek/spec/smell_matcher_spec.rb +3 -3
  73. data/spec/reek/tree_dresser_spec.rb +11 -12
  74. data/spec/spec_helper.rb +3 -12
  75. metadata +10 -9
@@ -9,12 +9,12 @@ module Reek
9
9
  class BadDetectorConfigurationKeyInCommentError < BaseError
10
10
  UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE.freeze
11
11
 
12
- Error: You are trying to configure the smell detector '%s'
13
- in one of your source code comments with the unknown option %s.
14
- The source is '%s' and the comment belongs to the expression starting in line %d.
12
+ Error: You are trying to configure the smell detector '%<detector>s'
13
+ in one of your source code comments with the unknown option %<option>s.
14
+ The source is '%<source>s' and the comment belongs to the expression starting in line %<line>d.
15
15
  Here's the original comment:
16
16
 
17
- %s
17
+ %<comment>s
18
18
 
19
19
  Please see the Reek docs for:
20
20
  * how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
@@ -26,11 +26,11 @@ module Reek
26
26
 
27
27
  def initialize(detector_name:, offensive_keys:, source:, line:, original_comment:)
28
28
  message = format(UNKNOWN_SMELL_DETECTOR_MESSAGE,
29
- detector_name,
30
- offensive_keys,
31
- source,
32
- line,
33
- original_comment)
29
+ detector: detector_name,
30
+ option: offensive_keys,
31
+ source: source,
32
+ line: line,
33
+ comment: original_comment)
34
34
  super message
35
35
  end
36
36
  end
@@ -10,12 +10,12 @@ module Reek
10
10
  class BadDetectorInCommentError < BaseError
11
11
  UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE.freeze
12
12
 
13
- Error: You are trying to configure an unknown smell detector '%s' in one
13
+ Error: You are trying to configure an unknown smell detector '%<detector>s' in one
14
14
  of your source code comments.
15
- The source is '%s' and the comment belongs to the expression starting in line %d.
15
+ The source is '%<source>s' and the comment belongs to the expression starting in line %<line>d.
16
16
  Here's the original comment:
17
17
 
18
- %s
18
+ %<comment>s
19
19
 
20
20
  Please see the Reek docs for:
21
21
  * how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
@@ -26,10 +26,10 @@ module Reek
26
26
 
27
27
  def initialize(detector_name:, source:, line:, original_comment:)
28
28
  message = format(UNKNOWN_SMELL_DETECTOR_MESSAGE,
29
- detector_name,
30
- source,
31
- line,
32
- original_comment)
29
+ detector: detector_name,
30
+ source: source,
31
+ line: line,
32
+ comment: original_comment)
33
33
  super message
34
34
  end
35
35
  end
@@ -4,6 +4,9 @@ module Reek
4
4
  module Errors
5
5
  # Base class for all runtime Reek errors
6
6
  class BaseError < ::RuntimeError
7
+ def long_message
8
+ message
9
+ end
7
10
  end
8
11
  end
9
12
  end
@@ -6,9 +6,11 @@ module Reek
6
6
  module Errors
7
7
  # Gets raised when Reek is unable to process the source due to an EncodingError
8
8
  class EncodingError < BaseError
9
- ENCODING_ERROR_TEMPLATE = <<-MESSAGE.freeze
9
+ TEMPLATE = "Source '%<source>s' cannot be processed by Reek due to an encoding error in the source file.".freeze
10
+
11
+ LONG_TEMPLATE = <<-MESSAGE.freeze
10
12
  !!!
11
- Source '%s' cannot be processed by Reek due to an encoding error in the source file.
13
+ %<message>s
12
14
 
13
15
  This is a problem that is outside of Reek's scope and should be fixed by you, the
14
16
  user, in order for Reek being able to continue.
@@ -17,21 +19,24 @@ module Reek
17
19
 
18
20
  Exception message:
19
21
 
20
- %s
22
+ %<exception>s
21
23
 
22
- Original exception:
24
+ Original backtrace:
23
25
 
24
- %s
26
+ %<original>s
25
27
 
26
28
  !!!
27
29
  MESSAGE
28
30
 
29
- def initialize(origin:, original_exception:)
30
- message = format(ENCODING_ERROR_TEMPLATE,
31
- origin,
32
- original_exception.message,
33
- original_exception.backtrace.join("\n\t"))
34
- super message
31
+ def initialize(origin:)
32
+ super format(TEMPLATE, source: origin)
33
+ end
34
+
35
+ def long_message
36
+ format(LONG_TEMPLATE,
37
+ message: message,
38
+ exception: cause.inspect,
39
+ original: cause.backtrace.join("\n\t"))
35
40
  end
36
41
  end
37
42
  end
@@ -9,12 +9,12 @@ module Reek
9
9
  class GarbageDetectorConfigurationInCommentError < BaseError
10
10
  BAD_DETECTOR_CONFIGURATION_MESSAGE = <<-MESSAGE.freeze
11
11
 
12
- Error: You are trying to configure the smell detector '%s'.
12
+ Error: You are trying to configure the smell detector '%<detector>s'.
13
13
  Unfortunately we cannot parse the configuration you have given.
14
- The source is '%s' and the comment belongs to the expression starting in line %d.
14
+ The source is '%<source>s' and the comment belongs to the expression starting in line %<line>d.
15
15
  Here's the original comment:
16
16
 
17
- %s
17
+ %<comment>s
18
18
 
19
19
  Please see the Reek docs for:
20
20
  * how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
@@ -25,10 +25,10 @@ module Reek
25
25
 
26
26
  def initialize(detector_name:, source:, line:, original_comment:)
27
27
  message = format(BAD_DETECTOR_CONFIGURATION_MESSAGE,
28
- detector_name,
29
- source,
30
- line,
31
- original_comment)
28
+ detector: detector_name,
29
+ source: source,
30
+ line: line,
31
+ comment: original_comment)
32
32
  super message
33
33
  end
34
34
  end
@@ -6,42 +6,40 @@ module Reek
6
6
  module Errors
7
7
  # Gets raised when Reek is unable to process the source
8
8
  class IncomprehensibleSourceError < BaseError
9
- INCOMPREHENSIBLE_SOURCE_TEMPLATE = <<-MESSAGE.freeze
10
- !!!
11
- Source %s cannot be processed by Reek.
9
+ TEMPLATE = 'Source %<source>s cannot be processed by Reek.'.freeze
12
10
 
13
- This is most likely either a problem in your Reek configuration (config file or
14
- source code comments) or a Reek bug.
11
+ LONG_TEMPLATE = <<-MESSAGE.freeze
12
+ !!!
13
+ %<message>s
15
14
 
16
- Please double check your Reek configuration taking the original exception
17
- below into account - you might have misspelled a smell detector for instance.
18
- (In the future Reek will handle configuration errors more gracefully, something
19
- we are working on already).
15
+ This is most likely a Reek bug.
20
16
 
21
- If you feel that this is not a problem with your Reek configuration but with
22
- Reek itself it would be great if you could report this back to the Reek
23
- team by opening up a corresponding issue at https://github.com/troessner/reek/issues.
17
+ It would be great if you could report this back to the Reek team by opening a
18
+ corresponding issue at https://github.com/troessner/reek/issues.
24
19
 
25
- Please make sure to include the source in question, the Reek version
26
- and the original exception below.
20
+ Please make sure to include the source in question, the Reek version and the
21
+ original exception below.
27
22
 
28
23
  Exception message:
29
24
 
30
- %s
25
+ %<exception>s
31
26
 
32
27
  Original exception:
33
28
 
34
- %s
29
+ %<original>s
35
30
 
36
31
  !!!
37
32
  MESSAGE
38
33
 
39
- def initialize(origin:, original_exception:)
40
- message = format(INCOMPREHENSIBLE_SOURCE_TEMPLATE,
41
- origin,
42
- original_exception.message,
43
- original_exception.backtrace.join("\n\t"))
44
- super message
34
+ def initialize(origin:)
35
+ super format(TEMPLATE, source: origin)
36
+ end
37
+
38
+ def long_message
39
+ format(LONG_TEMPLATE,
40
+ message: message,
41
+ exception: cause.inspect,
42
+ original: cause.backtrace.join("\n\t"))
45
43
  end
46
44
  end
47
45
  end
@@ -3,6 +3,7 @@
3
3
  require_relative 'context_builder'
4
4
  require_relative 'detector_repository'
5
5
  require_relative 'errors/incomprehensible_source_error'
6
+ require_relative 'errors/encoding_error'
6
7
  require_relative 'source/source_code'
7
8
 
8
9
  module Reek
@@ -95,26 +96,24 @@ module Reek
95
96
  #
96
97
  # @return [Array<SmellWarning>] the smells found in the source
97
98
  #
98
- # :reek:TooManyStatements { max_statements: 6 }
99
99
  def run
100
- if source.valid_syntax? && syntax_tree
101
- examine_tree
102
- else
103
- SmellDetectors::Syntax.smells_from_source(source)
100
+ wrap_exceptions do
101
+ examine_tree || report_syntax_errors
104
102
  end
105
103
  rescue StandardError => exception
106
- wrapper = wrap_exception exception
107
- raise wrapper unless @error_handler.handle wrapper
104
+ raise unless @error_handler.handle exception
108
105
  []
109
106
  end
110
107
 
111
- def wrap_exception(exception)
112
- case exception
113
- when Errors::BaseError
114
- exception
115
- else
116
- Errors::IncomprehensibleSourceError.new(origin: origin, original_exception: exception)
117
- end
108
+ # :reek:TooManyStatements { max_statements: 6 }
109
+ def wrap_exceptions
110
+ yield
111
+ rescue Errors::BaseError
112
+ raise
113
+ rescue EncodingError
114
+ raise Errors::EncodingError, origin: origin
115
+ rescue StandardError
116
+ raise Errors::IncomprehensibleSourceError, origin: origin
118
117
  end
119
118
 
120
119
  def syntax_tree
@@ -122,9 +121,14 @@ module Reek
122
121
  end
123
122
 
124
123
  def examine_tree
124
+ return unless syntax_tree
125
125
  ContextBuilder.new(syntax_tree).context_tree.flat_map do |element|
126
126
  detector_repository.examine(element)
127
127
  end
128
128
  end
129
+
130
+ def report_syntax_errors
131
+ SmellDetectors::Syntax.smells_from_source(source)
132
+ end
129
133
  end
130
134
  end
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'errors/bad_detector_configuration_key_in_comment_error'
4
- require_relative 'errors/bad_detector_in_comment_error'
5
- require_relative 'errors/garbage_detector_configuration_in_comment_error'
6
- require_relative 'errors/incomprehensible_source_error'
3
+ require_relative 'errors/base_error'
7
4
 
8
5
  module Reek
9
6
  # Handles errors by logging to stderr
10
7
  class LoggingErrorHandler
11
8
  def handle(exception)
12
- warn exception
9
+ case exception
10
+ when Errors::BaseError
11
+ warn exception.long_message
12
+ else
13
+ warn exception
14
+ end
13
15
  true
14
16
  end
15
17
  end
@@ -25,7 +25,8 @@ module Reek
25
25
  # @return [Array<SmellWarning>]
26
26
  #
27
27
  def sniff
28
- class_variables_in_context.map do |variable, lines|
28
+ class_variables_in_context.map do |variable, occurences|
29
+ lines = occurences.map(&:line)
29
30
  smell_warning(
30
31
  context: context,
31
32
  lines: lines,
@@ -38,16 +39,8 @@ module Reek
38
39
  # Collects the names of the class variables declared and/or used
39
40
  # in the given module.
40
41
  #
41
- # :reek:TooManyStatements: { max_statements: 7 }
42
42
  def class_variables_in_context
43
- result = Hash.new { |hash, key| hash[key] = [] }
44
- collector = proc do |cvar_node|
45
- result[cvar_node.name].push(cvar_node.line)
46
- end
47
- [:cvar, :cvasgn, :cvdecl].each do |stmt_type|
48
- expression.each_node(stmt_type, [:class, :module], &collector)
49
- end
50
- result
43
+ context.local_nodes([:cvar, :cvasgn, :cvdecl]).group_by(&:name)
51
44
  end
52
45
  end
53
46
  end
@@ -121,7 +121,7 @@ module Reek
121
121
  # :reek:TooManyStatements: { max_statements: 6 }
122
122
  # :reek:DuplicateMethodCall: { max_calls: 2 }
123
123
  def collect_calls(result)
124
- context.each_node(:send, [:mlhs]) do |call_node|
124
+ context.local_nodes(:send, [:mlhs]) do |call_node|
125
125
  next if call_node.object_creation_call?
126
126
  next if simple_method_call? call_node
127
127
  result[call_node].record(call_node)
@@ -55,17 +55,9 @@ module Reek
55
55
 
56
56
  def variables_from_context
57
57
  method_expressions.map do |method|
58
- method.find_nodes(assumption_nodes, ignored_nodes).map(&:name)
58
+ method.each_node(:ivar, [:or_asgn]).map(&:name)
59
59
  end.flatten
60
60
  end
61
-
62
- def assumption_nodes
63
- [:ivar]
64
- end
65
-
66
- def ignored_nodes
67
- [:or_asgn]
68
- end
69
61
  end
70
62
  end
71
63
  end
@@ -20,7 +20,7 @@ module Reek
20
20
  # @return [Array<SmellWarning>]
21
21
  #
22
22
  def sniff
23
- smelly_nodes = context.each_node(:send).select { |node| node.name == :respond_to? }
23
+ smelly_nodes = context.local_nodes(:send).select { |node| node.name == :respond_to? }
24
24
  return [] if smelly_nodes.empty?
25
25
  lines = smelly_nodes.map(&:line)
26
26
  [smell_warning(context: context, lines: lines, message: MESSAGE)]
@@ -21,7 +21,7 @@ module Reek
21
21
  # @return [Array<SmellWarning>]
22
22
  #
23
23
  def sniff
24
- context.local_nodes(:def) do |node|
24
+ context.defined_instance_methods.each do |node|
25
25
  if node.name == :initialize
26
26
  return smell_warning(
27
27
  context: context,
@@ -97,7 +97,8 @@ module Reek
97
97
  # :reek:TooManyStatements: { max_statements: 6 }
98
98
  def scout(exp:, depth:)
99
99
  return [] unless exp
100
- exp.find_nodes([:block]).flat_map do |iterator|
100
+ # Find all non-nested blocks in this expression
101
+ exp.each_node([:block], [:block]).flat_map do |iterator|
101
102
  new_depth = increment_depth(iterator, depth)
102
103
  # 1st case: we recurse down the given block of the iterator. In this case
103
104
  # we need to check if we should increment the depth.
@@ -35,7 +35,7 @@ module Reek
35
35
  # @return [Array<SmellWarning>]
36
36
  #
37
37
  def sniff
38
- count = context.each_node(:casgn, IGNORED_NODES).delete_if(&:defines_module?).length
38
+ count = context.local_nodes(:casgn).reject(&:defines_module?).length
39
39
 
40
40
  return [] if count <= max_allowed_constants
41
41
 
@@ -92,11 +92,11 @@ module Reek
92
92
  end
93
93
 
94
94
  def find_assignment_variable_names(accumulator)
95
- assignment_nodes = expression.each_node(:lvasgn, [:class, :module, :defs, :def])
95
+ assignment_nodes = expression.each_node(:lvasgn, [:class, :module, :defs, :def]).to_a
96
96
 
97
97
  case expression.type
98
98
  when :class, :module
99
- assignment_nodes += expression.each_node(:ivasgn, [:class, :module])
99
+ assignment_nodes += expression.each_node(:ivasgn, [:class, :module]).to_a
100
100
  end
101
101
 
102
102
  assignment_nodes.each { |asgn| accumulator[asgn.children.first].push(asgn.line) }
@@ -71,7 +71,7 @@ module Reek
71
71
  private
72
72
 
73
73
  def num_helper_methods
74
- context.local_nodes(:send).length
74
+ context.local_nodes(:send).to_a.length
75
75
  end
76
76
 
77
77
  def ignore_method?
@@ -2,12 +2,11 @@
2
2
 
3
3
  require_relative '../cli/silencer'
4
4
  Reek::CLI::Silencer.silently do
5
- require 'parser/ruby24'
5
+ require 'parser/ruby25'
6
6
  end
7
7
  require_relative '../tree_dresser'
8
8
  require_relative '../ast/node'
9
9
  require_relative '../ast/builder'
10
- require_relative '../errors/encoding_error'
11
10
 
12
11
  # Opt in to new way of representing lambdas
13
12
  Reek::AST::Builder.emit_lambda = true
@@ -54,11 +53,6 @@ module Reek
54
53
  end
55
54
  end
56
55
 
57
- # @return [true|false] Returns true if parsed file does not have any syntax errors.
58
- def valid_syntax?
59
- diagnostics.none? { |diagnostic| [:error, :fatal].include?(diagnostic.level) }
60
- end
61
-
62
56
  def diagnostics
63
57
  parse_if_needed
64
58
  @diagnostics
@@ -105,34 +99,26 @@ module Reek
105
99
  # where each node is possibly adorned with our SexpExtensions (see ast/ast_node_class_map
106
100
  # and ast/sexp_extensions for details).
107
101
  #
108
- # @param parser [Parser::Ruby24]
102
+ # @param parser [Parser::Ruby25]
109
103
  # @param source [String] - Ruby code
110
104
  # @return [Anonymous subclass of Reek::AST::Node] the AST presentation
111
105
  # for the given source
112
- # :reek:TooManyStatements { max_statements: 8 }
106
+ # :reek:TooManyStatements { max_statements: 6 }
113
107
  def parse(parser, source)
114
- begin
115
- buffer = Parser::Source::Buffer.new(origin, 1)
116
- source.force_encoding(Encoding::UTF_8)
117
- buffer.source = source
118
- rescue EncodingError => exception
119
- raise Errors::EncodingError, origin: origin, original_exception: exception
120
- end
121
- begin
122
- ast, comments = parser.parse_with_comments(buffer)
123
- rescue Parser::SyntaxError # rubocop:disable Lint/HandleExceptions
124
- # All errors are in diagnostics. No need to handle exception.
125
- end
108
+ buffer = Parser::Source::Buffer.new(origin, 1)
109
+ source.force_encoding(Encoding::UTF_8)
110
+ buffer.source = source
111
+ ast, comments = parser.parse_with_comments(buffer)
126
112
 
127
113
  # See https://whitequark.github.io/parser/Parser/Source/Comment/Associator.html
128
- comment_map = Parser::Source::Comment.associate(ast, comments) if ast
114
+ comment_map = Parser::Source::Comment.associate(ast, comments)
129
115
  TreeDresser.new.dress(ast, comment_map)
130
116
  end
131
117
 
132
118
  # :reek:TooManyStatements: { max_statements: 6 }
133
119
  # :reek:FeatureEnvy
134
120
  def default_parser
135
- Parser::Ruby24.new(AST::Builder.new).tap do |parser|
121
+ Parser::Ruby25.new(AST::Builder.new).tap do |parser|
136
122
  diagnostics = parser.diagnostics
137
123
  diagnostics.all_errors_are_fatal = false
138
124
  diagnostics.ignore_warnings = false