Skip to content

Commit 2edb89c

Browse files
committed
Lex arguments for __has_cpp_attribute and friends as expanded tokens
The C and C++ standards require the argument to __has_cpp_attribute and __has_c_attribute to be expanded ([cpp.cond]p5). It would make little sense to expand the argument to those operators but not expand the argument to __has_attribute and __has_declspec, so those were both also changed in this patch. Note that it might make sense for the other builtins to also expand their argument, but it wasn't as clear to me whether the behavior would be correct there, and so they were left for a future revision.
1 parent 2e0fb00 commit 2edb89c

File tree

6 files changed

+152
-36
lines changed

6 files changed

+152
-36
lines changed

clang/docs/ReleaseNotes.rst

+7
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ Attribute Changes in Clang
110110
attribute is handled instead, e.g. in ``handleDeclAttribute``.
111111
(This was changed in order to better support attributes in code completion).
112112

113+
- __has_cpp_attribute, __has_c_attribute, __has_attribute, and __has_declspec
114+
will now macro expand their argument. This causes a change in behavior for
115+
code using ``__has_cpp_attribute(__clang__::attr)`` (and same for
116+
``__has_c_attribute``) where it would previously expand to ``0`` for all
117+
attributes, but will now issue an error due to the expansion of the
118+
predefined ``__clang__`` macro.
119+
113120
Windows Support
114121
---------------
115122

clang/lib/Lex/PPMacroExpansion.cpp

