Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ codegraph-kernel/grammars/** linguist-vendored
codegraph-kernel/grammars/**/parser.c linguist-generated
# The kernel's patched copy of the tree-sitter runtime crate (docs/grammars/web-tree-sitter-runtime.md).
codegraph-kernel/vendor/** linguist-vendored
# Keep the CRLF line continuation in these fixtures byte-exact.
__tests__/fixtures/ts-syntax/tagged-template-invalid-escape.* -text
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Fixes

- A TSX file with a broken JSX attribute such as `<a {{b:>c:d(e f)` no longer stalls indexing for about 90 seconds and then drops out of the index; it is indexed like any other file with a syntax error.
- In TypeScript, a template string with a `\u` or `\x` that has no hex digits after it, like LaTeX in ``String.raw`\usepackage{x}` ``, now parses. Before, the file had a parse error there and the template's tag, such as `String.raw`, was not recorded as a call. Re-index TypeScript projects after upgrading.
- TypeScript variance annotations like `interface Stream<out A, in E>` or `<in out T>` now parse. Before, each one was a parse error, and in a large file enough of them could make the whole file fail to parse and show up with almost no symbols. Re-index TypeScript projects after upgrading.
- In TypeScript, an awaited call with type arguments like `await fetchApi<User>("/me")` or `await api.get<T>(url)` now links to the function it calls. Before, the call was dropped, so typed fetch wrappers looked like they had no callers. (#2586)
- In JavaScript and TypeScript, a call or `new` in a parameter's default value, like `function renderWithNote(note = buildNote())`, a destructured `{ size = defaultSize() }` or `constructor(private svc = new Service())`, now counts as the function's own call. Before, it was dropped, so `buildNote` was missing that caller and impact on it came back incomplete. Re-index JavaScript and TypeScript projects after upgrading. Thanks @inth3shadows for the report and the pull request. (#2588)
Expand Down
27 changes: 27 additions & 0 deletions __tests__/fixtures/ts-syntax/tagged-template-invalid-escape.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// A tagged template may contain escapes that are invalid in a string (ES2018,
// "template literal revision"): `\u`/`\x` not followed by hex digits (upstream #353).
declare const tag: (s: readonly string[], ...v: unknown[]) => string;

export const latex = String.raw`
\documentclass{article}
\usepackage{graphicx}
\begin{document}
\end{document}
`;

export const partial = tag`\x \xg \x4 \u \u12 \u{} \u{zz} ${latex} \unicode`;

export const valid = tag`\x41 \u0041 \u{1F600} \n \` \${x}`;

export const typed = tag<string>`\usepackage`;

// Line continuations, LF and CRLF, next to a bare escape.
export const cont = tag`a\
b\u\
c`;

// Bare escapes in template literal types: type arguments, not comparisons.
declare function g<T>(x: number): T;
export const ga = g<`\u`>(1);
export const gb = tag<`\x`>`x`;
export type Bare = `\u`;
30 changes: 30 additions & 0 deletions __tests__/fixtures/ts-syntax/tagged-template-invalid-escape.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// tsx twin of tagged-template-invalid-escape.ts.
// A tagged template may contain escapes that are invalid in a string (ES2018,
// "template literal revision"): `\u`/`\x` not followed by hex digits (upstream #353).
declare const tag: (s: readonly string[], ...v: unknown[]) => string;

export const latex = String.raw`
\documentclass{article}
\usepackage{graphicx}
\begin{document}
\end{document}
`;

export const partial = tag`\x \xg \x4 \u \u12 \u{} \u{zz} ${latex} \unicode`;

export const valid = tag`\x41 \u0041 \u{1F600} \n \` \${x}`;

export const typed = tag<string>`\usepackage`;

// Line continuations, LF and CRLF, next to a bare escape.
export const cont = tag`a\
b\u\
c`;

// Bare escapes in template literal types: type arguments, not comparisons.
declare function g<T>(x: number): T;
export const ga = g<`\u`>(1);
export const gb = tag<`\x`>`x`;
export type Bare = `\u`;

export const el = <a title={String.raw`\usepackage`}>{tag`\x`}</a>;
25 changes: 25 additions & 0 deletions __tests__/typescript-grammar-fork-shapes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,28 @@ describe('typeof unique in type arguments', () => {
}
});
});

describe('bare \\x / \\u escapes in templates (fork #15)', () => {
it.each(LANGS)('%s: String.raw`\\usepackage` parses clean and is a call edge', (language) => {
const src = 'export function f() {\n return String.raw`\\usepackage \\x41`;\n}';
const root = parse(language, src);
expect(root.hasError).toBe(false);
expect(root.descendantsOfType('escape_sequence').map((n) => n.text)).toEqual(['\\u', '\\x41']);
expect(calls(language, src)).toContain('String.raw');
});

it.each(LANGS)('%s: f<`\\u`>(1) is a call with type arguments, not a comparison', (language) => {
const src = 'export function g() {\n return f<`\\u`>(1);\n}';
const root = parse(language, src);
expect(root.hasError).toBe(false);
expect(root.descendantsOfType('binary_expression')).toHaveLength(0);
const call = root.descendantsOfType('call_expression')[0]!;
expect(call.childForFieldName('type_arguments')!.type).toBe('type_arguments');
expect(calls(language, src)).toContain('f');
});

it.each(LANGS)('%s: a string still rejects a bare \\u or \\x', (language) => {
expect(parse(language, 'const s = "\\u";').hasError).toBe(true);
expect(parse(language, "const s = '\\x';").hasError).toBe(true);
});
});
6 changes: 3 additions & 3 deletions codegraph-kernel/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ fn main() {
// the upstream repo so scanner.c's `../../common/scanner.h` resolves
// unedited. See docs/grammars/tree-sitter-typescript.md for what the base
// and the fork each fix.
// common/scanner.h daa69422522f270cb38b7927ad8b35205222ce9b3094a40074fab00c3d673ee5
// typescript/src/parser.c 69145b216b33599dd0c1d3e67bf57c9bc9b63243c1f60503c702df679da2cc85
// tsx/src/parser.c c9cd43c6d10e969e7e586475a1dd02c5070284567dc7dc4784b6170831abdc93
// common/scanner.h 23fd656d7e82f80bf66e0b9b8ef96aa6102ef5bfd9f32c561a4caabe08b9fd34
// typescript/src/parser.c 2a0e260b20b549794d633422ad63b4783252b3bd497d06b531cc2a21405670ac
// tsx/src/parser.c 1634073b56b61987f8435b80d4a335166e6c05b55c143f301415b52b86fce142
// Flags crib the crate's bindings/rust/build.rs (c11). Each parser.c is
// ~9 MB.
for dialect in ["typescript", "tsx"] {
Expand Down
Loading
Loading