[analyzer] Use the MacroExpansionContext for macro expansions in plists

Removes the obsolete ad-hoc macro expansions during bugreport constructions.
It will skip the macro expansion if the expansion happened in an imported TU.

Also removes the expected plist file, while expanding matching context for
the tests.
Adds a previously crashing `plist-macros-with-expansion.c` testfile.
Temporarily marks `plist-macros-with-expansion-ctu.c ` to `XFAIL`.

Reviewed By: xazax.hun, Szelethus

Differential Revision: https://reviews.llvm.org/D93224
This commit is contained in:
Balazs Benics 2021-02-22 11:12:18 +01:00 committed by Balazs Benics
parent 7c58fb6ba0
commit 170c67d5b8
5 changed files with 546 additions and 7598 deletions

View File

@ -157,17 +157,6 @@ private:
} // end of anonymous namespace
namespace {
struct ExpansionInfo {
std::string MacroName;
std::string Expansion;
ExpansionInfo(std::string N, std::string E)
: MacroName(std::move(N)), Expansion(std::move(E)) {}
};
} // end of anonymous namespace
/// Print coverage information to output stream {@code o}.
/// May modify the used list of files {@code Fids} by inserting new ones.
static void printCoverage(const PathDiagnostic *D,
@ -176,9 +165,9 @@ static void printCoverage(const PathDiagnostic *D,
FIDMap &FM,
llvm::raw_fd_ostream &o);
static ExpansionInfo
getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU);
static Optional<StringRef> getExpandedMacro(
SourceLocation MacroLoc, const cross_tu::CrossTranslationUnitContext &CTU,
const MacroExpansionContext &MacroExpansions, const SourceManager &SM);
//===----------------------------------------------------------------------===//
// Methods of PlistPrinter.
@ -391,7 +380,17 @@ void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
for (const PathDiagnosticMacroPiece *P : MacroPieces) {
const SourceManager &SM = PP.getSourceManager();
ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP, CTU);
SourceLocation MacroExpansionLoc =
P->getLocation().asLocation().getExpansionLoc();
const Optional<StringRef> MacroName =
MacroExpansions.getOriginalText(MacroExpansionLoc);
const Optional<StringRef> ExpansionText =
getExpandedMacro(MacroExpansionLoc, CTU, MacroExpansions, SM);
if (!MacroName.hasValue() || !ExpansionText.hasValue())
continue;
Indent(o, indent) << "<dict>\n";
++indent;
@ -408,11 +407,11 @@ void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
// Output the macro name.
Indent(o, indent) << "<key>name</key>";
EmitString(o, EI.MacroName) << '\n';
EmitString(o, MacroName.getValue()) << '\n';
// Output what it expands into.
Indent(o, indent) << "<key>expansion</key>";
EmitString(o, EI.Expansion) << '\n';
EmitString(o, ExpansionText.getValue()) << '\n';
// Finish up.
--indent;
@ -822,571 +821,18 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << "</dict>\n</plist>\n";
}
//===----------------------------------------------------------------------===//
// Declarations of helper functions and data structures for expanding macros.
//===----------------------------------------------------------------------===//
namespace {
using ArgTokensTy = llvm::SmallVector<Token, 2>;
} // end of anonymous namespace
LLVM_DUMP_METHOD static void dumpArgTokensToStream(llvm::raw_ostream &Out,
const Preprocessor &PP,
const ArgTokensTy &Toks);
namespace {
/// Maps unexpanded macro parameters to expanded arguments. A macro argument may
/// need to expanded further when it is nested inside another macro.
class MacroParamMap : public std::map<const IdentifierInfo *, ArgTokensTy> {
public:
void expandFromPrevMacro(const MacroParamMap &Super);
LLVM_DUMP_METHOD void dump(const Preprocessor &PP) const {
dumpToStream(llvm::errs(), PP);
}
LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out,
const Preprocessor &PP) const;
};
struct MacroExpansionInfo {
std::string Name;
const MacroInfo *MI = nullptr;
MacroParamMap ParamMap;
MacroExpansionInfo(std::string N, const MacroInfo *MI, MacroParamMap M)
: Name(std::move(N)), MI(MI), ParamMap(std::move(M)) {}
};
class TokenPrinter {
llvm::raw_ostream &OS;
const Preprocessor &PP;
Token PrevTok, PrevPrevTok;
TokenConcatenation ConcatInfo;
public:
TokenPrinter(llvm::raw_ostream &OS, const Preprocessor &PP)
: OS(OS), PP(PP), ConcatInfo(PP) {
PrevTok.setKind(tok::unknown);
PrevPrevTok.setKind(tok::unknown);
}
void printToken(const Token &Tok);
};
/// Wrapper around a Lexer object that can lex tokens one-by-one. Its possible
/// to "inject" a range of tokens into the stream, in which case the next token
/// is retrieved from the next element of the range, until the end of the range
/// is reached.
class TokenStream {
public:
TokenStream(SourceLocation ExpanLoc, const SourceManager &SM,
const LangOptions &LangOpts)
: ExpanLoc(ExpanLoc) {
FileID File;
unsigned Offset;
std::tie(File, Offset) = SM.getDecomposedLoc(ExpanLoc);
llvm::MemoryBufferRef MB = SM.getBufferOrFake(File);
const char *MacroNameTokenPos = MB.getBufferStart() + Offset;
RawLexer = std::make_unique<Lexer>(SM.getLocForStartOfFile(File), LangOpts,
MB.getBufferStart(), MacroNameTokenPos,
MB.getBufferEnd());
}
void next(Token &Result) {
if (CurrTokenIt == TokenRange.end()) {
RawLexer->LexFromRawLexer(Result);
return;
}
Result = *CurrTokenIt;
CurrTokenIt++;
}
void injectRange(const ArgTokensTy &Range) {
TokenRange = Range;
CurrTokenIt = TokenRange.begin();
}
std::unique_ptr<Lexer> RawLexer;
ArgTokensTy TokenRange;
ArgTokensTy::iterator CurrTokenIt = TokenRange.begin();
SourceLocation ExpanLoc;
};
} // end of anonymous namespace
/// The implementation method of getMacroExpansion: It prints the expansion of
/// a macro to \p Printer, and returns with the name of the macro.
///
/// Since macros can be nested in one another, this function may call itself
/// recursively.
///
/// Unfortunately, macro arguments have to expanded manually. To understand why,
/// observe the following example:
///
/// #define PRINT(x) print(x)
/// #define DO_SOMETHING(str) PRINT(str)
///
/// DO_SOMETHING("Cute panda cubs.");
///
/// As we expand the last line, we'll immediately replace PRINT(str) with
/// print(x). The information that both 'str' and 'x' refers to the same string
/// is an information we have to forward, hence the argument \p PrevParamMap.
///
/// To avoid infinite recursion we maintain the already processed tokens in
/// a set. This is carried as a parameter through the recursive calls. The set
/// is extended with the currently processed token and after processing it, the
/// token is removed. If the token is already in the set, then recursion stops:
///
/// #define f(y) x
/// #define x f(x)
static std::string getMacroNameAndPrintExpansion(
TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP,
const MacroParamMap &PrevParamMap,
llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens);
/// Retrieves the name of the macro and what it's parameters expand into
/// at \p ExpanLoc.
///
/// For example, for the following macro expansion:
///
/// #define SET_TO_NULL(x) x = 0
/// #define NOT_SUSPICIOUS(a) \
/// { \
/// int b = 0; \
/// } \
/// SET_TO_NULL(a)
///
/// int *ptr = new int(4);
/// NOT_SUSPICIOUS(&ptr);
/// *ptr = 5;
///
/// When \p ExpanLoc references the last line, the macro name "NOT_SUSPICIOUS"
/// and the MacroArgMap map { (a, &ptr) } will be returned.
///
/// When \p ExpanLoc references "SET_TO_NULL(a)" within the definition of
/// "NOT_SUSPICOUS", the macro name "SET_TO_NULL" and the MacroArgMap map
/// { (x, a) } will be returned.
static MacroExpansionInfo
getMacroExpansionInfo(const MacroParamMap &PrevParamMap,
SourceLocation ExpanLoc, const Preprocessor &PP);
/// Retrieves the ')' token that matches '(' \p It points to.
static MacroInfo::tokens_iterator getMatchingRParen(
MacroInfo::tokens_iterator It,
MacroInfo::tokens_iterator End);
/// Retrieves the macro info for \p II refers to at \p Loc. This is important
/// because macros can be redefined or undefined.
static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP,
const SourceManager &SM,
const IdentifierInfo *II,
SourceLocation Loc);
//===----------------------------------------------------------------------===//
// Definitions of helper functions and methods for expanding macros.
//===----------------------------------------------------------------------===//
static ExpansionInfo
getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
const Preprocessor *PPToUse = &PP;
if (auto LocAndUnit = CTU.getImportedFromSourceLocation(MacroLoc)) {
MacroLoc = LocAndUnit->first;
PPToUse = &LocAndUnit->second->getPreprocessor();
static Optional<StringRef>
getExpandedMacro(SourceLocation MacroExpansionLoc,
const cross_tu::CrossTranslationUnitContext &CTU,
const MacroExpansionContext &MacroExpansions,
const SourceManager &SM) {
if (auto LocAndUnit = CTU.getImportedFromSourceLocation(MacroExpansionLoc)) {
// TODO: Implement macro expansions for CTU.
return llvm::None;
}
llvm::SmallString<200> ExpansionBuf;
llvm::raw_svector_ostream OS(ExpansionBuf);
TokenPrinter Printer(OS, *PPToUse);
llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens;
std::string MacroName = getMacroNameAndPrintExpansion(
Printer, MacroLoc, *PPToUse, MacroParamMap{}, AlreadyProcessedTokens);
return {MacroName, std::string(OS.str())};
}
static std::string getMacroNameAndPrintExpansion(
TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP,
const MacroParamMap &PrevParamMap,
llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens) {
const SourceManager &SM = PP.getSourceManager();
MacroExpansionInfo MExpInfo =
getMacroExpansionInfo(PrevParamMap, SM.getExpansionLoc(MacroLoc), PP);
IdentifierInfo *MacroNameII = PP.getIdentifierInfo(MExpInfo.Name);
// TODO: If the macro definition contains another symbol then this function is
// called recursively. In case this symbol is the one being defined, it will
// be an infinite recursion which is stopped by this "if" statement. However,
// in this case we don't get the full expansion text in the Plist file. See
// the test file where "value" is expanded to "garbage_" instead of
// "garbage_value".
if (!AlreadyProcessedTokens.insert(MacroNameII).second)
return MExpInfo.Name;
if (!MExpInfo.MI)
return MExpInfo.Name;
// Manually expand its arguments from the previous macro.
MExpInfo.ParamMap.expandFromPrevMacro(PrevParamMap);
// Iterate over the macro's tokens and stringify them.
for (auto It = MExpInfo.MI->tokens_begin(), E = MExpInfo.MI->tokens_end();
It != E; ++It) {
Token T = *It;
// If this token is not an identifier, we only need to print it.
if (T.isNot(tok::identifier)) {
Printer.printToken(T);
continue;
}
const auto *II = T.getIdentifierInfo();
assert(II &&
"This token is an identifier but has no IdentifierInfo!");
// If this token is a macro that should be expanded inside the current
// macro.
if (getMacroInfoForLocation(PP, SM, II, T.getLocation())) {
getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP,
MExpInfo.ParamMap, AlreadyProcessedTokens);
// If this is a function-like macro, skip its arguments, as
// getExpandedMacro() already printed them. If this is the case, let's
// first jump to the '(' token.
auto N = std::next(It);
if (N != E && N->is(tok::l_paren))
It = getMatchingRParen(++It, E);
continue;
}
// If this token is the current macro's argument, we should expand it.
auto ParamToArgIt = MExpInfo.ParamMap.find(II);
if (ParamToArgIt != MExpInfo.ParamMap.end()) {
for (MacroInfo::tokens_iterator ArgIt = ParamToArgIt->second.begin(),
ArgEnd = ParamToArgIt->second.end();
ArgIt != ArgEnd; ++ArgIt) {
// These tokens may still be macros, if that is the case, handle it the
// same way we did above.
const auto *ArgII = ArgIt->getIdentifierInfo();
if (!ArgII) {
Printer.printToken(*ArgIt);
continue;
}
const auto *MI = PP.getMacroInfo(ArgII);
if (!MI) {
Printer.printToken(*ArgIt);
continue;
}
getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP,
MExpInfo.ParamMap,
AlreadyProcessedTokens);
// Peek the next token if it is a tok::l_paren. This way we can decide
// if this is the application or just a reference to a function maxro
// symbol:
//
// #define apply(f) ...
// #define func(x) ...
// apply(func)
// apply(func(42))
auto N = std::next(ArgIt);
if (N != ArgEnd && N->is(tok::l_paren))
ArgIt = getMatchingRParen(++ArgIt, ArgEnd);
}
continue;
}
// If control reached here, then this token isn't a macro identifier, nor an
// unexpanded macro argument that we need to handle, print it.
Printer.printToken(T);
}
AlreadyProcessedTokens.erase(MacroNameII);
return MExpInfo.Name;
}
static MacroExpansionInfo
getMacroExpansionInfo(const MacroParamMap &PrevParamMap,
SourceLocation ExpanLoc, const Preprocessor &PP) {
const SourceManager &SM = PP.getSourceManager();
const LangOptions &LangOpts = PP.getLangOpts();
// First, we create a Lexer to lex *at the expansion location* the tokens
// referring to the macro's name and its arguments.
TokenStream TStream(ExpanLoc, SM, LangOpts);
// Acquire the macro's name.
Token TheTok;
TStream.next(TheTok);
std::string MacroName = PP.getSpelling(TheTok);
const auto *II = PP.getIdentifierInfo(MacroName);
assert(II && "Failed to acquire the IdentifierInfo for the macro!");
const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc);
// assert(MI && "The macro must've been defined at it's expansion location!");
//
// We should always be able to obtain the MacroInfo in a given TU, but if
// we're running the analyzer with CTU, the Preprocessor won't contain the
// directive history (or anything for that matter) from another TU.
// TODO: assert when we're not running with CTU.
if (!MI)
return { MacroName, MI, {} };
// Acquire the macro's arguments at the expansion point.
//
// The rough idea here is to lex from the first left parentheses to the last
// right parentheses, and map the macro's parameter to what they will be
// expanded to. A macro argument may contain several token (like '3 + 4'), so
// we'll lex until we find a tok::comma or tok::r_paren, at which point we
// start lexing the next argument or finish.
ArrayRef<const IdentifierInfo *> MacroParams = MI->params();
if (MacroParams.empty())
return { MacroName, MI, {} };
TStream.next(TheTok);
// When this is a token which expands to another macro function then its
// parentheses are not at its expansion locaiton. For example:
//
// #define foo(x) int bar() { return x; }
// #define apply_zero(f) f(0)
// apply_zero(foo)
// ^
// This is not a tok::l_paren, but foo is a function.
if (TheTok.isNot(tok::l_paren))
return { MacroName, MI, {} };
MacroParamMap ParamMap;
// When the argument is a function call, like
// CALL_FN(someFunctionName(param1, param2))
// we will find tok::l_paren, tok::r_paren, and tok::comma that do not divide
// actual macro arguments, or do not represent the macro argument's closing
// parentheses, so we'll count how many parentheses aren't closed yet.
// If ParanthesesDepth
// * = 0, then there are no more arguments to lex.
// * = 1, then if we find a tok::comma, we can start lexing the next arg.
// * > 1, then tok::comma is a part of the current arg.
int ParenthesesDepth = 1;
// If we encounter the variadic arg, we will lex until the closing
// tok::r_paren, even if we lex a tok::comma and ParanthesesDepth == 1.
const IdentifierInfo *VariadicParamII = PP.getIdentifierInfo("__VA_ARGS__");
if (MI->isGNUVarargs()) {
// If macro uses GNU-style variadic args, the param name is user-supplied,
// an not "__VA_ARGS__". E.g.:
// #define FOO(a, b, myvargs...)
// In this case, just use the last parameter:
VariadicParamII = *(MacroParams.rbegin());
}
for (const IdentifierInfo *CurrParamII : MacroParams) {
MacroParamMap::mapped_type ArgTokens;
// One could also simply not supply a single argument to __VA_ARGS__ -- this
// results in a preprocessor warning, but is not an error:
// #define VARIADIC(ptr, ...) \
// someVariadicTemplateFunction(__VA_ARGS__)
//
// int *ptr;
// VARIADIC(ptr); // Note that there are no commas, this isn't just an
// // empty parameter -- there are no parameters for '...'.
// In any other case, ParenthesesDepth mustn't be 0 here.
if (ParenthesesDepth != 0) {
// Lex the first token of the next macro parameter.
TStream.next(TheTok);
while (CurrParamII == VariadicParamII || ParenthesesDepth != 1 ||
!TheTok.is(tok::comma)) {
assert(TheTok.isNot(tok::eof) &&
"EOF encountered while looking for expanded macro args!");
if (TheTok.is(tok::l_paren))
++ParenthesesDepth;
if (TheTok.is(tok::r_paren))
--ParenthesesDepth;
if (ParenthesesDepth == 0)
break;
if (TheTok.is(tok::raw_identifier)) {
PP.LookUpIdentifierInfo(TheTok);
// This token is a variadic parameter:
//
// #define PARAMS_RESOLVE_TO_VA_ARGS(i, fmt) foo(i, fmt); \
// i = 0;
// #define DISPATCH(...) \
// PARAMS_RESOLVE_TO_VA_ARGS(__VA_ARGS__);
// // ^~~~~~~~~~~ Variadic parameter here
//
// void multipleParamsResolveToVA_ARGS(void) {
// int x = 1;
// DISPATCH(x, "LF1M healer"); // Multiple arguments are mapped to
// // a single __VA_ARGS__ parameter.
// (void)(10 / x);
// }
//
// We will stumble across this while trying to expand
// PARAMS_RESOLVE_TO_VA_ARGS. By this point, we already noted during
// the processing of DISPATCH what __VA_ARGS__ maps to, so we'll
// retrieve the next series of tokens from that.
if (TheTok.getIdentifierInfo() == VariadicParamII) {
TStream.injectRange(PrevParamMap.at(VariadicParamII));
TStream.next(TheTok);
continue;
}
}
ArgTokens.push_back(TheTok);
TStream.next(TheTok);
}
} else {
assert(CurrParamII == VariadicParamII &&
"No more macro arguments are found, but the current parameter "
"isn't the variadic arg!");
}
ParamMap.emplace(CurrParamII, std::move(ArgTokens));
}
assert(TheTok.is(tok::r_paren) &&
"Expanded macro argument acquisition failed! After the end of the loop"
" this token should be ')'!");
return {MacroName, MI, ParamMap};
}
static MacroInfo::tokens_iterator getMatchingRParen(
MacroInfo::tokens_iterator It,
MacroInfo::tokens_iterator End) {
assert(It->is(tok::l_paren) && "This token should be '('!");
// Skip until we find the closing ')'.
int ParenthesesDepth = 1;
while (ParenthesesDepth != 0) {
++It;
assert(It->isNot(tok::eof) &&
"Encountered EOF while attempting to skip macro arguments!");
assert(It != End &&
"End of the macro definition reached before finding ')'!");
if (It->is(tok::l_paren))
++ParenthesesDepth;
if (It->is(tok::r_paren))
--ParenthesesDepth;
}
return It;
}
static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP,
const SourceManager &SM,
const IdentifierInfo *II,
SourceLocation Loc) {
const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II);
if (!MD)
return nullptr;
return MD->findDirectiveAtLoc(Loc, SM).getMacroInfo();
}
void MacroParamMap::expandFromPrevMacro(const MacroParamMap &Super) {
for (value_type &Pair : *this) {
ArgTokensTy &CurrArgTokens = Pair.second;
// For each token in the expanded macro argument.
auto It = CurrArgTokens.begin();
while (It != CurrArgTokens.end()) {
if (It->isNot(tok::identifier)) {
++It;
continue;
}
const auto *II = It->getIdentifierInfo();
assert(II);
// Is this an argument that "Super" expands further?
if (!Super.count(II)) {
++It;
continue;
}
const ArgTokensTy &SuperArgTokens = Super.at(II);
It = CurrArgTokens.insert(It, SuperArgTokens.begin(),
SuperArgTokens.end());
std::advance(It, SuperArgTokens.size());
It = CurrArgTokens.erase(It);
}
}
}
void MacroParamMap::dumpToStream(llvm::raw_ostream &Out,
const Preprocessor &PP) const {
for (const std::pair<const IdentifierInfo *, ArgTokensTy> Pair : *this) {
Out << Pair.first->getName() << " -> ";
dumpArgTokensToStream(Out, PP, Pair.second);
Out << '\n';
}
}
static void dumpArgTokensToStream(llvm::raw_ostream &Out,
const Preprocessor &PP,
const ArgTokensTy &Toks) {
TokenPrinter Printer(Out, PP);
for (Token Tok : Toks)
Printer.printToken(Tok);
}
void TokenPrinter::printToken(const Token &Tok) {
// TODO: Handle GNU extensions where hash and hashhash occurs right before
// __VA_ARGS__.
// cppreference.com: "some compilers offer an extension that allows ## to
// appear after a comma and before __VA_ARGS__, in which case the ## does
// nothing when the variable arguments are present, but removes the comma when
// the variable arguments are not present: this makes it possible to define
// macros such as fprintf (stderr, format, ##__VA_ARGS__)"
// FIXME: Handle named variadic macro parameters (also a GNU extension).
// If this is the first token to be printed, don't print space.
if (PrevTok.isNot(tok::unknown)) {
// If the tokens were already space separated, or if they must be to avoid
// them being implicitly pasted, add a space between them.
if(Tok.hasLeadingSpace() || ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok,
Tok)) {
// AvoidConcat doesn't check for ##, don't print a space around it.
if (PrevTok.isNot(tok::hashhash) && Tok.isNot(tok::hashhash)) {
OS << ' ';
}
}
}
if (!Tok.isOneOf(tok::hash, tok::hashhash)) {
if (PrevTok.is(tok::hash))
OS << '\"' << PP.getSpelling(Tok) << '\"';
else
OS << PP.getSpelling(Tok);
}
PrevPrevTok = PrevTok;
PrevTok = Tok;
return MacroExpansions.getExpandedText(MacroExpansionLoc);
}

