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()))));
44 return castExpr(anyOf(ImplicitCastToNull,
45 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
46 unless(hasAncestor(explicitCastExpr())))
50 bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
51 const SourceManager &SM) {
52 return SM.isWrittenInSameFile(StartLoc, EndLoc);
58 void replaceWithNullptr(ClangTidyCheck &Check, SourceManager &SM,
59 SourceLocation StartLoc, SourceLocation EndLoc) {
60 CharSourceRange
Range(SourceRange(StartLoc, EndLoc),
true);
64 SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
65 bool NeedsSpace = isAlphanumeric(*SM.getCharacterData(PreviousLocation));
66 Check.diag(
Range.getBegin(),
"use nullptr") << FixItHint::CreateReplacement(
67 Range, NeedsSpace ?
" nullptr" :
"nullptr");
77 StringRef getOutermostMacroName(SourceLocation
Loc,
const SourceManager &SM,
78 const LangOptions &LO) {
79 assert(Loc.isMacroID());
80 SourceLocation OutermostMacroLoc;
82 while (Loc.isMacroID()) {
83 OutermostMacroLoc =
Loc;
84 Loc = SM.getImmediateMacroCallerLoc(Loc);
87 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
93 class MacroArgUsageVisitor :
public RecursiveASTVisitor<MacroArgUsageVisitor> {
95 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
96 : CastLoc(CastLoc), SM(SM), Visited(false), CastFound(false),
98 assert(CastLoc.isFileID());
101 bool TraverseStmt(Stmt *S) {
102 bool VisitedPreviously = Visited;
104 if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
111 if (!VisitedPreviously) {
112 if (Visited && !CastFound) {
126 bool VisitStmt(Stmt *S) {
127 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
131 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
132 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
133 Cast->getCastKind() == CK_NullToMemberPointer))
139 bool TraverseInitListExpr(InitListExpr *S) {
144 return RecursiveASTVisitor<MacroArgUsageVisitor>::
145 TraverseSynOrSemInitListExpr(
146 S->isSemanticForm() ? S : S->getSemanticForm());
149 bool foundInvalid()
const {
return InvalidFound; }
152 SourceLocation CastLoc;
153 const SourceManager &SM;
171 class CastSequenceVisitor :
public RecursiveASTVisitor<CastSequenceVisitor> {
173 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
174 ClangTidyCheck &check)
175 : SM(Context.getSourceManager()), Context(Context),
176 NullMacros(NullMacros), Check(check), FirstSubExpr(nullptr),
177 PruneSubtree(false) {}
179 bool TraverseStmt(Stmt *S) {
182 PruneSubtree =
false;
185 return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
190 bool VisitStmt(Stmt *S) {
191 auto *C = dyn_cast<CastExpr>(S);
193 if (
auto *
E = dyn_cast<CXXDefaultArgExpr>(S)) {
194 C = dyn_cast<CastExpr>(
E->getExpr());
195 FirstSubExpr =
nullptr;
198 FirstSubExpr =
nullptr;
202 auto* CastSubExpr = C->getSubExpr()->IgnoreParens();
204 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr)) {
209 FirstSubExpr = CastSubExpr;
211 if (C->getCastKind() != CK_NullToPointer &&
212 C->getCastKind() != CK_NullToMemberPointer) {
216 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
217 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
224 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
225 SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
226 FileLocEnd = SM.getFileLoc(EndLoc);
227 SourceLocation ImmediateMacroArgLoc, MacroLoc;
229 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
230 ImmediateMacroArgLoc != FileLocStart)
231 return skipSubTree();
233 if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
234 allArgUsesValid(C)) {
235 replaceWithNullptr(Check, SM, FileLocStart, FileLocEnd);
240 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
241 StringRef OutermostMacroName =
242 getOutermostMacroName(StartLoc, SM, Context.getLangOpts());
245 if (!llvm::is_contained(NullMacros, OutermostMacroName))
246 return skipSubTree();
248 StartLoc = SM.getFileLoc(StartLoc);
249 EndLoc = SM.getFileLoc(EndLoc);
252 if (!isReplaceableRange(StartLoc, EndLoc, SM)) {
253 return skipSubTree();
255 replaceWithNullptr(Check, SM, StartLoc, EndLoc);
268 bool allArgUsesValid(
const CastExpr *CE) {
269 SourceLocation CastLoc = CE->getBeginLoc();
273 SourceLocation ArgLoc, MacroLoc;
274 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
278 ast_type_traits::DynTypedNode ContainingAncestor;
279 if (!findContainingAncestor(
280 ast_type_traits::DynTypedNode::create<Stmt>(*CE), MacroLoc,
289 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
290 if (
const auto *D = ContainingAncestor.get<
Decl>())
291 ArgUsageVisitor.TraverseDecl(const_cast<Decl *>(D));
292 else if (
const auto *S = ContainingAncestor.get<Stmt>())
293 ArgUsageVisitor.TraverseStmt(const_cast<Stmt *>(S));
295 llvm_unreachable(
"Unhandled ContainingAncestor node type");
297 return !ArgUsageVisitor.foundInvalid();
308 bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
309 SourceLocation &MacroLoc) {
310 assert(Loc.isMacroID() &&
"Only reasonble to call this on macros");
316 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
317 const SrcMgr::SLocEntry *
E = &SM.getSLocEntry(LocInfo.first);
318 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
320 SourceLocation OldArgLoc = ArgLoc;
321 ArgLoc = Expansion.getExpansionLocStart();
322 if (!Expansion.isMacroArgExpansion()) {
323 if (!MacroLoc.isFileID())
327 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
328 return llvm::is_contained(NullMacros, Name);
331 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
333 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
334 if (ArgLoc.isFileID())
339 FileID MacroFID = SM.getFileID(MacroLoc);
340 if (SM.isInFileID(ArgLoc, MacroFID)) {
347 llvm_unreachable(
"getMacroAndArgLocations");
361 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
362 if (TestLoc.isFileID()) {
366 SourceLocation Loc = TestLoc, MacroLoc;
369 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
370 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
371 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
373 Loc = Expansion.getExpansionLocStart();
375 if (!Expansion.isMacroArgExpansion()) {
376 if (Loc.isFileID()) {
377 return Loc == TestMacroLoc;
386 MacroLoc = SM.getImmediateExpansionRange(Loc).getBegin();
387 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
392 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
393 if (Loc.isFileID()) {
400 llvm_unreachable(
"expandsFrom");
408 bool findContainingAncestor(ast_type_traits::DynTypedNode Start,
409 SourceLocation MacroLoc,
410 ast_type_traits::DynTypedNode &Result) {
415 assert(MacroLoc.isFileID());
418 const auto &
Parents = Context.getParents(Start);
426 for (
const auto &
Parent : Parents) {
427 if (!
Parent.get<InitListExpr>())
432 const ast_type_traits::DynTypedNode &
Parent = Parents[0];
435 if (
const auto *D = Parent.get<
Decl>())
436 Loc = D->getBeginLoc();
437 else if (
const auto *S = Parent.get<Stmt>())
438 Loc = S->getBeginLoc();
443 if (!expandsFrom(Loc, MacroLoc)) {
451 llvm_unreachable(
"findContainingAncestor");
457 ArrayRef<StringRef> NullMacros;
458 ClangTidyCheck &Check;
467 NullMacrosStr(Options.get(
"NullMacros",
"")) {
468 StringRef(NullMacrosStr).split(NullMacros,
",");
480 Finder->addMatcher(makeCastSequenceMatcher(),
this);
484 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
485 assert(NullCast &&
"Bad Callback. No node provided");
490 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
491 .TraverseStmt(const_cast<CastExpr *>(NullCast));
SourceLocation Loc
'#' location in the include directive
const FunctionDecl * Decl
Some operations such as code completion produce a set of candidates.
AST_MATCHER(Expr, isMacroID)
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.