foodcritic 16.1.1 → 16.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/chef_dsl_metadata/{chef_14.12.9.json → chef_14.14.29.json} +264 -0
- data/chef_dsl_metadata/{chef_15.1.36.json → chef_15.4.45.json} +13 -0
- data/lib/foodcritic/api.rb +42 -35
- data/lib/foodcritic/chef.rb +5 -3
- data/lib/foodcritic/command_line.rb +54 -53
- data/lib/foodcritic/domain.rb +5 -5
- data/lib/foodcritic/dsl.rb +4 -1
- data/lib/foodcritic/linter.rb +23 -8
- data/lib/foodcritic/notifications.rb +3 -1
- data/lib/foodcritic/output.rb +1 -1
- data/lib/foodcritic/rules/fc001.rb +6 -6
- data/lib/foodcritic/rules/fc004.rb +1 -0
- data/lib/foodcritic/rules/fc006.rb +9 -9
- data/lib/foodcritic/rules/fc007.rb +3 -1
- data/lib/foodcritic/rules/fc016.rb +1 -0
- data/lib/foodcritic/rules/fc019.rb +7 -6
- data/lib/foodcritic/rules/fc022.rb +24 -25
- data/lib/foodcritic/rules/fc024.rb +16 -13
- data/lib/foodcritic/rules/fc029.rb +1 -0
- data/lib/foodcritic/rules/fc031.rb +1 -1
- data/lib/foodcritic/rules/fc032.rb +2 -2
- data/lib/foodcritic/rules/fc033.rb +2 -2
- data/lib/foodcritic/rules/fc034.rb +5 -2
- data/lib/foodcritic/rules/fc039.rb +12 -12
- data/lib/foodcritic/rules/fc040.rb +1 -1
- data/lib/foodcritic/rules/fc044.rb +8 -12
- data/lib/foodcritic/rules/fc048.rb +1 -0
- data/lib/foodcritic/rules/fc123.rb +5 -4
- data/lib/foodcritic/template.rb +3 -6
- data/lib/foodcritic/version.rb +1 -1
- metadata +5 -5
@@ -88,6 +88,7 @@
|
|
88
88
|
"locale",
|
89
89
|
"log",
|
90
90
|
"mac_os_x_userdefaults",
|
91
|
+
"mac_user",
|
91
92
|
"macos_userdefaults",
|
92
93
|
"macosx_service",
|
93
94
|
"macports_package",
|
@@ -849,6 +850,7 @@
|
|
849
850
|
],
|
850
851
|
"kernel_module": [
|
851
852
|
"blacklist",
|
853
|
+
"disable",
|
852
854
|
"install",
|
853
855
|
"load",
|
854
856
|
"nothing",
|
@@ -1257,6 +1259,7 @@
|
|
1257
1259
|
],
|
1258
1260
|
"windows_ad_join": [
|
1259
1261
|
"join",
|
1262
|
+
"leave",
|
1260
1263
|
"nothing"
|
1261
1264
|
],
|
1262
1265
|
"windows_autorun": [
|
@@ -4989,6 +4992,8 @@
|
|
4989
4992
|
"itself",
|
4990
4993
|
"kind_of?",
|
4991
4994
|
"lazy",
|
4995
|
+
"list_options",
|
4996
|
+
"list_options=",
|
4992
4997
|
"load_from",
|
4993
4998
|
"logger",
|
4994
4999
|
"lookup_provider_constant",
|
@@ -5016,6 +5021,8 @@
|
|
5016
5021
|
"package_name=",
|
5017
5022
|
"params",
|
5018
5023
|
"params=",
|
5024
|
+
"password",
|
5025
|
+
"password=",
|
5019
5026
|
"platform?",
|
5020
5027
|
"platform_family?",
|
5021
5028
|
"powershell_exec",
|
@@ -5107,6 +5114,8 @@
|
|
5107
5114
|
"updated?",
|
5108
5115
|
"updated_by_last_action",
|
5109
5116
|
"updated_by_last_action?",
|
5117
|
+
"user",
|
5118
|
+
"user=",
|
5110
5119
|
"valid_windows_architecture?",
|
5111
5120
|
"validate",
|
5112
5121
|
"validate_action",
|
@@ -11138,6 +11147,8 @@
|
|
11138
11147
|
"object_id",
|
11139
11148
|
"older_than_win_2012_or_8?",
|
11140
11149
|
"only_if",
|
11150
|
+
"options",
|
11151
|
+
"options=",
|
11141
11152
|
"params",
|
11142
11153
|
"params=",
|
11143
11154
|
"platform?",
|
@@ -26071,6 +26082,8 @@
|
|
26071
26082
|
"value_to_text",
|
26072
26083
|
"with_os_architecture",
|
26073
26084
|
"with_run_context",
|
26085
|
+
"workgroup_name",
|
26086
|
+
"workgroup_name=",
|
26074
26087
|
"wow64_architecture_override_required?",
|
26075
26088
|
"wow64_directory",
|
26076
26089
|
"yield_self"
|
data/lib/foodcritic/api.rb
CHANGED
@@ -29,7 +29,7 @@ module FoodCritic
|
|
29
29
|
options = { type: :any, ignore_calls: false }.merge!(options)
|
30
30
|
return [] unless ast.respond_to?(:xpath)
|
31
31
|
|
32
|
-
unless
|
32
|
+
unless %i{any string symbol vivified}.include?(options[:type])
|
33
33
|
raise ArgumentError, "Node type not recognised"
|
34
34
|
end
|
35
35
|
|
@@ -56,9 +56,7 @@ module FoodCritic
|
|
56
56
|
|
57
57
|
# get list of items in the dir and intersect with metadata array.
|
58
58
|
# until we get an interfact (we have a metadata) walk up the dir structure
|
59
|
-
until (Dir.entries(file) & %w{metadata.rb metadata.json}).any?
|
60
|
-
file = File.dirname(file)
|
61
|
-
end
|
59
|
+
file = File.dirname(file) until (Dir.entries(file) & %w{metadata.rb metadata.json}).any?
|
62
60
|
|
63
61
|
file
|
64
62
|
end
|
@@ -69,9 +67,7 @@ module FoodCritic
|
|
69
67
|
# @since 7.0.0
|
70
68
|
# @return [String] the value of the metadata field
|
71
69
|
def metadata_field(file, field)
|
72
|
-
until (file.split(File::SEPARATOR) & standard_cookbook_subdirs).empty?
|
73
|
-
file = File.absolute_path(File.dirname(file.to_s))
|
74
|
-
end
|
70
|
+
file = File.absolute_path(File.dirname(file.to_s)) until (file.split(File::SEPARATOR) & standard_cookbook_subdirs).empty?
|
75
71
|
file = File.dirname(file) unless File.extname(file).empty?
|
76
72
|
|
77
73
|
md_path = File.join(file, "metadata.rb")
|
@@ -80,6 +76,7 @@ module FoodCritic
|
|
80
76
|
command[ident/@value='#{field}']/
|
81
77
|
descendant::tstring_content/@value").to_s
|
82
78
|
raise "Cant read #{field} from #{md_path}" if value.to_s.empty?
|
79
|
+
|
83
80
|
return value
|
84
81
|
else
|
85
82
|
raise "Cant find #{md_path}"
|
@@ -98,9 +95,7 @@ module FoodCritic
|
|
98
95
|
begin
|
99
96
|
metadata_field(file, "name")
|
100
97
|
rescue RuntimeError
|
101
|
-
until (file.split(File::SEPARATOR) & standard_cookbook_subdirs).empty?
|
102
|
-
file = File.absolute_path(File.dirname(file.to_s))
|
103
|
-
end
|
98
|
+
file = File.absolute_path(File.dirname(file.to_s)) until (file.split(File::SEPARATOR) & standard_cookbook_subdirs).empty?
|
104
99
|
file = File.dirname(file) unless File.extname(file).empty?
|
105
100
|
File.basename(file)
|
106
101
|
end
|
@@ -156,6 +151,7 @@ module FoodCritic
|
|
156
151
|
if field_name.nil? || field_name.to_s.empty?
|
157
152
|
raise ArgumentError, "Field name cannot be nil or empty"
|
158
153
|
end
|
154
|
+
|
159
155
|
ast.xpath("(.//command[ident/@value='#{field_name}']|.//fcall[ident/@value='#{field_name}']/..)")
|
160
156
|
end
|
161
157
|
|
@@ -163,13 +159,14 @@ module FoodCritic
|
|
163
159
|
def field_value(ast, field_name)
|
164
160
|
field(ast, field_name).xpath('.//args_add_block//tstring_content
|
165
161
|
[count(ancestor::args_add) = 1][count(ancestor::string_add) = 1]
|
166
|
-
/@value').map
|
162
|
+
/@value').map(&:to_s).last
|
167
163
|
end
|
168
164
|
|
169
165
|
# Create a match for a specified file. Use this if the presence of the file
|
170
166
|
# triggers the warning rather than content.
|
171
167
|
def file_match(file)
|
172
168
|
raise ArgumentError, "Filename cannot be nil" if file.nil?
|
169
|
+
|
173
170
|
{ filename: file, matched: file, line: 1, column: 1 }
|
174
171
|
end
|
175
172
|
|
@@ -189,10 +186,11 @@ module FoodCritic
|
|
189
186
|
def find_resources(ast, options = {})
|
190
187
|
options = { type: :any }.merge!(options)
|
191
188
|
return [] unless ast.respond_to?(:xpath)
|
189
|
+
|
192
190
|
scope_type = ""
|
193
191
|
unless options[:type] == :any
|
194
|
-
type_array =
|
195
|
-
scope_type = "[#{type_array.join(
|
192
|
+
type_array = Array(options[:type]).map { |x| "@value='#{x}'" }
|
193
|
+
scope_type = "[#{type_array.join(" or ")}]"
|
196
194
|
end
|
197
195
|
|
198
196
|
# TODO: Include nested resources (provider actions)
|
@@ -241,6 +239,7 @@ module FoodCritic
|
|
241
239
|
# Searches with a query formed from a subexpression will be ignored.
|
242
240
|
def literal_searches(ast)
|
243
241
|
return [] unless ast.respond_to?(:xpath)
|
242
|
+
|
244
243
|
ast.xpath("//method_add_arg[fcall/ident/@value = 'search' and
|
245
244
|
count(descendant::string_embexpr) = 0]/descendant::tstring_content")
|
246
245
|
end
|
@@ -250,6 +249,7 @@ module FoodCritic
|
|
250
249
|
raise_unless_xpath!(node)
|
251
250
|
pos = node.xpath("descendant::pos").first
|
252
251
|
return nil if pos.nil?
|
252
|
+
|
253
253
|
{ matched: node.respond_to?(:name) ? node.name : "",
|
254
254
|
line: pos["line"].to_i, column: pos["column"].to_i }
|
255
255
|
end
|
@@ -273,6 +273,7 @@ module FoodCritic
|
|
273
273
|
# Retrieve a single-valued attribute from the specified resource.
|
274
274
|
def resource_attribute(resource, name)
|
275
275
|
raise ArgumentError, "Attribute name cannot be empty" if name.empty?
|
276
|
+
|
276
277
|
resource_attributes(resource)[name.to_s]
|
277
278
|
end
|
278
279
|
|
@@ -306,7 +307,8 @@ module FoodCritic
|
|
306
307
|
if name.xpath("descendant::string_add").size == 1 &&
|
307
308
|
name.xpath("descendant::string_literal").size == 1 &&
|
308
309
|
name.xpath(
|
309
|
-
"descendant::*[self::call or self::string_embexpr]"
|
310
|
+
"descendant::*[self::call or self::string_embexpr]"
|
311
|
+
).empty?
|
310
312
|
name.xpath("descendant::tstring_content/@value").to_s
|
311
313
|
else
|
312
314
|
name
|
@@ -320,7 +322,7 @@ module FoodCritic
|
|
320
322
|
# Resources in an AST, keyed by type.
|
321
323
|
def resources_by_type(ast)
|
322
324
|
raise_unless_xpath!(ast)
|
323
|
-
result = Hash.new { |hash, key| hash[key] =
|
325
|
+
result = Hash.new { |hash, key| hash[key] = [] }
|
324
326
|
find_resources(ast).each do |resource|
|
325
327
|
result[resource_type(resource)] << resource
|
326
328
|
end
|
@@ -334,6 +336,7 @@ module FoodCritic
|
|
334
336
|
if type.empty?
|
335
337
|
raise ArgumentError, "Provided AST node is not a resource"
|
336
338
|
end
|
339
|
+
|
337
340
|
type
|
338
341
|
end
|
339
342
|
|
@@ -350,6 +353,7 @@ module FoodCritic
|
|
350
353
|
# Searches performed by the provided AST.
|
351
354
|
def searches(ast)
|
352
355
|
return [] unless ast.respond_to?(:xpath)
|
356
|
+
|
353
357
|
ast.xpath("//fcall/ident[@value = 'search']")
|
354
358
|
end
|
355
359
|
|
@@ -398,9 +402,10 @@ module FoodCritic
|
|
398
402
|
|
399
403
|
def templates_included(all_templates, template_path, depth = 1)
|
400
404
|
raise RecursedTooFarError.new(template_path) if depth > 10
|
405
|
+
|
401
406
|
partials = read_ast(template_path).xpath('//*[self::command or
|
402
407
|
child::fcall][descendant::ident/@value="render"]//args_add/
|
403
|
-
string_literal//tstring_content/@value').map
|
408
|
+
string_literal//tstring_content/@value').map(&:to_s)
|
404
409
|
Array(template_path) + partials.map do |included_partial|
|
405
410
|
partial_path = Array(all_templates).find do |path|
|
406
411
|
(Pathname.new(template_path).dirname + included_partial).to_s == path
|
@@ -416,10 +421,10 @@ module FoodCritic
|
|
416
421
|
def template_paths(recipe_path)
|
417
422
|
Dir.glob(Pathname.new(recipe_path).dirname.dirname + "templates" +
|
418
423
|
"**/*", File::FNM_DOTMATCH).select do |path|
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
424
|
+
File.file?(path)
|
425
|
+
end.reject do |path|
|
426
|
+
File.basename(path) == ".DS_Store" || File.extname(path) == ".swp"
|
427
|
+
end
|
423
428
|
end
|
424
429
|
|
425
430
|
# Give a filename path it returns the hash of the JSON contents
|
@@ -447,11 +452,11 @@ module FoodCritic
|
|
447
452
|
atts = {}
|
448
453
|
resource.xpath("do_block/descendant::method_add_block[
|
449
454
|
count(ancestor::do_block) = 1][brace_block | do_block]").each do |batt|
|
450
|
-
|
451
|
-
|
452
|
-
|
455
|
+
att_name = batt.xpath("string(method_add_arg/fcall/ident/@value)")
|
456
|
+
if att_name && !att_name.empty? && batt.children.length > 1
|
457
|
+
atts[att_name] = batt.children[1]
|
458
|
+
end
|
453
459
|
end
|
454
|
-
end
|
455
460
|
atts
|
456
461
|
end
|
457
462
|
|
@@ -531,14 +536,15 @@ module FoodCritic
|
|
531
536
|
atts = {}
|
532
537
|
resource.xpath('do_block/descendant::*[self::command or
|
533
538
|
self::method_add_arg][count(ancestor::do_block) >= 1]').each do |att|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
539
|
+
blocks = att.xpath("ancestor::method_add_block/method_add_arg/fcall")
|
540
|
+
next if blocks.any? { |a| block_depth(a) > block_depth(resource) }
|
541
|
+
|
542
|
+
att_name = att.xpath('string(ident/@value |
|
543
|
+
fcall/ident[@value="variables"]/@value)')
|
544
|
+
unless att_name.empty?
|
545
|
+
atts[att_name] = extract_attribute_value(att, options)
|
546
|
+
end
|
540
547
|
end
|
541
|
-
end
|
542
548
|
atts
|
543
549
|
end
|
544
550
|
|
@@ -579,6 +585,7 @@ module FoodCritic
|
|
579
585
|
class AttFilter
|
580
586
|
def is_att_type(value)
|
581
587
|
return [] unless value.respond_to?(:select)
|
588
|
+
|
582
589
|
value.select do |n|
|
583
590
|
%w{
|
584
591
|
automatic_attrs
|
@@ -605,7 +612,7 @@ module FoodCritic
|
|
605
612
|
|
606
613
|
def standard_attribute_access(ast, options)
|
607
614
|
if options[:type] == :any
|
608
|
-
|
615
|
+
%i{string symbol}.map do |type|
|
609
616
|
standard_attribute_access(ast, options.merge(type: type))
|
610
617
|
end.inject(:+)
|
611
618
|
else
|
@@ -650,13 +657,13 @@ module FoodCritic
|
|
650
657
|
call.xpath("aref/args_add_block").size == 0 &&
|
651
658
|
(call.xpath("descendant::ident").size > 1 &&
|
652
659
|
!node_method?(call.xpath("ident/@value").to_s.to_sym,
|
653
|
-
|
660
|
+
options[:cookbook_dir]))
|
654
661
|
end.sort
|
655
662
|
end
|
656
663
|
|
657
664
|
def word_list_values(ast, xpath = nil)
|
658
665
|
# Find the node for the field argument variable. (e.g. given `foo d`, find `d`)
|
659
|
-
var_ref = ast.xpath("#{xpath ? xpath +
|
666
|
+
var_ref = ast.xpath("#{xpath ? xpath + "/" : ""}descendant::var_ref/ident")
|
660
667
|
if var_ref.empty?
|
661
668
|
# The field is either a static value, or took no arguments, or something
|
662
669
|
# more complex than we care to evaluate.
|
@@ -665,7 +672,7 @@ module FoodCritic
|
|
665
672
|
# Look back out the tree for a method_add_block which contains a block
|
666
673
|
# variable matching the field argument variable, and then drill down to
|
667
674
|
# all the literal content nodes.
|
668
|
-
ast.xpath(%Q{ancestor::method_add_block[//block_var//ident/@value='#{var_ref.first[
|
675
|
+
ast.xpath(%Q{ancestor::method_add_block[//block_var//ident/@value='#{var_ref.first["value"]}']/call//tstring_content})
|
669
676
|
end
|
670
677
|
end
|
671
678
|
end
|
data/lib/foodcritic/chef.rb
CHANGED
@@ -56,12 +56,12 @@ module FoodCritic
|
|
56
56
|
metadata_path(ver)
|
57
57
|
end.find { |m| File.exist?(m) }
|
58
58
|
@dsl_metadata ||= FFI_Yajl::Parser.parse(IO.read(metadata_path),
|
59
|
-
|
59
|
+
symbolize_keys: true)
|
60
60
|
end
|
61
61
|
|
62
62
|
def metadata_path(chef_version)
|
63
63
|
File.join(File.dirname(__FILE__), "..", "..",
|
64
|
-
|
64
|
+
"chef_dsl_metadata/chef_#{chef_version}.json")
|
65
65
|
end
|
66
66
|
|
67
67
|
def resource_check?(key, resource_type, field)
|
@@ -94,9 +94,11 @@ module FoodCritic
|
|
94
94
|
grammar_paths.inject(nil) do |parser, lucene_grammar|
|
95
95
|
begin
|
96
96
|
break parser unless parser.nil?
|
97
|
+
|
97
98
|
# Don't instantiate custom nodes
|
98
99
|
Treetop.load_from_string(
|
99
|
-
IO.read(lucene_grammar).gsub(/<[^>]+>/, "")
|
100
|
+
IO.read(lucene_grammar).gsub(/<[^>]+>/, "")
|
101
|
+
)
|
100
102
|
LuceneParser.new
|
101
103
|
rescue
|
102
104
|
# Silently swallow and try the next grammar
|
@@ -29,74 +29,74 @@ module FoodCritic
|
|
29
29
|
@parser = OptionParser.new do |opts|
|
30
30
|
opts.banner = "foodcritic [cookbook_paths]"
|
31
31
|
opts.on("-t", "--tags TAGS",
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
"Check against (or exclude ~) rules with the "\
|
33
|
+
"specified tags.") do |t|
|
34
|
+
@options[:tags] << t
|
35
|
+
end
|
36
36
|
opts.on("-l", "--list",
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
"List all enabled rules and their descriptions.") do |t|
|
38
|
+
@options[:list] = t
|
39
|
+
end
|
40
40
|
opts.on("-f", "--epic-fail TAGS",
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
"Fail the build based on tags. Default of 'any' to fail "\
|
42
|
+
"on all warnings.") do |t|
|
43
|
+
@options[:fail_tags] << t
|
44
|
+
end
|
45
45
|
opts.on("-c", "--chef-version VERSION",
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
"Only check against rules valid for this version "\
|
47
|
+
"of Chef.") do |c|
|
48
|
+
@options[:chef_version] = c
|
49
|
+
end
|
50
50
|
opts.on("-r", "--rule-file PATH",
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
"Specify file with rules to be used or ignored ") do |c|
|
52
|
+
@options[:rule_file] = c
|
53
|
+
end
|
54
54
|
opts.on("-s", "--ast-cache-size NUM", Integer,
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
"Change the size of the AST cache.") do |s|
|
56
|
+
@options[:ast_cache_size] = s
|
57
|
+
end
|
58
58
|
opts.on("-B", "--cookbook-path PATH",
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
"Cookbook path(s) to check.") do |b|
|
60
|
+
@options[:cookbook_paths] << b
|
61
|
+
end
|
62
62
|
opts.on("-C", "--[no-]context",
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
"Show lines matched against rather than the "\
|
64
|
+
"default summary.") do |c|
|
65
|
+
@options[:context] = c
|
66
|
+
end
|
67
67
|
opts.on("-E", "--environment-path PATH",
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
"Environment path(s) to check.") do |e|
|
69
|
+
@options[:environment_paths] << e
|
70
|
+
end
|
71
71
|
opts.on("-I", "--include PATH",
|
72
|
-
|
73
|
-
|
74
|
-
|
72
|
+
"Additional rule file path(s) to load.") do |i|
|
73
|
+
@options[:include_rules] << i
|
74
|
+
end
|
75
75
|
opts.on("-G", "--search-gems",
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
76
|
+
"Search rubygems for rule files with the path "\
|
77
|
+
"foodcritic/rules/**/*.rb") do |g|
|
78
|
+
@options[:search_gems] = true
|
79
|
+
end
|
80
80
|
opts.on("-P", "--[no-]progress",
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
"Show progress of files being checked. default: true") do |q|
|
82
|
+
@options[:progress] = q
|
83
|
+
end
|
84
84
|
opts.on("-R", "--role-path PATH",
|
85
|
-
|
86
|
-
|
87
|
-
|
85
|
+
"Role path(s) to check.") do |r|
|
86
|
+
@options[:role_paths] << r
|
87
|
+
end
|
88
88
|
opts.on("-S", "--search-grammar PATH",
|
89
|
-
|
90
|
-
|
91
|
-
|
89
|
+
"Specify grammar to use when validating search syntax.") do |s|
|
90
|
+
@options[:search_grammar] = s
|
91
|
+
end
|
92
92
|
opts.on("-V", "--version",
|
93
|
-
|
94
|
-
|
95
|
-
|
93
|
+
"Display the foodcritic version.") do |v|
|
94
|
+
@options[:version] = true
|
95
|
+
end
|
96
96
|
opts.on("-X", "--exclude PATH",
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
"Exclude path(s) from being linted. PATH is relative to the cookbook, not an absolute PATH. Default test/**/*,spec/**/*,features/**/*") do |e|
|
98
|
+
options[:exclude_paths] << e
|
99
|
+
end
|
100
100
|
end
|
101
101
|
# -v is not implemented but OptionParser gives the Foodcritic's version
|
102
102
|
# if that flag is passed
|
@@ -162,6 +162,7 @@ module FoodCritic
|
|
162
162
|
def valid_grammar?
|
163
163
|
return true unless options.key?(:search_grammar)
|
164
164
|
return false unless File.exist?(options[:search_grammar])
|
165
|
+
|
165
166
|
search = FoodCritic::Chef::Search.new
|
166
167
|
search.create_parser([options[:search_grammar]])
|
167
168
|
search.parser?
|