Author: Benedek Kaibas
Date: 2026-06-29T01:25:49+02:00
New Revision: f3a69752ac184360afeb921d157ae08b89c2c091

URL: 
https://github.com/llvm/llvm-project/commit/f3a69752ac184360afeb921d157ae08b89c2c091
DIFF: 
https://github.com/llvm/llvm-project/commit/f3a69752ac184360afeb921d157ae08b89c2c091.diff

LOG: [analyzer] Implement UseAfterLifetimeEnd checker (#205521)

Implemented the UseAfterLifetimEnd checker which is responsible for detecting 
lifetime safety violations involving the [[clang::lifetimebound]] annotation. 
This checker can catch violations in annotated code such as dangling 
pointer/reference bound to local variables that go out of scope. This checker 
is one of the reporting checkers that depend on the LifetimeModeling checker 
#205951. To detect dangling sources the checker queries the state at function 
exit points through the checkEndFunction callback. This checker does not handle 
lifetime issues where the code is unannotated.

Detailed work history of this checker can be found here: #200145

Co-authored-by: Balázs Benics <[email protected]>

Added: 
    clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
    clang/test/Analysis/debug-lifetime-bound.cpp
    clang/test/Analysis/lifetime-bound.cpp

Modified: 
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index d02c3195069f3..b565481d28fdb 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -788,6 +788,11 @@ def SmartPtrChecker: Checker<"SmartPtr">,
   Dependencies<[SmartPtrModeling]>,
   Documentation<HasDocumentation>;
 
+def UseAfterLifetimeEnd : Checker<"UseAfterLifetimeEnd">,
+  HelpText<"Check for uses of references or pointers that "
+           "outlive their bound object">,
+  Documentation<NotDocumented>;
+
 } // end: "alpha.cplusplus"
 
 
//===----------------------------------------------------------------------===//
@@ -1576,6 +1581,12 @@ def CheckerDocumentationChecker : 
Checker<"CheckerDocumentation">,
   HelpText<"Defines an empty checker callback for all possible handlers.">,
   Documentation<NotDocumented>;
 
+def DebugUseAfterLifetimeEnd : Checker<"DebugUseAfterLifetimeEnd">,
+  HelpText<"Prints the bindings recorded by the UseAfterLifetimeEnd checker. "
+           "Use with clang_analyzer_dumpLifetimeOriginsOf().">,
+  WeakDependencies<[UseAfterLifetimeEnd]>,
+  Documentation<NotDocumented>;
+
 } // end "debug"
 
 

diff  --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt 
b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 8a0621077b977..46c0c36fda736 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -126,6 +126,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   UninitializedObject/UninitializedPointee.cpp
   UnixAPIChecker.cpp
   UnreachableCodeChecker.cpp
+  UseAfterLifetimeEnd.cpp
   VforkChecker.cpp
   VLASizeChecker.cpp
   VAListChecker.cpp
@@ -146,6 +147,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   clangAST
   clangASTMatchers
   clangAnalysis
+  clangAnalysisLifetimeSafety
   clangBasic
   clangLex
   clangStaticAnalyzerCore

