Szelethus updated this revision to Diff 167924.
Szelethus added a comment.

- Fixes according to @whisperity's comments
- New test cases for
  - commas in brackets,
  - commas in braces,
  - macro arguments left empty.

> (The whole thing, however, is generally disgusting. I'd've expected the 
> Preprocessor to readily contain a lot of stuff that's going on here.)

Don't even get me started.


https://reviews.llvm.org/D52742

Files:
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  lib/StaticAnalyzer/Core/BugReporter.cpp
  lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
  test/Analysis/plist-macros-with-expansion.cpp

Index: test/Analysis/plist-macros-with-expansion.cpp
===================================================================
--- /dev/null
+++ test/Analysis/plist-macros-with-expansion.cpp
@@ -0,0 +1,212 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+//
+// RUN: %clang_analyze_cc1 -analyzer-checker=core %s  \
+// RUN:   -analyzer-output=plist -o %t.plist \
+// RUN:   -analyzer-config expand-macros=true \
+// RUN:
+// RUN: FileCheck --input-file=%t.plist %s
+
+void print(const void*);
+
+//===----------------------------------------------------------------------===//
+// Tests for non-function-like macro expansions.
+//===----------------------------------------------------------------------===//
+
+#define SET_PTR_VAR_TO_NULL \
+  ptr = 0
+
+void nonFunctionLikeMacroTest() {
+  int *ptr;
+  SET_PTR_VAR_TO_NULL;
+  *ptr = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;SET_PTR_VAR_TO_NULL&apos; to &apos;ptr = 0 &apos;</string>
+
+#define NULL 0
+#define SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO \
+  ptr = NULL
+
+void nonFunctionLikeNestedMacroTest() {
+  int *ptr;
+  SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO;
+  *ptr = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;SET_PTR_VAR_TO_NULL&apos; to &apos;ptr = 0 &apos;</string>
+
+//===----------------------------------------------------------------------===//
+// Tests for function-like macro expansions.
+//===----------------------------------------------------------------------===//
+
+void setToNull(int **vptr) {
+  *vptr = nullptr;
+}
+
+#define TO_NULL(x) \
+  setToNull(x)
+
+void functionLikeMacroTest() {
+  int *ptr;
+  TO_NULL(&ptr);
+  *ptr = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;TO_NULL&apos; to &apos;setToNull ( &amp; a ) &apos;</string>
+
+#define DOES_NOTHING(x) \
+  {                     \
+    int b;              \
+    b = 5;              \
+  }                     \
+  print(x)
+
+#define DEREF(x)   \
+  DOES_NOTHING(x); \
+  *x
+
+void functionLikeNestedMacroTest() {
+  int *a;
+  TO_NULL(&a);
+  DEREF(a) = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;TO_NULL&apos; to &apos;setToNull ( &amp; a ) &apos;</string>
+
+// CHECK: <string>Expanding macro &apos;DEREF&apos; to &apos;{ int b ; b = 5 ; } print ( a ) ; * a &apos;</string>
+
+//===----------------------------------------------------------------------===//
+// Tests for macro arguments containing commas and parantheses.
+//
+// As of writing these tests, the algorithm expands macro arguments by lexing
+// the macro's expansion location, and relies on finding tok::comma and
+// tok::l_paren/tok::r_paren.
+//===----------------------------------------------------------------------===//
+
+// Note that this commas, parantheses in strings aren't parsed as tok::comma or
+// tok::l_paren/tok::r_paren, but why not test them.
+
+#define TO_NULL_AND_PRINT(x, str) \
+  x = 0; \
+  print(str)
+
+void macroArgContainsCommaInStringTest() {
+  int *a;
+  TO_NULL_AND_PRINT(a, "Will this , cause a crash?");
+  *a = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;TO_NULL_AND_PRINT&apos; to &apos;a = 0 ; print ( &quot;Will this , cause a crash?&quot; ) &apos;</string>
+
+void macroArgContainsLParenInStringTest() {
+  int *a;
+  TO_NULL_AND_PRINT(a, "Will this ( cause a crash?");
+  *a = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;TO_NULL_AND_PRINT&apos; to &apos;a = 0 ; print ( &quot;Will this ( cause a crash?&quot; ) &apos;</string>
+
+void macroArgContainsRParenInStringTest() {
+  int *a;
+  TO_NULL_AND_PRINT(a, "Will this ) cause a crash?");
+  *a = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;TO_NULL_AND_PRINT&apos; to &apos;a = 0 ; print ( &quot;Will this ) cause a crash?&quot; ) &apos;</string>
+
+#define CALL_FUNCTION(funcCall)   \
+  funcCall
+
+// Function calls do contain both tok::comma and tok::l_paren/tok::r_paren.
+
+void macroArgContainsLParenRParenTest() {
+  int *a;
+  CALL_FUNCTION(setToNull(&a));
+  *a = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;CALL_FUNCTION&apos; to &apos;setToNull ( &amp; a ) &apos;</string>
+
+void setToNullAndPrint(int **vptr, const char *str) {
+  setToNull(vptr);
+  print(str);
+}
+
+void macroArgContainsCommaLParenRParenTest() {
+  int *a;
+  CALL_FUNCTION(setToNullAndPrint(&a, "Hello!"));
+  *a = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;CALL_FUNCTION&apos; to &apos;setToNullAndPrint ( &amp; a , &quot;Hello!&quot; ) &apos;</string>
+
+#define CALL_FUNCTION_WITH_TWO_PARAMS(funcCall, param1, param2) \
+  funcCall(param1, param2)
+
+void macroArgContainsCommaLParenRParenTest2() {
+  int *a;
+  CALL_FUNCTION_WITH_TWO_PARAMS(setToNullAndPrint, &a, "Hello!");
+  *a = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;CALL_FUNCTION_WITH_TWO_PARAMS&apos; to &apos;setToNullAndPrint ( &amp; a , &quot;Hello!&quot; ) &apos;</string>
+
+#define CALL_LAMBDA(l) \
+  l()
+
+void commaInBracketsTest() {
+  int *ptr;
+  const char str[] = "Hello!";
+  // You need to add parantheses around a lambda expression to compile this,
+  // else the comma in the capture will be parsed as divider of macro args.
+  CALL_LAMBDA(([&ptr, str] () mutable { TO_NULL(&ptr); }));
+  *ptr = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;CALL_LAMBDA&apos; to &apos;( [ &amp; ptr , str ] ( ) mutable { TO_NULL ( &amp; ptr ) ; } ) ( ) &apos;</string>
+
+#define PASTE_CODE(code) \
+  code
+
+void commaInBracesTest() {
+  PASTE_CODE({ // expected-warning{{Dereference of null pointer}}
+    // 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: <string>Expanding macro &apos;PASTE_CODE&apos; to &apos;{ int * ptr = nullptr ; * ptr = 5 ; } &apos;</string>
+
+// Example taken from
+// https://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments.html#Macro-Arguments.
+
+#define POTENTIALLY_EMPTY_PARAM(x, y) \
+  x;                                  \
+  y = nullptr
+
+void emptyParamTest() {
+  int *ptr;
+
+  POTENTIALLY_EMPTY_PARAM(,ptr);
+  *ptr = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;POTENTIALLY_EMPTY_PARAM&apos; to &apos;; ptr = nullptr &apos;</string>
+
+#define NESTED_EMPTY_PARAM(a, b) \
+  POTENTIALLY_EMPTY_PARAM(a, b);
+
+
+void nestedEmptyParamTest() {
+  int *ptr;
+
+  NESTED_EMPTY_PARAM(, ptr);
+  *ptr = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+// CHECK: <string>Expanding macro &apos;NESTED_EMPTY_PARAM&apos; to &apos;; ptr = nullptr ; &apos;</string>
Index: lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
===================================================================
--- lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -253,15 +253,64 @@
                 /*includeControlFlow*/ true);
 }
 
+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
+
+static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc,
+                                      const Preprocessor &PP);
+
 static void ReportMacro(raw_ostream &o,
-                        const PathDiagnosticMacroPiece& P,
-                        const FIDMap& FM,
+                        const PathDiagnosticMacroPiece &P,
+                        const FIDMap &FM,
                         const Preprocessor &PP,
                         unsigned indent,
                         unsigned depth) {
+  const SourceManager &SM = PP.getSourceManager();
 
-  for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end();
-       I!=E; ++I) {
+  llvm::SmallString<50> MacroMessage;
+  llvm::raw_svector_ostream MacroOS(MacroMessage);
+
+  {
+    ExpansionInfo EI = getExpandedMacro(P.getLocation().asLocation(), PP);
+
+    MacroOS << "Expanding macro '" << EI.MacroName << "' to '"
+            << EI.Expansion << '\'';
+  }
+
+  Indent(o, indent) << "<dict>\n";
+  ++indent;
+
+  Indent(o, indent) << "<key>kind</key><string>macro_expansion</string>\n";
+
+  // Output the location.
+  FullSourceLoc L = P.getLocation().asLocation();
+
+  Indent(o, indent) << "<key>location</key>\n";
+  EmitLocation(o, SM, L, FM, indent);
+
+  // Output the ranges (if any).
+  ArrayRef<SourceRange> Ranges = P.getRanges();
+  EmitRanges(o, Ranges, FM, PP, indent);
+
+  // Output the text.
+  EmitMessage(o, MacroOS.str(), indent);
+
+  // Finish up.
+  --indent;
+  Indent(o, indent);
+  o << "</dict>\n";
+
+  for (PathPieces::const_iterator I = P.subPieces.begin(),
+                                  E = P.subPieces.end();
+       I != E; ++I) {
     ReportPiece(o, **I, FM, PP, indent, depth, /*includeControlFlow*/ false);
   }
 }
@@ -606,3 +655,249 @@
   // Finish.
   o << "</dict>\n</plist>";
 }
+
+//===----------------------------------------------------------------------===//
+// Helper functions and data structures for expanding macros.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+using ExpArgTokens = llvm::SmallVector<Token, 2>;
+
+/// Maps unexpanded macro arguments to expanded arguments. A macro argument may
+/// need to expanded further when it is nested inside another macro.
+class MacroArgMap : public std::map<const IdentifierInfo *, ExpArgTokens> {
+public:
+  void expandFromPrevMacro(const MacroArgMap &Super);
+};
+
+struct MacroNameAndArgs {
+  std::string Name;
+  const MacroInfo *MI = nullptr;
+  llvm::Optional<MacroArgMap> Args;
+
+  MacroNameAndArgs(std::string N, const MacroInfo *MI,
+                   llvm::Optional<MacroArgMap> M)
+    : Name(std::move(N)), MI(MI), Args(std::move(M)) {}
+};
+
+} // end of anonymous namespace
+
+/// Retrieves the name of the macro and what it's arguments 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 MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
+                                            const Preprocessor &PP);
+
+static ExpansionInfo getExpandedMacroImpl(SourceLocation MacroLoc,
+                                          const Preprocessor &PP,
+                                          MacroArgMap *PrevArgs) {
+
+  const SourceManager &SM = PP.getSourceManager();
+
+  MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP);
+
+  std::string MacroName = std::move(Info.Name);
+  const MacroInfo *MI = Info.MI;
+  llvm::Optional<MacroArgMap> Args = std::move(Info.Args);
+
+  // If this macro is function-like.
+  if (Args) {
+    // If this macro is nested inside another one, let's manually expand its
+    // arguments from the previous macro.
+    if (PrevArgs)
+      Args->expandFromPrevMacro(*PrevArgs);
+    PrevArgs = Args.getPointer();
+  } else
+    PrevArgs = nullptr;
+
+  // Iterate over the macro's tokens and stringify them.
+  llvm::SmallString<200> ExpansionBuf;
+  llvm::raw_svector_ostream ExpansionOS(ExpansionBuf);
+
+  for (auto It = MI->tokens_begin(), E = MI->tokens_end(); It != E;) {
+    Token T = *It;
+
+    if (T.is(tok::identifier)) {
+      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 currect
+      // macro.
+      if (const MacroInfo *MI = PP.getMacroInfo(II)) {
+        ExpansionOS << getExpandedMacroImpl(T.getLocation(), PP, PrevArgs)
+                         .Expansion;
+
+        // If this is a function-like macro, skip its arguments, as
+        // getExpandedMacro() already printed them.
+        ++It;
+        if (It->is(tok::l_paren)) {
+          while ((++It)->isNot(tok::r_paren)) {
+            assert(It->isNot(tok::eof) &&
+                   "Encountered EOF while attempting to skip macro arguments!");
+          }
+          ++It;
+        }
+        continue;
+      }
+
+      // If this token is the current macro's argument, we should expand it.
+      if (Args && Args->count(II)) {
+        for (const Token &ExpandedArgT : Args->at(II)) {
+          ExpansionOS << PP.getSpelling(ExpandedArgT) + ' ';
+        }
+        ++It;
+        continue;
+      }
+    }
+
+    // If control reaches here, there's nothing left to do, print the token.
+    ExpansionOS << PP.getSpelling(T) + ' ';
+    ++It;
+  }
+
+  return {MacroName, ExpansionOS.str()};
+}
+
+static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc,
+                                      const Preprocessor &PP) {
+  return getExpandedMacroImpl(MacroLoc, PP, /* PrevArgs */ nullptr);
+}
+
+static MacroNameAndArgs getMacroNameAndArgs(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.
+  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ExpanLoc);
+  const llvm::MemoryBuffer *MB = SM.getBuffer(LocInfo.first);
+  const char *MacroNameTokenPos = MB->getBufferStart() + LocInfo.second;
+
+  Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts,
+                 MB->getBufferStart(), MacroNameTokenPos, MB->getBufferEnd());
+
+  // Acquire the macro's name.
+  Token TheTok;
+  RawLexer.LexFromRawLexer(TheTok);
+
+  std::string MacroName = PP.getSpelling(TheTok);
+
+  // Acquire the macro's arguments.
+  //
+  // The rough idea here is to lex from the first left parantheses to the last
+  // right parantheses, and map the macro's unexpanded arguments to what they
+  // will be expanded to. An expanded macro argument may contain several tokens
+  // (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.
+  const auto *II = PP.getIdentifierInfo(MacroName);
+  assert(II && "Failed to acquire the IndetifierInfo for the macro!");
+  const MacroInfo *MI = PP.getMacroInfo(II);
+  assert(MI && "This IdentifierInfo should refer to a macro!");
+
+  ArrayRef<const IdentifierInfo *> MacroArgs = MI->params();
+  if (MacroArgs.empty())
+    return { MacroName, MI, None };
+
+  RawLexer.LexFromRawLexer(TheTok);
+  assert(TheTok.is(tok::l_paren) &&
+         "The token after the macro's identifier token should be '('!");
+
+  MacroArgMap Args;
+
+  // When the macro's 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
+  // parantheses, so we'll count how many parantheses aren't closed yet.
+  int ParanthesesDepth = 1;
+
+  for (unsigned Index = 0; Index < MI->getNumParams(); ++Index) {
+    MacroArgMap::mapped_type ExpandedArgTokens;
+
+    // Lex the first token of the next macro parameter.
+    RawLexer.LexFromRawLexer(TheTok);
+
+    while (TheTok.isNot(tok::comma) || ParanthesesDepth != 1) {
+      assert(TheTok.isNot(tok::eof) &&
+             "EOF encountered while looking for expanded macro args!");
+
+      if (TheTok.is(tok::l_paren))
+        ++ParanthesesDepth;
+
+      if (TheTok.is(tok::r_paren))
+        --ParanthesesDepth;
+
+      if (ParanthesesDepth == 0)
+        break;
+
+      if (TheTok.is(tok::raw_identifier))
+        PP.LookUpIdentifierInfo(TheTok);
+
+      ExpandedArgTokens.push_back(TheTok);
+      RawLexer.LexFromRawLexer(TheTok);
+    }
+
+    Args.insert(std::make_pair(MacroArgs[Index], ExpandedArgTokens));
+  }
+
+  assert(TheTok.is(tok::r_paren) &&
+         "Expanded macro argument acquisition failed! After the end of the loop"
+         " this token should be ')'!");
+
+  return { MacroName, MI, Args };
+}
+
+void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) {
+
+  for (value_type &Pair : *this) {
+    ExpArgTokens &CurrExpArgTokens = Pair.second;
+
+    // For each token in the expanded macro argument.
+    auto It = CurrExpArgTokens.begin();
+    while (It != CurrExpArgTokens.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 ExpArgTokens &SuperExpArgTokens = Super.at(II);
+
+      It = CurrExpArgTokens.insert(
+          It, SuperExpArgTokens.begin(), SuperExpArgTokens.end());
+      std::advance(It, SuperExpArgTokens.size());
+      It = CurrExpArgTokens.erase(It);
+    }
+  }
+}
Index: lib/StaticAnalyzer/Core/BugReporter.cpp
===================================================================
--- lib/StaticAnalyzer/Core/BugReporter.cpp
+++ lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -546,7 +546,8 @@
   }
 }
 
-static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM);
+static void CompactMacroExpandedPieces(PathPieces &path,
+                                       const SourceManager& SM);
 
 
 std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP(
@@ -1978,8 +1979,6 @@
                   PathDiagnosticLocation::createBegin(D, SM), CalleeLC);
   }
 
-  if (!AddPathEdges && GenerateDiagnostics)
-    CompactPathDiagnostic(PD->getMutablePieces(), SM);
 
   // Finally, prune the diagnostic path of uninteresting stuff.
   if (!PD->path.empty()) {
@@ -2013,6 +2012,10 @@
     removeRedundantMsgs(PD->getMutablePieces());
     removeEdgesToDefaultInitializers(PD->getMutablePieces());
   }
+
+  if (GenerateDiagnostics && Opts.shouldDisplayMacroExpansions())
+    CompactMacroExpandedPieces(PD->getMutablePieces(), SM);
+
   return PD;
 }
 
@@ -2442,9 +2445,10 @@
   return true;
 }
 
