Skip to content

Commit b93c10f

Browse files
committed
[clang][dataflow] For bugprone-unchecked-optional-access report range
Report the range in diagnostics, in addition to the location in case the range helps disambiguate a little in chained `->` expressions. b->a->f->x = 1; ^~~~~~~ instead of just: b->a->f->x = 1; ^ As a followup we should probably also report the location/range of an `->` if that operator is used. Like: b->a->f->x = 1; ^~
1 parent e4a0fd5 commit b93c10f

File tree

4 files changed

+31
-21
lines changed

4 files changed

+31
-21
lines changed

clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
namespace clang::tidy::bugprone {
2020
using ast_matchers::MatchFinder;
2121
using dataflow::UncheckedOptionalAccessDiagnoser;
22+
using dataflow::UncheckedOptionalAccessDiagnostic;
2223
using dataflow::UncheckedOptionalAccessModel;
2324

2425
static constexpr llvm::StringLiteral FuncID("fun");
@@ -52,14 +53,16 @@ void UncheckedOptionalAccessCheck::check(
5253
UncheckedOptionalAccessDiagnoser Diagnoser(ModelOptions);
5354
// FIXME: Allow user to set the (defaulted) SAT iterations max for
5455
// `diagnoseFunction` with config options.
55-
if (llvm::Expected<llvm::SmallVector<SourceLocation>> Locs =
56-
dataflow::diagnoseFunction<UncheckedOptionalAccessModel,
57-
SourceLocation>(*FuncDecl, *Result.Context,
58-
Diagnoser))
59-
for (const SourceLocation &Loc : *Locs)
60-
diag(Loc, "unchecked access to optional value");
56+
if (llvm::Expected<llvm::SmallVector<UncheckedOptionalAccessDiagnostic>>
57+
Diags = dataflow::diagnoseFunction<UncheckedOptionalAccessModel,
58+
UncheckedOptionalAccessDiagnostic>(
59+
*FuncDecl, *Result.Context, Diagnoser))
60+
for (const UncheckedOptionalAccessDiagnostic &Diag : *Diags) {
61+
diag(Diag.Range.getBegin(), "unchecked access to optional value")
62+
<< Diag.Range;
63+
}
6164
else
62-
llvm::consumeError(Locs.takeError());
65+
llvm::consumeError(Diags.takeError());
6366
}
6467

6568
} // namespace clang::tidy::bugprone

clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h"
2121
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
2222
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
23+
#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
2324
#include "clang/Analysis/FlowSensitive/NoopLattice.h"
2425
#include "clang/Basic/SourceLocation.h"
2526
#include "llvm/ADT/SmallVector.h"
@@ -71,20 +72,26 @@ class UncheckedOptionalAccessModel
7172
TransferMatchSwitch;
7273
};
7374

75+
/// Diagnostic information for an unchecked optional access.
76+
struct UncheckedOptionalAccessDiagnostic {
77+
CharSourceRange Range;
78+
};
79+
7480
class UncheckedOptionalAccessDiagnoser {
7581
public:
7682
UncheckedOptionalAccessDiagnoser(
7783
UncheckedOptionalAccessModelOptions Options = {});
7884

79-
llvm::SmallVector<SourceLocation>
85+
llvm::SmallVector<UncheckedOptionalAccessDiagnostic>
8086
operator()(const CFGElement &Elt, ASTContext &Ctx,
8187
const TransferStateForDiagnostics<UncheckedOptionalAccessLattice>
8288
&State) {
8389
return DiagnoseMatchSwitch(Elt, Ctx, State.Env);
8490
}
8591

8692
private:
87-
CFGMatchSwitch<const Environment, llvm::SmallVector<SourceLocation>>
93+
CFGMatchSwitch<const Environment,
94+
llvm::SmallVector<UncheckedOptionalAccessDiagnostic>>
8895
DiagnoseMatchSwitch;
8996
};
9097

clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,8 +1120,8 @@ auto buildTransferMatchSwitch() {
11201120
.Build();
11211121
}
11221122

1123-
llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
1124-
const Environment &Env) {
1123+
llvm::SmallVector<UncheckedOptionalAccessDiagnostic>
1124+
diagnoseUnwrapCall(const Expr *ObjectExpr, const Environment &Env) {
11251125
if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
11261126
getLocBehindPossiblePointer(*ObjectExpr, Env))) {
11271127
auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
@@ -1132,9 +1132,8 @@ llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
11321132
}
11331133

11341134
// Record that this unwrap is *not* provably safe.
1135-
// FIXME: include either the name of the optional (if applicable) or a source
1136-
// range of the access for easier interpretation of the result.
1137-
return {ObjectExpr->getBeginLoc()};
1135+
auto Range = CharSourceRange::getTokenRange(ObjectExpr->getSourceRange());
1136+
return {UncheckedOptionalAccessDiagnostic{Range}};
11381137
}
11391138

11401139
auto buildDiagnoseMatchSwitch(
@@ -1143,8 +1142,9 @@ auto buildDiagnoseMatchSwitch(
11431142
// lot of duplicated work (e.g. string comparisons), consider providing APIs
11441143
// that avoid it through memoization.
11451144
auto IgnorableOptional = ignorableOptional(Options);
1146-
return CFGMatchSwitchBuilder<const Environment,
1147-
llvm::SmallVector<SourceLocation>>()
1145+
return CFGMatchSwitchBuilder<
1146+
const Environment,
1147+
llvm::SmallVector<UncheckedOptionalAccessDiagnostic>>()
11481148
// optional::value
11491149
.CaseOfCFGStmt<CXXMemberCallExpr>(
11501150
valueCall(IgnorableOptional),

clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,7 +1336,7 @@ class UncheckedOptionalAccessTest
13361336
T Make();
13371337
)");
13381338
UncheckedOptionalAccessModelOptions Options{IgnoreSmartPointerDereference};
1339-
std::vector<SourceLocation> Diagnostics;
1339+
std::vector<UncheckedOptionalAccessDiagnostic> Diagnostics;
13401340
llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>(
13411341
AnalysisInputs<UncheckedOptionalAccessModel>(
13421342
SourceCode, std::move(FuncMatcher),
@@ -1369,17 +1369,17 @@ class UncheckedOptionalAccessTest
13691369
}
13701370
auto &SrcMgr = AO.ASTCtx.getSourceManager();
13711371
llvm::DenseSet<unsigned> DiagnosticLines;
1372-
for (SourceLocation &Loc : Diagnostics) {
1373-
unsigned Line = SrcMgr.getPresumedLineNumber(Loc);
1372+
for (const UncheckedOptionalAccessDiagnostic &Diag : Diagnostics) {
1373+
unsigned Line = SrcMgr.getPresumedLineNumber(Diag.Range.getBegin());
13741374
DiagnosticLines.insert(Line);
13751375
if (!AnnotationLines.contains(Line)) {
13761376
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(
13771377
new DiagnosticOptions());
13781378
TextDiagnostic TD(llvm::errs(), AO.ASTCtx.getLangOpts(),
13791379
DiagOpts.get());
1380-
TD.emitDiagnostic(FullSourceLoc(Loc, SrcMgr),
1380+
TD.emitDiagnostic(FullSourceLoc(Diag.Range.getBegin(), SrcMgr),
13811381
DiagnosticsEngine::Error,
1382-
"unexpected diagnostic", {}, {});
1382+
"unexpected diagnostic", {Diag.Range}, {});
13831383
}
13841384
}
13851385

0 commit comments

Comments
 (0)