-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[clang-tidy] [dataflow] Cache reference accessors for bugprone-unchecked-optional-access
#128437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
Outdated
Show resolved
Hide resolved
clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
Outdated
Show resolved
Hide resolved
clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
Outdated
Show resolved
Hide resolved
clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
Outdated
Show resolved
Hide resolved
clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
Outdated
Show resolved
Hide resolved
clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
Outdated
Show resolved
Hide resolved
Addressed all comments. Thanks for the initial feedback! Regarding documentation: looks like we need to merge #122290 first and then update docs in my PR |
@llvm/pr-subscribers-clang Author: Valentyn Yukhymenko (BaLiKfromUA) ChangesFixes #126283 Extending #112605 to cache const getters which return references. This should fix false positive cases when we check optional via the chain of const getter calls. Full diff: https://github.com/llvm/llvm-project/pull/128437.diff 3 Files Affected:
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3bddeeda06e06..dfa4cb1d47150 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -100,6 +100,8 @@ Changes in existing checks
- Improved :doc:`bugprone-unsafe-functions
<clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
additional C++ member functions to match.
+- Improved :doc:`bugprone-unchecked-optional-access
+ <clang-tidy/checks/bugprone/unchecked-optional-access>` by fixing false positive from const reference accessors to objects containing optional member.
Removed checks
^^^^^^^^^^^^^^
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index e1394e28cd49a..9381c5c42e566 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -580,6 +580,22 @@ void handleConstMemberCall(const CallExpr *CE,
return;
}
+ // Cache if the const method returns a reference
+ if (RecordLoc != nullptr && CE->isGLValue()) {
+ const FunctionDecl *DirectCallee = CE->getDirectCallee();
+ if (DirectCallee == nullptr)
+ return;
+
+ StorageLocation &Loc =
+ State.Lattice.getOrCreateConstMethodReturnStorageLocation(
+ *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
+ // no-op
+ });
+
+ State.Env.setStorageLocation(*CE, Loc);
+ return;
+ }
+
// Cache if the const method returns a boolean or pointer type.
// We may decide to cache other return types in the future.
if (RecordLoc != nullptr &&
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
index 19c3ff49eab27..5031e17188e17 100644
--- a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -3863,6 +3863,200 @@ TEST_P(UncheckedOptionalAccessTest, ConstBoolAccessorWithModInBetween) {
)cc");
}
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObject) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.getA().get().has_value()) {
+ b.getA().get().value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(
+ UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithoutValueCheck) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ b.getA().get().value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefToOptionalSavedAsTemporaryVariable) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ const auto& opt = b.getA().get();
+ if (opt.has_value()) {
+ opt.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaAccessorToHoldingObjectByValue) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A copyA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.copyA().get().has_value()) {
+ b.copyA().get().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaNonConstRefAccessorToHoldingObject) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ A& getA() { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.getA().get().has_value()) {
+ b.getA().get().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(
+ UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithModAfterCheck) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A& getA() { return a; }
+
+ void clear() { a = A{}; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ // changing field A via non-const getter after const getter check
+ if (b.getA().get().has_value()) {
+ b.getA() = A{};
+ b.getA().get().value(); // [[unsafe]]
+ }
+
+ // calling non-const method which might change field A
+ if (b.getA().get().has_value()) {
+ b.clear();
+ b.getA().get().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(
+ UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithAnotherConstCallAfterCheck) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ void callWithoutChanges() const {
+ // no-op
+ }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.getA().get().has_value()) {
+ b.callWithoutChanges(); // calling const method which cannot change A
+ b.getA().get().value();
+ }
+ }
+ )cc");
+}
+
// FIXME: Add support for:
// - constructors (copy, move)
// - assignment operators (default, copy, move)
|
@llvm/pr-subscribers-clang-analysis Author: Valentyn Yukhymenko (BaLiKfromUA) ChangesFixes #126283 Extending #112605 to cache const getters which return references. This should fix false positive cases when we check optional via the chain of const getter calls. Full diff: https://github.com/llvm/llvm-project/pull/128437.diff 3 Files Affected:
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3bddeeda06e06..dfa4cb1d47150 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -100,6 +100,8 @@ Changes in existing checks
- Improved :doc:`bugprone-unsafe-functions
<clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
additional C++ member functions to match.
+- Improved :doc:`bugprone-unchecked-optional-access
+ <clang-tidy/checks/bugprone/unchecked-optional-access>` by fixing false positive from const reference accessors to objects containing optional member.
Removed checks
^^^^^^^^^^^^^^
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index e1394e28cd49a..9381c5c42e566 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -580,6 +580,22 @@ void handleConstMemberCall(const CallExpr *CE,
return;
}
+ // Cache if the const method returns a reference
+ if (RecordLoc != nullptr && CE->isGLValue()) {
+ const FunctionDecl *DirectCallee = CE->getDirectCallee();
+ if (DirectCallee == nullptr)
+ return;
+
+ StorageLocation &Loc =
+ State.Lattice.getOrCreateConstMethodReturnStorageLocation(
+ *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
+ // no-op
+ });
+
+ State.Env.setStorageLocation(*CE, Loc);
+ return;
+ }
+
// Cache if the const method returns a boolean or pointer type.
// We may decide to cache other return types in the future.
if (RecordLoc != nullptr &&
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
index 19c3ff49eab27..5031e17188e17 100644
--- a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -3863,6 +3863,200 @@ TEST_P(UncheckedOptionalAccessTest, ConstBoolAccessorWithModInBetween) {
)cc");
}
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObject) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.getA().get().has_value()) {
+ b.getA().get().value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(
+ UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithoutValueCheck) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ b.getA().get().value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefToOptionalSavedAsTemporaryVariable) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ const auto& opt = b.getA().get();
+ if (opt.has_value()) {
+ opt.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaAccessorToHoldingObjectByValue) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A copyA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.copyA().get().has_value()) {
+ b.copyA().get().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaNonConstRefAccessorToHoldingObject) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ A& getA() { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.getA().get().has_value()) {
+ b.getA().get().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(
+ UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithModAfterCheck) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A& getA() { return a; }
+
+ void clear() { a = A{}; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ // changing field A via non-const getter after const getter check
+ if (b.getA().get().has_value()) {
+ b.getA() = A{};
+ b.getA().get().value(); // [[unsafe]]
+ }
+
+ // calling non-const method which might change field A
+ if (b.getA().get().has_value()) {
+ b.clear();
+ b.getA().get().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(
+ UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithAnotherConstCallAfterCheck) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ void callWithoutChanges() const {
+ // no-op
+ }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.getA().get().has_value()) {
+ b.callWithoutChanges(); // calling const method which cannot change A
+ b.getA().get().value();
+ }
+ }
+ )cc");
+}
+
// FIXME: Add support for:
// - constructors (copy, move)
// - assignment operators (default, copy, move)
|
@llvm/pr-subscribers-clang-tools-extra Author: Valentyn Yukhymenko (BaLiKfromUA) ChangesFixes #126283 Extending #112605 to cache const getters which return references. This should fix false positive cases when we check optional via the chain of const getter calls. Full diff: https://github.com/llvm/llvm-project/pull/128437.diff 3 Files Affected:
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3bddeeda06e06..dfa4cb1d47150 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -100,6 +100,8 @@ Changes in existing checks
- Improved :doc:`bugprone-unsafe-functions
<clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
additional C++ member functions to match.
+- Improved :doc:`bugprone-unchecked-optional-access
+ <clang-tidy/checks/bugprone/unchecked-optional-access>` by fixing false positive from const reference accessors to objects containing optional member.
Removed checks
^^^^^^^^^^^^^^
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index e1394e28cd49a..9381c5c42e566 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -580,6 +580,22 @@ void handleConstMemberCall(const CallExpr *CE,
return;
}
+ // Cache if the const method returns a reference
+ if (RecordLoc != nullptr && CE->isGLValue()) {
+ const FunctionDecl *DirectCallee = CE->getDirectCallee();
+ if (DirectCallee == nullptr)
+ return;
+
+ StorageLocation &Loc =
+ State.Lattice.getOrCreateConstMethodReturnStorageLocation(
+ *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
+ // no-op
+ });
+
+ State.Env.setStorageLocation(*CE, Loc);
+ return;
+ }
+
// Cache if the const method returns a boolean or pointer type.
// We may decide to cache other return types in the future.
if (RecordLoc != nullptr &&
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
index 19c3ff49eab27..5031e17188e17 100644
--- a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -3863,6 +3863,200 @@ TEST_P(UncheckedOptionalAccessTest, ConstBoolAccessorWithModInBetween) {
)cc");
}
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObject) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.getA().get().has_value()) {
+ b.getA().get().value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(
+ UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithoutValueCheck) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ b.getA().get().value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefToOptionalSavedAsTemporaryVariable) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ const auto& opt = b.getA().get();
+ if (opt.has_value()) {
+ opt.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaAccessorToHoldingObjectByValue) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A copyA() const { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.copyA().get().has_value()) {
+ b.copyA().get().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaNonConstRefAccessorToHoldingObject) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ A& getA() { return a; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.getA().get().has_value()) {
+ b.getA().get().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(
+ UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithModAfterCheck) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ A& getA() { return a; }
+
+ void clear() { a = A{}; }
+
+ A a;
+ };
+
+ void target(B& b) {
+ // changing field A via non-const getter after const getter check
+ if (b.getA().get().has_value()) {
+ b.getA() = A{};
+ b.getA().get().value(); // [[unsafe]]
+ }
+
+ // calling non-const method which might change field A
+ if (b.getA().get().has_value()) {
+ b.clear();
+ b.getA().get().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(
+ UncheckedOptionalAccessTest,
+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithAnotherConstCallAfterCheck) {
+ ExpectDiagnosticsFor(R"cc(
+ #include "unchecked_optional_access_test.h"
+
+ struct A {
+ const $ns::$optional<int>& get() const { return x; }
+
+ $ns::$optional<int> x;
+ };
+
+ struct B {
+ const A& getA() const { return a; }
+
+ void callWithoutChanges() const {
+ // no-op
+ }
+
+ A a;
+ };
+
+ void target(B& b) {
+ if (b.getA().get().has_value()) {
+ b.callWithoutChanges(); // calling const method which cannot change A
+ b.getA().get().value();
+ }
+ }
+ )cc");
+}
+
// FIXME: Add support for:
// - constructors (copy, move)
// - assignment operators (default, copy, move)
|
Thanks for the reminder about #122290 ! Got back to that and merged. Can you update? It looked like the latest release notes had more entries under "Changes in existing checks" like "bugprone-unsafe-functions" ? |
Updated release notes. Not sure if I need to update |
Agreed that the " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Could someone merge it? Or do we need another reviewer? I don't have merge permissions :) |
…cked-optional-access` (llvm#128437) Fixes llvm#126283 Extending llvm#112605 to cache const getters which return references. Fixes false positives from const reference accessors to object containing optional member
…r handling Add test for llvm#125589 The crash is actually incidentally fixed by llvm#128437 since it added a branch for the reference case and would no longer fall through when the return type is a reference to a pointer. Clean up a bit as well: - make the fallback for early returns more consistent (check if returning optional and call transfer function for that case) - check RecordLoc == nullptr in one place Add some init for the reference to pointer/bool cases.
…r handling (#129930) Add test for #125589 The crash is actually incidentally fixed by #128437 since it added a branch for the reference case and would no longer fall through when the return type is a reference to a pointer. Clean up a bit as well: - make the fallback for early returns more consistent (check if returning optional and call transfer function for that case) - check RecordLoc == nullptr in one place - clean up extra spaces in test - clean up parameterization in test of `std::` vs `$ns::$`
…nst accessor handling (#129930) Add test for llvm/llvm-project#125589 The crash is actually incidentally fixed by llvm/llvm-project#128437 since it added a branch for the reference case and would no longer fall through when the return type is a reference to a pointer. Clean up a bit as well: - make the fallback for early returns more consistent (check if returning optional and call transfer function for that case) - check RecordLoc == nullptr in one place - clean up extra spaces in test - clean up parameterization in test of `std::` vs `$ns::$`
…r handling (llvm#129930) Add test for llvm#125589 The crash is actually incidentally fixed by llvm#128437 since it added a branch for the reference case and would no longer fall through when the return type is a reference to a pointer. Clean up a bit as well: - make the fallback for early returns more consistent (check if returning optional and call transfer function for that case) - check RecordLoc == nullptr in one place - clean up extra spaces in test - clean up parameterization in test of `std::` vs `$ns::$`
Fixes #126283
Extending #112605 to cache const getters which return references.
Fixes false positives from const reference accessors to object containing optional member