samestep created this revision.
Herald added subscribers: martong, tschuett, carlosgalvezp, xazax.hun, mgorny.
Herald added a project: All.
samestep requested review of this revision.
Herald added projects: clang, LLVM, clang-tools-extra.
Herald added subscribers: cfe-commits, llvm-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D127898

Files:
  clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
  
clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h
  clang/include/clang/Analysis/FlowSensitive/NoopLattice.h
  clang/include/clang/Analysis/FlowSensitive/SourceLocations.h
  clang/include/clang/Analysis/FlowSensitive/SourceLocationsLattice.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
  clang/lib/Analysis/FlowSensitive/SourceLocations.cpp
  clang/lib/Analysis/FlowSensitive/SourceLocationsLattice.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
  clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h
  clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
  clang/unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
  clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
  llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn
===================================================================
--- llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn
@@ -25,7 +25,6 @@
     "MultiVarConstantPropagationTest.cpp",
     "SingleVarConstantPropagationTest.cpp",
     "SolverTest.cpp",
-    "SourceLocationsLatticeTest.cpp",
     "TestingSupport.cpp",
     "TestingSupportTest.cpp",
     "TransferTest.cpp",
Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===================================================================
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -9,7 +9,7 @@
     "ControlFlowContext.cpp",
     "DataflowAnalysisContext.cpp",
     "DataflowEnvironment.cpp",
-    "SourceLocationsLattice.cpp",
+    "SourceLocations.cpp",
     "Transfer.cpp",
     "TypeErasedDataflowAnalysis.cpp",
     "WatchedLiteralsSolver.cpp",
Index: clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -11,7 +11,7 @@
 #include "TestingSupport.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
+#include "clang/Analysis/FlowSensitive/SourceLocations.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringExtras.h"
@@ -26,9 +26,6 @@
 using namespace dataflow;
 using namespace test;
 
-using ::testing::Pair;
-using ::testing::UnorderedElementsAre;
-
 // FIXME: Move header definitions in separate file(s).
 static constexpr char CSDtdDefHeader[] = R"(
 #ifndef CSTDDEF_H
@@ -1181,10 +1178,9 @@
 )";
 
 /// Converts `L` to string.
-static std::string ConvertToString(const SourceLocationsLattice &L,
+static std::string ConvertToString(const SourceLocations &L,
                                    const ASTContext &Ctx) {
-  return L.getSourceLocations().empty() ? "safe"
-                                        : "unsafe: " + DebugString(L, Ctx);
+  return L.empty() ? "safe" : "unsafe: " + DebugString(L, Ctx);
 }
 
 /// Replaces all occurrences of `Pattern` in `S` with `Replacement`.
@@ -1253,19 +1249,11 @@
                        /*IgnoreSmartPointerDereference=*/true});
         },
         [&MatchesLatticeChecks](
-            llvm::ArrayRef<std::pair<
-                std::string, DataflowAnalysisState<SourceLocationsLattice>>>
-                CheckToLatticeMap,
-            ASTContext &Ctx) {
-          // FIXME: Consider using a matcher instead of translating
-          // `CheckToLatticeMap` to `CheckToStringifiedLatticeMap`.
-          std::vector<std::pair<std::string, std::string>>
-              CheckToStringifiedLatticeMap;
-          for (const auto &E : CheckToLatticeMap) {
-            CheckToStringifiedLatticeMap.emplace_back(
-                E.first, ConvertToString(E.second.Lattice, Ctx));
-          }
-          EXPECT_THAT(CheckToStringifiedLatticeMap, MatchesLatticeChecks);
+            llvm::ArrayRef<
+                std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
+            SourceLocations &Locs, ASTContext &Ctx) {
+          std::string StringifiedDiags = ConvertToString(Locs, Ctx);
+          EXPECT_THAT(StringifiedDiags, MatchesLatticeChecks);
         },
         {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents);
     if (Error)
@@ -1289,7 +1277,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) {
@@ -1302,7 +1290,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7")));
+      "unsafe: input.cc:5:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -1313,7 +1301,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7")));
+      "unsafe: input.cc:5:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) {
@@ -1326,7 +1314,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8")));
+      "unsafe: input.cc:5:8");
 
   ExpectLatticeChecksFor(
       R"(
@@ -1337,7 +1325,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8")));
+      "unsafe: input.cc:5:8");
 }
 
 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) {
@@ -1354,7 +1342,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7")));
+      "unsafe: input.cc:9:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -1369,7 +1357,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7")));
+      "unsafe: input.cc:9:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, HasValueCheck) {
@@ -1383,7 +1371,7 @@
       }
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) {
@@ -1397,7 +1385,7 @@
       }
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) {
@@ -1411,7 +1399,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7")));
+      "unsafe: input.cc:5:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -1422,7 +1410,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7")));
+      "unsafe: input.cc:5:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) {
@@ -1436,7 +1424,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7")));
+      "unsafe: input.cc:6:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) {
@@ -1450,7 +1438,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7")));
+      "unsafe: input.cc:6:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) {
@@ -1463,7 +1451,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1476,7 +1464,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1491,7 +1479,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1506,7 +1494,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, ValueConstructor) {
@@ -1519,7 +1507,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1530,7 +1518,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
 
@@ -1540,7 +1528,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1555,7 +1543,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1572,7 +1560,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1587,7 +1575,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                        "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) {
@@ -1607,7 +1595,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7")));
+      "unsafe: input.cc:12:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -1625,7 +1613,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7")));
+      "unsafe: input.cc:12:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -1644,7 +1632,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:13:7")));
+      "unsafe: input.cc:13:7");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1662,7 +1650,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1680,7 +1668,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, MakeOptional) {
@@ -1693,7 +1681,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1708,7 +1696,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1724,7 +1712,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, ValueOr) {
@@ -1738,7 +1726,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, ValueOrComparison) {
@@ -1757,8 +1745,7 @@
       }
     }
   )code",
-      UnorderedElementsAre(Pair("check-ptrs-1", "safe"),
-                           Pair("check-ptrs-2", "unsafe: input.cc:9:9")));
+      "unsafe: input.cc:9:9");
 
   // Integers.
   ExpectLatticeChecksFor(
@@ -1775,8 +1762,7 @@
       }
     }
   )code",
