Skip to content

Commit e80f6bb

Browse files
committed
[Sema] Downgrade implicit self diag to warning for macro args
We previously missed diagnosing this for macro args, fixing it turned out to be a bit more source breaking than initially thought though, so downgrade to a warning until Swift 7. rdar://141963700
1 parent 7959485 commit e80f6bb

File tree

5 files changed

+191
-18
lines changed

5 files changed

+191
-18
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,13 +1721,18 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
17211721
/// these diagnostics.
17221722
SmallPtrSet<Expr *, 16> UnwrapStmtImplicitSelfExprs;
17231723

1724+
/// The number of parent macros.
1725+
unsigned MacroDepth = 0;
1726+
17241727
public:
17251728
explicit ImplicitSelfUsageChecker(ASTContext &Ctx, AbstractClosureExpr *ACE)
17261729
: Ctx(Ctx), Closures() {
17271730
if (ACE)
17281731
Closures.push_back(ACE);
17291732
}
17301733

1734+
bool isInMacro() const { return MacroDepth > 0; }
1735+
17311736
static bool
17321737
implicitWeakSelfReferenceIsValid510(const DeclRefExpr *DRE,
17331738
const AbstractClosureExpr *inClosure) {
@@ -2219,7 +2224,35 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
22192224
return isClosureRequiringSelfQualification(E);
22202225
}
22212226

2227+
template <typename... ArgTypes>
2228+
InFlightDiagnostic diagnoseImplicitSelfUse(
2229+
SourceLoc loc, Expr *base, AbstractClosureExpr *closure,
2230+
Diag<ArgTypes...> ID,
2231+
typename detail::PassArgument<ArgTypes>::type... Args) {
2232+
std::optional<unsigned> warnUntilVersion;
2233+
// Prior to Swift 6, we may need to downgrade to a warning for compatibility
2234+
// with the 5.10 diagnostic behavior.
2235+
if (!Ctx.isSwiftVersionAtLeast(6) &&
2236+
invalidImplicitSelfShouldOnlyWarn510(base, closure)) {
2237+
warnUntilVersion.emplace(6);
2238+
}
2239+
// Prior to Swift 7, downgrade to a warning if we're in a macro to preserve
2240+
// compatibility with the Swift 6 diagnostic behavior where we previously
2241+
// skipped diagnosing.
2242+
if (!Ctx.isSwiftVersionAtLeast(7) && isInMacro())
2243+
warnUntilVersion.emplace(7);
2244+
2245+
auto diag = Ctx.Diags.diagnose(loc, ID, std::move(Args)...);
2246+
if (warnUntilVersion)
2247+
diag.warnUntilSwiftVersion(*warnUntilVersion);
2248+
2249+
return diag;
2250+
}
2251+
22222252
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
2253+
if (isa<MacroExpansionExpr>(E))
2254+
MacroDepth += 1;
2255+
22232256
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
22242257
if (shouldRecordClosure(CE))
22252258
Closures.push_back(CE);
@@ -2249,12 +2282,10 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
22492282
selfDRE = dyn_cast_or_null<DeclRefExpr>(MRE->getBase());
22502283
auto baseName = MRE->getMember().getDecl()->getBaseName();
22512284
memberLoc = MRE->getLoc();
2252-
Diags
2253-
.diagnose(memberLoc,
2254-
diag::property_use_in_closure_without_explicit_self,
2255-
baseName.getIdentifier())
2256-
.warnUntilSwiftVersionIf(
2257-
invalidImplicitSelfShouldOnlyWarn510(MRE->getBase(), ACE), 6);
2285+
diagnoseImplicitSelfUse(
2286+
memberLoc, MRE->getBase(), ACE,
2287+
diag::property_use_in_closure_without_explicit_self,
2288+
baseName.getIdentifier());
22582289
}
22592290

22602291
// Handle method calls with a specific diagnostic + fixit.
@@ -2264,12 +2295,10 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
22642295
selfDRE = dyn_cast_or_null<DeclRefExpr>(DSCE->getBase());
22652296
auto MethodExpr = cast<DeclRefExpr>(DSCE->getFn());
22662297
memberLoc = DSCE->getLoc();
2267-
Diags
2268-
.diagnose(DSCE->getLoc(),
2269-
diag::method_call_in_closure_without_explicit_self,
2270-
MethodExpr->getDecl()->getBaseIdentifier())
2271-
.warnUntilSwiftVersionIf(
2272-
invalidImplicitSelfShouldOnlyWarn510(DSCE->getBase(), ACE), 6);
2298+
diagnoseImplicitSelfUse(
2299+
memberLoc, DSCE->getBase(), ACE,
2300+
diag::method_call_in_closure_without_explicit_self,
2301+
MethodExpr->getDecl()->getBaseIdentifier());
22732302
}
22742303

22752304
if (memberLoc.isValid()) {
@@ -2284,14 +2313,18 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
22842313
}
22852314

22862315
if (!selfDeclAllowsImplicitSelf(E, ACE)) {
2287-
Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure)
2288-
.warnUntilSwiftVersionIf(invalidImplicitSelfShouldOnlyWarn510(E, ACE),
2289-
6);
2316+
diagnoseImplicitSelfUse(E->getLoc(), E, ACE,
2317+
diag::implicit_use_of_self_in_closure);
22902318
}
22912319
return Action::Continue(E);
22922320
}
22932321

22942322
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
2323+
if (isa<MacroExpansionExpr>(E)) {
2324+
assert(isInMacro());
2325+
MacroDepth -= 1;
2326+
}
2327+
22952328
auto *ACE = dyn_cast<AbstractClosureExpr>(E);
22962329
if (!ACE) {
22972330
return Action::Continue(E);
@@ -2304,6 +2337,21 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
23042337
return Action::Continue(E);
23052338
}
23062339

2340+
PreWalkAction walkToDeclPre(Decl *D) override {
2341+
if (isa<MacroExpansionDecl>(D))
2342+
MacroDepth += 1;
2343+
2344+
return BaseDiagnosticWalker::walkToDeclPre(D);
2345+
}
2346+
2347+
PostWalkAction walkToDeclPost(Decl *D) override {
2348+
if (isa<MacroExpansionDecl>(D)) {
2349+
assert(isInMacro());
2350+
MacroDepth -= 1;
2351+
}
2352+
return Action::Continue();
2353+
}
2354+
23072355
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
23082356
/// Conditions like `if let self` or `guard let self`
23092357
/// have an RHS 'self' decl that is implicit, but this is not

lib/Sema/MiscDiagnostics.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,13 @@ namespace swift {
134134
ForEachStmt *forEach);
135135

