summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/include/clang/Basic/DiagnosticGroups.td1
-rw-r--r--clang/include/clang/Basic/DiagnosticLexKinds.td7
-rw-r--r--clang/lib/Lex/PPExpressions.cpp49
-rw-r--r--clang/test/Lexer/cxx-features.cpp1
-rw-r--r--clang/test/Preprocessor/expr_define_expansion.c26
5 files changed, 80 insertions, 4 deletions
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 2e4e57b63b8..a31cfdb2a93 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -204,6 +204,7 @@ def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">;
def DanglingElse: DiagGroup<"dangling-else">;
def DanglingField : DiagGroup<"dangling-field">;
def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
+def ExpansionToUndefined : DiagGroup<"expansion-to-undefined">;
def FlagEnum : DiagGroup<"flag-enum">;
def IncrementBool : DiagGroup<"increment-bool", [DeprecatedIncrementBool]>;
def InfiniteRecursion : DiagGroup<"infinite-recursion">;
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 2fc9664f49e..04ae1a4323f 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -658,6 +658,13 @@ def warn_header_guard : Warning<
def note_header_guard : Note<
"%0 is defined here; did you mean %1?">;
+def warn_defined_in_object_type_macro : Warning<
+ "macro expansion producing 'defined' has undefined behavior">,
+ InGroup<ExpansionToUndefined>;
+def warn_defined_in_function_type_macro : Extension<
+ "macro expansion producing 'defined' has undefined behavior">,
+ InGroup<ExpansionToUndefined>;
+
let CategoryName = "Nullability Issue" in {
def err_pp_assume_nonnull_syntax : Error<"expected 'begin' or 'end'">;
diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp
index c40598c0675..6603a17cbd4 100644
--- a/clang/lib/Lex/PPExpressions.cpp
+++ b/clang/lib/Lex/PPExpressions.cpp
@@ -140,6 +140,51 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
PP.LexNonComment(PeekTok);
}
+ // [cpp.cond]p4:
+ // Prior to evaluation, macro invocations in the list of preprocessing
+ // tokens that will become the controlling constant expression are replaced
+ // (except for those macro names modified by the 'defined' unary operator),
+ // just as in normal text. If the token 'defined' is generated as a result
+ // of this replacement process or use of the 'defined' unary operator does
+ // not match one of the two specified forms prior to macro replacement, the
+ // behavior is undefined.
+ // This isn't an idle threat, consider this program:
+ // #define FOO
+ // #define BAR defined(FOO)
+ // #if BAR
+ // ...
+ // #else
+ // ...
+ // #endif
+ // clang and gcc will pick the #if branch while Visual Studio will take the
+ // #else branch. Emit a warning about this undefined behavior.
+ if (beginLoc.isMacroID()) {
+ bool IsFunctionTypeMacro =
+ PP.getSourceManager()
+ .getSLocEntry(PP.getSourceManager().getFileID(beginLoc))
+ .getExpansion()
+ .isFunctionMacroExpansion();
+ // For object-type macros, it's easy to replace
+ // #define FOO defined(BAR)
+ // with
+ // #if defined(BAR)
+ // #define FOO 1
+ // #else
+ // #define FOO 0
+ // #endif
+ // and doing so makes sense since compilers handle this differently in
+ // practice (see example further up). But for function-type macros,
+ // there is no good way to write
+ // # define FOO(x) (defined(M_ ## x) && M_ ## x)
+ // in a different way, and compilers seem to agree on how to behave here.
+ // So warn by default on object-type macros, but only warn in -pedantic
+ // mode on function-type macros.
+ if (IsFunctionTypeMacro)
+ PP.Diag(beginLoc, diag::warn_defined_in_function_type_macro);
+ else
+ PP.Diag(beginLoc, diag::warn_defined_in_object_type_macro);
+ }
+
// Invoke the 'defined' callback.
if (PPCallbacks *Callbacks = PP.getPPCallbacks()) {
Callbacks->Defined(macroToken, Macro,
@@ -177,8 +222,8 @@ static bool EvaluateValue(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
if (IdentifierInfo *II = PeekTok.getIdentifierInfo()) {
// Handle "defined X" and "defined(X)".
if (II->isStr("defined"))
- return(EvaluateDefined(Result, PeekTok, DT, ValueLive, PP));
-
+ return EvaluateDefined(Result, PeekTok, DT, ValueLive, PP);
+
// If this identifier isn't 'defined' or one of the special
// preprocessor keywords and it wasn't macro expanded, it turns
// into a simple 0, unless it is the C++ keyword "true", in which case it
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 6c4a092b1c4..b0f382ec253 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -6,6 +6,7 @@
// expected-no-diagnostics
+// FIXME using `defined` in a macro has undefined behavior.
#if __cplusplus < 201103L
#define check(macro, cxx98, cxx11, cxx1y) cxx98 == 0 ? defined(__cpp_##macro) : __cpp_##macro != cxx98
#elif __cplusplus < 201304L
diff --git a/clang/test/Preprocessor/expr_define_expansion.c b/clang/test/Preprocessor/expr_define_expansion.c
index 3e5a2c4b0e6..23cb4355eb8 100644
--- a/clang/test/Preprocessor/expr_define_expansion.c
+++ b/clang/test/Preprocessor/expr_define_expansion.c
@@ -1,6 +1,28 @@
-// RUN: %clang_cc1 %s -E -CC -pedantic -verify
-// expected-no-diagnostics
+// RUN: %clang_cc1 %s -E -CC -verify
+// RUN: %clang_cc1 %s -E -CC -DPEDANTIC -pedantic -verify
#define FOO && 1
#if defined FOO FOO
#endif
+
+#define A
+#define B defined(A)
+#if B // expected-warning{{macro expansion producing 'defined' has undefined behavior}}
+#endif
+
+#define m_foo
+#define TEST(a) (defined(m_##a) && a)
+
+#if defined(PEDANTIC)
+// expected-warning@+4{{macro expansion producing 'defined' has undefined behavior}}
+#endif
+
+// This shouldn't warn by default, only with pedantic:
+#if TEST(foo)
+#endif
+
+
+// Only one diagnostic for this case:
+#define INVALID defined(
+#if INVALID // expected-error{{macro name missing}}
+#endif
OpenPOWER on IntegriCloud