11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/RecursiveASTVisitor.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "clang/Lex/Lexer.h" 16 using namespace clang;
25 const char CastSequence[] =
"sequence";
28 const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
29 if (
const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
30 return BT->getKind() == BuiltinType::NullPtr;
39 StatementMatcher makeCastSequenceMatcher() {
40 StatementMatcher ImplicitCastToNull = implicitCastExpr(
41 anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
42 unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))),
43 unless(hasSourceExpression(hasType(sugaredNullptrType()))));
45 return castExpr(anyOf(ImplicitCastToNull,
46 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
47 unless(hasAncestor(explicitCastExpr())))
51 bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
52 const SourceManager &SM) {
53 return SM.isWrittenInSameFile(StartLoc, EndLoc);
59 void replaceWithNullptr(ClangTidyCheck &Check, SourceManager &SM,
60 SourceLocation StartLoc, SourceLocation EndLoc) {
61 CharSourceRange
Range(SourceRange(StartLoc, EndLoc),
true);
65 SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
66 bool NeedsSpace = isAlphanumeric(*SM.getCharacterData(PreviousLocation));
67 Check.diag(
Range.getBegin(),
"use nullptr") << FixItHint::CreateReplacement(
68 Range, NeedsSpace ?
" nullptr" :
"nullptr");
78 StringRef getOutermostMacroName(SourceLocation
Loc,
const SourceManager &SM,
79 const LangOptions &LO) {
80 assert(Loc.isMacroID());
81 SourceLocation OutermostMacroLoc;
83 while (Loc.isMacroID()) {
84 OutermostMacroLoc =
Loc;
85 Loc = SM.getImmediateMacroCallerLoc(Loc);
88 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
94 class MacroArgUsageVisitor :
public RecursiveASTVisitor<MacroArgUsageVisitor> {
96 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
97 : CastLoc(CastLoc), SM(SM), Visited(false), CastFound(false),
99 assert(CastLoc.isFileID());
102 bool TraverseStmt(Stmt *S) {
103 bool VisitedPreviously = Visited;
105 if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
112 if (!VisitedPreviously) {
113 if (Visited && !CastFound) {
127 bool VisitStmt(Stmt *S) {
128 if (SM.getFileLoc(S->getLocStart()) != CastLoc)
132 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
133 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
134 Cast->getCastKind() == CK_NullToMemberPointer))
140 bool TraverseInitListExpr(InitListExpr *S) {
145 return RecursiveASTVisitor<MacroArgUsageVisitor>::
146 TraverseSynOrSemInitListExpr(
147 S->isSemanticForm() ? S : S->getSemanticForm());
150 bool foundInvalid()
const {
return InvalidFound; }
153 SourceLocation CastLoc;
154 const SourceManager &SM;
172 class CastSequenceVisitor :
public RecursiveASTVisitor<CastSequenceVisitor> {
174 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
175 ClangTidyCheck &check)
176 : SM(Context.getSourceManager()), Context(Context),
177 NullMacros(NullMacros), Check(check), FirstSubExpr(nullptr),
178 PruneSubtree(false) {}
180 bool TraverseStmt(Stmt *S) {
183 PruneSubtree =
false;
186 return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
191 bool VisitStmt(Stmt *S) {
192 auto *C = dyn_cast<CastExpr>(S);
194 if (
auto *E = dyn_cast<CXXDefaultArgExpr>(S)) {
195 C = dyn_cast<CastExpr>(E->getExpr());
196 FirstSubExpr =
nullptr;
199 FirstSubExpr =
nullptr;
203 auto* CastSubExpr = C->getSubExpr()->IgnoreParens();
205 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr)) {
210 FirstSubExpr = CastSubExpr;
212 if (C->getCastKind() != CK_NullToPointer &&
213 C->getCastKind() != CK_NullToMemberPointer) {
217 SourceLocation StartLoc = FirstSubExpr->getLocStart();
218 SourceLocation EndLoc = FirstSubExpr->getLocEnd();
225 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
226 SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
227 FileLocEnd = SM.getFileLoc(EndLoc);
228 SourceLocation ImmediateMacroArgLoc, MacroLoc;
230 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
231 ImmediateMacroArgLoc != FileLocStart)
232 return skipSubTree();
234 if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
235 allArgUsesValid(C)) {
236 replaceWithNullptr(Check, SM, FileLocStart, FileLocEnd);
241 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
242 StringRef OutermostMacroName =
243 getOutermostMacroName(StartLoc, SM, Context.getLangOpts());
246 if (std::find(NullMacros.begin(), NullMacros.end(), OutermostMacroName) ==
248 return skipSubTree();
251 StartLoc = SM.getFileLoc(StartLoc);
252 EndLoc = SM.getFileLoc(EndLoc);
255 if (!isReplaceableRange(StartLoc, EndLoc, SM)) {
256 return skipSubTree();
258 replaceWithNullptr(Check, SM, StartLoc, EndLoc);
271 bool allArgUsesValid(
const CastExpr *CE) {
272 SourceLocation CastLoc = CE->getLocStart();
276 SourceLocation ArgLoc, MacroLoc;
277 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
281 ast_type_traits::DynTypedNode ContainingAncestor;
282 if (!findContainingAncestor(
283 ast_type_traits::DynTypedNode::create<Stmt>(*CE), MacroLoc,
292 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
293 if (
const auto *D = ContainingAncestor.get<Decl>())
294 ArgUsageVisitor.TraverseDecl(const_cast<Decl *>(D));
295 else if (
const auto *S = ContainingAncestor.get<Stmt>())
296 ArgUsageVisitor.TraverseStmt(const_cast<Stmt *>(S));
298 llvm_unreachable(
"Unhandled ContainingAncestor node type");
300 return !ArgUsageVisitor.foundInvalid();
311 bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
312 SourceLocation &MacroLoc) {
313 assert(Loc.isMacroID() &&
"Only reasonble to call this on macros");
319 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
320 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
321 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
323 SourceLocation OldArgLoc = ArgLoc;
324 ArgLoc = Expansion.getExpansionLocStart();
325 if (!Expansion.isMacroArgExpansion()) {
326 if (!MacroLoc.isFileID())
330 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
331 return std::find(NullMacros.begin(), NullMacros.end(),
Name) !=
335 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
337 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
338 if (ArgLoc.isFileID())
343 FileID MacroFID = SM.getFileID(MacroLoc);
344 if (SM.isInFileID(ArgLoc, MacroFID)) {
351 llvm_unreachable(
"getMacroAndArgLocations");
365 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
366 if (TestLoc.isFileID()) {
370 SourceLocation Loc = TestLoc, MacroLoc;
373 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
374 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
375 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
377 Loc = Expansion.getExpansionLocStart();
379 if (!Expansion.isMacroArgExpansion()) {
380 if (Loc.isFileID()) {
381 return Loc == TestMacroLoc;
390 MacroLoc = SM.getImmediateExpansionRange(Loc).getBegin();
391 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
396 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
397 if (Loc.isFileID()) {
404 llvm_unreachable(
"expandsFrom");
412 bool findContainingAncestor(ast_type_traits::DynTypedNode Start,
413 SourceLocation MacroLoc,
414 ast_type_traits::DynTypedNode &Result) {
419 assert(MacroLoc.isFileID());
422 const auto &Parents = Context.getParents(Start);
425 if (Parents.size() > 1) {
430 for (
const auto &Parent : Parents) {
431 if (!Parent.get<InitListExpr>())
436 const ast_type_traits::DynTypedNode &Parent = Parents[0];
439 if (
const auto *D = Parent.get<Decl>())
440 Loc = D->getLocStart();
441 else if (
const auto *S = Parent.get<Stmt>())
442 Loc = S->getLocStart();
447 if (!expandsFrom(Loc, MacroLoc)) {
455 llvm_unreachable(
"findContainingAncestor");
461 ArrayRef<StringRef> NullMacros;
462 ClangTidyCheck &Check;
471 NullMacrosStr(Options.get(
"NullMacros",
"")) {
472 StringRef(NullMacrosStr).split(NullMacros,
",");
484 Finder->addMatcher(makeCastSequenceMatcher(),
this);
488 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
489 assert(NullCast &&
"Bad Callback. No node provided");
494 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
495 .TraverseStmt(const_cast<CastExpr *>(NullCast));
SourceLocation Loc
'#' location in the include directive
AST_MATCHER(BinaryOperator, isAssignmentOperator)
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.
Some operations such as code completion produce a set of candidates.
LangOptions getLangOpts() const
Returns the language options from the context.
Base class for all clang-tidy checks.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
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.