Skip to content

Commit 2e0bda5

Browse files
authored
Merge pull request #35933 from mininny/add-diagnostics-for-invalid-power-operator
[Diagnostics] Add diagnostic when using nonexistent '**' operator
2 parents cb1b825 + 56f4a7b commit 2e0bda5

File tree

5 files changed

+66
-1
lines changed

5 files changed

+66
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,9 @@ NOTE(unspaced_binary_operators_candidate,none,
854854
ERROR(unspaced_unary_operator,none,
855855
"unary operators must not be juxtaposed; parenthesize inner expression",
856856
())
857+
ERROR(nonexistent_power_operator,none,
858+
"no operator '**' is defined; did you mean 'pow(_:_:)'?",
859+
())
857860

858861
ERROR(cannot_find_in_scope,none,
859862
"cannot %select{find|find operator}1 %0 in scope", (DeclNameRef, bool))

lib/Sema/PreCheckExpr.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,30 @@ static bool diagnoseRangeOperatorMisspell(DiagnosticEngine &Diags,
238238
return false;
239239
}
240240

241+
static bool diagnoseNonexistentPowerOperator(DiagnosticEngine &Diags,
242+
UnresolvedDeclRefExpr *UDRE,
243+
DeclContext *DC) {
244+
auto name = UDRE->getName().getBaseIdentifier();
245+
if (!(name.isOperator() && name.is("**")))
246+
return false;
247+
248+
DC = DC->getModuleScopeContext();
249+
250+
auto &ctx = DC->getASTContext();
251+
DeclNameRef powerName(ctx.getIdentifier("pow"));
252+
253+
// Look if 'pow(_:_:)' exists within current context.
254+
auto lookUp = TypeChecker::lookupUnqualified(
255+
DC, powerName, UDRE->getLoc(), defaultUnqualifiedLookupOptions);
256+
if (lookUp) {
257+
Diags.diagnose(UDRE->getLoc(), diag::nonexistent_power_operator)
258+
.highlight(UDRE->getSourceRange());
259+
return true;
260+
}
261+
262+
return false;
263+
}
264+
241265
static bool diagnoseIncDecOperator(DiagnosticEngine &Diags,
242266
UnresolvedDeclRefExpr *UDRE) {
243267
auto name = UDRE->getName().getBaseIdentifier();
@@ -446,7 +470,8 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
446470
// e.g. (x*-4) that needs whitespace.
447471
if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) ||
448472
diagnoseIncDecOperator(Context.Diags, UDRE) ||
449-
diagnoseOperatorJuxtaposition(UDRE, DC)) {
473+
diagnoseOperatorJuxtaposition(UDRE, DC) ||
474+
diagnoseNonexistentPowerOperator(Context.Diags, UDRE, DC)) {
450475
return errorResult();
451476
}
452477

test/decl/func/operator.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,3 +422,11 @@ func testPostfixOperatorOnTuple<A, B>(a: A, b: B) {
422422
(§)((a, (b, b), a))
423423
_ = (a, ((), (b, (a, a), b)§), a)§
424424
}
425+
426+
func testNonexistentPowerOperatorWithPowFunctionOutOfScope() {
427+
func a(_ value: Double) { }
428+
let x: Double = 3.0
429+
let y: Double = 3.0
430+
let z: Double = x**y // expected-error {{cannot find operator '**' in scope}}
431+
let w: Double = a(x**2.0) // expected-error {{cannot find operator '**' in scope}}
432+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
infix operator **
4+
5+
func **(a: Double, b: Double) -> Double { return 0 }
6+
7+
func testDeclaredPowerOperator() {
8+
let x: Double = 3.0
9+
let y: Double = 3.0
10+
_ = 2.0**2.0 // no error
11+
_ = x**y // no error
12+
}
13+
14+
func testDeclaredPowerOperatorWithIncompatibleType() {
15+
let x: Int8 = 3
16+
let y: Int8 = 3
17+
_ = x**y // expected-error{{cannot convert value of type 'Int8' to expected argument type 'Double'}} expected-error{{cannot convert value of type 'Int8' to expected argument type 'Double'}}
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -I %t %s -verify
2+
3+
import cfuncs
4+
5+
func testNonexistentPowerOperatorWithPowFunctionInScope() {
6+
func a(_ value: Double) { }
7+
let x: Double = 3.0
8+
let y: Double = 3.0
9+
let z: Double = x**y // expected-error {{no operator '**' is defined; did you mean 'pow(_:_:)'?}}
10+
let w: Double = a(x**2.0) // expected-error {{no operator '**' is defined; did you mean 'pow(_:_:)'?}}
11+
}

0 commit comments

Comments
 (0)