diff  --git a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp 
b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
new file mode 100644
index 0000000000000..db776f1c1c49d
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
@@ -0,0 +1,258 @@
+#include "clang/AST/Attr.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *)
+REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet)
+
+namespace {
+class UseAfterLifetimeEnd
+    : public Checker<check::PostCall, check::EndFunction, check::DeadSymbols> {
+public:
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+                  const char *Sep) const override;
+  void reportDanglingSource(const MemRegion *Region, ExplodedNode *N,
+                            CheckerContext &C) const;
+  void checkReturnedBorrower(SVal Val, ProgramStateRef State,
+                             CheckerContext &C) const;
+  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+  const BugType BugMsg{this, "UseAfterLifetimeEnd", "LifetimeBound"};
+};
+
+} // namespace
+
+static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal,
+                                  const MemRegion *Source) {
+  LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
+
+  const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
+  LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet();
+  Set = F.add(Set, Source);
+  State = State->set<LifetimeBoundMap>(RetVal, Set);
+  return State;
+}
+
+void UseAfterLifetimeEnd::checkPostCall(const CallEvent &Call,
+                                        CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  const auto *FC = dyn_cast<AnyFunctionCall>(&Call);
+  if (!FC)
+    return;
+
+  const FunctionDecl *FD = FC->getDecl();
+  if (!FD)
+    return;
+
+  SVal RetVal = Call.getReturnValue();
+
+  for (const ParmVarDecl *PVD : FD->parameters()) {
+    if (PVD->hasAttr<LifetimeBoundAttr>()) {
+      unsigned Idx = PVD->getFunctionScopeIndex();
+      SVal Arg = Call.getArgSVal(Idx);
+      if (const MemRegion *ArgValRegion = Arg.getAsRegion())
+        State = bindValues(State, RetVal, ArgValRegion);
+    }
+  }
+
+  if (const auto *IC = dyn_cast<CXXInstanceCall>(&Call)) {
+    if (lifetimes::implicitObjectParamIsLifetimeBound(FD)) {
+      if (const MemRegion *AttrRegion = IC->getCXXThisVal().getAsRegion()) {
+        State = bindValues(State, RetVal, AttrRegion);
+      }
+    }
+  }
+  C.addTransition(State);
+}
+
+static bool hasDanglingSource(const MemRegion *Source, ProgramStateRef State,
+                              CheckerContext &C) {
+  // FIXME: The checker currently handles stack-region sources. Other
+  // region kinds require separate methodology. For example, heap
+  // regions do not go out of scope at the end of a stack frame, so
+  // in order to detect those type of dangling sources the function
+  // needs to be expanded to an event-driven approach as well.
+  if (const auto *StackSpace =
+          Source->getMemorySpaceAs<StackSpaceRegion>(State)) {
+    const StackFrame *SF = StackSpace->getStackFrame();
+    const StackFrame *CurrentSF = C.getStackFrame();
+    if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
+      return true;
+  }
+  return false;
+}
+
+void UseAfterLifetimeEnd::checkReturnedBorrower(SVal Val, ProgramStateRef 
State,
+                                                CheckerContext &C) const {
+  if (auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
+    ExplodedNode *N = nullptr;
+    for (const MemRegion *Region : *SourceSet) {
+      if (hasDanglingSource(Region, State, C)) {
+        if (!N)
+          N = C.generateNonFatalErrorNode();
+        if (!N)
+          return;
+        reportDanglingSource(Region, N, C);
+      }
+    }
+  }
+}
+
+void UseAfterLifetimeEnd::checkEndFunction(const ReturnStmt *RS,
+                                           CheckerContext &C) const {
+  if (!RS)
+    return;
+
+  ProgramStateRef State = C.getState();
+  auto LBMap = State->get<LifetimeBoundMap>();
+
+  if (LBMap.isEmpty())
+    return;
+
+  const Expr *RetExpr = RS->getRetValue();
+  if (!RetExpr)
+    return;
+
+  RetExpr = RetExpr->IgnoreParens();
+  SVal RetVal = C.getSVal(RetExpr);
+  checkReturnedBorrower(RetVal, State, C);
+}
+
+void UseAfterLifetimeEnd::reportDanglingSource(const MemRegion *Region,
+                                               ExplodedNode *N,
+                                               CheckerContext &C) const {
+  auto BR = std::make_unique<PathSensitiveBugReport>(
+      BugMsg,
+      (llvm::Twine("Returning value bound to '") + Region->getString() +
+       "' that will go out of scope"),
+      N);
+  C.emitReport(std::move(BR));
+}
+
+void UseAfterLifetimeEnd::checkDeadSymbols(SymbolReaper &SymReaper,
+                                           CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+  LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
+
+  for (SVal Val : llvm::make_first_range(LBMap)) {
+    if (const MemRegion *ValRegion = Val.getAsRegion()) {
+      if (!SymReaper.isLiveRegion(ValRegion))
+        State = State->remove<LifetimeBoundMap>(Val);
+    } else if (SymbolRef ValRef =
+                   Val.getAsSymbol(/*IncludeBaseRegions=*/true)) {
+      if (!SymReaper.isLive(ValRef))
+        State = State->remove<LifetimeBoundMap>(Val);
+    }
+  }
+
+  C.addTransition(State);
+}
+
+void UseAfterLifetimeEnd::printState(raw_ostream &Out, ProgramStateRef State,
+                                     const char *NL, const char *Sep) const {
+  auto LBMap = State->get<LifetimeBoundMap>();
+
+  if (LBMap.isEmpty())
+    return;
+
+  Out << Sep << "LifetimeBound bindings:" << NL;
+  for (auto &&[OriginSym, SourceSet] : LBMap) {
+    for (const auto *Region : SourceSet)
+      Out << " Origin " << OriginSym << " contains Loan " << Region << NL;
+  }
+}
+
+namespace {
+class DebugUseAfterLifetimeEnd : public Checker<eval::Call> {
+public:
+  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+  void analyzerDumpLifetimeOriginsOf(const CallEvent &Call,
+                                     CheckerContext &C) const;
+
+  const BugType BugMsg{this, "DebugUseAfterLifetimeEnd",
+                       "DebugUseAfterLifetimeEnd"};
+  using FnCheck = void (DebugUseAfterLifetimeEnd::*)(const CallEvent &Call,
+                                                     CheckerContext &C) const;
+
+  const CallDescriptionMap<FnCheck> Callbacks = {
+      {{CDM::SimpleFunc, {"clang_analyzer_dumpLifetimeOriginsOf"}},
+       &DebugUseAfterLifetimeEnd::analyzerDumpLifetimeOriginsOf},
+  };
+};
+
+} // namespace
+
+bool DebugUseAfterLifetimeEnd::evalCall(const CallEvent &Call,
+                                        CheckerContext &C) const {
+  const auto *CE = dyn_cast_if_present<CallExpr>(Call.getOriginExpr());
+  if (!CE)
+    return false;
+
+  const FnCheck *Handler = Callbacks.lookup(Call);
+  if (!Handler)
+    return false;
+
+  (this->*(*Handler))(Call, C);
+  return true;
+}
+
+void DebugUseAfterLifetimeEnd::analyzerDumpLifetimeOriginsOf(
+    const CallEvent &Call, CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  if (Call.getNumArgs() != 1) {
+    if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+      auto BR = std::make_unique<PathSensitiveBugReport>(
+          BugMsg,
+          "clang_analyzer_dumpLifetimeOriginsOf requires exactly 1 argument",
+          N);
+      C.emitReport(std::move(BR));
+    }
+    return;
+  }
+
+  SVal ArgSVal = Call.getArgSVal(0);
+  const LifetimeSourceSet *SourceSet = State->get<LifetimeBoundMap>(ArgSVal);
+
+  if (!SourceSet)
+    return;
+
+  if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+    llvm::SmallVector<std::string> RegionNames =
+        to_vector(map_range(llvm::make_pointee_range(*SourceSet),
+                            std::mem_fn(&MemRegion::getString)));
+    llvm::sort(RegionNames);
+
+    llvm::SmallString<128> Str;
+    llvm::raw_svector_ostream OS(Str);
+    OS << " Origin " << ArgSVal << " bound to ";
+    llvm::interleaveComma(RegionNames, OS);
+    C.emitReport(std::make_unique<PathSensitiveBugReport>(BugMsg, OS.str(), 
N));
+  }
+}
+
+void ento::registerUseAfterLifetimeEnd(CheckerManager &Mgr) {
+  Mgr.registerChecker<UseAfterLifetimeEnd>();
+}
+
+bool ento::shouldRegisterUseAfterLifetimeEnd(const CheckerManager &Mgr) {
+  return true;
+}
+
+void ento::registerDebugUseAfterLifetimeEnd(CheckerManager &Mgr) {
+  Mgr.registerChecker<DebugUseAfterLifetimeEnd>();
+}
+
+bool ento::shouldRegisterDebugUseAfterLifetimeEnd(const CheckerManager &Mgr) {
+  return true;
+}