-      UnorderedElementsAre(Pair("check-ints-1", "safe"),
-                           Pair("check-ints-2", "unsafe: input.cc:9:9")));
+      "unsafe: input.cc:9:9");
 
   // Strings.
   ExpectLatticeChecksFor(
@@ -1793,8 +1779,7 @@
       }
     }
   )code",
-      UnorderedElementsAre(Pair("check-strings-1", "safe"),
-                           Pair("check-strings-2", "unsafe: input.cc:9:9")));
+      "unsafe: input.cc:9:9");
 
   ExpectLatticeChecksFor(
       R"code(
@@ -1810,9 +1795,7 @@
       }
     }
   )code",
-      UnorderedElementsAre(
-          Pair("check-strings-neq-1", "safe"),
-          Pair("check-strings-neq-2", "unsafe: input.cc:9:9")));
+      "unsafe: input.cc:9:9");
 
   // Pointer-to-optional.
   //
@@ -1833,8 +1816,7 @@
       }
     }
   )code",
-      UnorderedElementsAre(Pair("check-pto-1", "safe"),
-                           Pair("check-pto-2", "unsafe: input.cc:10:9")));
+      "unsafe: input.cc:10:9");
 }
 
 TEST_P(UncheckedOptionalAccessTest, Emplace) {
@@ -1848,7 +1830,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1859,7 +1841,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   // FIXME: Add tests that call `emplace` in conditional branches:
   //  ExpectLatticeChecksFor(
@@ -1879,8 +1861,7 @@
   //      }
   //    }
   //  )",
-  //      UnorderedElementsAre(Pair("check-1", "safe"),
-  //                           Pair("check-2", "unsafe: input.cc:12:9")));
+  //      "unsafe: input.cc:12:9");
 }
 
 TEST_P(UncheckedOptionalAccessTest, Reset) {
@@ -1895,7 +1876,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7")));
+      "unsafe: input.cc:7:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -1909,7 +1890,7 @@
       }
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:9")));
+      "unsafe: input.cc:7:9");
 
   // FIXME: Add tests that call `reset` in conditional branches:
   //  ExpectLatticeChecksFor(
@@ -1930,8 +1911,7 @@
   //      }
   //    }
   //  )",
-  //      UnorderedElementsAre(Pair("check-1", "unsafe: input.cc:10:9"),
-  //                           Pair("check-2", "safe")));
+  //      "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, ValueAssignment) {
@@ -1947,7 +1927,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1961,7 +1941,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1977,7 +1957,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -1992,7 +1972,7 @@
       /*[[check]]*/
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) {
@@ -2014,7 +1994,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "safe")));
+      "safe");
 
   ExpectLatticeChecksFor(
       R"(
@@ -2036,7 +2016,7 @@
       }
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:15:9")));
+      "unsafe: input.cc:15:9");
 
   ExpectLatticeChecksFor(
       R"(
@@ -2056,7 +2036,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "safe")));
+      "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) {
@@ -2071,7 +2051,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7")));
+      "unsafe: input.cc:7:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -2083,7 +2063,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7")));
+      "unsafe: input.cc:6:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, OptionalSwap) {
@@ -2104,8 +2084,7 @@
       /*[[check-2]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check-1", "safe"),
-                           Pair("check-2", "unsafe: input.cc:13:7")));
+      "unsafe: input.cc:13:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -2124,8 +2103,7 @@
       /*[[check-4]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check-3", "safe"),
-                           Pair("check-4", "unsafe: input.cc:13:7")));
+      "unsafe: input.cc:13:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, StdSwap) {
@@ -2146,8 +2124,7 @@
       /*[[check-2]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check-1", "safe"),
-                           Pair("check-2", "unsafe: input.cc:13:7")));
+      "unsafe: input.cc:13:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -2166,8 +2143,7 @@
       /*[[check-4]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check-3", "safe"),
-                           Pair("check-4", "unsafe: input.cc:13:7")));
+      "unsafe: input.cc:13:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) {
@@ -2195,7 +2171,7 @@
       /*[[check-2]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check-1", "safe"), Pair("check-2", "safe")));
+      "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) {
@@ -2212,7 +2188,7 @@
       /*[[check-1]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check-1", "unsafe: input.cc:9:7")));
+      "unsafe: input.cc:9:7");
   ExpectLatticeChecksFor(
       R"(
     #include "unchecked_optional_access_test.h"
@@ -2226,7 +2202,7 @@
       /*[[check-2]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check-2", "unsafe: input.cc:9:7")));
+      "unsafe: input.cc:9:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -2242,7 +2218,7 @@
       /*[[check-3]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:10:7")));
+      "unsafe: input.cc:10:7");
 
   ExpectLatticeChecksFor(
       R"(
@@ -2258,7 +2234,7 @@
       /*[[check-4]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:10:7")));
+      "unsafe: input.cc:10:7");
 }
 
 // Verifies that the model sees through aliases.
@@ -2275,7 +2251,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:8:7")));
+      "unsafe: input.cc:8:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) {
@@ -2295,7 +2271,7 @@
       }
     }
   )",
-      UnorderedElementsAre(Pair("access", "safe")));
+      "safe");
 
   // Mutation is supported for nested values.
   ExpectLatticeChecksFor(
@@ -2312,7 +2288,7 @@
       }
     }
   )",
-      UnorderedElementsAre(Pair("reset", "unsafe: input.cc:9:9")));
+      "unsafe: input.cc:9:9");
 }
 
 // Tests that structs can be nested. We use an optional field because its easy
@@ -2333,7 +2309,7 @@
       }
     }
   )",
-      UnorderedElementsAre(Pair("access", "safe")));
+      "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) {
@@ -2363,7 +2339,7 @@
       /*[[merge]]*/
     }
   )",
-      UnorderedElementsAre(Pair("merge", "unsafe: input.cc:19:7")));
+      "unsafe: input.cc:19:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) {
@@ -2383,7 +2359,7 @@
       /*[[check]]*/
     }
   )",
-      UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7")));
+      "unsafe: input.cc:12:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) {
@@ -2399,7 +2375,7 @@
       }
     }
   )code",
-                         UnorderedElementsAre(Pair("check-1", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"code(
     #include "unchecked_optional_access_test.h"
@@ -2412,7 +2388,7 @@
       }
     }
   )code",
