@@ -2236,10 +2236,11 @@ class VarDeclUsageChecker : public ASTWalker {
2236
2236
DiagnosticEngine &Diags;
2237
2237
// Keep track of some information about a variable.
2238
2238
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.
2241
2242
2242
- RK_CaptureList = 4 // /< Var is an entry in a capture list.
2243
+ RK_CaptureList = 8 // /< Var is an entry in a capture list.
2243
2244
};
2244
2245
2245
2246
// / These are all of the variables that we are tracking. VarDecls get added
@@ -2249,13 +2250,10 @@ class VarDeclUsageChecker : public ASTWalker {
2249
2250
2250
2251
// / This is a mapping from an OpaqueValue to the expression that initialized
2251
2252
// / it.
2252
- llvm::SmallDenseMap<OpaqueValueExpr*, Expr*> OpaqueValueMap;
2253
+ llvm::SmallDenseMap<OpaqueValueExpr *, Expr *> OpaqueValueMap;
2253
2254
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;
2259
2257
2260
2258
// / This is a mapping from VarDecls to the if/while/guard statement that they
2261
2259
// / occur in, when they are in a pattern in a StmtCondition.
@@ -2271,36 +2269,8 @@ class VarDeclUsageChecker : public ASTWalker {
2271
2269
void operator =(const VarDeclUsageChecker &) = delete ;
2272
2270
2273
2271
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
-
2290
2272
VarDeclUsageChecker (DiagnosticEngine &Diags) : Diags(Diags) {}
2291
2273
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
-
2304
2274
// After we have scanned the entire region, diagnose variables that could be
2305
2275
// declared with a narrower usage kind.
2306
2276
~VarDeclUsageChecker () override ;
@@ -2323,10 +2293,6 @@ class VarDeclUsageChecker : public ASTWalker {
2323
2293
}
2324
2294
return sawMutation;
2325
2295
}
2326
-
2327
- bool isVarDeclEverWritten (VarDecl *VD) {
2328
- return (VarDecls[VD] & RK_Written) != 0 ;
2329
- }
2330
2296
2331
2297
bool shouldTrackVarDecl (VarDecl *VD) {
2332
2298
// If the variable is implicit, ignore it.
@@ -2355,9 +2321,7 @@ class VarDeclUsageChecker : public ASTWalker {
2355
2321
auto *vd = dyn_cast<VarDecl>(D);
2356
2322
if (!vd) return ;
2357
2323
2358
- auto vdi = VarDecls.find (vd);
2359
- if (vdi != VarDecls.end ())
2360
- vdi->second |= Flag;
2324
+ VarDecls[vd] |= Flag;
2361
2325
}
2362
2326
2363
2327
void markBaseOfStorageUse (Expr *E, ConcreteDeclRef decl, unsigned flags);
@@ -2385,17 +2349,17 @@ class VarDeclUsageChecker : public ASTWalker {
2385
2349
// that fact for better diagnostics.
2386
2350
auto parentAsExpr = Parent.getAsExpr ();
2387
2351
if (parentAsExpr && isa<CaptureListExpr>(parentAsExpr))
2388
- return RK_CaptureList;
2352
+ return RK_CaptureList | RK_Defined ;
2389
2353
// Otherwise, return none.
2390
- return 0 ;
2354
+ return RK_Defined ;
2391
2355
}();
2392
2356
2393
2357
if (!vd->isImplicit ()) {
2394
2358
if (auto *childVd =
2395
2359
vd->getCorrespondingCaseBodyVariable ().getPtrOrNull ()) {
2396
2360
// 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 ;
2399
2363
}
2400
2364
}
2401
2365
VarDecls[vd] |= defaultFlags;
@@ -2409,23 +2373,27 @@ class VarDeclUsageChecker : public ASTWalker {
2409
2373
return false ;
2410
2374
2411
2375
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
+ }
2424
2386
}
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 ();
2427
2394
return false ;
2428
2395
}
2396
+
2429
2397
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
2430
2398
// If this is a TopLevelCodeDecl, scan for global variables
2431
2399
auto *body = TLCD->getBody ();
@@ -2437,7 +2405,7 @@ class VarDeclUsageChecker : public ASTWalker {
2437
2405
if (!PBD) continue ;
2438
2406
for (auto idx : range (PBD->getNumPatternEntries ())) {
2439
2407
PBD->getPattern (idx)->forEachVariable ([&](VarDecl *VD) {
2440
- VarDecls[VD] = RK_Read|RK_Written;
2408
+ VarDecls[VD] = RK_Read|RK_Written|RK_Defined ;
2441
2409
});
2442
2410
}
2443
2411
} else if (node.is <Stmt *>()) {
@@ -2448,7 +2416,7 @@ class VarDeclUsageChecker : public ASTWalker {
2448
2416
for (StmtConditionElement SCE : GS->getCond ()) {
2449
2417
if (auto pattern = SCE.getPatternOrNull ()) {
2450
2418
pattern->forEachVariable ([&](VarDecl *VD) {
2451
- VarDecls[VD] = RK_Read|RK_Written;
2419
+ VarDecls[VD] = RK_Read|RK_Written|RK_Defined ;
2452
2420
});
2453
2421
}
2454
2422
}
@@ -2507,7 +2475,7 @@ class VarDeclUsageChecker : public ASTWalker {
2507
2475
// Make sure that we setup our case body variables.
2508
2476
if (auto *caseStmt = dyn_cast<CaseStmt>(S)) {
2509
2477
for (auto *vd : caseStmt->getCaseBodyVariablesOrEmptyArray ()) {
2510
- VarDecls[vd] |= 0 ;
2478
+ VarDecls[vd] |= RK_Defined ;
2511
2479
}
2512
2480
}
2513
2481
@@ -2661,6 +2629,10 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
2661
2629
unsigned access;
2662
2630
std::tie (var, access) = p;
2663
2631
2632
+ // If the variable was not defined in this scope, we can safely ignore it.
2633
+ if (!(access & RK_Defined))
2634
+ continue ;
2635
+
2664
2636
if (auto *caseStmt =
2665
2637
dyn_cast_or_null<CaseStmt>(var->getRecursiveParentPatternStmt ())) {
2666
2638
// Only diagnose VarDecls from the first CaseLabelItem in CaseStmts, as
@@ -2683,9 +2655,11 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
2683
2655
if (auto param = dyn_cast<ParamDecl>(var)) {
2684
2656
auto FD = dyn_cast<AccessorDecl>(param->getDeclContext ());
2685
2657
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 ;
2689
2663
Diags.diagnose (DRE->getLoc (), diag::unused_setter_parameter,
2690
2664
var->getName ());
2691
2665
Diags.diagnose (DRE->getLoc (), diag::fixit_for_unused_setter_parameter,
@@ -3029,16 +3003,14 @@ std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
3029
3003
3030
3004
// If the Expression is a read of a getter, track for diagnostics
3031
3005
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));
3034
3007
}
3035
3008
}
3036
3009
// If the Expression is a member reference, see if it is a read of the getter
3037
3010
// to track for diagnostics.
3038
3011
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
3039
3012
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));
3042
3014
markBaseOfStorageUse (MRE->getBase (), MRE->getMember (), RK_Read);
3043
3015
return { false , E };
3044
3016
}
@@ -3125,18 +3097,22 @@ performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD) {
3125
3097
}
3126
3098
3127
3099
// / 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) {
3132
3101
// Don't produce these diagnostics for implicitly generated code.
3133
3102
if (AFD->getLoc ().isInvalid () || AFD->isImplicit () || AFD->isInvalid ())
3134
3103
return ;
3135
3104
3136
3105
// 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
+
3140
3116
// If the function has an opaque return type, check the return expressions
3141
3117
// to determine the underlying type.
3142
3118
if (auto opaqueResultTy = AFD->getOpaqueResultTypeDecl ()) {
0 commit comments