@@ -3863,6 +3863,200 @@ TEST_P(UncheckedOptionalAccessTest, ConstBoolAccessorWithModInBetween) {
38633863 )cc" );
38643864}
38653865
3866+ TEST_P (UncheckedOptionalAccessTest,
3867+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObject) {
3868+ ExpectDiagnosticsFor (R"cc(
3869+ #include "unchecked_optional_access_test.h"
3870+
3871+ struct A {
3872+ const $ns::$optional<int>& get() const { return x; }
3873+
3874+ $ns::$optional<int> x;
3875+ };
3876+
3877+ struct B {
3878+ const A& getA() const { return a; }
3879+
3880+ A a;
3881+ };
3882+
3883+ void target(B& b) {
3884+ if (b.getA().get().has_value()) {
3885+ b.getA().get().value();
3886+ }
3887+ }
3888+ )cc" );
3889+ }
3890+
3891+ TEST_P (
3892+ UncheckedOptionalAccessTest,
3893+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithoutValueCheck) {
3894+ ExpectDiagnosticsFor (R"cc(
3895+ #include "unchecked_optional_access_test.h"
3896+
3897+ struct A {
3898+ const $ns::$optional<int>& get() const { return x; }
3899+
3900+ $ns::$optional<int> x;
3901+ };
3902+
3903+ struct B {
3904+ const A& getA() const { return a; }
3905+
3906+ A a;
3907+ };
3908+
3909+ void target(B& b) {
3910+ b.getA().get().value(); // [[unsafe]]
3911+ }
3912+ )cc" );
3913+ }
3914+
3915+ TEST_P (UncheckedOptionalAccessTest,
3916+ ConstRefToOptionalSavedAsTemporaryVariable) {
3917+ ExpectDiagnosticsFor (R"cc(
3918+ #include "unchecked_optional_access_test.h"
3919+
3920+ struct A {
3921+ const $ns::$optional<int>& get() const { return x; }
3922+
3923+ $ns::$optional<int> x;
3924+ };
3925+
3926+ struct B {
3927+ const A& getA() const { return a; }
3928+
3929+ A a;
3930+ };
3931+
3932+ void target(B& b) {
3933+ const auto& opt = b.getA().get();
3934+ if (opt.has_value()) {
3935+ opt.value();
3936+ }
3937+ }
3938+ )cc" );
3939+ }
3940+
3941+ TEST_P (UncheckedOptionalAccessTest,
3942+ ConstRefAccessorToOptionalViaAccessorToHoldingObjectByValue) {
3943+ ExpectDiagnosticsFor (R"cc(
3944+ #include "unchecked_optional_access_test.h"
3945+
3946+ struct A {
3947+ const $ns::$optional<int>& get() const { return x; }
3948+
3949+ $ns::$optional<int> x;
3950+ };
3951+
3952+ struct B {
3953+ const A copyA() const { return a; }
3954+
3955+ A a;
3956+ };
3957+
3958+ void target(B& b) {
3959+ if (b.copyA().get().has_value()) {
3960+ b.copyA().get().value(); // [[unsafe]]
3961+ }
3962+ }
3963+ )cc" );
3964+ }
3965+
3966+ TEST_P (UncheckedOptionalAccessTest,
3967+ ConstRefAccessorToOptionalViaNonConstRefAccessorToHoldingObject) {
3968+ ExpectDiagnosticsFor (R"cc(
3969+ #include "unchecked_optional_access_test.h"
3970+
3971+ struct A {
3972+ const $ns::$optional<int>& get() const { return x; }
3973+
3974+ $ns::$optional<int> x;
3975+ };
3976+
3977+ struct B {
3978+ A& getA() { return a; }
3979+
3980+ A a;
3981+ };
3982+
3983+ void target(B& b) {
3984+ if (b.getA().get().has_value()) {
3985+ b.getA().get().value(); // [[unsafe]]
3986+ }
3987+ }
3988+ )cc" );
3989+ }
3990+
3991+ TEST_P (
3992+ UncheckedOptionalAccessTest,
3993+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithModAfterCheck) {
3994+ ExpectDiagnosticsFor (R"cc(
3995+ #include "unchecked_optional_access_test.h"
3996+
3997+ struct A {
3998+ const $ns::$optional<int>& get() const { return x; }
3999+
4000+ $ns::$optional<int> x;
4001+ };
4002+
4003+ struct B {
4004+ const A& getA() const { return a; }
4005+
4006+ A& getA() { return a; }
4007+
4008+ void clear() { a = A{}; }
4009+
4010+ A a;
4011+ };
4012+
4013+ void target(B& b) {
4014+ // changing field A via non-const getter after const getter check
4015+ if (b.getA().get().has_value()) {
4016+ b.getA() = A{};
4017+ b.getA().get().value(); // [[unsafe]]
4018+ }
4019+
4020+ // calling non-const method which might change field A
4021+ if (b.getA().get().has_value()) {
4022+ b.clear();
4023+ b.getA().get().value(); // [[unsafe]]
4024+ }
4025+ }
4026+ )cc" );
4027+ }
4028+
4029+ TEST_P (
4030+ UncheckedOptionalAccessTest,
4031+ ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithAnotherConstCallAfterCheck) {
4032+ ExpectDiagnosticsFor (R"cc(
4033+ #include "unchecked_optional_access_test.h"
4034+
4035+ struct A {
4036+ const $ns::$optional<int>& get() const { return x; }
4037+
4038+ $ns::$optional<int> x;
4039+ };
4040+
4041+ struct B {
4042+ const A& getA() const { return a; }
4043+
4044+ void callWithoutChanges() const {
4045+ // no-op
4046+ }
4047+
4048+ A a;
4049+ };
4050+
4051+ void target(B& b) {
4052+ if (b.getA().get().has_value()) {
4053+ b.callWithoutChanges(); // calling const method which cannot change A
4054+ b.getA().get().value();
4055+ }
4056+ }
4057+ )cc" );
4058+ }
4059+
38664060// FIXME: Add support for:
38674061// - constructors (copy, move)
38684062// - assignment operators (default, copy, move)
0 commit comments