[clang] Respect the [[clang::unsafe_buffer_usage]] attribute for field and constructor initializers (PR #91991)

Dana Jansens via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 10 10:54:50 PDT 2024


================
@@ -3328,3 +3300,63 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
     }
   }
 }
+
+void clang::checkUnsafeBufferUsage(const Decl *D,
+                                   UnsafeBufferUsageHandler &Handler,
+                                   bool EmitSuggestions) {
+#ifndef NDEBUG
+  Handler.clearDebugNotes();
+#endif
+
+  assert(D);
+
+  SmallVector<Stmt *> Stmts;
+
+  // We do not want to visit a Lambda expression defined inside a method
+  // independently. Instead, it should be visited along with the outer method.
+  // FIXME: do we want to do the same thing for `BlockDecl`s?
+  if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) {
+    if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass())
+      return;
+  }
+
+  // Do not emit fixit suggestions for functions declared in an
+  // extern "C" block.
+  if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+    for (FunctionDecl *FReDecl : FD->redecls()) {
+      if (FReDecl->isExternC()) {
+        EmitSuggestions = false;
+        break;
+      }
+    }
+
+    Stmts.push_back(FD->getBody());
+
+    if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
+      for (const CXXCtorInitializer *CI : ID->inits()) {
+        Stmts.push_back(CI->getInit());
+      }
+    }
+  }
+
+  if (const auto *FD = dyn_cast<FieldDecl>(D)) {
----------------
danakj wrote:

> There's no constructor. There's no binary code for constructor. The unsafe function is not invoked from any binary code generated from your example.

Ah yes, this helped me figure out what's going on with the 3 different initialization paths.

If you construct the aggregate like `A a;` or `auto a = A();` then we do see a call to a constructor. So where is it? Now it appears in the AST for the class:
```
| |-CXXRecordDecl <col:1, col:8> col:8 implicit struct HoldsUnsafeMembers
| |-FieldDecl <line:8:5, col:30> col:19 FromField 'UnsafeMembers'
| | `-CXXConstructExpr <col:28, col:30> 'UnsafeMembers' 'void (int)' list
| |   `-IntegerLiteral <col:29> 'int' 3
| |-CXXConstructorDecl <line:7:8> col:8 implicit used constexpr HoldsUnsafeMembers 'void () noexcept(false)' inline default
| | |-CXXCtorInitializer Field 0xbc32670 'FromField' 'UnsafeMembers'
| | | `-CXXDefaultInitExpr <col:8> 'UnsafeMembers' has rewritten init
| | |   `-CXXConstructExpr <line:8:28, col:30> 'UnsafeMembers' 'void (int)' list
| | |     `-IntegerLiteral <col:29> 'int' 3
```

If you construct it as `auto a = A{};` then yes there is no ctor as you say, but it does appear at the call site. So okay I agree there's nothing to do for an aggregate that is never used. And maybe I can get TraverseCXXDefaultInitExpr to work with that information.


https://github.com/llvm/llvm-project/pull/91991


More information about the cfe-commits mailing list