-                         UnorderedElementsAre(Pair("check-2", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(
       R"code(
@@ -2426,7 +2402,7 @@
       }
     }
   )code",
-      UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:7:9")));
+      "unsafe: input.cc:7:9");
 
   ExpectLatticeChecksFor(R"code(
     #include "unchecked_optional_access_test.h"
@@ -2440,7 +2416,7 @@
       }
     }
   )code",
-                         UnorderedElementsAre(Pair("check-4", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -2454,7 +2430,7 @@
       }
     }
   )",
-                         UnorderedElementsAre(Pair("check-5", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -2468,7 +2444,7 @@
       }
     }
   )",
-                         UnorderedElementsAre(Pair("check-6", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -2487,7 +2463,7 @@
       }
     }
   )",
-                         UnorderedElementsAre(Pair("check", "safe")));
+                         "safe");
 
   // FIXME: Add support for operator==.
   // ExpectLatticeChecksFor(R"(
@@ -2502,7 +2478,7 @@
   //     }
   //   }
   // )",
-  //                     UnorderedElementsAre(Pair("check-7", "safe")));
+  //                     "safe");
 }
 
 TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) {
@@ -2526,8 +2502,7 @@
       }
     }
   )code",
-      UnorderedElementsAre(Pair("check-1", "safe"),
-                           Pair("check-2", "unsafe: input.cc:15:9")));
+      "unsafe: input.cc:15:9");
 
   ExpectLatticeChecksFor(R"code(
     #include "unchecked_optional_access_test.h"
@@ -2545,7 +2520,7 @@
       /*[[check-3]]*/
     }
   )code",
-                         UnorderedElementsAre(Pair("check-3", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(
       R"code(
@@ -2563,7 +2538,7 @@
       /*[[check-4]]*/
     }
   )code",
-      UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:12:7")));
+      "unsafe: input.cc:12:7");
 
   ExpectLatticeChecksFor(
       R"code(
@@ -2580,7 +2555,7 @@
       /*[[check-5]]*/
     }
   )code",
-      UnorderedElementsAre(Pair("check-5", "safe")));
+      "safe");
 
   ExpectLatticeChecksFor(
       R"code(
@@ -2597,7 +2572,7 @@
       /*[[check-6]]*/
     }
   )code",
-      UnorderedElementsAre(Pair("check-6", "unsafe: input.cc:11:7")));
+      "unsafe: input.cc:11:7");
 }
 
 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoop) {
@@ -2612,7 +2587,7 @@
       }
     }
   )",
-                         UnorderedElementsAre(Pair("check-1", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(R"(
     #include "unchecked_optional_access_test.h"
@@ -2628,7 +2603,7 @@
       }
     }
   )",
-                         UnorderedElementsAre(Pair("check-2", "safe")));
+                         "safe");
 
   ExpectLatticeChecksFor(
       R"(
@@ -2644,7 +2619,7 @@
       }
     }
   )",
-      UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:7:9")));
+      "unsafe: input.cc:7:9");
 
   ExpectLatticeChecksFor(
       R"(
@@ -2661,7 +2636,7 @@
       }
     }
   )",
-      UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:7:9")));
+      "unsafe: input.cc:7:9");
 }
 
 // FIXME: Add support for:
Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -203,6 +203,7 @@
                 llvm::ArrayRef<std::pair<
                     std::string, DataflowAnalysisState<FunctionCallLattice>>>
                     Results,
+                NoopDiags &,
                 ASTContext &) { EXPECT_THAT(Results, Expectations); },
             {"-fsyntax-only", "-std=c++17"}, FilesContents),
         llvm::Succeeded());
@@ -394,7 +395,7 @@
                 llvm::ArrayRef<
                     std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
                     Results,
-                ASTContext &ASTCtx) { Match(Results, ASTCtx); },
+                NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); },
             {"-fsyntax-only", "-std=c++17"}),
         llvm::Succeeded());
   }
@@ -549,7 +550,7 @@
                 llvm::ArrayRef<
                     std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
                     Results,
-                ASTContext &ASTCtx) { Match(Results, ASTCtx); },
+                NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); },
             {"-fsyntax-only", "-std=c++17"}, FilesContents),
         llvm::Succeeded());
   }
@@ -725,7 +726,7 @@
                 llvm::ArrayRef<
                     std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
                     Results,
-                ASTContext &ASTCtx) { Match(Results, ASTCtx); },
+                NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); },
             {"-fsyntax-only", "-std=c++17"}),
         llvm::Succeeded());
   }
Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -54,7 +54,7 @@
                 llvm::ArrayRef<
                     std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
                     Results,
-                ASTContext &ASTCtx) { Match(Results, ASTCtx); },
+                NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); },
             {"-fsyntax-only", "-fno-delayed-template-parsing",
              "-std=" +
                  std::string(
@@ -1325,7 +1325,7 @@
           [](llvm::ArrayRef<
                  std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
                  Results,
-             ASTContext &ASTCtx) {
+             NoopDiags &, ASTContext &ASTCtx) {
             // Regression test to verify that base-class initializers do not
             // trigger an assertion. If we add support for such initializers in
             // the future, we can expand this test to check more specific
Index: clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp
@@ -76,7 +76,7 @@
     llvm::StringRef Code, llvm::StringRef Target,
     std::function<void(llvm::ArrayRef<std::pair<
                            std::string, DataflowAnalysisState<NoopLattice>>>,
-                       ASTContext &)>
+                       NoopDiags &, ASTContext &)>
         Expectations) {
   ASSERT_THAT_ERROR(
       test::checkDataflow<NoopAnalysis>(
@@ -92,10 +92,10 @@
   ::testing::MockFunction<void(
       llvm::ArrayRef<
           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
-      ASTContext &)>
+      NoopDiags &, ASTContext &)>
       Expectations;
 
-  EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
+  EXPECT_CALL(Expectations, Call(IsEmpty(), _, _)).Times(1);
 
   checkDataflow("void target() {}", "target", Expectations.AsStdFunction());
 }
@@ -104,10 +104,10 @@
   ::testing::MockFunction<void(
       llvm::ArrayRef<
           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
-      ASTContext &)>
+      NoopDiags &, ASTContext &)>
       Expectations;
 
-  EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
+  EXPECT_CALL(Expectations, Call(IsEmpty(), _, _)).Times(1);
 
   checkDataflow("void fun() {}", "fun", Expectations.AsStdFunction());
 }