+25-17
Original file line numberDiff line numberDiff line change
@@ -1293,7 +1293,7 @@ static bool EvaluateHasIncludeNext(Token &Tok,
12931293
/// integer values.
12941294
static void EvaluateFeatureLikeBuiltinMacro(llvm::raw_svector_ostream& OS,
12951295
Token &Tok, IdentifierInfo *II,
1296-
Preprocessor &PP,
1296+
Preprocessor &PP, bool ExpandArgs,
12971297
llvm::function_ref<
12981298
int(Token &Tok,
12991299
bool &HasLexedNextTok)> Op) {
@@ -1319,7 +1319,10 @@ static void EvaluateFeatureLikeBuiltinMacro(llvm::raw_svector_ostream& OS,
13191319
bool SuppressDiagnostic = false;
13201320
while (true) {
13211321
// Parse next token.
1322-
PP.LexUnexpandedToken(Tok);
1322+
if (ExpandArgs)
1323+
PP.Lex(Tok);
1324+
else
1325+
PP.LexUnexpandedToken(Tok);
13231326

13241327
already_lexed:
13251328
switch (Tok.getKind()) {
@@ -1609,21 +1612,21 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
16091612
OS << CounterValue++;
16101613
Tok.setKind(tok::numeric_constant);
16111614
} else if (II == Ident__has_feature) {
1612-
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this,
1615+
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
16131616
[this](Token &Tok, bool &HasLexedNextToken) -> int {
16141617
IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
16151618
diag::err_feature_check_malformed);
16161619
return II && HasFeature(*this, II->getName());
16171620
});
16181621
} else if (II == Ident__has_extension) {
1619-
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this,
1622+
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
16201623
[this](Token &Tok, bool &HasLexedNextToken) -> int {
16211624
IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
16221625
diag::err_feature_check_malformed);
16231626
return II && HasExtension(*this, II->getName());
16241627
});
16251628
} else if (II == Ident__has_builtin) {
1626-
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this,
1629+
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
16271630
[this](Token &Tok, bool &HasLexedNextToken) -> int {
16281631
IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
16291632
diag::err_feature_check_malformed);
@@ -1675,20 +1678,20 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
16751678
}
16761679
});
16771680
} else if (II == Ident__is_identifier) {
1678-
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this,
1681+
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
16791682
[](Token &Tok, bool &HasLexedNextToken) -> int {
16801683
return Tok.is(tok::identifier);
16811684
});
16821685
} else if (II == Ident__has_attribute) {
1683-
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this,
1686+
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true,
16841687
[this](Token &Tok, bool &HasLexedNextToken) -> int {
16851688
IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
16861689
diag::err_feature_check_malformed);
16871690
return II ? hasAttribute(AttrSyntax::GNU, nullptr, II,
16881691
getTargetInfo(), getLangOpts()) : 0;
16891692
});
16901693
} else if (II == Ident__has_declspec) {
1691-
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this,
1694+
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true,
16921695
[this](Token &Tok, bool &HasLexedNextToken) -> int {
16931696
IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
16941697
diag::err_feature_check_malformed);
@@ -1704,8 +1707,8 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
17041707
} else if (II == Ident__has_cpp_attribute ||
17051708
II == Ident__has_c_attribute) {
17061709
bool IsCXX = II == Ident__has_cpp_attribute;
1707-
EvaluateFeatureLikeBuiltinMacro(
1708-
OS, Tok, II, *this, [&](Token &Tok, bool &HasLexedNextToken) -> int {
1710+
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true,
1711+
[&](Token &Tok, bool &HasLexedNextToken) -> int {
17091712
IdentifierInfo *ScopeII = nullptr;
17101713
IdentifierInfo *II = ExpectFeatureIdentifierInfo(
17111714
Tok, *this, diag::err_feature_check_malformed);
@@ -1719,7 +1722,8 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
17191722
HasLexedNextToken = true;
17201723
else {
17211724
ScopeII = II;
1722-
LexUnexpandedToken(Tok);
1725+
// Lex an expanded token for the attribute name.
1726+
Lex(Tok);
17231727
II = ExpectFeatureIdentifierInfo(Tok, *this,
17241728
diag::err_feature_check_malformed);
17251729
}
@@ -1746,7 +1750,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
17461750
Tok.setKind(tok::numeric_constant);
17471751
} else if (II == Ident__has_warning) {
17481752
// The argument should be a parenthesized string literal.
1749-
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this,
1753+
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
17501754
[this](Token &Tok, bool &HasLexedNextToken) -> int {
17511755
std::string WarningName;
17521756
SourceLocation StrStartLoc = Tok.getLocation();
@@ -1777,7 +1781,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
17771781
// The argument to this builtin should be an identifier. The
17781782
// builtin evaluates to 1 when that identifier names the module we are
17791783
// currently building.
1780-
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this,
1784+
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
17811785
[this](Token &Tok, bool &HasLexedNextToken) -> int {
17821786
IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
17831787
diag::err_expected_id_building_module);
@@ -1837,28 +1841,32 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
18371841
return;
18381842
} else if (II == Ident__is_target_arch) {
18391843
EvaluateFeatureLikeBuiltinMacro(
1840-
OS, Tok, II, *this, [this](Token &Tok, bool &HasLexedNextToken) -> int {
1844+
OS, Tok, II, *this, false,
1845+
[this](Token &Tok, bool &HasLexedNextToken) -> int {
18411846
IdentifierInfo *II = ExpectFeatureIdentifierInfo(
18421847
Tok, *this, diag::err_feature_check_malformed);
18431848
return II && isTargetArch(getTargetInfo(), II);
18441849
});
18451850
} else if (II == Ident__is_target_vendor) {
18461851
EvaluateFeatureLikeBuiltinMacro(
1847-
OS, Tok, II, *this, [this](Token &Tok, bool &HasLexedNextToken) -> int {
1852+
OS, Tok, II, *this, false,
1853+
[this](Token &Tok, bool &HasLexedNextToken) -> int {
18481854
IdentifierInfo *II = ExpectFeatureIdentifierInfo(
18491855
Tok, *this, diag::err_feature_check_malformed);
18501856
return II && isTargetVendor(getTargetInfo(), II);
18511857
});
18521858
} else if (II == Ident__is_target_os) {
18531859
EvaluateFeatureLikeBuiltinMacro(
1854-
OS, Tok, II, *this, [this](Token &Tok, bool &HasLexedNextToken) -> int {
1860+
OS, Tok, II, *this, false,
1861+
[this](Token &Tok, bool &HasLexedNextToken) -> int {
18551862
IdentifierInfo *II = ExpectFeatureIdentifierInfo(
18561863
Tok, *this, diag::err_feature_check_malformed);
18571864
return II && isTargetOS(getTargetInfo(), II);
18581865
});
18591866
} else if (II == Ident__is_target_environment) {
18601867
EvaluateFeatureLikeBuiltinMacro(
1861-
OS, Tok, II, *this, [this](Token &Tok, bool &HasLexedNextToken) -> int {
1868+
OS, Tok, II, *this, false,
1869+
[this](Token &Tok, bool &HasLexedNextToken) -> int {
18621870
IdentifierInfo *II = ExpectFeatureIdentifierInfo(
18631871
Tok, *this, diag::err_feature_check_malformed);
18641872
return II && isTargetEnvironment(getTargetInfo(), II);

clang/test/Preprocessor/has_attribute.c

+8
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,11 @@ int has_no_volatile_attribute();
5656

5757
#if __has_cpp_attribute(selectany) // expected-error {{function-like macro '__has_cpp_attribute' is not defined}}
5858
#endif
59+
60+
// Test that macro expansion of the builtin argument works.
61+
#define F fallthrough
62+
63+
#if __has_attribute(F)
64+
int has_fallthrough;
65+
#endif
66+
// CHECK: int has_fallthrough;

clang/test/Preprocessor/has_attribute.cpp

+54-10
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,6 @@ CXX11(clang::__fallthrough__)
1818
// CHECK: __gsl__::suppress: 0
1919
CXX11(__gsl__::suppress)
2020

21-
// We do somewhat support the __clang__ vendor namespace, but it is a
22-
// predefined macro and thus we encourage users to use _Clang instead.
23-
// Because of this, we do not support __has_cpp_attribute for that
24-
// vendor namespace.
25-
//
26-
// Note, we can't use CXX11 here because it will expand __clang__ to 1
27-
// too early.
28-
// CHECK: 1::fallthrough: 0
29-
__clang__::fallthrough: __has_cpp_attribute(__clang__::fallthrough)
30-
3121
// CHECK: _Clang::fallthrough: 201603L
3222
CXX11(_Clang::fallthrough)
3323

@@ -70,6 +60,50 @@ CXX11(unlikely)
7060
// CHECK: noreturn: 200809L
7161
// CHECK: unlikely: 201803L
7262

63+
namespace PR48462 {
64+
// Test that macro expansion of the builtin argument works.
65+
#define C clang
66+
#define F fallthrough
67+
#define CF clang::fallthrough
68+
69+
#if __has_cpp_attribute(F)
70+
int has_fallthrough;
71+
#endif
72+
// CHECK: int has_fallthrough;
73+
74+
#if __has_cpp_attribute(C::F)
75+
int has_clang_falthrough_1;
76+
#endif
77+
// CHECK: int has_clang_falthrough_1;
78+
79+
#if __has_cpp_attribute(clang::F)
80+
int has_clang_falthrough_2;
81+
#endif
82+
// CHECK: int has_clang_falthrough_2;
83+
84+
#if __has_cpp_attribute(C::fallthrough)
85+
int has_clang_falthrough_3;
86+
#endif
87+
// CHECK: int has_clang_falthrough_3;
88+
89+
#if __has_cpp_attribute(CF)
90+
int has_clang_falthrough_4;
91+
#endif
92+
// CHECK: int has_clang_falthrough_4;
93+
94+
#define FUNCLIKE1(x) clang::x
95+
#if __has_cpp_attribute(FUNCLIKE1(fallthrough))
96+
int funclike_1;
97+
#endif
98+
// CHECK: int funclike_1;
99+
100+
#define FUNCLIKE2(x) _Clang::x
101+
#if __has_cpp_attribute(FUNCLIKE2(fallthrough))
102+
int funclike_2;
103+
#endif
104+
// CHECK: int funclike_2;
105+
}
106+
73107
// Test for Microsoft __declspec attributes
74108

75109
#define DECLSPEC(x) x: __has_declspec_attribute(x)
@@ -81,3 +115,13 @@ DECLSPEC(__uuid__)
81115

82116
// CHECK: fallthrough: 0
83117
DECLSPEC(fallthrough)
118+
119+
namespace PR48462 {
120+
// Test that macro expansion of the builtin argument works.
121+
#define U uuid
122+
123+
#if __has_declspec_attribute(U)
124+
int has_uuid;
125+
#endif
126+
// CHECK: int has_uuid;
127+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_cc1 -triple i386-unknown-unknown -Eonly -verify %s
2+
3+
// We warn users if they write an attribute like
4+
// [[__clang__::fallthrough]] because __clang__ is a macro that expands to 1.
5+
// Instead, we suggest users use [[_Clang::fallthrough]] in this situation.
6+
// However, because __has_cpp_attribute (and __has_c_attribute) require
7+
// expanding their argument tokens, __clang__ expands to 1 in the feature test
8+
// macro as well. We don't currently give users a kind warning in this case,
9+
// but we previously did not expand macros and so this would return 0. Now that
10+
// we properly expand macros, users will now get an error about using incorrect
11+
// syntax.
12+
13+
__has_cpp_attribute(__clang__::fallthrough) // expected-error {{missing ')' after <numeric_constant>}} \
14+
// expected-note {{to match this '('}} \
15+
// expected-error {{builtin feature check macro requires a parenthesized identifier}}
16+

clang/test/Preprocessor/has_c_attribute.c

+42-9
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,45 @@ C2x(__gnu__::warn_unused_result)
3333
// CHECK: gnu::__warn_unused_result__: 201904L
3434
C2x(gnu::__warn_unused_result__)
3535

36-
// We do somewhat support the __clang__ vendor namespace, but it is a
37-
// predefined macro and thus we encourage users to use _Clang instead.
38-
// Because of this, we do not support __has_c_attribute for that
39-
// vendor namespace.
40-
//
41-
// Note, we can't use C2x here because it will expand __clang__ to 1
42-
// too early.
43-
// CHECK: 1::fallthrough: 0
44-
__clang__::fallthrough: __has_c_attribute(__clang__::fallthrough)
36+
// Test that macro expansion of the builtin argument works.
37+
#define C clang
38+
#define L likely
39+
#define CL clang::likely
40+
#define N nodiscard
41+
42+
#if __has_c_attribute(N)
43+
int has_nodiscard;
44+
#endif
45+
// CHECK: int has_nodiscard;
46+
47+
#if __has_c_attribute(C::L)
48+
int has_clang_likely_1;
49+
#endif
50+
// CHECK: int has_clang_likely_1;
51+
52+
#if __has_c_attribute(clang::L)
53+
int has_clang_likely_2;
54+
#endif
55+
// CHECK: int has_clang_likely_2;
56+
57+
#if __has_c_attribute(C::likely)
58+
int has_clang_likely_3;
59+
#endif
60+
// CHECK: int has_clang_likely_3;
61+
62+
#if __has_c_attribute(CL)
63+
int has_clang_likely_4;
64+
#endif
65+
// CHECK: int has_clang_likely_4;
66+
67+
#define FUNCLIKE1(x) clang::x
68+
#if __has_c_attribute(FUNCLIKE1(likely))
69+
int funclike_1;
70+
#endif
71+
// CHECK: int funclike_1;
72+
73+
#define FUNCLIKE2(x) _Clang::x
74+
#if __has_c_attribute(FUNCLIKE2(likely))
75+
int funclike_2;
76+
#endif
77+
// CHECK: int funclike_2;

0 commit comments

Comments
 (0)