Don't propagate tooling directives from doc comments into Rust bindings.
Doc comments should exclude tooling directives like `// NOLINTNEXTLINE`.
PiperOrigin-RevId: 424916890
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 05f3016..313b148 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -24,6 +24,7 @@
#include "third_party/absl/status/statusor.h"
#include "third_party/absl/strings/cord.h"
#include "third_party/absl/strings/str_cat.h"
+#include "third_party/absl/strings/str_join.h"
#include "third_party/absl/strings/string_view.h"
#include "third_party/absl/strings/substitute.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/ASTContext.h"
@@ -41,6 +42,7 @@
#include "third_party/llvm/llvm-project/clang/include/clang/Sema/Sema.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/Optional.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Casting.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Regex.h"
#include "util/gtl/flat_map.h"
namespace rs_bindings_from_cc {
@@ -596,6 +598,16 @@
}
}
+static bool ShouldKeepCommentLine(absl::string_view line) {
+ // Based on https://clang.llvm.org/extra/clang-tidy/:
+ llvm::Regex patterns_to_ignore(
+ "^[[:space:]/]*" // Whitespace, or extra //
+ "(NOLINT|NOLINTNEXTLINE|NOLINTBEGIN|NOLINTEND)"
+ "(\\([^)[:space:]]*\\)?)?" // Optional (...)
+ "[[:space:]]*$"); // Whitespace
+ return !patterns_to_ignore.match(line);
+}
+
std::optional<std::string> Importer::GetComment(const clang::Decl* decl) const {
// This does currently not distinguish between different types of comments.
// In general it is not possible in C++ to reliably only extract doc comments.
@@ -606,9 +618,15 @@
if (raw_comment == nullptr) {
return {};
- } else {
- return raw_comment->getFormattedText(sm, sm.getDiagnostics());
}
+
+ std::string raw_comment_text =
+ raw_comment->getFormattedText(sm, sm.getDiagnostics());
+ std::string cleaned_comment_text = absl::StrJoin(
+ absl::StrSplit(raw_comment_text, '\n', ShouldKeepCommentLine), "\n");
+ return cleaned_comment_text.empty()
+ ? std::nullopt
+ : std::optional<std::string>(std::move(cleaned_comment_text));
}
SourceLoc Importer::ConvertSourceLocation(clang::SourceLocation loc) const {
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index e116103..83c8004 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -362,6 +362,56 @@
}
#[test]
+fn test_doc_comment_vs_tooling_directives() -> Result<()> {
+ let ir = ir_from_cc(
+ r#" // Doc comment for `f1`
+ // NOLINTNEXTLINE(google3-readability-pass-trivial-by-value)
+ void f1();
+
+ // Doc comment for `f2`
+ // // NOLINT
+ void f2();
+
+ // // NOLINT
+ static void f3();
+
+ // Mid-sentence usage: [...] this is why we need NOLINT / virtual [...].
+ void f4();
+
+ // No closing paren still suppresses
+ // NOLINTNEXTLINE(google3-readability
+ void f5();
+
+ // Multiple, comma-separated directives listed in parens
+ // NOLINTNEXTLINE(foo,bar)
+ void f6();
+ "#,
+ )?;
+
+ let comments: HashMap<&str, Option<&str>> = ir
+ .functions()
+ .map(|f| {
+ if let UnqualifiedIdentifier::Identifier(id) = &f.name {
+ (id.identifier.as_str(), f.doc_comment.as_deref())
+ } else {
+ panic!("No constructors/destructors expected in this test.")
+ }
+ })
+ .collect();
+
+ assert_eq!(comments["f1"], Some("Doc comment for `f1`"));
+ assert_eq!(comments["f2"], Some("Doc comment for `f2`"));
+ assert_eq!(comments["f3"], None);
+ assert_eq!(
+ comments["f4"],
+ Some("Mid-sentence usage: [...] this is why we need NOLINT / virtual [...].")
+ );
+ assert_eq!(comments["f5"], Some("No closing paren still suppresses"));
+ assert_eq!(comments["f6"], Some("Multiple, comma-separated directives listed in parens"));
+ Ok(())
+}
+
+#[test]
fn test_type_conversion() -> Result<()> {
// TODO(mboehme): Add tests for the corresponding versions of the types in
// the `std` namespace. We currently can't do this because we can't include
diff --git a/rs_bindings_from_cc/test/golden/doc_comment_rs_api.rs b/rs_bindings_from_cc/test/golden/doc_comment_rs_api.rs
index 7ec574c..ad627d5 100644
--- a/rs_bindings_from_cc/test/golden/doc_comment_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/doc_comment_rs_api.rs
@@ -44,7 +44,6 @@
/// An implicit conversion constructor which will get translated into `impl
/// From<int> for DocCommentSlashes`.
-/// NOLINTNEXTLINE(google-explicit-constructor)
impl From<i32> for DocCommentSlashes {
#[inline(always)]
fn from(__param_0: i32) -> Self {