[clang] [llvm] Reapply "[LifetimeSafety] Support C Language in LifetimeSafety" (PR #204536)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 18 02:47:19 PDT 2026
https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/204536
>From 571fa96d31e733fe1168a1b1ce1ad013a41a74aa Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Thu, 18 Jun 2026 12:47:03 +0300
Subject: [PATCH 1/2] Revert "Revert "[LifetimeSafety] Support C Language in
LifetimeSafety" (#204481)"
---
.gitignore | 8 +-
.../Analyses/LifetimeSafety/FactsGenerator.h | 5 +-
clang/include/clang/Basic/Attr.td | 4 +-
clang/include/clang/Basic/LangOptions.def | 2 +
clang/include/clang/Options/Options.td | 7 +
.../LifetimeSafety/FactsGenerator.cpp | 24 ++-
clang/lib/Sema/AnalysisBasedWarnings.cpp | 9 +-
clang/lib/Sema/SemaLifetimeSafety.h | 19 ++
clang/test/Sema/LifetimeSafety/safety-c.c | 181 ++++++++++++++++++
clang/test/Sema/attr-lifetime-capture-by.c | 28 +++
clang/test/Sema/attr-lifetimebound.c | 21 ++
11 files changed, 297 insertions(+), 11 deletions(-)
create mode 100644 clang/test/Sema/LifetimeSafety/safety-c.c
create mode 100644 clang/test/Sema/attr-lifetime-capture-by.c
create mode 100644 clang/test/Sema/attr-lifetimebound.c
diff --git a/.gitignore b/.gitignore
index 9d4e86ab10caa..0420627d58a56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,7 +27,7 @@
/*/CMakeUserPresets.json
# Nested build directory
-/build*
+/build_*
#==============================================================================#
# Explicit files to ignore (only matches one).
@@ -66,6 +66,12 @@ AGENTS.md
.cursor
.cursorignore
.cursorindexingignore
+/.opencode/
+/.nvim/
+/.nvimlog
+/dhw-extension/
+/install*/
+/tmp/
#==============================================================================#
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 2c961bd305fac..a021c09dcd34b 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -29,7 +29,9 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
public:
FactsGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
- : FactMgr(FactMgr), AC(AC) {}
+ : FactMgr(FactMgr), AC(AC),
+ IsCMode(!AC.getASTContext().getLangOpts().CPlusPlus &&
+ !AC.getASTContext().getLangOpts().ObjC) {}
void run();
@@ -160,6 +162,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
// exempting it from the check.
llvm::DenseMap<const Expr *, UseFact *> UseFacts;
const CFGBlock *CurrentBlock;
+ bool IsCMode = false;
};
} // namespace clang::lifetimes::internal
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 1745c5a4f4c2a..a222092cd42cf 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2129,14 +2129,14 @@ def ExplicitInit : InheritableAttr {
}
def LifetimeBound : DeclOrTypeAttr {
- let Spellings = [Clang<"lifetimebound", 0>];
+ let Spellings = [Clang<"lifetimebound", 1>];
let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;
let Documentation = [LifetimeBoundDocs];
let SimpleHandler = 1;
}
def LifetimeCaptureBy : DeclOrTypeAttr {
- let Spellings = [Clang<"lifetime_capture_by", 0>];
+ let Spellings = [Clang<"lifetime_capture_by", 1>];
let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;
let Args = [VariadicParamOrParamIdxArgument<"Params">];
let Documentation = [LifetimeCaptureByDocs];
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 75267ffea941d..319fd18cddb36 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -521,6 +521,8 @@ LANGOPT(BoundsSafety, 1, 0, NotCompatible, "Bounds safety extension for C")
LANGOPT(DebugRunLifetimeSafety, 1, 0, Benign, "Run lifetime safety analysis for C++. Does not enable warnings.")
+LANGOPT(EnableLifetimeSafetyInC, 1, 0, Benign, "Lifetime safety analysis for C")
+
LANGOPT(LifetimeSafetyMaxCFGBlocks, 32, 0, Benign, "Skip LifetimeSafety analysis for functions with CFG block count exceeding this threshold. Specify 0 for no limit")
LANGOPT(EnableLifetimeSafetyInference, 1, 0, Benign, "Lifetime safety inference analysis for C++")
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index ecd66325bf8da..0a24f8b35a3ea 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2017,6 +2017,13 @@ defm debug_run_lifetime_safety : BoolFOption<
NegFlag<SetFalse, [], [CC1Option], "Disable">,
BothFlags<[], [CC1Option], " lifetime safety analysis for C++. Warnings are still controlled by -Wlifetime-safety. Primarily used to surface crashes or compile-time regressions without showing analysis findings.">>;
+defm experimental_lifetime_safety_c : BoolFOption<
+ "experimental-lifetime-safety-c",
+ LangOpts<"EnableLifetimeSafetyInC">, DefaultFalse,
+ PosFlag<SetTrue, [], [CC1Option], "Enable">,
+ NegFlag<SetFalse, [], [CC1Option], "Disable">,
+ BothFlags<[], [CC1Option], " experimental lifetime safety analysis for C">>;
+
def lifetime_safety_max_cfg_blocks
: Joined<["-"], "lifetime-safety-max-cfg-blocks=">,
Group<m_Group>,
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 10339a9f2e3c5..d56703a4b29c4 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -324,6 +324,10 @@ void FactsGenerator::VisitCastExpr(const CastExpr *CE) {
flow(Dest, Src, /*Kill=*/true);
return;
case CK_ArrayToPointerDecay:
+ // va_arg(ap, array_type) is UB and does not provide addressable array
+ // storage to model.
+ if (isa<VAArgExpr>(SubExpr->IgnoreParens()))
+ return;
assert(Src && "Array expression should have origins as it is GL value");
CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
Dest->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true));
@@ -347,6 +351,15 @@ void FactsGenerator::VisitUnaryOperator(const UnaryOperator *UO) {
switch (UO->getOpcode()) {
case UO_AddrOf: {
const Expr *SubExpr = UO->getSubExpr();
+ // Function addresses do not need lifetime tracking.
+ if (SubExpr->getType()->isFunctionType())
+ return;
+ // Skip address-of on void expressions: GNU C permits them, but void itself
+ // has no origins to track.
+ if (IsCMode && SubExpr->getType()->isVoidType())
+ return;
+ assert(!SubExpr->getType()->isVoidType() &&
+ "Taking address of void is not valid in C++");
// The origin of an address-of expression (e.g., &x) is the origin of
// its sub-expression (x). This fact will cause the dataflow analysis
// to propagate any loans held by the sub-expression's origin to the
@@ -435,7 +448,12 @@ void FactsGenerator::handleAssignment(const Expr *TargetExpr,
// Kill the old loans of the destination origin and flow the new loans
// from the source origin.
flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true);
- killAndFlowOrigin(*TargetExpr, *LHSExpr);
+
+ // In C, assignment expressions are not GLValues, so the assignment result has
+ // the assigned value origins, not the LHS storage origin.
+ if (IsCMode)
+ LHSList = getRValueOrigins(LHSExpr, LHSList);
+ flow(getOriginsList(*TargetExpr), LHSList, /*Kill=*/true);
}
void FactsGenerator::handlePointerArithmetic(const BinaryOperator *BO) {
@@ -631,6 +649,10 @@ void FactsGenerator::VisitLambdaExpr(const LambdaExpr *LE) {
}
void FactsGenerator::VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) {
+ // Some C subscripts do not refer to addressable storage with origins, such as
+ // GNU void-pointer subscripts and vector element extraction from rvalues.
+ if (IsCMode && !ASE->isGLValue())
+ return;
assert(ASE->isGLValue() && "Array subscript should be a GL value");
OriginList *Dst = getOriginsList(*ASE);
assert(Dst && "Array subscript should have origins as it is a GL value");
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 207ce373339f3..52fe7765c6c6a 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2999,8 +2999,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
}
}
- if (S.getLangOpts().CPlusPlus &&
- S.getLangOpts().EnableLifetimeSafetyTUAnalysis)
+ if (lifetimes::IsLifetimeSafetyEnabled(S, TU))
LifetimeSafetyTUAnalysis(S, TU, LSStats);
}
@@ -3049,9 +3048,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
AC.getCFGBuildOptions().AddCXXNewAllocator = false;
AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true;
- bool EnableLifetimeSafetyAnalysis =
- !S.getLangOpts().EnableLifetimeSafetyTUAnalysis &&
- lifetimes::IsLifetimeSafetyEnabled(S, D);
+ bool EnableLifetimeSafetyAnalysis = lifetimes::IsLifetimeSafetyEnabled(S, D);
// Force that certain expressions appear as CFGElements in the CFG. This
// is used to speed up various analyses.
@@ -3164,7 +3161,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
// TODO: Enable lifetime safety analysis for other languages once it is
// stable.
- if (EnableLifetimeSafetyAnalysis && S.getLangOpts().CPlusPlus) {
+ if (EnableLifetimeSafetyAnalysis) {
if (AC.getCFG()) {
lifetimes::LifetimeSafetySemaHelperImpl LifetimeSafetySemaHelper(S);
lifetimes::runLifetimeSafetyAnalysis(AC, &LifetimeSafetySemaHelper,
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 91f1290f38723..a8bde363e3397 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -25,6 +25,25 @@
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;
DiagnosticsEngine &Diags = S.getDiagnostics();
diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c b/clang/test/Sema/LifetimeSafety/safety-c.c
new file mode 100644
index 0000000000000..95c8cf7bb00c7
--- /dev/null
+++ b/clang/test/Sema/LifetimeSafety/safety-c.c
@@ -0,0 +1,181 @@
+// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -Wno-varargs -Wno-non-pod-varargs -verify -fexperimental-lifetime-safety-c %s
+// RUN: %clang_cc1 -fsyntax-only -Werror=lifetime-safety -Wno-dangling -Wno-varargs -Wno-non-pod-varargs %s
+
+int *identity(int *p __attribute__((lifetimebound))) { return p; }
+
+struct PointerField {
+ int *ptr;
+};
+
+struct IntField {
+ int field;
+};
+
+void simple_case(void) {
+ int *p;
+ {
+ int i;
+ p = &i; // expected-warning {{local variable 'i' does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void chained_assignment(void) {
+ int *p, *q, *r;
+ {
+ int i;
+ p = q = r = &i; // expected-warning {{local variable 'i' does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void conditional_branch(int cond) {
+ int safe;
+ int *p = &safe;
+ if (cond) {
+ int i;
+ p = &i; // expected-warning {{local variable 'i' does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void loop_with_break(int cond) {
+ int safe;
+ int *p = &safe;
+ for (int n = 0; n != 10; ++n) {
+ if (cond) {
+ int i;
+ p = &i; // expected-warning {{local variable 'i' does not live long enough}}
+ break; // expected-note {{destroyed here}}
+ }
+ }
+ (void)*p; // expected-note {{later used here}}
+}
+
+int *return_stack_address(void) {
+ int i;
+ int *p = &i; // expected-warning {{stack memory associated with local variable 'i' is returned}}
+ return p; // expected-note {{returned here}}
+}
+
+void lifetimebound_call(void) {
+ int *p;
+ {
+ int i;
+ p = identity(&i); // expected-warning {{local variable 'i' does not live long enough}} \
+ // expected-note {{result of call to 'identity' aliases the storage of local variable 'i'}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void struct_pointer_field(void) {
+ int *p;
+ {
+ int i;
+ struct PointerField holder;
+ // FIXME: Track origins stored in struct pointer fields.
+ holder.ptr = &i;
+ p = holder.ptr;
+ }
+ (void)*p;
+}
+
+void struct_address_of_field(void) {
+ int *p;
+ {
+ struct IntField holder;
+ p = &holder.field; // expected-warning {{local variable 'holder' does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void conditional_operator_lifetimebound(int cond) {
+ int *p;
+ {
+ int a, b;
+ p = identity(cond ? &a // expected-warning {{local variable 'a' does not live long enough}}
+ : &b); // expected-warning {{local variable 'b' does not live long enough}}
+ } // expected-note 2 {{destroyed here}}
+ (void)*p; // expected-note 2 {{later used here}}
+}
+
+union IntOrPtr {
+ int i;
+ int *p;
+};
+
+void union_member(void) {
+ int *p;
+ {
+ union IntOrPtr u;
+ p = &u.i; // expected-warning {{local variable 'u' does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+struct AnonymousUnion {
+ union {
+ int i;
+ float f;
+ };
+};
+
+void anonymous_union_member(void) {
+ int *p;
+ {
+ struct AnonymousUnion u;
+ p = &u.i; // expected-warning {{local variable 'u' does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void function_address_regression(void) {
+ extern void function_address_target(void);
+ char *p = (char *)&function_address_target;
+ (void)p;
+}
+
+int void_pointer_subscript_regression(void *bytes) {
+ return &bytes[0] == &bytes[1];
+}
+
+typedef __attribute__((vector_size(16))) int v4i32;
+v4i32 (*vector_factory)(int);
+
+int vector_subscript_regression(void) {
+ return (*vector_factory)(0)[0];
+}
+
+void va_arg_array_regression(int n, ...) {
+ __builtin_va_list ap;
+ __builtin_va_start(ap, n);
+ int *p = __builtin_va_arg(ap, int[4]);
+ (void)p;
+}
+
+void take(int* q);
+void va_arg_array_paren_regression(int n, ...) {
+ __builtin_va_list ap;
+ take((__builtin_va_arg(ap, int[4])));
+}
+
+void va_arg_function_regression(int n, ...) {
+ __builtin_va_list ap;
+ __builtin_va_start(ap, n);
+ int (*p)(void) = __builtin_va_arg(ap, int(void));
+ (void)p;
+}
+
+// FIXME: We miss the origins of void* after dereference, so we miss to warn here.
+void *void_pointer_dereference(void) {
+ int value;
+ void *bytes = &value;
+ return &*bytes;
+}
+
+// FIXME: Atomics are not modeled yet.
+int *atomic_pointer_declref(void) {
+ int value;
+ _Atomic(int *) p = &value;
+ return p;
+}
diff --git a/clang/test/Sema/attr-lifetime-capture-by.c b/clang/test/Sema/attr-lifetime-capture-by.c
new file mode 100644
index 0000000000000..f847a5bbca7c5
--- /dev/null
+++ b/clang/test/Sema/attr-lifetime-capture-by.c
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -std=c2x -verify %s
+
+struct S {
+ int *x;
+};
+
+void capture(int *x [[clang::lifetime_capture_by(s, t)]],
+ struct S *s,
+ struct S *t);
+void capture_gnu(int *x __attribute__((lifetime_capture_by(s, t))),
+ struct S *s,
+ struct S *t);
+
+[[clang::lifetime_capture_by(s)]] // expected-error {{'clang::lifetime_capture_by' attribute only applies to parameters and implicit object parameters}}
+void attr_on_function(int *x);
+void attr_on_function_gnu(int *x) __attribute__((lifetime_capture_by(s))); // expected-error {{'lifetime_capture_by' attribute only applies to parameters and implicit object parameters}}
+
+void invalid_args(int *x1 [[clang::lifetime_capture_by(12345 + 12)]], // expected-error {{'lifetime_capture_by' attribute argument '12345 + 12' is not a known function parameter; must be a function parameter, 'this', 'global' or 'unknown'}}
+ int *x2 [[clang::lifetime_capture_by(no_such_param)]], // expected-error {{'lifetime_capture_by' attribute argument 'no_such_param' is not a known function parameter; must be a function parameter, 'this', 'global' or 'unknown'}}
+ int *x3 [[clang::lifetime_capture_by("no_such_param")]], // expected-error {{'lifetime_capture_by' attribute argument '"no_such_param"' is not a known function parameter; must be a function parameter, 'this', 'global' or 'unknown'}}
+ int *x4 [[clang::lifetime_capture_by()]], // expected-error {{'lifetime_capture_by' attribute specifies no capturing entity}}
+ int *x5 [[clang::lifetime_capture_by(x5)]]); // expected-error {{'lifetime_capture_by' argument references itself}}
+
+void invalid_args_gnu(int *x1 __attribute__((lifetime_capture_by(12345 + 12))), // expected-error {{'lifetime_capture_by' attribute argument '12345 + 12' is not a known function parameter; must be a function parameter, 'this', 'global' or 'unknown'}}
+ int *x2 __attribute__((lifetime_capture_by(no_such_param))), // expected-error {{'lifetime_capture_by' attribute argument 'no_such_param' is not a known function parameter; must be a function parameter, 'this', 'global' or 'unknown'}}
+ int *x3 __attribute__((lifetime_capture_by("no_such_param"))), // expected-error {{'lifetime_capture_by' attribute argument '"no_such_param"' is not a known function parameter; must be a function parameter, 'this', 'global' or 'unknown'}}
+ int *x4 __attribute__((lifetime_capture_by())), // expected-error {{'lifetime_capture_by' attribute specifies no capturing entity}}
+ int *x5 __attribute__((lifetime_capture_by(x5)))); // expected-error {{'lifetime_capture_by' argument references itself}}
diff --git a/clang/test/Sema/attr-lifetimebound.c b/clang/test/Sema/attr-lifetimebound.c
new file mode 100644
index 0000000000000..6292fe90bdf68
--- /dev/null
+++ b/clang/test/Sema/attr-lifetimebound.c
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -std=c2x -verify %s
+
+int *ptr_param(int *param [[clang::lifetimebound]]);
+int *ptr_param_gnu(int *param __attribute__((lifetimebound)));
+int *ptr_param_redecl(int *param);
+int *ptr_param_redecl(int *param [[clang::lifetimebound]]);
+int *ptr_param_redecl(int *param) { return param; }
+int *ptr_param_redecl_gnu(int *param);
+int *ptr_param_redecl_gnu(int *param __attribute__((lifetimebound)));
+int *ptr_param_redecl_gnu(int *param) { return param; }
+
+void void_return(int *param [[clang::lifetimebound]]); // expected-error {{'lifetimebound' attribute cannot be applied to a parameter of a function that returns void; did you mean 'lifetime_capture_by(X)'}}
+void void_return_gnu(int *param __attribute__((lifetimebound))); // expected-error {{'lifetimebound' attribute cannot be applied to a parameter of a function that returns void; did you mean 'lifetime_capture_by(X)'}}
+
+int *attr_with_arg(int *param [[clang::lifetimebound(1)]]); // expected-error {{'clang::lifetimebound' attribute takes no arguments}}
+int *attr_with_arg_gnu(int *param __attribute__((lifetimebound(1)))); // expected-error {{'lifetimebound' attribute takes no arguments}}
+
+int attr_on_var [[clang::lifetimebound]]; // expected-error {{'clang::lifetimebound' attribute only applies to parameters and implicit object parameters}}
+int attr_on_var_gnu __attribute__((lifetimebound)); // expected-error {{'lifetimebound' attribute only applies to parameters and implicit object parameters}}
+int * [[clang::lifetimebound]] attr_on_pointee; // expected-error {{'clang::lifetimebound' attribute only applies to parameters and implicit object parameters}}
+int (*func_ptr)(int) [[clang::lifetimebound]]; // expected-error {{'clang::lifetimebound' attribute only applies to parameters and implicit object parameters}}
>From abeed159e8a4959c97d956315bbf64c7a72819fe Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Thu, 18 Jun 2026 12:47:08 +0300
Subject: [PATCH 2/2] revert local gitignore changes
---
.gitignore | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/.gitignore b/.gitignore
index 0420627d58a56..9d4e86ab10caa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,7 +27,7 @@
/*/CMakeUserPresets.json
# Nested build directory
-/build_*
+/build*
#==============================================================================#
# Explicit files to ignore (only matches one).
@@ -66,12 +66,6 @@ AGENTS.md
.cursor
.cursorignore
.cursorindexingignore
-/.opencode/
-/.nvim/
-/.nvimlog
-/dhw-extension/
-/install*/
-/tmp/
#==============================================================================#
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
More information about the cfe-commits
mailing list