Avoid clashes of include_dir in rust-project.json.

In the event of a BUILD.gn file containing multiple crates:
```
# path/to/BUILD.gn
rust_library("foo") {
  sources = [..., "//gen/path/to/foo/foo.rs"]
}

rust_library("bar") {
  sources = [..., "//gen/path/to/bar/bar.rs"]
}
```

It would previously generate a rust-project.json containing an include
dir of `gen/path/to`. This means that rust-analyzer thought that foo
contains bar.rs and vice versa.

Now we generate the include_dir of `gen/path/to/foo` instead.

Change-Id: Iac4f87c42aa09339ebe01425fed6ed5b39c22747
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/19801
Reviewed-by: Takuto Ikuta <tikuta@google.com>
Commit-Queue: Takuto Ikuta <tikuta@google.com>
diff --git a/src/gn/rust_project_writer.cc b/src/gn/rust_project_writer.cc
index 6c9aef8..c5b4590 100644
--- a/src/gn/rust_project_writer.cc
+++ b/src/gn/rust_project_writer.cc
@@ -4,10 +4,10 @@
 
 #include "gn/rust_project_writer.h"
 
-#include <fstream>
+#include <algorithm>
 #include <optional>
-#include <sstream>
-#include <tuple>
+#include <ranges>
+#include <vector>
 
 #include "base/json/string_escape.h"
 #include "base/strings/string_split.h"
@@ -195,9 +195,33 @@
     edition = FindArgValue("--edition", compiler_args);
   }
 
-  auto gen_dir = GetBuildDirForTargetAsOutputFile(target, BuildDirType::GEN);
+  auto source_root = build_settings->root_path();
+  // Generated sources also need to be added to include dirs. However, we can't
+  // naively add the top-level build dir for this target as an include dir.
+  // It's possible to define multiple crates in the same buildfile, and the
+  // default build dir corresponds to the buildfile's containing directory.
+  // Hence they would incorrectly share the same include dir, despite being
+  // separate crates.
+  // Unfortunately this doesn't help if the buildfile author places unrelated
+  // generated sources for multiple crates in the same directory, but that's
+  // impossible to solve anyway.
+  auto gen_dir = GetBuildDirForTargetAsSourceDir(target, BuildDirType::GEN)
+                     .Resolve(source_root);
+  std::vector<SourceDir> include_dirs;
+  for (const SourceFile& source : target->sources()) {
+    if (gen_dir.IsParent(source.Resolve(source_root))) {
+      include_dirs.push_back(source.GetDir());
+    }
+  }
+  // Remove duplicates. We could use a set-like structure, but chances are
+  // there's very few elements.
+  std::ranges::sort(include_dirs, [](const auto& lhs, const auto& rhs) {
+    return lhs.value() < rhs.value();
+  });
+  include_dirs.erase(std::ranges::unique(include_dirs).begin(),
+                     include_dirs.end());
 
