12#include "clang/AST/ASTContext.h"
13#include "clang/AST/RecursiveASTVisitor.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
15#include "clang/Lex/Lexer.h"
25 const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
26 if (
const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
27 return BT->getKind() == BuiltinType::NullPtr;
31AST_MATCHER(DecltypeType, decltypeTypeNullptrLiteral) {
32 if (
const Expr *E = Node.getUnderlyingExpr())
33 return isa<CXXNullPtrLiteralExpr>(E->IgnoreParens());
42 "^std::__cmp_cat::__unspec;"
43 "^std::__cmp_cat::__literal_zero";
59 auto ImplicitCastToNull = implicitCastExpr(
60 anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
61 anyOf(hasSourceExpression(gnuNullExpr()),
62 unless(hasImplicitDestinationType(
63 qualType(substTemplateTypeParmType())))),
64 unless(hasSourceExpression(hasType(sugaredNullptrType()))),
65 unless(hasImplicitDestinationType(
68 const auto IsOrHasDescendant = [](
const auto &InnerMatcher) {
69 return anyOf(InnerMatcher, hasDescendant(InnerMatcher));
73 castExpr(anyOf(ImplicitCastToNull,
74 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
75 unless(hasAncestor(explicitCastExpr())),
76 unless(hasAncestor(cxxRewrittenBinaryOperator())))
81 cxxRewrittenBinaryOperator(
85 expr().bind(
"matchBinopOperands"),
86 hasEitherOperand(IsOrHasDescendant(
87 implicitCastExpr(ImplicitCastToNull,
88 hasAncestor(cxxRewrittenBinaryOperator().bind(
89 "checkBinopOperands")))
92 unless(hasAncestor(functionDecl(isDefaulted())))),
96 Finder->addMatcher(typeLoc(loc(decltypeType(decltypeTypeNullptrLiteral())))
97 .bind(
"matchDecltypeNullptr"),
102 const SourceManager &SM) {
103 return SM.isWrittenInSameFile(StartLoc, EndLoc);
110 SourceLocation StartLoc, SourceLocation EndLoc) {
111 const CharSourceRange Range(SourceRange(StartLoc, EndLoc),
true);
115 const SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
116 const bool NeedsSpace =
117 isAlphanumeric(*SM.getCharacterData(PreviousLocation));
118 Check.diag(Range.getBegin(),
"use nullptr") << FixItHint::CreateReplacement(
119 Range, NeedsSpace ?
" nullptr" :
"nullptr");
130 const SourceManager &SM,
131 const LangOptions &LO) {
132 assert(Loc.isMacroID());
133 SourceLocation OutermostMacroLoc;
135 while (Loc.isMacroID()) {
136 OutermostMacroLoc = Loc;
137 Loc = SM.getImmediateMacroCallerLoc(Loc);
140 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
148class MacroArgUsageVisitor :
public RecursiveASTVisitor<MacroArgUsageVisitor> {
150 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
151 : CastLoc(CastLoc), SM(SM) {
152 assert(CastLoc.isFileID());
155 bool TraverseStmt(Stmt *S) {
156 const bool VisitedPreviously = Visited;
158 if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
165 if (!VisitedPreviously) {
166 if (Visited && !CastFound) {
180 bool VisitStmt(Stmt *S) {
181 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
185 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
186 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
187 Cast->getCastKind() == CK_NullToMemberPointer))
193 bool TraverseInitListExpr(InitListExpr *S) {
198 return RecursiveASTVisitor<MacroArgUsageVisitor>::
199 TraverseSynOrSemInitListExpr(
200 S->isSemanticForm() ? S : S->getSemanticForm());
203 bool foundInvalid()
const {
return InvalidFound; }
206 SourceLocation CastLoc;
207 const SourceManager &SM;
209 bool Visited =
false;
210 bool CastFound =
false;
211 bool InvalidFound =
false;
225class CastSequenceVisitor :
public RecursiveASTVisitor<CastSequenceVisitor> {
227 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
228 ClangTidyCheck &Check)
229 : SM(Context.getSourceManager()), Context(Context),
230 NullMacros(NullMacros), Check(Check) {}
232 bool TraverseStmt(Stmt *S) {
235 PruneSubtree =
false;
238 return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
243 bool VisitStmt(Stmt *S) {
244 auto *
C = dyn_cast<CastExpr>(S);
246 if (
auto *E = dyn_cast<CXXDefaultArgExpr>(S)) {
247 C = dyn_cast<CastExpr>(E->getExpr());
248 FirstSubExpr =
nullptr;
251 FirstSubExpr =
nullptr;
255 auto *CastSubExpr =
C->getSubExpr()->IgnoreParens();
257 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr))
261 FirstSubExpr = CastSubExpr;
263 if (
C->getCastKind() != CK_NullToPointer &&
264 C->getCastKind() != CK_NullToMemberPointer) {
268 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
269 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
276 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
277 const SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
278 FileLocEnd = SM.getFileLoc(EndLoc);
279 SourceLocation ImmediateMacroArgLoc, MacroLoc;
281 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
282 ImmediateMacroArgLoc != FileLocStart)
283 return skipSubTree();
286 allArgUsesValid(C)) {
292 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
293 const StringRef OutermostMacroName =
297 if (!llvm::is_contained(NullMacros, OutermostMacroName))
298 return skipSubTree();
300 StartLoc = SM.getFileLoc(StartLoc);
301 EndLoc = SM.getFileLoc(EndLoc);
305 return skipSubTree();
319 bool allArgUsesValid(
const CastExpr *CE) {
320 const SourceLocation CastLoc = CE->getBeginLoc();
324 SourceLocation ArgLoc, MacroLoc;
325 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
329 DynTypedNode ContainingAncestor;
330 if (!findContainingAncestor(DynTypedNode::create<Stmt>(*CE), MacroLoc,
339 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
340 if (
const auto *D = ContainingAncestor.get<Decl>())
341 ArgUsageVisitor.TraverseDecl(
const_cast<Decl *
>(D));
342 else if (
const auto *S = ContainingAncestor.get<Stmt>())
343 ArgUsageVisitor.TraverseStmt(
const_cast<Stmt *
>(S));
345 llvm_unreachable(
"Unhandled ContainingAncestor node type");
347 return !ArgUsageVisitor.foundInvalid();
358 bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
359 SourceLocation &MacroLoc) {
360 assert(Loc.isMacroID() &&
"Only reasonable to call this on macros");
366 const std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
367 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
368 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
370 const SourceLocation OldArgLoc = ArgLoc;
371 ArgLoc = Expansion.getExpansionLocStart();
372 if (!Expansion.isMacroArgExpansion()) {
373 if (!MacroLoc.isFileID())
376 const StringRef Name =
377 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
378 return llvm::is_contained(NullMacros, Name);
381 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
383 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
384 if (ArgLoc.isFileID())
389 const FileID MacroFID = SM.getFileID(MacroLoc);
390 if (SM.isInFileID(ArgLoc, MacroFID)) {
397 llvm_unreachable(
"getMacroAndArgLocations");
411 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
412 if (TestLoc.isFileID())
415 SourceLocation Loc = TestLoc, MacroLoc;
418 const std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
419 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
420 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
422 Loc = Expansion.getExpansionLocStart();
424 if (!Expansion.isMacroArgExpansion()) {
426 return Loc == TestMacroLoc;
434 MacroLoc = SM.getImmediateExpansionRange(Loc).getBegin();
435 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
440 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
441 if (Loc.isFileID()) {
448 llvm_unreachable(
"expandsFrom");
456 bool findContainingAncestor(DynTypedNode Start, SourceLocation MacroLoc,
457 DynTypedNode &Result) {
462 assert(MacroLoc.isFileID());
465 const auto &
Parents = Context.getParents(Start);
473 for (
const auto &Parent : Parents)
474 if (!Parent.get<InitListExpr>())
478 const DynTypedNode &Parent =
Parents[0];
481 if (
const auto *D = Parent.get<Decl>())
482 Loc =
D->getBeginLoc();
483 else if (
const auto *S = Parent.get<Stmt>())
484 Loc = S->getBeginLoc();
488 if (Loc.isValid() && !expandsFrom(Loc, MacroLoc)) {
496 llvm_unreachable(
"findContainingAncestor");
501 ArrayRef<StringRef> NullMacros;
502 ClangTidyCheck &Check;
503 Expr *FirstSubExpr =
nullptr;
504 bool PruneSubtree =
false;
511 NullMacrosStr(Options.get(
"NullMacros",
"NULL")),
512 IgnoredTypes(
utils::options::parseStringList(
514 UseNullptrT(Options.get(
"UseNullptrT", true)),
515 IncludeInserter(Options.getLocalOrGlobal(
"IncludeStyle",
516 utils::IncludeSorter::IS_LLVM),
517 areDiagsSelfContained()) {
518 NullMacrosStr.split(NullMacros,
",");
523 Preprocessor *ModuleExpanderPP) {
524 IncludeInserter.registerPreprocessor(PP);
528 Options.store(Opts,
"NullMacros", NullMacrosStr);
529 Options.store(Opts,
"IgnoredTypes",
531 Options.store(Opts,
"IncludeStyle", IncludeInserter.getStyle());
532 Options.store(Opts,
"UseNullptrT", UseNullptrT);
536 if (
const auto *MatchedTypeLoc =
537 Result.Nodes.getNodeAs<TypeLoc>(
"matchDecltypeNullptr")) {
538 diag(MatchedTypeLoc->getBeginLoc(),
"use std::nullptr_t instead")
539 << IncludeInserter.createIncludeInsertion(
540 Result.SourceManager->getFileID(MatchedTypeLoc->getBeginLoc()),
542 << FixItHint::CreateReplacement(MatchedTypeLoc->getSourceRange(),
547 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(
CastSequence);
548 assert(NullCast &&
"Bad Callback. No node provided");
550 if (Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
551 "matchBinopOperands") !=
552 Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
"checkBinopOperands"))
558 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
559 .TraverseStmt(
const_cast<CastExpr *
>(NullCast));
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
UseNullptrCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Create a matcher that finds implicit casts as well as the head of a sequence of zero or more nested e...
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
@ Type
An inlay hint that for a type annotation.
inline ::clang::ast_matchers::internal::Matcher< QualType > matchesAnyListedTypeName(llvm::ArrayRef< StringRef > NameList, bool CanonicalTypes)
AST_MATCHER(BinaryOperator, isRelationalOperator)
static StringRef getOutermostMacroName(SourceLocation Loc, const SourceManager &SM, const LangOptions &LO)
Returns the name of the outermost macro.
static constexpr char DefaultIgnoredTypes[]
static constexpr char CastSequence[]
static void replaceWithNullptr(ClangTidyCheck &Check, const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc)
Replaces the provided range with the text "nullptr", but only if the start and end location are both ...
static bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc, const SourceManager &SM)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
llvm::StringMap< ClangTidyValue > OptionMap