Make -Wnull-conversion more useful.

When a null constant is used in a macro, walk through the macro stack to
determine where the null constant is written and where the context is located.
Only warn if both locations are within the same macro expansion.  This helps
function-like macros which involve pointers be treated as if they were
functions.

llvm-svn: 260776
This commit is contained in:
Richard Trieu 2016-02-13 00:58:53 +00:00
parent 5b79ede65a
commit 0a5e166a0b
2 changed files with 56 additions and 7 deletions

View File

@ -7265,14 +7265,21 @@ void DiagnoseNullConversion(Sema &S, Expr *E, QualType T, SourceLocation CC) {
SourceLocation Loc = E->getSourceRange().getBegin();
// Venture through the macro stacks to get to the source of macro arguments.
// The new location is a better location than the complete location that was
// passed in.
while (S.SourceMgr.isMacroArgExpansion(Loc))
Loc = S.SourceMgr.getImmediateMacroCallerLoc(Loc);
while (S.SourceMgr.isMacroArgExpansion(CC))
CC = S.SourceMgr.getImmediateMacroCallerLoc(CC);
// __null is usually wrapped in a macro. Go up a macro if that is the case.
if (NullKind == Expr::NPCK_GNUNull) {
if (Loc.isMacroID()) {
StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
Loc, S.SourceMgr, S.getLangOpts());
if (MacroName == "NULL")
Loc = S.SourceMgr.getImmediateExpansionRange(Loc).first;
}
if (NullKind == Expr::NPCK_GNUNull && Loc.isMacroID()) {
StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
Loc, S.SourceMgr, S.getLangOpts());
if (MacroName == "NULL")
Loc = S.SourceMgr.getImmediateExpansionRange(Loc).first;
}
// Only warn if the null and context location are in the same macro expansion.

View File

@ -256,3 +256,45 @@ bool run() {
}
}
// More tests with macros. Specficially, test function-like macros that either
// have a pointer return type or take pointer arguments. Basically, if the
// macro was changed into a function and Clang doesn't warn, then it shouldn't
// warn for the macro either.
namespace test13 {
#define check_str_nullptr_13(str) ((str) ? str : nullptr)
#define check_str_null_13(str) ((str) ? str : NULL)
#define test13(condition) if (condition) return;
#define identity13(arg) arg
#define CHECK13(condition) test13(identity13(!(condition)))
void function1(const char* str) {
CHECK13(check_str_nullptr_13(str));
CHECK13(check_str_null_13(str));
}
bool some_bool_function(bool);
void function2() {
CHECK13(some_bool_function(nullptr)); // expected-warning{{implicit conversion of nullptr constant to 'bool'}}
CHECK13(some_bool_function(NULL)); // expected-warning{{implicit conversion of NULL constant to 'bool'}}
}
#define run_check_nullptr_13(str) \
if (check_str_nullptr_13(str)) return;
#define run_check_null_13(str) \
if (check_str_null_13(str)) return;
void function3(const char* str) {
run_check_nullptr_13(str)
run_check_null_13(str)
if (check_str_nullptr_13(str)) return;
if (check_str_null_13(str)) return;
}
void run(int* ptr);
#define conditional_run_13(ptr) \
if (ptr) run(ptr);
void function4() {
conditional_run_13(nullptr);
conditional_run_13(NULL);
}
}