Skip to content

Commit 6c42309

Browse files
committed
[LifetimeSafety] Track view types/gsl::Pointer.
1 parent 92a91f7 commit 6c42309

File tree

4 files changed

+55
-26
lines changed

4 files changed

+55
-26
lines changed

clang/lib/Analysis/LifetimeSafety.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,6 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
445445
void VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
446446
if (!hasOrigin(ICE->getType()))
447447
return;
448-
Visit(ICE->getSubExpr());
449448
// An ImplicitCastExpr node itself gets an origin, which flows from the
450449
// origin of its sub-expression (after stripping its own parens/casts).
451450
// TODO: Consider if this is actually useful in practice. Alternatively, we

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,18 +2905,19 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
29052905
AC.getCFGBuildOptions().AddCXXNewAllocator = false;
29062906
AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true;
29072907

2908+
bool EnableLifetimeSafetyAnalysis = S.getLangOpts().EnableLifetimeSafety;
2909+
29082910
// Force that certain expressions appear as CFGElements in the CFG. This
29092911
// is used to speed up various analyses.
29102912
// FIXME: This isn't the right factoring. This is here for initial
29112913
// prototyping, but we need a way for analyses to say what expressions they
29122914
// expect to always be CFGElements and then fill in the BuildOptions
29132915
// appropriately. This is essentially a layering violation.
29142916
if (P.enableCheckUnreachable || P.enableThreadSafetyAnalysis ||
2915-
P.enableConsumedAnalysis) {
2917+
P.enableConsumedAnalysis || EnableLifetimeSafetyAnalysis) {
29162918
// Unreachable code analysis and thread safety require a linearized CFG.
29172919
AC.getCFGBuildOptions().setAllAlwaysAdd();
2918-
}
2919-
else {
2920+
} else {
29202921
AC.getCFGBuildOptions()
29212922
.setAlwaysAdd(Stmt::BinaryOperatorClass)
29222923
.setAlwaysAdd(Stmt::CompoundAssignOperatorClass)
@@ -2927,7 +2928,6 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
29272928
.setAlwaysAdd(Stmt::UnaryOperatorClass);
29282929
}
29292930

