50 Commits

Author SHA1 Message Date
Marco Vitale
c86c815fc5
[Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (#145164)
C++23 mandates that temporaries used in range-based for loops are
lifetime-extended
to cover the full loop. This patch adds a check for loop variables and
compiler-
generated `__range` bindings to apply the correct extension.

Includes test cases based on examples from CWG900/P2644R1.

Fixes https://github.com/llvm/llvm-project/issues/109793
2025-07-10 09:57:07 +08:00
Aaron Ballman
0adf6e8d33
Work around a build issue with MSVC; NFC (#142195)
Microsoft helpfully defines `THIS` to `void` in two different platform
SDK headers, at least one of which is reachable via <Windows.h>. We have
a user who ran into a build because of `THIS` unfortunate macro name
collision.

Rename the members to better match our naming conventions.

Fixes #142186
2025-05-31 08:35:26 -04:00
Haojian Wu
9c49b188b8
[clang] Fix false positive regression for lifetime analysis warning. (#127460)
This fixes a false positive caused by #114044.

For `GSLPointer*` types, it's less clear whether the lifetime issue is
about the GSLPointer object itself or the owner it points to. To avoid
false positives, we take a conservative approach in our heuristic.

Fixes #127195

(This will be backported to release 20).
2025-02-17 14:40:31 +01:00
Haojian Wu
bd56950b9c
[clang] Refine the temporay object member access filtering for GSL pointer (#122088)
We currently have ad-hoc filtering logic for temporary object member
access in `VisitGSLPointerArg`. This logic filters out more cases than
it should, leading to false negatives. Furthermore, this location lacks
sufficient context to implement a more accurate solution.

This patch refines the filtering logic by moving it to the central
filtering location, `analyzePathForGSLPointer`, consolidating the logic
and avoiding scattered filtering across multiple places. As a result,
the special handling for conditional operators (#120233) is no longer
necessary.

This change also resolves #120543.
2025-01-22 14:11:16 +01:00
higher-performance
4cc9bf149f
Propagate lifetimebound from formal parameters to those in the canonical declaration and use that for analysis (#107627)
This partially fixes #62072 by making sure that re-declarations of a
function do not have the effect of removing lifetimebound from the
canonical declaration.

It doesn't handle the implicit 'this' parameter, but that can be
addressed in a separate fix.
2025-01-14 10:22:20 +01:00
Haojian Wu
1374aa35a3
[clang] Don't infer lifetime_capture-by for reference of raw pointer types. (#122240)
When a vector is instantiated with a pointer type (`T` being `const
Foo*`), the inferred annotation becomes `push_back(const Foo*& value
[[clang::lifetime_capture_by(this)]])`.

For reference parameters, the `lifetime_capture_by` attribute treats the
lifetime as referring to the referenced object -- in this case, the
**pointer** itself, not the pointee object. In the `push_back`, we copy
the pointer's value, which does not establish a reference to the
pointer. This behavior is safe and does not capture the pointer's
lifetime.

The annotation should not be inferred for cases where `T` is a pointer
type, as the intended semantics do not align with the annotation.

Fixes #121391
2025-01-09 16:26:56 +01:00
Haojian Wu
a6d26c56ff
[clang] Fix dangling false positives for conditional operators. (#120233)
When analyzing a dangling gsl pointer, we currently filter out all field
access `MemberExpr` to avoid common false positives (`string_view sv =
Temp().sv`), However, this filter only applies to direct MemberExpr
instances, leaving the conditional operator as an escaping example
(`GSLPointer pointer(Cond ? Owner().ptr : GSLPointer());`).

This patch extends the MemberExpr logic to handle the conditional
operator. The heuristic is intentionally simple, which may result in
some false negatives. However, it effectively covers common cases like
`std::string_view sv = cond ? "123" : std::string();`, which is a
reasonable trade-off.

Fixes https://github.com/llvm/llvm-project/issues/120206
2024-12-20 09:26:38 +01:00
Haojian Wu
eace8269d9 [clang] NFC, simplify the shouldLifetimeExtendThroughPath. 2024-12-19 13:05:57 +01:00
Haojian Wu
33b910cde3
[clang] Fix the post-filtering heuristic for GSLPointer. (#114044)
The lifetime analyzer processes GSL pointers:

- when encountering a constructor for a `gsl::pointer`, the analyzer
continues traversing the constructor argument, regardless of whether the
parameter has a `lifetimebound` annotation. This aims to catch cases
where a GSL pointer is constructed from a GSL owner, either directly
(e.g., `FooPointer(FooOwner)`) or through a chain of GSL pointers (e.g.,
`FooPointer(FooPointer(FooOwner))`);
- When a temporary object is reported in the callback, the analyzer has
heuristics to exclude non-owner types, aiming to avoid false positives
(like `FooPointer(FooPointer())`).

In the problematic case (discovered in
https://github.com/llvm/llvm-project/pull/112751#issuecomment-2441055471)
of `return foo.get();`:

- When the analyzer reports the local object `foo`, the `Path` is
`[GslPointerInit, Lifetimebound]`.
- The `Path` goes through
[`pathOnlyHandlesGslPointer`](https://github.com/llvm/llvm-project/blob/main/clang/lib/Sema/CheckExprLifetime.cpp#L1136)
and isn’t filtered out by the [[heuristics]](because `foo` is an owner
type), the analyzer treats it as the `FooPointer(FooOwner())` scenario,
thus triggering a diagnostic.

Filtering out base on the object 'foo' is wrong, because the GSLPointer
is constructed from the return result of the `foo.get()`. The patch
fixes this by teaching the heuristic to use the return result (only
`const GSLOwner&` is considered) of the lifetimebound annotated
function.
2024-12-12 20:57:39 +01:00
Haojian Wu
52690db47d
[clang] Fix -Wdangling false negative regressions caused by 117315 (#118088)
A specialization declaration can have an attribute even if the primary
template does not, particularly when the specialization is instantiated
from an annotated using-alias declaration.

Fix #118064
2024-11-29 23:48:09 +01:00
Haojian Wu
26baa00908
[clang] Diagnose dangling references for parenthesized aggregate initialization. (#117690)
Unlike brace initialization, the parenthesized aggregate initialization
in C++20 does not extend the lifetime of a temporary object bound to a
reference in an aggreate. This can lead to dangling references:

```
struct A { const int& r; };
 A a1(1); // well-formed, but results in a dangling reference.
 ``` 

With this patch, clang will diagnose this common dangling issues.

Fixes #101957
2024-11-29 10:15:19 +01:00
Utkarsh Saxena
12ccb628da
[clang] Add a common definition of isPointerLikeType for lifetime analysis (#117315)
Also checks for annotation for template specializations which sometimes
may not have the annotation attached.
2024-11-28 17:54:40 +05:30
Haojian Wu
6e720df1ae
[clang] Improve the lifetime_capture_by diagnostic on the constructor. (#117792)
With this change, the lifetime_capture_by code path will not handle the
constructor decl to avoid bogus diagnostics (see the testcase).

Instead, we reuse the lifetimebound code as the
lifetime_capture_by(this) has the same semantic as lifetimebound in
constructor. The downside is that the lifetimebound diagnostic is reused
for the capture case (I think it is not a big issue).


Fixes #117680
2024-11-28 12:35:15 +01:00
smanna12
2369a582c2
[Clang] Fix handling of non-member functions in isNormalAssignmentOperator() (#115880)
This patch correctes the handling of non-member functions in the
`isNormalAssignmentOperator` function within `CheckExprLifetime.cpp`.

The previous implementation incorrectly assumed that `FunctionDecl` is
always a `CXXMethodDecl`, leading to potential null pointer
dereferencing.

Change: - Correctly handle the case where `FD` is not a `CXXMethodDecl`
by using `FD->getParamDecl(0)->getType()`.

This fix ensures that the function correctly handles non-member
assignment operators, such as:

`struct S {}; void operator|=(S, S) {}`

This change improves the robustness of the `isNormalAssignmentOperator`
function by correctly identifying and handling different types of
function declarations.
2024-11-21 19:18:49 -06:00
Gábor Horváth
4862febdce
[clang][APINotes] Do not add duplicate lifetimebound annotations (#117194)
In case a method already is lifetimebound annotated we should not add a
second annotation to the type.
2024-11-21 23:25:37 +00:00
Utkarsh Saxena
c22bb6f5b1
[clang] Implement lifetime analysis for lifetime_capture_by(X) (#115921)
This PR uses the existing lifetime analysis for the `capture_by`
attribute.

The analysis is behind `-Wdangling-capture` warning and is disabled by
default for now. Once it is found to be stable, it will be default
enabled.

Planned followup:
- add implicit inference of this attribute on STL container methods like
`std::vector::push_back`.
- (consider) warning if capturing `X` cannot capture anything. It should
be a reference, pointer or a view type.
- refactoring temporary visitors and other related handlers.
- start discussing `__global` vs `global` in the annotation in a
separate PR.

---------

Co-authored-by: Boaz Brickner <brickner@google.com>
2024-11-20 15:17:00 +01:00
Boaz Brickner
91c1699943
[clang] [NFC] Merge conditions (#116612) 2024-11-19 09:57:00 +01:00
Haojian Wu
2804762e26 [clang][NFC] Use const reference for IndirectLocalPath if possible. 2024-11-02 12:09:52 +01:00
Haojian Wu
67c8b0efbe [clang][NFC] Remove an unnecessary variable in CheckExprLifetime.cpp 2024-11-02 12:00:18 +01:00
Haojian Wu
f484a04d79
[clang] Suppress a dangling false positive when owner is moved in member initializer. (#114213)
This patch extends the filtering heuristic to apply for the
Lifetimebound code path.

This will suppress a common false positive:

```
namespace std {
template<typename T>
struct unique_ptr {
  T &operator*();
  T *get() const [[clang::lifetimebound]];
};
} // namespace std

struct X {
  X(std::unique_ptr<int> up) :
    pointer(up.get()), owner(std::move(up)) {}

  int *pointer;
  std::unique_ptr<int> owner;
};
```

See #114201.
2024-11-01 13:32:57 +01:00
Boaz Brickner
f490697cb9
[clang] [NFC] Fix a couple of typos: assuments and assingment 2024-10-29 15:08:24 +01:00
Haojian Wu
a6d6c00fe7
[clang] Lifetimebound in assignment operator should work for non-gsl annotated types. (#113180)
This issue is identified during the discussion of [this
comment](https://github.com/llvm/llvm-project/issues/112234#issuecomment-2426102198).

There will be no release note for this fix as it is a follow-up to
[#106997](https://github.com/llvm/llvm-project/pull/106997).
2024-10-22 14:22:06 +02:00
higher-performance
dd47920ce9
Make [[clang::lifetimebound]] work for expressions coming from default arguments (#112047)
Fixes #68596.
2024-10-15 13:41:52 -07:00
Haojian Wu
0eaccee180
[Clang] Diagnose dangling references in std::vector. (#111753)
This is a follow-up to https://github.com/llvm/llvm-project/pull/108344.

The original bailout check was overly strict, causing it to miss cases
like the vector(initializer_list, allocator) constructor. This patch
relaxes the check to address that issue.

Fix #111680
2024-10-14 21:09:00 +02:00
Haojian Wu
fe06a6daae
Reland: [clang] Diagnose dangling issues for the "Container<GSLPointer>" case. #107213 (#108344)
This relands #107213, with with fixes to address false positives
(`make_optional(nullptr)`).
2024-09-25 14:12:49 +02:00
Oliver Stannard
0b0a37e158
[clang] Lifetime of locals must end before musttail call (#109255)
The lifetimes of local variables and function parameters must end before
the call to a [[clang::musttail]] function, instead of before the
return, because we will not have a stack frame to hold them when doing
the call.

This documents this limitation, and adds diagnostics to warn about some
code which is invalid because of it.
2024-09-23 10:04:08 +01:00
Haojian Wu
abe964aa47
[clang] Don't emit bogus dangling diagnostics when [[gsl::Owner]] and [[clang::lifetimebound]] are used together. (#108280)
In the GSL analysis, we don't track the `this` object if the conversion
is not from gsl::owner to gsl pointer, we want to be conservative here
to avoid triggering false positives.

Fixes #108272
2024-09-16 15:21:33 +02:00
Haojian Wu
0683c4e839 Revert "[clang] Diagnose dangling issues for the "Container<GSLPointer>" case. (#107213)"
This reverts commit e50131aa068f74daa70d4135c92020aadae3af33.

It introduces a new false positive, see comment https://github.com/llvm/llvm-project/pull/107213#issuecomment-2345465256
2024-09-12 09:24:32 +02:00
yronglin
060137038a
Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" (#108039)
The PR reapply https://github.com/llvm/llvm-project/pull/97308. 

- Implement [CWG1815](https://wg21.link/CWG1815): Support lifetime
extension of temporary created by aggregate initialization using a
default member initializer.

- Fix crash that introduced in
https://github.com/llvm/llvm-project/pull/97308. In
`InitListChecker::FillInEmptyInitForField`, when we enter
rebuild-default-init context, we copy all the contents of the parent
context to the current context, which will cause the `MaybeODRUseExprs`
to be lost. But we don't need to copy the entire context, only the
`DelayedDefaultInitializationContext` was required, which is used to
build `SourceLocExpr`, etc.

---------

Signed-off-by: yronglin <yronglin777@gmail.com>
2024-09-12 06:29:48 +08:00
Haojian Wu
e50131aa06
[clang] Diagnose dangling issues for the "Container<GSLPointer>" case. (#107213)
This pull request enhances the GSL lifetime analysis to detect
situations where a dangling `Container<GSLPointer>` object is
constructed:

```cpp
std::vector<std::string_view> bad = {std::string()}; // dangling
```

The assignment case is not yet supported, but they will be addressed in
a follow-up.

Fixes #100526 (excluding the `push_back` case).
2024-09-11 13:20:59 +02:00
Martin Storsjö
cca54e347a Revert "Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" (#97308)"
This reverts commit 45c8766973bb3bb73dd8d996231e114dcf45df9f
and 049512e39d96995cb373a76cf2d009a86eaf3aab.

This change triggers failed asserts on inputs like this:

    struct a {
    } constexpr b;
    class c {
    public:
      c(a);
    };
    class B {
    public:
      using d = int;
      struct e {
        enum { f } g;
        int h;
        c i;
        d j{};
      };
    };
    B::e k{B::e::f, int(), b};

Compiled like this:

    clang -target x86_64-linux-gnu -c repro.cpp
    clang: ../../clang/lib/CodeGen/CGExpr.cpp:3105: clang::CodeGen::LValue
    clang::CodeGen::CodeGenFunction::EmitDeclRefLValue(const clang::DeclRefExpr*):
    Assertion `(ND->isUsed(false) || !isa<VarDecl>(ND) || E->isNonOdrUse() ||
    !E->getLocation().isValid()) && "Should not use decl without marking it used!"' failed.
2024-09-09 15:09:45 +03:00
yronglin
45c8766973
Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" (#97308)
The PR reapply https://github.com/llvm/llvm-project/pull/92527.
Implemented CWG1815 and fixed the bugs mentioned in the comments of
https://github.com/llvm/llvm-project/pull/92527 and
https://github.com/llvm/llvm-project/pull/87933.

The reason why the original PR was reverted was that errors might occur
during the rebuild.

---------

Signed-off-by: yronglin <yronglin777@gmail.com>
2024-09-08 22:36:49 +08:00
Haojian Wu
a918fa117a
[clang] Emit -Wdangling diagnoses for cases where a gsl-pointer is construct from a gsl-owner object in a container. (#104556)
The warning is not emitted for the case `string_view c =
std::vector<std::string>({""}).at(0);` because we bail out during the
visit of the LHS at [this
point](5d2c324fea/clang/lib/Sema/CheckExprLifetime.cpp (L341-L343))
in the code.

This bailout was introduced in [this
commit](bcd0798c47)
to address a false positive with
`vector<vector::iterator>({""}).at(0);`. This PR refines that fix by
ensuring that, for initialization involving a gsl-pointer, we only
consider constructor calls that take the gsl-owner object.

Fixes #100384.
2024-09-06 12:37:21 +02:00
Haojian Wu
3e070906ef Fix llvm-else-after-return clang-tidy warning in CheckExprLifetime.cpp, NFC 2024-09-05 13:24:38 +02:00
Haojian Wu
87b4b64858 Fix a typo in CheckExprLifetime.cpp, NFC 2024-09-05 13:18:39 +02:00
Haojian Wu
d94199c8ff
[clang] Make lifetimebound and GSL analysis more coherent (#105884)
This allows clang to detect more use-after-free bugs (shown in the
#100549).

This relands the remaining change (removing the EnableLifetimeWarnings
flag) in https://github.com/llvm/llvm-project/pull/104906, with a proper
fix for the regression.

Fixes #100549
2024-09-04 13:49:48 +02:00
Haojian Wu
01e5684900
[clang] Respect the lifetimebound in assignment operator. (#106997)
Fixes #106372
2024-09-04 10:09:04 +02:00
Haojian Wu
7b2fe84ff5 Don't run the lifetime analysis for pointer assignment if the warning is
disabled.
2024-09-01 16:19:18 +02:00
Haojian Wu
b1560bdb2b
Reland "[clang] Merge lifetimebound and GSL code paths for lifetime analysis (#104906)" (#105838)
Reland without the `EnableLifetimeWarnings` removal. I will remove the
EnableLifetimeWarnings in a follow-up patch.

I have added a test to prevent regression.
2024-08-23 17:50:27 +02:00
Vitaly Buka
1df15042bd
Revert "[clang] Merge lifetimebound and GSL code paths for lifetime analysis (#104906)" (#105752)
Revert as it breaks libc++ tests, see #104906.

This reverts commit c368a720a0b40bb8fe4aff3971fe9a7009c85aa6.
2024-08-22 16:38:19 -07:00
Haojian Wu
c368a720a0
[clang] Merge lifetimebound and GSL code paths for lifetime analysis (#104906)
In the current lifetime analysis, we have two parallel code paths: one
for lifetimebound and another for GSL. These paths perform the same
logic, both determining whether to continue visiting subexpressions.

This PR merges the two paths into a single code path. As a result, we'll
reduce the overhead by eliminating a redundant visit to subexpressions.
The change is mostly NFC (No Functional Change). The only notable
difference is that when a subexpression is visited due to either
lifetimebound or GSL, we will prioritize the lifetimebound path. This
means the final diagnostic will be -Wdangling (rather than both
`-Wdangling` and `-Wdangling-gsl`)

This might cause a slight change in behavior if the -Wdangling
diagnostic is disabled, but I think this is not a major concern since
both diagnostics are enabled by default.

Fixes #93386
2024-08-22 10:35:49 +02:00
Haojian Wu
5d2c324fea Remove a leftover debug-only statement in CheckExprLifetime.cpp 2024-07-29 09:22:03 +02:00
Utkarsh Saxena
1feef92a77
Fix lifetimebound for field access (#100197)
Fixes: https://github.com/llvm/llvm-project/issues/81589

There is no way to switch this off without  `-Wno-dangling`.
2024-07-24 15:58:52 +02:00
Haojian Wu
71e2b8df30
[clang] NFC, simplify the code in CheckExprLifetime.cpp (#99637)
No need to get the Owner/Pointer attr via the type. The Decl has this
attr information.
2024-07-19 16:46:31 +02:00
Haojian Wu
3eba28d1fd
[clang] Extend lifetime analysis to support assignments for pointer-like objects. (#99032)
This is a follow-up patch to #96475 to detect dangling assignments for
C++ pointer-like objects (classes annotated with the
`[[gsl::Pointer]]`). Fixes #63310.

Similar to the behavior for built-in pointer types, if a temporary owner
(`[[gsl::Owner]]`) object is assigned to a pointer-like class object,
and this temporary object is destroyed at the end of the full assignment
expression, the assignee pointer is considered dangling. In such cases,
clang will emit a warning:

```
/tmp/t.cpp:7:20: warning: object backing the pointer my_string_view will be destroyed at the end of the full-expression [-Wdangling-assignment-gsl]
    7 |   my_string_view = CreateString();
      |                    ^~~~~~~~~~~~~~
1 warning generated.
```

This new warning is `-Wdangling-assignment-gsl`. It is initially
disabled, but I intend to enable it by default in clang 20.

I have initially tested this patch on our internal codebase, and it has
identified many use-after-free bugs, primarily related to `string_view`.
2024-07-18 10:02:35 +02:00
Haojian Wu
c30ce8b9d3
[clang] Refactor: Introduce a new LifetimeKind for the assignment case, NFC (#99005)
The current implementation for the assignment case uses a combination of
the `LK_Extended` lifetime kind and the validity of `AEntity`, which is
somewhat messy and doesn't align well with the intended mental model.

This patch introduces a dedicated lifetime kind to handle the assignment
case, simplifying the implementation and improving clarity.
2024-07-16 11:23:39 +02:00
Haojian Wu
c3079ffcd3
[clang] Don't emit the warn_dangling_lifetime_pointer diagnostic for the assignment case. (#97408)
The `lifetime_pointer` case is handled before the assignment case. In
scenarios where we have the `gsl::Pointer` attribute, we may emit the
`-Wdangling-gsl` warning for assignment cases. This means we cannot use
`-Wno-dangling-assignment` to suppress the newly-added warning, this
patch fixes it.
2024-07-02 15:21:06 +02:00
Gábor Horváth
ffca4ef5b1
[clang] Remove a pointer union from the lifetime bound analysis (#97327)
Since all callers of the API is called with the same type, we do not
actually need the pointer union.
2024-07-01 23:56:59 +01:00
Haojian Wu
5c287efee7
[Clang] Extend lifetime bound analysis to support assignments for the built-in pointer type (#96475)
The lifetime bound warning in Clang currently only considers
initializations. This patch extends the warning to include assignments.

- **Support for assignments of built-in pointer types**: this is done is
by reusing the existing statement-local implementation. Clang now warns
if the pointer is assigned to a temporary object that being destoryed at
the end of the full assignment expression.

With this patch, we will detect more cases under the on-by-default
diagnostic `-Wdangling`. I have added a new category for this specific
diagnostic so that people can temporarily disable it if their codebase
is not yet clean.

This is the first step to address #63310, focusing only on pointer
types. Support for C++ assignment operators will come in a follow-up
patch.

Fixes #54492
2024-07-01 17:43:07 +02:00
Haojian Wu
8a43dc3efd
[clang][Sema] Move the initializer lifetime checking code from SemaInit.cpp to a new place, NFC (#96758)
This is a refactoring change for better code isolation and reuse, the
first step to extend it for assignments.
2024-06-27 10:56:06 +02:00