Skip to content

Commit 960890e

Browse files
authored
Fix columns being off by one / implement line cache (#1174)
1 parent 0910982 commit 960890e

File tree

188 files changed

+13698
-13664
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

188 files changed

+13698
-13664
lines changed

src/ast.ts

+40
Original file line numberDiff line numberDiff line change
@@ -1718,6 +1718,46 @@ export class Source extends Node {
17181718
var kind = this.sourceKind;
17191719
return kind == SourceKind.LIBRARY || kind == SourceKind.LIBRARY_ENTRY;
17201720
}
1721+
1722+
/** Cached line starts. */
1723+
private lineCache: i32[] | null = null;
1724+
1725+
/** Rememberd column number. */
1726+
private lineColumn: i32 = 0;
1727+
1728+
/** Determines the line number at the specified position. */
1729+
lineAt(pos: i32): i32 {
1730+
assert(pos >= 0 && pos < 0x7fffffff);
1731+
var lineCache = this.lineCache;
1732+
if (!lineCache) {
1733+
this.lineCache = lineCache = [0];
1734+
let text = this.text;
1735+
let off = 0;
1736+
let end = text.length;
1737+
while (off < end) {
1738+
if (text.charCodeAt(off++) == CharCode.LINEFEED) lineCache.push(off);
1739+
}
1740+
lineCache.push(0x7fffffff);
1741+
}
1742+
var l = 0;
1743+
var r = lineCache.length - 1;
1744+
while (l < r) {
1745+
let m = l + ((r - l) >> 1);
1746+
let s = unchecked(lineCache[m]);
1747+
if (pos < s) r = m;
1748+
else if (pos < unchecked(lineCache[m + 1])) {
1749+
this.lineColumn = pos - s + 1;
1750+
return m + 1;
1751+
}
1752+
else l = m + 1;
1753+
}
1754+
return assert(0);
1755+
}
1756+
1757+
/** Gets the column number at the last position queried with `lineAt`. */
1758+
columnAt(): i32 {
1759+
return this.lineColumn;
1760+
}
17211761
}
17221762

17231763
/** Base class of all declaration statements. */

src/compiler.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -10091,13 +10091,15 @@ export class Compiler extends DiagnosticEmitter {
1009110091
}
1009210092

