Skip to content

Commit e5a7fd3

Browse files
authored
Merge pull request #30802 from slavapestov/var-decl-usage-checker-fixes
VarDeclUsageChecker fixes
2 parents 9c1a703 + 319a026 commit e5a7fd3

11 files changed

+82
-111
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 55 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,10 +2236,11 @@ class VarDeclUsageChecker : public ASTWalker {
22362236
DiagnosticEngine &Diags;
22372237
// Keep track of some information about a variable.
22382238
enum {
2239-
RK_Read = 1, ///< Whether it was ever read.
2240-
RK_Written = 2, ///< Whether it was ever written or passed inout.
2239+
RK_Defined = 1, ///< Whether it was ever defined in this scope.
2240+
RK_Read = 2, ///< Whether it was ever read.
2241+
RK_Written = 4, ///< Whether it was ever written or passed inout.
22412242

2242-
RK_CaptureList = 4 ///< Var is an entry in a capture list.
2243+
RK_CaptureList = 8 ///< Var is an entry in a capture list.
22432244
};
22442245

22452246
/// These are all of the variables that we are tracking. VarDecls get added
@@ -2249,13 +2250,10 @@ class VarDeclUsageChecker : public ASTWalker {
22492250

22502251
/// This is a mapping from an OpaqueValue to the expression that initialized
22512252
/// it.
2252-
llvm::SmallDenseMap<OpaqueValueExpr*, Expr*> OpaqueValueMap;
2253+
llvm::SmallDenseMap<OpaqueValueExpr *, Expr *> OpaqueValueMap;
22532254

2254-
/// The getter associated with a setter function declaration.
2255-
const VarDecl *AssociatedGetter = nullptr;
2256-
2257-
/// The first reference to the associated getter.
2258-
const Expr *AssociatedGetterRefExpr = nullptr;
2255+
/// The first reference to the given property.
2256+
llvm::SmallDenseMap<VarDecl *, Expr *> AssociatedGetterRefExpr;
22592257

22602258
/// This is a mapping from VarDecls to the if/while/guard statement that they
22612259
/// occur in, when they are in a pattern in a StmtCondition.
@@ -2271,36 +2269,8 @@ class VarDeclUsageChecker : public ASTWalker {
22712269
void operator=(const VarDeclUsageChecker &) = delete;
22722270

22732271
public:
2274-
VarDeclUsageChecker(AbstractFunctionDecl *AFD)
2275-
: Diags(AFD->getASTContext().Diags) {
2276-
// If this AFD is a setter, track the parameter and the getter for
2277-
// the containing property so if newValue isn't used but the getter is used
2278-
// an error can be reported.
2279-
if (auto FD = dyn_cast<AccessorDecl>(AFD)) {
2280-
if (FD->getAccessorKind() == AccessorKind::Set) {
2281-
if (auto getter = dyn_cast<VarDecl>(FD->getStorage())) {
2282-
auto arguments = FD->getParameters();
2283-
VarDecls[arguments->get(0)] = 0;
2284-
AssociatedGetter = getter;
2285-
}
2286-
}
2287-
}
2288-
}
2289-
22902272
VarDeclUsageChecker(DiagnosticEngine &Diags) : Diags(Diags) {}
22912273

2292-
VarDeclUsageChecker(VarDecl *vd) : Diags(vd->getASTContext().Diags) {
2293-
// Track a specific VarDecl
2294-
VarDecls[vd] = 0;
2295-
if (auto *childVd = vd->getCorrespondingCaseBodyVariable().getPtrOrNull()) {
2296-
VarDecls[childVd] = 0;
2297-
}
2298-
}
2299-
2300-
void suppressDiagnostics() {
2301-
sawError = true; // set this flag so that no diagnostics will be emitted on delete.
2302-
}
2303-
23042274
// After we have scanned the entire region, diagnose variables that could be
23052275
// declared with a narrower usage kind.
23062276
~VarDeclUsageChecker() override;
@@ -2323,10 +2293,6 @@ class VarDeclUsageChecker : public ASTWalker {
23232293
}
23242294
return sawMutation;
23252295
}
2326-
2327-
bool isVarDeclEverWritten(VarDecl *VD) {
2328-
return (VarDecls[VD] & RK_Written) != 0;
2329-
}
23302296

23312297
bool shouldTrackVarDecl(VarDecl *VD) {
23322298
// If the variable is implicit, ignore it.
@@ -2355,9 +2321,7 @@ class VarDeclUsageChecker : public ASTWalker {
23552321
auto *vd = dyn_cast<VarDecl>(D);
23562322
if (!vd) return;
23572323

2358-
auto vdi = VarDecls.find(vd);
2359-
if (vdi != VarDecls.end())
2360-
vdi->second |= Flag;
2324+
VarDecls[vd] |= Flag;
23612325
}
23622326

23632327
void markBaseOfStorageUse(Expr *E, ConcreteDeclRef decl, unsigned flags);
@@ -2385,17 +2349,17 @@ class VarDeclUsageChecker : public ASTWalker {
23852349
// that fact for better diagnostics.
23862350
auto parentAsExpr = Parent.getAsExpr();
23872351
if (parentAsExpr && isa<CaptureListExpr>(parentAsExpr))
2388-
return RK_CaptureList;
2352+
return RK_CaptureList | RK_Defined;
23892353
// Otherwise, return none.
2390-
return 0;
2354+
return RK_Defined;
23912355
}();
23922356

23932357
if (!vd->isImplicit()) {
23942358
if (auto *childVd =
23952359
vd->getCorrespondingCaseBodyVariable().getPtrOrNull()) {
23962360
// Child vars are never in capture lists.
2397-
assert(defaultFlags == 0);
2398-
VarDecls[childVd] |= 0;
2361+
assert(defaultFlags == RK_Defined);
2362+
VarDecls[childVd] |= RK_Defined;
23992363
}
24002364
}
24012365
VarDecls[vd] |= defaultFlags;
@@ -2409,23 +2373,27 @@ class VarDeclUsageChecker : public ASTWalker {
24092373
return false;
24102374

24112375
if (auto *afd = dyn_cast<AbstractFunctionDecl>(D)) {
2412-
// If this is a nested function with a capture list, mark any captured
2413-
// variables.
2414-
if (afd->isBodyTypeChecked()) {
2415-
TypeChecker::computeCaptures(afd);
2416-
for (const auto &capture : afd->getCaptureInfo().getCaptures())
2417-
addMark(capture.getDecl(), RK_Read|RK_Written);
2418-
} else {
2419-
// If the body hasn't been type checked yet, be super-conservative and
2420-
// mark all variables as used. This can be improved later, e.g. by
2421-
// walking the untype-checked body to look for things that could
2422-
// possibly be used.
2423-
VarDecls.clear();
2376+
// If this AFD is a setter, track the parameter and the getter for
2377+
// the containing property so if newValue isn't used but the getter is used
2378+
// an error can be reported.
2379+
if (auto FD = dyn_cast<AccessorDecl>(afd)) {
2380+
if (FD->getAccessorKind() == AccessorKind::Set) {
2381+
if (isa<VarDecl>(FD->getStorage())) {
2382+
auto arguments = FD->getParameters();
2383+
VarDecls[arguments->get(0)] = RK_Defined;
2384+
}
2385+
}
24242386
}
2425-
2426-
// Don't walk into it though, it may not even be type checked yet.
2387+
2388+
if (afd->isBodyTypeChecked())
2389+
return true;
2390+
2391+
// Don't walk into a body that has not yet been type checked. This should
2392+
// only occur for top-level code.
2393+
VarDecls.clear();
24272394
return false;
24282395
}
2396+
24292397
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
24302398
// If this is a TopLevelCodeDecl, scan for global variables
24312399
auto *body = TLCD->getBody();
@@ -2437,7 +2405,7 @@ class VarDeclUsageChecker : public ASTWalker {
24372405
if (!PBD) continue;
24382406
for (auto idx : range(PBD->getNumPatternEntries())) {
24392407
PBD->getPattern(idx)->forEachVariable([&](VarDecl *VD) {
2440-
VarDecls[VD] = RK_Read|RK_Written;
2408+
VarDecls[VD] = RK_Read|RK_Written|RK_Defined;
24412409
});
24422410
}
24432411
} else if (node.is<Stmt *>()) {
@@ -2448,7 +2416,7 @@ class VarDeclUsageChecker : public ASTWalker {
24482416
for (StmtConditionElement SCE : GS->getCond()) {
24492417
if (auto pattern = SCE.getPatternOrNull()) {
24502418
pattern->forEachVariable([&](VarDecl *VD) {
2451-
VarDecls[VD] = RK_Read|RK_Written;
2419+
VarDecls[VD] = RK_Read|RK_Written|RK_Defined;
24522420
});
24532421
}
24542422
}
@@ -2507,7 +2475,7 @@ class VarDeclUsageChecker : public ASTWalker {
25072475
// Make sure that we setup our case body variables.
25082476
if (auto *caseStmt = dyn_cast<CaseStmt>(S)) {
25092477
for (auto *vd : caseStmt->getCaseBodyVariablesOrEmptyArray()) {
2510-
VarDecls[vd] |= 0;
2478+
VarDecls[vd] |= RK_Defined;
25112479
}
25122480
}
25132481

@@ -2661,6 +2629,10 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
26612629
unsigned access;
26622630
std::tie(var, access) = p;
26632631

2632+
// If the variable was not defined in this scope, we can safely ignore it.
2633+
if (!(access & RK_Defined))
2634+
continue;
2635+
26642636
if (auto *caseStmt =
26652637
dyn_cast_or_null<CaseStmt>(var->getRecursiveParentPatternStmt())) {
26662638
// Only diagnose VarDecls from the first CaseLabelItem in CaseStmts, as
@@ -2683,9 +2655,11 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
26832655
if (auto param = dyn_cast<ParamDecl>(var)) {
26842656
auto FD = dyn_cast<AccessorDecl>(param->getDeclContext());
26852657
if (FD && FD->getAccessorKind() == AccessorKind::Set) {
2686-
auto getter = dyn_cast<VarDecl>(FD->getStorage());
2687-
if ((access & RK_Read) == 0 && AssociatedGetter == getter) {
2688-
if (auto DRE = AssociatedGetterRefExpr) {
2658+
auto VD = dyn_cast<VarDecl>(FD->getStorage());
2659+
if ((access & RK_Read) == 0) {
2660+
auto found = AssociatedGetterRefExpr.find(VD);
2661+
if (found != AssociatedGetterRefExpr.end()) {
2662+
auto *DRE = found->second;
26892663
Diags.diagnose(DRE->getLoc(), diag::unused_setter_parameter,
26902664
var->getName());
26912665
Diags.diagnose(DRE->getLoc(), diag::fixit_for_unused_setter_parameter,
@@ -3029,16 +3003,14 @@ std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
30293003

30303004
// If the Expression is a read of a getter, track for diagnostics
30313005
if (auto VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3032-
if (AssociatedGetter == VD && AssociatedGetterRefExpr == nullptr)
3033-
AssociatedGetterRefExpr = DRE;
3006+
AssociatedGetterRefExpr.insert(std::make_pair(VD, DRE));
30343007
}
30353008
}
30363009
// If the Expression is a member reference, see if it is a read of the getter
30373010
// to track for diagnostics.
30383011
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
30393012
if (auto VD = dyn_cast<VarDecl>(MRE->getMember().getDecl())) {
3040-
if (AssociatedGetter == VD && AssociatedGetterRefExpr == nullptr)
3041-
AssociatedGetterRefExpr = MRE;
3013+
AssociatedGetterRefExpr.insert(std::make_pair(VD, MRE));
30423014
markBaseOfStorageUse(MRE->getBase(), MRE->getMember(), RK_Read);
30433015
return { false, E };
30443016
}
@@ -3125,18 +3097,22 @@ performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD) {
31253097
}
31263098

31273099
/// Perform diagnostics for func/init/deinit declarations.
3128-
void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD,
3129-
BraceStmt *body) {
3130-
assert(body && "Need a body to check");
3131-
3100+
void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) {
31323101
// Don't produce these diagnostics for implicitly generated code.
31333102
if (AFD->getLoc().isInvalid() || AFD->isImplicit() || AFD->isInvalid())
31343103
return;
31353104

31363105
// Check for unused variables, as well as variables that are could be
3137-
// declared as constants.
3138-
body->walk(VarDeclUsageChecker(AFD));
3139-
3106+
// declared as constants. Skip local functions though, since they will
3107+
// be checked as part of their parent function or TopLevelCodeDecl.
3108+
if (!AFD->getDeclContext()->isLocalContext()) {
3109+
auto &ctx = AFD->getDeclContext()->getASTContext();
3110+
VarDeclUsageChecker checker(ctx.Diags);
3111+
AFD->walk(checker);
3112+
}
3113+
3114+
auto *body = AFD->getBody();
3115+
31403116
// If the function has an opaque return type, check the return expressions
31413117
// to determine the underlying type.
31423118
if (auto opaqueResultTy = AFD->getOpaqueResultTypeDecl()) {

lib/Sema/MiscDiagnostics.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ void performSyntacticExprDiagnostics(const Expr *E, const DeclContext *DC,
4040
/// Emit diagnostics for a given statement.
4141
void performStmtDiagnostics(ASTContext &ctx, const Stmt *S);
4242

43-
void performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD,
44-
BraceStmt *body);
43+
void performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD);
4544

4645
/// Perform diagnostics on the top level code declaration.
4746
void performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD);

lib/Sema/TypeCheckStmt.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,7 @@ static Type getFunctionBuilderType(FuncDecl *FD) {
16881688
bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) {
16891689
auto res = TypeChecker::typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc());
16901690
TypeChecker::checkFunctionErrorHandling(AFD);
1691+
TypeChecker::computeCaptures(AFD);
16911692
return res;
16921693
}
16931694

@@ -1972,12 +1973,13 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator,
19721973
}
19731974
}
19741975

1976+
// Wire up the function body now.
1977+
AFD->setBody(body, AbstractFunctionDecl::BodyKind::TypeChecked);
1978+
19751979
// If nothing went wrong yet, perform extra checking.
19761980
if (!hadError && endTypeCheckLoc.isInvalid())
1977-
performAbstractFuncDeclDiagnostics(AFD, body);
1981+
performAbstractFuncDeclDiagnostics(AFD);
19781982

1979-
// Wire up the function body now.
1980-
AFD->setBody(body, AbstractFunctionDecl::BodyKind::TypeChecked);
19811983
return hadError;
19821984
}
19831985

lib/Sema/TypeChecker.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -306,11 +306,6 @@ static void typeCheckDelayedFunctions(SourceFile &SF) {
306306
} while (currentFunctionIdx < SF.DelayedFunctions.size() ||
307307
currentSynthesizedDecl < SF.SynthesizedDecls.size());
308308

309-
310-
for (AbstractFunctionDecl *FD : llvm::reverse(SF.DelayedFunctions)) {
311-
TypeChecker::computeCaptures(FD);
312-
}
313-
314309
SF.DelayedFunctions.clear();
315310
}
316311

test/SILGen/c_function_pointers.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %target-swift-emit-silgen -verify %s | %FileCheck %s
22

33
if true {
4-
var x = 0
4+
var x = 0 // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}
55
func local() -> Int { return 0 }
66
func localWithContext() -> Int { return x }
77
func transitiveWithoutContext() -> Int { return local() }

test/SILGen/capture_order.swift

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ public func captureBeforeDefLet(amount: Int) -> () -> Int {
99
}
1010
let closure = getter
1111
let modifiedAmount = amount // expected-note{{captured value declared here}}
12-
// FIXME: Bogus warning!
13-
// expected-warning@-2 {{initialization of immutable value 'modifiedAmount' was never used; consider replacing with assignment to '_' or removing it}}
1412
return closure
1513
}
1614

@@ -21,8 +19,6 @@ public func captureBeforeDefVar(amount: Int) -> () -> Int {
2119
}
2220
let closure = incrementor
2321
var currentTotal = 0 // expected-note{{captured value declared here}}
24-
// FIXME: Bogus warning!
25-
// expected-warning@-2 {{variable 'currentTotal' was written to, but never read}}
2622
currentTotal = 1
2723
return closure
2824
}
@@ -33,8 +29,6 @@ public func captureBeforeDefWeakVar(obj: AnyObject) -> () -> AnyObject? {
3329
}
3430
let closure = getter
3531
weak var weakObj: AnyObject? = obj // expected-note{{captured value declared here}}
36-
// FIXME: Bogus warning!
37-
// expected-warning@-2 {{variable 'weakObj' was written to, but never read}}
3832
return closure
3933
}
4034

@@ -44,8 +38,6 @@ public func captureBeforeDefUnownedLet(obj: AnyObject) -> () -> AnyObject? {
4438
}
4539
let closure = getter
4640
unowned let unownedObj: AnyObject = obj // expected-note{{captured value declared here}}
47-
// FIXME: Bogus warning!
48-
// expected-warning@-2 {{immutable value 'unownedObj' was never used; consider replacing with '_' or removing it}}
4941
return closure
5042
}
5143

@@ -55,8 +47,7 @@ public func captureBeforeDefUnownedVar(obj: AnyObject) -> () -> AnyObject? {
5547
}
5648
let closure = getter
5749
unowned var unownedObj: AnyObject = obj // expected-note{{captured value declared here}}
58-
// FIXME: Bogus warning!
59-
// expected-warning@-2 {{variable 'unownedObj' was never used; consider replacing with '_' or removing it}}
50+
// expected-warning@-1 {{variable 'unownedObj' was never mutated; consider changing to 'let' constant}}
6051
return closure
6152
}
6253

@@ -122,8 +113,6 @@ func captureInClosure() {
122113
}
123114

124115
var currentTotal = 0 // expected-note {{captured value declared here}}
125-
// FIXME: Bogus warning!
126-
// expected-warning@-2 {{initialization of variable 'currentTotal' was never used; consider replacing with assignment to '_' or removing it}}
127116

128117
_ = x
129118
}
@@ -139,8 +128,6 @@ func sr3210_crash() {
139128

140129
let b = 2 // expected-note {{captured value declared here}}
141130
// expected-warning@-1 {{code after 'return' will never be executed}}
142-
// FIXME: Bogus warning!
143-
// expected-warning@-3 {{initialization of immutable value 'b' was never used; consider replacing with assignment to '_' or removing it}}
144131
}
145132

146133
func sr3210() {
@@ -149,8 +136,6 @@ func sr3210() {
149136
}
150137

151138
let b = 2
152-
// FIXME: Bogus warning!
153-
// expected-warning@-2 {{initialization of immutable value 'b' was never used; consider replacing with assignment to '_' or removing it}}
154139
}
155140

156141
class SR4812 {
@@ -193,8 +178,7 @@ class rdar40600800 {
193178

194179
func innerFunction() {
195180
let closure = {
196-
// FIXME: Bogus warning!
197-
// expected-warning@-2 {{initialization of immutable value 'closure' was never used; consider replacing with assignment to '_' or removing it}}
181+
// expected-warning@-1 {{initialization of immutable value 'closure' was never used; consider replacing with assignment to '_' or removing it}}
198182
callback() // expected-note {{captured here}}
199183
}
200184
}

0 commit comments

Comments
 (0)