Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
luau_type_check.cpp
Go to the documentation of this file.
1// luau_type_check.cpp — パッケージの Luau を読み込む前に型検査する(luau_type_check.h。T-0140・ADR-0046)。
2// Luau.Analysis の Frontend を 1 つ持ち、型の定義(定義ファイル)をグローバルに足しておく。
3// 検査のたびにモジュールの状態を捨て、そのパッケージのファイルだけを見せる(ほかのパッケージには require でも届かない)。
4// 戻り値の形は、型の定義の型と欄ごとに突き合わせ(ShapeWalker)、葉だけを合成したモジュールで Luau に確かめさせる。
5// 誤りは欄を書いた行に付け替える。
6// Luau.Analysis を知るのはこのファイルだけ(ヘッダが大きいので pch.h にも入れない)。
8
9#include <algorithm>
10#include <format>
11#include <fstream>
12#include <iterator>
13#include <map>
14#include <optional>
15#include <set>
16#include <utility>
17
18#include "core/paths.h"
19#include "script/luau_package.h"
20
21#if defined(_MSC_VER)
22#pragma warning(push, 0) // Luau のヘッダの警告(使わない引数など)は自分のコードではないので止める
23#endif
24#include "Luau/Ast.h"
25#include "Luau/BuiltinDefinitions.h"
26#include "Luau/Config.h"
27#include "Luau/ConfigResolver.h"
28#include "Luau/Error.h"
29#include "Luau/FileResolver.h"
30#include "Luau/Frontend.h"
31#include "Luau/ToString.h"
32#include "Luau/Type.h"
33#include "Luau/TypeArena.h"
34#include "Luau/TypePack.h"
35#if defined(_MSC_VER)
36#pragma warning(pop)
37#endif
38
39namespace bicameral::script {
40
41 namespace {
42
43 constexpr std::string_view MANIFEST_FILE = "package.luau";
44 constexpr std::string_view MODULE_EXTENSION = ".luau";
45 constexpr std::string_view
46 RETURN_CHECK_MODULE = "@check/return"; // パッケージの名前・パスに @ は無いので重ならない
47 constexpr std::string_view DEFINITIONS_PACKAGE = "@bicameral";
48
49 // 殻(ADR-0030)で見えないグローバル。検査でも見えないようにして、走らせる前に「知らない名前」で落とす
50 constexpr const char* HIDDEN_GLOBALS[] = {"os", "debug", "gcinfo", "getfenv",
51 "setfenv", "newproxy", "loadstring", "collectgarbage"};
52
53 std::string ToUtf8String(const fs::path& path) {
54 const std::u8string text = path.generic_u8string();
55
56 return {text.begin(), text.end()};
57 }
58
59 // --- 型検査に見せるファイル(1 つのパッケージ + 合成したモジュール)---
60
61 class PackageFileResolver final : public Luau::FileResolver {
62 public:
63 void Reset(const PackageSource& package) {
64 m_package = package.name;
65 m_sources.clear();
66 for (const auto& [path, source] : package.files)
67 m_sources.emplace(package.name + "/" + path, source);
68 }
69
70 void AddModule(std::string name, std::string source) {
71 m_sources.insert_or_assign(std::move(name), std::move(source));
72 }
73
74 std::optional<Luau::SourceCode> readSource(const Luau::ModuleName& name) override {
75 const auto found = m_sources.find(name);
76 if (found == m_sources.end())
77 return std::nullopt;
78
79 return Luau::SourceCode{found->second, Luau::SourceCode::Module};
80 }
81
82 // require("sub/util") → "パッケージ/sub/util.luau"(殻の require と同じ規則。luau_package の PackageModulePath)。
83 // 規則に合わない・無いファイルも名前を返す(readSource が無いと答え、Luau が「require の先が無い」を出す)
84 std::optional<Luau::ModuleInfo> resolveModule(const Luau::ModuleInfo* context, Luau::AstExpr* expr,
85 const Luau::TypeCheckLimits& /*limits*/) override {
86 const auto* constant = expr ? expr->as<Luau::AstExprConstantString>() : nullptr;
87 if (context == nullptr || constant == nullptr)
88 return std::nullopt;
89
90 const std::string_view name(constant->value.data, constant->value.size);
91 const auto path = PackageModulePath(name);
92
93 return Luau::ModuleInfo{m_package + "/" + (path ? *path : std::string(name))};
94 }
95
96 private:
97 std::string m_package;
98 std::map<std::string, std::string> m_sources; // "パッケージ/相対パス" → ソース
99 };
100
101 // 全部のファイルを strict で見る(ファイルの先頭の --!nonstrict などはそのファイルだけに効く)
102 class StrictConfigResolver final : public Luau::ConfigResolver {
103 public:
104 StrictConfigResolver() { m_config.mode = Luau::Mode::Strict; }
105
106 const Luau::Config& getConfig(const Luau::ModuleName& /*name*/,
107 const Luau::TypeCheckLimits& /*limits*/) const override {
108 return m_config;
109 }
110
111 private:
112 Luau::Config m_config;
113 };
114
115 // --- 誤り → 診断 ---
116
117 std::string KindText(const Luau::TypeError& error) {
118 if (Luau::get_if<Luau::SyntaxError>(&error.data))
119 return "文法の誤り";
120 if (Luau::get_if<Luau::UnknownSymbol>(&error.data))
121 return "知らない名前";
122 if (Luau::get_if<Luau::UnknownProperty>(&error.data))
123 return "知らない欄";
124 if (Luau::get_if<Luau::MissingProperties>(&error.data))
125 return "欄が足りない";
126 if (Luau::get_if<Luau::TypeMismatch>(&error.data))
127 return "型が合わない";
128 if (Luau::get_if<Luau::UnknownRequire>(&error.data))
129 return "require の先が無い";
130 if (Luau::get_if<Luau::CountMismatch>(&error.data))
131 return "数が合わない";
132
133 return "型の誤り";
134 }
135
136 // 戻り値の検査: どのファイルが返す表を、型の定義のどの型で確かめるか
137 struct ReturnCheck {
138 std::string targetModule; // "パッケージ/init.luau"
139 std::string typeName;
140 };
141
142 // そのモジュールの最後の return の場所(無ければ 1 行目)
143 Luau::Location ReturnLocation(Luau::Frontend& frontend, const std::string& moduleName) {
144 Luau::Location location;
145 const Luau::SourceModule* source = frontend.getSourceModule(moduleName);
146 if (source == nullptr || source->root == nullptr)
147 return location;
148
149 for (Luau::AstStat* statement : source->root->body) {
150 if (statement->is<Luau::AstStatReturn>())
151 location = statement->location;
152 }
153
154 return location;
155 }
156
157 // Luau の説明は改行・タブを含むことがある。ログの 1 行に収まるよう空白 1 つにまとめる
158 std::string OneLine(std::string_view text) {
159 std::string line;
160 bool space = false;
161 for (const char character : text) {
162 const bool isSpace = character == '\n' || character == '\r' || character == '\t' || character == ' ';
163 if (isSpace) {
164 space = !line.empty();
165 continue;
166 }
167
168 if (space)
169 line += ' ';
170 line += character;
171 space = false;
172 }
173
174 return line;
175 }
176
177 TypeDiagnostic MakeDiagnostic(std::string file, const Luau::Location& location, std::string kind,
178 std::string_view message) {
179 return TypeDiagnostic{.file = std::move(file),
180 .line = location.begin.line + 1,
181 .column = location.begin.column + 1,
182 .kind = std::move(kind),
183 .message = OneLine(message)};
184 }
185
186 // --- 型の道具 ---
187
188 std::string TypeText(Luau::TypeId type) {
189 Luau::ToStringOptions options;
190 options.exhaustive = true;
191
192 return Luau::toString(type, options);
193 }
194
195 bool IsNil(Luau::TypeId type) {
196 const auto* primitive = Luau::get<Luau::PrimitiveType>(Luau::follow(type));
197
198 return primitive != nullptr && primitive->type == Luau::PrimitiveType::NilType;
199 }
200
201 bool IsOptional(Luau::TypeId type) {
202 const auto* union_ = Luau::get<Luau::UnionType>(Luau::follow(type));
203
204 return union_ != nullptr && std::ranges::any_of(union_->options, IsNil);
205 }
206
207 // X? → X(nil を除いた選択肢が 1 つのときだけ。値があるときに使う)
208 Luau::TypeId StripOptional(Luau::TypeId type) {
209 type = Luau::follow(type);
210 const auto* union_ = Luau::get<Luau::UnionType>(type);
211 if (union_ == nullptr)
212 return type;
213
214 std::vector<Luau::TypeId> options;
215 for (Luau::TypeId option : union_->options) {
216 if (!IsNil(option))
217 options.push_back(option);
218 }
219
220 return options.size() == 1 ? Luau::follow(options[0]) : type;
221 }
222
223 std::string QuoteKey(std::string_view key) {
224 std::string text = "[\"";
225 for (const char character : key) {
226 if (character == '"' || character == '\\')
227 text += '\\';
228 text += character;
229 }
230
231 return text + "\"]";
232 }
233
234 // --- 戻り値の形を欄ごとに突き合わせる ---
235 // Luau の新しい型ソルバーは「省ける欄を書かなかった表」を、省ける欄つきの型(entry: string? など)へ渡せない
236 // (表の欄の型を不変として扱うため。T-0140 の実験)。そこで表は欄ごとに降り、欄が無い・知らない欄はここで診断にし、
237 // 葉(数・文字列など)だけを合成したモジュールの 1 行「local _n: <型> = value["..."]」で Luau に確かめさせる。
238 // 誤りは葉の欄を書いた行に付け替える。
240 public:
241 struct Leaf {
242 std::string expression; // value["species"]["water"]["formation_enthalpy_j_per_mol"]
243 std::string typeText; // number
244 std::string path; // species.water.formation_enthalpy_j_per_mol
245 std::string module; // 欄を書いたファイル
246 Luau::Location location;
247 };
248
249 struct Place {
250 std::string expression;
251 std::string path;
252 std::string module;
253 Luau::Location location;
254 };
255
256 void Walk(Luau::TypeId expected, Luau::TypeId actual, const Place& place, int depth) {
257 expected = StripOptional(expected);
258 actual = Luau::follow(actual);
259 if (Luau::get<Luau::AnyType>(expected))
260 return;
261
262 const auto* expectedTable = Luau::get<Luau::TableType>(expected);
263 const auto* actualTable = Luau::get<Luau::TableType>(actual);
264 if (expectedTable == nullptr || actualTable == nullptr || actualTable->indexer || depth > MAX_DEPTH) {
265 leaves.push_back(Leaf{.expression = place.expression,
266 .typeText = TypeText(expected),
267 .path = place.path,
268 .module = place.module,
269 .location = place.location});
270 return;
271 }
272
273 // 表の欄を書いたファイル(require した先の表なら、そのファイル)
274 const std::string tableModule = actualTable->definitionModuleName.empty()
275 ? place.module
276 : actualTable->definitionModuleName;
277 WalkExpectedFields(*expectedTable, *actualTable, place, tableModule, depth);
278 WalkExtraFields(*expectedTable, *actualTable, place, tableModule, depth);
279 }
280
281 std::vector<Leaf> leaves;
282 std::vector<TypeDiagnostic> diagnostics;
283
284 private:
285 static constexpr int MAX_DEPTH = 32;
286
287 static std::string ChildPath(const Place& place, const std::string& name) {
288 return place.path.empty() ? name : place.path + "." + name;
289 }
290
291 void WalkProperty(Luau::TypeId expected, const std::string& name, const Luau::Property& property,
292 const Place& parent, const std::string& tableModule, int depth) {
293 if (!property.readTy)
294 return;
295
296 const Place child{.expression = parent.expression + QuoteKey(name),
297 .path = ChildPath(parent, name),
298 .module = tableModule,
299 .location = property.location.value_or(parent.location)};
300 Walk(expected, *property.readTy, child, depth + 1);
301 }
302
303 // 型の定義にある欄: 書いてあれば降りる。書いていない欄が省けない欄なら「欄が足りない」
304 void WalkExpectedFields(const Luau::TableType& expected, const Luau::TableType& actual, const Place& place,
305 const std::string& tableModule, int depth) {
306 for (const auto& [name, property] : expected.props) {
307 if (!property.readTy)
308 continue;
309
310 const auto found = actual.props.find(name);
311 if (found != actual.props.end()) {
312 WalkProperty(*property.readTy, name, found->second, place, tableModule, depth);
313 continue;
314 }
315
316 if (!IsOptional(*property.readTy))
317 diagnostics.push_back(MakeDiagnostic(
318 place.module, place.location, "欄が足りない",
319 std::format("{} に {} が無い(型 {})", place.path.empty() ? "返す表" : place.path, name,
320 TypeText(*property.readTy))));
321 }
322 }
323
324 // 型の定義に無い欄: 鍵の型(例 { [string]: Species })があればそれで降りる。無ければ「知らない欄」(綴りの誤りなど)
325 void WalkExtraFields(const Luau::TableType& expected, const Luau::TableType& actual, const Place& place,
326 const std::string& tableModule, int depth) {
327 for (const auto& [name, property] : actual.props) {
328 if (expected.props.contains(name))
329 continue;
330
331 if (expected.indexer) {
332 WalkProperty(expected.indexer->indexResultType, name, property, place, tableModule, depth);
333 continue;
334 }
335
336 diagnostics.push_back(
337 MakeDiagnostic(tableModule, property.location.value_or(place.location), "知らない欄",
338 std::format("{} は型の定義に無い欄(綴りの誤り?)", ChildPath(place, name))));
339 }
340 }
341 };
342
343 // 合成したモジュールのソース: 1 行目 --!strict、2 行目 require、3 行目から葉が 1 行ずつ
344 constexpr uint32_t FIRST_LEAF_LINE = 2; // 0 始まり
345
346 std::string ShapeCheckSource(const std::string& requireName, const std::vector<ShapeWalker::Leaf>& leaves) {
347 std::string source = std::format("--!strict\nlocal value = require(\"{}\")\n", requireName);
348 for (size_t index = 0; index < leaves.size(); ++index)
349 source += std::format("local _{}: {} = value{}\n", index, leaves[index].typeText,
350 leaves[index].expression);
351
352 return source + "return nil\n";
353 }
354
355 void HideGlobals(Luau::GlobalTypes& globals) {
356 for (const char* name : HIDDEN_GLOBALS) {
357 const Luau::AstName astName = globals.globalNames.names->get(name);
358 if (astName.value != nullptr)
359 globals.globalScope->bindings.erase(Luau::Symbol(astName));
360 }
361 }
362
363 std::string DefinitionErrors(const Luau::LoadDefinitionFileResult& loaded) {
364 std::string text;
365 for (const Luau::ParseError& error : loaded.parseResult.errors)
366 text += std::format(" {}:{}: {}", error.getLocation().begin.line + 1,
367 error.getLocation().begin.column + 1, error.getMessage());
368 if (loaded.module) {
369 for (const Luau::TypeError& error : loaded.module->errors)
370 text += std::format(" {}:{}: {}", error.location.begin.line + 1, error.location.begin.column + 1,
371 Luau::toString(error));
372 }
373
374 return text;
375 }
376
377 } // namespace
378
380 PackageFileResolver fileResolver;
381 StrictConfigResolver configResolver;
382 Luau::Frontend frontend{Luau::SolverMode::New, &fileResolver, &configResolver};
383
384 // 1 つのパッケージを検査する。files はパッケージの中の相対パス、returnCheck があれば戻り値の形も確かめる
385 std::vector<TypeDiagnostic> Check(const PackageSource& package, const std::vector<std::string>& files,
386 const std::optional<ReturnCheck>& returnCheck);
387
388 // returnCheck のファイルが返す表を、型の定義の型と欄ごとに突き合わせる(ShapeWalker)
389 void CheckReturnShape(const PackageSource& package, const ReturnCheck& returnCheck,
390 std::set<TypeDiagnostic>& found);
391 };
392
393 std::vector<TypeDiagnostic> LuauTypeChecker::Impl::Check(const PackageSource& package,
394 const std::vector<std::string>& files,
395 const std::optional<ReturnCheck>& returnCheck) {
396 // --- 前の検査の状態を捨てる(順番・回数に依らない診断にする)---
397 frontend.clear();
398 fileResolver.Reset(package);
399
400 // --- ファイルごとの検査(ファイル → 行 → 列 → 種類 → 文の順。重複は 1 つ)---
401 std::set<TypeDiagnostic> found;
402 bool targetHasSyntaxError = false;
403 for (const std::string& file : files) {
404 const Luau::CheckResult result = frontend.check(package.name + "/" + file);
405 for (const Luau::TypeError& error : result.errors) {
406 // 「引数に型を付けるとよい」は誤りではなく提案なので出さない
407 if (Luau::get_if<Luau::ExplicitFunctionAnnotationRecommended>(&error.data))
408 continue;
409
410 if (returnCheck && error.moduleName == returnCheck->targetModule &&
411 Luau::get_if<Luau::SyntaxError>(&error.data))
412 targetHasSyntaxError = true;
413
414 found.insert(MakeDiagnostic(error.moduleName, error.location, KindText(error),
415 Luau::toString(error, Luau::TypeErrorToStringOptions{&fileResolver})));
416 }
417 }
418
419 // 文法の誤りで読めないファイルの戻り値の形は見ない(誤りが重なって読みにくくなる)
420 if (returnCheck && !targetHasSyntaxError)
421 CheckReturnShape(package, *returnCheck, found);
422
423 return {found.begin(), found.end()};
424 }
425
426 void LuauTypeChecker::Impl::CheckReturnShape(const PackageSource& package, const ReturnCheck& returnCheck,
427 std::set<TypeDiagnostic>& found) {
428 const Luau::ModulePtr module = frontend.moduleResolver.getModule(returnCheck.targetModule);
429 const auto expectedType = frontend.globals.globalScope->lookupType(returnCheck.typeName);
430 if (!module || !expectedType)
431 return;
432
433 const Luau::Location returnLocation = ReturnLocation(frontend, returnCheck.targetModule);
434 const std::optional<Luau::TypeId> actual = Luau::first(module->returnType);
435 if (!actual) {
436 found.insert(MakeDiagnostic(returnCheck.targetModule, returnLocation, "返す表が無い",
437 std::format("{} の形の表を 1 つ返す", returnCheck.typeName)));
438 return;
439 }
440
441 // --- 欄ごとに降りる → 葉を合成したモジュールで確かめる ---
442 ShapeWalker walker;
443 walker.Walk(expectedType->type, *actual,
444 ShapeWalker::Place{
445 .expression = "", .path = "", .module = returnCheck.targetModule, .location = returnLocation},
446 0);
447 found.insert(walker.diagnostics.begin(), walker.diagnostics.end());
448 if (walker.leaves.empty())
449 return;
450
451 std::string requireName = returnCheck.targetModule.substr(package.name.size() + 1);
452 requireName.resize(requireName.size() - MODULE_EXTENSION.size());
453 const std::string shapeModule = package.name + "/" + std::string(RETURN_CHECK_MODULE);
454 fileResolver.AddModule(shapeModule, ShapeCheckSource(requireName, walker.leaves));
455
456 // --- 合成したモジュールの誤り → 葉の欄を書いた行 ---
457 const Luau::CheckResult result = frontend.check(shapeModule);
458 for (const Luau::TypeError& error : result.errors) {
459 if (error.moduleName != shapeModule)
460 continue;
461
462 const std::string message = Luau::toString(error, Luau::TypeErrorToStringOptions{&fileResolver});
463 const uint32_t line = error.location.begin.line;
464 if (line >= FIRST_LEAF_LINE && line - FIRST_LEAF_LINE < walker.leaves.size()) {
465 const ShapeWalker::Leaf& leaf = walker.leaves[line - FIRST_LEAF_LINE];
466 found.insert(MakeDiagnostic(leaf.module, leaf.location, KindText(error),
467 std::format("{}: {}", leaf.path, message)));
468 continue;
469 }
470
471 found.insert(MakeDiagnostic(returnCheck.targetModule, returnLocation,
472 std::format("返す表が {} の形でない", returnCheck.typeName), message));
473 }
474 }
475
476 LuauTypeChecker::LuauTypeChecker(std::unique_ptr<Impl> impl) : m_impl(std::move(impl)) {}
477
479
480 std::expected<std::unique_ptr<LuauTypeChecker>, std::string> LuauTypeChecker::Create(std::string_view definitions) {
481 auto impl = std::make_unique<Impl>();
482 Luau::Frontend& frontend = impl->frontend;
483
484 // --- 標準の型 → 殻で見えないものを外す → 型の定義を足す ---
485 Luau::registerBuiltinGlobals(frontend, frontend.globals);
486 HideGlobals(frontend.globals);
487
488 const Luau::LoadDefinitionFileResult loaded = frontend.loadDefinitionFile(
489 frontend.globals, frontend.globals.globalScope, definitions, std::string(DEFINITIONS_PACKAGE), false,
490 false);
491 Luau::freeze(frontend.globals.globalTypes);
492 if (!loaded.success)
493 return std::unexpected(std::format("型の定義に誤りがある:{}", DefinitionErrors(loaded)));
494
495 for (const std::string_view name : {MANIFEST_TYPE_NAME, ENTRY_TYPE_NAME}) {
496 if (!frontend.globals.globalScope->lookupType(std::string(name)))
497 return std::unexpected(std::format("型の定義に export type {} が無い", name));
498 }
499
500 return std::unique_ptr<LuauTypeChecker>(new LuauTypeChecker(std::move(impl)));
501 }
502
503 std::vector<TypeDiagnostic> LuauTypeChecker::CheckManifest(const PackageSource& package) {
504 if (!package.files.contains(std::string(MANIFEST_FILE)))
505 return {}; // 無いことは読み込みが理由を出す
506
507 const std::string manifest = package.name + "/" + std::string(MANIFEST_FILE);
508
509 return m_impl->Check(package, {std::string(MANIFEST_FILE)},
510 ReturnCheck{.targetModule = manifest, .typeName = std::string(MANIFEST_TYPE_NAME)});
511 }
512
513 std::vector<TypeDiagnostic> LuauTypeChecker::CheckPackage(const PackageSource& package, std::string_view entry) {
514 std::vector<std::string> files;
515 for (const auto& [path, source] : package.files)
516 files.push_back(path);
517
518 // entry のファイルが無い・名前の規則に合わないときは、戻り値の検査をしない(読み込みが理由を出す)
519 std::optional<ReturnCheck> returnCheck;
520 const auto entryPath = PackageModulePath(entry);
521 if (entryPath && package.files.contains(*entryPath))
522 returnCheck = ReturnCheck{.targetModule = package.name + "/" + *entryPath,
523 .typeName = std::string(ENTRY_TYPE_NAME)};
524
525 return m_impl->Check(package, files, returnCheck);
526 }
527
528 // --- 表示 ---
529
530 std::string FormatDiagnostic(const TypeDiagnostic& diagnostic) {
531 return std::format("{}:{}:{}: {}: {}", diagnostic.file, diagnostic.line, diagnostic.column, diagnostic.kind,
532 diagnostic.message);
533 }
534
535 std::string SummarizeDiagnostics(const std::vector<TypeDiagnostic>& diagnostics, size_t maxShown) {
536 std::string text;
537 for (size_t index = 0; index < diagnostics.size() && index < maxShown; ++index) {
538 if (index > 0)
539 text += " / ";
540 text += FormatDiagnostic(diagnostics[index]);
541 }
542
543 if (diagnostics.size() > maxShown)
544 text += std::format("(ほか {} 件)", diagnostics.size() - maxShown);
545
546 return text;
547 }
548
549 // --- ファイルから ---
550
552 return ExecutableDirectory() / "data" / "types" / "bicameral.d.luau";
553 }
554
555 std::expected<std::unique_ptr<LuauTypeChecker>, std::string> CreateTypeCheckerFromFile(
556 const fs::path& definitionsFile) {
557 std::ifstream file(definitionsFile, std::ios::binary);
558 if (!file)
559 return std::unexpected(std::format("型の定義 {} を開けない", ToUtf8String(definitionsFile)));
560
561 const std::string source{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
562 auto checker = LuauTypeChecker::Create(source);
563 if (!checker)
564 return std::unexpected(std::format("{}: {}", ToUtf8String(definitionsFile), checker.error()));
565
566 return checker;
567 }
568
569} // namespace bicameral::script
LuauTypeChecker(const LuauTypeChecker &)=delete
static std::expected< std::unique_ptr< LuauTypeChecker >, std::string > Create(std::string_view definitions)
std::vector< TypeDiagnostic > CheckPackage(const PackageSource &package, std::string_view entry)
std::vector< TypeDiagnostic > CheckManifest(const PackageSource &package)
std::optional< Luau::SourceCode > readSource(const Luau::ModuleName &name) override
std::optional< Luau::ModuleInfo > resolveModule(const Luau::ModuleInfo *context, Luau::AstExpr *expr, const Luau::TypeCheckLimits &) override
void Walk(Luau::TypeId expected, Luau::TypeId actual, const Place &place, int depth)
void WalkExtraFields(const Luau::TableType &expected, const Luau::TableType &actual, const Place &place, const std::string &tableModule, int depth)
void WalkProperty(Luau::TypeId expected, const std::string &name, const Luau::Property &property, const Place &parent, const std::string &tableModule, int depth)
static std::string ChildPath(const Place &place, const std::string &name)
void WalkExpectedFields(const Luau::TableType &expected, const Luau::TableType &actual, const Place &place, const std::string &tableModule, int depth)
const Luau::Config & getConfig(const Luau::ModuleName &, const Luau::TypeCheckLimits &) const override
std::string ShapeCheckSource(const std::string &requireName, const std::vector< ShapeWalker::Leaf > &leaves)
Luau::Location ReturnLocation(Luau::Frontend &frontend, const std::string &moduleName)
std::string DefinitionErrors(const Luau::LoadDefinitionFileResult &loaded)
TypeDiagnostic MakeDiagnostic(std::string file, const Luau::Location &location, std::string kind, std::string_view message)
std::expected< std::string, std::string > PackageModulePath(std::string_view name)
std::string SummarizeDiagnostics(const std::vector< TypeDiagnostic > &diagnostics, size_t maxShown)
std::string FormatDiagnostic(const TypeDiagnostic &diagnostic)
fs::path DefaultTypeDefinitionsPath()
std::expected< std::unique_ptr< LuauTypeChecker >, std::string > CreateTypeCheckerFromFile(const fs::path &definitionsFile)
constexpr std::string_view ENTRY_TYPE_NAME
constexpr std::string_view MANIFEST_TYPE_NAME
fs::path ExecutableDirectory()
Definition paths.cpp:12
void CheckReturnShape(const PackageSource &package, const ReturnCheck &returnCheck, std::set< TypeDiagnostic > &found)
std::vector< TypeDiagnostic > Check(const PackageSource &package, const std::vector< std::string > &files, const std::optional< ReturnCheck > &returnCheck)
std::map< std::string, std::string > files