136136
class BaseDiagnosticWalker : public ASTWalker {
137+
protected:
137138
PreWalkAction walkToDeclPre(Decl *D) override {
138139
// We don't walk into any nested local decls, except PatternBindingDecls,
139140
// which are type-checked along with the parent, and MacroExpansionDecl,
140141
// which needs to be visited to visit the macro arguments.
141-
return Action::VisitNodeIf(isa<PatternBindingDecl>(D) ||
142-
isa<MacroExpansionDecl>(D));
142+
return Action::VisitChildrenIf(isa<PatternBindingDecl>(D) ||
143+
isa<MacroExpansionDecl>(D));
143144
}
144145

145146
MacroWalking getMacroWalkingBehavior() const override {

test/Macros/macro_misc_diags.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ public struct MakeBinding : DeclarationMacro {
4848
}
4949
}
5050

51+
public struct MakeFunc : DeclarationMacro {
52+
static public func expansion(
53+
of node: some FreestandingMacroExpansionSyntax,
54+
in context: some MacroExpansionContext
55+
) throws -> [DeclSyntax] {
56+
["func expansionFn() -> Int { 0 }"]
57+
}
58+
}
59+
5160
//--- Client.swift
5261
@freestanding(expression)
5362
macro identity<T>(_ x: T) -> T = #externalMacro(module: "MacroPlugin", type: "IdentityMacro")
@@ -58,6 +67,9 @@ macro trailingClosure<T>(_ x: T) -> T = #externalMacro(module: "MacroPlugin", ty
5867
@freestanding(declaration, names: named(x))
5968
macro makeBinding<T>(_ x: T) = #externalMacro(module: "MacroPlugin", type: "MakeBinding")
6069

70+
@freestanding(declaration, names: named(expansionFn))
71+
macro makeFunc<T>(_ x: T) = #externalMacro(module: "MacroPlugin", type: "MakeFunc")
72+
6173
@available(*, deprecated)
6274
func deprecatedFunc() -> Int { 0 }
6375

@@ -132,12 +144,19 @@ struct rdar138997009 {
132144
class rdar138997009_Class {
133145
func foo() {}
134146
func bar() {
147+
// rdar://141963700 - Downgrade these to a warning for the macro argument,
148+
// but is still an error in the expansion.
135149
_ = {
136150
_ = #trailingClosure {
137151
foo()
138152
// CHECK-DIAG: @__swiftmacro_6Client0017Clientswift_yEEFcfMX[[@LINE-3]]{{.*}}trailingClosurefMf_.swift:2:9: error: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit
139-
// CHECK-DIAG: Client.swift:[[@LINE-2]]:9: error: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit
153+
// CHECK-DIAG: Client.swift:[[@LINE-2]]:9: warning: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit; this will be an error in a future Swift language mode
140154
}
155+
// Use an attribute to force a MacroExpansionDecl (otherwise we parse a
156+
// MacroExpansionExpr)
157+
@discardableResult
158+
#makeFunc(foo())
159+
// CHECK-DIAG: Client.swift:[[@LINE-1]]:17: warning: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit; this will be an error in a future Swift language mode
141160
}
142161
}
143162
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// REQUIRES: swift_swift_parser
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file --leading-lines %s %t
5+
6+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroPlugin) -module-name=MacroPlugin %t/MacroPlugin.swift -g -no-toolchain-stdlib-rpath
7+
8+
// RUN: %target-swift-frontend -typecheck -swift-version 5 -load-plugin-library %t/%target-library-name(MacroPlugin) %t/Client.swift -module-name Client -diagnostic-style=llvm 2> %t/diags
9+
// RUN: %FileCheck --check-prefix=CHECK-DIAG --implicit-check-not="{{error|warning}}: " -input-file=%t/diags %s
10+
11+
//--- MacroPlugin.swift
12+
import SwiftSyntax
13+
import SwiftSyntaxMacros
14+
15+
public struct TrailingClosureMacro: ExpressionMacro {
16+
public static func expansion(
17+
of macro: some FreestandingMacroExpansionSyntax,
18+
in context: some MacroExpansionContext
19+
) -> ExprSyntax {
20+
guard let argument = macro.trailingClosure else {
21+
fatalError()
22+
}
23+
return "\(argument)"
24+
}
25+
}
26+
27+
//--- Client.swift
28+
@freestanding(expression)
29+
macro trailingClosure<T>(_ x: T) -> T = #externalMacro(module: "MacroPlugin", type: "TrailingClosureMacro")
30+
31+
class rdar138997009_Class {
32+
func foo() {}
33+
func bar() {
34+
// rdar://141963700 - This is downgraded to a warning for Swift 6 in the
35+
// expansion, and Swift 7 for the argument.
36+
_ = { [self] in
37+
_ = #trailingClosure {
38+
foo()
39+
// CHECK-DIAG: @__swiftmacro_6Client0017Clientswift_yEEFcfMX[[@LINE-3]]{{.*}}trailingClosurefMf_.swift:2:9: warning: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit; this is an error in the Swift 6 language mode
40+
// CHECK-DIAG: Client.swift:[[@LINE-2]]:9: warning: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit; this will be an error in a future Swift language mode
41+
}
42+
}
43+
}
44+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// REQUIRES: swift_swift_parser
2+
// REQUIRES: swift7
3+
4+
// RUN: %empty-directory(%t)
5+
// RUN: split-file --leading-lines %s %t
6+
7+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroPlugin) -module-name=MacroPlugin %t/MacroPlugin.swift -g -no-toolchain-stdlib-rpath
8+
9+
// RUN: not %target-swift-frontend -typecheck -swift-version 7 -load-plugin-library %t/%target-library-name(MacroPlugin) %t/Client.swift -module-name Client -diagnostic-style=llvm 2> %t/diags
10+
// RUN: %FileCheck --check-prefix=CHECK-DIAG --implicit-check-not="{{error|warning}}: " -input-file=%t/diags %s
11+
12+
//--- MacroPlugin.swift
13+
import SwiftSyntax
14+
import SwiftSyntaxMacros
15+
16+
public struct TrailingClosureMacro: ExpressionMacro {
17+
public static func expansion(
18+
of macro: some FreestandingMacroExpansionSyntax,
19+
in context: some MacroExpansionContext
20+
) -> ExprSyntax {
21+
guard let argument = macro.trailingClosure else {
22+
fatalError()
23+
}
24+
return "\(argument)"
25+
}
26+
}
27+
28+
public struct MakeFunc : DeclarationMacro {
29+
static public func expansion(
30+
of node: some FreestandingMacroExpansionSyntax,
31+
in context: some MacroExpansionContext
32+
) throws -> [DeclSyntax] {
33+
["func expansionFn() -> Int { 0 }"]
34+
}
35+
}
36+
37+
//--- Client.swift
38+
@freestanding(expression)
39+
macro trailingClosure<T>(_ x: T) -> T = #externalMacro(module: "MacroPlugin", type: "TrailingClosureMacro")
40+
41+
@freestanding(declaration, names: named(expansionFn))
42+
macro makeFunc<T>(_ x: T) = #externalMacro(module: "MacroPlugin", type: "MakeFunc")
43+
44+
class rdar138997009_Class {
45+
func foo() {}
46+
func bar() {
47+
// rdar://141963700 - In Swift 7 these are errors.
48+
_ = {
49+
_ = #trailingClosure {
50+
foo()
51+
// CHECK-DIAG: @__swiftmacro_6Client0017Clientswift_yEEFcfMX[[@LINE-3]]{{.*}}trailingClosurefMf_.swift:2:9: error: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit
52+
// CHECK-DIAG: Client.swift:[[@LINE-2]]:9: error: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit
53+
}
54+
// Use an attribute to force a MacroExpansionDecl (otherwise we parse a
55+
// MacroExpansionExpr)
56+
@discardableResult
57+
#makeFunc(foo())
58+
// CHECK-DIAG: Client.swift:[[@LINE-1]]:17: error: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)