clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

ExplicitConstructorCheck.cpp
Go to the documentation of this file.
1 //===--- ExplicitConstructorCheck.cpp - clang-tidy ------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "ExplicitConstructorCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Lex/Lexer.h"
15
16 using namespace clang::ast_matchers;
17
18 namespace clang {
19 namespace tidy {
20 namespace google {
21
22 void ExplicitConstructorCheck::registerMatchers(MatchFinder *Finder) {
23 // Only register the matchers for C++; the functionality currently does not
24 // provide any benefit to other languages, despite being benign.
25 if (getLangOpts().CPlusPlus)
26 Finder->addMatcher(
27 cxxConstructorDecl(unless(isInstantiated())).bind("ctor"), this);
28 }
29
30 // Looks for the token matching the predicate and returns the range of the found
31 // token including trailing whitespace.
32 static SourceRange FindToken(const SourceManager &Sources, LangOptions LangOpts,
33 SourceLocation StartLoc, SourceLocation EndLoc,
34 bool (*Pred)(const Token &)) {
35 if (StartLoc.isMacroID() || EndLoc.isMacroID())
36 return SourceRange();
37 FileID File = Sources.getFileID(Sources.getSpellingLoc(StartLoc));
38 StringRef Buf = Sources.getBufferData(File);
39 const char *StartChar = Sources.getCharacterData(StartLoc);
40 Lexer Lex(StartLoc, LangOpts, StartChar, StartChar, Buf.end());
41 Lex.SetCommentRetentionState(true);
42 Token Tok;
43 do {
44 Lex.LexFromRawLexer(Tok);
45 if (Pred(Tok)) {
46 Token NextTok;
47 Lex.LexFromRawLexer(NextTok);
48 return SourceRange(Tok.getLocation(), NextTok.getLocation());
49 }
50 } while (Tok.isNot(tok::eof) && Tok.getLocation() < EndLoc);
51
52 return SourceRange();
53 }
54
55 static bool declIsStdInitializerList(const NamedDecl *D) {
56 // First use the fast getName() method to avoid unnecessary calls to the
57 // slow getQualifiedNameAsString().
58 return D->getName() == "initializer_list" &&
59 D->getQualifiedNameAsString() == "std::initializer_list";
60 }
61
62 static bool isStdInitializerList(QualType Type) {
63 Type = Type.getCanonicalType();
64 if (const auto *TS = Type->getAs<TemplateSpecializationType>()) {
65 if (const TemplateDecl *TD = TS->getTemplateName().getAsTemplateDecl())
66 return declIsStdInitializerList(TD);
67 }
68 if (const auto *RT = Type->getAs<RecordType>()) {
69 if (const auto *Specialization =
70 dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl()))
71 return declIsStdInitializerList(Specialization->getSpecializedTemplate());
72 }
73 return false;
74 }
75
76 void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
77 const CXXConstructorDecl *Ctor =
78 Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
79 // Do not be confused: isExplicit means 'explicit' keyword is present,
80 // isImplicit means that it's a compiler-generated constructor.
81 if (Ctor->isOutOfLine() || Ctor->isImplicit() || Ctor->isDeleted() ||
82 Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1)
83 return;
84
85 bool takesInitializerList = isStdInitializerList(
86 Ctor->getParamDecl(0)->getType().getNonReferenceType());
87 if (Ctor->isExplicit() &&
88 (Ctor->isCopyOrMoveConstructor() || takesInitializerList)) {
89 auto isKWExplicit = [](const Token &Tok) {
90 return Tok.is(tok::raw_identifier) &&
91 Tok.getRawIdentifier() == "explicit";
92 };
93 SourceRange ExplicitTokenRange =
94 FindToken(*Result.SourceManager, Result.Context->getLangOpts(),
95 Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit);
96 StringRef ConstructorDescription;
97 if (Ctor->isMoveConstructor())
98 ConstructorDescription = "move";
99 else if (Ctor->isCopyConstructor())
100 ConstructorDescription = "copy";
101 else
102 ConstructorDescription = "initializer-list";
103
104 DiagnosticBuilder Diag =
105 diag(Ctor->getLocation(),
106 "%0 constructor should not be declared explicit")
107 << ConstructorDescription;
108 if (ExplicitTokenRange.isValid()) {
109 Diag << FixItHint::CreateRemoval(
110 CharSourceRange::getCharRange(ExplicitTokenRange));
111 }
112 return;
113 }
114
115 if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() ||
116 takesInitializerList)
117 return;
118
119 bool SingleArgument =
120 Ctor->getNumParams() == 1 && !Ctor->getParamDecl(0)->isParameterPack();
121 SourceLocation Loc = Ctor->getLocation();
122 diag(Loc,
123 "%0 must be marked explicit to avoid unintentional implicit conversions")
124 << (SingleArgument
125 ? "single-argument constructors"
126 : "constructors that are callable with a single argument")
127 << FixItHint::CreateInsertion(Loc, "explicit ");
128 }
129
130 } // namespace google
131 } // namespace tidy
132 } // namespace clang
clang::tidy::google::FindToken
static SourceRange FindToken(const SourceManager &Sources, LangOptions LangOpts, SourceLocation StartLoc, SourceLocation EndLoc, bool(*Pred)(const Token &))
Definition: ExplicitConstructorCheck.cpp:32
Loc
SourceLocation Loc
'#' location in the include directive
Definition: IncludeOrderCheck.cpp:34
clang::tidy::google::isStdInitializerList
static bool isStdInitializerList(QualType Type)
Definition: ExplicitConstructorCheck.cpp:62
LangOpts
LangOptions LangOpts
Definition: ClangTidy.cpp:168
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
File
HeaderHandle File
Definition: PreprocessorTracker.cpp:526
ExplicitConstructorCheck.h
clang::tidy::google::declIsStdInitializerList
static bool declIsStdInitializerList(const NamedDecl *D)
Definition: ExplicitConstructorCheck.cpp:55
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121