Skip to content

Commit 7df141c

Browse files
authored
Merge pull request #29341 from brentdax/path-dependence
[MiscDiagnostics] Warn if magic identifiers don’t match
2 parents aabc849 + c504eb1 commit 7df141c

File tree

11 files changed

+419
-50
lines changed

11 files changed

+419
-50
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7405,10 +7405,12 @@ inline EnumElementDecl *EnumDecl::getUniqueElement(bool hasValue) const {
74057405
return result;
74067406
}
74077407

7408-
/// Retrieve the parameter list for a given declaration.
7408+
/// Retrieve the parameter list for a given declaration, or nullputr if there
7409+
/// is none.
74097410
ParameterList *getParameterList(ValueDecl *source);
74107411

7411-
/// Retrieve parameter declaration from the given source at given index.
7412+
/// Retrieve parameter declaration from the given source at given index, or
7413+
/// nullptr if the source does not have a parameter list.
74127414
const ParamDecl *getParameterAt(const ValueDecl *source, unsigned index);
74137415

74147416
/// Display Decl subclasses.

include/swift/AST/DiagnosticsSema.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4678,6 +4678,17 @@ WARNING(observe_keypath_property_not_objc_dynamic, none,
46784678
"passing reference to non-'@objc dynamic' property %0 to KVO method %1 "
46794679
"may lead to unexpected behavior or runtime trap", (DeclName, DeclName))
46804680

4681+
WARNING(default_magic_identifier_mismatch, none,
4682+
"parameter %0 with default argument '%1' passed to parameter %2, whose "
4683+
"default argument is '%3'",
4684+
(Identifier, StringRef, Identifier, StringRef))
4685+
NOTE(change_caller_default_to_match_callee, none,
4686+
"did you mean for parameter %0 to default to '%1'?",
4687+
(Identifier, StringRef))
4688+
NOTE(silence_default_magic_identifier_mismatch, none,
4689+
"add parentheses to silence this warning", ())
4690+
4691+
46814692
//------------------------------------------------------------------------------
46824693
// MARK: Debug diagnostics
46834694
//------------------------------------------------------------------------------

