10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/RecursiveASTVisitor.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
15 using namespace clang;
24 const char CastSequence[] =
"sequence";
27 const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
28 if (
const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
29 return BT->getKind() == BuiltinType::NullPtr;
38 StatementMatcher makeCastSequenceMatcher() {
39 StatementMatcher ImplicitCastToNull = implicitCastExpr(
40 anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
41 unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))),
42 unless(hasSourceExpression(hasType(sugaredNullptrType()))));
45 ast_type_traits::TK_AsIs,
46 castExpr(anyOf(ImplicitCastToNull,
47 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
48 unless(hasAncestor(explicitCastExpr())))
52 bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
53 const SourceManager &SM) {
54 return SM.isWrittenInSameFile(StartLoc, EndLoc);
60 void replaceWithNullptr(ClangTidyCheck &Check, SourceManager &SM,
61 SourceLocation StartLoc, SourceLocation EndLoc) {
62 CharSourceRange
Range(SourceRange(StartLoc, EndLoc),
true);
66 SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
67 bool NeedsSpace = isAlphanumeric(*SM.getCharacterData(PreviousLocation));
68 Check.diag(
Range.getBegin(),
"use nullptr") << FixItHint::CreateReplacement(
69 Range, NeedsSpace ?
" nullptr" :
"nullptr");
79 StringRef getOutermostMacroName(SourceLocation
Loc,
const SourceManager &SM,
80 const LangOptions &LO) {
81 assert(
Loc.isMacroID());
82 SourceLocation OutermostMacroLoc;
84 while (
Loc.isMacroID()) {
85 OutermostMacroLoc =
Loc;
86 Loc = SM.getImmediateMacroCallerLoc(
Loc);
89 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
95 class MacroArgUsageVisitor :
public RecursiveASTVisitor<MacroArgUsageVisitor> {
97 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
98 : CastLoc(CastLoc), SM(SM), Visited(false), CastFound(false),
100 assert(CastLoc.isFileID());
103 bool TraverseStmt(Stmt *S) {
104 bool VisitedPreviously = Visited;
106 if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
113 if (!VisitedPreviously) {
114 if (Visited && !CastFound) {
128 bool VisitStmt(Stmt *S) {
129 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
133 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
134 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
135 Cast->getCastKind() == CK_NullToMemberPointer))
141 bool TraverseInitListExpr(InitListExpr *S) {
146 return RecursiveASTVisitor<MacroArgUsageVisitor>::
147 TraverseSynOrSemInitListExpr(
148 S->isSemanticForm() ? S : S->getSemanticForm());
151 bool foundInvalid()
const {
return InvalidFound; }
154 SourceLocation CastLoc;
155 const SourceManager &SM;
173 class CastSequenceVisitor :
public RecursiveASTVisitor<CastSequenceVisitor> {
175 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
176 ClangTidyCheck &check)
177 : SM(Context.getSourceManager()), Context(Context),
178 NullMacros(NullMacros), Check(check), FirstSubExpr(nullptr),
179 PruneSubtree(false) {}
181 bool TraverseStmt(Stmt *S) {
184 PruneSubtree =
false;
187 return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
192 bool VisitStmt(Stmt *S) {
193 auto *C = dyn_cast<CastExpr>(S);
195 if (
auto *
E = dyn_cast<CXXDefaultArgExpr>(S)) {
196 C = dyn_cast<CastExpr>(
E->getExpr());
197 FirstSubExpr =
nullptr;
200 FirstSubExpr =
nullptr;
204 auto* CastSubExpr = C->getSubExpr()->IgnoreParens();
206 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr)) {
211 FirstSubExpr = CastSubExpr;
213 if (C->getCastKind() != CK_NullToPointer &&
214 C->getCastKind() != CK_NullToMemberPointer) {
218 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
219 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
226 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
227 SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
228 FileLocEnd = SM.getFileLoc(EndLoc);
229 SourceLocation ImmediateMacroArgLoc, MacroLoc;
231 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
232 ImmediateMacroArgLoc != FileLocStart)
233 return skipSubTree();
235 if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
236 allArgUsesValid(C)) {
237 replaceWithNullptr(Check, SM, FileLocStart, FileLocEnd);
242 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
243 StringRef OutermostMacroName =
244 getOutermostMacroName(StartLoc, SM, Context.getLangOpts());
247 if (!llvm::is_contained(NullMacros, OutermostMacroName))
248 return skipSubTree();
250 StartLoc = SM.getFileLoc(StartLoc);
251 EndLoc = SM.getFileLoc(EndLoc);
254 if (!isReplaceableRange(StartLoc, EndLoc, SM)) {
255 return skipSubTree();
257 replaceWithNullptr(Check, SM, StartLoc, EndLoc);
270 bool allArgUsesValid(
const CastExpr *
CE) {
271 SourceLocation CastLoc =
CE->getBeginLoc();
275 SourceLocation ArgLoc, MacroLoc;
276 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
280 ast_type_traits::DynTypedNode ContainingAncestor;
281 if (!findContainingAncestor(
282 ast_type_traits::DynTypedNode::create<Stmt>(*
CE), MacroLoc,
291 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
292 if (
const auto *D = ContainingAncestor.get<
Decl>())
293 ArgUsageVisitor.TraverseDecl(const_cast<Decl *>(D));
294 else if (
const auto *S = ContainingAncestor.get<Stmt>())
295 ArgUsageVisitor.TraverseStmt(const_cast<Stmt *>(S));
297 llvm_unreachable(
"Unhandled ContainingAncestor node type");
299 return !ArgUsageVisitor.foundInvalid();
310 bool getMacroAndArgLocations(SourceLocation
Loc, SourceLocation &ArgLoc,
311 SourceLocation &MacroLoc) {
312 assert(
Loc.isMacroID() &&
"Only reasonable to call this on macros");
318 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
319 const SrcMgr::SLocEntry *
E = &SM.getSLocEntry(LocInfo.first);
320 const SrcMgr::ExpansionInfo &Expansion =
E->getExpansion();
322 SourceLocation OldArgLoc = ArgLoc;
323 ArgLoc = Expansion.getExpansionLocStart();
324 if (!Expansion.isMacroArgExpansion()) {
325 if (!MacroLoc.isFileID())
329 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
330 return llvm::is_contained(NullMacros,
Name);
333 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
335 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
336 if (ArgLoc.isFileID())
341 FileID MacroFID = SM.getFileID(MacroLoc);
342 if (SM.isInFileID(ArgLoc, MacroFID)) {
349 llvm_unreachable(
"getMacroAndArgLocations");
363 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
364 if (TestLoc.isFileID()) {
368 SourceLocation
Loc = TestLoc, MacroLoc;
371 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(
Loc);
372 const SrcMgr::SLocEntry *
E = &SM.getSLocEntry(LocInfo.first);
373 const SrcMgr::ExpansionInfo &Expansion =
E->getExpansion();
375 Loc = Expansion.getExpansionLocStart();
377 if (!Expansion.isMacroArgExpansion()) {
378 if (
Loc.isFileID()) {
379 return Loc == TestMacroLoc;
388 MacroLoc = SM.getImmediateExpansionRange(
Loc).getBegin();
389 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
394 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
395 if (
Loc.isFileID()) {
402 llvm_unreachable(
"expandsFrom");
410 bool findContainingAncestor(ast_type_traits::DynTypedNode Start,
411 SourceLocation MacroLoc,
412 ast_type_traits::DynTypedNode &Result) {
417 assert(MacroLoc.isFileID());
420 const auto &
Parents = Context.getParents(Start);
428 for (
const auto &
Parent : Parents) {
429 if (!
Parent.get<InitListExpr>())
438 Loc = D->getBeginLoc();
439 else if (
const auto *S =
Parent.get<Stmt>())
440 Loc = S->getBeginLoc();
445 if (!expandsFrom(
Loc, MacroLoc)) {
453 llvm_unreachable(
"findContainingAncestor");
459 ArrayRef<StringRef> NullMacros;
460 ClangTidyCheck &Check;
469 NullMacrosStr(Options.get(
"NullMacros",
"")) {
470 StringRef(NullMacrosStr).split(NullMacros,
",");
478 Finder->addMatcher(makeCastSequenceMatcher(),
this);
482 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
483 assert(NullCast &&
"Bad Callback. No node provided");
488 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
489 .TraverseStmt(const_cast<CastExpr *>(NullCast));