-  Crate crate = Crate(crate_root, gen_dir, crate_id, crate_label,
+  Crate crate = Crate(crate_root, include_dirs, crate_id, crate_label,
                       edition.value_or("2015"));
 
   crate.SetCompilerArgs(compiler_args);
@@ -284,16 +308,12 @@
                  << FilePathToUTF8(
                         build_settings->GetFullPath(crate.root().GetDir()))
                  << "\"";
-    auto gen_dir = crate.gen_dir();
-    if (gen_dir.has_value()) {
-      auto gen_dir_path = FilePathToUTF8(
-          build_settings->GetFullPath(gen_dir->AsSourceDir(build_settings)));
-      rust_project << "," NEWLINE << "               \"" << gen_dir_path
-                   << "\"" NEWLINE;
-    } else {
-      rust_project << NEWLINE;
+
+    for (const auto& include_dir : crate.extra_include_dirs()) {
+      auto path = FilePathToUTF8(build_settings->GetFullPath(include_dir));
+      rust_project << "," << NEWLINE << "               \"" << path << "\"";
     }
-    rust_project << "          ]," NEWLINE
+    rust_project << NEWLINE "          ]," NEWLINE
                  << "          \"exclude_dirs\": []" NEWLINE
                  << "      }," NEWLINE;
 
diff --git a/src/gn/rust_project_writer_helpers.h b/src/gn/rust_project_writer_helpers.h
index 3073fd5..4feff94 100644
--- a/src/gn/rust_project_writer_helpers.h
+++ b/src/gn/rust_project_writer_helpers.h
@@ -5,13 +5,9 @@
 #ifndef TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
 #define TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
 
-#include <fstream>
 #include <optional>
-#include <sstream>
 #include <string>
-#include <string_view>
-#include <tuple>
-#include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include "base/containers/flat_map.h"
@@ -35,12 +31,12 @@
 class Crate {
  public:
   Crate(SourceFile root,
-        std::optional<OutputFile> gen_dir,
+        std::vector<SourceDir> extra_include_dirs,
         CrateIndex index,
         std::string label,
         std::string edition)
       : root_(root),
-        gen_dir_(gen_dir),
+        extra_include_dirs_(std::move(extra_include_dirs)),
         index_(index),
         label_(label),
         edition_(edition) {}
@@ -74,8 +70,8 @@
   // Returns the root file for the crate.
   SourceFile& root() { return root_; }
 
-  // Returns the root file for the crate.
-  std::optional<OutputFile>& gen_dir() { return gen_dir_; }
+  // Extra include dirs in addition to the directory containing the crate root.
+  std::vector<SourceDir>& extra_include_dirs() { return extra_include_dirs_; }
 
   // Returns the crate index.
   CrateIndex index() { return index_; }
@@ -111,7 +107,7 @@
 
  private:
   SourceFile root_;
-  std::optional<OutputFile> gen_dir_;
+  std::vector<SourceDir> extra_include_dirs_;
   CrateIndex index_;
   std::string label_;
   std::string edition_;
diff --git a/src/gn/rust_project_writer_helpers_unittest.cc b/src/gn/rust_project_writer_helpers_unittest.cc
index 87ccd83..c37f215 100644
--- a/src/gn/rust_project_writer_helpers_unittest.cc
+++ b/src/gn/rust_project_writer_helpers_unittest.cc
@@ -29,10 +29,11 @@
   std::optional<std::string> sysroot;
 
   CrateList crates;
-  Crate dep = Crate(SourceFile("/root/tortoise/lib.rs"), std::nullopt, 0,
+  Crate dep = Crate(SourceFile("/root/tortoise/lib.rs"), {}, 0,
                     "//tortoise:bar", "2015");
-  Crate target = Crate(SourceFile("/root/hare/lib.rs"),
-                       OutputFile("gendir/hare/"), 1, "//hare:bar", "2015");
+  Crate target =
+      Crate(SourceFile("/root/hare/lib.rs"),
+            {SourceDir("//out/Debug/gendir/hare")}, 1, "//hare:bar", "2015");
   target.AddDependency(0, "tortoise");
   target.AddConfigItem("unix");
   target.AddConfigItem("feature=\"test\"");
diff --git a/src/gn/rust_project_writer_unittest.cc b/src/gn/rust_project_writer_unittest.cc
index f0b806b..8f80824 100644
--- a/src/gn/rust_project_writer_unittest.cc
+++ b/src/gn/rust_project_writer_unittest.cc
@@ -34,6 +34,10 @@
   target.visibility().SetPublic();
   SourceFile lib("//foo/lib.rs");
   target.sources().push_back(lib);
+  target.sources().push_back(
+      SourceFile("//out/Debug/gen/foo/bar/generated.rs"));
+  target.sources().push_back(
+      SourceFile("//out/Debug/gen/foo/bar/generated2.rs"));
   target.source_types_used().Set(SourceFile::SOURCE_RS);
   target.rust_values().set_crate_root(lib);
   target.rust_values().crate_name() = "foo";
@@ -59,7 +63,7 @@
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
       "               \"path/foo/\",\n"
-      "               \"path/out/Debug/gen/foo/\"\n"
+      "               \"path/out/Debug/gen/foo/bar/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -123,8 +127,7 @@
       "      \"label\": \"//tortoise:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"tortoise/\",\n"
-      "               \"out/Debug/gen/tortoise/\"\n"
+      "               \"tortoise/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -142,8 +145,7 @@
       "      \"label\": \"//hare:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"hare/\",\n"
-      "               \"out/Debug/gen/hare/\"\n"
+      "               \"hare/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -221,8 +223,7 @@
       "      \"label\": \"//tortoise:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"tortoise/\",\n"
-      "               \"out/Debug/gen/tortoise/\"\n"
+      "               \"tortoise/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -240,8 +241,7 @@
       "      \"label\": \"//achilles:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"achilles/\",\n"
-      "               \"out/Debug/gen/achilles/\"\n"
+      "               \"achilles/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -259,8 +259,7 @@
       "      \"label\": \"//hare:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"hare/\",\n"
-      "               \"out/Debug/gen/hare/\"\n"
+      "               \"hare/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -358,8 +357,7 @@
       "      \"label\": \"//tortoise:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"tortoise/\",\n"
-      "               \"out/Debug/gen/tortoise/\"\n"
+      "               \"tortoise/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -377,8 +375,7 @@
       "      \"label\": \"//tortoise:macro\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"tortoise/macro/\",\n"
-      "               \"out/Debug/gen/tortoise/\"\n"
+      "               \"tortoise/macro/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -399,8 +396,7 @@
       "      \"label\": \"//hare:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"hare/\",\n"
-      "               \"out/Debug/gen/hare/\"\n"
+      "               \"hare/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -461,8 +457,7 @@
       "      \"label\": \"//foo:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"path/foo/\",\n"
-      "               \"path/out/Debug/gen/foo/\"\n"
+      "               \"path/foo/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -516,8 +511,7 @@
       "      \"label\": \"//foo:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"path/foo/\",\n"
-      "               \"path/out/Debug/gen/foo/\"\n"
+      "               \"path/foo/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -571,8 +565,7 @@
       "      \"label\": \"//foo:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"path/foo/\",\n"
-      "               \"path/out/Debug/gen/foo/\"\n"
+      "               \"path/foo/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"
@@ -627,8 +620,7 @@
       "      \"label\": \"//foo:bar\",\n"
       "      \"source\": {\n"
       "          \"include_dirs\": [\n"
-      "               \"path/foo/\",\n"
-      "               \"path/out/Debug/gen/foo/\"\n"
+      "               \"path/foo/\"\n"
       "          ],\n"
       "          \"exclude_dirs\": []\n"
       "      },\n"