19
19
#include " TypeCheckConcurrency.h"
20
20
#include " TypeCheckInvertible.h"
21
21
#include " TypeChecker.h"
22
+ #include " swift/AST/ASTBridging.h"
22
23
#include " swift/AST/ASTWalker.h"
23
24
#include " swift/AST/ConformanceLookup.h"
24
25
#include " swift/AST/DiagnosticsSema.h"
@@ -3160,6 +3161,9 @@ class VarDeclUsageChecker : public ASTWalker {
3160
3161
DeclContext *DC;
3161
3162
3162
3163
DiagnosticEngine &Diags;
3164
+
3165
+ SourceRange FullRange;
3166
+
3163
3167
// Keep track of some information about a variable.
3164
3168
enum {
3165
3169
RK_Defined = 1 , // /< Whether it was ever defined in this scope.
@@ -3195,8 +3199,9 @@ class VarDeclUsageChecker : public ASTWalker {
3195
3199
void operator =(const VarDeclUsageChecker &) = delete ;
3196
3200
3197
3201
public:
3198
- VarDeclUsageChecker (DeclContext *DC,
3199
- DiagnosticEngine &Diags) : DC(DC), Diags(Diags) {}
3202
+ VarDeclUsageChecker (DeclContext *DC, DiagnosticEngine &Diags,
3203
+ SourceRange range)
3204
+ : DC(DC), Diags(Diags), FullRange(range) {}
3200
3205
3201
3206
// After we have scanned the entire region, diagnose variables that could be
3202
3207
// declared with a narrower usage kind.
@@ -3266,11 +3271,6 @@ class VarDeclUsageChecker : public ASTWalker {
3266
3271
if (isa<TypeDecl>(D))
3267
3272
return Action::SkipNode ();
3268
3273
3269
- // The body of #if clauses are not walked into, we need custom processing
3270
- // for them.
3271
- if (auto *ICD = dyn_cast<IfConfigDecl>(D))
3272
- handleIfConfig (ICD);
3273
-
3274
3274
// If this is a VarDecl, then add it to our list of things to track.
3275
3275
if (auto *vd = dyn_cast<VarDecl>(D)) {
3276
3276
if (shouldTrackVarDecl (vd)) {
@@ -3354,9 +3354,6 @@ class VarDeclUsageChecker : public ASTWalker {
3354
3354
// / The heavy lifting happens when visiting expressions.
3355
3355
PreWalkResult<Expr *> walkToExprPre (Expr *E) override ;
3356
3356
3357
- // / handle #if directives.
3358
- void handleIfConfig (IfConfigDecl *ICD);
3359
-
3360
3357
// / Custom handling for statements.
3361
3358
PreWalkResult<Stmt *> walkToStmtPre (Stmt *S) override {
3362
3359
// Keep track of an association between vardecls and the StmtCondition that
@@ -3864,6 +3861,16 @@ SourceLoc swift::getFixItLocForVarToLet(VarDecl *var) {
3864
3861
return SourceLoc ();
3865
3862
}
3866
3863
3864
+ extern " C" bool swift_ASTGen_inactiveCodeContainsReference (
3865
+ BridgedASTContext ctx,
3866
+ BridgedStringRef sourceFileBuffer,
3867
+ BridgedStringRef searchRange,
3868
+ BridgedStringRef name,
3869
+ void **cache
3870
+ );
3871
+
3872
+ extern " C" void swift_ASTGen_freeInactiveCodeContainsReferenceCache (void *cache);
3873
+
3867
3874
// After we have scanned the entire region, diagnose variables that could be
3868
3875
// declared with a narrower usage kind.
3869
3876
VarDeclUsageChecker::~VarDeclUsageChecker () {
@@ -3873,6 +3880,35 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
3873
3880
if (sawError)
3874
3881
return ;
3875
3882
3883
+ // / Check whether the given variable might have been referenced from an
3884
+ // / inactive region of the source code.
3885
+ void *usedInInactiveCache = nullptr ;
3886
+ auto isUsedInInactive = [&](VarDecl *var) -> bool {
3887
+ #if SWIFT_BUILD_SWIFT_SYNTAX
3888
+ // Extract the full buffer containing the source range.
3889
+ ASTContext &ctx = DC->getASTContext ();
3890
+ SourceManager &sourceMgr = ctx.SourceMgr ;
3891
+ auto bufferID = sourceMgr.findBufferContainingLoc (FullRange.Start );
3892
+ StringRef sourceFileText = sourceMgr.getEntireTextForBuffer (bufferID);
3893
+
3894
+ // Extract the search text from that buffer.
3895
+ auto searchTextCharRange = Lexer::getCharSourceRangeFromSourceRange (
3896
+ sourceMgr, FullRange);
3897
+ StringRef searchText = sourceMgr.extractText (searchTextCharRange, bufferID);
3898
+
3899
+ return swift_ASTGen_inactiveCodeContainsReference (
3900
+ ctx, sourceFileText, searchText, var->getName ().str (),
3901
+ &usedInInactiveCache);
3902
+ #else
3903
+ return false ;
3904
+ #endif
3905
+ };
3906
+ SWIFT_DEFER {
3907
+ #if SWIFT_BUILD_SWIFT_SYNTAX
3908
+ swift_ASTGen_freeInactiveCodeContainsReferenceCache (usedInInactiveCache);
3909
+ #endif
3910
+ };
3911
+
3876
3912
for (auto p : VarDecls) {
3877
3913
VarDecl *var;
3878
3914
unsigned access;
@@ -3907,7 +3943,7 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
3907
3943
auto VD = dyn_cast<VarDecl>(FD->getStorage ());
3908
3944
if ((access & RK_Read) == 0 ) {
3909
3945
auto found = AssociatedGetterRefExpr.find (VD);
3910
- if (found != AssociatedGetterRefExpr.end ()) {
3946
+ if (found != AssociatedGetterRefExpr.end () && ! isUsedInInactive (VD) ) {
3911
3947
auto *DRE = found->second ;
3912
3948
Diags.diagnose (DRE->getLoc (), diag::unused_setter_parameter,
3913
3949
var->getName ());
@@ -3941,6 +3977,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
3941
3977
// produce a fixit hint with a parent map, but this is a lot of effort for
3942
3978
// a narrow case.
3943
3979
if (access & RK_CaptureList) {
3980
+ if (isUsedInInactive (var))
3981
+ continue ;
3982
+
3944
3983
Diags.diagnose (var->getLoc (), diag::capture_never_used,
3945
3984
var->getName ());
3946
3985
continue ;
@@ -3954,6 +3993,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
3954
3993
if (auto *pbd = var->getParentPatternBinding ()) {
3955
3994
if (pbd->getSingleVar () == var && pbd->getInit (0 ) != nullptr &&
3956
3995
!isa<TypedPattern>(pbd->getPattern (0 ))) {
3996
+ if (isUsedInInactive (var))
3997
+ continue ;
3998
+
3957
3999
unsigned varKind = var->isLet ();
3958
4000
SourceRange replaceRange (
3959
4001
pbd->getStartLoc (),
@@ -3984,6 +4026,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
3984
4026
if (isa<NamedPattern>(LP->getSubPattern ())) {
3985
4027
auto initExpr = SC->getCond ()[0 ].getInitializer ();
3986
4028
if (initExpr->getStartLoc ().isValid ()) {
4029
+ if (isUsedInInactive (var))
4030
+ continue ;
4031
+
3987
4032
unsigned noParens = initExpr->canAppendPostfixExpression ();
3988
4033
3989
4034
// If the subexpr is an "as?" cast, we can rewrite it to
@@ -4051,6 +4096,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
4051
4096
});
4052
4097
4053
4098
if (foundVP) {
4099
+ if (isUsedInInactive (var))
4100
+ continue ;
4101
+
4054
4102
unsigned varKind = var->isLet ();
4055
4103
Diags
4056
4104
.diagnose (var->getLoc (), diag::variable_never_used,
@@ -4062,6 +4110,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
4062
4110
4063
4111
// Otherwise, this is something more complex, perhaps
4064
4112
// let (a,b) = foo()
4113
+ if (isUsedInInactive (var))
4114
+ continue ;
4115
+
4065
4116
if (isWrittenLet) {
4066
4117
Diags.diagnose (var->getLoc (),
4067
4118
diag::immutable_value_never_used_but_assigned,
@@ -4085,6 +4136,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
4085
4136
!isVarDeclPartOfPBDThatHadSomeMutation (var)) {
4086
4137
SourceLoc FixItLoc = getFixItLocForVarToLet (var);
4087
4138
4139
+ if (isUsedInInactive (var))
4140
+ continue ;
4141
+
4088
4142
// If this is a parameter explicitly marked 'var', remove it.
4089
4143
if (FixItLoc.isInvalid ()) {
4090
4144
Diags.diagnose (var->getLoc (), diag::variable_never_mutated,
@@ -4113,6 +4167,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
4113
4167
4114
4168
// If this is a variable that was only written to, emit a warning.
4115
4169
if ((access & RK_Read) == 0 ) {
4170
+ if (isUsedInInactive (var))
4171
+ continue ;
4172
+
4116
4173
Diags.diagnose (var->getLoc (), diag::variable_never_read, var->getName ());
4117
4174
continue ;
4118
4175
}
@@ -4334,56 +4391,12 @@ ASTWalker::PreWalkResult<Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
4334
4391
return Action::Continue (E);
4335
4392
}
4336
4393
4337
- // / handle #if directives. All of the active clauses are already walked by the
4338
- // / AST walker, but we also want to handle the inactive ones to avoid false
4339
- // / positives.
4340
- void VarDeclUsageChecker::handleIfConfig (IfConfigDecl *ICD) {
4341
- struct ConservativeDeclMarker : public ASTWalker {
4342
- VarDeclUsageChecker &VDUC;
4343
- SourceFile *SF;
4344
-
4345
- ConservativeDeclMarker (VarDeclUsageChecker &VDUC)
4346
- : VDUC(VDUC), SF(VDUC.DC->getParentSourceFile ()) {}
4347
-
4348
- MacroWalking getMacroWalkingBehavior () const override {
4349
- return MacroWalking::Arguments;
4350
- }
4351
-
4352
- PostWalkResult<Expr *> walkToExprPost (Expr *E) override {
4353
- // If we see a bound reference to a decl in an inactive #if block, then
4354
- // conservatively mark it read and written. This will silence "variable
4355
- // unused" and "could be marked let" warnings for it.
4356
- if (auto *DRE = dyn_cast<DeclRefExpr>(E))
4357
- VDUC.addMark (DRE->getDecl (), RK_Read | RK_Written);
4358
- else if (auto *declRef = dyn_cast<UnresolvedDeclRefExpr>(E)) {
4359
- auto name = declRef->getName ();
4360
- auto loc = declRef->getLoc ();
4361
- if (name.isSimpleName () && loc.isValid ()) {
4362
- auto *varDecl = dyn_cast_or_null<VarDecl>(
4363
- ASTScope::lookupSingleLocalDecl (SF, name.getFullName (), loc));
4364
- if (varDecl)
4365
- VDUC.addMark (varDecl, RK_Read|RK_Written);
4366
- }
4367
- }
4368
- return Action::Continue (E);
4369
- }
4370
- };
4371
-
4372
- for (auto &clause : ICD->getClauses ()) {
4373
- // Active clauses are handled by the normal AST walk.
4374
- if (clause.isActive ) continue ;
4375
-
4376
- for (auto elt : clause.Elements )
4377
- elt.walk (ConservativeDeclMarker (*this ));
4378
- }
4379
- }
4380
-
4381
4394
// / Apply the warnings managed by VarDeclUsageChecker to the top level
4382
4395
// / code declarations that haven't been checked yet.
4383
4396
void swift::
4384
4397
performTopLevelDeclDiagnostics (TopLevelCodeDecl *TLCD) {
4385
4398
auto &ctx = TLCD->getDeclContext ()->getASTContext ();
4386
- VarDeclUsageChecker checker (TLCD, ctx.Diags );
4399
+ VarDeclUsageChecker checker (TLCD, ctx.Diags , TLCD-> getSourceRange () );
4387
4400
TLCD->walk (checker);
4388
4401
}
4389
4402
@@ -4398,7 +4411,7 @@ void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) {
4398
4411
// declared as constants. Skip local functions though, since they will
4399
4412
// be checked as part of their parent function or TopLevelCodeDecl.
4400
4413
auto &ctx = AFD->getDeclContext ()->getASTContext ();
4401
- VarDeclUsageChecker checker (AFD, ctx.Diags );
4414
+ VarDeclUsageChecker checker (AFD, ctx.Diags , AFD-> getBody ()-> getSourceRange () );
4402
4415
AFD->walk (checker);
4403
4416
}
4404
4417
0 commit comments