Skip to content

Commit ef48014

Browse files
authored
Merge pull request #737 from cbowman0/issue736
Fix line numbers being off with multi-line strings containing variables
2 parents a32f983 + 88f7095 commit ef48014

File tree

2 files changed

+195
-4
lines changed

2 files changed

+195
-4
lines changed

lib/puppet-lint/lexer.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def initialize(line_no, column, reason = nil)
3232

3333
# Internal: The puppet-lint lexer. Converts your manifest into its tokenised
3434
# form.
35-
class Lexer
35+
class Lexer # rubocop:disable Metrics/ClassLength
3636
def initialize
3737
@line_no = 1
3838
@column = 1
@@ -428,9 +428,9 @@ def interpolate_string(string, line, column)
428428
tokens << new_token(:STRING, value, :line => line, :column => column)
429429
first = false
430430
else
431-
line += value.scan(%r{(\r\n|\r|\n)}).size
432431
token_column = column + (ss.pos - value.size)
433432
tokens << new_token(:DQPOST, value, :line => line, :column => token_column)
433+
line += value.scan(%r{(\r\n|\r|\n)}).size
434434
@column = column + ss.pos + 1
435435
@line_no = line
436436
end
@@ -439,9 +439,9 @@ def interpolate_string(string, line, column)
439439
tokens << new_token(:DQPRE, value, :line => line, :column => column)
440440
first = false
441441
else
442-
line += value.scan(%r{(\r\n|\r|\n)}).size
443442
token_column = column + (ss.pos - value.size)
444443
tokens << new_token(:DQMID, value, :line => line, :column => token_column)
444+
line += value.scan(%r{(\r\n|\r|\n)}).size
445445
end
446446
if ss.scan(%r{\{}).nil?
447447
var_name = ss.scan(%r{(::)?(\w+(-\w+)*::)*\w+(-\w+)*})
@@ -453,6 +453,7 @@ def interpolate_string(string, line, column)
453453
tokens << new_token(:UNENC_VARIABLE, var_name, :line => line, :column => token_column)
454454
end
455455
else
456+
line += value.scan(%r{(\r\n|\r|\n)}).size
456457
contents = ss.scan_until(%r{\}})[0..-2]
457458
raw = contents.dup
458459
if contents.match(%r{\A(::)?([\w-]+::)*[\w-]+(\[.+?\])*}) && !contents.match(%r{\A\w+\(})

spec/puppet-lint/lexer_spec.rb

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
require 'spec_helper'
44

5-
describe PuppetLint::Lexer do
5+
describe PuppetLint::Lexer do # rubocop:disable Metrics/BlockLength
66
before do
77
@lexer = PuppetLint::Lexer.new
88
end
@@ -139,6 +139,196 @@
139139
expect(tokens[2].column).to eq(8)
140140
end
141141

142+
it 'should handle a string with newline characters' do
143+
# rubocop:disable Layout/TrailingWhitespace
144+
manifest = <<END
145+
exec {
146+
'do-something':
147+
command => "echo > /home/bar/.token;
148+
kdestroy;
149+
kinit ${pseudouser}@EXAMPLE.COM -kt ${keytab_path};
150+
test $(klist | egrep '^Default principal:' | sed 's/Default principal:\s//') = '${pseudouser}'@EXAMPLE.COM",
151+
refreshonly => true;
152+
}
153+
END
154+
# rubocop:enable Layout/TrailingWhitespace
155+
tokens = @lexer.tokenise(manifest)
156+
157+
expect(tokens.length).to eq(36)
158+
159+
expect(tokens[0].type).to eq(:WHITESPACE)
160+
expect(tokens[0].value).to eq(' ')
161+
expect(tokens[0].line).to eq(1)
162+
expect(tokens[0].column).to eq(1)
163+
expect(tokens[1].type).to eq(:NAME)
164+
expect(tokens[1].value).to eq('exec')
165+
expect(tokens[1].line).to eq(1)
166+
expect(tokens[1].column).to eq(3)
167+
expect(tokens[2].type).to eq(:WHITESPACE)
168+
expect(tokens[2].value).to eq(' ')
169+
expect(tokens[2].line).to eq(1)
170+
expect(tokens[2].column).to eq(7)
171+
expect(tokens[3].type).to eq(:LBRACE)
172+
expect(tokens[3].value).to eq('{')
173+
expect(tokens[3].line).to eq(1)
174+
expect(tokens[3].column).to eq(8)
175+
expect(tokens[4].type).to eq(:NEWLINE)
176+
expect(tokens[4].value).to eq("\n")
177+
expect(tokens[4].line).to eq(1)
178+
expect(tokens[4].column).to eq(9)
179+
expect(tokens[5].type).to eq(:INDENT)
180+
expect(tokens[5].value).to eq(' ')
181+
expect(tokens[5].line).to eq(2)
182+
expect(tokens[5].column).to eq(1)
183+
expect(tokens[6].type).to eq(:SSTRING)
184+
expect(tokens[6].value).to eq('do-something')
185+
expect(tokens[6].line).to eq(2)
186+
expect(tokens[6].column).to eq(5)
187+
expect(tokens[7].type).to eq(:COLON)
188+
expect(tokens[7].value).to eq(':')
189+
expect(tokens[7].line).to eq(2)
190+
expect(tokens[7].column).to eq(19)
191+
expect(tokens[8].type).to eq(:NEWLINE)
192+
expect(tokens[8].value).to eq("\n")
193+
expect(tokens[8].line).to eq(2)
194+
expect(tokens[8].column).to eq(20)
195+
expect(tokens[9].type).to eq(:INDENT)
196+
expect(tokens[9].value).to eq(' ')
197+
expect(tokens[9].line).to eq(3)
198+
expect(tokens[9].column).to eq(1)
199+
expect(tokens[10].type).to eq(:NAME)
200+
expect(tokens[10].value).to eq('command')
201+
expect(tokens[10].line).to eq(3)
202+
expect(tokens[10].column).to eq(7)
203+
expect(tokens[11].type).to eq(:WHITESPACE)
204+
expect(tokens[11].value).to eq(' ')
205+
expect(tokens[11].line).to eq(3)
206+
expect(tokens[11].column).to eq(14)
207+
expect(tokens[12].type).to eq(:FARROW)
208+
expect(tokens[12].value).to eq('=>')
209+
expect(tokens[12].line).to eq(3)
210+
expect(tokens[12].column).to eq(19)
211+
expect(tokens[13].type).to eq(:WHITESPACE)
212+
expect(tokens[13].value).to eq(' ')
213+
expect(tokens[13].line).to eq(3)
214+
expect(tokens[13].column).to eq(21)
215+
expect(tokens[14].type).to eq(:DQPRE)
216+
expect(tokens[14].value).to eq("echo > /home/bar/.token; \n kdestroy; \n kinit ")
217+
expect(tokens[14].line).to eq(3)
218+
expect(tokens[14].column).to eq(22)
219+
expect(tokens[15].type).to eq(:VARIABLE)
220+
expect(tokens[15].value).to eq('pseudouser')
221+
expect(tokens[15].line).to eq(5)
222+
expect(tokens[15].column).to eq(131)
223+
expect(tokens[16].type).to eq(:DQMID)
224+
expect(tokens[16].value).to eq('@EXAMPLE.COM -kt ')
225+
expect(tokens[16].line).to eq(5)
226+
expect(tokens[16].column).to eq(143)
227+
expect(tokens[17].type).to eq(:VARIABLE)
228+
expect(tokens[17].value).to eq('keytab_path')
229+
expect(tokens[17].line).to eq(5)
230+
expect(tokens[17].column).to eq(161)
231+
expect(tokens[18].type).to eq(:DQMID)
232+
expect(tokens[18].value).to eq("; \n test ")
233+
expect(tokens[18].line).to eq(5)
234+
expect(tokens[18].column).to eq(174)
235+
expect(tokens[19].type).to eq(:DQMID)
236+
expect(tokens[19].value).to eq('$')
237+
expect(tokens[19].line).to eq(6)
238+
expect(tokens[19].column).to eq(213)
239+
expect(tokens[20].type).to eq(:DQMID)
240+
expect(tokens[20].value).to eq("(klist | egrep '^Default principal:' | sed 's/Default principal: //') = '")
241+
expect(tokens[20].line).to eq(6)
242+
expect(tokens[20].column).to eq(215)
243+
expect(tokens[21].type).to eq(:VARIABLE)
244+
expect(tokens[21].value).to eq('pseudouser')
245+
expect(tokens[21].line).to eq(6)
246+
expect(tokens[21].column).to eq(289)
247+
expect(tokens[22].type).to eq(:DQPOST)
248+
expect(tokens[22].value).to eq("'@EXAMPLE.COM")
249+
expect(tokens[22].line).to eq(6)
250+
expect(tokens[22].column).to eq(301)
251+
expect(tokens[23].type).to eq(:COMMA)
252+
expect(tokens[23].value).to eq(',')
253+
expect(tokens[23].line).to eq(6)
254+
expect(tokens[23].column).to eq(315)
255+
expect(tokens[24].type).to eq(:NEWLINE)
256+
expect(tokens[24].value).to eq("\n")
257+
expect(tokens[24].line).to eq(6)
258+
expect(tokens[24].column).to eq(316)
259+
expect(tokens[25].type).to eq(:INDENT)
260+
expect(tokens[25].value).to eq(' ')
261+
expect(tokens[25].line).to eq(7)
262+
expect(tokens[25].column).to eq(1)
263+
expect(tokens[26].type).to eq(:NAME)
264+
expect(tokens[26].value).to eq('refreshonly')
265+
expect(tokens[26].line).to eq(7)
266+
expect(tokens[26].column).to eq(7)
267+
expect(tokens[27].type).to eq(:WHITESPACE)
268+
expect(tokens[27].value).to eq(' ')
269+
expect(tokens[27].line).to eq(7)
270+
expect(tokens[27].column).to eq(18)
271+
expect(tokens[28].type).to eq(:FARROW)
272+
expect(tokens[28].value).to eq('=>')
273+
expect(tokens[28].line).to eq(7)
274+
expect(tokens[28].column).to eq(19)
275+
expect(tokens[29].type).to eq(:WHITESPACE)
276+
expect(tokens[29].value).to eq(' ')
277+
expect(tokens[29].line).to eq(7)
278+
expect(tokens[29].column).to eq(21)
279+
expect(tokens[30].type).to eq(:TRUE)
280+
expect(tokens[30].value).to eq('true')
281+
expect(tokens[30].line).to eq(7)
282+
expect(tokens[30].column).to eq(22)
283+
expect(tokens[31].type).to eq(:SEMIC)
284+
expect(tokens[31].value).to eq(';')
285+
expect(tokens[31].line).to eq(7)
286+
expect(tokens[31].column).to eq(26)
287+
expect(tokens[32].type).to eq(:NEWLINE)
288+
expect(tokens[32].value).to eq("\n")
289+
expect(tokens[32].line).to eq(7)
290+
expect(tokens[32].column).to eq(27)
291+
expect(tokens[33].type).to eq(:INDENT)
292+
expect(tokens[33].value).to eq(' ')
293+
expect(tokens[33].line).to eq(8)
294+
expect(tokens[33].column).to eq(1)
295+
expect(tokens[34].type).to eq(:RBRACE)
296+
expect(tokens[34].value).to eq('}')
297+
expect(tokens[34].line).to eq(8)
298+
expect(tokens[34].column).to eq(3)
299+
expect(tokens[35].type).to eq(:NEWLINE)
300+
expect(tokens[35].value).to eq("\n")
301+
expect(tokens[35].line).to eq(8)
302+
expect(tokens[35].column).to eq(4)
303+
end
304+
305+
it 'should handle a string with a single variable and newline characters' do
306+
manifest = <<-END
307+
foo
308+
/bin/${foo} >>
309+
/bar/baz"
310+
END
311+
@lexer.interpolate_string(manifest, 1, 1)
312+
tokens = @lexer.tokens
313+
314+
expect(tokens.length).to eq(3)
315+
316+
expect(tokens[0].type).to eq(:DQPRE)
317+
expect(tokens[0].value).to eq(" foo\n /bin/")
318+
expect(tokens[0].line).to eq(1)
319+
expect(tokens[0].column).to eq(1)
320+
321+
expect(tokens[1].type).to eq(:VARIABLE)
322+
expect(tokens[1].value).to eq('foo')
323+
expect(tokens[1].line).to eq(2)
324+
expect(tokens[1].column).to eq(20)
325+
326+
expect(tokens[2].type).to eq(:DQPOST)
327+
expect(tokens[2].value).to eq(" >>\n /bar/baz")
328+
expect(tokens[2].line).to eq(2)
329+
expect(tokens[2].column).to eq(25)
330+
end
331+
142332
it 'should handle a string with a single variable and surrounding text' do
143333
@lexer.interpolate_string('foo${bar}baz"', 1, 1)
144334
tokens = @lexer.tokens

0 commit comments

Comments
 (0)