@@ -116,11 +116,11 @@
   ::testing::MockFunction<void(
       llvm::ArrayRef<
           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
-      ASTContext &)>
+      NoopDiags &, ASTContext &)>
       Expectations;
 
   EXPECT_CALL(Expectations,
-              Call(UnorderedElementsAre(Pair("program-point", _)), _))
+              Call(UnorderedElementsAre(Pair("program-point", _)), _, _))
       .Times(1);
 
   checkDataflow(R"cc(void target() {
@@ -134,13 +134,13 @@
   ::testing::MockFunction<void(
       llvm::ArrayRef<
           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
-      ASTContext &)>
+      NoopDiags &, ASTContext &)>
       Expectations;
 
   EXPECT_CALL(Expectations,
               Call(UnorderedElementsAre(Pair("program-point-1", _),
                                         Pair("program-point-2", _)),
-                   _))
+                   _, _))
       .Times(1);
 
   checkDataflow(R"cc(void target(bool b) {
Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -63,7 +63,7 @@
                                   llvm::Annotations AnnotatedCode);
 
 // Runs dataflow on the body of the function that matches `func_matcher` in code
-// snippet `code`. Requires: `Analysis` contains a type `Lattice`.
+// snippet `code`. Requires: `Analysis` contains types `Lattice` and `Diags`.
 template <typename AnalysisT>
 llvm::Error checkDataflow(
     llvm::StringRef Code,
@@ -72,7 +72,7 @@
     std::function<void(
         llvm::ArrayRef<std::pair<
             std::string, DataflowAnalysisState<typename AnalysisT::Lattice>>>,
-        ASTContext &)>
+        typename AnalysisT::Diags &, ASTContext &)>
         Expectations,
     ArrayRef<std::string> Args,
     const tooling::FileContentMappings &VirtualMappedFiles = {}) {
@@ -121,8 +121,10 @@
     return MaybeBlockStates.takeError();
   auto &BlockStates = *MaybeBlockStates;
 
+  typename AnalysisT::Diags Diags;
+
   if (BlockStates.empty()) {
-    Expectations({}, Context);
+    Expectations({}, Diags, Context);
     return llvm::Error::success();
   }
 
@@ -136,17 +138,21 @@
 
     transferBlock(
         *CFCtx, BlockStates, *Block, Env, Analysis,
-        [&Results, &Annotations](const clang::CFGStmt &Stmt,
-                                 const TypeErasedDataflowAnalysisState &State) {
-          auto It = Annotations.find(Stmt.getStmt());
-          if (It == Annotations.end())
-            return;
+        [&Results, &Diags, &Annotations,
+         &Analysis](const clang::CFGStmt &Stmt,
+                    const TypeErasedDataflowAnalysisState &State) {
+          auto S = Stmt.getStmt();
           auto *Lattice =
               llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value);
+          Analysis.check(S, Diags, *Lattice, State.Env);
+
+          auto It = Annotations.find(S);
+          if (It == Annotations.end())
+            return;
           Results.emplace_back(It->second, StateT{*Lattice, State.Env});
         });
   }
-  Expectations(Results, Context);
+  Expectations(Results, Diags, Context);
   return llvm::Error::success();
 }
 
@@ -159,7 +165,7 @@
     std::function<void(
         llvm::ArrayRef<std::pair<
             std::string, DataflowAnalysisState<typename AnalysisT::Lattice>>>,
-        ASTContext &)>
+        typename AnalysisT::Diags &, ASTContext &)>
         Expectations,
     ArrayRef<std::string> Args,
     const tooling::FileContentMappings &VirtualMappedFiles = {}) {
Index: clang/unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//===- unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp ----===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
-
-#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
-#include "clang/Basic/SourceLocation.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace clang {
-namespace dataflow {
-namespace {
-
-TEST(SourceLocationsLatticeTest, Comparison) {
-  const SourceLocationsLattice Bottom;
-  const SourceLocationsLattice NonBottom(
-      {SourceLocation::getFromRawEncoding(0)});
-
-  EXPECT_TRUE(Bottom == Bottom);
-  EXPECT_FALSE(Bottom == NonBottom);
-  EXPECT_FALSE(NonBottom == Bottom);
-  EXPECT_TRUE(NonBottom == NonBottom);
-
-  EXPECT_FALSE(Bottom != Bottom);
-  EXPECT_TRUE(Bottom != NonBottom);
-  EXPECT_TRUE(NonBottom != Bottom);
-  EXPECT_FALSE(NonBottom != NonBottom);
-}
-
-TEST(SourceLocationsLatticeTest, Join) {
-  const SourceLocationsLattice Bottom;
-  const SourceLocationsLattice NonBottom(
-      {SourceLocation::getFromRawEncoding(0)});
-  {
-    SourceLocationsLattice LHS = Bottom;
-    const SourceLocationsLattice RHS = Bottom;
-    EXPECT_EQ(LHS.join(RHS), LatticeJoinEffect::Unchanged);
-    EXPECT_EQ(LHS, Bottom);
-  }
-  {
-    SourceLocationsLattice LHS = NonBottom;
-    const SourceLocationsLattice RHS = Bottom;
-    EXPECT_EQ(LHS.join(RHS), LatticeJoinEffect::Unchanged);
-    EXPECT_EQ(LHS, NonBottom);
-  }
-  {
-    SourceLocationsLattice LHS = Bottom;
-    const SourceLocationsLattice RHS = NonBottom;
-    EXPECT_EQ(LHS.join(RHS), LatticeJoinEffect::Changed);
-    EXPECT_EQ(LHS, NonBottom);
-  }
-  {
-    SourceLocationsLattice LHS = NonBottom;
-    const SourceLocationsLattice RHS = NonBottom;
-    EXPECT_EQ(LHS.join(RHS), LatticeJoinEffect::Unchanged);
-    EXPECT_EQ(LHS, NonBottom);
-  }
-}
-
-} // namespace
-} // namespace dataflow
-} // namespace clang
Index: clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
@@ -201,6 +201,7 @@
                     std::string, DataflowAnalysisState<
                                      ConstantPropagationAnalysis::Lattice>>>
                     Results,
+                ConstantPropagationAnalysis::Diags &,
                 ASTContext &) { EXPECT_THAT(Results, Expectations); },
             {"-fsyntax-only", "-std=c++17"}),
         llvm::Succeeded());
