diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h @@ -219,7 +219,9 @@ std::function>( ASTContext &)> MakeDiagnosis, - std::function, ASTContext &)> Expectations, + std::function &, + llvm::DenseSet &, ASTContext &)> + Expectations, ArrayRef Args, const tooling::FileContentMappings &VirtualMappedFiles = {}) { return checkDataflowHelper( @@ -229,7 +231,8 @@ auto Diags = diagnoseCFG( AnalysisResults.CFCtx, AnalysisResults.BlockStates, AnalysisResults.Env, AnalysisResults.Analysis, Diagnose); - Expectations(Diags, AnalysisResults.Context); + Expectations(AnalysisResults.Annotations, Diags, + AnalysisResults.Context); }, Args, VirtualMappedFiles); } diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp @@ -11,7 +11,6 @@ #include "TestingSupport.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" #include "clang/Basic/SourceLocation.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/ArrayRef.h" @@ -28,6 +27,8 @@ using namespace dataflow; using namespace test; +using ::testing::ContainerEq; + // FIXME: Move header definitions in separate file(s). static constexpr char CSDtdDefHeader[] = R"( #ifndef CSTDDEF_H @@ -1179,12 +1180,6 @@ } // namespace base )"; -/// Converts `L` to string. -static std::string ConvertToString(const llvm::DenseSet &L, - const ASTContext &Ctx) { - return L.empty() ? "safe" : "unsafe: " + DebugString(L, Ctx); -} - /// Replaces all occurrences of `Pattern` in `S` with `Replacement`. static void ReplaceAllOccurrences(std::string &S, const std::string &Pattern, const std::string &Replacement) { @@ -1205,18 +1200,14 @@ class UncheckedOptionalAccessTest : public ::testing::TestWithParam { protected: - template - void ExpectLatticeChecksFor(std::string SourceCode, - LatticeChecksMatcher MatchesLatticeChecks) { - ExpectLatticeChecksFor(SourceCode, ast_matchers::hasName("target"), - MatchesLatticeChecks); + void ExpectDiagnosticsFor(std::string SourceCode) { + ExpectDiagnosticsFor(SourceCode, ast_matchers::hasName("target")); } private: - template - void ExpectLatticeChecksFor(std::string SourceCode, - FuncDeclMatcher FuncMatcher, - LatticeChecksMatcher MatchesLatticeChecks) { + template + void ExpectDiagnosticsFor(std::string SourceCode, + FuncDeclMatcher FuncMatcher) { ReplaceAllOccurrences(SourceCode, "$ns", GetParam().NamespaceName); ReplaceAllOccurrences(SourceCode, "$optional", GetParam().TypeName); @@ -1260,10 +1251,30 @@ return Diagnosis.diagnose(Stmt, Env); }; }, - [&MatchesLatticeChecks](llvm::DenseSet Locs, - ASTContext &Ctx) { - std::string StringifiedDiags = ConvertToString(Locs, Ctx); - EXPECT_THAT(StringifiedDiags, MatchesLatticeChecks); + [](llvm::DenseMap &Annotations, + llvm::DenseSet &Locs, ASTContext &Ctx) { + auto &SrcMgr = Ctx.getSourceManager(); + + llvm::DenseSet AnnotationLines; + for (const auto &Pair : Annotations) { + auto *Stmt = Pair.getFirst(); + AnnotationLines.insert( + SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc())); + // We add both the begin and end locations, so that if the + // statement spans multiple lines then the test will fail. Going + // forward, we should change this to instead just get the single + // line number from the annotation itself, rather than looking + // at the statement it's attached to. + AnnotationLines.insert( + SrcMgr.getPresumedLineNumber(Stmt->getEndLoc())); + } + + llvm::DenseSet DiagnosticLines; + for (SourceLocation &Loc : Locs) { + DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc)); + } + + EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines)); }, {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents); @@ -1282,65 +1293,55 @@ }); TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( void target() { (void)0; - /*[[check]]*/ } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { - opt.value(); - /*[[check]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:5:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { - std::move(opt).value(); - /*[[check]]*/ + std::move(opt).value(); // [[unsafe]] } - )", - "unsafe: input.cc:5:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { - *opt; - /*[[check]]*/ + *opt; // [[unsafe]] } - )", - "unsafe: input.cc:5:8"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { - *std::move(opt); - /*[[check]]*/ + *std::move(opt); // [[unsafe]] } - )", - "unsafe: input.cc:5:8"); + )"); } TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -1349,13 +1350,11 @@ }; void target($ns::$optional opt) { - opt->foo(); - /*[[check]]*/ + opt->foo(); // [[unsafe]] } - )", - "unsafe: input.cc:9:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -1364,107 +1363,91 @@ }; void target($ns::$optional opt) { - std::move(opt)->foo(); - /*[[check]]*/ + std::move(opt)->foo(); // [[unsafe]] } - )", - "unsafe: input.cc:9:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, HasValueCheck) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { if (opt.has_value()) { opt.value(); - /*[[check]]*/ } } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { if (opt) { opt.value(); - /*[[check]]*/ } } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target() { - Make<$ns::$optional>().value(); + Make<$ns::$optional>().value(); // [[unsafe]] (void)0; - /*[[check]]*/ } - )", - "unsafe: input.cc:5:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { - std::move(opt).value(); - /*[[check]]*/ + std::move(opt).value(); // [[unsafe]] } - )", - "unsafe: input.cc:5:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt; - opt.value(); - /*[[check]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:6:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt($ns::nullopt); - opt.value(); - /*[[check]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:6:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt($ns::in_place, 3); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo {}; @@ -1472,12 +1455,10 @@ void target() { $ns::$optional opt($ns::in_place); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo { @@ -1487,12 +1468,10 @@ void target() { $ns::$optional opt($ns::in_place, 3, false); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo { @@ -1502,46 +1481,38 @@ void target() { $ns::$optional opt($ns::in_place, {3}); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, ValueConstructor) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt(21); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt = $ns::$optional(21); opt.value(); - /*[[check]]*/ } - )", - "safe"); - ExpectLatticeChecksFor(R"( + )"); + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional<$ns::$optional> opt(Make<$ns::$optional>()); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct MyString { @@ -1551,12 +1522,10 @@ void target() { $ns::$optional opt("foo"); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo {}; @@ -1568,12 +1537,10 @@ void target() { $ns::$optional opt(Make()); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo { @@ -1583,14 +1550,12 @@ void target() { $ns::$optional opt(3); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -1602,13 +1567,11 @@ void target() { $ns::$optional opt(Make<$ns::$optional>()); - opt.value(); - /*[[check]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:12:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -1620,13 +1583,11 @@ void target() { $ns::$optional opt(Make<$ns::$optional>()); - opt.value(); - /*[[check]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:12:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -1639,13 +1600,11 @@ void target() { $ns::$optional opt1 = $ns::nullopt; $ns::$optional opt2(opt1); - opt2.value(); - /*[[check]]*/ + opt2.value(); // [[unsafe]] } - )", - "unsafe: input.cc:13:7"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo {}; @@ -1658,12 +1617,10 @@ $ns::$optional opt1(Make()); $ns::$optional opt2(opt1); opt2.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo {}; @@ -1676,25 +1633,21 @@ $ns::$optional opt1(Make()); $ns::$optional opt2(opt1); opt2.value(); - /*[[check]]*/ } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, MakeOptional) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt = $ns::make_optional(0); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo { @@ -1704,12 +1657,10 @@ void target() { $ns::$optional opt = $ns::make_optional(21, 22); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo { @@ -1720,99 +1671,83 @@ char a = 'a'; $ns::$optional opt = $ns::make_optional({a}); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, ValueOr) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt; opt.value_or(0); (void)0; - /*[[check]]*/ } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, ValueOrComparison) { // Pointers. - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { if (opt.value_or(nullptr) != nullptr) { opt.value(); - /*[[check-ptrs-1]]*/ } else { - opt.value(); - /*[[check-ptrs-2]]*/ + opt.value(); // [[unsafe]] } } - )code", - "unsafe: input.cc:9:9"); + )code"); // Integers. - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { if (opt.value_or(0) != 0) { opt.value(); - /*[[check-ints-1]]*/ } else { - opt.value(); - /*[[check-ints-2]]*/ + opt.value(); // [[unsafe]] } } - )code", - "unsafe: input.cc:9:9"); + )code"); // Strings. - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { if (!opt.value_or("").empty()) { opt.value(); - /*[[check-strings-1]]*/ } else { - opt.value(); - /*[[check-strings-2]]*/ + opt.value(); // [[unsafe]] } } - )code", - "unsafe: input.cc:9:9"); + )code"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" void target($ns::$optional opt) { if (opt.value_or("") != "") { opt.value(); - /*[[check-strings-neq-1]]*/ } else { - opt.value(); - /*[[check-strings-neq-2]]*/ + opt.value(); // [[unsafe]] } } - )code", - "unsafe: input.cc:9:9"); + )code"); // Pointer-to-optional. // // FIXME: make `opt` a parameter directly, once we ensure that all `optional` // values have a `has_value` property. - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" @@ -1820,42 +1755,35 @@ $ns::$optional *opt = &p; if (opt->value_or(0) != 0) { opt->value(); - /*[[check-pto-1]]*/ } else { - opt->value(); - /*[[check-pto-2]]*/ + opt->value(); // [[unsafe]] } } - )code", - "unsafe: input.cc:10:9"); + )code"); } TEST_P(UncheckedOptionalAccessTest, Emplace) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt; opt.emplace(0); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target($ns::$optional *opt) { opt->emplace(0); opt->value(); - /*[[check]]*/ } - )", - "safe"); + )"); // FIXME: Add tests that call `emplace` in conditional branches: - // ExpectLatticeChecksFor( + // ExpectDiagnosticsFor( // R"( // #include "unchecked_optional_access_test.h" // @@ -1865,46 +1793,39 @@ // } // if (b) { // opt.value(); - // /*[[check-1]]*/ // } else { - // opt.value(); - // /*[[check-2]]*/ + // opt.value(); // [[unsafe]] // } // } - // )", - // "unsafe: input.cc:12:9"); + // )"); } TEST_P(UncheckedOptionalAccessTest, Reset) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt = $ns::make_optional(0); opt.reset(); - opt.value(); - /*[[check]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:7:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target($ns::$optional &opt) { if (opt.has_value()) { opt.reset(); - opt.value(); - /*[[check]]*/ + opt.value(); // [[unsafe]] } } - )", - "unsafe: input.cc:7:9"); + )"); // FIXME: Add tests that call `reset` in conditional branches: - // ExpectLatticeChecksFor( + // ExpectDiagnosticsFor( // R"( // #include "unchecked_optional_access_test.h" // @@ -1914,19 +1835,16 @@ // opt.reset(); // } // if (b) { - // opt.value(); - // /*[[check-1]]*/ + // opt.value(); // [[unsafe]] // } else { // opt.value(); - // /*[[check-2]]*/ // } // } - // )", - // "safe"); + // )"); } TEST_P(UncheckedOptionalAccessTest, ValueAssignment) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo {}; @@ -1935,12 +1853,10 @@ $ns::$optional opt; opt = Foo(); opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct Foo {}; @@ -1949,12 +1865,10 @@ $ns::$optional opt; (opt = Foo()).value(); (void)0; - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct MyString { @@ -1965,12 +1879,10 @@ $ns::$optional opt; opt = "foo"; opt.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" struct MyString { @@ -1980,14 +1892,12 @@ void target() { $ns::$optional opt; (opt = "foo").value(); - /*[[check]]*/ } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2002,12 +1912,10 @@ $ns::$optional opt2; opt2 = opt1; opt2.value(); - /*[[check]]*/ } - )", - "safe"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2022,14 +1930,12 @@ $ns::$optional opt2; if (opt2.has_value()) { opt2 = opt1; - opt2.value(); - /*[[check]]*/ + opt2.value(); // [[unsafe]] } } - )", - "unsafe: input.cc:15:9"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2044,41 +1950,35 @@ $ns::$optional opt2; (opt2 = opt1).value(); (void)0; - /*[[check]]*/ } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt = 3; opt = $ns::nullopt; - opt.value(); - /*[[check]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:7:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt = 3; - (opt = $ns::nullopt).value(); - /*[[check]]*/ + (opt = $ns::nullopt).value(); // [[unsafe]] } - )", - "unsafe: input.cc:6:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, OptionalSwap) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2089,15 +1989,12 @@ opt1.swap(opt2); opt1.value(); - /*[[check-1]]*/ - opt2.value(); - /*[[check-2]]*/ + opt2.value(); // [[unsafe]] } - )", - "unsafe: input.cc:13:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2108,17 +2005,14 @@ opt2.swap(opt1); opt1.value(); - /*[[check-3]]*/ - opt2.value(); - /*[[check-4]]*/ + opt2.value(); // [[unsafe]] } - )", - "unsafe: input.cc:13:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, StdSwap) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2129,15 +2023,12 @@ std::swap(opt1, opt2); opt1.value(); - /*[[check-1]]*/ - opt2.value(); - /*[[check-2]]*/ + opt2.value(); // [[unsafe]] } - )", - "unsafe: input.cc:13:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2148,19 +2039,16 @@ std::swap(opt2, opt1); opt1.value(); - /*[[check-3]]*/ - opt2.value(); - /*[[check-4]]*/ + opt2.value(); // [[unsafe]] } - )", - "unsafe: input.cc:13:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) { // We suppress diagnostics for values reachable from smart pointers (other // than `optional` itself). - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2177,16 +2065,13 @@ void target() { smart_ptr foo; *foo->opt; - /*[[check-1]]*/ *(*foo).opt; - /*[[check-2]]*/ } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2195,12 +2080,10 @@ void target() { $ns::$optional opt = 0; opt = MakeOpt(); - opt.value(); - /*[[check-1]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:9:7"); - ExpectLatticeChecksFor( + )"); + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2209,13 +2092,11 @@ void target() { $ns::$optional opt = 0; opt = MakeOpt(); - opt.value(); - /*[[check-2]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:9:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2225,13 +2106,11 @@ void target() { IntOpt opt = 0; opt = MakeOpt(); - opt.value(); - /*[[check-3]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:10:7"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2241,16 +2120,14 @@ void target() { IntOpt opt = 0; opt = MakeOpt(); - opt.value(); - /*[[check-4]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:10:7"); + )"); } // Verifies that the model sees through aliases. TEST_P(UncheckedOptionalAccessTest, WithAlias) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2258,18 +2135,16 @@ using MyOptional = $ns::$optional; void target(MyOptional opt) { - opt.value(); - /*[[check]]*/ + opt.value(); // [[unsafe]] } - )", - "unsafe: input.cc:8:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) { // Basic test that nested values are populated. We nest an optional because // its easy to use in a test, but the type of the nested value shouldn't // matter. - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2278,14 +2153,12 @@ void target($ns::$optional foo) { if (foo && *foo) { foo->value(); - /*[[access]]*/ } } - )", - "safe"); + )"); // Mutation is supported for nested values. - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2294,18 +2167,16 @@ void target($ns::$optional foo) { if (foo && *foo) { foo->reset(); - foo->value(); - /*[[reset]]*/ + foo->value(); // [[unsafe]] } } - )", - "unsafe: input.cc:9:9"); + )"); } // Tests that structs can be nested. We use an optional field because its easy // to use in a test, but the type of the field shouldn't matter. TEST_P(UncheckedOptionalAccessTest, OptionalValueStruct) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2316,18 +2187,16 @@ void target($ns::$optional foo) { if (foo && foo->opt) { foo->opt.value(); - /*[[access]]*/ } } - )", - "safe"); + )"); } TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) { // FIXME: Fix when to initialize `value`. All unwrapping should be safe in // this example, but `value` initialization is done multiple times during the // fixpoint iterations and joining the environment won't correctly merge them. - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2346,15 +2215,13 @@ } // Now we merge the two values. UncheckedOptionalAccessModel::merge() will // throw away the "value" property. - foo->value(); - /*[[merge]]*/ + foo->value(); // [[unsafe]] } - )", - "unsafe: input.cc:19:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" @@ -2366,56 +2233,48 @@ void target() { smart_ptr<$ns::$optional> x; *x = $ns::nullopt; - (*x).value(); - /*[[check]]*/ + (*x).value(); // [[unsafe]] } - )", - "unsafe: input.cc:12:7"); + )"); } TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) { - ExpectLatticeChecksFor(R"code( + ExpectDiagnosticsFor(R"code( #include "unchecked_optional_access_test.h" void target(bool b, $ns::$optional opt) { if (b || opt.has_value()) { if (!b) { opt.value(); - /*[[check-1]]*/ } } } - )code", - "safe"); + )code"); - ExpectLatticeChecksFor(R"code( + ExpectDiagnosticsFor(R"code( #include "unchecked_optional_access_test.h" void target(bool b, $ns::$optional opt) { if (b && !opt.has_value()) return; if (b) { opt.value(); - /*[[check-2]]*/ } } - )code", - "safe"); + )code"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" void target(bool b, $ns::$optional opt) { if (opt.has_value()) b = true; if (b) { - opt.value(); - /*[[check-3]]*/ + opt.value(); // [[unsafe]] } } - )code", - "unsafe: input.cc:7:9"); + )code"); - ExpectLatticeChecksFor(R"code( + ExpectDiagnosticsFor(R"code( #include "unchecked_optional_access_test.h" void target(bool b, $ns::$optional opt) { @@ -2423,41 +2282,35 @@ if (opt.has_value()) b = true; if (b) { opt.value(); - /*[[check-4]]*/ } } - )code", - "safe"); + )code"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target(bool b, $ns::$optional opt) { if (opt.has_value() == b) { if (b) { opt.value(); - /*[[check-5]]*/ } } } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target(bool b, $ns::$optional opt) { if (opt.has_value() != b) { if (!b) { opt.value(); - /*[[check-6]]*/ } } } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target(bool b) { @@ -2470,30 +2323,26 @@ } if (opt2.has_value()) { opt1.value(); - /*[[check]]*/ } } - )", - "safe"); + )"); // FIXME: Add support for operator==. - // ExpectLatticeChecksFor(R"( + // ExpectDiagnosticsFor(R"( // #include "unchecked_optional_access_test.h" // // void target($ns::$optional opt1, $ns::$optional opt2) { // if (opt1 == opt2) { // if (opt1.has_value()) { // opt2.value(); - // /*[[check-7]]*/ // } // } // } - // )", - // "safe"); + // )"); } TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) { - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" @@ -2506,16 +2355,13 @@ } if (opt.has_value()) { opt.value(); - /*[[check-1]]*/ } else { - opt.value(); - /*[[check-2]]*/ + opt.value(); // [[unsafe]] } } - )code", - "unsafe: input.cc:15:9"); + )code"); - ExpectLatticeChecksFor(R"code( + ExpectDiagnosticsFor(R"code( #include "unchecked_optional_access_test.h" void target(bool b) { @@ -2528,12 +2374,10 @@ if (!opt.has_value()) return; } opt.value(); - /*[[check-3]]*/ } - )code", - "safe"); + )code"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" @@ -2545,13 +2389,11 @@ } else { opt = Make<$ns::$optional>(); } - opt.value(); - /*[[check-4]]*/ + opt.value(); // [[unsafe]] } - )code", - "unsafe: input.cc:12:7"); + )code"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" @@ -2563,12 +2405,10 @@ opt = 2; } opt.value(); - /*[[check-5]]*/ } - )code", - "safe"); + )code"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"code( #include "unchecked_optional_access_test.h" @@ -2579,75 +2419,65 @@ } else { opt = Make<$ns::$optional>(); } - opt.value(); - /*[[check-6]]*/ + opt.value(); // [[unsafe]] } - )code", - "unsafe: input.cc:11:7"); + )code"); } TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoop) { - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt = 3; while (Make()) { opt.value(); - /*[[check-1]]*/ } } - )", - "safe"); + )"); - ExpectLatticeChecksFor(R"( + ExpectDiagnosticsFor(R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt = 3; while (Make()) { opt.value(); - /*[[check-2]]*/ opt = Make<$ns::$optional>(); if (!opt.has_value()) return; } } - )", - "safe"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt = 3; while (Make()) { - opt.value(); - /*[[check-3]]*/ + opt.value(); // [[unsafe]] opt = Make<$ns::$optional>(); } } - )", - "unsafe: input.cc:7:9"); + )"); - ExpectLatticeChecksFor( + ExpectDiagnosticsFor( R"( #include "unchecked_optional_access_test.h" void target() { $ns::$optional opt = 3; while (Make()) { - opt.value(); - /*[[check-4]]*/ + opt.value(); // [[unsafe]] opt = Make<$ns::$optional>(); if (!opt.has_value()) continue; } } - )", - "unsafe: input.cc:7:9"); + )"); } // FIXME: Add support for: