Index: clang-tidy/modernize/UseNullptrCheck.cpp =================================================================== --- clang-tidy/modernize/UseNullptrCheck.cpp +++ clang-tidy/modernize/UseNullptrCheck.cpp @@ -150,6 +150,16 @@ return true; } + bool TraverseInitListExpr(InitListExpr *S) { + // Only go through the semantic form of the InitListExpr, because + // ImplicitCast might not appear in the syntactic form, and this results in + // finding usages of the macro argument that don't have a ImplicitCast as an + // ancestor (thus invalidating the replacement) when they actually have. + return RecursiveASTVisitor:: + TraverseSynOrSemInitListExpr( + S->isSemanticForm() ? S : S->getSemanticForm()); + } + bool foundInvalid() const { return InvalidFound; } private: @@ -273,7 +283,7 @@ // Visit children of this containing parent looking for the least-descended // nodes of the containing parent which are macro arg expansions that expand // from the given arg location. - // Visitor needs: arg loc + // Visitor needs: arg loc. MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM); if (const auto *D = ContainingAncestor.get()) ArgUsageVisitor.TraverseDecl(const_cast(D)); @@ -345,8 +355,8 @@ /// also handled. /// /// False means either: - /// - TestLoc is not from a macro expansion - /// - TestLoc is from a different macro expansion + /// - TestLoc is not from a macro expansion. + /// - TestLoc is from a different macro expansion. bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) { if (TestLoc.isFileID()) { return false; @@ -399,8 +409,7 @@ ast_type_traits::DynTypedNode &Result) { // Below we're only following the first parent back up the AST. This should // be fine since for the statements we care about there should only be one - // parent as far up as we care. If this assumption doesn't hold, need to - // revisit what to do here. + // parent, except for the case specified below. assert(MacroLoc.isFileID()); @@ -408,8 +417,16 @@ const auto &Parents = Context.getParents(Start); if (Parents.empty()) return false; - assert(Parents.size() == 1 && - "Found an ancestor with more than one parent!"); + if (Parents.size() > 1) { + // If there are more than one parents, don't do the replacement unless + // they are InitListsExpr (semantic and syntactic form). In this case we + // can choose any one here, and the ASTVisitor will take care of + // traversing the right one. + for (const auto &Parent : Parents) { + if (!Parent.get()) + return false; + } + } const ast_type_traits::DynTypedNode &Parent = Parents[0]; Index: test/clang-tidy/modernize-use-nullptr.cpp =================================================================== --- test/clang-tidy/modernize-use-nullptr.cpp +++ test/clang-tidy/modernize-use-nullptr.cpp @@ -174,4 +174,13 @@ #define CALL(X) X OPTIONAL_CODE(NOT_NULL); CALL(NOT_NULL); + +#define ENTRY(X) {X} + struct A { + int *Ptr; + } a[2] = {ENTRY(0), {0}}; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr + // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: use nullptr + // CHECK-FIXES: a[2] = {ENTRY(nullptr), {nullptr}}; +#undef ENTRY }