From 1a9159daff5fabfb3419a9cf728434d9a8e5d876 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz <johnnymorganz@outlook.com> Date: Thu, 2 Nov 2023 16:14:51 +0000 Subject: [PATCH] Record table type alias property locations (#1046) For table type aliases, records the location of the property in the alias declaration. This is an alternate solution to the particular case noted in #802. Instead of tracking the type alias definition for FTVs, it tracks it for the encompassing property instead. Note that we still don't have positions in the following case: ``` type Func = () -> () ``` Closes #802 (Although not completely...) --- Analysis/include/Luau/Type.h | 3 ++- Analysis/src/Type.cpp | 3 ++- Analysis/src/TypeInfer.cpp | 2 +- tests/TypeInfer.aliases.test.cpp | 23 +++++++++++++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Analysis/include/Luau/Type.h b/Analysis/include/Luau/Type.h index 9b8564fb8..959ec49a8 100644 --- a/Analysis/include/Luau/Type.h +++ b/Analysis/include/Luau/Type.h @@ -374,6 +374,7 @@ struct Property bool deprecated = false; std::string deprecatedSuggestion; std::optional<Location> location = std::nullopt; + std::optional<Location> typeLocation = std::nullopt; Tags tags; std::optional<std::string> documentationSymbol; @@ -381,7 +382,7 @@ struct Property // TODO: Kill all constructors in favor of `Property::rw(TypeId read, TypeId write)` and friends. Property(); Property(TypeId readTy, bool deprecated = false, const std::string& deprecatedSuggestion = "", std::optional<Location> location = std::nullopt, - const Tags& tags = {}, const std::optional<std::string>& documentationSymbol = std::nullopt); + const Tags& tags = {}, const std::optional<std::string>& documentationSymbol = std::nullopt, std::optional<Location> typeLocation = std::nullopt); // DEPRECATED: Should only be called in non-RWP! We assert that the `readTy` is not nullopt. // TODO: Kill once we don't have non-RWP. diff --git a/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index 1859131bf..2f98e85a3 100644 --- a/Analysis/src/Type.cpp +++ b/Analysis/src/Type.cpp @@ -604,10 +604,11 @@ FunctionType::FunctionType(TypeLevel level, Scope* scope, std::vector<TypeId> ge Property::Property() {} Property::Property(TypeId readTy, bool deprecated, const std::string& deprecatedSuggestion, std::optional<Location> location, const Tags& tags, - const std::optional<std::string>& documentationSymbol) + const std::optional<std::string>& documentationSymbol, std::optional<Location> typeLocation) : deprecated(deprecated) , deprecatedSuggestion(deprecatedSuggestion) , location(location) + , typeLocation(typeLocation) , tags(tags) , documentationSymbol(documentationSymbol) , readTy(readTy) diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index a29b1e06e..1ff8b30b9 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -5403,7 +5403,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno std::optional<TableIndexer> tableIndexer; for (const auto& prop : table->props) - props[prop.name.value] = {resolveType(scope, *prop.type)}; + props[prop.name.value] = {resolveType(scope, *prop.type), /* deprecated: */ false, {}, std::nullopt, {}, std::nullopt, prop.location}; if (const auto& indexer = table->indexer) tableIndexer = TableIndexer(resolveType(scope, *indexer->indexType), resolveType(scope, *indexer->resultType)); diff --git a/tests/TypeInfer.aliases.test.cpp b/tests/TypeInfer.aliases.test.cpp index 3f6d90fa9..de273e98b 100644 --- a/tests/TypeInfer.aliases.test.cpp +++ b/tests/TypeInfer.aliases.test.cpp @@ -1037,4 +1037,27 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "alias_expands_to_bare_reference_to_imported_ LUAU_REQUIRE_NO_ERRORS(result); } +TEST_CASE_FIXTURE(Fixture, "table_types_record_the_property_locations") +{ + CheckResult result = check(R"( + type Table = { + create: () -> () + } + + local x: Table + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + auto ty = requireTypeAlias("Table"); + + auto ttv = Luau::get<Luau::TableType>(ty); + REQUIRE(ttv); + + auto propIt = ttv->props.find("create"); + REQUIRE(propIt != ttv->props.end()); + + CHECK_EQ(propIt->second.location, std::nullopt); + CHECK_EQ(propIt->second.typeLocation, Location({2, 12}, {2, 18})); +} + TEST_SUITE_END();