Skip to content

Commit b8ab2c0

Browse files
committed
[ASTScope] Match parent scopes in lookupCatchNode for brace stmts
Rather than looking for a given BraceStmtScope child for a particular catch node, check whether the given catch node is the parent of the innermost BraceStmtScope we've found, looking through an intermediate source file if needed. This ensures it works correctly when the BraceStmtScope is in a macro expansion. rdar://149036108
1 parent 3718a15 commit b8ab2c0

File tree

3 files changed

+42
-31
lines changed

3 files changed

+42
-31
lines changed

lib/AST/ASTScopeLookup.cpp

+20-31
Original file line numberDiff line numberDiff line change
@@ -703,42 +703,24 @@ lookupEnclosingABIAttributeScope(SourceFile *sourceFile, SourceLoc loc) {
703703
return nullptr;
704704
}
705705

706-
static std::pair<CatchNode, const BraceStmtScope *>
707-
getCatchNodeBody(const ASTScopeImpl *scope, CatchNode node) {
708-
const auto &children = scope->getChildren();
709-
if (children.empty())
710-
return { CatchNode(), nullptr };
711-
712-
auto stmt = dyn_cast<BraceStmtScope>(children[0]);
713-
if (!stmt || stmt->getStmt()->empty())
714-
return { CatchNode(), nullptr };
715-
716-
return std::make_pair(node, stmt);
717-
}
718-
719706
/// Retrieve the catch node associated with this scope, if any.
720-
static std::pair<CatchNode, const BraceStmtScope *>
721-
getCatchNode(const ASTScopeImpl *scope) {
707+
static CatchNode getCatchNode(const ASTScopeImpl *scope) {
722708
// Closures introduce a catch scope for errors initiated in their body.
723709
if (auto closureParams = dyn_cast<ClosureParametersScope>(scope)) {
724-
if (auto closure = dyn_cast<ClosureExpr>(closureParams->closureExpr)) {
725-
return getCatchNodeBody(scope, const_cast<ClosureExpr *>(closure));
726-
}
710+
if (auto closure = dyn_cast<ClosureExpr>(closureParams->closureExpr))
711+
return closure;
727712
}
728713

729714
// Functions introduce a catch scope for errors initiated in their body.
730-
if (auto function = dyn_cast<FunctionBodyScope>(scope)) {
731-
return getCatchNodeBody(
732-
scope, const_cast<AbstractFunctionDecl *>(function->decl));
733-
}
715+
if (auto function = dyn_cast<FunctionBodyScope>(scope))
716+
return function->decl;
734717

735718
// Do..catch blocks introduce a catch scope for errors initiated in the `do`
736719
// body.
737-
if (auto doCatch = dyn_cast<DoCatchStmtScope>(scope)) {
738-
return getCatchNodeBody(scope, const_cast<DoCatchStmt *>(doCatch->stmt));
739-
}
720+
if (auto doCatch = dyn_cast<DoCatchStmtScope>(scope))
721+
return doCatch->stmt;
740722

741-
return { CatchNode(), nullptr };
723+
return CatchNode();
742724
}
743725

744726
/// Check whether the given location precedes the start of the catch location
@@ -767,15 +749,22 @@ CatchNode ASTScopeImpl::lookupCatchNode(ModuleDecl *module, SourceLoc loc) {
767749
ASTScopeAssert(innermost->getWasExpanded(),
768750
"If looking in a scope, it must have been expanded.");
769751

770-
// Look for a body scope that's the
752+
// Look for a body scope that's the direct descendent of a catch node.
771753
const BraceStmtScope *innerBodyScope = nullptr;
772754
for (auto scope = innermost; scope; scope = scope->getParent().getPtrOrNull()) {
773755
// If we are at a catch node and in the body of the region from which that
774756
// node catches thrown errors, we have our result.
775-
auto caught = getCatchNode(scope);
776-
if (caught.first && caught.second == innerBodyScope &&
777-
!locationIsPriorToStartOfCatchScope(loc, caught.first)) {
778-
return caught.first;
757+
if (innerBodyScope && innerBodyScope->getParent() == scope) {
758+
// For a macro expansion, we may have an intermediate source file scope,
759+
// we can look through it.
760+
auto catchScope = scope;
761+
if (auto *sfScope = dyn_cast<ASTSourceFileScope>(catchScope)) {
762+
if (auto parent = sfScope->getParent())
763+
catchScope = parent.get();
764+
}
765+
auto caught = getCatchNode(catchScope);
766+
if (caught && !locationIsPriorToStartOfCatchScope(loc, caught))
767+
return caught;
779768
}
780769

781770
// If this is a try scope for a try! or try?, it catches the error.

test/Macros/Inputs/syntax_macro_definitions.swift

+10
Original file line numberDiff line numberDiff line change
@@ -2683,6 +2683,16 @@ public struct BodyMacroWithControlFlow: BodyMacro {
26832683
}
26842684
}
26852685

2686+
struct ThrowCancellationMacro: BodyMacro {
2687+
static func expansion(
2688+
of node: AttributeSyntax,
2689+
providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
2690+
in context: some MacroExpansionContext
2691+
) throws -> [CodeBlockItemSyntax] {
2692+
["throw CancellationError()"]
2693+
}
2694+
}
2695+
26862696
@_spi(ExperimentalLanguageFeature)
26872697
public struct TracedPreambleMacro: PreambleMacro {
26882698
public static func expansion(

test/Macros/macro_expand.swift

+12
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,18 @@ struct RedeclChecking {
152152
// CHECK-DIAGS: }
153153
// CHECK-DIAGS: END CONTENTS OF FILE
154154

155+
@attached(body)
156+
public macro ThrowCancellation() = #externalMacro(module: "MacroDefinition", type: "ThrowCancellationMacro")
157+
158+
// https://github.com/swiftlang/swift/issues/79039 - Make sure we diagnose the
159+
// error mismatch.
160+
@ThrowCancellation // expected-note {{in expansion of macro 'ThrowCancellation' on global function 'issue79039()' here}}
161+
func issue79039() throws(DecodingError)
162+
// CHECK-DIAGS: @__swiftmacro_9MacroUser10issue7903917ThrowCancellationfMb_.swift:2:11: error: thrown expression type 'CancellationError' cannot be converted to error type 'DecodingError'
163+
164+
@ThrowCancellation // expected-note {{in expansion of macro 'ThrowCancellation' on global function 'issue79039_2()' here}}
165+
func issue79039_2() throws(DecodingError) {}
166+
// CHECK-DIAGS: @__swiftmacro_9MacroUser12issue79039_217ThrowCancellationfMb_.swift:2:11: error: thrown expression type 'CancellationError' cannot be converted to error type 'DecodingError'
155167
#endif
156168

157169
@freestanding(declaration)

0 commit comments

Comments
 (0)