1009310093
var filenameArg = this.ensureStaticString(codeLocation.range.source.normalizedPath);
10094+
var range = codeLocation.range;
10095+
var source = range.source;
1009410096
return module.block(null, [
1009510097
module.call(
1009610098
abortInstance.internalName, [
1009710099
messageArg,
1009810100
filenameArg,
10099-
module.i32(codeLocation.range.line),
10100-
module.i32(codeLocation.range.column)
10101+
module.i32(source.lineAt(range.start)),
10102+
module.i32(source.columnAt())
1010110103
],
1010210104
NativeType.None
1010310105
),

src/diagnostics.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,19 @@ export class DiagnosticMessage {
128128
toString(): string {
129129
var range = this.range;
130130
if (range) {
131+
let source = range.source;
131132
return (
132133
diagnosticCategoryToString(this.category) +
133134
" " +
134135
this.code.toString() +
135136
": \"" +
136137
this.message +
137138
"\" in " +
138-
range.source.normalizedPath +
139+
source.normalizedPath +
139140
":" +
140-
range.line.toString() +
141+
source.lineAt(range.start).toString() +
141142
":" +
142-
range.column.toString()
143+
source.columnAt().toString()
143144
);
144145
}
145146
return (
@@ -172,6 +173,7 @@ export function formatDiagnosticMessage(
172173
// include range information if available
173174
var range = message.range;
174175
if (range) {
176+
let source = range.source;
175177

176178
// include context information if requested
177179
if (showContext) {
@@ -180,26 +182,27 @@ export function formatDiagnosticMessage(
180182
}
181183
sb.push("\n");
182184
sb.push(" in ");
183-
sb.push(range.source.normalizedPath);
185+
sb.push(source.normalizedPath);
184186
sb.push("(");
185-
sb.push(range.line.toString());
187+
sb.push(source.lineAt(range.start).toString());
186188
sb.push(",");
187-
sb.push(range.column.toString());
189+
sb.push(source.columnAt().toString());
188190
sb.push(")");
189191

190192
let relatedRange = message.relatedRange;
191193
if (relatedRange) {
194+
let relatedSource = relatedRange.source;
192195
if (showContext) {
193196
sb.push("\n");
194197
sb.push(formatDiagnosticContext(relatedRange, useColors));
195198
}
196199
sb.push("\n");
197200
sb.push(" in ");
198-
sb.push(relatedRange.source.normalizedPath);
201+
sb.push(relatedSource.normalizedPath);
199202
sb.push("(");
200-
sb.push(relatedRange.line.toString());
203+
sb.push(relatedSource.lineAt(relatedRange.start).toString());
201204
sb.push(",");
202-
sb.push(relatedRange.column.toString());
205+
sb.push(relatedSource.columnAt().toString());
203206
sb.push(")");
204207
}
205208
}

src/parser.ts

+17-5
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,8 @@ export class Parser extends DiagnosticEmitter {
932932
if ((flags & CommonFlags.DEFINITE_ASSIGNMENT) != 0 && initializer !== null) {
933933
this.error(
934934
DiagnosticCode.A_definite_assignment_assertion_is_not_permitted_in_this_context,
935-
range);
935+
range
936+
);
936937
}
937938
return Node.createVariableDeclaration(
938939
identifier,
@@ -1775,11 +1776,11 @@ export class Parser extends DiagnosticEmitter {
17751776
// ('get' | 'set')?
17761777
// Identifier ...
17771778

1778-
var startPos = tn.pos;
17791779
var isInterface = parent.kind == NodeKind.INTERFACEDECLARATION;
1780-
1780+
var startPos = 0;
17811781
var decorators: DecoratorNode[] | null = null;
17821782
if (tn.skip(Token.AT)) {
1783+
startPos = tn.tokenPos;
17831784
do {
17841785
let decorator = this.parseDecorator(tn);
17851786
if (!decorator) break;
@@ -1812,6 +1813,7 @@ export class Parser extends DiagnosticEmitter {
18121813
flags |= CommonFlags.PUBLIC;
18131814
accessStart = tn.tokenPos;
18141815
accessEnd = tn.pos;
1816+
if (!startPos) startPos = accessStart;
18151817
} else if (tn.skip(Token.PRIVATE)) {
18161818
if (isInterface) {
18171819
this.error(
@@ -1822,6 +1824,7 @@ export class Parser extends DiagnosticEmitter {
18221824
flags |= CommonFlags.PRIVATE;
18231825
accessStart = tn.tokenPos;
18241826
accessEnd = tn.pos;
1827+
if (!startPos) startPos = accessStart;
18251828
} else if (tn.skip(Token.PROTECTED)) {
18261829
if (isInterface) {
18271830
this.error(
@@ -1832,6 +1835,7 @@ export class Parser extends DiagnosticEmitter {
18321835
flags |= CommonFlags.PROTECTED;
18331836
accessStart = tn.tokenPos;
18341837
accessEnd = tn.pos;
1838+
if (!startPos) startPos = accessStart;
18351839
}
18361840

18371841
var staticStart = 0;
@@ -1848,6 +1852,7 @@ export class Parser extends DiagnosticEmitter {
18481852
flags |= CommonFlags.STATIC;
18491853
staticStart = tn.tokenPos;
18501854
staticEnd = tn.pos;
1855+
if (!startPos) startPos = staticStart;
18511856
} else {
18521857
flags |= CommonFlags.INSTANCE;
18531858
if (tn.skip(Token.ABSTRACT)) {
@@ -1860,6 +1865,7 @@ export class Parser extends DiagnosticEmitter {
18601865
flags |= CommonFlags.ABSTRACT;
18611866
abstractStart = tn.tokenPos;
18621867
abstractEnd = tn.pos;
1868+
if (!startPos) startPos = abstractStart;
18631869
}
18641870
if (parent.flags & CommonFlags.GENERIC) flags |= CommonFlags.GENERIC_CONTEXT;
18651871
}
@@ -1874,6 +1880,7 @@ export class Parser extends DiagnosticEmitter {
18741880
flags |= CommonFlags.READONLY;
18751881
readonlyStart = tn.tokenPos;
18761882
readonlyEnd = tn.pos;
1883+
if (!startPos) startPos = readonlyStart;
18771884
} else { // identifier
18781885
tn.reset(state);
18791886
}
@@ -1893,8 +1900,9 @@ export class Parser extends DiagnosticEmitter {
18931900
if (tn.peek(true, IdentifierHandling.PREFER) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
18941901
flags |= CommonFlags.GET;
18951902
isGetter = true;
1896-
setStart = tn.tokenPos;
1897-
setEnd = tn.pos;
1903+
getStart = tn.tokenPos;
1904+
getEnd = tn.pos;
1905+
if (!startPos) startPos = getStart;
18981906
if (flags & CommonFlags.READONLY) {
18991907
this.error(
19001908
DiagnosticCode._0_modifier_cannot_be_used_here,
@@ -1910,6 +1918,7 @@ export class Parser extends DiagnosticEmitter {
19101918
isSetter = true;
19111919
setStart = tn.tokenPos;
19121920
setEnd = tn.pos;
1921+
if (!startPos) startPos = setStart;
19131922
if (flags & CommonFlags.READONLY) {
19141923
this.error(
19151924
DiagnosticCode._0_modifier_cannot_be_used_here,
@@ -1922,6 +1931,7 @@ export class Parser extends DiagnosticEmitter {
19221931
} else if (tn.skip(Token.CONSTRUCTOR)) {
19231932
flags |= CommonFlags.CONSTRUCTOR;
19241933
isConstructor = true;
1934+
if (!startPos) startPos = tn.tokenPos;
19251935
if (flags & CommonFlags.STATIC) {
19261936
this.error(
19271937
DiagnosticCode._0_modifier_cannot_be_used_here,
@@ -1948,6 +1958,7 @@ export class Parser extends DiagnosticEmitter {
19481958
name = Node.createConstructorExpression(tn.range());
19491959
} else {
19501960
if (!(isGetter || isSetter) && tn.skip(Token.OPENBRACKET)) {
1961+
if (!startPos) startPos = tn.tokenPos;
19511962
// TODO: also handle symbols, which might have some of these modifiers
19521963
if (flags & CommonFlags.PUBLIC) {
19531964
this.error(
@@ -1997,6 +2008,7 @@ export class Parser extends DiagnosticEmitter {
19972008
);
19982009
return null;
19992010
}
2011+
if (!startPos) startPos = tn.tokenPos;
20002012
name = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
20012013
}
20022014
var typeParameters: TypeParameterNode[] | null = null;

src/program.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -3106,13 +3106,14 @@ export class Function extends TypedElement {
31063106
if (this.program.options.sourceMap) {
31073107
let debugLocations = this.debugLocations;
31083108
for (let i = 0, k = debugLocations.length; i < k; ++i) {
3109-
let debugLocation = debugLocations[i];
3109+
let range = debugLocations[i];
3110+
let source = range.source;
31103111
module.setDebugLocation(
31113112
ref,
3112-
debugLocation.debugInfoRef,
3113-
debugLocation.source.debugInfoIndex,
3114-
debugLocation.line,
3115-
debugLocation.column
3113+
range.debugInfoRef,
3114+
source.debugInfoIndex,
3115+
source.lineAt(range.start),
3116+
source.columnAt()
31163117
);
31173118
}
31183119
}

src/tokenizer.ts

+1-25
Original file line numberDiff line numberDiff line change
@@ -402,10 +402,7 @@ export class Range {
402402
source: Source;
403403
start: i32;
404404
end: i32;
405-
406-
// TODO: set these while tokenizing
407-
// line: i32;
408-
// column: i32;
405+
debugInfoRef: usize = 0;
409406

410407
constructor(source: Source, start: i32, end: i32) {
411408
this.source = source;
@@ -429,30 +426,9 @@ export class Range {
429426
return new Range(this.source, this.end, this.end);
430427
}
431428

432-
get line(): i32 {
433-
var text = this.source.text;
434-
var line = 1;
435-
for (let pos = this.start; pos >= 0; --pos) {
436-
if (text.charCodeAt(pos) == CharCode.LINEFEED) line++;
437-
}
438-
return line;
439-
}
440-
441-
get column(): i32 {
442-
var text = this.source.text;
443-
var column = 0;
444-
for (let pos = this.start - 1; pos >= 0; --pos) {
445-
if (text.charCodeAt(pos) == CharCode.LINEFEED) break;
446-
++column;
447-
}
448-
return column;
449-
}
450-
451429
toString(): string {
452430
return this.source.text.substring(this.start, this.end);
453431
}
454-
455-
debugInfoRef: usize = 0;
456432
}
457433

458434
/** Handler for intercepting comments while tokenizing. */

tests/compiler/abi.untouched.wat

+8-8
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
i32.const 0
3838
i32.const 32
3939
i32.const 32
40-
i32.const 2
40+
i32.const 3
4141
call $~lib/builtins/abort
4242
unreachable
4343
end
@@ -74,7 +74,7 @@
7474
i32.const 0
7575
i32.const 32
7676
i32.const 45
77-
i32.const 2
77+
i32.const 3
7878
call $~lib/builtins/abort
7979
unreachable
8080
end
@@ -103,7 +103,7 @@
103103
i32.const 0
104104
i32.const 32
105105
i32.const 58
106-
i32.const 2
106+
i32.const 3
107107
call $~lib/builtins/abort
108108
unreachable
109109
end
@@ -120,7 +120,7 @@
120120
i32.const 0
121121
i32.const 32
122122
i32.const 65
123-
i32.const 2
123+
i32.const 3
124124
call $~lib/builtins/abort
125125
unreachable
126126
end
@@ -135,7 +135,7 @@
135135
i32.const 0
136136
i32.const 32
137137
i32.const 72
138-
i32.const 2
138+
i32.const 3
139139
call $~lib/builtins/abort
140140
unreachable
141141
end
@@ -150,7 +150,7 @@
150150
i32.const 0
151151
i32.const 32
152152
i32.const 74
153-
i32.const 2
153+
i32.const 3
154154
call $~lib/builtins/abort
155155
unreachable
156156
end
@@ -163,7 +163,7 @@
163163
i32.const 0
164164
i32.const 32
165165
i32.const 77
166-
i32.const 2
166+
i32.const 3
167167
call $~lib/builtins/abort
168168
unreachable
169169
end
@@ -176,7 +176,7 @@
176176
i32.const 0
177177
i32.const 32
178178
i32.const 79
179-
i32.const 2
179+
i32.const 3
180180
call $~lib/builtins/abort
181181
unreachable
182182
end

0 commit comments

Comments
 (0)