Skip to content

Commit 172c4be

Browse files
authored
[Sema] Diagnose use of ambiguous property wrappers (swiftlang#33688)
1 parent 5525a78 commit 172c4be

File tree

7 files changed

+108
-13
lines changed

7 files changed

+108
-13
lines changed

include/swift/AST/DiagnosticsCommon.def

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,5 +188,19 @@ ERROR(scanner_find_cycle, none,
188188
ERROR(scanner_arguments_invalid, none,
189189
"dependencies scanner cannot be configured with arguments: '%0'", (StringRef))
190190

191+
//------------------------------------------------------------------------------
192+
// MARK: custom attribute diagnostics
193+
//------------------------------------------------------------------------------
194+
195+
ERROR(ambiguous_custom_attribute_ref,none,
196+
"ambiguous use of attribute %0", (Identifier))
197+
NOTE(ambiguous_custom_attribute_ref_fix,none,
198+
"use '%0.' to reference the attribute %1 in module %2",
199+
(StringRef, Identifier, Identifier))
200+
NOTE(found_attribute_candidate,none,
201+
"found this attribute", ())
202+
ERROR(unknown_attribute,none,
203+
"unknown attribute '%0'", (StringRef))
204+
191205
#define UNDEFINE_DIAGNOSTIC_MACROS
192206
#include "DefineDiagnosticMacros.h"

include/swift/AST/DiagnosticsParse.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,8 +1324,6 @@ ERROR(replace_equal_with_colon_for_value,none,
13241324
"'=' has been replaced with ':' in attribute arguments", ())
13251325
ERROR(expected_attribute_name,none,
13261326
"expected an attribute name", ())
1327-
ERROR(unknown_attribute,none,
1328-
"unknown attribute '%0'", (StringRef))
13291327
ERROR(unexpected_lparen_in_attribute,none,
13301328
"unexpected '(' in attribute '%0'", (StringRef))
13311329
ERROR(duplicate_attribute,none,

lib/AST/NameLookup.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2434,6 +2434,53 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator,
24342434
}
24352435
}
24362436

2437+
// If we have more than one attribute declaration, we have an ambiguity.
2438+
// So, emit an ambiguity diagnostic.
2439+
if (auto typeRepr = attr->getTypeRepr()) {
2440+
if (nominals.size() > 1) {
2441+
SmallVector<NominalTypeDecl *, 4> ambiguousCandidates;
2442+
// Filter out declarations that cannot be attributes.
2443+
for (auto decl : nominals) {
2444+
if (isa<ProtocolDecl>(decl)) {
2445+
continue;
2446+
}
2447+
ambiguousCandidates.push_back(decl);
2448+
}
2449+
if (ambiguousCandidates.size() > 1) {
2450+
auto attrName = nominals.front()->getName();
2451+
ctx.Diags.diagnose(typeRepr->getLoc(),
2452+
diag::ambiguous_custom_attribute_ref, attrName);
2453+
for (auto candidate : ambiguousCandidates) {
2454+
ctx.Diags.diagnose(candidate->getLoc(),
2455+
diag::found_attribute_candidate);
2456+
// If the candidate is a top-level attribute, let's suggest
2457+
// adding module name to resolve the ambiguity.
2458+
if (candidate->getDeclContext()->isModuleScopeContext()) {
2459+
auto moduleName = candidate->getParentModule()->getName();
2460+
ctx.Diags
2461+
.diagnose(typeRepr->getLoc(),
2462+
diag::ambiguous_custom_attribute_ref_fix,
2463+
moduleName.str(), attrName, moduleName)
2464+
.fixItInsert(typeRepr->getLoc(), moduleName.str().str() + ".");
2465+
}
2466+
}
2467+
return nullptr;
2468+
}
2469+
}
2470+
}
2471+
2472+
// There is no nominal type with this name, so complain about this being
2473+
// an unknown attribute.
2474+
std::string typeName;
2475+
if (auto typeRepr = attr->getTypeRepr()) {
2476+
llvm::raw_string_ostream out(typeName);
2477+
typeRepr->print(out);
2478+
} else {
2479+
typeName = attr->getType().getString();
2480+
}
2481+
2482+
ctx.Diags.diagnose(attr->getLocation(), diag::unknown_attribute, typeName);
2483+
24372484
return nullptr;
24382485
}
24392486

lib/Sema/TypeCheckAttr.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2901,18 +2901,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
29012901
auto nominal = evaluateOrDefault(
29022902
Ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr);
29032903

2904-
// If there is no nominal type with this name, complain about this being
2905-
// an unknown attribute.
29062904
if (!nominal) {
2907-
std::string typeName;
2908-
if (auto typeRepr = attr->getTypeRepr()) {
2909-
llvm::raw_string_ostream out(typeName);
2910-
typeRepr->print(out);
2911-
} else {
2912-
typeName = attr->getType().getString();
2913-
}
2914-
2915-
diagnose(attr->getLocation(), diag::unknown_attribute, typeName);
29162905
attr->setInvalid();
29172906
return;
29182907
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@propertyWrapper
2+
public struct Wrapper<Value> {
3+
public var wrappedValue: Value
4+
5+
public init(wrappedValue: Value) {
6+
self.wrappedValue = wrappedValue
7+
}
8+
}
9+
10+
@_functionBuilder
11+
public struct Builder {
12+
static func buildBlock<T>(_ component: T) -> T { component }
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@propertyWrapper
2+
public struct Wrapper<Value> {
3+
public var wrappedValue: Value
4+
5+
public init(wrappedValue: Value) {
6+
self.wrappedValue = wrappedValue
7+
}
8+
}
9+
10+
@_functionBuilder
11+
public struct Builder {
12+
static func buildBlock<T>(_ component: T) -> T { component }
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/Inputs/custom_attrs_A.swift
3+
// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/Inputs/custom_attrs_B.swift
4+
// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unknown -I %t %s
5+
import custom_attrs_A
6+
import custom_attrs_B
7+
8+
// SR-13470
9+
10+
struct Test {
11+
@Wrapper var x: Int = 17
12+
// expected-error@-1 {{ambiguous use of attribute 'Wrapper'}}
13+
// expected-note@-2 {{use 'custom_attrs_A.' to reference the attribute 'Wrapper' in module 'custom_attrs_A'}} {{4-4=custom_attrs_A.}}
14+
// expected-note@-3 {{use 'custom_attrs_B.' to reference the attribute 'Wrapper' in module 'custom_attrs_B'}} {{4-4=custom_attrs_B.}}
15+
16+
init(@Builder closure: () -> Int) {}
17+
// expected-error@-1 {{ambiguous use of attribute 'Builder'}}
18+
// expected-note@-2 {{use 'custom_attrs_A.' to reference the attribute 'Builder' in module 'custom_attrs_A'}} {{9-9=custom_attrs_A.}}
19+
// expected-note@-3 {{use 'custom_attrs_B.' to reference the attribute 'Builder' in module 'custom_attrs_B'}} {{9-9=custom_attrs_B.}}
20+
}
21+

0 commit comments

Comments
 (0)