2930-
bool EnableLifetimeSafetyAnalysis = S.getLangOpts().EnableLifetimeSafety;
29312931
// Install the logical handler.
29322932
std::optional<LogicalErrorHandler> LEH;
29332933
if (LogicalErrorHandler::hasActiveDiagnostics(Diags, D->getBeginLoc())) {

clang/test/Sema/warn-lifetime-safety-dataflow.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,22 @@ void pointer_indirection() {
293293
int *q = *pp;
294294
// CHECK: AssignOrigin (Dest: {{[0-9]+}} (Decl: q), Src: {{[0-9]+}} (Expr: ImplicitCastExpr))
295295
}
296+
297+
// CHECK-LABEL: Function: ternary_operator
298+
// FIXME: Propagate origins across ConditionalOperator.
299+
void ternary_operator() {
300+
int a, b;
301+
int *p;
302+
p = (a > b) ? &a : &b;
303+
// CHECK: Block B2:
304+
// CHECK: Issue (LoanID: [[L_A:[0-9]+]], ToOrigin: [[O_ADDR_A:[0-9]+]] (Expr: UnaryOperator))
305+
// CHECK: End of Block
306+
307+
// CHECK: Block B3:
308+
// CHECK: Issue (LoanID: [[L_B:[0-9]+]], ToOrigin: [[O_ADDR_A:[0-9]+]] (Expr: UnaryOperator))
309+
// CHECK: End of Block
310+
311+
// CHECK: Block B1:
312+
// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: {{[0-9]+}} (Expr: ConditionalOperator))
313+
// CHECK: End of Block
314+
}

clang/unittests/Analysis/LifetimeSafetyTest.cpp

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "clang/ASTMatchers/ASTMatchers.h"
1212
#include "clang/Testing/TestAST.h"
1313
#include "llvm/ADT/StringMap.h"
14+
#include "llvm/Testing/Support/Error.h"
1415
#include "gmock/gmock.h"
1516
#include "gtest/gtest.h"
1617
#include <optional>
@@ -20,6 +21,7 @@ namespace clang::lifetimes::internal {
2021
namespace {
2122

2223
using namespace ast_matchers;
24+
using ::testing::SizeIs;
2325
using ::testing::UnorderedElementsAreArray;
2426

2527
// A helper class to run the full lifetime analysis on a piece of code
@@ -96,21 +98,18 @@ class LifetimeTestHelper {
9698
return OID;
9799
}
98100

99-
std::optional<LoanID> getLoanForVar(llvm::StringRef VarName) {
101+
std::vector<LoanID> getLoansForVar(llvm::StringRef VarName) {
100102
auto *VD = findDecl<VarDecl>(VarName);
101-
if (!VD)
102-
return std::nullopt;
103+
if (!VD) {
104+
ADD_FAILURE() << "No VarDecl found for '" << VarName << "'";
105+
return {};
106+
}
103107
std::vector<LoanID> LID = Analysis.getLoanIDForVar(VD);
104108
if (LID.empty()) {
105109
ADD_FAILURE() << "Loan for '" << VarName << "' not found.";
106-
return std::nullopt;
107-
}
108-
// TODO: Support retrieving more than one loans to a var.
109-
if (LID.size() > 1) {
110-
ADD_FAILURE() << "More than 1 loans found for '" << VarName;
111-
return std::nullopt;
110+
return {};
112111
}
113-
return LID[0];
112+
return LID;
114113
}
115114

116115
std::optional<LoanSet> getLoansAtPoint(OriginID OID,
@@ -121,13 +120,12 @@ class LifetimeTestHelper {
121120
return Analysis.getLoansAtPoint(OID, PP);
122121
}
123122

124-
std::optional<llvm::DenseSet<LoanID>>
123+
std::optional<std::vector<LoanID>>
125124
getExpiredLoansAtPoint(llvm::StringRef Annotation) {
126125
ProgramPoint PP = Runner.getProgramPoint(Annotation);
127126
if (!PP)
128127
return std::nullopt;
129-
auto Expired = Analysis.getExpiredLoansAtPoint(PP);
130-
return llvm::DenseSet<LoanID>{Expired.begin(), Expired.end()};
128+
return Analysis.getExpiredLoansAtPoint(PP);
131129
}
132130

133131
private:
@@ -197,12 +195,13 @@ MATCHER_P2(HasLoansToImpl, LoanVars, Annotation, "") {
197195

198196
std::vector<LoanID> ExpectedLoans;
199197
for (const auto &LoanVar : LoanVars) {
200-
std::optional<LoanID> ExpectedLIDOpt = Info.Helper.getLoanForVar(LoanVar);
201-
if (!ExpectedLIDOpt) {
198+
std::vector<LoanID> ExpectedLIDs = Info.Helper.getLoansForVar(LoanVar);
199+
if (ExpectedLIDs.empty()) {
202200
*result_listener << "could not find loan for var '" << LoanVar << "'";
203201
return false;
204202
}
205-
ExpectedLoans.push_back(*ExpectedLIDOpt);
203+
ExpectedLoans.insert(ExpectedLoans.end(), ExpectedLIDs.begin(),
204+
ExpectedLIDs.end());
206205
}
207206

208207
return ExplainMatchResult(UnorderedElementsAreArray(ExpectedLoans),
@@ -221,17 +220,17 @@ MATCHER_P(AreExpiredAt, Annotation, "") {
221220
<< Annotation << "'";
222221
return false;
223222
}
224-
std::vector<LoanID> ActualExpiredLoans(ActualExpiredSetOpt->begin(),
225-
ActualExpiredSetOpt->end());
223+
std::vector<LoanID> ActualExpiredLoans = *ActualExpiredSetOpt;
226224
std::vector<LoanID> ExpectedExpiredLoans;
227225
for (const auto &VarName : Info.LoanVars) {
228-
auto LoanIDOpt = Helper.getLoanForVar(VarName);
229-
if (!LoanIDOpt) {
226+
auto LoanIDs = Helper.getLoansForVar(VarName);
227+
if (LoanIDs.empty()) {
230228
*result_listener << "could not find a loan for variable '" << VarName
231229
<< "'";
232230
return false;
233231
}
234-
ExpectedExpiredLoans.push_back(*LoanIDOpt);
232+
ExpectedExpiredLoans.insert(ExpectedExpiredLoans.end(), LoanIDs.begin(),
233+
LoanIDs.end());
235234
}
236235
return ExplainMatchResult(UnorderedElementsAreArray(ExpectedExpiredLoans),
237236
ActualExpiredLoans, result_listener);
@@ -730,5 +729,17 @@ TEST_F(LifetimeAnalysisTest, ReassignedPointerThenOriginalExpires) {
730729
EXPECT_THAT(LoansTo({"s1", "s2"}), AreExpiredAt("p_after_s1_expires"));
731730
}
732731

732+
TEST_F(LifetimeAnalysisTest, NoDuplicateLoansForImplicitCastToConst) {
733+
SetupTest(R"(
734+
void target() {
735+
MyObj a;
736+
const MyObj* p = &a;
737+
const MyObj* q = &a;
738+
POINT(at_end);
739+
}
740+
)");
741+
EXPECT_THAT(Helper->getLoansForVar("a"), SizeIs(2));
742+
}
743+
733744
} // anonymous namespace
734745
} // namespace clang::lifetimes::internal

0 commit comments

Comments
 (0)