Skip to content

Commit 71ca9c8

Browse files
committed
[Macros] Diagnose when we forget to provide macro arguments.
Unlike functions, you can't curry macros; diagnose when one omits the arguments in a macro expansion of a macro that has a parameter list.
1 parent 6bad02c commit 71ca9c8

File tree

9 files changed

+135
-8
lines changed

9 files changed

+135
-8
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6796,6 +6796,8 @@ ERROR(macro_without_context,none,
67966796
(DeclName))
67976797
ERROR(macro_expansion_missing_pound,none,
67986798
"expansion of macro %0 requires leading '#'", (DeclName))
6799+
ERROR(macro_expansion_missing_arguments,none,
6800+
"expansion of macro %0 requires arguments", (DeclName))
67996801

68006802
//------------------------------------------------------------------------------
68016803
// MARK: Move Only Errors

include/swift/Sema/CSFix.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,9 @@ enum class FixKind : uint8_t {
418418

419419
/// Macro without leading #.
420420
MacroMissingPound,
421+
422+
/// Macro that has parameters but was not provided with any arguments.
423+
MacroMissingArguments,
421424
};
422425

423426
class ConstraintFix {
@@ -3257,6 +3260,32 @@ class MacroMissingPound final : public ConstraintFix {
32573260
}
32583261
};
32593262

3263+
class MacroMissingArguments final : public ConstraintFix {
3264+
MacroDecl *macro;
3265+
3266+
MacroMissingArguments(ConstraintSystem &cs, MacroDecl *macro,
3267+
ConstraintLocator *locator)
3268+
: ConstraintFix(cs, FixKind::MacroMissingArguments, locator),
3269+
macro(macro) { }
3270+
3271+
public:
3272+
std::string getName() const override { return "macro missing arguments"; }
3273+
3274+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3275+
3276+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
3277+
return diagnose(*commonFixes.front().first);
3278+
}
3279+
3280+
static MacroMissingArguments *
3281+
create(ConstraintSystem &cs, MacroDecl *macro,
3282+
ConstraintLocator *locator);
3283+
3284+
static bool classof(ConstraintFix *fix) {
3285+
return fix->getKind() == FixKind::MacroMissingArguments;
3286+
}
3287+
};
3288+
32603289
} // end namespace constraints
32613290
} // end namespace swift
32623291

lib/Sema/CSDiagnostics.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8502,3 +8502,32 @@ bool AddMissingMacroPound::diagnoseAsError() {
85028502
.fixItInsert(getLoc(), "#");
85038503
return true;
85048504
}
8505+
8506+
bool AddMissingMacroArguments::diagnoseAsError() {
8507+
std::string argumentString;
8508+
{
8509+
llvm::raw_string_ostream out(argumentString);
8510+
out << "(";
8511+
llvm::interleave(
8512+
macro->parameterList->begin(), macro->parameterList->end(),
8513+
[&](ParamDecl *param) {
8514+
if (!param->getArgumentName().empty()) {
8515+
out << param->getArgumentName() << ": ";
8516+
}
8517+
8518+
out << "<#" << param->getInterfaceType().getString() << "#" << ">";
8519+
},
8520+
[&] {
8521+
out << ", ";
8522+
});
8523+
out << ")";
8524+
}
8525+
8526+
auto insertLoc = getRawAnchor().getEndLoc();
8527+
emitDiagnostic(diag::macro_expansion_missing_arguments, macro->getName())
8528+
.fixItInsertAfter(insertLoc, argumentString);
8529+
macro->diagnose(
8530+
diag::kind_declname_declared_here, macro->getDescriptiveKind(),
8531+
macro->getName());
8532+
return true;
8533+
}

lib/Sema/CSDiagnostics.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2877,6 +2877,29 @@ class AddMissingMacroPound final : public FailureDiagnostic {
28772877
bool diagnoseAsError() override;
28782878
};
28792879

2880+
/// Diagnose situations where we end up type checking a reference to a macro
2881+
/// that has parameters, but was not provided any arguments.
2882+
///
2883+
/// \code
2884+
/// func print(_ value: Any)
2885+
/// @expression macro print<Value...>(_ value: Value...)
2886+
///
2887+
/// func test(e: E) {
2888+
/// #print
2889+
/// }
2890+
/// \endcode
2891+
class AddMissingMacroArguments final : public FailureDiagnostic {
2892+
MacroDecl *macro;
2893+
2894+
public:
2895+
AddMissingMacroArguments(const Solution &solution, MacroDecl *macro,
2896+
ConstraintLocator *locator)
2897+
: FailureDiagnostic(solution, locator),
2898+
macro(macro) { }
2899+
2900+
bool diagnoseAsError() override;
2901+
};
2902+
28802903
} // end namespace constraints
28812904
} // end namespace swift
28822905

lib/Sema/CSFix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2613,3 +2613,15 @@ MacroMissingPound::create(ConstraintSystem &cs, MacroDecl *macro,
26132613
ConstraintLocator *locator) {
26142614
return new (cs.getAllocator()) MacroMissingPound(cs, macro, locator);
26152615
}
2616+
2617+
bool MacroMissingArguments::diagnose(const Solution &solution,
2618+
bool asNote) const {
2619+
AddMissingMacroArguments failure(solution, macro, getLocator());
2620+
return failure.diagnose(asNote);
2621+
}
2622+
2623+
MacroMissingArguments *
2624+
MacroMissingArguments::create(ConstraintSystem &cs, MacroDecl *macro,
2625+
ConstraintLocator *locator) {
2626+
return new (cs.getAllocator()) MacroMissingArguments(cs, macro, locator);
2627+
}

