brintos

brintos / llvm-project-archived public Read only

0
0
Text · 65.0 KiB · ba509e8 Raw
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