Skip to content

Commit 2a57027

Browse files
authored
Merge pull request #16967 from dingobye/sr7660
[Sema] Fix spurious use_local_before_declaration errors.
2 parents b68ae2e + 95b95e3 commit 2a57027

File tree

5 files changed

+174
-27
lines changed

5 files changed

+174
-27
lines changed

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,60 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) {
569569

570570
// FIXME: Need to refactor the way we build an AST node from a lookup result!
571571

572+
SmallVector<ValueDecl*, 4> ResultValues;
573+
ValueDecl *localDeclAfterUse = nullptr;
574+
auto isValid = [&](ValueDecl *D) {
575+
// FIXME: The source-location checks won't make sense once
576+
// EnableASTScopeLookup is the default.
577+
if (Loc.isValid() && D->getLoc().isValid() &&
578+
D->getDeclContext()->isLocalContext() &&
579+
D->getDeclContext() == DC &&
580+
Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc())) {
581+
localDeclAfterUse = D;
582+
return false;
583+
}
584+
return true;
585+
};
586+
bool AllDeclRefs = findNonMembers(
587+
*this, Lookup.innerResults(), UDRE->getRefKind(), /*breakOnMember=*/true,
588+
ResultValues, isValid);
589+
590+
// If local declaration after use is found, check outer results for
591+
// better matching candidates.
592+
if (localDeclAfterUse) {
593+
auto innerDecl = localDeclAfterUse;
594+
595+
// Perform a thorough lookup if outer results was not included before.
596+
if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) {
597+
auto option = lookupOptions;
598+
option |= NameLookupFlags::IncludeOuterResults;
599+
Lookup = lookupUnqualified(DC, Name, Loc, option);
600+
}
601+
602+
while (localDeclAfterUse) {
603+
if (Lookup.outerResults().empty()) {
604+
diagnose(Loc, diag::use_local_before_declaration, Name);
605+
diagnose(innerDecl, diag::decl_declared_here, Name);
606+
Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange());
607+
return error;
608+
}
609+
610+
Lookup.shiftDownResults();
611+
ResultValues.clear();
612+
localDeclAfterUse = nullptr;
613+
AllDeclRefs = findNonMembers(
614+
*this, Lookup.innerResults(), UDRE->getRefKind(), /*breakOnMember=*/true,
615+
ResultValues, isValid);
616+
}
617+
618+
// Drop outer results if they are not supposed to be included.
619+
if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) {
620+
Lookup.filter([&](LookupResultEntry Result, bool isOuter) {
621+
return !isOuter;
622+
});
623+
}
624+
}
625+
572626
// If we have an unambiguous reference to a type decl, form a TypeExpr.
573627
if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary &&
574628
isa<TypeDecl>(Lookup[0].getValueDecl())) {
@@ -586,27 +640,6 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) {
586640
UDRE->isImplicit());
587641
}
588642

