https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/221610
We split IsLifetimeSafetyEnabled into individual check functions in SemaLifetimeSafety.h: - ShouldCheckSafety - ShouldCheckNoescapeViolations - ShouldCheckLifetimeboundViolations - ShouldCheckMisplacedLifetimebound - ShouldCheckInapplicableLifetimebound - ShouldSuggestLifetimeAnnotations These properties are stored in LifetimeSafetyOpts, which are then checked inside the Checker to avoid invoking expensive reporting functions mapping to diagnostics that are disabled. >From f5101b2299031dd9a69bde61dc5afc9729d7515e Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <[email protected]> Date: Sun, 6 Sep 2026 21:19:22 +0000 Subject: [PATCH] Implement pay-for-what-you-use warnings in LifetimeSafetyChecker We split IsLifetimeSafetyEnabled into individual check functions in SemaLifetimeSafety.h: - ShouldCheckSafety - ShouldCheckNoescapeViolations - ShouldCheckLifetimeboundViolations - ShouldCheckMisplacedLifetimebound - ShouldCheckInapplicableLifetimebound - ShouldSuggestLifetimeAnnotations These properties are stored in LifetimeSafetyOpts, which are then checked inside the Checker to avoid invoking expensive reporting functions mapping to diagnostics that are disabled. TAG=agy CONV=a79c61c3-92c6-404e-83e3-3e3088f77c0e --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 5 + clang/lib/Analysis/LifetimeSafety/Checker.cpp | 17 ++- clang/lib/Sema/SemaLifetimeSafety.h | 99 +++++++++------ patch.diff | 14 +++ patch_checker.py | 24 ++++ patch_checker2.py | 43 +++++++ patch_sema.py | 116 ++++++++++++++++++ patch_sema2.py | 21 ++++ patch_sema3.py | 20 +++ 9 files changed, 320 insertions(+), 39 deletions(-) create mode 100644 patch.diff create mode 100644 patch_checker.py create mode 100644 patch_checker2.py create mode 100644 patch_sema.py create mode 100644 patch_sema2.py create mode 100644 patch_sema3.py diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 101407e91aa65..68e84961010dd 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -41,6 +41,11 @@ struct LifetimeSafetyOpts { /// Whether to suggest lifetime annotations. bool SuggestAnnotations; + + bool CheckNoescapeViolations; + bool CheckLifetimeboundViolations; + bool CheckMisplacedLifetimebound; + bool CheckInapplicableLifetimebound; }; /// Enum to track functions visible across or within TU. diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index b136da486365a..a358215a295d1 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -105,10 +105,14 @@ class LifetimeChecker { checkAnnotations(OEF); issuePendingWarnings(); suggestAnnotations(); - reportNoescapeViolations(); - reportLifetimeboundViolations(); - reportMisplacedLifetimebound(); - reportInapplicableLifetimebound(); + if (LSOpts.CheckNoescapeViolations) + reportNoescapeViolations(); + if (LSOpts.CheckLifetimeboundViolations) + reportLifetimeboundViolations(); + if (LSOpts.CheckMisplacedLifetimebound) + reportMisplacedLifetimebound(); + if (LSOpts.CheckInapplicableLifetimebound) + reportInapplicableLifetimebound(); // Annotation inference is currently guarded by a frontend flag. In the // future, this might be replaced by a design that differentiates between // explicit and inferred findings with separate warning groups. @@ -254,6 +258,7 @@ class LifetimeChecker { } void issuePendingWarnings() { + llvm::TimeTraceScope TimeTrace("IssuePendingWarnings"); if (!SemaHelper) return; for (const auto &[LID, Warning] : FinalWarningsMap) { @@ -436,6 +441,7 @@ class LifetimeChecker { } void reportNoescapeViolations() { + llvm::TimeTraceScope TimeTrace("ReportNoescapeViolations"); for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) { if (const auto *E = EscapeTarget.dyn_cast<const Expr *>()) SemaHelper->reportNoescapeViolation(PVD, E); @@ -449,6 +455,7 @@ class LifetimeChecker { } void reportLifetimeboundViolations() { + llvm::TimeTraceScope TimeTrace("ReportLifetimeboundViolations"); if (!isa<FunctionDecl>(FD)) return; if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); @@ -471,6 +478,7 @@ class LifetimeChecker { // Reports lifetimebound attributes that are placed on a function definition // but not on the corresponding declaration. void reportMisplacedLifetimebound() { + llvm::TimeTraceScope TimeTrace("ReportMisplacedLifetimebound"); const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD); if (!FDef) return; @@ -501,6 +509,7 @@ class LifetimeChecker { } void reportInapplicableLifetimebound() { + llvm::TimeTraceScope TimeTrace("ReportInapplicableLifetimebound"); const auto *FDef = dyn_cast<FunctionDecl>(FD); if (!FDef) return; diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index d87c9003e83cf..ca941375a003f 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -25,28 +25,7 @@ namespace clang::lifetimes { -inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) { - // TODO: Enable ObjectiveC later when we know it's stable enough. - if (S.getLangOpts().ObjC) - return false; - - // TODO: Default this flag to on in the future. - if (!S.getLangOpts().CPlusPlus && !S.getLangOpts().EnableLifetimeSafetyInC) - return false; - - // Translation-unit mode: whole-program analysis runs once on TU. - // Individual function analysis is disabled when TU mode is enabled. - if (S.getLangOpts().EnableLifetimeSafetyTUAnalysis) - return isa<TranslationUnitDecl>(D); - - // Per-function mode: analysis runs on each function/method individually. - // Skip TU-level calls when per-function mode is enabled. - if (isa<TranslationUnitDecl>(D)) - return false; - - // Enable per-function mode via debug flag or specific diagnostics. - if (S.getLangOpts().DebugRunLifetimeSafety) - return true; +inline bool ShouldCheckSafety(Sema &S, const Decl *D) { DiagnosticsEngine &Diags = S.getDiagnostics(); constexpr unsigned DiagIDs[] = { diag::warn_lifetime_safety_use_after_scope, @@ -59,25 +38,40 @@ inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) { diag::warn_lifetime_safety_dangling_field_moved, diag::warn_lifetime_safety_dangling_global, diag::warn_lifetime_safety_dangling_global_moved, - diag::warn_lifetime_safety_noescape_escapes, - diag::warn_lifetime_safety_lifetimebound_violation, - diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound, - diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound, diag::warn_lifetime_safety_invalidated_field, - diag::warn_lifetime_safety_invalidated_global, - diag::warn_lifetime_safety_cross_tu_param_suggestion, - diag::warn_lifetime_safety_intra_tu_param_suggestion, - diag::warn_lifetime_safety_cross_tu_ctor_param_suggestion, - diag::warn_lifetime_safety_intra_tu_ctor_param_suggestion, - diag::warn_lifetime_safety_cross_tu_this_suggestion, - diag::warn_lifetime_safety_intra_tu_this_suggestion, - diag::warn_lifetime_safety_inapplicable_lifetimebound}; + diag::warn_lifetime_safety_invalidated_global}; for (unsigned DiagID : DiagIDs) if (!Diags.isIgnored(DiagID, D->getBeginLoc())) return true; return false; } +inline bool ShouldCheckNoescapeViolations(Sema &S, const Decl *D) { + return !S.getDiagnostics().isIgnored( + diag::warn_lifetime_safety_noescape_escapes, D->getBeginLoc()); +} + +inline bool ShouldCheckLifetimeboundViolations(Sema &S, const Decl *D) { + return !S.getDiagnostics().isIgnored( + diag::warn_lifetime_safety_lifetimebound_violation, D->getBeginLoc()); +} + +inline bool ShouldCheckMisplacedLifetimebound(Sema &S, const Decl *D) { + DiagnosticsEngine &Diags = S.getDiagnostics(); + constexpr unsigned DiagIDs[] = { + diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound, + diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound}; + for (unsigned DiagID : DiagIDs) + if (!Diags.isIgnored(DiagID, D->getBeginLoc())) + return true; + return false; +} + +inline bool ShouldCheckInapplicableLifetimebound(Sema &S, const Decl *D) { + return !S.getDiagnostics().isIgnored( + diag::warn_lifetime_safety_inapplicable_lifetimebound, D->getBeginLoc()); +} + inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) { DiagnosticsEngine &Diags = S.getDiagnostics(); constexpr unsigned DiagIDs[] = { @@ -93,13 +87,48 @@ inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) { return false; } +inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) { + // TODO: Enable ObjectiveC later when we know it's stable enough. + if (S.getLangOpts().ObjC) + return false; + + // TODO: Default this flag to on in the future. + if (!S.getLangOpts().CPlusPlus && !S.getLangOpts().EnableLifetimeSafetyInC) + return false; + + // Translation-unit mode: whole-program analysis runs once on TU. + // Individual function analysis is disabled when TU mode is enabled. + if (S.getLangOpts().EnableLifetimeSafetyTUAnalysis) + return isa<TranslationUnitDecl>(D); + + // Per-function mode: analysis runs on each function/method individually. + // Skip TU-level calls when per-function mode is enabled. + if (isa<TranslationUnitDecl>(D)) + return false; + + // Enable per-function mode via debug flag or specific diagnostics. + if (S.getLangOpts().DebugRunLifetimeSafety) + return true; + + return ShouldCheckSafety(S, D) || ShouldCheckNoescapeViolations(S, D) || + ShouldCheckLifetimeboundViolations(S, D) || + ShouldCheckMisplacedLifetimebound(S, D) || + ShouldCheckInapplicableLifetimebound(S, D) || + ShouldSuggestLifetimeAnnotations(S, D); +} + inline LifetimeSafetyOpts GetLifetimeSafetyOpts(Sema &S, const Decl *D) { LifetimeSafetyOpts LSOpts; LSOpts.MaxCFGBlocks = S.getLangOpts().LifetimeSafetyMaxCFGBlocks; LSOpts.SuggestAnnotations = ShouldSuggestLifetimeAnnotations(S, D); + LSOpts.CheckNoescapeViolations = ShouldCheckNoescapeViolations(S, D); + LSOpts.CheckLifetimeboundViolations = + ShouldCheckLifetimeboundViolations(S, D); + LSOpts.CheckMisplacedLifetimebound = ShouldCheckMisplacedLifetimebound(S, D); + LSOpts.CheckInapplicableLifetimebound = + ShouldCheckInapplicableLifetimebound(S, D); return LSOpts; } - class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { public: diff --git a/patch.diff b/patch.diff new file mode 100644 index 0000000000000..665139146f600 --- /dev/null +++ b/patch.diff @@ -0,0 +1,14 @@ +--- clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h ++++ clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +@@ -41,6 +41,11 @@ struct LifetimeSafetyOpts { + + /// Whether to suggest lifetime annotations. + bool SuggestAnnotations; ++ ++ bool CheckNoescapeViolations; ++ bool CheckLifetimeboundViolations; ++ bool CheckMisplacedLifetimebound; ++ bool CheckInapplicableLifetimebound; + }; + + /// Enum to track functions visible across or within TU. diff --git a/patch_checker.py b/patch_checker.py new file mode 100644 index 0000000000000..241b01cd220d0 --- /dev/null +++ b/patch_checker.py @@ -0,0 +1,24 @@ +import re + +with open('clang/lib/Analysis/LifetimeSafety/Checker.cpp', 'r') as f: + orig = f.read() + +# Replace the calls in `check()` function +orig = orig.replace(""" issuePendingWarnings(); + suggestAnnotations(); + reportNoescapeViolations(); + reportLifetimeboundViolations(); + reportMisplacedLifetimebound(); + reportInapplicableLifetimebound();""", """ issuePendingWarnings(); + suggestAnnotations(); + if (LSOpts.CheckNoescapeViolations) + reportNoescapeViolations(); + if (LSOpts.CheckLifetimeboundViolations) + reportLifetimeboundViolations(); + if (LSOpts.CheckMisplacedLifetimebound) + reportMisplacedLifetimebound(); + if (LSOpts.CheckInapplicableLifetimebound) + reportInapplicableLifetimebound();""") + +with open('clang/lib/Analysis/LifetimeSafety/Checker.cpp', 'w') as f: + f.write(orig) diff --git a/patch_checker2.py b/patch_checker2.py new file mode 100644 index 0000000000000..a5c0265739093 --- /dev/null +++ b/patch_checker2.py @@ -0,0 +1,43 @@ +import re + +with open('clang/lib/Analysis/LifetimeSafety/Checker.cpp', 'r') as f: + orig = f.read() + +# Add TimeTraceScope to reportNoescapeViolations +orig = re.sub( + r'(void reportNoescapeViolations\(\) {\n)', + r'\1 llvm::TimeTraceScope TimeTrace("ReportNoescapeViolations");\n', + orig +) + +# Add TimeTraceScope to reportLifetimeboundViolations +orig = re.sub( + r'(void reportLifetimeboundViolations\(\) {\n)', + r'\1 llvm::TimeTraceScope TimeTrace("ReportLifetimeboundViolations");\n', + orig +) + +# Add TimeTraceScope to reportMisplacedLifetimebound +orig = re.sub( + r'(void reportMisplacedLifetimebound\(\) {\n)', + r'\1 llvm::TimeTraceScope TimeTrace("ReportMisplacedLifetimebound");\n', + orig +) + +# Add TimeTraceScope to reportInapplicableLifetimebound +orig = re.sub( + r'(void reportInapplicableLifetimebound\(\) {\n)', + r'\1 llvm::TimeTraceScope TimeTrace("ReportInapplicableLifetimebound");\n', + orig +) + +# Might as well do issuePendingWarnings +orig = re.sub( + r'(void issuePendingWarnings\(\) {\n)', + r'\1 llvm::TimeTraceScope TimeTrace("IssuePendingWarnings");\n', + orig +) + + +with open('clang/lib/Analysis/LifetimeSafety/Checker.cpp', 'w') as f: + f.write(orig) diff --git a/patch_sema.py b/patch_sema.py new file mode 100644 index 0000000000000..1bc743da23dcf --- /dev/null +++ b/patch_sema.py @@ -0,0 +1,116 @@ +import re + +with open('clang/lib/Sema/SemaLifetimeSafety.h', 'r') as f: + orig = f.read() + +replacement = """ +inline bool ShouldCheckSafety(Sema &S, const Decl *D) { + DiagnosticsEngine &Diags = S.getDiagnostics(); + constexpr unsigned DiagIDs[] = { + diag::warn_lifetime_safety_use_after_scope, + diag::warn_lifetime_safety_use_after_scope_moved, + diag::warn_lifetime_safety_use_after_free, + diag::warn_lifetime_safety_return_stack_addr, + diag::warn_lifetime_safety_return_stack_addr_moved, + diag::warn_lifetime_safety_invalidation, + diag::warn_lifetime_safety_dangling_field, + diag::warn_lifetime_safety_dangling_field_moved, + diag::warn_lifetime_safety_dangling_global, + diag::warn_lifetime_safety_dangling_global_moved, + diag::warn_lifetime_safety_invalidated_field, + diag::warn_lifetime_safety_invalidated_global}; + for (unsigned DiagID : DiagIDs) + if (!Diags.isIgnored(DiagID, D->getBeginLoc())) + return true; + return false; +} + +inline bool ShouldCheckNoescapeViolations(Sema &S, const Decl *D) { + return !S.getDiagnostics().isIgnored(diag::warn_lifetime_safety_noescape_escapes, D->getBeginLoc()); +} + +inline bool ShouldCheckLifetimeboundViolations(Sema &S, const Decl *D) { + return !S.getDiagnostics().isIgnored(diag::warn_lifetime_safety_lifetimebound_violation, D->getBeginLoc()); +} + +inline bool ShouldCheckMisplacedLifetimebound(Sema &S, const Decl *D) { + DiagnosticsEngine &Diags = S.getDiagnostics(); + constexpr unsigned DiagIDs[] = { + diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound, + diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound}; + for (unsigned DiagID : DiagIDs) + if (!Diags.isIgnored(DiagID, D->getBeginLoc())) + return true; + return false; +} + +inline bool ShouldCheckInapplicableLifetimebound(Sema &S, const Decl *D) { + return !S.getDiagnostics().isIgnored(diag::warn_lifetime_safety_inapplicable_lifetimebound, D->getBeginLoc()); +} + +inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) { + DiagnosticsEngine &Diags = S.getDiagnostics(); + constexpr unsigned DiagIDs[] = { + diag::warn_lifetime_safety_intra_tu_param_suggestion, + diag::warn_lifetime_safety_cross_tu_param_suggestion, + diag::warn_lifetime_safety_intra_tu_ctor_param_suggestion, + diag::warn_lifetime_safety_cross_tu_ctor_param_suggestion, + diag::warn_lifetime_safety_intra_tu_this_suggestion, + diag::warn_lifetime_safety_cross_tu_this_suggestion}; + for (unsigned DiagID : DiagIDs) + if (!Diags.isIgnored(DiagID, D->getBeginLoc())) + return true; + return false; +} + +inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) { + // TODO: Enable ObjectiveC later when we know it's stable enough. + if (S.getLangOpts().ObjC) + return false; + + // TODO: Default this flag to on in the future. + if (!S.getLangOpts().CPlusPlus && !S.getLangOpts().EnableLifetimeSafetyInC) + return false; + + // Translation-unit mode: whole-program analysis runs once on TU. + // Individual function analysis is disabled when TU mode is enabled. + if (S.getLangOpts().EnableLifetimeSafetyTUAnalysis) + return isa<TranslationUnitDecl>(D); + + // Per-function mode: analysis runs on each function/method individually. + // Skip TU-level calls when per-function mode is enabled. + if (isa<TranslationUnitDecl>(D)) + return false; + + // Enable per-function mode via debug flag or specific diagnostics. + if (S.getLangOpts().DebugRunLifetimeSafety) + return true; + + return ShouldCheckSafety(S, D) || + ShouldCheckNoescapeViolations(S, D) || + ShouldCheckLifetimeboundViolations(S, D) || + ShouldCheckMisplacedLifetimebound(S, D) || + ShouldCheckInapplicableLifetimebound(S, D) || + ShouldSuggestLifetimeAnnotations(S, D); +} + +inline LifetimeSafetyOpts GetLifetimeSafetyOpts(Sema &S, const Decl *D) { + LifetimeSafetyOpts LSOpts; + LSOpts.MaxCFGBlocks = S.getLangOpts().LifetimeSafetyMaxCFGBlocks; + LSOpts.SuggestAnnotations = ShouldSuggestLifetimeAnnotations(S, D); + LSOpts.CheckNoescapeViolations = ShouldCheckNoescapeViolations(S, D); + LSOpts.CheckLifetimeboundViolations = ShouldCheckLifetimeboundViolations(S, D); + LSOpts.CheckMisplacedLifetimebound = ShouldCheckMisplacedLifetimebound(S, D); + LSOpts.CheckInapplicableLifetimebound = ShouldCheckInapplicableLifetimebound(S, D); + return LSOpts; +} +""" + +start_idx = orig.find('inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {') +end_idx = orig.find('class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {') + +new_content = orig[:start_idx] + replacement + orig[end_idx:] + +with open('clang/lib/Sema/SemaLifetimeSafety.h', 'w') as f: + f.write(new_content) + diff --git a/patch_sema2.py b/patch_sema2.py new file mode 100644 index 0000000000000..424c95372d336 --- /dev/null +++ b/patch_sema2.py @@ -0,0 +1,21 @@ +import re + +with open('clang/lib/Sema/SemaLifetimeSafety.h', 'r') as f: + orig = f.read() + +replacement = """inline LifetimeSafetyOpts GetLifetimeSafetyOpts(Sema &S, const Decl *D) { + LifetimeSafetyOpts LSOpts; + LSOpts.MaxCFGBlocks = S.getLangOpts().LifetimeSafetyMaxCFGBlocks; + bool Debug = S.getLangOpts().DebugRunLifetimeSafety; + LSOpts.SuggestAnnotations = Debug || ShouldSuggestLifetimeAnnotations(S, D); + LSOpts.CheckNoescapeViolations = Debug || ShouldCheckNoescapeViolations(S, D); + LSOpts.CheckLifetimeboundViolations = Debug || ShouldCheckLifetimeboundViolations(S, D); + LSOpts.CheckMisplacedLifetimebound = Debug || ShouldCheckMisplacedLifetimebound(S, D); + LSOpts.CheckInapplicableLifetimebound = Debug || ShouldCheckInapplicableLifetimebound(S, D); + return LSOpts; +}""" + +orig = re.sub(r'inline LifetimeSafetyOpts GetLifetimeSafetyOpts\(Sema &S, const Decl \*D\) \{.*return LSOpts;\n}', replacement, orig, flags=re.DOTALL) + +with open('clang/lib/Sema/SemaLifetimeSafety.h', 'w') as f: + f.write(orig) diff --git a/patch_sema3.py b/patch_sema3.py new file mode 100644 index 0000000000000..e054cd8a0578e --- /dev/null +++ b/patch_sema3.py @@ -0,0 +1,20 @@ +import re + +with open('clang/lib/Sema/SemaLifetimeSafety.h', 'r') as f: + orig = f.read() + +replacement = """inline LifetimeSafetyOpts GetLifetimeSafetyOpts(Sema &S, const Decl *D) { + LifetimeSafetyOpts LSOpts; + LSOpts.MaxCFGBlocks = S.getLangOpts().LifetimeSafetyMaxCFGBlocks; + LSOpts.SuggestAnnotations = ShouldSuggestLifetimeAnnotations(S, D); + LSOpts.CheckNoescapeViolations = ShouldCheckNoescapeViolations(S, D); + LSOpts.CheckLifetimeboundViolations = ShouldCheckLifetimeboundViolations(S, D); + LSOpts.CheckMisplacedLifetimebound = ShouldCheckMisplacedLifetimebound(S, D); + LSOpts.CheckInapplicableLifetimebound = ShouldCheckInapplicableLifetimebound(S, D); + return LSOpts; +}""" + +orig = re.sub(r'inline LifetimeSafetyOpts GetLifetimeSafetyOpts\(Sema &S, const Decl \*D\) \{.*return LSOpts;\n}', replacement, orig, flags=re.DOTALL) + +with open('clang/lib/Sema/SemaLifetimeSafety.h', 'w') as f: + f.write(orig) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