-/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object
-///  and collapses PathDiagosticPieces that are expanded by macros.
-static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) {
+/// CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic
+/// object and collapses PathDiagosticPieces that are expanded by macros.
+static void CompactMacroExpandedPieces(PathPieces &path,
+                                       const SourceManager& SM) {
   using MacroStackTy =
       std::vector<
           std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>;
@@ -2460,7 +2464,7 @@
 
     // Recursively compact calls.
     if (auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
-      CompactPathDiagnostic(call->path, SM);
+      CompactMacroExpandedPieces(call->path, SM);
     }
 
     // Get the location of the PathDiagnosticPiece.
Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
===================================================================
--- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -463,6 +463,13 @@
   return DisplayNotesAsEvents.getValue();
 }
 
+bool AnalyzerOptions::shouldDisplayMacroExpansions() {
+  if (!DisplayMacroExpansions.hasValue())
+    DisplayMacroExpansions =
+        getBooleanOption("expand-macros", /*Default=*/false);
+  return DisplayMacroExpansions.getValue();
+}
+
 bool AnalyzerOptions::shouldAggressivelySimplifyBinaryOperation() {
   if (!AggressiveBinaryOperationSimplification.hasValue())
     AggressiveBinaryOperationSimplification =
Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===================================================================
--- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -308,6 +308,9 @@
   /// \sa shouldDisplayNotesAsEvents
   Optional<bool> DisplayNotesAsEvents;
 
+  /// \sa shouldDisplayMacroExpansions
+  Optional<bool> DisplayMacroExpansions;
+
   /// \sa shouldAggressivelySimplifyBinaryOperation
   Optional<bool> AggressiveBinaryOperationSimplification;
 
@@ -683,6 +686,13 @@
   /// to false when unset.
   bool shouldDisplayNotesAsEvents();
 
+  /// Returns true if macros related to the bugpath should be expanded and
+  /// included in the plist output.
+  ///
+  /// This is controlled by the 'expand-macros' option, which defaults to false
+  /// when unset.
+  bool shouldDisplayMacroExpansions();
+
   /// Returns true if SValBuilder should rearrange comparisons and additive
   /// operations of symbolic expressions which consist of a sum of a symbol and
   /// a concrete integer into the format where symbols are on the left-hand
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to