Skip to content

Commit c5c4d7c

Browse files
authored
Merge pull request #4313 from eelco/no-whitespace-mixing-strict
Don’t allow mixing spaces and tabs for indentation
2 parents f8b0c80 + bb40b11 commit c5c4d7c

File tree

5 files changed

+61
-26
lines changed

5 files changed

+61
-26
lines changed

src/lexer.coffee

+14
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ exports.Lexer = class Lexer
4040
@indebt = 0 # The over-indentation at the current level.
4141
@outdebt = 0 # The under-outdentation at the current level.
4242
@indents = [] # The stack of all current indentation levels.
43+
@indentLiteral = '' # The indentation
4344
@ends = [] # The stack for pairing up tokens.
4445
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`.
4546
@seenFor = no # Used to recognize FORIN and FOROF tokens.
@@ -351,6 +352,16 @@ exports.Lexer = class Lexer
351352
size = indent.length - 1 - indent.lastIndexOf '\n'
352353
noNewlines = @unfinished()
353354

355+
newIndentLiteral = if size > 0 then indent[-size..] else ''
356+
unless /^(.?)\1*$/.exec newIndentLiteral
357+
@error 'mixed indentation', offset: indent.length
358+
return indent.length
359+
360+
minLiteralLength = Math.min newIndentLiteral.length, @indentLiteral.length
361+
if newIndentLiteral[...minLiteralLength] isnt @indentLiteral[...minLiteralLength]
362+
@error 'indentation mismatch', offset: indent.length
363+
return indent.length
364+
354365
if size - @indebt is @indent
355366
if noNewlines then @suppressNewlines() else @newlineToken 0
356367
return indent.length
@@ -362,13 +373,15 @@ exports.Lexer = class Lexer
362373
return indent.length
363374
unless @tokens.length
364375
@baseIndent = @indent = size
376+
@indentLiteral = newIndentLiteral
365377
return indent.length
366378
diff = size - @indent + @outdebt
367379
@token 'INDENT', diff, indent.length - size, size
368380
@indents.push diff
369381
@ends.push {tag: 'OUTDENT'}
370382
@outdebt = @indebt = 0
371383
@indent = size
384+
@indentLiteral = newIndentLiteral
372385
else if size < @baseIndent
373386
@error 'missing indentation', offset: indent.length
374387
else
@@ -405,6 +418,7 @@ exports.Lexer = class Lexer
405418

406419
@token 'TERMINATOR', '\n', outdentLength, 0 unless @tag() is 'TERMINATOR' or noNewlines
407420
@indent = decreasedIndent
421+
@indentLiteral = @indentLiteral[...decreasedIndent]
408422
this
409423

410424
# Matches and consumes non-meaningful whitespace. Tag the previous token

test/compilation.coffee

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ test "#2516: Unicode spaces should not be part of identifiers", ->
7676
eq 5, {c: 5}[ 'c' ]
7777

7878
# A line where every space in non-breaking
79-
  eq 1 + 12  
79+
eq 1 + 12  
8080

8181
test "don't accidentally stringify keywords", ->
8282
ok (-> this == 'this')() is false

test/error_messages.coffee

-12
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,6 @@ test "compiler error formatting", ->
4141
^^^^
4242
'''
4343

44-
test "compiler error formatting with mixed tab and space", ->
45-
assertErrorFormat """
46-
\t if a
47-
\t test
48-
""",
49-
'''
50-
[stdin]:1:4: error: unexpected if
51-
\t if a
52-
\t ^^
53-
'''
54-
55-
5644
if require?
5745
fs = require 'fs'
5846
path = require 'path'

test/formatting.coffee

+33
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,36 @@ test "#1275: allow indentation before closing brackets", ->
254254
a = 1
255255
)
256256
eq 1, a
257+
258+
test "don’t allow mixing of spaces and tabs for indentation", ->
259+
try
260+
CoffeeScript.compile '''
261+
new Layer
262+
x: 0
263+
y: 1
264+
'''
265+
ok no
266+
catch e
267+
eq 'indentation mismatch', e.message
268+
269+
test "each code block that starts at indentation 0 can use a different style", ->
270+
doesNotThrow ->
271+
CoffeeScript.compile '''
272+
new Layer
273+
x: 0
274+
y: 1
275+
new Layer
276+
x: 0
277+
y: 1
278+
'''
279+
280+
test "tabs and spaces cannot be mixed for indentation", ->
281+
try
282+
CoffeeScript.compile '''
283+
new Layer
284+
x: 0
285+
y: 1
286+
'''
287+
ok no
288+
catch e
289+
eq 'mixed indentation', e.message

test/scope.coffee

+13-13
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,21 @@ test "#1183: super + fat arrows", ->
5959
dolater = (cb) -> cb()
6060

6161
class A
62-
constructor: ->
63-
@_i = 0
64-
foo : (cb) ->
65-
dolater =>
66-
@_i += 1
67-
cb()
62+
constructor: ->
63+
@_i = 0
64+
foo : (cb) ->
65+
dolater =>
66+
@_i += 1
67+
cb()
6868

6969
class B extends A
70-
constructor : ->
71-
super
72-
foo : (cb) ->
73-
dolater =>
74-
dolater =>
75-
@_i += 2
76-
super cb
70+
constructor : ->
71+
super
72+
foo : (cb) ->
73+
dolater =>
74+
dolater =>
75+
@_i += 2
76+
super cb
7777

7878
b = new B
7979
b.foo => eq b._i, 3

0 commit comments

Comments
 (0)