blob: d262f9fa89c6d1fecea7353c5855961dcbe7a784 [file] [log] [blame]
Alexander Kornienko72f1e752014-06-18 09:33:46 +00001//===--- 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
16using namespace clang::ast_matchers;
17
18namespace clang {
19namespace tidy {
Alexander Kornienkoed824e02015-03-05 13:46:14 +000020namespace google {
Alexander Kornienko72f1e752014-06-18 09:33:46 +000021
22void ExplicitConstructorCheck::registerMatchers(MatchFinder *Finder) {
Alexander Kornienkoad5074d2014-11-30 19:41:41 +000023 Finder->addMatcher(constructorDecl(unless(isInstantiated())).bind("ctor"),
24 this);
Alexander Kornienko72f1e752014-06-18 09:33:46 +000025}
26
27// Looks for the token matching the predicate and returns the range of the found
28// token including trailing whitespace.
Benjamin Kramere7103712015-03-23 12:49:15 +000029static SourceRange FindToken(const SourceManager &Sources, LangOptions LangOpts,
30 SourceLocation StartLoc, SourceLocation EndLoc,
31 bool (*Pred)(const Token &)) {
Alexander Kornienko72f1e752014-06-18 09:33:46 +000032 if (StartLoc.isMacroID() || EndLoc.isMacroID())
33 return SourceRange();
34 FileID File = Sources.getFileID(Sources.getSpellingLoc(StartLoc));
35 StringRef Buf = Sources.getBufferData(File);
36 const char *StartChar = Sources.getCharacterData(StartLoc);
37 Lexer Lex(StartLoc, LangOpts, StartChar, StartChar, Buf.end());
38 Lex.SetCommentRetentionState(true);
39 Token Tok;
40 do {
41 Lex.LexFromRawLexer(Tok);
42 if (Pred(Tok)) {
43 Token NextTok;
44 Lex.LexFromRawLexer(NextTok);
45 return SourceRange(Tok.getLocation(), NextTok.getLocation());
46 }
47 } while (Tok.isNot(tok::eof) && Tok.getLocation() < EndLoc);
48
49 return SourceRange();
50}
51
Benjamin Kramere7103712015-03-23 12:49:15 +000052static bool declIsStdInitializerList(const NamedDecl *D) {
Alexander Kornienkodd2dad02015-02-05 12:49:07 +000053 // First use the fast getName() method to avoid unnecessary calls to the
54 // slow getQualifiedNameAsString().
55 return D->getName() == "initializer_list" &&
56 D->getQualifiedNameAsString() == "std::initializer_list";
57}
58
Benjamin Kramere7103712015-03-23 12:49:15 +000059static bool isStdInitializerList(QualType Type) {
Alexander Kornienkodd2dad02015-02-05 12:49:07 +000060 Type = Type.getCanonicalType();
61 if (const auto *TS = Type->getAs<TemplateSpecializationType>()) {
62 if (const TemplateDecl *TD = TS->getTemplateName().getAsTemplateDecl())
63 return declIsStdInitializerList(TD);
64 }
65 if (const auto *RT = Type->getAs<RecordType>()) {
66 if (const auto *Specialization =
67 dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl()))
68 return declIsStdInitializerList(Specialization->getSpecializedTemplate());
Alexander Kornienko15c5e6a2014-11-27 11:11:47 +000069 }
70 return false;
71}
72
Alexander Kornienko72f1e752014-06-18 09:33:46 +000073void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
74 const CXXConstructorDecl *Ctor =
75 Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
76 // Do not be confused: isExplicit means 'explicit' keyword is present,
77 // isImplicit means that it's a compiler-generated constructor.
Alexander Kornienko15c5e6a2014-11-27 11:11:47 +000078 if (Ctor->isOutOfLine() || Ctor->isImplicit() || Ctor->isDeleted() ||
79 Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1)
Alexander Kornienko72f1e752014-06-18 09:33:46 +000080 return;
81
Alexander Kornienko15c5e6a2014-11-27 11:11:47 +000082 bool takesInitializerList = isStdInitializerList(
83 Ctor->getParamDecl(0)->getType().getNonReferenceType());
84 if (Ctor->isExplicit() &&
85 (Ctor->isCopyOrMoveConstructor() || takesInitializerList)) {
Alexander Kornienko72f1e752014-06-18 09:33:46 +000086 auto isKWExplicit = [](const Token &Tok) {
87 return Tok.is(tok::raw_identifier) &&
88 Tok.getRawIdentifier() == "explicit";
89 };
90 SourceRange ExplicitTokenRange =
91 FindToken(*Result.SourceManager, Result.Context->getLangOpts(),
92 Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit);
Alexander Kornienko15c5e6a2014-11-27 11:11:47 +000093 StringRef ConstructorDescription;
94 if (Ctor->isMoveConstructor())
95 ConstructorDescription = "move";
96 else if (Ctor->isCopyConstructor())
97 ConstructorDescription = "copy";
98 else
99 ConstructorDescription = "initializer-list";
100
Alexander Kornienko72f1e752014-06-18 09:33:46 +0000101 DiagnosticBuilder Diag =
Alexander Kornienko15c5e6a2014-11-27 11:11:47 +0000102 diag(Ctor->getLocation(),
103 "%0 constructor should not be declared explicit")
104 << ConstructorDescription;
Alexander Kornienko72f1e752014-06-18 09:33:46 +0000105 if (ExplicitTokenRange.isValid()) {
106 Diag << FixItHint::CreateRemoval(
107 CharSourceRange::getCharRange(ExplicitTokenRange));
108 }
Alexander Kornienko15c5e6a2014-11-27 11:11:47 +0000109 return;
Alexander Kornienko72f1e752014-06-18 09:33:46 +0000110 }
111
112 if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() ||
Alexander Kornienko15c5e6a2014-11-27 11:11:47 +0000113 takesInitializerList)
Alexander Kornienko72f1e752014-06-18 09:33:46 +0000114 return;
115
116 SourceLocation Loc = Ctor->getLocation();
Alexander Kornienko15c5e6a2014-11-27 11:11:47 +0000117 diag(Loc, "single-argument constructors must be explicit")
Alexander Kornienko72f1e752014-06-18 09:33:46 +0000118 << FixItHint::CreateInsertion(Loc, "explicit ");
119}
120
Alexander Kornienkoed824e02015-03-05 13:46:14 +0000121} // namespace google
Alexander Kornienko72f1e752014-06-18 09:33:46 +0000122} // namespace tidy
123} // namespace clang