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();