r199123 - [analyzer] Add a CFG node for the allocator call in a C++ 'new' expression.

Jordan Rose jordan_rose at apple.com
Mon Jan 13 10:08:44 PST 2014


Richard^2, just wanted to double-check that it makes sense not to turn this on for the analysis-based warnings at this time.

Jordan


On Jan 13, 2014, at 9:59 , Jordan Rose <jordan_rose at apple.com> wrote:

> Author: jrose
> Date: Mon Jan 13 11:59:19 2014
> New Revision: 199123
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=199123&view=rev
> Log:
> [analyzer] Add a CFG node for the allocator call in a C++ 'new' expression.
> 
> In an expression like "new (a, b) Foo(x, y)", two things happen:
> - Memory is allocated by calling a function named 'operator new'.
> - The memory is initialized using the constructor for 'Foo'.
> 
> Currently the analyzer only models the second event, though it has special
> cases for both the default and placement forms of operator new. This patch
> is the first step towards properly modeling both events: it changes the CFG
> so that the above expression now generates the following elements.
> 
> 1. a
> 2. b
> 3. (CFGNewAllocator)
> 4. x
> 5. y
> 6. Foo::Foo
> 
> The analyzer currently ignores the CFGNewAllocator element, but the next
> step is to treat that as a call like any other.
> 
> The CFGNewAllocator element is not added to the CFG for analysis-based
> warnings, since none of them take advantage of it yet.
> 
> Modified:
>    cfe/trunk/include/clang/Analysis/AnalysisContext.h
>    cfe/trunk/include/clang/Analysis/CFG.h
>    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
>    cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
>    cfe/trunk/lib/Analysis/CFG.cpp
>    cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
>    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
>    cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
>    cfe/trunk/test/Analysis/cfg.cpp
> 
> Modified: cfe/trunk/include/clang/Analysis/AnalysisContext.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/AnalysisContext.h?rev=199123&r1=199122&r2=199123&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Analysis/AnalysisContext.h (original)
> +++ cfe/trunk/include/clang/Analysis/AnalysisContext.h Mon Jan 13 11:59:19 2014
> @@ -409,7 +409,8 @@ public:
>                              bool addInitializers = false,
>                              bool addTemporaryDtors = false,
>                              bool synthesizeBodies = false,
> -                             bool addStaticInitBranches = false);
> +                             bool addStaticInitBranches = false,
> +                             bool addCXXNewAllocator = true);
> 
>   ~AnalysisDeclContextManager();
> 
> 
> Modified: cfe/trunk/include/clang/Analysis/CFG.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CFG.h?rev=199123&r1=199122&r2=199123&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Analysis/CFG.h (original)
> +++ cfe/trunk/include/clang/Analysis/CFG.h Mon Jan 13 11:59:19 2014
> @@ -46,6 +46,7 @@ namespace clang {
>   class ASTContext;
>   class CXXRecordDecl;
>   class CXXDeleteExpr;
> +  class CXXNewExpr;
> 
> /// CFGElement - Represents a top-level expression in a basic block.
> class CFGElement {
> @@ -54,6 +55,7 @@ public:
>     // main kind
>     Statement,
>     Initializer,
> +    NewAllocator,
>     // dtor kind
>     AutomaticObjectDtor,
>     DeleteDtor,
> @@ -71,7 +73,9 @@ protected:
> 
>   CFGElement(Kind kind, const void *Ptr1, const void *Ptr2 = 0)
>     : Data1(const_cast<void*>(Ptr1), ((unsigned) kind) & 0x3),
> -      Data2(const_cast<void*>(Ptr2), (((unsigned) kind) >> 2) & 0x3) {}
> +      Data2(const_cast<void*>(Ptr2), (((unsigned) kind) >> 2) & 0x3) {
> +    assert(getKind() == kind);
> +  }
> 
>   CFGElement() {}
> public:
> @@ -142,6 +146,25 @@ private:
>   }
> };
> 
> +/// CFGNewAllocator - Represents C++ allocator call.
> +class CFGNewAllocator : public CFGElement {
> +public:
> +  explicit CFGNewAllocator(const CXXNewExpr *S)
> +    : CFGElement(NewAllocator, S) {}
> +
> +  // Get the new expression.
> +  const CXXNewExpr *getAllocatorExpr() const {
> +    return static_cast<CXXNewExpr *>(Data1.getPointer());
> +  }
> +
> +private:
> +  friend class CFGElement;
> +  CFGNewAllocator() {}
> +  static bool isKind(const CFGElement &elem) {
> +    return elem.getKind() == NewAllocator;
> +  }
> +};
> +
> /// CFGImplicitDtor - Represents C++ object destructor implicitly generated
> /// by compiler on various occasions.
> class CFGImplicitDtor : public CFGElement {
> @@ -580,6 +603,11 @@ public:
>     Elements.push_back(CFGInitializer(initializer), C);
>   }
> 
> +  void appendNewAllocator(CXXNewExpr *NE,
> +                          BumpVectorContext &C) {
> +    Elements.push_back(CFGNewAllocator(NE), C);
> +  }
> +
>   void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) {
>     Elements.push_back(CFGBaseDtor(BS), C);
>   }
> @@ -638,6 +666,7 @@ public:
>     bool AddImplicitDtors;
>     bool AddTemporaryDtors;
>     bool AddStaticInitBranches;
> +    bool AddCXXNewAllocator;
> 
>     bool alwaysAdd(const Stmt *stmt) const {
>       return alwaysAddMask[stmt->getStmtClass()];
> @@ -659,7 +688,8 @@ public:
>       ,AddInitializers(false)
>       ,AddImplicitDtors(false)
>       ,AddTemporaryDtors(false)
> -      ,AddStaticInitBranches(false) {}
> +      ,AddStaticInitBranches(false)
> +      ,AddCXXNewAllocator(false) {}
>   };
> 
>   /// \brief Provides a custom implementation of the iterator class to have the
> 
> Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=199123&r1=199122&r2=199123&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)
> +++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Mon Jan 13 11:59:19 2014
> @@ -201,7 +201,9 @@ public:
> 
>   void ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNode *Pred);
> 
> -  void ProcessAutomaticObjDtor(const CFGAutomaticObjDtor D, 
> +  void ProcessNewAllocator(const CXXNewExpr *NE, ExplodedNode *Pred);
> +
> +  void ProcessAutomaticObjDtor(const CFGAutomaticObjDtor D,
>                                ExplodedNode *Pred, ExplodedNodeSet &Dst);
>   void ProcessDeleteDtor(const CFGDeleteDtor D,
>                          ExplodedNode *Pred, ExplodedNodeSet &Dst);
> 
> Modified: cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp?rev=199123&r1=199122&r2=199123&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp (original)
> +++ cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp Mon Jan 13 11:59:19 2014
> @@ -68,7 +68,8 @@ AnalysisDeclContextManager::AnalysisDecl
>                                                        bool addInitializers,
>                                                        bool addTemporaryDtors,
>                                                        bool synthesizeBodies,
> -                                                       bool addStaticInitBranch)
> +                                                       bool addStaticInitBranch,
> +                                                       bool addCXXNewAllocator)
>   : SynthesizeBodies(synthesizeBodies)
> {
>   cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG;
> @@ -76,6 +77,7 @@ AnalysisDeclContextManager::AnalysisDecl
>   cfgBuildOptions.AddInitializers = addInitializers;
>   cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors;
>   cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch;
> +  cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator;
> }
> 
> void AnalysisDeclContextManager::clear() {
> 
> Modified: cfe/trunk/lib/Analysis/CFG.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=199123&r1=199122&r2=199123&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Analysis/CFG.cpp (original)
> +++ cfe/trunk/lib/Analysis/CFG.cpp Mon Jan 13 11:59:19 2014
> @@ -363,6 +363,7 @@ private:
>                                       AddStmtChoice asc);
>   CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S);
>   CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc);
> +  CFGBlock *VisitCXXNewExpr(CXXNewExpr *DE, AddStmtChoice asc);
>   CFGBlock *VisitCXXDeleteExpr(CXXDeleteExpr *DE, AddStmtChoice asc);
>   CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S);
>   CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E,
> @@ -459,6 +460,9 @@ private:
>   void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) {
>     B->appendInitializer(I, cfg->getBumpVectorContext());
>   }
> +  void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) {
> +    B->appendNewAllocator(NE, cfg->getBumpVectorContext());
> +  }
>   void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) {
>     B->appendBaseDtor(BS, cfg->getBumpVectorContext());
>   }
> @@ -1122,6 +1126,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, Ad
>     case Stmt::CXXConstructExprClass:
>       return VisitCXXConstructExpr(cast<CXXConstructExpr>(S), asc);
> 
> +    case Stmt::CXXNewExprClass:
> +      return VisitCXXNewExpr(cast<CXXNewExpr>(S), asc);
> +
>     case Stmt::CXXDeleteExprClass:
>       return VisitCXXDeleteExpr(cast<CXXDeleteExpr>(S), asc);
> 
> @@ -3124,6 +3131,22 @@ CFGBlock *CFGBuilder::VisitCXXConstructE
>   return VisitChildren(C);
> }
> 
> +CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE,
> +                                      AddStmtChoice asc) {
> +
> +  autoCreateBlock();
> +  appendStmt(Block, NE);
> +  if (NE->getInitializer())
> +    Block = VisitStmt(NE->getInitializer(), asc);
> +  if (BuildOpts.AddCXXNewAllocator)
> +    appendNewAllocator(Block, NE);
> +  if (NE->isArray())
> +    Block = VisitStmt(NE->getArraySize(), asc);
> +  for (CXXNewExpr::arg_iterator I = NE->placement_arg_begin(),
> +       E = NE->placement_arg_end(); I != E; ++I)
> +    Block = VisitStmt(*I, asc);
> +  return Block;
> +}
> 
> CFGBlock *CFGBuilder::VisitCXXDeleteExpr(CXXDeleteExpr *DE,
>                                          AddStmtChoice asc) {
> @@ -3426,6 +3449,7 @@ CFGImplicitDtor::getDestructorDecl(ASTCo
>   switch (getKind()) {
>     case CFGElement::Statement:
>     case CFGElement::Initializer:
> +    case CFGElement::NewAllocator:
>       llvm_unreachable("getDestructorDecl should only be used with "
>                        "ImplicitDtors");
>     case CFGElement::AutomaticObjectDtor: {
> @@ -3789,6 +3813,11 @@ static void print_elem(raw_ostream &OS,
>     OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";
>     OS << " (Implicit destructor)\n";
> 
> +  } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) {
> +    OS << "CFGNewAllocator(";
> +    if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr())
> +      AllocExpr->getType().print(OS, PrintingPolicy(Helper.getLangOpts()));
> +    OS << ")\n";
>   } else if (Optional<CFGDeleteDtor> DE = E.getAs<CFGDeleteDtor>()) {
>     const CXXRecordDecl *RD = DE->getCXXRecordDecl();
>     if (!RD)
> 
> Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=199123&r1=199122&r2=199123&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
> +++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Mon Jan 13 11:59:19 2014
> @@ -1730,6 +1730,7 @@ AnalysisBasedWarnings::IssueWarnings(sem
>   AC.getCFGBuildOptions().AddInitializers = true;
>   AC.getCFGBuildOptions().AddImplicitDtors = true;
>   AC.getCFGBuildOptions().AddTemporaryDtors = true;
> +  AC.getCFGBuildOptions().AddCXXNewAllocator = false;
> 
>   // Force that certain expressions appear as CFGElements in the CFG.  This
>   // is used to speed up various analyses.
> 
> Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=199123&r1=199122&r2=199123&view=diff
> ==============================================================================
> --- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
> +++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Mon Jan 13 11:59:19 2014
> @@ -286,6 +286,10 @@ void ExprEngine::processCFGElement(const
>     case CFGElement::Initializer:
>       ProcessInitializer(E.castAs<CFGInitializer>().getInitializer(), Pred);
>       return;
> +    case CFGElement::NewAllocator:
> +      ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(),
> +                          Pred);
> +      return;
>     case CFGElement::AutomaticObjectDtor:
>     case CFGElement::DeleteDtor:
>     case CFGElement::BaseDtor:
> @@ -547,6 +551,17 @@ void ExprEngine::ProcessImplicitDtor(con
>   Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
> }
> 
> +void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE,
> +                                     ExplodedNode *Pred) {
> +  //TODO: Implement VisitCXXNewAllocatorCall
> +  ExplodedNodeSet Dst;
> +  NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
> +  const LocationContext *LCtx = Pred->getLocationContext();
> +  PostImplicitCall PP(NE->getOperatorNew(), NE->getLocStart(), LCtx);
> +  Bldr.generateNode(PP, Pred->getState(), Pred);
> +  Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
> +}
> +
> void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
>                                          ExplodedNode *Pred,
>                                          ExplodedNodeSet &Dst) {
> 
> Modified: cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp?rev=199123&r1=199122&r2=199123&view=diff
> ==============================================================================
> --- cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp (original)
> +++ cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp Mon Jan 13 11:59:19 2014
> @@ -571,6 +571,7 @@ getLocationForCaller(const StackFrameCon
>     return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM);
>   }
>   case CFGElement::TemporaryDtor:
> +  case CFGElement::NewAllocator:
>     llvm_unreachable("not yet implemented!");
>   }
> 
> 
> Modified: cfe/trunk/test/Analysis/cfg.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cfg.cpp?rev=199123&r1=199122&r2=199123&view=diff
> ==============================================================================
> --- cfe/trunk/test/Analysis/cfg.cpp (original)
> +++ cfe/trunk/test/Analysis/cfg.cpp Mon Jan 13 11:59:19 2014
> @@ -110,13 +110,14 @@ public:
> // CHECK: [B2 (ENTRY)]
> // CHECK-NEXT:   Succs (1): B1
> // CHECK: [B1]
> -// CHECK-NEXT:   1:  (CXXConstructExpr, class A)
> -// CHECK-NEXT:   2: new A([B1.1])
> -// CHECK-NEXT:   3: A *a = new A();
> -// CHECK-NEXT:   4: a
> -// CHECK-NEXT:   5: [B1.4] (ImplicitCastExpr, LValueToRValue, class A *)
> -// CHECK-NEXT:   6: [B1.5]->~A() (Implicit destructor)
> -// CHECK-NEXT:   7: delete [B1.5]
> +// CHECK-NEXT:   1:  CFGNewAllocator(A *)
> +// CHECK-NEXT:   2:  (CXXConstructExpr, class A)
> +// CHECK-NEXT:   3: new A([B1.2])
> +// CHECK-NEXT:   4: A *a = new A();
> +// CHECK-NEXT:   5: a
> +// CHECK-NEXT:   6: [B1.5] (ImplicitCastExpr, LValueToRValue, class A *)
> +// CHECK-NEXT:   7: [B1.6]->~A() (Implicit destructor)
> +// CHECK-NEXT:   8: delete [B1.6]
> // CHECK-NEXT:   Preds (1): B2
> // CHECK-NEXT:   Succs (1): B0
> // CHECK: [B0 (EXIT)]
> @@ -130,13 +131,14 @@ void test_deletedtor() {
> // CHECK-NEXT:   Succs (1): B1
> // CHECK: [B1]
> // CHECK-NEXT:   1: 5
> -// CHECK-NEXT:   2:  (CXXConstructExpr, class A)
> -// CHECK-NEXT:   3: new A {{\[\[}}B1.1]]
> -// CHECK-NEXT:   4: A *a = new A [5];
> -// CHECK-NEXT:   5: a
> -// CHECK-NEXT:   6: [B1.5] (ImplicitCastExpr, LValueToRValue, class A *)
> -// CHECK-NEXT:   7: [B1.6]->~A() (Implicit destructor)
> -// CHECK-NEXT:   8: delete [] [B1.6]
> +// CHECK-NEXT:   2: CFGNewAllocator(A *)
> +// CHECK-NEXT:   3:  (CXXConstructExpr, class A)
> +// CHECK-NEXT:   4: new A {{\[\[}}B1.1]]
> +// CHECK-NEXT:   5: A *a = new A [5];
> +// CHECK-NEXT:   6: a
> +// CHECK-NEXT:   7: [B1.6] (ImplicitCastExpr, LValueToRValue, class A *)
> +// CHECK-NEXT:   8: [B1.7]->~A() (Implicit destructor)
> +// CHECK-NEXT:   9: delete [] [B1.7]
> // CHECK-NEXT:   Preds (1): B2
> // CHECK-NEXT:   Succs (1): B0
> // CHECK: [B0 (EXIT)]
> @@ -308,4 +310,65 @@ int test_enum_with_extension_default(enu
>     default: x = 4; break;
>   }
>   return x;
> -}
> \ No newline at end of file
> +}
> +
> +
> +// CHECK:  [B1 (ENTRY)]
> +// CHECK-NEXT:  Succs (1): B0
> +// CHECK:  [B0 (EXIT)]
> +// CHECK-NEXT:  Preds (1): B1
> +// CHECK:  [B1 (ENTRY)]
> +// CHECK-NEXT:  Succs (1): B0
> +// CHECK:  [B0 (EXIT)]
> +// CHECK-NEXT:  Preds (1): B1
> +// CHECK:  [B2 (ENTRY)]
> +// CHECK-NEXT:  Succs (1): B1
> +// CHECK:  [B1]
> +// CHECK-NEXT:  1: int buffer[16];
> +// CHECK-NEXT:  2: buffer
> +// CHECK-NEXT:  3: [B1.2] (ImplicitCastExpr, ArrayToPointerDecay, int *)
> +// CHECK-NEXT:  4: [B1.3] (ImplicitCastExpr, BitCast, void *)
> +// CHECK-NEXT:  5: CFGNewAllocator(MyClass *)
> +// CHECK-NEXT:  6:  (CXXConstructExpr, class MyClass)
> +// CHECK-NEXT:  7: new ([B1.4]) MyClass([B1.6])
> +// CHECK-NEXT:  8: MyClass *obj = new (buffer) MyClass();
> +// CHECK-NEXT:  Preds (1): B2
> +// CHECK-NEXT:  Succs (1): B0
> +// CHECK: [B0 (EXIT)]
> +// CHECK-NEXT:  Preds (1): B1
> +
> +extern void* operator new (unsigned long sz, void* v);
> +extern void* operator new[] (unsigned long sz, void* ptr);
> +
> +class MyClass {
> +public:
> +  MyClass() {}
> +  ~MyClass() {}
> +};
> +
> +void test_placement_new() {
> +  int buffer[16];
> +  MyClass* obj = new (buffer) MyClass();
> +}
> +
> +// CHECK:  [B2 (ENTRY)]
> +// CHECK-NEXT:  Succs (1): B1
> +// CHECK: [B1]
> +// CHECK-NEXT:  1: int buffer[16];
> +// CHECK-NEXT:  2: buffer
> +// CHECK-NEXT:  3: [B1.2] (ImplicitCastExpr, ArrayToPointerDecay, int *)
> +// CHECK-NEXT:  4: [B1.3] (ImplicitCastExpr, BitCast, void *)
> +// CHECK-NEXT:  5: 5
> +// CHECK-NEXT:  6: CFGNewAllocator(MyClass *)
> +// CHECK-NEXT:  7:  (CXXConstructExpr, class MyClass)
> +// CHECK-NEXT:  8: new ([B1.4]) MyClass {{\[\[}}B1.5]]
> +// CHECK-NEXT:  9: MyClass *obj = new (buffer) MyClass [5];
> +// CHECK-NEXT:  Preds (1): B2
> +// CHECK-NEXT:  Succs (1): B0
> +// CHECK: [B0 (EXIT)]
> +// CHECK-NEXT:  Preds (1): B1
> +
> +void test_placement_new_array() {
> +  int buffer[16];
> +  MyClass* obj = new (buffer) MyClass[5];
> +}
> 
> 
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits




More information about the cfe-commits mailing list