Index: clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h
===================================================================
--- clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h
+++ clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h
@@ -18,25 +18,11 @@
 #include "clang/AST/Stmt.h"
 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
-#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
-#include <ostream>
+#include "clang/Analysis/FlowSensitive/NoopLattice.h"
 
 namespace clang {
 namespace dataflow {
 
-class NoopLattice {
-public:
-  bool operator==(const NoopLattice &) const { return true; }
-
-  LatticeJoinEffect join(const NoopLattice &) {
-    return LatticeJoinEffect::Unchanged;
-  }
-};
-
-inline std::ostream &operator<<(std::ostream &OS, const NoopLattice &) {
-  return OS << "noop";
-}
-
 class NoopAnalysis : public DataflowAnalysis<NoopAnalysis, NoopLattice> {
 public:
   /// `ApplyBuiltinTransfer` controls whether to run the built-in transfer
Index: clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
@@ -223,6 +223,7 @@
                     std::string, DataflowAnalysisState<
                                      ConstantPropagationAnalysis::Lattice>>>
                     Results,
+                ConstantPropagationAnalysis::Diags &,
                 ASTContext &) { EXPECT_THAT(Results, Expectations); },
             {"-fsyntax-only", "-std=c++17"}),
         llvm::Succeeded());
Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -135,6 +135,7 @@
                 llvm::ArrayRef<std::pair<
                     std::string, DataflowAnalysisState<TestAnalysis::Lattice>>>
                     Results,
+                TestAnalysis::Diags &,
                 ASTContext &) { EXPECT_THAT(Results, Expectations); },
             {"-fsyntax-only", "-std=c++17"}),
         llvm::Succeeded());
Index: clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
@@ -145,7 +145,7 @@
                 llvm::ArrayRef<
                     std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
                     Results,
-                ASTContext &ASTCtx) { Match(Results, ASTCtx); },
+                NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); },
             {"-fsyntax-only", "-fno-delayed-template-parsing", "-std=c++17"},
             FileContents),
         llvm::Succeeded());
Index: clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
===================================================================
--- clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -11,7 +11,6 @@
   MatchSwitchTest.cpp
   MultiVarConstantPropagationTest.cpp
   SingleVarConstantPropagationTest.cpp
-  SourceLocationsLatticeTest.cpp
   TestingSupport.cpp
   TestingSupportTest.cpp
   TransferTest.cpp
Index: clang/lib/Analysis/FlowSensitive/SourceLocations.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/SourceLocations.cpp
+++ clang/lib/Analysis/FlowSensitive/SourceLocations.cpp
@@ -1,4 +1,4 @@
-//===- SourceLocationsLattice.cpp -----------------------------------------===//
+//===- SourceLocations.cpp ------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,13 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 //
-//  This file implements a lattice that collects source locations of interest.
+//  This file implements a function to stringify source locations of interest.
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
+#include "clang/Analysis/FlowSensitive/SourceLocations.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
@@ -22,22 +21,14 @@
 namespace clang {
 namespace dataflow {
 
-LatticeJoinEffect
-SourceLocationsLattice::join(const SourceLocationsLattice &Other) {
-  auto SizeBefore = Locs.size();
-  Locs.insert(Other.Locs.begin(), Other.Locs.end());
-  return SizeBefore == Locs.size() ? LatticeJoinEffect::Unchanged
-                                   : LatticeJoinEffect::Changed;
-}
-
-std::string DebugString(const SourceLocationsLattice &Lattice,
+std::string DebugString(const SourceLocations &Locs,
                         const ASTContext &Context) {
-  if (Lattice.getSourceLocations().empty())
+  if (Locs.empty())
     return "";
 
   std::vector<std::string> Locations;
-  Locations.reserve(Lattice.getSourceLocations().size());
-  for (const clang::SourceLocation &Loc : Lattice.getSourceLocations()) {
+  Locations.reserve(Locs.size());
+  for (const clang::SourceLocation &Loc : Locs) {
     Locations.push_back(Loc.printToString(Context.getSourceManager()));
   }
   std::sort(Locations.begin(), Locations.end());
Index: clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -20,7 +20,8 @@
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
-#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
+#include "clang/Analysis/FlowSensitive/NoopLattice.h"
+#include "clang/Analysis/FlowSensitive/SourceLocations.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
@@ -33,7 +34,7 @@
 namespace {
 
 using namespace ::clang::ast_matchers;
-using LatticeTransferState = TransferState<SourceLocationsLattice>;
+using LatticeTransferState = TransferState<NoopLattice>;
 
 DeclarationMatcher optionalClass() {
   return classTemplateSpecializationDecl(
@@ -310,18 +311,7 @@
       if (auto *Loc = maybeInitializeOptionalValueMember(
               UnwrapExpr->getType(), *OptionalVal, State.Env))
         State.Env.setStorageLocation(*UnwrapExpr, *Loc);
-
-    auto *Prop = OptionalVal->getProperty("has_value");
-    if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
-      if (State.Env.flowConditionImplies(*HasValueVal))
-        return;
-    }
   }
-
-  // Record that this unwrap is *not* provably safe.
-  // FIXME: include either the name of the optional (if applicable) or a source
-  // range of the access for easier interpretation of the result.
-  State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc());
 }
 
 void transferMakeOptionalCall(const CallExpr *E,
@@ -542,6 +532,23 @@
   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
 }
 
+void checkUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
+                     CheckState<SourceLocations> &State) {
+  if (auto *OptionalVal =
+          State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
+    auto *Prop = OptionalVal->getProperty("has_value");
+    if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
+      if (State.Env.flowConditionImplies(*HasValueVal))
+        return;
+    }
+  }
+
+  // Record that this unwrap is *not* provably safe.
+  // FIXME: include either the name of the optional (if applicable) or a source
+  // range of the access for easier interpretation of the result.
+  State.Diags.insert(ObjectExpr->getBeginLoc());
+}
+
 llvm::Optional<StatementMatcher>
 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
   if (Options.IgnoreSmartPointerDereference)
@@ -552,12 +559,8 @@
   return llvm::None;
 }
 
-auto buildTransferMatchSwitch(
-    const UncheckedOptionalAccessModelOptions &Options) {
-  // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
-  // lot of duplicated work (e.g. string comparisons), consider providing APIs
-  // that avoid it through memoization.
-  auto IgnorableOptional = ignorableOptional(Options);
+auto buildTransferMatchSwitch(StatementMatcher &ValueCall,
+                              StatementMatcher &ValueOperatorCall) {
   return MatchSwitchBuilder<LatticeTransferState>()
       // Attach a symbolic "has_value" state to optional values that we see for
       // the first time.
@@ -593,20 +596,18 @@
 
       // optional::value
       .CaseOf<CXXMemberCallExpr>(
-          isOptionalMemberCallWithName("value", IgnorableOptional),
+          ValueCall,
           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
              LatticeTransferState &State) {
             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
           })
 
       // optional::operator*, optional::operator->
-      .CaseOf<CallExpr>(
-          expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
-                     isOptionalOperatorCallWithName("->", IgnorableOptional))),
-          [](const CallExpr *E, const MatchFinder::MatchResult &,
-             LatticeTransferState &State) {
-            transferUnwrapCall(E, E->getArg(0), State);
-          })
+      .CaseOf<CallExpr>(ValueOperatorCall,
+                        [](const CallExpr *E, const MatchFinder::MatchResult &,
+                           LatticeTransferState &State) {
+                          transferUnwrapCall(E, E->getArg(0), State);
+                        })
 
       // optional::has_value
       .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"),
