From 6e836153be989446a96fa8f17a3b0e3c573526c9 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Sat, 17 Oct 2026 12:32:38 +0100 Subject: [PATCH] An error in code the compiler makes is reported at the code it was made for A node the compiler builds (a for...of's or for...in's loop, the call of `[Symbol.iterator]`, a `using`'s dispose call) has no position, so an error in it was reported at 1:1: for (const x of 5) {} // 1:1: Can't resolve property 'length' of type s32 using x = 5; // 1:1: Can't resolve property 'dispose' of type s32 Each statement now records where it is (without the debug scope), and loc() gives a node with no position that location, in the current scope; a statement the compiler made keeps the anchor of the one it was made for. The dispose call, made at the scope's exit, is anchored at its declaration. #541 and #557 had stamped positions on the generated cast and constructor nodes one at a time; this covers the rest. Co-Authored-By: Claude Opus 5.5 --- tslang/lib/TypeScript/MLIRGenImpl.h | 28 +++++++++++++++---- tslang/lib/TypeScript/MLIRGenStatements.cpp | 8 ++++++ tslang/test/tester/CMakeLists.txt | 20 +++++++++++++ .../test/tester/diagnostic-location/for_in.ts | 9 ++++++ .../test/tester/diagnostic-location/for_of.ts | 9 ++++++ .../test/tester/diagnostic-location/using.ts | 6 ++++ 6 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 tslang/test/tester/diagnostic-location/for_in.ts create mode 100644 tslang/test/tester/diagnostic-location/for_of.ts create mode 100644 tslang/test/tester/diagnostic-location/using.ts diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index bfacf7354..b62a7b92e 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -161,7 +161,8 @@ class MLIRGenImpl path(pathParam), declarationMode(false), tempEntryBlock(nullptr), - overwriteLoc(mlir::UnknownLoc::get(builder.getContext())) + overwriteLoc(mlir::UnknownLoc::get(builder.getContext())), + generatedNodesAnchor(mlir::UnknownLoc::get(builder.getContext())) { setCompileOptions(compileOptions); @@ -1157,6 +1158,10 @@ class MLIRGenImpl mlir::LogicalResult mlirGenDisposeOne(mlir::Location location, ts::VariableDeclarationDOM::TypePtr varDecl, mlir::Value varValue, const GenContext &genContext) { + // the call is made at the scope's exit, but an error in it belongs to the declaration + MLIRLocationGuard anchorGuard(generatedNodesAnchor); + generatedNodesAnchor = stripMetadata(varDecl->getLoc()); + auto disposeGuard = varDecl->getDisposeGuard(); if (!disposeGuard) { @@ -13947,13 +13952,23 @@ class MLIRGenImpl return mlir::UnknownLoc::get(builder.getContext()); } + // a node the compiler made (a for...of's loop, a `using`'s dispose call) has no position: + // it is where the code it was made for is, not at the start of the file + if (loc->isTextRangeEmpty() && !isa(generatedNodesAnchor)) + { + return locFuseWithScope(combine(overwriteLoc, generatedNodesAnchor)); + } + + return locFuseWithScope(combine(overwriteLoc, locWithoutScope(loc))); + } + + // where the node is in the source, without the debug scope and the overwriteLoc loc() adds + mlir::Location locWithoutScope(TextRange loc) + { auto pos = loc->pos.textPos > 0 ? loc->pos.textPos : loc->pos.pos; //return loc1(sourceFile, fileName.str(), pos, loc->_end - pos); //return loc2(sourceFile, fileName.str(), pos, loc->_end - pos); - return locFuseWithScope( - combine( - overwriteLoc, - loc2Fuse(sourceFile, mainSourceFileName.str(), pos, loc->_end - pos))); + return loc2Fuse(sourceFile, mainSourceFileName.str(), pos, loc->_end - pos); } mlir::Location loc1(ts::SourceFile sourceFile, std::string fileName, int start, int length) @@ -14375,6 +14390,9 @@ class MLIRGenImpl mlir::ModuleOp tempModule; mlir_ts::FuncOp tempFuncOp; mlir::Location overwriteLoc; + // where the innermost statement being generated is (no debug scope: loc() adds the current + // one): the location of the nodes the compiler makes for it, which have none of their own + mlir::Location generatedNodesAnchor; }; } // namespace mlirgen } // namespace typescript diff --git a/tslang/lib/TypeScript/MLIRGenStatements.cpp b/tslang/lib/TypeScript/MLIRGenStatements.cpp index c77e5015b..6eae033d5 100644 --- a/tslang/lib/TypeScript/MLIRGenStatements.cpp +++ b/tslang/lib/TypeScript/MLIRGenStatements.cpp @@ -292,6 +292,14 @@ namespace mlirgen mlir::LogicalResult MLIRGenImpl::mlirGen(Statement statementAST, const GenContext &genContext) { + // what the compiler makes for this statement is reported where the statement is; a + // statement it made itself keeps the anchor of the one it was made for + MLIRLocationGuard anchorGuard(generatedNodesAnchor); + if (!statementAST->isTextRangeEmpty()) + { + generatedNodesAnchor = locWithoutScope(statementAST); + } + auto kind = (SyntaxKind)statementAST; if (kind == SyntaxKind::FunctionDeclaration) { diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index fc8ccd084..6e0c09750 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2947,6 +2947,26 @@ set_tests_properties(test-compile-diagnostic-location-implicit-constructor PROPERTIES PASS_REGULAR_EXPRESSION "implicit_constructor\\.ts:9:1: error: Class constructor is not accessable" FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|implicit_constructor\\.ts:1:") +# An error in the code the compiler makes for a statement (a for...of's or for...in's loop) is +# reported at the statement, and one in a `using`'s dispose call at the declaration - not at 1:1, +# as a node the compiler makes has no position of its own. +set(diagnostic_location_statement_cases + "for_of|for_of\\.ts:6:5: error: Can't resolve property 'length' of type s32" + "for_in|for_in\\.ts:6:5: error: Can't resolve property 'length' of type s32" + "using|using\\.ts:5:11: error: Can't resolve property 'dispose' of type s32") +foreach(diagnostic_location_case ${diagnostic_location_statement_cases}) + string(REPLACE "|" ";" diagnostic_location_parts "${diagnostic_location_case}") + list(GET diagnostic_location_parts 0 diagnostic_location_name) + list(GET diagnostic_location_parts 1 diagnostic_location_error) + add_test(NAME test-compile-diagnostic-location-${diagnostic_location_name} + COMMAND $ --emit=obj --no-default-lib -mm=none + "${PROJECT_SOURCE_DIR}/test/tester/diagnostic-location/${diagnostic_location_name}.ts" + -o "${CMAKE_CURRENT_BINARY_DIR}/diagnostic-location-${diagnostic_location_name}.obj") + set_tests_properties(test-compile-diagnostic-location-${diagnostic_location_name} + PROPERTIES PASS_REGULAR_EXPRESSION "${diagnostic_location_error}" + FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|${diagnostic_location_name}\\.ts:1:") +endforeach() + # A call to the collector's API (GC_gcollect, or the weak-link functions DefaultLib's WeakRef uses) # is an error under every model but gc, which alone links the collector - it used to reach the # linker as an unresolved symbol. Under gc the same file compiles. diff --git a/tslang/test/tester/diagnostic-location/for_in.ts b/tslang/test/tester/diagnostic-location/for_in.ts new file mode 100644 index 000000000..1a20a9e1e --- /dev/null +++ b/tslang/test/tester/diagnostic-location/for_in.ts @@ -0,0 +1,9 @@ +// A for...in over something without a length was reported at the start of the file: the loop +// the compiler makes for it had no position. + +function main() { + const n = 5; + for (const k in n) { + print(k); + } +} diff --git a/tslang/test/tester/diagnostic-location/for_of.ts b/tslang/test/tester/diagnostic-location/for_of.ts new file mode 100644 index 000000000..1c5b8abc4 --- /dev/null +++ b/tslang/test/tester/diagnostic-location/for_of.ts @@ -0,0 +1,9 @@ +// A for...of over something that isn't iterable was reported at the start of the file: the loop +// the compiler makes for it had no position. + +function main() { + const n = 5; + for (const x of n) { + print(x); + } +} diff --git a/tslang/test/tester/diagnostic-location/using.ts b/tslang/test/tester/diagnostic-location/using.ts new file mode 100644 index 000000000..efe694eab --- /dev/null +++ b/tslang/test/tester/diagnostic-location/using.ts @@ -0,0 +1,6 @@ +// A `using` of a value with no dispose method was reported at the start of the file: the dispose +// call the compiler makes for it had no position. + +function main() { + using x = 5; +}