[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)

via cfe-commits cfe-commits at lists.llvm.org
Wed Feb 14 23:35:05 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Ryosuke Niwa (rniwa)

<details>
<summary>Changes</summary>

Allow address-of operator (&), enum constant, and a reference to constant as well as materializing temporqary expression and an expression with cleanups to appear within a trivial function.

---
Full diff: https://github.com/llvm/llvm-project/pull/81829.diff


3 Files Affected:

- (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp (+20-1) 
- (modified) clang/test/Analysis/Checkers/WebKit/mock-types.h (+1) 
- (modified) clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp (+94-1) 


``````````diff
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index bf6f9a64877c64..e8295938a855d7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -285,7 +285,7 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
     // Operator '*' and '!' are allowed as long as the operand is trivial.
-    if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+    if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || UO->getOpcode() == UO_LNot)
       return Visit(UO->getSubExpr());
 
     // Other operators are non-trivial.
@@ -306,6 +306,10 @@ class TrivialFunctionAnalysisVisitor
     if (auto *decl = DRE->getDecl()) {
       if (isa<ParmVarDecl>(decl))
         return true;
+      if (isa<EnumConstantDecl>(decl))
+        return true;
+      if (auto *VD = dyn_cast<VarDecl>(decl))
+        return VD->hasConstantInitialization() && VD->getEvaluatedValue();
     }
     return false;
   }
@@ -377,6 +381,15 @@ class TrivialFunctionAnalysisVisitor
     return Visit(ECE->getSubExpr());
   }
 
+  bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT)
+  {
+    return Visit(VMT->getSubExpr());
+  }
+
+  bool VisitExprWithCleanups(const ExprWithCleanups *EWC) {
+    return Visit(EWC->getSubExpr());
+  }
+
   bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); }
 
   bool VisitInitListExpr(const InitListExpr *ILE) {
@@ -397,6 +410,12 @@ class TrivialFunctionAnalysisVisitor
     return true;
   }
 
+  bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E)
+  {
+    // nullptr is trivial.
+    return true;
+  }
+
   // Constant literal expressions are always trivial
   bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
   bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index cc40487614a83d..d08a997aa8c043 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -19,6 +19,7 @@ template <typename T> struct RefPtr {
   RefPtr(T *t) : t(t) {}
   T *get() { return t; }
   T *operator->() { return t; }
+  const T *operator->() const { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *) { return *this; }
   operator bool() { return t; }
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index 156a2480901bf0..83c4414d1d01aa 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -1,7 +1,6 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
 
 #include "mock-types.h"
-//#include <type_traits>
 
 void WTFBreakpointTrap();
 void WTFCrashWithInfo(int, const char*, const char*, int);
@@ -60,11 +59,86 @@ NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void WTFCrashWithInfo(int line, const char*
     WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason));
 }
 
+enum class Flags : unsigned short {
+  Flag1 = 1 << 0,
+  Flag2 = 1 << 1,
+  Flag3 = 1 << 2,
+};
+
+template<typename E> class OptionSet {
+public:
+  using StorageType = unsigned short;
+
+  static constexpr OptionSet fromRaw(StorageType rawValue) {
+    return OptionSet(static_cast<E>(rawValue), FromRawValue);
+  }
+
+  constexpr OptionSet() = default;
+
+  constexpr OptionSet(E e)
+    : m_storage(static_cast<StorageType>(e)) {
+  }
+
+  constexpr StorageType toRaw() const { return m_storage; }
+
+  constexpr bool isEmpty() const { return !m_storage; }
+
+  constexpr explicit operator bool() const { return !isEmpty(); }
+
+  constexpr bool contains(E option) const { return containsAny(option); }
+  constexpr bool containsAny(OptionSet optionSet) const {
+    return !!(*this & optionSet);
+  }
+
+  constexpr bool containsAll(OptionSet optionSet) const {
+    return (*this & optionSet) == optionSet;
+  }
+
+  constexpr void add(OptionSet optionSet) { m_storage |= optionSet.m_storage; }
+
+  constexpr void remove(OptionSet optionSet)
+  {
+      m_storage &= ~optionSet.m_storage;
+  }
+
+  constexpr void set(OptionSet optionSet, bool value)
+  {
+    if (value)
+      add(optionSet);
+    else
+      remove(optionSet);
+  }
+
+  constexpr friend OptionSet operator|(OptionSet lhs, OptionSet rhs) {
+    return fromRaw(lhs.m_storage | rhs.m_storage);
+  }
+
+  constexpr friend OptionSet operator&(OptionSet lhs, OptionSet rhs) {
+    return fromRaw(lhs.m_storage & rhs.m_storage);
+  }
+
+  constexpr friend OptionSet operator-(OptionSet lhs, OptionSet rhs) {
+    return fromRaw(lhs.m_storage & ~rhs.m_storage);
+  }
+
+  constexpr friend OptionSet operator^(OptionSet lhs, OptionSet rhs) {
+    return fromRaw(lhs.m_storage ^ rhs.m_storage);
+  }
+
+private:
+  enum InitializationTag { FromRawValue };
+  constexpr OptionSet(E e, InitializationTag)
+    : m_storage(static_cast<StorageType>(e)) {
+  }
+  StorageType m_storage { 0 };
+};
+
 class Number {
 public:
   Number(int v) : v(v) { }
   Number(double);
   Number operator+(const Number&);
+  const int& value() const { return v; }
 private:
   int v;
 };
@@ -112,6 +186,19 @@ class RefCounted {
   RefCounted& trivial18() const { RELEASE_ASSERT(this, "this must be not null"); return const_cast<RefCounted&>(*this); }
   void trivial19() const { return; }
 
+  static constexpr unsigned numBits = 4;
+  int trivial20() { return v >> numBits; }
+
+  const int* trivial21() { return number ? &number->value() : nullptr; }
+
+  enum class Enum : unsigned short  {
+      Value1 = 1,
+      Value2 = 2,
+  };
+  bool trivial22() { return enumValue == Enum::Value1; }
+
+  bool trivial23() const { return OptionSet<Flags>::fromRaw(v).contains(Flags::Flag1); }
+
   static RefCounted& singleton() {
     static RefCounted s_RefCounted;
     s_RefCounted.ref();
@@ -170,6 +257,8 @@ class RefCounted {
   }
 
   unsigned v { 0 };
+  Number* number { nullptr };
+  Enum enumValue { Enum::Value1 };
 };
 
 RefCounted* refCountedObj();
@@ -208,6 +297,10 @@ class UnrelatedClass {
     getFieldTrivial().trivial17(); // no-warning
     getFieldTrivial().trivial18(); // no-warning
     getFieldTrivial().trivial19(); // no-warning
+    getFieldTrivial().trivial20(); // no-warning
+    getFieldTrivial().trivial21(); // no-warning
+    getFieldTrivial().trivial22(); // no-warning
+    getFieldTrivial().trivial23(); // no-warning
     RefCounted::singleton().trivial18(); // no-warning
     RefCounted::singleton().someFunction(); // no-warning
 

``````````

</details>


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


More information about the cfe-commits mailing list