@@ -654,6 +655,39 @@
       .Build();
 }
 
+auto buildCheckMatchSwitch(StatementMatcher &ValueCall,
+                           StatementMatcher &ValueOperatorCall) {
+  return MatchSwitchBuilder<CheckState<SourceLocations>>()
+      // optional::value
+      .CaseOf<CXXMemberCallExpr>(
+          ValueCall,
+          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
+             CheckState<SourceLocations> &State) {
+            checkUnwrapCall(E, E->getImplicitObjectArgument(), State);
+          })
+
+      // optional::operator*, optional::operator->
+      .CaseOf<CallExpr>(ValueOperatorCall,
+                        [](const CallExpr *E, const MatchFinder::MatchResult &,
+                           CheckState<SourceLocations> &State) {
+                          checkUnwrapCall(E, E->getArg(0), State);
+                        })
+      .Build();
+}
+
+auto buildMatchSwitches(const UncheckedOptionalAccessModelOptions &Options) {
+  // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
+  // lot of duplicated work (e.g. string comparisons), consider providing APIs
+  // that avoid it through memoization.
+  auto IgnorableOptional = ignorableOptional(Options);
+  auto ValueCall = isOptionalMemberCallWithName("value", IgnorableOptional);
+  auto ValueOperatorCall =
+      expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
+                 isOptionalOperatorCallWithName("->", IgnorableOptional)));
+  return std::make_pair(buildTransferMatchSwitch(ValueCall, ValueOperatorCall),
+                        buildCheckMatchSwitch(ValueCall, ValueOperatorCall));
+}
+
 } // namespace
 
 ast_matchers::DeclarationMatcher
@@ -663,12 +697,20 @@
 
 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(
     ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options)
-    : DataflowAnalysis<UncheckedOptionalAccessModel, SourceLocationsLattice>(
-          Ctx),
-      TransferMatchSwitch(buildTransferMatchSwitch(Options)) {}
+    : UncheckedOptionalAccessModel(Ctx, buildMatchSwitches(Options)) {}
 
-void UncheckedOptionalAccessModel::transfer(const Stmt *S,
-                                            SourceLocationsLattice &L,
+// We use a delegating constructor to build both `MatchSwitch`es at once while
+// still avoiding unnecessary work by using initializer lists.
+UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(
+    ASTContext &Ctx, std::pair<MatchSwitch<TransferState<NoopLattice>>,
+                               MatchSwitch<CheckState<SourceLocations>>>
+                         MatchSwitches)
+    : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice,
+                       SourceLocations>(Ctx),
+      TransferMatchSwitch(MatchSwitches.first),
+      CheckMatchSwitch(MatchSwitches.second) {}
+
+void UncheckedOptionalAccessModel::transfer(const Stmt *S, NoopLattice &L,
                                             Environment &Env) {
   LatticeTransferState State(L, Env);
   TransferMatchSwitch(*S, getASTContext(), State);
@@ -700,5 +742,12 @@
   return true;
 }
 
+void UncheckedOptionalAccessModel::check(const Stmt *S, SourceLocations &Locs,
+                                         const NoopLattice &,
+                                         const Environment &Env) {
+  CheckState<SourceLocations> State(Locs, Env);
+  CheckMatchSwitch(*S, getASTContext(), State);
+}
+
 } // namespace dataflow
 } // namespace clang
Index: clang/lib/Analysis/FlowSensitive/CMakeLists.txt
===================================================================
--- clang/lib/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/lib/Analysis/FlowSensitive/CMakeLists.txt
@@ -2,7 +2,7 @@
   ControlFlowContext.cpp
   DataflowAnalysisContext.cpp
   DataflowEnvironment.cpp
-  SourceLocationsLattice.cpp
+  SourceLocations.cpp
   Transfer.cpp
   TypeErasedDataflowAnalysis.cpp
   WatchedLiteralsSolver.cpp
