[clang] [clang] Migrate away from PointerUnion::dyn_cast (NFC) (PR #225022)
Kazu Hirata via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 21 00:20:53 PDT 2026
https://github.com/kazutakahirata created https://github.com/llvm/llvm-project/pull/225022
Note that PointerUnion::dyn_cast has been soft deprecated in
PointerUnion.h:
// FIXME: Replace the uses of is(), get() and dyn_cast() with
// isa<T>, cast<T> and the llvm::dyn_cast<T>
Literal migration would result in dyn_cast_if_present (see the
definition of PointerUnion::dyn_cast), but this patch uses dyn_cast
where the operands are known to be nonnull.
The operands are known to be nonnull in one of three ways:
- Preceding checks: A null check, assertion, or dereference a few
lines above guarantees the PointerUnion is nonnull.
- Downstream code: The else or fallthrough branch unconditionally
calls cast<...>, dereferences the pointer, or reaches
llvm_unreachable.
- By construction: In DeclContextInternals.h, FileEntry.h,
FileIndexRecord.cpp, and LifetimeSafety/Checker.cpp, only nonnull
pointers are ever stored in the PointerUnion.
Assisted-by: Antigravity
>From dbce047d013c096dca229aebac23607dd12c4319 Mon Sep 17 00:00:00 2001
From: Kazu Hirata <kazu at google.com>
Date: Sun, 20 Sep 2026 17:00:10 -0700
Subject: [PATCH] [clang] Migrate away from PointerUnion::dyn_cast (NFC)
Note that PointerUnion::dyn_cast has been soft deprecated in
PointerUnion.h:
// FIXME: Replace the uses of is(), get() and dyn_cast() with
// isa<T>, cast<T> and the llvm::dyn_cast<T>
Literal migration would result in dyn_cast_if_present (see the
definition of PointerUnion::dyn_cast), but this patch uses dyn_cast
where the operands are known to be nonnull.
The operands are known to be nonnull in one of three ways:
- Preceding checks: A null check, assertion, or dereference a few
lines above guarantees the PointerUnion is nonnull.
- Downstream code: The else or fallthrough branch unconditionally
calls cast<...>, dereferences the pointer, or reaches
llvm_unreachable.
- By construction: In DeclContextInternals.h, FileEntry.h,
FileIndexRecord.cpp, and LifetimeSafety/Checker.cpp, only nonnull
pointers are ever stored in the PointerUnion.
Assisted-by: Antigravity
---
.../include/clang/AST/DeclContextInternals.h | 8 ++++----
clang/include/clang/AST/DeclTemplate.h | 4 ++--
clang/include/clang/Basic/FileEntry.h | 2 +-
clang/lib/AST/ASTImporter.cpp | 7 +++----
clang/lib/ASTMatchers/ASTMatchFinder.cpp | 2 +-
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 20 +++++++++----------
.../Analysis/LifetimeSafety/LiveOrigins.cpp | 4 ++--
clang/lib/Index/FileIndexRecord.cpp | 2 +-
clang/lib/Sema/SemaConcept.cpp | 3 +--
clang/lib/Sema/SemaSYCL.cpp | 4 ++--
clang/lib/Sema/SemaTemplateInstantiate.cpp | 2 +-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 4 ++--
clang/lib/Sema/SemaTemplateVariadic.cpp | 2 +-
clang/lib/Serialization/ASTReaderStmt.cpp | 2 +-
clang/lib/Serialization/ASTWriterDecl.cpp | 4 ++--
.../StaticAnalyzer/Core/BugSuppression.cpp | 2 +-
16 files changed, 35 insertions(+), 37 deletions(-)
diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h
index d87d8e8a663fa..0412ed8594023 100644
--- a/clang/include/clang/AST/DeclContextInternals.h
+++ b/clang/include/clang/AST/DeclContextInternals.h
@@ -56,13 +56,13 @@ class StoredDeclsList {
if (!ShouldErase(*DeclListNode::iterator(List))) {
NewLast = NewTail;
*NewTail = List;
- if (auto *Node = List.dyn_cast<DeclListNode*>()) {
+ if (auto *Node = dyn_cast<DeclListNode *>(List)) {
NewTail = &Node->Rest;
List = Node->Rest;
} else {
break;
}
- } else if (DeclListNode *N = List.dyn_cast<DeclListNode*>()) {
+ } else if (DeclListNode *N = dyn_cast<DeclListNode *>(List)) {
List = N->Rest;
C.DeallocateDeclListNode(N);
} else {
@@ -111,7 +111,7 @@ class StoredDeclsList {
// If this is a list-form, free the list.
ASTContext &C = getASTContext();
Decls List = Data.getPointer();
- while (DeclListNode *ToDealloc = List.dyn_cast<DeclListNode *>()) {
+ while (DeclListNode *ToDealloc = dyn_cast<DeclListNode *>(List)) {
List = ToDealloc->Rest;
C.DeallocateDeclListNode(ToDealloc);
}
@@ -290,7 +290,7 @@ class StoredDeclsList {
}
while (true) {
- if (auto *Node = D.dyn_cast<DeclListNode*>()) {
+ if (auto *Node = dyn_cast<DeclListNode *>(D)) {
llvm::errs() << '[' << Node->D << "] -> ";
D = Node->Rest;
} else {
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 0d110bc3e6306..d32b044ef40e2 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3470,9 +3470,9 @@ class CXXExpansionStmtDecl : public Decl, public DeclContext {
};
inline NamedDecl *getAsNamedDecl(TemplateParameter P) {
- if (auto *PD = P.dyn_cast<TemplateTypeParmDecl *>())
+ if (auto *PD = dyn_cast<TemplateTypeParmDecl *>(P))
return PD;
- if (auto *PD = P.dyn_cast<NonTypeTemplateParmDecl *>())
+ if (auto *PD = dyn_cast<NonTypeTemplateParmDecl *>(P))
return PD;
return cast<TemplateTemplateParmDecl *>(P);
}
diff --git a/clang/include/clang/Basic/FileEntry.h b/clang/include/clang/Basic/FileEntry.h
index b22b6c00bb6ce..2b1b175aa5ce2 100644
--- a/clang/include/clang/Basic/FileEntry.h
+++ b/clang/include/clang/Basic/FileEntry.h
@@ -169,7 +169,7 @@ class FileEntryRef {
/// Retrieve the base MapEntry after redirects.
const MapEntry &getBaseMapEntry() const {
const MapEntry *Base = ME;
- while (const auto *Next = Base->second->V.dyn_cast<const MapEntry *>())
+ while (const auto *Next = dyn_cast<const MapEntry *>(Base->second->V))
Base = Next;
return *Base;
}
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 22c3af47f451f..bec73d820d009 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1079,19 +1079,18 @@ Error ASTNodeImporter::ImportConstraintSatisfaction(
ToSat.ContainsErrors = FromSat.ContainsErrors;
if (!ToSat.IsSatisfied) {
for (auto Record = FromSat.begin(); Record != FromSat.end(); ++Record) {
- if (const Expr *E = Record->dyn_cast<const Expr *>()) {
+ if (const Expr *E = dyn_cast<const Expr *>(*Record)) {
ExpectedExpr ToSecondExpr = import(E);
if (!ToSecondExpr)
return ToSecondExpr.takeError();
ToSat.Details.emplace_back(ToSecondExpr.get());
- } else if (auto CR = Record->dyn_cast<const ConceptReference *>()) {
+ } else if (auto CR = dyn_cast<const ConceptReference *>(*Record)) {
Expected<ConceptReference *> ToCROrErr = import(CR);
if (!ToCROrErr)
return ToCROrErr.takeError();
ToSat.Details.emplace_back(ToCROrErr.get());
} else {
- auto Pair =
- Record->dyn_cast<const ConstraintSubstitutionDiagnostic *>();
+ auto Pair = dyn_cast<const ConstraintSubstitutionDiagnostic *>(*Record);
ExpectedSLoc ToPairFirst = import(Pair->first);
if (!ToPairFirst)
diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
index 004a02c279099..d2c024abfe02e 100644
--- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -805,7 +805,7 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
const T *> \
getNode() const { \
assertHoldsState(); \
- return Callback.getInt() == (Index) ? Node##Index.dyn_cast<const T *>() \
+ return Callback.getInt() == (Index) ? dyn_cast<const T *>(Node##Index) \
: nullptr; \
}
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index c49b9102ef869..c4cc872dcd568 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -73,9 +73,9 @@ class LifetimeChecker {
static SourceLocation
GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
- if (const auto *UF = F.dyn_cast<const UseFact *>())
+ if (const auto *UF = dyn_cast<const UseFact *>(F))
return UF->getUseExpr()->getExprLoc();
- if (const auto *OEF = F.dyn_cast<const OriginEscapesFact *>()) {
+ if (const auto *OEF = dyn_cast<const OriginEscapesFact *>(F)) {
if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
return ReturnEsc->getReturnExpr()->getExprLoc();
if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
@@ -433,10 +433,10 @@ class LifetimeChecker {
return;
llvm::TimeTraceScope TimeTrace("SuggestAnnotations");
for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
- if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>())
+ if (const auto *PVD = dyn_cast<const ParmVarDecl *>(Target))
suggestWithScopeForParmVar(PVD, EscapeTarget);
- else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
- if (const auto *EscapeExpr = EscapeTarget.dyn_cast<const Expr *>())
+ else if (const auto *MD = dyn_cast<const CXXMethodDecl *>(Target)) {
+ if (const auto *EscapeExpr = dyn_cast<const Expr *>(EscapeTarget))
suggestWithScopeForImplicitThis(MD, EscapeExpr);
else
llvm_unreachable("Implicit this can only escape via Expr (return)");
@@ -447,11 +447,11 @@ class LifetimeChecker {
void reportNoescapeViolations() {
llvm::TimeTraceScope TimeTrace("ReportNoescapeViolations");
for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
- if (const auto *E = EscapeTarget.dyn_cast<const Expr *>())
+ if (const auto *E = dyn_cast<const Expr *>(EscapeTarget))
SemaHelper->reportNoescapeViolation(PVD, E);
- else if (const auto *FD = EscapeTarget.dyn_cast<const FieldDecl *>())
+ else if (const auto *FD = dyn_cast<const FieldDecl *>(EscapeTarget))
SemaHelper->reportNoescapeViolation(PVD, FD);
- else if (const auto *G = EscapeTarget.dyn_cast<const VarDecl *>())
+ else if (const auto *G = dyn_cast<const VarDecl *>(EscapeTarget))
SemaHelper->reportNoescapeViolation(PVD, G);
else
llvm_unreachable("Unhandled EscapingTarget type");
@@ -533,10 +533,10 @@ class LifetimeChecker {
void inferAnnotations() {
for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
- if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
+ if (const auto *MD = dyn_cast<const CXXMethodDecl *>(Target)) {
if (!implicitObjectParamIsLifetimeBound(MD))
SemaHelper->addLifetimeBoundToImplicitThis(cast<CXXMethodDecl>(MD));
- } else if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) {
+ } else if (const auto *PVD = dyn_cast<const ParmVarDecl *>(Target)) {
const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
if (!FD)
continue;
diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
index b64acc56a2221..f834f24921aaa 100644
--- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
@@ -60,9 +60,9 @@ struct Lattice {
};
static SourceLocation GetFactLoc(CausingFactType F) {
- if (const auto *UF = F.dyn_cast<const UseFact *>())
+ if (const auto *UF = dyn_cast<const UseFact *>(F))
return UF->getUseExpr()->getExprLoc();
- if (const auto *OEF = F.dyn_cast<const OriginEscapesFact *>()) {
+ if (const auto *OEF = dyn_cast<const OriginEscapesFact *>(F)) {
if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
return ReturnEsc->getReturnExpr()->getExprLoc();
if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
diff --git a/clang/lib/Index/FileIndexRecord.cpp b/clang/lib/Index/FileIndexRecord.cpp
index d6b0ee5c4ff10..a419fffc41a92 100644
--- a/clang/lib/Index/FileIndexRecord.cpp
+++ b/clang/lib/Index/FileIndexRecord.cpp
@@ -44,7 +44,7 @@ void FileIndexRecord::addMacroOccurence(SymbolRoleSet Roles, unsigned Offset,
void FileIndexRecord::removeHeaderGuardMacros() {
llvm::erase_if(Decls, [](const DeclOccurrence &D) {
- if (const auto *MI = D.DeclOrMacro.dyn_cast<const MacroInfo *>())
+ if (const auto *MI = dyn_cast<const MacroInfo *>(D.DeclOrMacro))
return MI->isUsedForHeaderGuard();
return false;
});
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index fb66bdd6d2bd0..611d8b0545011 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2529,8 +2529,7 @@ const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
}
// FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement!
- const NamedDecl *ND =
- ConstrainedDeclOrNestedReq.dyn_cast<const NamedDecl *>();
+ const NamedDecl *ND = dyn_cast<const NamedDecl *>(ConstrainedDeclOrNestedReq);
auto CacheEntry = NormalizationCache.find(ConstrainedDeclOrNestedReq);
if (CacheEntry == NormalizationCache.end()) {
auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index b942f19761f40..e180b3655bd73 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -676,7 +676,7 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
void emitObjectAccessPathNotes() {
for (auto Parent : llvm::reverse(ObjectAccessPath)) {
- if (auto *FD = Parent.dyn_cast<const FieldDecl *>()) {
+ if (auto *FD = dyn_cast<const FieldDecl *>(Parent)) {
const CXXRecordDecl *ParentRD = cast<CXXRecordDecl>(FD->getParent());
if (ParentRD->isLambda()) {
SemaSYCLRef.Diag(ParentRD->getLocation(), diag::note_within_capture)
@@ -686,7 +686,7 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
diag::note_within_field_of_type)
<< ParentRD;
}
- } else if (auto *BS = Parent.dyn_cast<const CXXBaseSpecifier *>()) {
+ } else if (auto *BS = dyn_cast<const CXXBaseSpecifier *>(Parent)) {
CXXRecordDecl *RD = BS->getType()->getAsCXXRecordDecl();
assert(RD);
SemaSYCLRef.Diag(BS->getBeginLoc(), diag::note_within_base_of_type)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 20b16c3e49bea..bbca1267461e4 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4129,7 +4129,7 @@ static ActionResult<CXXRecordDecl *> getPatternForClassTemplateSpecialization(
CXXRecordDecl *Pattern = nullptr;
Specialized = ClassTemplateSpec->getSpecializedTemplateOrPartial();
if (auto *PartialSpec =
- Specialized.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
+ dyn_cast<ClassTemplatePartialSpecializationDecl *>(Specialized)) {
// Instantiate using the best class template partial specialization.
while (PartialSpec->getInstantiatedFromMember()) {
// If we've found an explicit specialization of this class template,
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 7668b75e836e4..7e29de5be2e97 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6826,7 +6826,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
VarTemplatePartialSpecializationDecl *> PatternPtr =
VarSpec->getSpecializedTemplateOrPartial();
if (VarTemplatePartialSpecializationDecl *Partial =
- PatternPtr.dyn_cast<VarTemplatePartialSpecializationDecl *>())
+ dyn_cast<VarTemplatePartialSpecializationDecl *>(PatternPtr))
cast<VarTemplateSpecializationDecl>(Var)->setInstantiationOf(
Partial, &VarSpec->getTemplateInstantiationArgs());
@@ -7264,7 +7264,7 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
// declarations to their instantiations.
if (CurrentInstantiationScope) {
if (auto Found = CurrentInstantiationScope->findInstantiationOf(D)) {
- if (Decl *FD = Found->dyn_cast<Decl *>()) {
+ if (Decl *FD = dyn_cast<Decl *>(*Found)) {
if (auto *BD = dyn_cast<BindingDecl>(FD);
BD && BD->isParameterPack() && ArgPackSubstIndex) {
return BD->getBindingPackDecls()[*ArgPackSubstIndex];
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 0f5c4d41c3295..3235750f408ff 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -453,7 +453,7 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
if (sema::CapturingScopeInfo *CSI = getEnclosingLambdaOrBlock()) {
for (auto &Pack : Unexpanded) {
auto DeclaresThisPack = [&](NamedDecl *LocalPack) {
- if (auto *TTPT = Pack.first.dyn_cast<const TemplateTypeParmType *>()) {
+ if (auto *TTPT = dyn_cast<const TemplateTypeParmType *>(Pack.first)) {
auto *TTPD = dyn_cast<TemplateTypeParmDecl>(LocalPack);
return TTPD && TTPD->getTypeForDecl() == TTPT;
}
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 92c555dc427b3..f4cd6422332f7 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -939,7 +939,7 @@ void ASTStmtReader::VisitRequiresExpr(RequiresExpr *E) {
break;
}
}
- if (Expr *Ex = E.dyn_cast<Expr *>())
+ if (Expr *Ex = dyn_cast<Expr *>(E))
R = new (Record.getContext()) concepts::ExprRequirement(
Ex, RK == concepts::Requirement::RK_Simple, NoexceptLoc,
std::move(*Req), Status, SubstitutedConstraintExpr);
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 73fe1ee990eb9..2f5b9ed925097 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1952,7 +1952,7 @@ void ASTDeclWriter::VisitClassTemplateSpecializationDecl(
llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *> InstFrom
= D->getSpecializedTemplateOrPartial();
- if (Decl *InstFromD = InstFrom.dyn_cast<ClassTemplateDecl *>()) {
+ if (Decl *InstFromD = dyn_cast<ClassTemplateDecl *>(InstFrom)) {
Record.AddDeclRef(InstFromD);
} else {
Record.AddDeclRef(cast<ClassTemplatePartialSpecializationDecl *>(InstFrom));
@@ -2035,7 +2035,7 @@ void ASTDeclWriter::VisitVarTemplateSpecializationDecl(
llvm::PointerUnion<VarTemplateDecl *, VarTemplatePartialSpecializationDecl *>
InstFrom = D->getSpecializedTemplateOrPartial();
- if (Decl *InstFromD = InstFrom.dyn_cast<VarTemplateDecl *>()) {
+ if (Decl *InstFromD = dyn_cast<VarTemplateDecl *>(InstFrom)) {
Record.AddDeclRef(InstFromD);
} else {
Record.AddDeclRef(cast<VarTemplatePartialSpecializationDecl *>(InstFrom));
diff --git a/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp b/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
index 6c1a55f79b908..eb3f437b28ed9 100644
--- a/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
@@ -257,7 +257,7 @@ preferTemplateDefinitionForTemplateSpecializations(const Decl *D) {
if (!InstantiatedFrom)
return D;
- if (const auto *Tmpl = InstantiatedFrom.dyn_cast<ClassTemplateDecl *>()) {
+ if (const auto *Tmpl = dyn_cast<ClassTemplateDecl *>(InstantiatedFrom)) {
// Interestingly, the source template might be a forward declaration, so we
// need to find the definition redeclaration.
return chooseDefinitionRedecl(walkInstantiatedFromChain(Tmpl));
More information about the cfe-commits
mailing list