11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Lex/Lexer.h"
16 using namespace clang::ast_matchers;
22 void ExplicitConstructorCheck::registerMatchers(MatchFinder *
Finder) {
25 if (!getLangOpts().CPlusPlus)
27 Finder->addMatcher(cxxConstructorDecl(unless(isInstantiated())).bind(
"ctor"),
30 cxxConversionDecl(unless(anyOf(isExplicit(),
40 static SourceRange
FindToken(
const SourceManager &Sources,
42 SourceLocation StartLoc, SourceLocation EndLoc,
43 bool (*Pred)(
const Token &)) {
44 if (StartLoc.isMacroID() || EndLoc.isMacroID())
46 FileID
File = Sources.getFileID(Sources.getSpellingLoc(StartLoc));
47 StringRef Buf = Sources.getBufferData(File);
48 const char *StartChar = Sources.getCharacterData(StartLoc);
49 Lexer Lex(StartLoc, LangOpts, StartChar, StartChar, Buf.end());
50 Lex.SetCommentRetentionState(
true);
53 Lex.LexFromRawLexer(Tok);
56 Lex.LexFromRawLexer(NextTok);
57 return SourceRange(Tok.getLocation(), NextTok.getLocation());
59 }
while (Tok.isNot(tok::eof) && Tok.getLocation() < EndLoc);
67 return D->getName() ==
"initializer_list" &&
68 D->getQualifiedNameAsString() ==
"std::initializer_list";
72 Type = Type.getCanonicalType();
73 if (
const auto *TS = Type->getAs<TemplateSpecializationType>()) {
74 if (
const TemplateDecl *TD = TS->getTemplateName().getAsTemplateDecl())
77 if (
const auto *RT = Type->getAs<RecordType>()) {
78 if (
const auto *Specialization =
79 dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl()))
85 void ExplicitConstructorCheck::check(
const MatchFinder::MatchResult &
Result) {
86 constexpr
char WarningMessage[] =
87 "%0 must be marked explicit to avoid unintentional implicit conversions";
89 if (
const auto *Conversion =
90 Result.Nodes.getNodeAs<CXXConversionDecl>(
"conversion")) {
91 SourceLocation
Loc = Conversion->getLocation();
96 diag(Loc, WarningMessage)
97 << Conversion << FixItHint::CreateInsertion(Loc,
"explicit ");
101 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor");
104 if (Ctor->isOutOfLine() || Ctor->isImplicit() || Ctor->isDeleted() ||
105 Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1)
109 Ctor->getParamDecl(0)->getType().getNonReferenceType());
110 if (Ctor->isExplicit() &&
111 (Ctor->isCopyOrMoveConstructor() || takesInitializerList)) {
112 auto isKWExplicit = [](
const Token &Tok) {
113 return Tok.is(tok::raw_identifier) &&
114 Tok.getRawIdentifier() ==
"explicit";
116 SourceRange ExplicitTokenRange =
117 FindToken(*Result.SourceManager, getLangOpts(),
118 Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit);
119 StringRef ConstructorDescription;
120 if (Ctor->isMoveConstructor())
121 ConstructorDescription =
"move";
122 else if (Ctor->isCopyConstructor())
123 ConstructorDescription =
"copy";
125 ConstructorDescription =
"initializer-list";
127 auto Diag = diag(Ctor->getLocation(),
128 "%0 constructor should not be declared explicit")
129 << ConstructorDescription;
130 if (ExplicitTokenRange.isValid()) {
131 Diag << FixItHint::CreateRemoval(
132 CharSourceRange::getCharRange(ExplicitTokenRange));
137 if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() ||
138 takesInitializerList)
141 bool SingleArgument =
142 Ctor->getNumParams() == 1 && !Ctor->getParamDecl(0)->isParameterPack();
143 SourceLocation
Loc = Ctor->getLocation();
144 diag(Loc, WarningMessage)
146 ?
"single-argument constructors"
147 :
"constructors that are callable with a single argument")
148 << FixItHint::CreateInsertion(Loc,
"explicit ");
SourceLocation Loc
'#' location in the include directive
static bool isStdInitializerList(QualType Type)
std::unique_ptr< ast_matchers::MatchFinder > Finder
static SourceRange FindToken(const SourceManager &Sources, const LangOptions &LangOpts, SourceLocation StartLoc, SourceLocation EndLoc, bool(*Pred)(const Token &))
static bool declIsStdInitializerList(const NamedDecl *D)