589-
SmallVector<ValueDecl*, 4> ResultValues;
590-
Expr *error = nullptr;
591-
bool AllDeclRefs = findNonMembers(
592-
*this, Lookup.innerResults(), UDRE->getRefKind(), /*breakOnMember=*/true,
593-
ResultValues, [&](ValueDecl *D) {
594-
// FIXME: The source-location checks won't make sense once
595-
// EnableASTScopeLookup is the default.
596-
if (Loc.isValid() && D->getLoc().isValid() &&
597-
D->getDeclContext()->isLocalContext() &&
598-
D->getDeclContext() == DC &&
599-
Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc())) {
600-
diagnose(Loc, diag::use_local_before_declaration, Name);
601-
diagnose(D, diag::decl_declared_here, Name);
602-
error = new (Context) ErrorExpr(UDRE->getSourceRange());
603-
return false;
604-
}
605-
return true;
606-
});
607-
if (error)
608-
return error;
609-
610643
if (AllDeclRefs) {
611644
// Diagnose uses of operators that found no matching candidates.
612645
if (ResultValues.empty()) {

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,26 @@ void LookupResult::filter(
4747
Results.end());
4848
}
4949

50+
void LookupResult::shiftDownResults() {
51+
// Remove inner results.
52+
Results.erase(Results.begin(), Results.begin() + IndexOfFirstOuterResult);
53+
IndexOfFirstOuterResult = 0;
54+
55+
if (Results.empty())
56+
return;
57+
58+
// Compute IndexOfFirstOuterResult.
59+
const DeclContext *dcInner = Results.front().getValueDecl()->getDeclContext();
60+
for (auto &&result : Results) {
61+
const DeclContext *dc = result.getValueDecl()->getDeclContext();
62+
if (dc == dcInner ||
63+
(dc->isModuleScopeContext() && dcInner->isModuleScopeContext()))
64+
++IndexOfFirstOuterResult;
65+
else
66+
break;
67+
}
68+
}
69+
5070
namespace {
5171
/// Builder that helps construct a lookup result from the raw lookup
5272
/// data.

lib/Sema/TypeChecker.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ class LookupResult {
148148
/// Filter out any results that aren't accepted by the given predicate.
149149
void
150150
filter(llvm::function_ref<bool(LookupResultEntry, /*isOuter*/ bool)> pred);
151+
152+
/// Shift down results by dropping inner results while keeping outer
153+
/// results (if any), the innermost of which are recogized as inner
154+
/// results afterwards.
155+
void shiftDownResults();
151156
};
152157

153158
/// An individual result of a name lookup for a type.

test/NameBinding/name-binding.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,16 +195,16 @@ test+++
195195
//===----------------------------------------------------------------------===//
196196

197197
func forwardReference() {
198-
x = 0 // expected-error{{use of local variable 'x' before its declaration}}
199-
var x: Float = 0.0 // expected-note{{'x' declared here}}
198+
v = 0 // expected-error{{use of local variable 'v' before its declaration}}
199+
var v: Float = 0.0 // expected-note{{'v' declared here}}
200200
}
201201

202202
class ForwardReference {
203203
var x: Int = 0
204204

205205
func test() {
206-
x = 0 // expected-error{{use of local variable 'x' before its declaration}}
207-
var x: Float = 0.0 // expected-note{{'x' declared here}}
206+
x = 0
207+
var x: Float = 0.0 // expected-warning{{variable 'x' was never used; consider replacing with '_' or removing it}}
208208
}
209209
}
210210

test/Sema/diag_use_before_declaration.swift

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,97 @@ func sr5163() {
1515
var foo: Int?
1616

1717
func test() {
18-
guard let bar = foo else { // expected-error {{use of local variable 'foo' before its declaration}}
18+
guard let bar = foo else {
1919
return
2020
}
21-
let foo = String(bar) // expected-note {{'foo' declared here}}
21+
let foo = String(bar)
22+
}
23+
24+
// SR-7660
25+
class C {
26+
var variable: Int?
27+
func f() {
28+
guard let _ = variable else { return }
29+
let variable = 1 // expected-warning {{initialization of immutable value 'variable' was never used; consider replacing with assignment to '_' or removing it}}
30+
}
31+
}
32+
33+
//===----------------------------------------------------------------------===//
34+
// Nested scope
35+
//===----------------------------------------------------------------------===//
36+
37+
func nested_scope_1() {
38+
do {
39+
do {
40+
let _ = x // expected-error {{use of local variable 'x' before its declaration}}
41+
let x = 111 // expected-note {{'x' declared here}}
42+
}
43+
let x = 11
44+
}
45+
let x = 1
46+
}
47+
48+
func nested_scope_2() {
49+
do {
50+
let x = 11
51+
do {
52+
let _ = x
53+
let x = 111 // expected-warning {{initialization of immutable value 'x' was never used; consider replacing with assignment to '_' or removing it}}
54+
}
55+
}
56+
let x = 1 // expected-warning {{initialization of immutable value 'x' was never used; consider replacing with assignment to '_' or removing it}}
57+
}
58+
59+
func nested_scope_3() {
60+
let x = 1
61+
do {
62+
do {
63+
let _ = x
64+
let x = 111 // expected-warning {{initialization of immutable value 'x' was never used; consider replacing with assignment to '_' or removing it}}
65+
}
66+
let x = 11 // expected-warning {{initialization of immutable value 'x' was never used; consider replacing with assignment to '_' or removing it}}
67+
}
68+
}
69+
70+
//===----------------------------------------------------------------------===//
71+
// Type scope
72+
//===----------------------------------------------------------------------===//
73+
74+
class Ty {
75+
var v : Int?
76+
77+
func fn() {
78+
let _ = v
79+
let v = 1 // expected-warning {{initialization of immutable value 'v' was never used; consider replacing with assignment to '_' or removing it}}
80+
}
81+
}
82+
83+
//===----------------------------------------------------------------------===//
84+
// File scope
85+
//===----------------------------------------------------------------------===//
86+
87+
let g = 0
88+
89+
func file_scope_1() {
90+
let _ = g
91+
let g = 1 // expected-warning {{initialization of immutable value 'g' was never used; consider replacing with assignment to '_' or removing it}}
92+
}
93+
94+
func file_scope_2() {
95+
let _ = gg // expected-error {{use of local variable 'gg' before its declaration}}
96+
let gg = 1 // expected-note {{'gg' declared here}}
97+
}
98+
99+
//===----------------------------------------------------------------------===//
100+
// Module scope
101+
//===----------------------------------------------------------------------===//
102+
103+
func module_scope_1() {
104+
let _ = print // Legal use of func print declared in Swift Standard Library
105+
let print = "something" // expected-warning {{initialization of immutable value 'print' was never used; consider replacing with assignment to '_' or removing it}}
106+
}
107+
108+
func module_scope_2() {
109+
let _ = another_print // expected-error {{use of local variable 'another_print' before its declaration}}
110+
let another_print = "something" // expected-note {{'another_print' declared here}}
22111
}

0 commit comments

Comments
 (0)