mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-14 14:56:47 +00:00
Reject varargs '...' in function prototype if there are more parameters after
it. Diagnose with recovery if it appears after a function parameter that was obviously supposed to be a parameter pack. Otherwise, warn if it immediately follows a function parameter pack, because the user most likely didn't intend to write a parameter pack followed by a C-style varargs ellipsis. This warning can be syntactically disabled by using ", ..." instead of "...". llvm-svn: 215408
This commit is contained in:
parent
76502d8417
commit
36ee9fb219
@ -503,6 +503,17 @@ def note_bracket_depth : Note<
|
||||
def err_misplaced_ellipsis_in_declaration : Error<
|
||||
"'...' must %select{immediately precede declared identifier|"
|
||||
"be innermost component of anonymous pack declaration}0">;
|
||||
def warn_misplaced_ellipsis_vararg : Warning<
|
||||
"'...' in this location creates a C-style varargs function"
|
||||
"%select{, not a function parameter pack|}0">,
|
||||
InGroup<DiagGroup<"ambiguous-ellipsis">>;
|
||||
def note_misplaced_ellipsis_vararg_existing_ellipsis : Note<
|
||||
"preceding '...' declares a function parameter pack">;
|
||||
def note_misplaced_ellipsis_vararg_add_ellipsis : Note<
|
||||
"place '...' %select{immediately before declared identifier|here}0 "
|
||||
"to declare a function parameter pack">;
|
||||
def note_misplaced_ellipsis_vararg_add_comma : Note<
|
||||
"insert ',' before '...' to silence this warning">;
|
||||
def ext_abstract_pack_declarator_parens : ExtWarn<
|
||||
"ISO C++11 requires a parenthesized pack declaration to have a name">,
|
||||
InGroup<DiagGroup<"anonymous-pack-parens">>;
|
||||
|
@ -5585,6 +5585,10 @@ public:
|
||||
// C++ Variadic Templates (C++0x [temp.variadic])
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Determine whether an unexpanded parameter pack might be permitted in this
|
||||
/// location. Useful for error recovery.
|
||||
bool isUnexpandedParameterPackPermitted();
|
||||
|
||||
/// \brief The context in which an unexpanded parameter pack is
|
||||
/// being diagnosed.
|
||||
///
|
||||
|
@ -5426,6 +5426,15 @@ void Parser::ParseParameterDeclarationClause(
|
||||
// Otherwise, we have something. Add it and let semantic analysis try
|
||||
// to grok it and add the result to the ParamInfo we are building.
|
||||
|
||||
// Last chance to recover from a misplaced ellipsis in an attempted
|
||||
// parameter pack declaration.
|
||||
if (Tok.is(tok::ellipsis) &&
|
||||
(NextToken().isNot(tok::r_paren) ||
|
||||
(!ParmDeclarator.getEllipsisLoc().isValid() &&
|
||||
!Actions.isUnexpandedParameterPackPermitted())) &&
|
||||
Actions.containsUnexpandedParameterPacks(ParmDeclarator))
|
||||
DiagnoseMisplacedEllipsisInDeclarator(ConsumeToken(), ParmDeclarator);
|
||||
|
||||
// Inform the actions module about the parameter declarator, so it gets
|
||||
// added to the current scope.
|
||||
Decl *Param = Actions.ActOnParamDeclarator(getCurScope(),
|
||||
@ -5492,12 +5501,34 @@ void Parser::ParseParameterDeclarationClause(
|
||||
Param, DefArgToks));
|
||||
}
|
||||
|
||||
if (TryConsumeToken(tok::ellipsis, EllipsisLoc) &&
|
||||
!getLangOpts().CPlusPlus) {
|
||||
// We have ellipsis without a preceding ',', which is ill-formed
|
||||
// in C. Complain and provide the fix.
|
||||
Diag(EllipsisLoc, diag::err_missing_comma_before_ellipsis)
|
||||
if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) {
|
||||
if (!getLangOpts().CPlusPlus) {
|
||||
// We have ellipsis without a preceding ',', which is ill-formed
|
||||
// in C. Complain and provide the fix.
|
||||
Diag(EllipsisLoc, diag::err_missing_comma_before_ellipsis)
|
||||
<< FixItHint::CreateInsertion(EllipsisLoc, ", ");
|
||||
} else if (ParmDeclarator.getEllipsisLoc().isValid() ||
|
||||
Actions.containsUnexpandedParameterPacks(ParmDeclarator)) {
|
||||
// It looks like this was supposed to be a parameter pack. Warn and
|
||||
// point out where the ellipsis should have gone.
|
||||
SourceLocation ParmEllipsis = ParmDeclarator.getEllipsisLoc();
|
||||
Diag(EllipsisLoc, diag::warn_misplaced_ellipsis_vararg)
|
||||
<< ParmEllipsis.isValid() << ParmEllipsis;
|
||||
if (ParmEllipsis.isValid()) {
|
||||
Diag(ParmEllipsis,
|
||||
diag::note_misplaced_ellipsis_vararg_existing_ellipsis);
|
||||
} else {
|
||||
Diag(ParmDeclarator.getIdentifierLoc(),
|
||||
diag::note_misplaced_ellipsis_vararg_add_ellipsis)
|
||||
<< FixItHint::CreateInsertion(ParmDeclarator.getIdentifierLoc(),
|
||||
"...")
|
||||
<< !ParmDeclarator.hasName();
|
||||
}
|
||||
Diag(EllipsisLoc, diag::note_misplaced_ellipsis_vararg_add_comma)
|
||||
<< FixItHint::CreateInsertion(EllipsisLoc, ", ");
|
||||
}
|
||||
|
||||
// We can't have any more parameters after an ellipsis.
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -197,6 +197,20 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
/// \brief Determine whether it's possible for an unexpanded parameter pack to
|
||||
/// be valid in this location. This only happens when we're in a declaration
|
||||
/// that is nested within an expression that could be expanded, such as a
|
||||
/// lambda-expression within a function call.
|
||||
///
|
||||
/// This is conservatively correct, but may claim that some unexpanded packs are
|
||||
/// permitted when they are not.
|
||||
bool Sema::isUnexpandedParameterPackPermitted() {
|
||||
for (auto *SI : FunctionScopes)
|
||||
if (isa<sema::LambdaScopeInfo>(SI))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Diagnose all of the unexpanded parameter packs in the given
|
||||
/// vector.
|
||||
bool
|
||||
|
@ -1,5 +1,4 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
template<typename T> struct identity;
|
||||
template<typename ...Types> struct tuple;
|
||||
@ -22,7 +21,7 @@ template<typename T> struct is_same<T, T> {
|
||||
template<typename T, typename ...Types>
|
||||
struct X0 {
|
||||
typedef identity<T(Types...)> function_pack_1;
|
||||
typedef identity<T(Types......)> variadic_function_pack_1;
|
||||
typedef identity<T(Types......)> variadic_function_pack_1; // expected-warning {{varargs}} expected-note {{pack}} expected-note {{insert ','}}
|
||||
typedef identity<T(T...)> variadic_1;
|
||||
typedef tuple<T(Types, ...)...> template_arg_expansion_1;
|
||||
};
|
||||
|
@ -243,7 +243,7 @@ namespace FunctionTypes {
|
||||
};
|
||||
|
||||
template<typename R, typename ...Types>
|
||||
struct Arity<R(Types......)> {
|
||||
struct Arity<R(Types......)> { // expected-warning {{varargs}} expected-note {{pack}} expected-note {{insert ','}}
|
||||
static const unsigned value = sizeof...(Types);
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
void f(...) {
|
||||
int g(int(...));
|
||||
// FIXME: There's no disambiguation here; this is unambiguous.
|
||||
int g(int(...)); // expected-warning {{disambiguated}} expected-note {{paren}}
|
||||
}
|
||||
|
||||
void h(int n..., int m); // expected-error {{expected ')'}} expected-note {{to match}}
|
||||
|
@ -7,3 +7,40 @@ struct S {
|
||||
|
||||
template <typename Ty = char>
|
||||
static_assert(sizeof(Ty) != 1, "Not a char"); // expected-error {{a static_assert declaration cannot be a template}}
|
||||
|
||||
namespace Ellipsis {
|
||||
template<typename ...T> void f(T t..., int n); // expected-error {{must immediately precede declared identifier}}
|
||||
template<typename ...T> void f(int n, T t...); // expected-error {{must immediately precede declared identifier}}
|
||||
template<typename ...T> void f(int n, T t, ...); // expected-error {{unexpanded parameter pack}}
|
||||
template<typename ...T> void f() {
|
||||
f([]{
|
||||
void g(T
|
||||
t // expected-note {{place '...' immediately before declared identifier to declare a function parameter pack}}
|
||||
... // expected-warning {{'...' in this location creates a C-style varargs function, not a function parameter pack}}
|
||||
// expected-note@-1 {{insert ',' before '...' to silence this warning}}
|
||||
);
|
||||
void h(T (&
|
||||
) // expected-note {{place '...' here to declare a function parameter pack}}
|
||||
... // expected-warning {{'...' in this location creates a C-style varargs function, not a function parameter pack}}
|
||||
// expected-note@-1 {{insert ',' before '...' to silence this warning}}
|
||||
);
|
||||
void i(T (&), ...);
|
||||
}...);
|
||||
}
|
||||
template<typename ...T> struct S {
|
||||
void f(T t...); // expected-error {{must immediately precede declared identifier}}
|
||||
void f(T ... // expected-note {{preceding '...' declares a function parameter pack}}
|
||||
t...); // expected-warning-re {{'...' in this location creates a C-style varargs function{{$}}}}
|
||||
// expected-note@-1 {{insert ',' before '...' to silence this warning}}
|
||||
};
|
||||
|
||||
// FIXME: We should just issue a single error in this case pointing out where
|
||||
// the '...' goes. It's tricky to recover correctly in this case, though,
|
||||
// because the parameter is in scope in the default argument, so must be
|
||||
// passed to Sema before we reach the ellipsis.
|
||||
template<typename...T> void f(T n = 1 ...);
|
||||
// expected-warning@-1 {{creates a C-style varargs}}
|
||||
// expected-note@-2 {{place '...' immediately before declared identifier}}
|
||||
// expected-note@-3 {{insert ','}}
|
||||
// expected-error@-4 {{unexpanded parameter pack}}
|
||||
}
|
||||
|
@ -27,32 +27,32 @@ struct classify_function<R(Args...) const volatile> {
|
||||
};
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
struct classify_function<R(Args......)> {
|
||||
struct classify_function<R(Args..., ...)> {
|
||||
static const unsigned value = 5;
|
||||
};
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
struct classify_function<R(Args......) const> {
|
||||
struct classify_function<R(Args..., ...) const> {
|
||||
static const unsigned value = 6;
|
||||
};
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
struct classify_function<R(Args......) volatile> {
|
||||
struct classify_function<R(Args..., ...) volatile> {
|
||||
static const unsigned value = 7;
|
||||
};
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
struct classify_function<R(Args......) const volatile> {
|
||||
struct classify_function<R(Args..., ...) const volatile> {
|
||||
static const unsigned value = 8;
|
||||
};
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
struct classify_function<R(Args......) &&> {
|
||||
struct classify_function<R(Args..., ...) &&> {
|
||||
static const unsigned value = 9;
|
||||
};
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
struct classify_function<R(Args......) const &> {
|
||||
struct classify_function<R(Args..., ...) const &> {
|
||||
static const unsigned value = 10;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user