include/swift/AST/Expr.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ class alignas(8) Expr {
503503

504504
/// Retrieves the declaration that is being referenced by this
505505
/// expression, if any.
506-
ConcreteDeclRef getReferencedDecl() const;
506+
ConcreteDeclRef getReferencedDecl(bool stopAtParenExpr = false) const;
507507

508508
/// Determine whether this expression is 'super', possibly converted to
509509
/// a base class.
@@ -1049,6 +1049,20 @@ class MagicIdentifierLiteralExpr : public LiteralExpr {
10491049
enum Kind : unsigned {
10501050
File, FilePath, Line, Column, Function, DSOHandle
10511051
};
1052+
1053+
static StringRef getKindString(MagicIdentifierLiteralExpr::Kind value) {
1054+
switch (value) {
1055+
case File: return "#file";
1056+
case FilePath: return "#filePath";
1057+
case Function: return "#function";
1058+
case Line: return "#line";
1059+
case Column: return "#column";
1060+
case DSOHandle: return "#dsohandle";
1061+
}
1062+
1063+
llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in getKindString.");
1064+
}
1065+
10521066
private:
10531067
SourceLoc Loc;
10541068
ConcreteDeclRef BuiltinInitializer;

lib/AST/ASTDumper.cpp

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -314,19 +314,6 @@ static StringRef getDefaultArgumentKindString(DefaultArgumentKind value) {
314314
llvm_unreachable("Unhandled DefaultArgumentKind in switch.");
315315
}
316316
static StringRef
317-
getMagicIdentifierLiteralExprKindString(MagicIdentifierLiteralExpr::Kind value) {
318-
switch (value) {
319-
case MagicIdentifierLiteralExpr::File: return "#file";
320-
case MagicIdentifierLiteralExpr::FilePath: return "#filePath";
321-
case MagicIdentifierLiteralExpr::Function: return "#function";
322-
case MagicIdentifierLiteralExpr::Line: return "#line";
323-
case MagicIdentifierLiteralExpr::Column: return "#column";
324-
case MagicIdentifierLiteralExpr::DSOHandle: return "#dsohandle";
325-
}
326-
327-
llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in switch.");
328-
}
329-
static StringRef
330317
getObjCSelectorExprKindString(ObjCSelectorExpr::ObjCSelectorKind value) {
331318
switch (value) {
332319
case ObjCSelectorExpr::Method: return "method";
@@ -1957,7 +1944,7 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
19571944
}
19581945
void visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E) {
19591946
printCommon(E, "magic_identifier_literal_expr")
1960-
<< " kind=" << getMagicIdentifierLiteralExprKindString(E->getKind());
1947+
<< " kind=" << MagicIdentifierLiteralExpr::getKindString(E->getKind());
19611948

19621949
if (E->isString()) {
19631950
OS << " encoding="

lib/AST/Decl.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6623,14 +6623,19 @@ ParameterList *swift::getParameterList(ValueDecl *source) {
66236623
return AFD->getParameters();
66246624
} else if (auto *EED = dyn_cast<EnumElementDecl>(source)) {
66256625
return EED->getParameterList();
6626-
} else {
6627-
return cast<SubscriptDecl>(source)->getIndices();
6626+
} else if (auto *SD = dyn_cast<SubscriptDecl>(source)) {
6627+
return SD->getIndices();
66286628
}
6629+
6630+
return nullptr;
66296631
}
66306632

66316633
const ParamDecl *swift::getParameterAt(const ValueDecl *source,
66326634
unsigned index) {
6633-
return getParameterList(const_cast<ValueDecl *>(source))->get(index);
6635+
if (auto *params = getParameterList(const_cast<ValueDecl *>(source))) {
6636+
return params->get(index);
6637+
}
6638+
return nullptr;
66346639
}
66356640

66366641
Type AbstractFunctionDecl::getMethodInterfaceType() const {

lib/AST/Expr.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ DeclRefExpr *Expr::getMemberOperatorRef() {
228228
return operatorRef;
229229
}
230230

231-
ConcreteDeclRef Expr::getReferencedDecl() const {
231+
ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const {
232232
switch (getKind()) {
233233
// No declaration reference.
234234
#define NO_REFERENCE(Id) case ExprKind::Id: return ConcreteDeclRef()
@@ -237,7 +237,8 @@ ConcreteDeclRef Expr::getReferencedDecl() const {
237237
return cast<Id##Expr>(this)->Getter()
238238
#define PASS_THROUGH_REFERENCE(Id, GetSubExpr) \
239239
case ExprKind::Id: \
240-
return cast<Id##Expr>(this)->GetSubExpr()->getReferencedDecl()
240+
return cast<Id##Expr>(this) \
241+
->GetSubExpr()->getReferencedDecl(stopAtParenExpr)
241242

242243
NO_REFERENCE(Error);
243244
NO_REFERENCE(NilLiteral);
@@ -275,7 +276,12 @@ ConcreteDeclRef Expr::getReferencedDecl() const {
275276
NO_REFERENCE(UnresolvedMember);
276277
NO_REFERENCE(UnresolvedDot);
277278
NO_REFERENCE(Sequence);
278-
PASS_THROUGH_REFERENCE(Paren, getSubExpr);
279+
280+
case ExprKind::Paren:
281+
if (stopAtParenExpr) return ConcreteDeclRef();
282+
return cast<ParenExpr>(this)
283+
->getSubExpr()->getReferencedDecl(stopAtParenExpr);
284+
279285
PASS_THROUGH_REFERENCE(DotSelf, getSubExpr);
280286
PASS_THROUGH_REFERENCE(Try, getSubExpr);
281287
PASS_THROUGH_REFERENCE(ForceTry, getSubExpr);

lib/Sema/MiscDiagnostics.cpp

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -194,28 +194,30 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
194194
callee = dynamicMRE->getMember();
195195
}
196196

197-
visitArguments(Call, [&](unsigned argIndex, Expr *arg) {
198-
// InOutExprs can be wrapped in some implicit casts.
199-
Expr *unwrapped = arg;
200-
if (auto *IIO = dyn_cast<InjectIntoOptionalExpr>(arg))
201-
unwrapped = IIO->getSubExpr();
202-
203-
if (isa<InOutToPointerExpr>(unwrapped) ||
204-
isa<ArrayToPointerExpr>(unwrapped) ||
205-
isa<ErasureExpr>(unwrapped)) {
206-
auto operand =
207-
cast<ImplicitConversionExpr>(unwrapped)->getSubExpr();
208-
if (auto *IOE = dyn_cast<InOutExpr>(operand))
209-
operand = IOE->getSubExpr();
210-
211-
// Also do some additional work based on how the function uses
212-
// the argument.
213-
if (callee) {
197+
if (callee) {
198+
visitArguments(Call, [&](unsigned argIndex, Expr *arg) {
199+
checkMagicIdentifierMismatch(callee, uncurryLevel, argIndex, arg);
200+
201+
// InOutExprs can be wrapped in some implicit casts.
202+
Expr *unwrapped = arg;
203+
if (auto *IIO = dyn_cast<InjectIntoOptionalExpr>(arg))
204+
unwrapped = IIO->getSubExpr();
205+
206+
if (isa<InOutToPointerExpr>(unwrapped) ||
207+
isa<ArrayToPointerExpr>(unwrapped) ||
208+
isa<ErasureExpr>(unwrapped)) {
209+
auto operand =
210+
cast<ImplicitConversionExpr>(unwrapped)->getSubExpr();
211+
if (auto *IOE = dyn_cast<InOutExpr>(operand))
212+
operand = IOE->getSubExpr();
213+
214+
// Also do some additional work based on how the function uses
215+
// the argument.
214216
checkConvertedPointerArgument(callee, uncurryLevel, argIndex,
215217
unwrapped, operand);
216218
}
217-
}
218-
});
219+
});
220+
}
219221
}
220222

221223
// If we have an assignment expression, scout ahead for acceptable _'s.
@@ -421,6 +423,91 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
421423
// Otherwise, we can't support this.
422424
}
423425

426+
void checkMagicIdentifierMismatch(ConcreteDeclRef callee,
427+
unsigned uncurryLevel,
428+
unsigned argIndex,
429+
Expr *arg) {
430+
// We only care about args in the arg list.
431+
if (uncurryLevel != (callee.getDecl()->hasCurriedSelf() ? 1 : 0))
432+
return;
433+
434+
// Get underlying params for both callee and caller, if declared.
435+
auto *calleeParam = getParameterAt(callee.getDecl(), argIndex);
436+
auto *callerParam = dyn_cast_or_null<ParamDecl>(
437+
arg->getReferencedDecl(/*stopAtParenExpr=*/true).getDecl()
438+
);
439+
440+
// (Otherwise, we don't need to do anything.)
441+
if (!calleeParam || !callerParam)
442+
return;
443+
444+
auto calleeDefaultArg = getMagicIdentifierDefaultArgKind(calleeParam);
445+
auto callerDefaultArg = getMagicIdentifierDefaultArgKind(callerParam);
446+
447+
// If one of the parameters doesn't have a default arg, or they both have
448+
// the same one, everything's fine.
449+
if (!calleeDefaultArg || !callerDefaultArg ||
450+
*calleeDefaultArg == *callerDefaultArg)
451+
return;
452+
453+
StringRef calleeDefaultArgString =
454+
MagicIdentifierLiteralExpr::getKindString(*calleeDefaultArg);
455+
StringRef callerDefaultArgString =
456+
MagicIdentifierLiteralExpr::getKindString(*callerDefaultArg);
457+
458+
// Emit main warning
459+
Ctx.Diags.diagnose(arg->getLoc(), diag::default_magic_identifier_mismatch,
460+
callerParam->getName(), callerDefaultArgString,
461+
calleeParam->getName(), calleeDefaultArgString);
462+
463+
// Add "change caller default arg" fixit
464+
SourceLoc callerDefaultArgLoc =
465+
callerParam->getStructuralDefaultExpr()->getLoc();
466+
Ctx.Diags.diagnose(callerDefaultArgLoc,
467+
diag::change_caller_default_to_match_callee,
468+
callerParam->getName(), calleeDefaultArgString)
469+
.fixItReplace(callerDefaultArgLoc, calleeDefaultArgString);
470+
471+
// Add "silence with parens" fixit
472+
Ctx.Diags.diagnose(arg->getLoc(),
473+
diag::silence_default_magic_identifier_mismatch)
474+
.fixItInsert(arg->getStartLoc(), "(")
475+
.fixItInsertAfter(arg->getEndLoc(), ")");
476+
477+
// Point to callee parameter
478+
Ctx.Diags.diagnose(calleeParam, diag::decl_declared_here,
479+
calleeParam->getFullName());
480+
}
481+
482+
Optional<MagicIdentifierLiteralExpr::Kind>
483+
getMagicIdentifierDefaultArgKind(const ParamDecl *param) {
484+
switch (param->getDefaultArgumentKind()) {
485+
case DefaultArgumentKind::Column:
486+
return MagicIdentifierLiteralExpr::Kind::Column;
487+
case DefaultArgumentKind::DSOHandle:
488+
return MagicIdentifierLiteralExpr::Kind::DSOHandle;
489+
case DefaultArgumentKind::File:
490+
return MagicIdentifierLiteralExpr::Kind::File;
491+
case DefaultArgumentKind::FilePath:
492+
return MagicIdentifierLiteralExpr::Kind::FilePath;
493+
case DefaultArgumentKind::Function:
494+
return MagicIdentifierLiteralExpr::Kind::Function;
495+
case DefaultArgumentKind::Line:
496+
return MagicIdentifierLiteralExpr::Kind::Line;
497+
498+
case DefaultArgumentKind::None:
499+
case DefaultArgumentKind::Normal:
500+
case DefaultArgumentKind::Inherited:
501+
case DefaultArgumentKind::NilLiteral:
502+
case DefaultArgumentKind::EmptyArray:
503+
case DefaultArgumentKind::EmptyDictionary:
504+
case DefaultArgumentKind::StoredProperty:
505+
return None;
506+
}
507+
508+
llvm_unreachable("Unhandled DefaultArgumentKind in "
509+
"getMagicIdentifierDefaultArgKind");
510+
}
424511

425512
void checkUseOfModule(DeclRefExpr *E) {
426513
// Allow module values as a part of:

lib/Sema/TypeCheckStmt.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,14 +1390,8 @@ static void diagnoseIgnoredLiteral(ASTContext &Ctx, LiteralExpr *LE) {
13901390
case ExprKind::StringLiteral: return "string";
13911391
case ExprKind::InterpolatedStringLiteral: return "string";
13921392
case ExprKind::MagicIdentifierLiteral:
1393-
switch (cast<MagicIdentifierLiteralExpr>(LE)->getKind()) {
1394-
case MagicIdentifierLiteralExpr::Kind::File: return "#file";
1395-
case MagicIdentifierLiteralExpr::Kind::FilePath: return "#filePath";
1396-
case MagicIdentifierLiteralExpr::Kind::Line: return "#line";
1397-
case MagicIdentifierLiteralExpr::Kind::Column: return "#column";
1398-
case MagicIdentifierLiteralExpr::Kind::Function: return "#function";
1399-
case MagicIdentifierLiteralExpr::Kind::DSOHandle: return "#dsohandle";
1400-
}
1393+
return MagicIdentifierLiteralExpr::getKindString(
1394+
cast<MagicIdentifierLiteralExpr>(LE)->getKind());
14011395
case ExprKind::NilLiteral: return "nil";
14021396
case ExprKind::ObjectLiteral: return "object";
14031397

0 commit comments

Comments
 (0)