View File

@ -8,7 +8,7 @@
// RUN: -analyzer-config ctu-dir=%t/ctudir \
// RUN: -analyzer-config expand-macros=true \
// RUN: -analyzer-output=plist-multi-file -o %t.plist -verify %s
// XFAIL: *
// Check the macro expansions from the plist output here, to make the test more
// understandable.
// RUN: FileCheck --input-file=%t.plist %s

View File

@ -0,0 +1,28 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core %s \
// RUN: -analyzer-output=plist -o %t.plist \
// RUN: -analyzer-config expand-macros=true -verify
//
// RUN: FileCheck --input-file=%t.plist %s
#define STRANGE_FN(x) STRANGE_FN(x, 0)
void test_strange_macro_expansion() {
char *path;
STRANGE_FN(path); // no-crash
// expected-warning@-1 {{implicit declaration of function}}
// expected-warning@-2 {{1st function call argument is an uninitialized value}}
}
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>10</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>STRANGE_FN(path)</string>
// CHECK-NEXT: <key>expansion</key><string>STRANGE_FN (path ,0)</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>

View File

@ -1,14 +1,8 @@
// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=core %s \
// RUN: -analyzer-output=plist -o %t.plist \
// RUN: -analyzer-config expand-macros=true
// RUN: -analyzer-config expand-macros=true -verify
//
// Check the actual plist output.
// RUN: %normalize_plist <%t.plist | diff -ub \
// RUN: %S/Inputs/expected-plists/plist-macros-with-expansion.cpp.plist -
//
// Check the macro expansions from the plist output here, to make the test more
// understandable.
// RUN: FileCheck --input-file=%t.plist %s
// RUN: FileCheck --input-file=%t.plist %s
void print(const void*);
@ -25,8 +19,19 @@ void nonFunctionLikeMacroTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>SET_PTR_VAR_TO_NULL</string>
// CHECK-NEXT: <key>expansion</key><string>ptr = 0</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>18</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>SET_PTR_VAR_TO_NULL</string>
// CHECK-NEXT: <key>expansion</key><string>ptr =0</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define NULL 0
#define SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO \
@ -38,8 +43,19 @@ void nonFunctionLikeNestedMacroTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO</string>
// CHECK-NEXT: <key>expansion</key><string>ptr =0</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>42</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO</string>
// CHECK-NEXT: <key>expansion</key><string>ptr =0</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
//===----------------------------------------------------------------------===//
// Tests for function-like macro expansions.
@ -58,8 +74,19 @@ void functionLikeMacroTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>TO_NULL</string>
// CHECK-NEXT: <key>expansion</key><string>setToNull(&amp;ptr)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>73</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>TO_NULL(&amp;ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>setToNull (&amp;ptr )</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define DOES_NOTHING(x) \
{ \
@ -78,11 +105,29 @@ void functionLikeNestedMacroTest() {
DEREF(a) = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>TO_NULL</string>
// CHECK-NEXT: <key>expansion</key><string>setToNull(&amp;a)</string>
// CHECK: <key>name</key><string>DEREF</string>
// CHECK-NEXT: <key>expansion</key><string>{ int b; b = 5; } print(a); *a</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>104</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>TO_NULL(&amp;a)</string>
// CHECK-NEXT: <key>expansion</key><string>setToNull (&amp;a )</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>105</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>DEREF(a)</string>
// CHECK-NEXT: <key>expansion</key><string>{int b ;b =5;}print (a );*a </string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
//===----------------------------------------------------------------------===//
// Tests for undefining and/or redifining macros.
@ -99,8 +144,19 @@ void undefinedMacroByTheEndOfParsingTest() {
#undef WILL_UNDEF_SET_NULL_TO_PTR
// CHECK: <key>name</key><string>WILL_UNDEF_SET_NULL_TO_PTR</string>
// CHECK-NEXT: <key>expansion</key><string>ptr = nullptr;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>141</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>WILL_UNDEF_SET_NULL_TO_PTR(ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>ptr =nullptr ;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr) \
/* Nothing */
@ -119,8 +175,19 @@ void macroRedefinedMultipleTimesTest() {
print("This string shouldn't be in the plist file at all. Or anywhere, " \
"but here.");
// CHECK: <key>name</key><string>WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL</string>
// CHECK-NEXT: <key>expansion</key><string>ptr = nullptr;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>169</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>ptr =nullptr ;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define WILL_UNDEF_SET_NULL_TO_PTR_2(ptr) \
ptr = nullptr;
@ -134,9 +201,19 @@ void undefinedMacroInsideAnotherMacroTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// TODO: Expand arguments.
// CHECK: <key>name</key><string>PASS_PTR_TO_MACRO_THAT_WILL_BE_UNDEFD</string>
// CHECK-NEXT: <key>expansion</key><string>ptr = nullptr;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>200</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>PASS_PTR_TO_MACRO_THAT_WILL_BE_UNDEFD(ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>ptr =nullptr ;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#undef WILL_UNDEF_SET_NULL_TO_PTR_2
@ -161,8 +238,19 @@ void macroArgContainsCommaInStringTest() {
*a = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string>
// CHECK-NEXT: <key>expansion</key><string>a = 0; print( &quot;Will this , cause a crash?&quot;)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>237</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>TO_NULL_AND_PRINT(a, &quot;Will this , cause a crash?&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>a =0;print (&quot;Will this , cause a crash?&quot;)</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void macroArgContainsLParenInStringTest() {
int *a;
@ -170,8 +258,19 @@ void macroArgContainsLParenInStringTest() {
*a = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string>
// CHECK-NEXT: <key>expansion</key><string>a = 0; print( &quot;Will this ( cause a crash?&quot;)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>257</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>TO_NULL_AND_PRINT(a, &quot;Will this ( cause a crash?&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>a =0;print (&quot;Will this ( cause a crash?&quot;)</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void macroArgContainsRParenInStringTest() {
int *a;
@ -179,8 +278,19 @@ void macroArgContainsRParenInStringTest() {
*a = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string>
// CHECK-NEXT: <key>expansion</key><string>a = 0; print( &quot;Will this ) cause a crash?&quot;)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>277</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>TO_NULL_AND_PRINT(a, &quot;Will this ) cause a crash?&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>a =0;print (&quot;Will this ) cause a crash?&quot;)</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define CALL_FUNCTION(funcCall) \
funcCall
@ -193,8 +303,19 @@ void macroArgContainsLParenRParenTest() {
*a = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>CALL_FUNCTION</string>
// CHECK-NEXT: <key>expansion</key><string>setToNull(&amp;a)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>302</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>CALL_FUNCTION(setToNull(&amp;a))</string>
// CHECK-NEXT: <key>expansion</key><string>setToNull (&amp;a )</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void setToNullAndPrint(int **vptr, const char *str) {
setToNull(vptr);
@ -207,8 +328,19 @@ void macroArgContainsCommaLParenRParenTest() {
*a = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>CALL_FUNCTION</string>
// CHECK-NEXT: <key>expansion</key><string>setToNullAndPrint(&amp;a, &quot;Hello!&quot;)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>327</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>CALL_FUNCTION(setToNullAndPrint(&amp;a, &quot;Hello!&quot;))</string>
// CHECK-NEXT: <key>expansion</key><string>setToNullAndPrint (&amp;a ,&quot;Hello!&quot;)</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define CALL_FUNCTION_WITH_TWO_PARAMS(funcCall, param1, param2) \
funcCall(param1, param2)
@ -219,8 +351,19 @@ void macroArgContainsCommaLParenRParenTest2() {
*a = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>CALL_FUNCTION_WITH_TWO_PARAMS</string>
// CHECK-NEXT: <key>expansion</key><string>setToNullAndPrint( &amp;a, &quot;Hello!&quot;)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>350</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>CALL_FUNCTION_WITH_TWO_PARAMS(setToNullAndPrint, &amp;a, &quot;Hello!&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>setToNullAndPrint (&amp;a ,&quot;Hello!&quot;)</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define CALL_LAMBDA(l) \
l()
@ -233,9 +376,30 @@ void commaInBracketsTest() {
CALL_LAMBDA(([&ptr, str] () mutable { TO_NULL(&ptr); }));
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>CALL_LAMBDA</string>
// CHECK-NEXT: <key>expansion</key><string>([&amp;ptr, str] () mutable { setToNull(&amp;ptr); })()</string>
// FIXME: Why does the expansion appear twice?
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>376</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>CALL_LAMBDA(([&amp;ptr, str] () mutable { TO_NULL(&amp;ptr); }))</string>
// CHECK-NEXT: <key>expansion</key><string>([&amp;ptr ,str ]()mutable {setToNull (&amp;ptr );})()</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>376</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>CALL_LAMBDA(([&amp;ptr, str] () mutable { TO_NULL(&amp;ptr); }))</string>
// CHECK-NEXT: <key>expansion</key><string>([&amp;ptr ,str ]()mutable {setToNull (&amp;ptr );})()</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define PASTE_CODE(code) \
code
@ -245,15 +409,34 @@ void commaInBracesTest() {
// NOTE: If we were to add a new variable here after a comma, we'd get a
// compilation error, so this test is mainly here to show that this was also
// investigated.
//
// int *ptr = nullptr, a;
int *ptr = nullptr;
*ptr = 5;
})
}
// CHECK: <key>name</key><string>PASTE_CODE</string>
// CHECK-NEXT: <key>expansion</key><string>{ int *ptr = nullptr; *ptr = 5; }</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>408</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>PASTE_CODE({ // expected-
// CHECK-NEXT: // NOTE: If we were to add a new variable here after a comma, we&apos;d get a
// CHECK-NEXT: // compilation error, so this test is mainly here to show that this was also
// CHECK-NEXT: // investigated.
// CHECK-NEXT: //
// CHECK-NEXT: // int *ptr = nullptr, a;
// CHECK-NEXT: int *ptr = nullptr;
// CHECK-NEXT: *ptr = 5;
// CHECK-NEXT: })</string>
// CHECK-NEXT: <key>expansion</key><string>{int *ptr =nullptr ;*ptr =5;}</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// Example taken from
// https://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments.html#Macro-Arguments.
@ -269,8 +452,19 @@ void emptyParamTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>POTENTIALLY_EMPTY_PARAM</string>
// CHECK-NEXT: <key>expansion</key><string>;ptr = nullptr</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>451</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>POTENTIALLY_EMPTY_PARAM(,ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>;ptr =nullptr </string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define NESTED_EMPTY_PARAM(a, b) \
POTENTIALLY_EMPTY_PARAM(a, b);
@ -283,8 +477,19 @@ void nestedEmptyParamTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>NESTED_EMPTY_PARAM</string>
// CHECK-NEXT: <key>expansion</key><string>; ptr = nullptr;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>476</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>NESTED_EMPTY_PARAM(, ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>;ptr =nullptr ;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define CALL_FUNCTION_WITH_ONE_PARAM_THROUGH_MACRO(func, param) \
CALL_FUNCTION(func(param))
@ -295,8 +500,19 @@ void lParenRParenInNestedMacro() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>CALL_FUNCTION_WITH_ONE_PARAM_THROUGH_MACRO</string>
// CHECK-NEXT: <key>expansion</key><string>setToNull( &amp;ptr)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>499</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>CALL_FUNCTION_WITH_ONE_PARAM_THROUGH_MACRO(setToNull, &amp;ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>setToNull (&amp;ptr )</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
//===----------------------------------------------------------------------===//
// Tests for variadic macro arguments.
@ -315,8 +531,19 @@ void variadicMacroArgumentTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>VARIADIC_SET_TO_NULL</string>
// CHECK-NEXT: <key>expansion</key><string>ptr = nullptr; variadicFunc( 1, 5, &quot;haha!&quot;)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>530</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>VARIADIC_SET_TO_NULL(ptr, 1, 5, &quot;haha!&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>ptr =nullptr ;variadicFunc (1,5,&quot;haha!&quot;)</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void variadicMacroArgumentWithoutAnyArgumentTest() {
int *ptr;
@ -326,8 +553,19 @@ void variadicMacroArgumentWithoutAnyArgumentTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>VARIADIC_SET_TO_NULL</string>
// CHECK-NEXT: <key>expansion</key><string>ptr = nullptr; variadicFunc()</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>552</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>VARIADIC_SET_TO_NULL(ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>ptr =nullptr ;variadicFunc ()</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
//===----------------------------------------------------------------------===//
// Tests for # and ##.
@ -343,8 +581,19 @@ void hashHashOperatorTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>DECLARE_FUNC_AND_SET_TO_NULL</string>
// CHECK-NEXT: <key>expansion</key><string>void generated_whatever(); ptr = nullptr;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>580</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>DECLARE_FUNC_AND_SET_TO_NULL(whatever, ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>void generated_whatever ();ptr =nullptr ;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void macroArgContainsHashHashInStringTest() {
int *a;
@ -352,8 +601,19 @@ void macroArgContainsHashHashInStringTest() {
*a = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string>
// CHECK-NEXT: <key>expansion</key><string>a = 0; print( &quot;Will this ## cause a crash?&quot;)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>600</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>TO_NULL_AND_PRINT(a, &quot;Will this ## cause a crash?&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>a =0;print (&quot;Will this ## cause a crash?&quot;)</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define PRINT_STR(str, ptr) \
print(#str); \
@ -365,8 +625,19 @@ void hashOperatorTest() {
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>PRINT_STR</string>
// CHECK-NEXT: <key>expansion</key><string>print(&quot;Hello&quot;); ptr = nullptr</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>624</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>PRINT_STR(Hello, ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>print (&quot;Hello&quot;);ptr =nullptr </string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void macroArgContainsHashInStringTest() {
int *a;
@ -374,8 +645,19 @@ void macroArgContainsHashInStringTest() {
*a = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string>
// CHECK-NEXT: <key>expansion</key><string>a = 0; print( &quot;Will this # cause a crash?&quot;)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>644</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>TO_NULL_AND_PRINT(a, &quot;Will this # cause a crash?&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>a =0;print (&quot;Will this # cause a crash?&quot;)</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
//===----------------------------------------------------------------------===//
// Tests for more complex macro expansions.
@ -420,8 +702,20 @@ void testVeryComplexAlgorithm() {
int tmp = 8 / (getLowestCommonDenominator(5, 7) - 1);
print(&tmp);
}
// CHECK: <key>name</key><string>EUCLIDEAN_ALGORITHM</string>
// CHECK-NEXT: <key>expansion</key><string>if (A&lt;0 ){A=-A;} if ( B&lt;0 ){ B=- B;}return B / ( B - B);</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>698</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>EUCLIDEAN_ALGORITHM(A, B)</string>
// CHECK-NEXT: <key>expansion</key><string>if (A &lt;0){A =-A ;}if (B &lt;0){B =-B ;}return B /(B -B );</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define YET_ANOTHER_SET_TO_NULL(x, y, z) \
print((void *) x); \
@ -436,8 +730,20 @@ void test() {
YET_ANOTHER_SET_TO_NULL(5, DO_NOTHING2("Remember the Vasa"), ptr);
*ptr = 5; // expected-warning{{Dereference of null pointer}}
}
// CHECK: <key>name</key><string>YET_ANOTHER_SET_TO_NULL</string>
// CHECK-NEXT: <key>expansion</key><string>print((void *)5); print((void *)&quot;Remember the Vasa&quot;); ptr = nullptr;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>730</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>YET_ANOTHER_SET_TO_NULL(5, DO_NOTHING2(&quot;Remember the Vasa&quot;), ptr)</string>
// CHECK-NEXT: <key>expansion</key><string>print ((void *)5);print ((void *)&quot;Remember the Vasa&quot;);ptr =nullptr ;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
int garbage_value;
@ -450,8 +756,19 @@ void recursiveMacroUser() {
// expected-warning@-1{{expression result unused}}
}
// CHECK: <key>name</key><string>value</string>
// CHECK-NEXT: <key>expansion</key><string>garbage_</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>754</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>value</string>
// CHECK-NEXT: <key>expansion</key><string>garbage_value </string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define FOO(x) int foo() { return x; }
#define APPLY_ZERO1(function) function(0)
@ -459,8 +776,19 @@ void recursiveMacroUser() {
APPLY_ZERO1(FOO)
void useZeroApplier1() { (void)(1 / foo()); } // expected-warning{{Division by zero}}
// CHECK: <key>name</key><string>APPLY_ZERO1</string>
// CHECK-NEXT: <key>expansion</key><string>int foo() { return x; }(0)</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>776</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>APPLY_ZERO1(FOO)</string>
// CHECK-NEXT: <key>expansion</key><string>int foo (){return 0;}</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define BAR(x) int bar() { return x; }
#define APPLY_ZERO2 BAR(0)
@ -468,8 +796,19 @@ void useZeroApplier1() { (void)(1 / foo()); } // expected-warning{{Division by z
APPLY_ZERO2
void useZeroApplier2() { (void)(1 / bar()); } // expected-warning{{Division by zero}}
// CHECK: <key>name</key><string>APPLY_ZERO2</string>
// CHECK-NEXT: <key>expansion</key><string>int bar() { return 0; }</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>796</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>APPLY_ZERO2</string>
// CHECK-NEXT: <key>expansion</key><string>int bar (){return 0;}</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void foo(int &x, const char *str);
@ -482,8 +821,20 @@ void mulitpleParamsResolveToVA_ARGS(void) {
DISPATCH(x, "LF1M healer");
(void)(10 / x); // expected-warning{{Division by zero}}
}
// CHECK: <key>name</key><string>DISPATCH</string>
// CHECK-NEXT: <key>expansion</key><string>foo(x, &quot;LF1M healer&quot;);x = 0;;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>821</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>DISPATCH(x, &quot;LF1M healer&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>foo (x ,&quot;LF1M healer&quot;);x =0;;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void variadicCFunction(int &x, const char *str, ...);
@ -495,17 +846,40 @@ void concatVA_ARGS(void) {
CONCAT_VA_ARGS(x, "You need to construct additional pylons.", 'c', 9);
(void)(10 / x); // expected-warning{{Division by zero}}
}
// CHECK: <key>name</key><string>CONCAT_VA_ARGS</string>
// CHECK-NEXT: <key>expansion</key><string>variadicCFunction(x, &quot;You need to construct additional pylons.&quot;,&apos;c&apos;, 9);x = 0;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>846</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>CONCAT_VA_ARGS(x, &quot;You need to construct additional pylons.&quot;, &apos;c&apos;, 9)</string>
// CHECK-NEXT: <key>expansion</key><string>variadicCFunction (x ,&quot;You need to construct additional pylons.&quot;,&apos;c&apos;,9);x =0;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void concatVA_ARGSEmpty(void) {
int x = 1;
CONCAT_VA_ARGS(x, "You need to construct");
(void)(10 / x); // expected-warning{{Division by zero}}
}
// FIXME: The comma shouldn't be present after the last argument.
// CHECK: <key>name</key><string>CONCAT_VA_ARGS</string>
// CHECK-NEXT: <key>expansion</key><string>variadicCFunction(x, &quot;You need to construct&quot;,);x = 0;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>866</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>CONCAT_VA_ARGS(x, &quot;You need to construct&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>variadicCFunction (x ,&quot;You need to construct&quot;);x =0;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
#define STRINGIFIED_VA_ARGS(i, fmt, ...) variadicCFunction(i, fmt, #__VA_ARGS__); \
i = 0;
@ -516,9 +890,19 @@ void stringifyVA_ARGS(void) {
(void)(10 / x); // expected-warning{{Division by zero}}
}
// FIXME: Stringify and escape __VA_ARGS__ correctly.
// CHECK: <key>name</key><string>STRINGIFIED_VA_ARGS</string>
// CHECK-NEXT: <key>expansion</key><string>variadicCFunction(x, &quot;Additional supply depots required.&quot;, &quot;&apos;a&apos;&quot;, 10);x = 0;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>889</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>STRINGIFIED_VA_ARGS(x, &quot;Additional supply depots required.&quot;, &apos;a&apos;, 10)</string>
// CHECK-NEXT: <key>expansion</key><string>variadicCFunction (x ,&quot;Additional supply depots required.&quot;,&quot;&apos;a&apos;, 10&quot;);x =0;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
void stringifyVA_ARGSEmpty(void) {
int x = 1;
@ -526,9 +910,19 @@ void stringifyVA_ARGSEmpty(void) {
(void)(10 / x); // expected-warning{{Division by zero}}
}
// FIXME: Stringify and escape __VA_ARGS__ correctly.
// CHECK: <key>name</key><string>STRINGIFIED_VA_ARGS</string>
// CHECK-NEXT: <key>expansion</key><string>variadicCFunction(x, &quot;Additional supply depots required.&quot;, &quot;)&quot;;x = 0;</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>909</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>STRINGIFIED_VA_ARGS(x, &quot;Additional supply depots required.&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>variadicCFunction (x ,&quot;Additional supply depots required.&quot;,&quot;&quot;);x =0;</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// bz44493: Support GNU-style named variadic arguments in plister
#define BZ44493_GNUVA(i, args...) --(i);
@ -541,5 +935,16 @@ int bz44493(void) {
return 0;
}
// CHECK: <key>name</key><string>BZ44493_GNUVA</string>
// CHECK-NEXT: <key>expansion</key><string>--(a);</string>
// CHECK: <key>macro_expansions</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>933</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>name</key><string>BZ44493_GNUVA(a, &quot;arg2&quot;)</string>
// CHECK-NEXT: <key>expansion</key><string>--(a );</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>