Only emit comments from the public headers.
Also, move the logic for parsing headers into a single function
invocation, instead of the somewhat complicated state machine that we
needed while using an `AstVisitor`
PiperOrigin-RevId: 424637057
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index cf034be..5fcbea1 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -35,6 +35,7 @@
#include "third_party/llvm/llvm-project/clang/include/clang/AST/RawCommentList.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/RecordLayout.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/Basic/FileManager.h"
#include "third_party/llvm/llvm-project/clang/include/clang/Basic/SourceLocation.h"
#include "third_party/llvm/llvm-project/clang/include/clang/Basic/SourceManager.h"
#include "third_party/llvm/llvm-project/clang/include/clang/Basic/Specifiers.h"
@@ -101,12 +102,57 @@
ImportDeclsFromDeclContext(translation_unit_decl);
- // Emit comments after the last decl
- comment_manager_.FlushComments();
-
EmitIRItems();
}
+std::vector<clang::RawComment*> Importer::ImportFreeComments() {
+ clang::SourceManager& sm = ctx_->getSourceManager();
+
+ // We put all comments into an ordered set in source order. Later we'll remove
+ // the comments that we don't want or that we get by other means.
+ auto source_order = [&sm](const clang::SourceLocation& a,
+ const clang::SourceLocation& b) {
+ return b.isValid() && (a.isInvalid() || sm.isBeforeInTranslationUnit(a, b));
+ };
+ auto ordered_comments = std::map<clang::SourceLocation, clang::RawComment*,
+ decltype(source_order)>(source_order);
+
+ // We start off by getting the comments from all public header files...
+ for (const auto& header : public_header_names_) {
+ if (auto file = sm.getFileManager().getFile(header.IncludePath())) {
+ if (auto comments = ctx_->Comments.getCommentsInFile(
+ sm.getOrCreateFileID(*file, clang::SrcMgr::C_User))) {
+ for (const auto& [_, comment] : *comments) {
+ ordered_comments.insert({comment->getBeginLoc(), comment});
+ }
+ }
+ }
+ }
+
+ // ... and then we remove those that "conflict" with an IR item.
+ for (const auto& [decl, result] : lookup_cache_) {
+ if (result.item()) {
+ // Remove doc comments of imported items.
+ if (auto raw_comment = ctx_->getRawCommentForDeclNoCache(decl)) {
+ ordered_comments.erase(raw_comment->getBeginLoc());
+ }
+ // Remove comments that are within a visited decl.
+ // TODO(forster): We should retain floating comments in decls like
+ // records and namespaces.
+ ordered_comments.erase(ordered_comments.lower_bound(decl->getBeginLoc()),
+ ordered_comments.upper_bound(decl->getEndLoc()));
+ }
+ }
+
+ // Return the remaining comments as a `std::vector`.
+ std::vector<clang::RawComment*> result;
+ result.reserve(ordered_comments.size());
+ for (auto& [_, comment] : ordered_comments) {
+ result.push_back(comment);
+ }
+ return result;
+}
+
void Importer::EmitIRItems() {
using OrderedItem = std::tuple<clang::SourceRange, int, IR::Item>;
clang::SourceManager& sm = ctx_->getSourceManager();
@@ -169,7 +215,7 @@
}
}
- for (auto comment : comment_manager_.comments()) {
+ for (auto comment : ImportFreeComments()) {
items.push_back(std::make_tuple(
comment->getSourceRange(), 0 /* local_order */,
Comment{.text = comment->getFormattedText(sm, sm.getDiagnostics())}));
@@ -184,9 +230,6 @@
void Importer::ImportDeclsFromDeclContext(
const clang::DeclContext* decl_context) {
for (auto decl : decl_context->decls()) {
- // Emit all comments in the current file before the decl
- comment_manager_.TraverseDecl(decl);
-
LookupDecl(decl->getCanonicalDecl());
if (auto* nested_context = clang::dyn_cast<clang::DeclContext>(decl)) {
@@ -798,65 +841,4 @@
}
}
-void Importer::CommentManager::TraverseDecl(clang::Decl* decl) {
- ctx_ = &decl->getASTContext();
-
- // When we go to a new file we flush the comments from the previous file,
- // because source locations won't be comparable by '<' any more.
- clang::FileID file = ctx_->getSourceManager().getFileID(decl->getBeginLoc());
- // For example, we hit an invalid FileID for virtual destructor definitions.
- if (file.isInvalid()) {
- return;
- }
- if (file != current_file_) {
- FlushComments();
- current_file_ = file;
- LoadComments();
- }
-
- // Visit all comments from the current file up to the current decl.
- clang::RawComment* decl_comment = ctx_->getRawCommentForDeclNoCache(decl);
- while (next_comment_ != file_comments_.end() &&
- (*next_comment_)->getBeginLoc() < decl->getBeginLoc()) {
- // Skip the decl's doc comment, which will be emitted as part of the decl.
- if (*next_comment_ != decl_comment) {
- VisitTopLevelComment(*next_comment_);
- }
- ++next_comment_;
- }
-
- // Skip comments that are within the decl, e.g., comments in the body of an
- // inline function
- // TODO(forster): We should retain floating comments within `Record`s
- if (!clang::isa<clang::NamespaceDecl>(decl)) {
- while (next_comment_ != file_comments_.end() &&
- (*next_comment_)->getBeginLoc() < decl->getEndLoc()) {
- ++next_comment_;
- }
- }
-}
-
-void Importer::CommentManager::LoadComments() {
- auto comments = ctx_->Comments.getCommentsInFile(current_file_);
- if (comments) {
- for (auto [_, comment] : *comments) {
- file_comments_.push_back(comment);
- }
- }
- next_comment_ = file_comments_.begin();
-}
-
-void Importer::CommentManager::FlushComments() {
- while (next_comment_ != file_comments_.end()) {
- VisitTopLevelComment(*next_comment_);
- next_comment_++;
- }
- file_comments_.clear();
-}
-
-void Importer::CommentManager::VisitTopLevelComment(
- const clang::RawComment* comment) {
- comments_.push_back(comment);
-}
-
} // namespace rs_bindings_from_cc
diff --git a/rs_bindings_from_cc/importer.h b/rs_bindings_from_cc/importer.h
index 58a77b9..843d228 100644
--- a/rs_bindings_from_cc/importer.h
+++ b/rs_bindings_from_cc/importer.h
@@ -91,6 +91,7 @@
absl::StatusOr<std::vector<Field>> ImportFields(
clang::RecordDecl* record_decl, clang::AccessSpecifier default_access);
+ std::vector<clang::RawComment*> ImportFreeComments();
void EmitIRItems();
std::string GetMangledName(const clang::NamedDecl* named_decl) const;
@@ -151,35 +152,6 @@
std::unique_ptr<clang::MangleContext> mangler_;
absl::flat_hash_map<const clang::Decl*, LookupResult> lookup_cache_;
absl::flat_hash_set<const clang::TypeDecl*> known_type_decls_;
-
- // A component that keeps track of all top-level comments that are not doc
- // comments.
- class CommentManager {
- public:
- // Notify the comment manager that we the visitor is traversing a decl.
- // This will emit IR for all preceding comments.
- void TraverseDecl(clang::Decl* decl);
-
- // Emit IR for the remaining comments after the last decl.
- void FlushComments();
-
- // Return the found comments.
- const std::vector<const clang::RawComment*>& comments() const {
- return comments_;
- }
-
- private:
- void LoadComments();
- void VisitTopLevelComment(const clang::RawComment* comment);
-
- clang::ASTContext* ctx_;
- clang::FileID current_file_;
- std::vector<const clang::RawComment*> file_comments_;
- std::vector<const clang::RawComment*>::iterator next_comment_;
- std::vector<const clang::RawComment*> comments_;
- };
-
- CommentManager comment_manager_;
const devtools_rust::LifetimeAnnotationContext& lifetime_context_;
}; // class AstVisitor
diff --git a/rs_bindings_from_cc/importer_test.cc b/rs_bindings_from_cc/importer_test.cc
index 91e0f92..1da8f50 100644
--- a/rs_bindings_from_cc/importer_test.cc
+++ b/rs_bindings_from_cc/importer_test.cc
@@ -284,7 +284,9 @@
}
TEST(ImporterTest, Noop) {
- ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("// nothing interesting there."));
+ // Nothing interesting there, but also not empty, so that the header gets
+ // generated.
+ ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc(" "));
EXPECT_THAT(ItemsWithoutBuiltins(ir), IsEmpty());
}
diff --git a/rs_bindings_from_cc/test/golden/user_of_imported_type_rs_api.rs b/rs_bindings_from_cc/test/golden/user_of_imported_type_rs_api.rs
index 4c95390..3bf22f0 100644
--- a/rs_bindings_from_cc/test/golden/user_of_imported_type_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/user_of_imported_type_rs_api.rs
@@ -11,8 +11,6 @@
pub type __builtin_ms_va_list = *mut u8;
-// CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_TRIVIAL_TYPE_H_
-
#[inline(always)]
pub fn UsesImportedType(t: trivial_type_cc::Trivial) -> trivial_type_cc::Trivial {
unsafe { crate::detail::__rust_thunk___Z16UsesImportedType7Trivial(t) }
diff --git a/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api.rs b/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api.rs
index a4c0981..b87f48d 100644
--- a/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/user_of_unsupported_rs_api.rs
@@ -9,10 +9,6 @@
pub type __builtin_ms_va_list = *mut u8;
-// namespace ns
-
-// CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_UNSUPPORTED_H_
-
// rs_bindings_from_cc/test/golden/user_of_unsupported.h;l=8
// Error while generating bindings for item 'UseNontrivialCustomType':
// Non-trivial_abi type 'struct NontrivialCustomType' is not supported by value as a parameter