diff  --git a/clang/test/Analysis/debug-lifetime-bound.cpp 
b/clang/test/Analysis/debug-lifetime-bound.cpp
new file mode 100644
index 0000000000000..8ef704195dcc6
--- /dev/null
+++ b/clang/test/Analysis/debug-lifetime-bound.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugUseAfterLifetimeEnd
 -verify %s
+
+// expected-no-diagnostics
+
+void clang_analyzer_dumpLifetimeOriginsOf(int);
+
+void test() {
+  int x = 5;
+  clang_analyzer_dumpLifetimeOriginsOf(x); // no-warning
+}
+

diff  --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
new file mode 100644
index 0000000000000..e99299b5d03bf
--- /dev/null
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -0,0 +1,219 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugUseAfterLifetimeEnd
 \
+// RUN:   -analyzer-config cfg-lifetime=true -verify %s
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugUseAfterLifetimeEnd
 \
+// RUN:   -analyzer-config c++-container-inlining=false -analyzer-config 
cfg-lifetime=true -verify %s
+
+struct A {};
+
+void clang_analyzer_dumpLifetimeOriginsOf(int*);
+void clang_analyzer_dumpLifetimeOriginsOf(int&);
+void clang_analyzer_dumpLifetimeOriginsOf(A*);
+void clang_analyzer_dumpLifetimeOriginsOf(A&);
+
+// These are the cases when the result of function calls are MemRegions.
+
+// Ref type parameter annotated case.
+struct X {
+  int &choose(int &a [[clang::lifetimebound]]) { return a; }
+};
+
+void caller() {
+  int v = 0;
+  X obj;
+  int &r = obj.choose(v);
+  clang_analyzer_dumpLifetimeOriginsOf(r); // expected-warning {{Origin &v 
bound to v}}
+}
+
+// Obj ref type function return annotated case.
+struct Y {
+  A a;
+  A &getA() [[clang::lifetimebound]] { return a; }
+};
+
+void caller_two() {
+  // Return statement is annotated case.
+  Y y;
+  A &f = y.getA();
+  clang_analyzer_dumpLifetimeOriginsOf(f); // expected-warning {{Origin &y.a 
bound to y}}
+}
+
+// Obj ptr type function return annotated case.
+struct Z {
+  A a;
+  A *getA() [[clang::lifetimebound]] { return &a; }
+};
+
+void caller_three() {
+  Z z;
+  A *func = z.getA();
+  clang_analyzer_dumpLifetimeOriginsOf(func); // expected-warning {{Origin 
&z.a bound to z}}
+}
+
+// Free function with annotated param and ref return.
+int &foo(int &num [[clang::lifetimebound]]) { return num; }
+
+void caller_four() {
+  int num = 5;
+  int &s = foo(num);
+  clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &num 
bound to num}}
+}
+
+// Free function with annotated param and ptr return.
+int *boo(int *num [[clang::lifetimebound]]) { return num; }
+
+void caller_five() {
+  int n = 55;
+  int *n_ptr = &n;
+  int *s = boo(n_ptr);
+
+  clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &n 
bound to n}}
+}
+
+// Free function with both annotated and non-annotated parameters.
+int &fn(int &f, int &s [[clang::lifetimebound]]) { return s; }
+
+void caller_six() {
+  int even = 50;
+  int odd = 55;
+  int &s = fn(even, odd);
+
+  clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &odd 
bound to odd}}
+}
+
+
+
+// These are the cases when the result of function calls are SymbolRefs.
+
+// Function returns ptr and has an annotated parameter.
+int *foo(int *n [[clang::lifetimebound]]);
+
+void caller_seven() {
+  int y = 15;
+  int *y_ptr = &y;
+  auto *bind = foo(y_ptr);
+
+  clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to y}}
+}
+
+// Function returns a reference and has an annotated parameter.
+int &func(int &some_number [[clang::lifetimebound]]);
+
+void caller_eight() {
+  int f = 15;
+  auto &bind = func(f);
+
+  clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to f}}
+}
+
+// Function returns a reference and has two annotated parameters.
+int &f(int &a [[clang::lifetimebound]], int &b [[clang::lifetimebound]]);
+
+void caller_nine() {
+  int first_num = 1;
+  int second_num = 2;
+  int &numbers = f(first_num, second_num);
+
+  clang_analyzer_dumpLifetimeOriginsOf(numbers); // expected-warning-re 
{{Origin &SymRegion{{.*}} bound to first_num, second_num}}
+}
+
+struct View {
+  int *p;
+};
+View makeView(int &x [[clang::lifetimebound]]);
+
+void clang_analyzer_dumpLifetimeOriginsOf(View);
+
+void caller_view() {
+  int v = 42;
+  View w = makeView(v);
+  // FIXME: Currently none of the maps cover LazyCompoundVal.
+  clang_analyzer_dumpLifetimeOriginsOf(w); // no-warning
+}
+
+
+
+// These are the test cases for testing the correctness of the emitted warning 
from the UseAfterLifetimeEnd checker.
+
+// Return value bound to annotated param cases.
+int *test_func(int *p [[clang::lifetimebound]]);
+
+
+int *direct_return() {
+  int i = 5;
+  return test_func(&i);
+  // expected-warning@-1 {{Returning value bound to 'i' that will go out of 
scope}}
+  // expected-warning@-2 {{address of stack memory associated with local 
variable 'i' returned}}
+}
+
+int *variable_return() {
+  int y = 5;
+  int *p = test_func(&y);
+  return p; // expected-warning {{Returning value bound to 'y' that will go 
out of scope}}
+}
+
+int *borrow_from_caller(int *b [[clang::lifetimebound]]) {
+  return test_func(b); // no-warning
+}
+
+void no_return() {
+  int i = 5;
+  int *p = test_func(&i);
+  (void)p; // no-warning
+}
+
+int *g() {
+  int i = 5;
+  int *p = test_func(&i);
+  (void)p;
+  return nullptr; // no-warning
+}
+
+int &multi_param_test_ref(int &a [[clang::lifetimebound]], int &b 
[[clang::lifetimebound]]);
+
+// Return value bound to annotated parameters (two dangling sources).
+int &dangling_sources_ref() {
+  int x = 1, y = 2;
+  return multi_param_test_ref(x, y);
+  // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
+  // expected-warning@-2 {{Returning value bound to 'y' that will go out of 
scope}}
+  // expected-warning@-3 {{reference to stack memory associated with local 
variable 'x' returned}}
+  // expected-warning@-4 {{reference to stack memory associated with local 
variable 'y' returned}}
+}
+
+// Return value bound to annotated parameters (no dangling sources).
+int &no_dangling_sources_ref(int &a [[clang::lifetimebound]], int &b 
[[clang::lifetimebound]]) {
+  return multi_param_test_ref(a, b); // no-warning
+}
+
+// Return value bound to annotated parameters (one dangling source).
+int &one_dangling_source_ref(int &a [[clang::lifetimebound]]) {
+  int x = 1;
+  return multi_param_test_ref(a, x);
+  // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
+  // expected-warning@-2 {{reference to stack memory associated with local 
variable 'x' returned}}
+}
+
+int *multi_param_test_ptr(int *a [[clang::lifetimebound]], int *b 
[[clang::lifetimebound]]);
+
+// Return value bound to annotated parameters (two dangling sources).
+int *dangling_sources_ptr() {
+  int x = 1, y = 2;
+  int *x_ptr = &x;
+  int *y_ptr = &y;
+  return multi_param_test_ptr(x_ptr, y_ptr);
+  // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
+  // expected-warning@-2 {{Returning value bound to 'y' that will go out of 
scope}}
+}
+
+// Return value bound to annotated parameters (no dangling sources).
+int *no_dangling_sources_ptr(int *a [[clang::lifetimebound]], int *b 
[[clang::lifetimebound]]) {
+  return multi_param_test_ptr(a, b); // no-warning
+}
+
+// Return value bound to annotated parameters (one dangling source).
+int *one_dangling_source_ptr(int *a [[clang::lifetimebound]]) {
+  int x = 1;
+  int *x_ptr = &x;
+  return multi_param_test_ptr(a, x_ptr); // expected-warning {{Returning value 
bound to 'x' that will go out of scope}}
+}
+


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to