Index: clang/include/clang/Analysis/FlowSensitive/SourceLocationsLattice.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/SourceLocationsLattice.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//===-- SourceLocationsLattice.h --------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-//  This file defines a lattice that collects source locations of interest.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_LATTICE_H
-#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_LATTICE_H
-
-#include "clang/AST/ASTContext.h"
-#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
-#include "clang/Basic/SourceLocation.h"
-#include "llvm/ADT/DenseSet.h"
-#include <string>
-#include <utility>
-
-namespace clang {
-namespace dataflow {
-
-/// Lattice for dataflow analysis that keeps track of a set of source locations.
-///
-/// Bottom is the empty set, join is set union, and equality is set equality.
-///
-/// FIXME: Generalize into a (templated) PowerSetLattice.
-class SourceLocationsLattice {
-public:
-  SourceLocationsLattice() = default;
-
-  explicit SourceLocationsLattice(llvm::DenseSet<SourceLocation> Locs)
-      : Locs(std::move(Locs)) {}
-
-  bool operator==(const SourceLocationsLattice &Other) const {
-    return Locs == Other.Locs;
-  }
-
-  bool operator!=(const SourceLocationsLattice &Other) const {
-    return !(*this == Other);
-  }
-
-  LatticeJoinEffect join(const SourceLocationsLattice &Other);
-
-  llvm::DenseSet<SourceLocation> &getSourceLocations() { return Locs; }
-
-  const llvm::DenseSet<SourceLocation> &getSourceLocations() const {
-    return Locs;
-  }
-
-private:
-  llvm::DenseSet<SourceLocation> Locs;
-};
-
-/// Returns a string that represents the source locations of the lattice.
-std::string DebugString(const SourceLocationsLattice &Lattice,
-                        const ASTContext &Context);
-
-} // namespace dataflow
-} // namespace clang
-
-#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_LATTICE_H
Index: clang/include/clang/Analysis/FlowSensitive/SourceLocations.h
===================================================================
--- /dev/null
+++ clang/include/clang/Analysis/FlowSensitive/SourceLocations.h
@@ -0,0 +1,35 @@
+//===-- SourceLocations.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines a type alias for collecting source locations of interest.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_H
+#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/DenseSet.h"
+#include <string>
+#include <utility>
+
+namespace clang {
+namespace dataflow {
+
+/// Set of source locations.
+using SourceLocations = llvm::DenseSet<SourceLocation>;
+
+/// Returns a string that represents the source locations of the lattice.
+std::string DebugString(const SourceLocations &Locs, const ASTContext &Context);
+
+} // namespace dataflow
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_H
Index: clang/include/clang/Analysis/FlowSensitive/NoopLattice.h
===================================================================
--- /dev/null
+++ clang/include/clang/Analysis/FlowSensitive/NoopLattice.h
@@ -0,0 +1,41 @@
+//===-- NoopLattice.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the lattice with exactly one element.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOP_LATTICE_H
+#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOP_LATTICE_H
+
+#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
+#include <ostream>
+
+namespace clang {
+namespace dataflow {
+
+/// Trivial lattice for dataflow analysis with exactly one element.
+///
+/// Useful for analyses that only need the Environment and nothing more.
+class NoopLattice {
+public:
+  bool operator==(const NoopLattice &Other) const { return true; }
+
+  LatticeJoinEffect join(const NoopLattice &Other) {
+    return LatticeJoinEffect::Unchanged;
+  }
+};
+
+inline std::ostream &operator<<(std::ostream &OS, const NoopLattice &) {
+  return OS << "noop";
+}
+
+} // namespace dataflow
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOP_LATTICE_H
Index: clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h
+++ clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h
@@ -19,7 +19,10 @@
 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
-#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
+#include "clang/Analysis/FlowSensitive/NoopLattice.h"
+#include "clang/Analysis/FlowSensitive/SourceLocations.h"
+#include "clang/Basic/SourceLocation.h"
+#include <utility>
 
 namespace clang {
 namespace dataflow {
@@ -36,15 +39,12 @@
   bool IgnoreSmartPointerDereference = false;
 };
 
-/// Dataflow analysis that discovers unsafe accesses of optional values and
-/// adds the respective source locations to the lattice.
+/// Dataflow analysis that models whether optionals hold values or not.
 ///
 /// Models the `std::optional`, `absl::optional`, and `base::Optional` types.
-///
-/// FIXME: Consider separating the models from the unchecked access analysis.
 class UncheckedOptionalAccessModel
-    : public DataflowAnalysis<UncheckedOptionalAccessModel,
-                              SourceLocationsLattice> {
+    : public DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice,
+                              SourceLocations> {
 public:
   UncheckedOptionalAccessModel(
       ASTContext &AstContext, UncheckedOptionalAccessModelOptions Options = {});
@@ -52,12 +52,14 @@
   /// Returns a matcher for the optional classes covered by this model.
   static ast_matchers::DeclarationMatcher optionalClassDecl();
 
-  static SourceLocationsLattice initialElement() {
-    return SourceLocationsLattice();
-  }
+  static NoopLattice initialElement() { return {}; }
+
+  void transfer(const Stmt *Stmt, NoopLattice &State, Environment &Env);
 
-  void transfer(const Stmt *Stmt, SourceLocationsLattice &State,
-                Environment &Env);
+  /// Discovers unsafe accesses of optional values and adds the respective
+  /// source locations to a set.
+  void check(const Stmt *Stmt, SourceLocations &Locs, const NoopLattice &,
+             const Environment &Env) override;
 
   bool compareEquivalent(QualType Type, const Value &Val1,
                          const Environment &Env1, const Value &Val2,
@@ -68,7 +70,14 @@
              Environment &MergedEnv) override;
 
 private:
-  MatchSwitch<TransferState<SourceLocationsLattice>> TransferMatchSwitch;
+  UncheckedOptionalAccessModel(
+      ASTContext &AstContext,
+      std::pair<MatchSwitch<TransferState<NoopLattice>>,
+                MatchSwitch<CheckState<SourceLocations>>>
+          MatchSwitches);
+
+  MatchSwitch<TransferState<NoopLattice>> TransferMatchSwitch;
+  MatchSwitch<CheckState<SourceLocations>> CheckMatchSwitch;
 };
 
 } // namespace dataflow
Index: clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
+++ clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
@@ -44,6 +44,15 @@
   Environment &Env;
 };
 
+/// A common form of state shared between the cases of a check function.
+template <typename DiagsT> struct CheckState {
+  CheckState(DiagsT &Diags, const Environment &Env) : Diags(Diags), Env(Env) {}
+
+  /// Current set of diagnostics.
+  DiagsT &Diags;
+  const Environment &Env;
+};
+
 /// Matches against `Stmt` and, based on its structure, dispatches to an
 /// appropriate handler.
 template <typename State>
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -32,6 +32,8 @@
 namespace clang {
 namespace dataflow {
 
+using NoopDiags = std::tuple<>;
+
 /// Base class template for dataflow analyses built on a single lattice type.
 ///
 /// Requirements:
@@ -47,6 +49,8 @@
 ///   * `bool merge(QualType, const Value &, const Value &, Value &,
 ///     Environment &)` -  joins distinct values. This could be a strict
 ///     lattice join or a more general widening operation.
+///   * `void check(const Stmt *, DiagsT &, const LatticeT &, const Environment
+///     &)` - collects diagnostics for a given statement and analysis result.
 ///
 ///  `LatticeT` is a bounded join-semilattice that is used by `Derived` and must
 ///  provide the following public members:
@@ -56,11 +60,15 @@
 ///     made to it;
 ///   * `bool operator==(const LatticeT &) const` - returns true if and only if
 ///     the object is equal to the argument.
-template <typename Derived, typename LatticeT>
+///
+/// `DiagsT` must have a default constructor.
+template <typename Derived, typename LatticeT, typename DiagsT = NoopDiags>
 class DataflowAnalysis : public TypeErasedDataflowAnalysis {
 public:
   /// Bounded join-semilattice that is used in the analysis.
   using Lattice = LatticeT;
+  /// Diagnostics set that is used after the analysis.
+  using Diags = DiagsT;
 
   explicit DataflowAnalysis(ASTContext &Context) : Context(Context) {}
   explicit DataflowAnalysis(ASTContext &Context, bool ApplyBuiltinTransfer)
@@ -92,6 +100,9 @@
     static_cast<Derived *>(this)->transfer(Stmt, L, Env);
   }
 
+  virtual void check(const Stmt *Stmt, Diags &E, const Lattice &L,
+                     const Environment &Env) {}
+
 private:
   ASTContext &Context;
 };
Index: clang/docs/tools/clang-formatted-files.txt
===================================================================
--- clang/docs/tools/clang-formatted-files.txt
+++ clang/docs/tools/clang-formatted-files.txt
@@ -131,8 +131,9 @@
 clang/include/clang/Analysis/FlowSensitive/DataflowWorklist.h
 clang/include/clang/Analysis/FlowSensitive/MapLattice.h
 clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
+clang/include/clang/Analysis/FlowSensitive/NoopLattice.h
 clang/include/clang/Analysis/FlowSensitive/Solver.h
-clang/include/clang/Analysis/FlowSensitive/SourceLocationsLattice.h
+clang/include/clang/Analysis/FlowSensitive/SourceLocations.h
 clang/include/clang/Analysis/FlowSensitive/StorageLocation.h
 clang/include/clang/Analysis/FlowSensitive/Transfer.h
 clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -309,7 +310,7 @@
 clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
 clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
 clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
-clang/lib/Analysis/FlowSensitive/SourceLocationsLattice.cpp
+clang/lib/Analysis/FlowSensitive/SourceLocations.cpp
 clang/lib/Analysis/FlowSensitive/Transfer.cpp
 clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
 clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
@@ -636,7 +637,6 @@
 clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h
 clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
 clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
-clang/unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp
 clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
 clang/unittests/Analysis/FlowSensitive/TestingSupport.h
 clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp
Index: clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp
@@ -18,7 +18,8 @@
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
-#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
+#include "clang/Analysis/FlowSensitive/NoopLattice.h"
+#include "clang/Analysis/FlowSensitive/SourceLocations.h"
 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/Any.h"
@@ -31,14 +32,16 @@
 namespace tidy {
 namespace bugprone {
 using ast_matchers::MatchFinder;
-using dataflow::SourceLocationsLattice;
+using dataflow::NoopLattice;
+using dataflow::SourceLocations;
+using dataflow::TypeErasedDataflowAnalysisState;
 using dataflow::UncheckedOptionalAccessModel;
 using llvm::Optional;
 
 static constexpr llvm::StringLiteral FuncID("fun");
 
-static Optional<SourceLocationsLattice>
-analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) {
+static Optional<SourceLocations> analyzeFunction(const FunctionDecl &FuncDecl,
+                                                 ASTContext &ASTCtx) {
   using dataflow::ControlFlowContext;
   using dataflow::DataflowAnalysisState;
   using llvm::Expected;
@@ -52,23 +55,29 @@
       std::make_unique<dataflow::WatchedLiteralsSolver>());
   dataflow::Environment Env(AnalysisContext, FuncDecl);
   UncheckedOptionalAccessModel Analysis(ASTCtx);
-  Expected<std::vector<Optional<DataflowAnalysisState<SourceLocationsLattice>>>>
+  Expected<std::vector<Optional<TypeErasedDataflowAnalysisState>>>
       BlockToOutputState =
-          dataflow::runDataflowAnalysis(*Context, Analysis, Env);
+          dataflow::runTypeErasedDataflowAnalysis(*Context, Analysis, Env);
   if (!BlockToOutputState)
     return llvm::None;
-  assert(Context->getCFG().getExit().getBlockID() < BlockToOutputState->size());
+  auto &BlockStates = *BlockToOutputState;
 
-  const Optional<DataflowAnalysisState<SourceLocationsLattice>>
-      &ExitBlockState =
-          (*BlockToOutputState)[Context->getCFG().getExit().getBlockID()];
-  // `runDataflowAnalysis` doesn't guarantee that the exit block is visited;
-  // for example, when it is unreachable.
-  // FIXME: Diagnose violations even when the exit block is unreachable.
-  if (!ExitBlockState.hasValue())
-    return llvm::None;
+  SourceLocations Locs;
+  for (const CFGBlock *Block : Context->getCFG()) {
+    // Skip blocks that were not evaluated.
+    if (!BlockStates[Block->getBlockID()].hasValue())
+      continue;
+
+    transferBlock(
+        *Context, BlockStates, *Block, Env, Analysis,
+        [&Locs, &Analysis](const clang::CFGStmt &Stmt,
+                           const TypeErasedDataflowAnalysisState &State) {
+          auto *Lattice = llvm::any_cast<NoopLattice>(&State.Lattice.Value);
+          Analysis.check(Stmt.getStmt(), Locs, *Lattice, State.Env);
+        });
+  }
 
-  return std::move(ExitBlockState->Lattice);
+  return std::move(Locs);
 }
 
 void UncheckedOptionalAccessCheck::registerMatchers(MatchFinder *Finder) {
@@ -97,9 +106,9 @@
   if (FuncDecl->isTemplated())
     return;
 
-  if (Optional<SourceLocationsLattice> Errors =
+  if (Optional<SourceLocations> Errors =
           analyzeFunction(*FuncDecl, *Result.Context))
-    for (const SourceLocation &Loc : Errors->getSourceLocations())
+    for (const SourceLocation &Loc : *Errors)
       diag(Loc, "unchecked access to optional value");
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to