2903 lines · cpp
1//===- UncheckedOptionalAccessModelTest.cpp -------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8// FIXME: Move this to clang/unittests/Analysis/FlowSensitive/Models.9 10#include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"11#include "MockHeaders.h"12#include "TestingSupport.h"13#include "clang/AST/ASTContext.h"14#include "clang/ASTMatchers/ASTMatchers.h"15#include "clang/Basic/SourceLocation.h"16#include "clang/Frontend/TextDiagnostic.h"17#include "clang/Tooling/Tooling.h"18#include "llvm/ADT/DenseMap.h"19#include "llvm/ADT/DenseSet.h"20#include "llvm/ADT/STLExtras.h"21#include "llvm/Support/Error.h"22#include "gmock/gmock.h"23#include "gtest/gtest.h"24#include <optional>25#include <string>26#include <utility>27#include <vector>28 29using namespace clang;30using namespace dataflow;31using namespace test;32 33using ::testing::ContainerEq;34 35/// Replaces all occurrences of `Pattern` in `S` with `Replacement`.36static void ReplaceAllOccurrences(std::string &S, const std::string &Pattern,37 const std::string &Replacement) {38 size_t Pos = 0;39 while (true) {40 Pos = S.find(Pattern, Pos);41 if (Pos == std::string::npos)42 break;43 S.replace(Pos, Pattern.size(), Replacement);44 }45}46 47struct OptionalTypeIdentifier {48 std::string NamespaceName;49 std::string TypeName;50};51 52static raw_ostream &operator<<(raw_ostream &OS,53 const OptionalTypeIdentifier &TypeId) {54 OS << TypeId.NamespaceName << "::" << TypeId.TypeName;55 return OS;56}57 58class UncheckedOptionalAccessTest59 : public ::testing::TestWithParam<OptionalTypeIdentifier> {60protected:61 // Check that after running the analysis on SourceCode, it produces the62 // expected diagnostics according to [[unsafe]] annotations.63 // - No annotations => no diagnostics.64 // - Given "// [[unsafe]]" annotations on a line, we expect a diagnostic on65 // that line.66 // - Given "// [[unsafe:range_text]]" annotations on a line, we expect a67 // diagnostic on that line, and we expect the diagnostic Range (printed as68 // a string) to match the "range_text".69 void ExpectDiagnosticsFor(std::string SourceCode,70 bool IgnoreSmartPointerDereference = true) {71 ExpectDiagnosticsFor(SourceCode, ast_matchers::hasName("target"),72 IgnoreSmartPointerDereference);73 }74 75 void ExpectDiagnosticsForLambda(std::string SourceCode,76 bool IgnoreSmartPointerDereference = true) {77 ExpectDiagnosticsFor(78 SourceCode,79 ast_matchers::hasDeclContext(80 ast_matchers::cxxRecordDecl(ast_matchers::isLambda())),81 IgnoreSmartPointerDereference);82 }83 84 template <typename FuncDeclMatcher>85 void ExpectDiagnosticsFor(std::string SourceCode, FuncDeclMatcher FuncMatcher,86 bool IgnoreSmartPointerDereference = true) {87 // Run in C++17 and C++20 mode to cover differences in the AST between modes88 // (e.g. C++20 can contain `CXXRewrittenBinaryOperator`).89 for (const char *CxxMode : {"-std=c++17", "-std=c++20"})90 ExpectDiagnosticsFor(SourceCode, FuncMatcher, CxxMode,91 IgnoreSmartPointerDereference);92 }93 94 template <typename FuncDeclMatcher>95 void ExpectDiagnosticsFor(std::string SourceCode, FuncDeclMatcher FuncMatcher,96 const char *CxxMode,97 bool IgnoreSmartPointerDereference) {98 ReplaceAllOccurrences(SourceCode, "$ns", GetParam().NamespaceName);99 ReplaceAllOccurrences(SourceCode, "$optional", GetParam().TypeName);100 101 auto Headers = getMockHeaders();102 Headers.emplace_back("unchecked_optional_access_test.h", R"(103 #include "absl_optional.h"104 #include "base_optional.h"105 #include "std_initializer_list.h"106 #include "std_optional.h"107 #include "std_string.h"108 #include "std_utility.h"109 110 template <typename T>111 T Make();112 )");113 UncheckedOptionalAccessModelOptions Options{IgnoreSmartPointerDereference};114 std::vector<UncheckedOptionalAccessDiagnostic> Diagnostics;115 llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>(116 AnalysisInputs<UncheckedOptionalAccessModel>(117 SourceCode, std::move(FuncMatcher),118 [](ASTContext &Ctx, Environment &Env) {119 return UncheckedOptionalAccessModel(Ctx, Env);120 })121 .withDiagnosisCallbacks(122 {/*Before=*/[&Diagnostics,123 Diagnoser =124 UncheckedOptionalAccessDiagnoser(Options)](125 ASTContext &Ctx, const CFGElement &Elt,126 const TransferStateForDiagnostics<127 UncheckedOptionalAccessLattice>128 &State) mutable {129 auto EltDiagnostics = Diagnoser(Elt, Ctx, State);130 llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));131 },132 /*After=*/nullptr})133 .withASTBuildArgs(134 {"-fsyntax-only", CxxMode, "-Wno-undefined-inline"})135 .withASTBuildVirtualMappedFiles(136 tooling::FileContentMappings(Headers.begin(), Headers.end())),137 /*VerifyResults=*/[&Diagnostics](138 const llvm::DenseMap<unsigned, std::string>139 &Annotations,140 const AnalysisOutputs &AO) {141 llvm::DenseSet<unsigned> AnnotationLines;142 llvm::DenseMap<unsigned, std::string> AnnotationRangesInLines;143 for (const auto &[Line, AnnotationWithMaybeRange] : Annotations) {144 AnnotationLines.insert(Line);145 auto it = AnnotationWithMaybeRange.find(':');146 if (it != std::string::npos) {147 AnnotationRangesInLines[Line] =148 AnnotationWithMaybeRange.substr(it + 1);149 }150 }151 auto &SrcMgr = AO.ASTCtx.getSourceManager();152 llvm::DenseSet<unsigned> DiagnosticLines;153 for (const UncheckedOptionalAccessDiagnostic &Diag : Diagnostics) {154 unsigned Line = SrcMgr.getPresumedLineNumber(Diag.Range.getBegin());155 DiagnosticLines.insert(Line);156 if (!AnnotationLines.contains(Line)) {157 DiagnosticOptions DiagOpts;158 TextDiagnostic TD(llvm::errs(), AO.ASTCtx.getLangOpts(),159 DiagOpts);160 TD.emitDiagnostic(FullSourceLoc(Diag.Range.getBegin(), SrcMgr),161 DiagnosticsEngine::Error,162 "unexpected diagnostic", {Diag.Range}, {});163 } else {164 auto it = AnnotationRangesInLines.find(Line);165 if (it != AnnotationRangesInLines.end()) {166 EXPECT_EQ(Diag.Range.getAsRange().printToString(SrcMgr),167 it->second);168 }169 }170 }171 172 EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines));173 });174 if (Error)175 FAIL() << llvm::toString(std::move(Error));176 }177};178 179INSTANTIATE_TEST_SUITE_P(180 UncheckedOptionalUseTestInst, UncheckedOptionalAccessTest,181 ::testing::Values(OptionalTypeIdentifier{"std", "optional"},182 OptionalTypeIdentifier{"absl", "optional"},183 OptionalTypeIdentifier{"base", "Optional"}),184 [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) {185 return Info.param.NamespaceName;186 });187 188// Verifies that similarly-named types are ignored.189TEST_P(UncheckedOptionalAccessTest, NonTrackedOptionalType) {190 ExpectDiagnosticsFor(191 R"(192 namespace other {193 namespace $ns {194 template <typename T>195 struct $optional {196 T value();197 };198 }199 200 void target($ns::$optional<int> opt) {201 opt.value();202 }203 }204 )");205}206 207TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) {208 ExpectDiagnosticsFor(R"(209 void target() {210 (void)0;211 }212 )");213}214 215TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) {216 ExpectDiagnosticsFor(217 R"(218 #include "unchecked_optional_access_test.h"219 220 void target($ns::$optional<int> opt) {221 opt.value(); // [[unsafe]]222 }223 )");224 225 ExpectDiagnosticsFor(226 R"(227 #include "unchecked_optional_access_test.h"228 229 void target($ns::$optional<int> opt) {230 std::move(opt).value(); // [[unsafe]]231 }232 )");233}234 235TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) {236 ExpectDiagnosticsFor(237 R"(238 #include "unchecked_optional_access_test.h"239 240 void target($ns::$optional<int> opt) {241 *opt; // [[unsafe]]242 }243 )");244 245 ExpectDiagnosticsFor(246 R"(247 #include "unchecked_optional_access_test.h"248 249 void target($ns::$optional<int> opt) {250 *std::move(opt); // [[unsafe]]251 }252 )");253}254 255TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) {256 ExpectDiagnosticsFor(257 R"(258 #include "unchecked_optional_access_test.h"259 260 struct Foo {261 void foo();262 };263 264 void target($ns::$optional<Foo> opt) {265 opt->foo(); // [[unsafe]]266 }267 )");268 269 ExpectDiagnosticsFor(270 R"(271 #include "unchecked_optional_access_test.h"272 273 struct Foo {274 void foo();275 };276 277 void target($ns::$optional<Foo> opt) {278 std::move(opt)->foo(); // [[unsafe]]279 }280 )");281}282 283TEST_P(UncheckedOptionalAccessTest, HasValueCheck) {284 ExpectDiagnosticsFor(R"(285 #include "unchecked_optional_access_test.h"286 287 void target($ns::$optional<int> opt) {288 if (opt.has_value()) {289 opt.value();290 }291 }292 )");293}294 295TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) {296 ExpectDiagnosticsFor(R"(297 #include "unchecked_optional_access_test.h"298 299 void target($ns::$optional<int> opt) {300 if (opt) {301 opt.value();302 }303 }304 )");305}306 307TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) {308 ExpectDiagnosticsFor(309 R"(310 #include "unchecked_optional_access_test.h"311 312 void target() {313 Make<$ns::$optional<int>>().value(); // [[unsafe]]314 (void)0;315 }316 )");317 318 ExpectDiagnosticsFor(319 R"(320 #include "unchecked_optional_access_test.h"321 322 void target($ns::$optional<int> opt) {323 std::move(opt).value(); // [[unsafe]]324 }325 )");326}327 328TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) {329 ExpectDiagnosticsFor(330 R"(331 #include "unchecked_optional_access_test.h"332 333 void target() {334 $ns::$optional<int> opt;335 opt.value(); // [[unsafe]]336 }337 )");338}339 340TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) {341 ExpectDiagnosticsFor(342 R"(343 #include "unchecked_optional_access_test.h"344 345 void target() {346 $ns::$optional<int> opt($ns::nullopt);347 opt.value(); // [[unsafe]]348 }349 )");350}351 352TEST_P(UncheckedOptionalAccessTest, NulloptConstructorWithSugaredType) {353 ExpectDiagnosticsFor(354 R"(355 #include "unchecked_optional_access_test.h"356 template <typename T>357 using wrapper = T;358 359 template <typename T>360 wrapper<T> wrap(T);361 362 void target() {363 $ns::$optional<int> opt(wrap($ns::nullopt));364 opt.value(); // [[unsafe]]365 }366 )");367}368 369TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) {370 ExpectDiagnosticsFor(R"(371 #include "unchecked_optional_access_test.h"372 373 void target() {374 $ns::$optional<int> opt($ns::in_place, 3);375 opt.value();376 }377 )");378 379 ExpectDiagnosticsFor(R"(380 #include "unchecked_optional_access_test.h"381 382 struct Foo {};383 384 void target() {385 $ns::$optional<Foo> opt($ns::in_place);386 opt.value();387 }388 )");389 390 ExpectDiagnosticsFor(R"(391 #include "unchecked_optional_access_test.h"392 393 struct Foo {394 explicit Foo(int, bool);395 };396 397 void target() {398 $ns::$optional<Foo> opt($ns::in_place, 3, false);399 opt.value();400 }401 )");402 403 ExpectDiagnosticsFor(R"(404 #include "unchecked_optional_access_test.h"405 406 struct Foo {407 explicit Foo(std::initializer_list<int>);408 };409 410 void target() {411 $ns::$optional<Foo> opt($ns::in_place, {3});412 opt.value();413 }414 )");415}416 417TEST_P(UncheckedOptionalAccessTest, ValueConstructor) {418 ExpectDiagnosticsFor(R"(419 #include "unchecked_optional_access_test.h"420 421 void target() {422 $ns::$optional<int> opt(21);423 opt.value();424 }425 )");426 427 ExpectDiagnosticsFor(R"(428 #include "unchecked_optional_access_test.h"429 430 void target() {431 $ns::$optional<int> opt = $ns::$optional<int>(21);432 opt.value();433 }434 )");435 ExpectDiagnosticsFor(R"(436 #include "unchecked_optional_access_test.h"437 438 void target() {439 $ns::$optional<$ns::$optional<int>> opt(Make<$ns::$optional<int>>());440 opt.value();441 }442 )");443 444 ExpectDiagnosticsFor(R"(445 #include "unchecked_optional_access_test.h"446 447 struct MyString {448 MyString(const char*);449 };450 451 void target() {452 $ns::$optional<MyString> opt("foo");453 opt.value();454 }455 )");456 457 ExpectDiagnosticsFor(R"(458 #include "unchecked_optional_access_test.h"459 460 struct Foo {};461 462 struct Bar {463 Bar(const Foo&);464 };465 466 void target() {467 $ns::$optional<Bar> opt(Make<Foo>());468 opt.value();469 }470 )");471 472 ExpectDiagnosticsFor(R"(473 #include "unchecked_optional_access_test.h"474 475 struct Foo {476 explicit Foo(int);477 };478 479 void target() {480 $ns::$optional<Foo> opt(3);481 opt.value();482 }483 )");484}485 486TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) {487 ExpectDiagnosticsFor(488 R"(489 #include "unchecked_optional_access_test.h"490 491 struct Foo {};492 493 struct Bar {494 Bar(const Foo&);495 };496 497 void target() {498 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>());499 opt.value(); // [[unsafe]]500 }501 )");502 503 ExpectDiagnosticsFor(504 R"(505 #include "unchecked_optional_access_test.h"506 507 struct Foo {};508 509 struct Bar {510 explicit Bar(const Foo&);511 };512 513 void target() {514 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>());515 opt.value(); // [[unsafe]]516 }517 )");518 519 ExpectDiagnosticsFor(520 R"(521 #include "unchecked_optional_access_test.h"522 523 struct Foo {};524 525 struct Bar {526 Bar(const Foo&);527 };528 529 void target() {530 $ns::$optional<Foo> opt1 = $ns::nullopt;531 $ns::$optional<Bar> opt2(opt1);532 opt2.value(); // [[unsafe]]533 }534 )");535 536 ExpectDiagnosticsFor(R"(537 #include "unchecked_optional_access_test.h"538 539 struct Foo {};540 541 struct Bar {542 Bar(const Foo&);543 };544 545 void target() {546 $ns::$optional<Foo> opt1(Make<Foo>());547 $ns::$optional<Bar> opt2(opt1);548 opt2.value();549 }550 )");551 552 ExpectDiagnosticsFor(R"(553 #include "unchecked_optional_access_test.h"554 555 struct Foo {};556 557 struct Bar {558 explicit Bar(const Foo&);559 };560 561 void target() {562 $ns::$optional<Foo> opt1(Make<Foo>());563 $ns::$optional<Bar> opt2(opt1);564 opt2.value();565 }566 )");567}568 569TEST_P(UncheckedOptionalAccessTest, MakeOptional) {570 ExpectDiagnosticsFor(R"(571 #include "unchecked_optional_access_test.h"572 573 void target() {574 $ns::$optional<int> opt = $ns::make_optional(0);575 opt.value();576 }577 )");578 579 ExpectDiagnosticsFor(R"(580 #include "unchecked_optional_access_test.h"581 582 struct Foo {583 Foo(int, int);584 };585 586 void target() {587 $ns::$optional<Foo> opt = $ns::make_optional<Foo>(21, 22);588 opt.value();589 }590 )");591 592 ExpectDiagnosticsFor(R"(593 #include "unchecked_optional_access_test.h"594 595 struct Foo {596 constexpr Foo(std::initializer_list<char>);597 };598 599 void target() {600 char a = 'a';601 $ns::$optional<Foo> opt = $ns::make_optional<Foo>({a});602 opt.value();603 }604 )");605}606 607TEST_P(UncheckedOptionalAccessTest, ValueOr) {608 ExpectDiagnosticsFor(R"(609 #include "unchecked_optional_access_test.h"610 611 void target() {612 $ns::$optional<int> opt;613 opt.value_or(0);614 (void)0;615 }616 )");617}618 619TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonPointers) {620 ExpectDiagnosticsFor(621 R"code(622 #include "unchecked_optional_access_test.h"623 624 void target($ns::$optional<int*> opt) {625 if (opt.value_or(nullptr) != nullptr) {626 opt.value();627 } else {628 opt.value(); // [[unsafe]]629 }630 }631 )code");632}633 634TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonIntegers) {635 ExpectDiagnosticsFor(636 R"code(637 #include "unchecked_optional_access_test.h"638 639 void target($ns::$optional<int> opt) {640 if (opt.value_or(0) != 0) {641 opt.value();642 } else {643 opt.value(); // [[unsafe]]644 }645 }646 )code");647}648 649TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonStrings) {650 ExpectDiagnosticsFor(651 R"code(652 #include "unchecked_optional_access_test.h"653 654 void target($ns::$optional<std::string> opt) {655 if (!opt.value_or("").empty()) {656 opt.value();657 } else {658 opt.value(); // [[unsafe]]659 }660 }661 )code");662 663 ExpectDiagnosticsFor(664 R"code(665 #include "unchecked_optional_access_test.h"666 667 void target($ns::$optional<std::string> opt) {668 if (opt.value_or("") != "") {669 opt.value();670 } else {671 opt.value(); // [[unsafe]]672 }673 }674 )code");675}676 677TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonPointerToOptional) {678 // FIXME: make `opt` a parameter directly, once we ensure that all `optional`679 // values have a `has_value` property.680 ExpectDiagnosticsFor(681 R"code(682 #include "unchecked_optional_access_test.h"683 684 void target($ns::$optional<int> p) {685 $ns::$optional<int> *opt = &p;686 if (opt->value_or(0) != 0) {687 opt->value();688 } else {689 opt->value(); // [[unsafe]]690 }691 }692 )code");693}694 695TEST_P(UncheckedOptionalAccessTest, Emplace) {696 ExpectDiagnosticsFor(R"(697 #include "unchecked_optional_access_test.h"698 699 void target() {700 $ns::$optional<int> opt;701 opt.emplace(0);702 opt.value();703 }704 )");705 706 ExpectDiagnosticsFor(R"(707 #include "unchecked_optional_access_test.h"708 709 void target($ns::$optional<int> *opt) {710 opt->emplace(0);711 opt->value();712 }713 )");714 715 // FIXME: Add tests that call `emplace` in conditional branches:716 // ExpectDiagnosticsFor(717 // R"(718 // #include "unchecked_optional_access_test.h"719 //720 // void target($ns::$optional<int> opt, bool b) {721 // if (b) {722 // opt.emplace(0);723 // }724 // if (b) {725 // opt.value();726 // } else {727 // opt.value(); // [[unsafe]]728 // }729 // }730 // )");731}732 733TEST_P(UncheckedOptionalAccessTest, Reset) {734 ExpectDiagnosticsFor(735 R"(736 #include "unchecked_optional_access_test.h"737 738 void target() {739 $ns::$optional<int> opt = $ns::make_optional(0);740 opt.reset();741 opt.value(); // [[unsafe]]742 }743 )");744 745 ExpectDiagnosticsFor(746 R"(747 #include "unchecked_optional_access_test.h"748 749 void target($ns::$optional<int> &opt) {750 if (opt.has_value()) {751 opt.reset();752 opt.value(); // [[unsafe]]753 }754 }755 )");756 757 // FIXME: Add tests that call `reset` in conditional branches:758 // ExpectDiagnosticsFor(759 // R"(760 // #include "unchecked_optional_access_test.h"761 //762 // void target(bool b) {763 // $ns::$optional<int> opt = $ns::make_optional(0);764 // if (b) {765 // opt.reset();766 // }767 // if (b) {768 // opt.value(); // [[unsafe]]769 // } else {770 // opt.value();771 // }772 // }773 // )");774}775 776TEST_P(UncheckedOptionalAccessTest, ValueAssignment) {777 ExpectDiagnosticsFor(R"(778 #include "unchecked_optional_access_test.h"779 780 struct Foo {};781 782 void target() {783 $ns::$optional<Foo> opt;784 opt = Foo();785 opt.value();786 }787 )");788 789 ExpectDiagnosticsFor(R"(790 #include "unchecked_optional_access_test.h"791 792 struct Foo {};793 794 void target() {795 $ns::$optional<Foo> opt;796 (opt = Foo()).value();797 (void)0;798 }799 )");800 801 ExpectDiagnosticsFor(R"(802 #include "unchecked_optional_access_test.h"803 804 struct MyString {805 MyString(const char*);806 };807 808 void target() {809 $ns::$optional<MyString> opt;810 opt = "foo";811 opt.value();812 }813 )");814 815 ExpectDiagnosticsFor(R"(816 #include "unchecked_optional_access_test.h"817 818 struct MyString {819 MyString(const char*);820 };821 822 void target() {823 $ns::$optional<MyString> opt;824 (opt = "foo").value();825 }826 )");827}828 829TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) {830 ExpectDiagnosticsFor(831 R"(832 #include "unchecked_optional_access_test.h"833 834 struct Foo {};835 836 struct Bar {837 Bar(const Foo&);838 };839 840 void target() {841 $ns::$optional<Foo> opt1 = Foo();842 $ns::$optional<Bar> opt2;843 opt2 = opt1;844 opt2.value();845 }846 )");847 848 ExpectDiagnosticsFor(849 R"(850 #include "unchecked_optional_access_test.h"851 852 struct Foo {};853 854 struct Bar {855 Bar(const Foo&);856 };857 858 void target() {859 $ns::$optional<Foo> opt1;860 $ns::$optional<Bar> opt2;861 if (opt2.has_value()) {862 opt2 = opt1;863 opt2.value(); // [[unsafe]]864 }865 }866 )");867 868 ExpectDiagnosticsFor(869 R"(870 #include "unchecked_optional_access_test.h"871 872 struct Foo {};873 874 struct Bar {875 Bar(const Foo&);876 };877 878 void target() {879 $ns::$optional<Foo> opt1 = Foo();880 $ns::$optional<Bar> opt2;881 (opt2 = opt1).value();882 (void)0;883 }884 )");885}886 887TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) {888 ExpectDiagnosticsFor(889 R"(890 #include "unchecked_optional_access_test.h"891 892 void target() {893 $ns::$optional<int> opt = 3;894 opt = $ns::nullopt;895 opt.value(); // [[unsafe]]896 }897 )");898 899 ExpectDiagnosticsFor(900 R"(901 #include "unchecked_optional_access_test.h"902 903 void target() {904 $ns::$optional<int> opt = 3;905 (opt = $ns::nullopt).value(); // [[unsafe]]906 }907 )");908}909 910TEST_P(UncheckedOptionalAccessTest, OptionalSwap) {911 ExpectDiagnosticsFor(912 R"(913 #include "unchecked_optional_access_test.h"914 915 void target() {916 $ns::$optional<int> opt1 = $ns::nullopt;917 $ns::$optional<int> opt2 = 3;918 919 opt1.swap(opt2);920 921 opt1.value();922 923 opt2.value(); // [[unsafe]]924 }925 )");926 927 ExpectDiagnosticsFor(928 R"(929 #include "unchecked_optional_access_test.h"930 931 void target() {932 $ns::$optional<int> opt1 = $ns::nullopt;933 $ns::$optional<int> opt2 = 3;934 935 opt2.swap(opt1);936 937 opt1.value();938 939 opt2.value(); // [[unsafe]]940 }941 )");942}943 944TEST_P(UncheckedOptionalAccessTest, OptionalReturnedFromFuntionCall) {945 ExpectDiagnosticsFor(946 R"(947 #include "unchecked_optional_access_test.h"948 949 struct S {950 $ns::$optional<float> x;951 } s;952 S getOptional() {953 return s;954 }955 956 void target() {957 getOptional().x = 0;958 }959 )");960}961 962TEST_P(UncheckedOptionalAccessTest, NonConstMethodMayClearOptionalField) {963 ExpectDiagnosticsFor(964 R"(965 #include "unchecked_optional_access_test.h"966 967 struct Foo {968 $ns::$optional<std::string> opt;969 void clear(); // assume this may modify the opt field's state970 };971 972 void target(Foo& foo) {973 if (foo.opt) {974 foo.opt.value();975 foo.clear();976 foo.opt.value(); // [[unsafe]]977 }978 }979 )");980}981 982TEST_P(UncheckedOptionalAccessTest,983 NonConstMethodMayNotClearConstOptionalField) {984 ExpectDiagnosticsFor(985 R"(986 #include "unchecked_optional_access_test.h"987 988 struct Foo {989 const $ns::$optional<std::string> opt;990 void clear();991 };992 993 void target(Foo& foo) {994 if (foo.opt) {995 foo.opt.value();996 foo.clear();997 foo.opt.value();998 }999 }1000 )");1001}1002 1003TEST_P(UncheckedOptionalAccessTest, StdSwap) {1004 ExpectDiagnosticsFor(1005 R"(1006 #include "unchecked_optional_access_test.h"1007 1008 void target() {1009 $ns::$optional<int> opt1 = $ns::nullopt;1010 $ns::$optional<int> opt2 = 3;1011 1012 std::swap(opt1, opt2);1013 1014 opt1.value();1015 1016 opt2.value(); // [[unsafe]]1017 }1018 )");1019 1020 ExpectDiagnosticsFor(1021 R"(1022 #include "unchecked_optional_access_test.h"1023 1024 void target() {1025 $ns::$optional<int> opt1 = $ns::nullopt;1026 $ns::$optional<int> opt2 = 3;1027 1028 std::swap(opt2, opt1);1029 1030 opt1.value();1031 1032 opt2.value(); // [[unsafe]]1033 }1034 )");1035}1036 1037TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledLocLeft) {1038 ExpectDiagnosticsFor(1039 R"(1040 #include "unchecked_optional_access_test.h"1041 1042 struct L { $ns::$optional<int> hd; L* tl; };1043 1044 void target() {1045 $ns::$optional<int> foo = 3;1046 L bar;1047 1048 // Any `tl` beyond the first is not modeled.1049 bar.tl->tl->hd.swap(foo);1050 1051 bar.tl->tl->hd.value(); // [[unsafe]]1052 foo.value(); // [[unsafe]]1053 }1054 )");1055}1056 1057TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledLocRight) {1058 ExpectDiagnosticsFor(1059 R"(1060 #include "unchecked_optional_access_test.h"1061 1062 struct L { $ns::$optional<int> hd; L* tl; };1063 1064 void target() {1065 $ns::$optional<int> foo = 3;1066 L bar;1067 1068 // Any `tl` beyond the first is not modeled.1069 foo.swap(bar.tl->tl->hd);1070 1071 bar.tl->tl->hd.value(); // [[unsafe]]1072 foo.value(); // [[unsafe]]1073 }1074 )");1075}1076 1077TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueLeftSet) {1078 ExpectDiagnosticsFor(1079 R"(1080 #include "unchecked_optional_access_test.h"1081 1082 struct S { int x; };1083 struct A { $ns::$optional<S> late; };1084 struct B { A f3; };1085 struct C { B f2; };1086 struct D { C f1; };1087 1088 void target() {1089 $ns::$optional<S> foo = S{3};1090 D bar;1091 1092 bar.f1.f2.f3.late.swap(foo);1093 1094 bar.f1.f2.f3.late.value();1095 foo.value(); // [[unsafe]]1096 }1097 )");1098}1099 1100TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueLeftUnset) {1101 ExpectDiagnosticsFor(1102 R"(1103 #include "unchecked_optional_access_test.h"1104 1105 struct S { int x; };1106 struct A { $ns::$optional<S> late; };1107 struct B { A f3; };1108 struct C { B f2; };1109 struct D { C f1; };1110 1111 void target() {1112 $ns::$optional<S> foo;1113 D bar;1114 1115 bar.f1.f2.f3.late.swap(foo);1116 1117 bar.f1.f2.f3.late.value(); // [[unsafe]]1118 foo.value(); // [[unsafe]]1119 }1120 )");1121}1122 1123// fixme: use recursion instead of depth.1124TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueRightSet) {1125 ExpectDiagnosticsFor(1126 R"(1127 #include "unchecked_optional_access_test.h"1128 1129 struct S { int x; };1130 struct A { $ns::$optional<S> late; };1131 struct B { A f3; };1132 struct C { B f2; };1133 struct D { C f1; };1134 1135 void target() {1136 $ns::$optional<S> foo = S{3};1137 D bar;1138 1139 foo.swap(bar.f1.f2.f3.late);1140 1141 bar.f1.f2.f3.late.value();1142 foo.value(); // [[unsafe]]1143 }1144 )");1145}1146 1147TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueRightUnset) {1148 ExpectDiagnosticsFor(1149 R"(1150 #include "unchecked_optional_access_test.h"1151 1152 struct S { int x; };1153 struct A { $ns::$optional<S> late; };1154 struct B { A f3; };1155 struct C { B f2; };1156 struct D { C f1; };1157 1158 void target() {1159 $ns::$optional<S> foo;1160 D bar;1161 1162 foo.swap(bar.f1.f2.f3.late);1163 1164 bar.f1.f2.f3.late.value(); // [[unsafe]]1165 foo.value(); // [[unsafe]]1166 }1167 )");1168}1169 1170TEST_P(UncheckedOptionalAccessTest, UniquePtrToOptional) {1171 // We suppress diagnostics for optionals in smart pointers (other than1172 // `optional` itself).1173 ExpectDiagnosticsFor(1174 R"(1175 #include "unchecked_optional_access_test.h"1176 1177 template <typename T>1178 struct smart_ptr {1179 T& operator*() &;1180 T* operator->();1181 };1182 1183 void target() {1184 smart_ptr<$ns::$optional<bool>> foo;1185 foo->value();1186 (*foo).value();1187 }1188 )");1189}1190 1191TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) {1192 // We suppress diagnostics for optional fields reachable from smart pointers1193 // (other than `optional` itself) through (exactly) one member access.1194 ExpectDiagnosticsFor(1195 R"(1196 #include "unchecked_optional_access_test.h"1197 1198 template <typename T>1199 struct smart_ptr {1200 T& operator*() &;1201 T* operator->();1202 };1203 1204 struct Foo {1205 $ns::$optional<int> opt;1206 };1207 1208 void target() {1209 smart_ptr<Foo> foo;1210 *foo->opt;1211 *(*foo).opt;1212 }1213 )");1214}1215 1216TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) {1217 ExpectDiagnosticsFor(1218 R"(1219 #include "unchecked_optional_access_test.h"1220 1221 $ns::$optional<int> MakeOpt();1222 1223 void target() {1224 $ns::$optional<int> opt = 0;1225 opt = MakeOpt();1226 opt.value(); // [[unsafe]]1227 }1228 )");1229 ExpectDiagnosticsFor(1230 R"(1231 #include "unchecked_optional_access_test.h"1232 1233 const $ns::$optional<int>& MakeOpt();1234 1235 void target() {1236 $ns::$optional<int> opt = 0;1237 opt = MakeOpt();1238 opt.value(); // [[unsafe]]1239 }1240 )");1241 1242 ExpectDiagnosticsFor(1243 R"(1244 #include "unchecked_optional_access_test.h"1245 1246 using IntOpt = $ns::$optional<int>;1247 IntOpt MakeOpt();1248 1249 void target() {1250 IntOpt opt = 0;1251 opt = MakeOpt();1252 opt.value(); // [[unsafe]]1253 }1254 )");1255 1256 ExpectDiagnosticsFor(1257 R"(1258 #include "unchecked_optional_access_test.h"1259 1260 using IntOpt = $ns::$optional<int>;1261 const IntOpt& MakeOpt();1262 1263 void target() {1264 IntOpt opt = 0;1265 opt = MakeOpt();1266 opt.value(); // [[unsafe]]1267 }1268 )");1269}1270 1271 1272TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftSet) {1273 ExpectDiagnosticsFor(1274 R"(1275 #include "unchecked_optional_access_test.h"1276 1277 void target() {1278 $ns::$optional<int> opt1 = 3;1279 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1280 1281 if (opt1 == opt2) {1282 opt2.value();1283 } else {1284 opt2.value(); // [[unsafe]]1285 }1286 }1287 )");1288}1289 1290TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightSet) {1291 ExpectDiagnosticsFor(1292 R"(1293 #include "unchecked_optional_access_test.h"1294 1295 void target() {1296 $ns::$optional<int> opt1 = 3;1297 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1298 1299 if (opt2 == opt1) {1300 opt2.value();1301 } else {1302 opt2.value(); // [[unsafe]]1303 }1304 }1305 )");1306}1307 1308TEST_P(UncheckedOptionalAccessTest, EqualityCheckVerifySetAfterEq) {1309 ExpectDiagnosticsFor(1310 R"(1311 #include "unchecked_optional_access_test.h"1312 1313 void target() {1314 $ns::$optional<int> opt1 = Make<$ns::$optional<int>>();1315 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1316 1317 if (opt1 == opt2) {1318 if (opt1.has_value())1319 opt2.value();1320 if (opt2.has_value())1321 opt1.value();1322 }1323 }1324 )");1325}1326 1327TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftUnset) {1328 ExpectDiagnosticsFor(1329 R"(1330 #include "unchecked_optional_access_test.h"1331 1332 void target() {1333 $ns::$optional<int> opt1 = $ns::nullopt;1334 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1335 1336 if (opt1 == opt2) {1337 opt2.value(); // [[unsafe]]1338 } else {1339 opt2.value();1340 }1341 }1342 )");1343}1344 1345TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightUnset) {1346 ExpectDiagnosticsFor(1347 R"(1348 #include "unchecked_optional_access_test.h"1349 1350 void target() {1351 $ns::$optional<int> opt1 = $ns::nullopt;1352 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1353 1354 if (opt2 == opt1) {1355 opt2.value(); // [[unsafe]]1356 } else {1357 opt2.value();1358 }1359 }1360 )");1361}1362 1363TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightNullopt) {1364 ExpectDiagnosticsFor(1365 R"(1366 #include "unchecked_optional_access_test.h"1367 1368 void target() {1369 $ns::$optional<int> opt = Make<$ns::$optional<int>>();1370 1371 if (opt == $ns::nullopt) {1372 opt.value(); // [[unsafe]]1373 } else {1374 opt.value();1375 }1376 }1377 )");1378}1379 1380TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftNullopt) {1381 ExpectDiagnosticsFor(1382 R"(1383 #include "unchecked_optional_access_test.h"1384 1385 void target() {1386 $ns::$optional<int> opt = Make<$ns::$optional<int>>();1387 1388 if ($ns::nullopt == opt) {1389 opt.value(); // [[unsafe]]1390 } else {1391 opt.value();1392 }1393 }1394 )");1395}1396 1397TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightValue) {1398 ExpectDiagnosticsFor(1399 R"(1400 #include "unchecked_optional_access_test.h"1401 1402 void target() {1403 $ns::$optional<int> opt = Make<$ns::$optional<int>>();1404 1405 if (opt == 3) {1406 opt.value();1407 } else {1408 opt.value(); // [[unsafe]]1409 }1410 }1411 )");1412}1413 1414TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftValue) {1415 ExpectDiagnosticsFor(1416 R"(1417 #include "unchecked_optional_access_test.h"1418 1419 void target() {1420 $ns::$optional<int> opt = Make<$ns::$optional<int>>();1421 1422 if (3 == opt) {1423 opt.value();1424 } else {1425 opt.value(); // [[unsafe]]1426 }1427 }1428 )");1429}1430 1431TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftSet) {1432 ExpectDiagnosticsFor(1433 R"(1434 #include "unchecked_optional_access_test.h"1435 1436 void target() {1437 $ns::$optional<int> opt1 = 3;1438 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1439 1440 if (opt1 != opt2) {1441 opt2.value(); // [[unsafe]]1442 } else {1443 opt2.value();1444 }1445 }1446 )");1447}1448 1449TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightSet) {1450 ExpectDiagnosticsFor(1451 R"(1452 #include "unchecked_optional_access_test.h"1453 1454 void target() {1455 $ns::$optional<int> opt1 = 3;1456 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1457 1458 if (opt2 != opt1) {1459 opt2.value(); // [[unsafe]]1460 } else {1461 opt2.value();1462 }1463 }1464 )");1465}1466 1467TEST_P(UncheckedOptionalAccessTest, InequalityCheckVerifySetAfterEq) {1468 ExpectDiagnosticsFor(1469 R"(1470 #include "unchecked_optional_access_test.h"1471 1472 void target() {1473 $ns::$optional<int> opt1 = Make<$ns::$optional<int>>();1474 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1475 1476 if (opt1 != opt2) {1477 if (opt1.has_value())1478 opt2.value(); // [[unsafe]]1479 if (opt2.has_value())1480 opt1.value(); // [[unsafe]]1481 }1482 }1483 )");1484}1485 1486TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftUnset) {1487 ExpectDiagnosticsFor(1488 R"(1489 #include "unchecked_optional_access_test.h"1490 1491 void target() {1492 $ns::$optional<int> opt1 = $ns::nullopt;1493 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1494 1495 if (opt1 != opt2) {1496 opt2.value();1497 } else {1498 opt2.value(); // [[unsafe]]1499 }1500 }1501 )");1502}1503 1504TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightUnset) {1505 ExpectDiagnosticsFor(1506 R"(1507 #include "unchecked_optional_access_test.h"1508 1509 void target() {1510 $ns::$optional<int> opt1 = $ns::nullopt;1511 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();1512 1513 if (opt2 != opt1) {1514 opt2.value();1515 } else {1516 opt2.value(); // [[unsafe]]1517 }1518 }1519 )");1520}1521 1522TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightNullopt) {1523 ExpectDiagnosticsFor(1524 R"(1525 #include "unchecked_optional_access_test.h"1526 1527 void target() {1528 $ns::$optional<int> opt = Make<$ns::$optional<int>>();1529 1530 if (opt != $ns::nullopt) {1531 opt.value();1532 } else {1533 opt.value(); // [[unsafe]]1534 }1535 }1536 )");1537}1538 1539TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftNullopt) {1540 ExpectDiagnosticsFor(1541 R"(1542 #include "unchecked_optional_access_test.h"1543 1544 void target() {1545 $ns::$optional<int> opt = Make<$ns::$optional<int>>();1546 1547 if ($ns::nullopt != opt) {1548 opt.value();1549 } else {1550 opt.value(); // [[unsafe]]1551 }1552 }1553 )");1554}1555 1556TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightValue) {1557 ExpectDiagnosticsFor(1558 R"(1559 #include "unchecked_optional_access_test.h"1560 1561 void target() {1562 $ns::$optional<int> opt = Make<$ns::$optional<int>>();1563 1564 if (opt != 3) {1565 opt.value(); // [[unsafe]]1566 } else {1567 opt.value();1568 }1569 }1570 )");1571}1572 1573TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftValue) {1574 ExpectDiagnosticsFor(1575 R"(1576 #include "unchecked_optional_access_test.h"1577 1578 void target() {1579 $ns::$optional<int> opt = Make<$ns::$optional<int>>();1580 1581 if (3 != opt) {1582 opt.value(); // [[unsafe]]1583 } else {1584 opt.value();1585 }1586 }1587 )");1588}1589 1590// Verifies that the model sees through aliases.1591TEST_P(UncheckedOptionalAccessTest, WithAlias) {1592 ExpectDiagnosticsFor(1593 R"(1594 #include "unchecked_optional_access_test.h"1595 1596 template <typename T>1597 using MyOptional = $ns::$optional<T>;1598 1599 void target(MyOptional<int> opt) {1600 opt.value(); // [[unsafe]]1601 }1602 )");1603}1604 1605TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) {1606 // Basic test that nested values are populated. We nest an optional because1607 // its easy to use in a test, but the type of the nested value shouldn't1608 // matter.1609 ExpectDiagnosticsFor(1610 R"(1611 #include "unchecked_optional_access_test.h"1612 1613 using Foo = $ns::$optional<std::string>;1614 1615 void target($ns::$optional<Foo> foo) {1616 if (foo && *foo) {1617 foo->value();1618 }1619 }1620 )");1621 1622 // Mutation is supported for nested values.1623 ExpectDiagnosticsFor(1624 R"(1625 #include "unchecked_optional_access_test.h"1626 1627 using Foo = $ns::$optional<std::string>;1628 1629 void target($ns::$optional<Foo> foo) {1630 if (foo && *foo) {1631 foo->reset();1632 foo->value(); // [[unsafe]]1633 }1634 }1635 )");1636}1637 1638TEST_P(UncheckedOptionalAccessTest, NestedOptionalAssignValue) {1639 ExpectDiagnosticsFor(1640 R"(1641 #include "unchecked_optional_access_test.h"1642 1643 using OptionalInt = $ns::$optional<int>;1644 1645 void target($ns::$optional<OptionalInt> opt) {1646 if (!opt) return;1647 1648 // Accessing the outer optional is OK now.1649 *opt;1650 1651 // But accessing the nested optional is still unsafe because we haven't1652 // checked it.1653 **opt; // [[unsafe]]1654 1655 *opt = 1;1656 1657 // Accessing the nested optional is safe after assigning a value to it.1658 **opt;1659 }1660 )");1661}1662 1663TEST_P(UncheckedOptionalAccessTest, NestedOptionalAssignOptional) {1664 ExpectDiagnosticsFor(1665 R"(1666 #include "unchecked_optional_access_test.h"1667 1668 using OptionalInt = $ns::$optional<int>;1669 1670 void target($ns::$optional<OptionalInt> opt) {1671 if (!opt) return;1672 1673 // Accessing the outer optional is OK now.1674 *opt;1675 1676 // But accessing the nested optional is still unsafe because we haven't1677 // checked it.1678 **opt; // [[unsafe]]1679 1680 // Assign from `optional<short>` so that we trigger conversion assignment1681 // instead of move assignment.1682 *opt = $ns::$optional<short>();1683 1684 // Accessing the nested optional is still unsafe after assigning an empty1685 // optional to it.1686 **opt; // [[unsafe]]1687 }1688 )");1689}1690 1691// Tests that structs can be nested. We use an optional field because its easy1692// to use in a test, but the type of the field shouldn't matter.1693TEST_P(UncheckedOptionalAccessTest, OptionalValueStruct) {1694 ExpectDiagnosticsFor(1695 R"(1696 #include "unchecked_optional_access_test.h"1697 1698 struct Foo {1699 $ns::$optional<std::string> opt;1700 };1701 1702 void target($ns::$optional<Foo> foo) {1703 if (foo && foo->opt) {1704 foo->opt.value();1705 }1706 }1707 )");1708}1709 1710// FIXME: A case that we should handle but currently don't.1711// When there is a field of type reference to non-optional, we may1712// stop recursively creating storage locations.1713// E.g., the field `second` below in `pair` should eventually lead to1714// the optional `x` in `A`.1715TEST_P(UncheckedOptionalAccessTest, NestedOptionalThroughNonOptionalRefField) {1716 ExpectDiagnosticsFor(R"(1717 #include "unchecked_optional_access_test.h"1718 1719 struct A {1720 $ns::$optional<int> x;1721 };1722 1723 struct pair {1724 int first;1725 const A &second;1726 };1727 1728 struct B {1729 $ns::$optional<pair>& nonConstGetRef();1730 };1731 1732 void target(B b) {1733 const auto& maybe_pair = b.nonConstGetRef();1734 if (!maybe_pair.has_value())1735 return;1736 1737 if(!maybe_pair->second.x.has_value())1738 return;1739 maybe_pair->second.x.value(); // [[unsafe]]1740 }1741 )");1742}1743 1744TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) {1745 ExpectDiagnosticsFor(1746 R"(1747 #include "unchecked_optional_access_test.h"1748 1749 using Foo = $ns::$optional<std::string>;1750 1751 void target($ns::$optional<Foo> foo, bool b) {1752 if (!foo.has_value()) return;1753 if (b) {1754 if (!foo->has_value()) return;1755 // We have created `foo.value()`.1756 foo->value();1757 } else {1758 if (!foo->has_value()) return;1759 // We have created `foo.value()` again, in a different environment.1760 foo->value();1761 }1762 // Now we merge the two values. UncheckedOptionalAccessModel::merge() will1763 // throw away the "value" property.1764 foo->value();1765 }1766 )");1767}1768 1769// This test is aimed at the core model, not the diagnostic. It is a regression1770// test against a crash when using non-trivial smart pointers, like1771// `std::unique_ptr`. As such, it doesn't test the access itself, which would be1772// ignored regardless because of `IgnoreSmartPointerDereference = true`, above.1773TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) {1774 ExpectDiagnosticsFor(1775 R"(1776 #include "unchecked_optional_access_test.h"1777 1778 template <typename T>1779 struct smart_ptr {1780 typename std::add_lvalue_reference<T>::type operator*() &;1781 };1782 1783 void target() {1784 smart_ptr<$ns::$optional<int>> x;1785 // Verify that this assignment does not crash.1786 *x = 3;1787 }1788 )");1789}1790 1791TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) {1792 ExpectDiagnosticsFor(R"code(1793 #include "unchecked_optional_access_test.h"1794 1795 void target(bool b, $ns::$optional<int> opt) {1796 if (b || opt.has_value()) {1797 if (!b) {1798 opt.value();1799 }1800 }1801 }1802 )code");1803 1804 ExpectDiagnosticsFor(R"code(1805 #include "unchecked_optional_access_test.h"1806 1807 void target(bool b, $ns::$optional<int> opt) {1808 if (b && !opt.has_value()) return;1809 if (b) {1810 opt.value();1811 }1812 }1813 )code");1814 1815 ExpectDiagnosticsFor(1816 R"code(1817 #include "unchecked_optional_access_test.h"1818 1819 void target(bool b, $ns::$optional<int> opt) {1820 if (opt.has_value()) b = true;1821 if (b) {1822 opt.value(); // [[unsafe]]1823 }1824 }1825 )code");1826 1827 ExpectDiagnosticsFor(R"code(1828 #include "unchecked_optional_access_test.h"1829 1830 void target(bool b, $ns::$optional<int> opt) {1831 if (b) return;1832 if (opt.has_value()) b = true;1833 if (b) {1834 opt.value();1835 }1836 }1837 )code");1838 1839 ExpectDiagnosticsFor(R"(1840 #include "unchecked_optional_access_test.h"1841 1842 void target(bool b, $ns::$optional<int> opt) {1843 if (opt.has_value() == b) {1844 if (b) {1845 opt.value();1846 }1847 }1848 }1849 )");1850 1851 ExpectDiagnosticsFor(R"(1852 #include "unchecked_optional_access_test.h"1853 1854 void target(bool b, $ns::$optional<int> opt) {1855 if (opt.has_value() != b) {1856 if (!b) {1857 opt.value();1858 }1859 }1860 }1861 )");1862 1863 ExpectDiagnosticsFor(R"(1864 #include "unchecked_optional_access_test.h"1865 1866 void target(bool b) {1867 $ns::$optional<int> opt1 = $ns::nullopt;1868 $ns::$optional<int> opt2;1869 if (b) {1870 opt2 = $ns::nullopt;1871 } else {1872 opt2 = $ns::nullopt;1873 }1874 if (opt2.has_value()) {1875 opt1.value();1876 }1877 }1878 )");1879}1880 1881TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) {1882 ExpectDiagnosticsFor(1883 R"code(1884 #include "unchecked_optional_access_test.h"1885 1886 void target(bool b) {1887 $ns::$optional<int> opt;1888 if (b) {1889 opt = Make<$ns::$optional<int>>();1890 } else {1891 opt = Make<$ns::$optional<int>>();1892 }1893 if (opt.has_value()) {1894 opt.value();1895 } else {1896 opt.value(); // [[unsafe]]1897 }1898 }1899 )code");1900 1901 ExpectDiagnosticsFor(R"code(1902 #include "unchecked_optional_access_test.h"1903 1904 void target(bool b) {1905 $ns::$optional<int> opt;1906 if (b) {1907 opt = Make<$ns::$optional<int>>();1908 if (!opt.has_value()) return;1909 } else {1910 opt = Make<$ns::$optional<int>>();1911 if (!opt.has_value()) return;1912 }1913 opt.value();1914 }1915 )code");1916 1917 ExpectDiagnosticsFor(1918 R"code(1919 #include "unchecked_optional_access_test.h"1920 1921 void target(bool b) {1922 $ns::$optional<int> opt;1923 if (b) {1924 opt = Make<$ns::$optional<int>>();1925 if (!opt.has_value()) return;1926 } else {1927 opt = Make<$ns::$optional<int>>();1928 }1929 opt.value(); // [[unsafe]]1930 }1931 )code");1932 1933 ExpectDiagnosticsFor(1934 R"code(1935 #include "unchecked_optional_access_test.h"1936 1937 void target(bool b) {1938 $ns::$optional<int> opt;1939 if (b) {1940 opt = 1;1941 } else {1942 opt = 2;1943 }1944 opt.value();1945 }1946 )code");1947 1948 ExpectDiagnosticsFor(1949 R"code(1950 #include "unchecked_optional_access_test.h"1951 1952 void target(bool b) {1953 $ns::$optional<int> opt;1954 if (b) {1955 opt = 1;1956 } else {1957 opt = Make<$ns::$optional<int>>();1958 }1959 opt.value(); // [[unsafe]]1960 }1961 )code");1962}1963 1964TEST_P(UncheckedOptionalAccessTest, AccessValueInLoop) {1965 ExpectDiagnosticsFor(R"(1966 #include "unchecked_optional_access_test.h"1967 1968 void target() {1969 $ns::$optional<int> opt = 3;1970 while (Make<bool>()) {1971 opt.value();1972 }1973 }1974 )");1975}1976 1977TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopWithCheckSafe) {1978 ExpectDiagnosticsFor(R"(1979 #include "unchecked_optional_access_test.h"1980 1981 void target() {1982 $ns::$optional<int> opt = 3;1983 while (Make<bool>()) {1984 opt.value();1985 1986 opt = Make<$ns::$optional<int>>();1987 if (!opt.has_value()) return;1988 }1989 }1990 )");1991}1992 1993TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopNoCheckUnsafe) {1994 ExpectDiagnosticsFor(1995 R"(1996 #include "unchecked_optional_access_test.h"1997 1998 void target() {1999 $ns::$optional<int> opt = 3;2000 while (Make<bool>()) {2001 opt.value(); // [[unsafe]]2002 2003 opt = Make<$ns::$optional<int>>();2004 }2005 }2006 )");2007}2008 2009TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToUnsetUnsafe) {2010 ExpectDiagnosticsFor(2011 R"(2012 #include "unchecked_optional_access_test.h"2013 2014 void target() {2015 $ns::$optional<int> opt = 3;2016 while (Make<bool>())2017 opt = $ns::nullopt;2018 $ns::$optional<int> opt2 = $ns::nullopt;2019 if (opt.has_value())2020 opt2 = $ns::$optional<int>(3);2021 opt2.value(); // [[unsafe]]2022 }2023 )");2024}2025 2026TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToSetUnsafe) {2027 ExpectDiagnosticsFor(2028 R"(2029 #include "unchecked_optional_access_test.h"2030 2031 void target() {2032 $ns::$optional<int> opt = $ns::nullopt;2033 while (Make<bool>())2034 opt = $ns::$optional<int>(3);2035 $ns::$optional<int> opt2 = $ns::nullopt;2036 if (!opt.has_value())2037 opt2 = $ns::$optional<int>(3);2038 opt2.value(); // [[unsafe]]2039 }2040 )");2041}2042 2043TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToUnknownUnsafe) {2044 ExpectDiagnosticsFor(2045 R"(2046 #include "unchecked_optional_access_test.h"2047 2048 void target() {2049 $ns::$optional<int> opt = $ns::nullopt;2050 while (Make<bool>())2051 opt = Make<$ns::$optional<int>>();2052 $ns::$optional<int> opt2 = $ns::nullopt;2053 if (!opt.has_value())2054 opt2 = $ns::$optional<int>(3);2055 opt2.value(); // [[unsafe]]2056 }2057 )");2058}2059 2060TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopBadConditionUnsafe) {2061 ExpectDiagnosticsFor(2062 R"(2063 #include "unchecked_optional_access_test.h"2064 2065 void target() {2066 $ns::$optional<int> opt = 3;2067 while (Make<bool>()) {2068 opt.value(); // [[unsafe]]2069 2070 opt = Make<$ns::$optional<int>>();2071 if (!opt.has_value()) continue;2072 }2073 }2074 )");2075}2076 2077TEST_P(UncheckedOptionalAccessTest, StructuredBindingsFromStruct) {2078 ExpectDiagnosticsFor(R"(2079 #include "unchecked_optional_access_test.h"2080 2081 struct kv { $ns::$optional<int> opt; int x; };2082 int target() {2083 auto [contents, x] = Make<kv>();2084 return contents ? *contents : x;2085 }2086 )");2087 2088 ExpectDiagnosticsFor(R"(2089 #include "unchecked_optional_access_test.h"2090 2091 template <typename T1, typename T2>2092 struct pair { T1 fst; T2 snd; };2093 int target() {2094 auto [contents, x] = Make<pair<$ns::$optional<int>, int>>();2095 return contents ? *contents : x;2096 }2097 )");2098}2099 2100TEST_P(UncheckedOptionalAccessTest, StructuredBindingsFromTupleLikeType) {2101 ExpectDiagnosticsFor(R"(2102 #include "unchecked_optional_access_test.h"2103 2104 namespace std {2105 template <class> struct tuple_size;2106 template <size_t, class> struct tuple_element;2107 template <class...> class tuple;2108 2109 template <class... T>2110 struct tuple_size<tuple<T...>> : integral_constant<size_t, sizeof...(T)> {};2111 2112 template <size_t I, class... T>2113 struct tuple_element<I, tuple<T...>> {2114 using type = __type_pack_element<I, T...>;2115 };2116 2117 template <class...> class tuple {};2118 template <size_t I, class... T>2119 typename tuple_element<I, tuple<T...>>::type get(tuple<T...>);2120 } // namespace std2121 2122 std::tuple<$ns::$optional<const char *>, int> get_opt();2123 void target() {2124 auto [content, ck] = get_opt();2125 content ? *content : "";2126 }2127 )");2128}2129 2130TEST_P(UncheckedOptionalAccessTest, CtorInitializerNullopt) {2131 using namespace ast_matchers;2132 ExpectDiagnosticsFor(2133 R"(2134 #include "unchecked_optional_access_test.h"2135 2136 struct Target {2137 Target(): opt($ns::nullopt) {2138 opt.value(); // [[unsafe]]2139 }2140 $ns::$optional<int> opt;2141 };2142 )",2143 cxxConstructorDecl(ofClass(hasName("Target"))));2144}2145 2146TEST_P(UncheckedOptionalAccessTest, CtorInitializerValue) {2147 using namespace ast_matchers;2148 ExpectDiagnosticsFor(2149 R"(2150 #include "unchecked_optional_access_test.h"2151 2152 struct Target {2153 Target(): opt(3) {2154 opt.value();2155 }2156 $ns::$optional<int> opt;2157 };2158 )",2159 cxxConstructorDecl(ofClass(hasName("Target"))));2160}2161 2162// This is regression test, it shouldn't crash.2163TEST_P(UncheckedOptionalAccessTest, Bitfield) {2164 using namespace ast_matchers;2165 ExpectDiagnosticsFor(2166 R"(2167 #include "unchecked_optional_access_test.h"2168 struct Dst {2169 unsigned int n : 1;2170 };2171 void target() {2172 $ns::$optional<bool> v;2173 Dst d;2174 if (v.has_value())2175 d.n = v.value();2176 }2177 )");2178}2179 2180TEST_P(UncheckedOptionalAccessTest, LambdaParam) {2181 ExpectDiagnosticsForLambda(R"(2182 #include "unchecked_optional_access_test.h"2183 2184 void target() {2185 []($ns::$optional<int> opt) {2186 if (opt.has_value()) {2187 opt.value();2188 } else {2189 opt.value(); // [[unsafe]]2190 }2191 }(Make<$ns::$optional<int>>());2192 }2193 )");2194}2195 2196TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByCopy) {2197 ExpectDiagnosticsForLambda(R"(2198 #include "unchecked_optional_access_test.h"2199 2200 void target($ns::$optional<int> opt) {2201 [opt]() {2202 if (opt.has_value()) {2203 opt.value();2204 } else {2205 opt.value(); // [[unsafe]]2206 }2207 }();2208 }2209 )");2210}2211 2212TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByReference) {2213 ExpectDiagnosticsForLambda(R"(2214 #include "unchecked_optional_access_test.h"2215 2216 void target($ns::$optional<int> opt) {2217 [&opt]() {2218 if (opt.has_value()) {2219 opt.value();2220 } else {2221 opt.value(); // [[unsafe]]2222 }2223 }();2224 }2225 )");2226}2227 2228TEST_P(UncheckedOptionalAccessTest, LambdaCaptureWithInitializer) {2229 ExpectDiagnosticsForLambda(R"(2230 #include "unchecked_optional_access_test.h"2231 2232 void target($ns::$optional<int> opt) {2233 [opt2=opt]() {2234 if (opt2.has_value()) {2235 opt2.value();2236 } else {2237 opt2.value(); // [[unsafe]]2238 }2239 }();2240 }2241 )");2242}2243 2244TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByCopyImplicit) {2245 ExpectDiagnosticsForLambda(R"(2246 #include "unchecked_optional_access_test.h"2247 2248 void target($ns::$optional<int> opt) {2249 [=]() {2250 if (opt.has_value()) {2251 opt.value();2252 } else {2253 opt.value(); // [[unsafe]]2254 }2255 }();2256 }2257 )");2258}2259 2260TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByReferenceImplicit) {2261 ExpectDiagnosticsForLambda(R"(2262 #include "unchecked_optional_access_test.h"2263 2264 void target($ns::$optional<int> opt) {2265 [&]() {2266 if (opt.has_value()) {2267 opt.value();2268 } else {2269 opt.value(); // [[unsafe]]2270 }2271 }();2272 }2273 )");2274}2275 2276TEST_P(UncheckedOptionalAccessTest, LambdaCaptureThis) {2277 ExpectDiagnosticsForLambda(R"(2278 #include "unchecked_optional_access_test.h"2279 2280 struct Foo {2281 $ns::$optional<int> opt;2282 2283 void target() {2284 [this]() {2285 if (opt.has_value()) {2286 opt.value();2287 } else {2288 opt.value(); // [[unsafe]]2289 }2290 }();2291 }2292 };2293 )");2294}2295 2296TEST_P(UncheckedOptionalAccessTest, LambdaCaptureStateNotPropagated) {2297 // We can't propagate information from the surrounding context.2298 ExpectDiagnosticsForLambda(R"(2299 #include "unchecked_optional_access_test.h"2300 2301 void target($ns::$optional<int> opt) {2302 if (opt.has_value()) {2303 [&opt]() {2304 opt.value(); // [[unsafe]]2305 }();2306 }2307 }2308 )");2309}2310 2311TEST_P(UncheckedOptionalAccessTest, ClassDerivedFromOptional) {2312 ExpectDiagnosticsFor(R"(2313 #include "unchecked_optional_access_test.h"2314 2315 struct Derived : public $ns::$optional<int> {};2316 2317 void target(Derived opt) {2318 *opt; // [[unsafe]]2319 if (opt.has_value())2320 *opt;2321 2322 // The same thing, but with a pointer receiver.2323 Derived *popt = &opt;2324 **popt; // [[unsafe]]2325 if (popt->has_value())2326 **popt;2327 }2328 )");2329}2330 2331TEST_P(UncheckedOptionalAccessTest, ClassTemplateDerivedFromOptional) {2332 ExpectDiagnosticsFor(R"(2333 #include "unchecked_optional_access_test.h"2334 2335 template <class T>2336 struct Derived : public $ns::$optional<T> {};2337 2338 void target(Derived<int> opt) {2339 *opt; // [[unsafe]]2340 if (opt.has_value())2341 *opt;2342 2343 // The same thing, but with a pointer receiver.2344 Derived<int> *popt = &opt;2345 **popt; // [[unsafe]]2346 if (popt->has_value())2347 **popt;2348 }2349 )");2350}2351 2352TEST_P(UncheckedOptionalAccessTest, ClassDerivedPrivatelyFromOptional) {2353 // Classes that derive privately from optional can themselves still call2354 // member functions of optional. Check that we model the optional correctly2355 // in this situation.2356 ExpectDiagnosticsFor(R"(2357 #include "unchecked_optional_access_test.h"2358 2359 struct Derived : private $ns::$optional<int> {2360 void Method() {2361 **this; // [[unsafe]]2362 if (this->has_value())2363 **this;2364 }2365 };2366 )",2367 ast_matchers::hasName("Method"));2368}2369 2370TEST_P(UncheckedOptionalAccessTest, ClassDerivedFromOptionalValueConstructor) {2371 ExpectDiagnosticsFor(R"(2372 #include "unchecked_optional_access_test.h"2373 2374 struct Derived : public $ns::$optional<int> {2375 Derived(int);2376 };2377 2378 void target(Derived opt) {2379 *opt; // [[unsafe]]2380 opt = 1;2381 *opt;2382 }2383 )");2384}2385 2386TEST_P(UncheckedOptionalAccessTest, ConstRefAccessor) {2387 ExpectDiagnosticsFor(R"cc(2388 #include "unchecked_optional_access_test.h"2389 2390 struct A {2391 const $ns::$optional<int>& get() const { return x; }2392 $ns::$optional<int> x;2393 };2394 2395 void target(A& a) {2396 if (a.get().has_value()) {2397 a.get().value();2398 }2399 }2400 )cc");2401}2402 2403TEST_P(UncheckedOptionalAccessTest, ConstRefAccessorWithModInBetween) {2404 ExpectDiagnosticsFor(R"cc(2405 #include "unchecked_optional_access_test.h"2406 2407 struct A {2408 const $ns::$optional<int>& get() const { return x; }2409 void clear();2410 $ns::$optional<int> x;2411 };2412 2413 void target(A& a) {2414 if (a.get().has_value()) {2415 a.clear();2416 a.get().value(); // [[unsafe]]2417 }2418 }2419 )cc");2420}2421 2422TEST_P(UncheckedOptionalAccessTest, ConstRefAccessorWithModReturningOptional) {2423 ExpectDiagnosticsFor(R"cc(2424 #include "unchecked_optional_access_test.h"2425 2426 struct A {2427 const $ns::$optional<int>& get() const { return x; }2428 $ns::$optional<int> take();2429 $ns::$optional<int> x;2430 };2431 2432 void target(A& a) {2433 if (a.get().has_value()) {2434 $ns::$optional<int> other = a.take();2435 a.get().value(); // [[unsafe]]2436 if (other.has_value()) {2437 other.value();2438 }2439 }2440 }2441 )cc");2442}2443 2444TEST_P(UncheckedOptionalAccessTest, ConstRefAccessorDifferentObjects) {2445 ExpectDiagnosticsFor(R"cc(2446 #include "unchecked_optional_access_test.h"2447 2448 struct A {2449 const $ns::$optional<int>& get() const { return x; }2450 $ns::$optional<int> x;2451 };2452 2453 void target(A& a1, A& a2) {2454 if (a1.get().has_value()) {2455 a2.get().value(); // [[unsafe]]2456 }2457 }2458 )cc");2459}2460 2461TEST_P(UncheckedOptionalAccessTest, ConstRefAccessorLoop) {2462 ExpectDiagnosticsFor(R"cc(2463 #include "unchecked_optional_access_test.h"2464 2465 struct A {2466 const $ns::$optional<int>& get() const { return x; }2467 $ns::$optional<int> x;2468 };2469 2470 void target(A& a, int N) {2471 for (int i = 0; i < N; ++i) {2472 if (a.get().has_value()) {2473 a.get().value();2474 }2475 }2476 }2477 )cc");2478}2479 2480TEST_P(UncheckedOptionalAccessTest, ConstByValueAccessor) {2481 ExpectDiagnosticsFor(R"cc(2482 #include "unchecked_optional_access_test.h"2483 2484 struct A {2485 $ns::$optional<int> get() const { return x; }2486 $ns::$optional<int> x;2487 };2488 2489 void target(A& a) {2490 if (a.get().has_value()) {2491 a.get().value();2492 }2493 }2494 )cc");2495}2496 2497TEST_P(UncheckedOptionalAccessTest, ConstByValueAccessorWithModInBetween) {2498 ExpectDiagnosticsFor(R"cc(2499 #include "unchecked_optional_access_test.h"2500 2501 struct A {2502 $ns::$optional<int> get() const { return x; }2503 void clear();2504 $ns::$optional<int> x;2505 };2506 2507 void target(A& a) {2508 if (a.get().has_value()) {2509 a.clear();2510 a.get().value(); // [[unsafe]]2511 }2512 }2513 )cc");2514}2515 2516TEST_P(UncheckedOptionalAccessTest, ConstPointerAccessor) {2517 ExpectDiagnosticsFor(R"cc(2518 #include "unchecked_optional_access_test.h"2519 2520 struct A {2521 $ns::$optional<int> x;2522 };2523 2524 struct MyUniquePtr {2525 A* operator->() const;2526 };2527 2528 void target(MyUniquePtr p) {2529 if (p->x) {2530 *p->x;2531 }2532 }2533 )cc",2534 /*IgnoreSmartPointerDereference=*/false);2535}2536 2537TEST_P(UncheckedOptionalAccessTest, ConstPointerAccessorWithModInBetween) {2538 ExpectDiagnosticsFor(R"cc(2539 #include "unchecked_optional_access_test.h"2540 2541 struct A {2542 $ns::$optional<int> x;2543 };2544 2545 struct MyUniquePtr {2546 A* operator->() const;2547 void reset(A*);2548 };2549 2550 void target(MyUniquePtr p) {2551 if (p->x) {2552 p.reset(nullptr);2553 *p->x; // [[unsafe]]2554 }2555 }2556 )cc",2557 /*IgnoreSmartPointerDereference=*/false);2558}2559 2560TEST_P(UncheckedOptionalAccessTest, SmartPointerAccessorMixed) {2561 ExpectDiagnosticsFor(R"cc(2562 #include "unchecked_optional_access_test.h"2563 2564 struct A {2565 $ns::$optional<int> x;2566 };2567 2568 namespace absl {2569 template<typename T>2570 class StatusOr {2571 public:2572 bool ok() const;2573 2574 const T& operator*() const&;2575 T& operator*() &;2576 2577 const T* operator->() const;2578 T* operator->();2579 2580 const T& value() const;2581 T& value();2582 };2583 }2584 2585 void target(absl::StatusOr<A> &mut, const absl::StatusOr<A> &imm) {2586 if (!mut.ok() || !imm.ok())2587 return;2588 2589 if (mut->x.has_value()) {2590 mut->x.value();2591 ((*mut).x).value();2592 (mut.value().x).value();2593 2594 // check flagged after modifying2595 mut = imm;2596 mut->x.value(); // [[unsafe]]2597 }2598 if (imm->x.has_value()) {2599 imm->x.value();2600 ((*imm).x).value();2601 (imm.value().x).value();2602 }2603 }2604 )cc",2605 /*IgnoreSmartPointerDereference=*/false);2606}2607 2608TEST_P(UncheckedOptionalAccessTest, ConstBoolAccessor) {2609 ExpectDiagnosticsFor(R"cc(2610 #include "unchecked_optional_access_test.h"2611 2612 struct A {2613 bool isFoo() const { return f; }2614 bool f;2615 };2616 2617 void target(A& a) {2618 $ns::$optional<int> opt;2619 if (a.isFoo()) {2620 opt = 1;2621 }2622 if (a.isFoo()) {2623 opt.value();2624 }2625 }2626 )cc");2627}2628 2629TEST_P(UncheckedOptionalAccessTest, ConstBoolAccessorWithModInBetween) {2630 ExpectDiagnosticsFor(R"cc(2631 #include "unchecked_optional_access_test.h"2632 2633 struct A {2634 bool isFoo() const { return f; }2635 void clear();2636 bool f;2637 };2638 2639 void target(A& a) {2640 $ns::$optional<int> opt;2641 if (a.isFoo()) {2642 opt = 1;2643 }2644 a.clear();2645 if (a.isFoo()) {2646 opt.value(); // [[unsafe]]2647 }2648 }2649 )cc");2650}2651 2652TEST_P(UncheckedOptionalAccessTest,2653 ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObject) {2654 ExpectDiagnosticsFor(R"cc(2655 #include "unchecked_optional_access_test.h"2656 2657 struct A {2658 const $ns::$optional<int>& get() const { return x; }2659 2660 $ns::$optional<int> x;2661 };2662 2663 struct B {2664 const A& getA() const { return a; }2665 2666 A a;2667 };2668 2669 void target(B& b) {2670 if (b.getA().get().has_value()) {2671 b.getA().get().value();2672 }2673 }2674 )cc");2675}2676 2677TEST_P(2678 UncheckedOptionalAccessTest,2679 ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithoutValueCheck) {2680 ExpectDiagnosticsFor(R"cc(2681 #include "unchecked_optional_access_test.h"2682 2683 struct A {2684 const $ns::$optional<int>& get() const { return x; }2685 2686 $ns::$optional<int> x;2687 };2688 2689 struct B {2690 const A& getA() const { return a; }2691 2692 A a;2693 };2694 2695 void target(B& b) {2696 b.getA().get().value(); // [[unsafe]]2697 }2698 )cc");2699}2700 2701TEST_P(UncheckedOptionalAccessTest,2702 ConstRefToOptionalSavedAsTemporaryVariable) {2703 ExpectDiagnosticsFor(R"cc(2704 #include "unchecked_optional_access_test.h"2705 2706 struct A {2707 const $ns::$optional<int>& get() const { return x; }2708 2709 $ns::$optional<int> x;2710 };2711 2712 struct B {2713 const A& getA() const { return a; }2714 2715 A a;2716 };2717 2718 void target(B& b) {2719 const auto& opt = b.getA().get();2720 if (opt.has_value()) {2721 opt.value();2722 }2723 }2724 )cc");2725}2726 2727TEST_P(UncheckedOptionalAccessTest,2728 ConstRefAccessorToOptionalViaAccessorToHoldingObjectByValue) {2729 ExpectDiagnosticsFor(R"cc(2730 #include "unchecked_optional_access_test.h"2731 2732 struct A {2733 const $ns::$optional<int>& get() const { return x; }2734 2735 $ns::$optional<int> x;2736 };2737 2738 struct B {2739 const A copyA() const { return a; }2740 2741 A a;2742 };2743 2744 void target(B& b) {2745 if (b.copyA().get().has_value()) {2746 b.copyA().get().value(); // [[unsafe]]2747 }2748 }2749 )cc");2750}2751 2752TEST_P(UncheckedOptionalAccessTest,2753 ConstRefAccessorToOptionalViaNonConstRefAccessorToHoldingObject) {2754 ExpectDiagnosticsFor(R"cc(2755 #include "unchecked_optional_access_test.h"2756 2757 struct A {2758 const $ns::$optional<int>& get() const { return x; }2759 2760 $ns::$optional<int> x;2761 };2762 2763 struct B {2764 A& getA() { return a; }2765 2766 A a;2767 };2768 2769 void target(B& b) {2770 if (b.getA().get().has_value()) {2771 b.getA().get().value(); // [[unsafe]]2772 }2773 }2774 )cc");2775}2776 2777TEST_P(2778 UncheckedOptionalAccessTest,2779 ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithModAfterCheck) {2780 ExpectDiagnosticsFor(R"cc(2781 #include "unchecked_optional_access_test.h"2782 2783 struct A {2784 const $ns::$optional<int>& get() const { return x; }2785 2786 $ns::$optional<int> x;2787 };2788 2789 struct B {2790 const A& getA() const { return a; }2791 2792 A& getA() { return a; }2793 2794 void clear() { a = A{}; }2795 2796 A a;2797 };2798 2799 void target(B& b) {2800 // changing field A via non-const getter after const getter check2801 if (b.getA().get().has_value()) {2802 b.getA() = A{};2803 b.getA().get().value(); // [[unsafe]]2804 }2805 2806 // calling non-const method which might change field A2807 if (b.getA().get().has_value()) {2808 b.clear();2809 b.getA().get().value(); // [[unsafe]]2810 }2811 }2812 )cc");2813}2814 2815TEST_P(2816 UncheckedOptionalAccessTest,2817 ConstRefAccessorToOptionalViaConstRefAccessorToHoldingObjectWithAnotherConstCallAfterCheck) {2818 ExpectDiagnosticsFor(R"cc(2819 #include "unchecked_optional_access_test.h"2820 2821 struct A {2822 const $ns::$optional<int>& get() const { return x; }2823 2824 $ns::$optional<int> x;2825 };2826 2827 struct B {2828 const A& getA() const { return a; }2829 2830 void callWithoutChanges() const { 2831 // no-op 2832 }2833 2834 A a;2835 };2836 2837 void target(B& b) { 2838 if (b.getA().get().has_value()) {2839 b.callWithoutChanges(); // calling const method which cannot change A2840 b.getA().get().value();2841 }2842 }2843 )cc");2844}2845 2846TEST_P(UncheckedOptionalAccessTest, ConstPointerRefAccessor) {2847 // A crash reproducer for https://github.com/llvm/llvm-project/issues/1255892848 // NOTE: we currently cache const ref accessors's locations.2849 // If we want to support const ref to pointers or bools, we should initialize2850 // their values.2851 ExpectDiagnosticsFor(R"cc(2852 #include "unchecked_optional_access_test.h"2853 2854 struct A {2855 $ns::$optional<int> x;2856 };2857 2858 struct PtrWrapper {2859 A*& getPtrRef() const;2860 void reset(A*);2861 };2862 2863 void target(PtrWrapper p) {2864 if (p.getPtrRef()->x) {2865 *p.getPtrRef()->x; // [[unsafe]]2866 p.reset(nullptr);2867 *p.getPtrRef()->x; // [[unsafe]]2868 }2869 }2870 )cc",2871 /*IgnoreSmartPointerDereference=*/false);2872}2873 2874TEST_P(UncheckedOptionalAccessTest, DiagnosticsHaveRanges) {2875 ExpectDiagnosticsFor(R"cc(2876 #include "unchecked_optional_access_test.h"2877 2878 struct A {2879 $ns::$optional<int> fi;2880 };2881 struct B {2882 $ns::$optional<A> fa;2883 };2884 2885 void target($ns::$optional<B> opt) {2886 opt.value(); // [[unsafe:<input.cc:12:7>]]2887 if (opt) {2888 opt // [[unsafe:<input.cc:14:9, line:16:13>]]2889 ->2890 fa.value();2891 if (opt->fa) {2892 opt->fa->fi.value(); // [[unsafe:<input.cc:18:11, col:20>]]2893 }2894 }2895 }2896 )cc");2897}2898 2899// FIXME: Add support for:2900// - constructors (copy, move)2901// - assignment operators (default, copy, move)2902// - invalidation (passing optional by non-const reference/pointer)2903