1414#include " clang/Basic/SourceLocation.h"
1515#include " clang/Frontend/TextDiagnostic.h"
1616#include " clang/Tooling/Tooling.h"
17+ #include " llvm/ADT/DenseMap.h"
1718#include " llvm/ADT/DenseSet.h"
1819#include " llvm/ADT/STLExtras.h"
1920#include " llvm/Support/Error.h"
@@ -1282,6 +1283,14 @@ static raw_ostream &operator<<(raw_ostream &OS,
12821283class UncheckedOptionalAccessTest
12831284 : public ::testing::TestWithParam<OptionalTypeIdentifier> {
12841285protected:
1286+ // Check that after running the analysis on SourceCode, it produces the
1287+ // expected diagnostics according to [[unsafe]] annotations.
1288+ // - No annotations => no diagnostics.
1289+ // - Given "// [[unsafe]]" annotations on a line, we expect a diagnostic on
1290+ // that line.
1291+ // - Given "// [[unsafe:range_text]]" annotations on a line, we expect a
1292+ // diagnostic on that line, and we expect the diagnostic Range (printed as
1293+ // a string) to match the "range_text".
12851294 void ExpectDiagnosticsFor (std::string SourceCode,
12861295 bool IgnoreSmartPointerDereference = true ) {
12871296 ExpectDiagnosticsFor (SourceCode, ast_matchers::hasName (" target" ),
@@ -1364,8 +1373,14 @@ class UncheckedOptionalAccessTest
13641373 &Annotations,
13651374 const AnalysisOutputs &AO) {
13661375 llvm::DenseSet<unsigned > AnnotationLines;
1367- for (const auto &[Line, _] : Annotations) {
1376+ llvm::DenseMap<unsigned , std::string> AnnotationRangesInLines;
1377+ for (const auto &[Line, AnnotationWithMaybeRange] : Annotations) {
13681378 AnnotationLines.insert (Line);
1379+ auto it = AnnotationWithMaybeRange.find (' :' );
1380+ if (it != std::string::npos) {
1381+ AnnotationRangesInLines[Line] =
1382+ AnnotationWithMaybeRange.substr (it + 1 );
1383+ }
13691384 }
13701385 auto &SrcMgr = AO.ASTCtx .getSourceManager ();
13711386 llvm::DenseSet<unsigned > DiagnosticLines;
@@ -1380,6 +1395,12 @@ class UncheckedOptionalAccessTest
13801395 TD.emitDiagnostic (FullSourceLoc (Diag.Range .getBegin (), SrcMgr),
13811396 DiagnosticsEngine::Error,
13821397 " unexpected diagnostic" , {Diag.Range }, {});
1398+ } else {
1399+ auto it = AnnotationRangesInLines.find (Line);
1400+ if (it != AnnotationRangesInLines.end ()) {
1401+ EXPECT_EQ (Diag.Range .getAsRange ().printToString (SrcMgr),
1402+ it->second );
1403+ }
13831404 }
13841405 }
13851406
@@ -4085,6 +4106,31 @@ TEST_P(UncheckedOptionalAccessTest, ConstPointerRefAccessor) {
40854106 /* IgnoreSmartPointerDereference=*/ false );
40864107}
40874108
4109+ TEST_P (UncheckedOptionalAccessTest, DiagnosticsHaveRanges) {
4110+ ExpectDiagnosticsFor (R"cc(
4111+ #include "unchecked_optional_access_test.h"
4112+
4113+ struct A {
4114+ $ns::$optional<int> fi;
4115+ };
4116+ struct B {
4117+ $ns::$optional<A> fa;
4118+ };
4119+
4120+ void target($ns::$optional<B> opt) {
4121+ opt.value(); // [[unsafe:<input.cc:12:7>]]
4122+ if (opt) {
4123+ opt // [[unsafe:<input.cc:14:9, line:16:13>]]
4124+ ->
4125+ fa.value();
4126+ if (opt->fa) {
4127+ opt->fa->fi.value(); // [[unsafe:<input.cc:18:11, col:20>]]
4128+ }
4129+ }
4130+ }
4131+ )cc" );
4132+ }
4133+
40884134// FIXME: Add support for:
40894135// - constructors (copy, move)
40904136// - assignment operators (default, copy, move)
0 commit comments