lib/Sema/CSGen.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3782,9 +3782,16 @@ namespace {
37823782
#if SWIFT_SWIFT_PARSER
37833783
auto &ctx = CS.getASTContext();
37843784
if (ctx.LangOpts.hasFeature(Feature::Macros)) {
3785+
auto locator = CS.getConstraintLocator(expr);
3786+
3787+
// For calls, set up the argument list.
3788+
bool isCall = expr->getArgs() != nullptr;
3789+
if (isCall) {
3790+
CS.associateArgumentList(locator, expr->getArgs());
3791+
}
3792+
37853793
// Look up the macros with this name.
37863794
auto macroIdent = expr->getMacroName().getBaseIdentifier();
3787-
bool isCall = expr->getArgs() != nullptr;
37883795
FunctionRefKind functionRefKind = isCall ? FunctionRefKind::SingleApply
37893796
: FunctionRefKind::Unapplied;
37903797
auto macros = lookupMacros(
@@ -3798,7 +3805,6 @@ namespace {
37983805
}
37993806

38003807
// Introduce an overload set for the macro reference.
3801-
auto locator = CS.getConstraintLocator(expr);
38023808
auto macroRefType = Type(CS.createTypeVariable(locator, 0));
38033809
CS.addOverloadSet(macroRefType, macros, CurDC, locator);
38043810

@@ -3814,10 +3820,8 @@ namespace {
38143820
if (!isCall)
38153821
return macroRefType;
38163822

3817-
// For calls, set up the argument list and form the applicable-function
3818-
// constraint. The result type is the result of that call.
3819-
CS.associateArgumentList(locator, expr->getArgs());
3820-
3823+
// For calls, form the applicable-function constraint. The result type
3824+
// is the result of that call.
38213825
SmallVector<AnyFunctionType::Param, 8> params;
38223826
getMatchingParams(expr->getArgs(), params);
38233827

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13810,7 +13810,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1381013810
case FixKind::IgnoreKeyPathContextualMismatch:
1381113811
case FixKind::NotCompileTimeConst:
1381213812
case FixKind::RenameConflictingPatternVariables:
13813-
case FixKind::MacroMissingPound: {
13813+
case FixKind::MacroMissingPound:
13814+
case FixKind::MacroMissingArguments: {
1381413815
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1381513816
}
1381613817
case FixKind::IgnoreInvalidASTNode: {

lib/Sema/ConstraintSystem.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3569,12 +3569,34 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
35693569
}
35703570
}
35713571

3572-
// If we have a macro, it can only be used in an expansion.
3572+
// If we have a macro, check for correct usage.
35733573
if (auto macro = dyn_cast<MacroDecl>(decl)) {
3574+
// Macro can only be used in an expansion. If we end up here, it's
3575+
// because we found a macro but are missing the leading '#'.
35743576
if (!locator->isForMacroExpansion()) {
35753577
// Record a fix here
35763578
(void)recordFix(MacroMissingPound::create(*this, macro, locator));
35773579
}
3580+
3581+
// If the macro has parameters but wasn't provided with any arguments,
3582+
// introduce a fix to add the arguments.
3583+
bool isCall;
3584+
switch (choice.getFunctionRefKind()) {
3585+
case FunctionRefKind::SingleApply:
3586+
case FunctionRefKind::DoubleApply:
3587+
isCall = true;
3588+
break;
3589+
3590+
case FunctionRefKind::Unapplied:
3591+
case FunctionRefKind::Compound:
3592+
// Note: macros don't have compound name references.
3593+
isCall = false;
3594+
break;
3595+
}
3596+
if (macro->parameterList && !isCall) {
3597+
// Record a fix here
3598+
(void)recordFix(MacroMissingArguments::create(*this, macro, locator));
3599+
}
35783600
}
35793601
}
35803602

test/Macros/macros_diagnostics.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ func overloaded1(_ p: Any) { }
4747
@expression macro notOverloaded1(_ p: P) = MissingModule.MissingType // expected-note{{'notOverloaded1' previously declared here}}
4848
@expression macro notOverloaded1(_ p: P) = MissingModule.MissingOtherType // expected-error{{invalid redeclaration of 'notOverloaded1'}}
4949

50+
@expression macro intIdentity(value: Int, _: Float) -> Int = MissingModule.MissingType
51+
// expected-note@-1{{macro 'intIdentity(value:_:)' declared here}}
52+
5053
func testDiags(a: Int, b: Int) {
5154
// FIXME: Bad diagnostic.
5255
let s = #stringify<Int, Int>(a + b) // expected-error{{type of expression is ambiguous without more context}}
@@ -66,6 +69,8 @@ func testDiags(a: Int, b: Int) {
6669
_ = stringify(a + b)
6770
// expected-error@-1{{expansion of macro 'stringify' requires leading '#'}}{{7-7=#}}
6871

72+
_ = #intIdentity // expected-error{{expansion of macro 'intIdentity(value:_:)' requires arguments}}{{19-19=(value: <#Int#>, <#Float#>)}}
73+
6974
overloaded1(a) // okay, calls the function
7075
#overloaded1(a) // expected-error{{argument type 'Int' does not conform to expected type 'P'}}
7176
}

0 commit comments

Comments
 (0)