UseAutoCheck.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. //===--- UseAutoCheck.cpp - clang-tidy-------------------------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "UseAutoCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include "clang/ASTMatchers/ASTMatchers.h"
  12. #include "clang/Basic/CharInfo.h"
  13. #include "clang/Tooling/FixIt.h"
  14. using namespace clang;
  15. using namespace clang::ast_matchers;
  16. using namespace clang::ast_matchers::internal;
  17. namespace clang::tidy::modernize {
  18. namespace {
  19. const char IteratorDeclStmtId[] = "iterator_decl";
  20. const char DeclWithNewId[] = "decl_new";
  21. const char DeclWithCastId[] = "decl_cast";
  22. const char DeclWithTemplateCastId[] = "decl_template";
  23. size_t getTypeNameLength(bool RemoveStars, StringRef Text) {
  24. enum CharType { Space, Alpha, Punctuation };
  25. CharType LastChar = Space, BeforeSpace = Punctuation;
  26. size_t NumChars = 0;
  27. int TemplateTypenameCntr = 0;
  28. for (const unsigned char C : Text) {
  29. if (C == '<')
  30. ++TemplateTypenameCntr;
  31. else if (C == '>')
  32. --TemplateTypenameCntr;
  33. const CharType NextChar =
  34. isAlphanumeric(C)
  35. ? Alpha
  36. : (isWhitespace(C) ||
  37. (!RemoveStars && TemplateTypenameCntr == 0 && C == '*'))
  38. ? Space
  39. : Punctuation;
  40. if (NextChar != Space) {
  41. ++NumChars; // Count the non-space character.
  42. if (LastChar == Space && NextChar == Alpha && BeforeSpace == Alpha)
  43. ++NumChars; // Count a single space character between two words.
  44. BeforeSpace = NextChar;
  45. }
  46. LastChar = NextChar;
  47. }
  48. return NumChars;
  49. }
  50. /// Matches variable declarations that have explicit initializers that
  51. /// are not initializer lists.
  52. ///
  53. /// Given
  54. /// \code
  55. /// iterator I = Container.begin();
  56. /// MyType A(42);
  57. /// MyType B{2};
  58. /// MyType C;
  59. /// \endcode
  60. ///
  61. /// varDecl(hasWrittenNonListInitializer()) maches \c I and \c A but not \c B
  62. /// or \c C.
  63. AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
  64. const Expr *Init = Node.getAnyInitializer();
  65. if (!Init)
  66. return false;
  67. Init = Init->IgnoreImplicit();
  68. // The following test is based on DeclPrinter::VisitVarDecl() to find if an
  69. // initializer is implicit or not.
  70. if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
  71. return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
  72. !Construct->getArg(0)->isDefaultArgument();
  73. }
  74. return Node.getInitStyle() != VarDecl::ListInit;
  75. }
  76. /// Matches QualTypes that are type sugar for QualTypes that match \c
  77. /// SugarMatcher.
  78. ///
  79. /// Given
  80. /// \code
  81. /// class C {};
  82. /// typedef C my_type;
  83. /// typedef my_type my_other_type;
  84. /// \endcode
  85. ///
  86. /// qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
  87. /// matches \c my_type and \c my_other_type.
  88. AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
  89. QualType QT = Node;
  90. while (true) {
  91. if (SugarMatcher.matches(QT, Finder, Builder))
  92. return true;
  93. QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
  94. if (NewQT == QT)
  95. return false;
  96. QT = NewQT;
  97. }
  98. }
  99. /// Matches named declarations that have one of the standard iterator
  100. /// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator.
  101. ///
  102. /// Given
  103. /// \code
  104. /// iterator I;
  105. /// const_iterator CI;
  106. /// \endcode
  107. ///
  108. /// namedDecl(hasStdIteratorName()) matches \c I and \c CI.
  109. Matcher<NamedDecl> hasStdIteratorName() {
  110. static const StringRef IteratorNames[] = {"iterator", "reverse_iterator",
  111. "const_iterator",
  112. "const_reverse_iterator"};
  113. return hasAnyName(IteratorNames);
  114. }
  115. /// Matches named declarations that have one of the standard container
  116. /// names.
  117. ///
  118. /// Given
  119. /// \code
  120. /// class vector {};
  121. /// class forward_list {};
  122. /// class my_ver{};
  123. /// \endcode
  124. ///
  125. /// recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
  126. /// but not \c my_vec.
  127. Matcher<NamedDecl> hasStdContainerName() {
  128. static StringRef ContainerNames[] = {"array", "deque",
  129. "forward_list", "list",
  130. "vector",
  131. "map", "multimap",
  132. "set", "multiset",
  133. "unordered_map", "unordered_multimap",
  134. "unordered_set", "unordered_multiset",
  135. "queue", "priority_queue",
  136. "stack"};
  137. return hasAnyName(ContainerNames);
  138. }
  139. /// Matches declaration reference or member expressions with explicit template
  140. /// arguments.
  141. AST_POLYMORPHIC_MATCHER(hasExplicitTemplateArgs,
  142. AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
  143. MemberExpr)) {
  144. return Node.hasExplicitTemplateArgs();
  145. }
  146. /// Returns a DeclarationMatcher that matches standard iterators nested
  147. /// inside records with a standard container name.
  148. DeclarationMatcher standardIterator() {
  149. return decl(
  150. namedDecl(hasStdIteratorName()),
  151. hasDeclContext(recordDecl(hasStdContainerName(), isInStdNamespace())));
  152. }
  153. /// Returns a TypeMatcher that matches typedefs for standard iterators
  154. /// inside records with a standard container name.
  155. TypeMatcher typedefIterator() {
  156. return typedefType(hasDeclaration(standardIterator()));
  157. }
  158. /// Returns a TypeMatcher that matches records named for standard
  159. /// iterators nested inside records named for standard containers.
  160. TypeMatcher nestedIterator() {
  161. return recordType(hasDeclaration(standardIterator()));
  162. }
  163. /// Returns a TypeMatcher that matches types declared with using
  164. /// declarations and which name standard iterators for standard containers.
  165. TypeMatcher iteratorFromUsingDeclaration() {
  166. auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
  167. // Types resulting from using declarations are represented by elaboratedType.
  168. return elaboratedType(
  169. // Unwrap the nested name specifier to test for one of the standard
  170. // containers.
  171. hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
  172. namedDecl(hasStdContainerName(), isInStdNamespace()))))),
  173. // the named type is what comes after the final '::' in the type. It
  174. // should name one of the standard iterator names.
  175. namesType(
  176. anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl))));
  177. }
  178. /// This matcher returns declaration statements that contain variable
  179. /// declarations with written non-list initializer for standard iterators.
  180. StatementMatcher makeIteratorDeclMatcher() {
  181. return declStmt(unless(has(
  182. varDecl(anyOf(unless(hasWrittenNonListInitializer()),
  183. unless(hasType(isSugarFor(anyOf(
  184. typedefIterator(), nestedIterator(),
  185. iteratorFromUsingDeclaration())))))))))
  186. .bind(IteratorDeclStmtId);
  187. }
  188. StatementMatcher makeDeclWithNewMatcher() {
  189. return declStmt(
  190. unless(has(varDecl(anyOf(
  191. unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
  192. // FIXME: TypeLoc information is not reliable where CV
  193. // qualifiers are concerned so these types can't be
  194. // handled for now.
  195. hasType(pointerType(
  196. pointee(hasCanonicalType(hasLocalQualifiers())))),
  197. // FIXME: Handle function pointers. For now we ignore them
  198. // because the replacement replaces the entire type
  199. // specifier source range which includes the identifier.
  200. hasType(pointsTo(
  201. pointsTo(parenType(innerType(functionType()))))))))))
  202. .bind(DeclWithNewId);
  203. }
  204. StatementMatcher makeDeclWithCastMatcher() {
  205. return declStmt(
  206. unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
  207. .bind(DeclWithCastId);
  208. }
  209. StatementMatcher makeDeclWithTemplateCastMatcher() {
  210. auto ST =
  211. substTemplateTypeParmType(hasReplacementType(equalsBoundNode("arg")));
  212. auto ExplicitCall =
  213. anyOf(has(memberExpr(hasExplicitTemplateArgs())),
  214. has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
  215. auto TemplateArg =
  216. hasTemplateArgument(0, refersToType(qualType().bind("arg")));
  217. auto TemplateCall = callExpr(
  218. ExplicitCall,
  219. callee(functionDecl(TemplateArg,
  220. returns(anyOf(ST, pointsTo(ST), references(ST))))));
  221. return declStmt(unless(has(varDecl(
  222. unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
  223. .bind(DeclWithTemplateCastId);
  224. }
  225. StatementMatcher makeCombinedMatcher() {
  226. return declStmt(
  227. // At least one varDecl should be a child of the declStmt to ensure
  228. // it's a declaration list and avoid matching other declarations,
  229. // e.g. using directives.
  230. has(varDecl(unless(isImplicit()))),
  231. // Skip declarations that are already using auto.
  232. unless(has(varDecl(anyOf(hasType(autoType()),
  233. hasType(qualType(hasDescendant(autoType()))))))),
  234. anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
  235. makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
  236. }
  237. } // namespace
  238. UseAutoCheck::UseAutoCheck(StringRef Name, ClangTidyContext *Context)
  239. : ClangTidyCheck(Name, Context),
  240. MinTypeNameLength(Options.get("MinTypeNameLength", 5)),
  241. RemoveStars(Options.get("RemoveStars", false)) {}
  242. void UseAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  243. Options.store(Opts, "MinTypeNameLength", MinTypeNameLength);
  244. Options.store(Opts, "RemoveStars", RemoveStars);
  245. }
  246. void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
  247. Finder->addMatcher(traverse(TK_AsIs, makeCombinedMatcher()), this);
  248. }
  249. void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) {
  250. for (const auto *Dec : D->decls()) {
  251. const auto *V = cast<VarDecl>(Dec);
  252. const Expr *ExprInit = V->getInit();
  253. // Skip expressions with cleanups from the initializer expression.
  254. if (const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
  255. ExprInit = E->getSubExpr();
  256. const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
  257. if (!Construct)
  258. continue;
  259. // Ensure that the constructor receives a single argument.
  260. if (Construct->getNumArgs() != 1)
  261. return;
  262. // Drill down to the as-written initializer.
  263. const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
  264. if (E != E->IgnoreConversionOperatorSingleStep()) {
  265. // We hit a conversion operator. Early-out now as they imply an implicit
  266. // conversion from a different type. Could also mean an explicit
  267. // conversion from the same type but that's pretty rare.
  268. return;
  269. }
  270. if (const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
  271. // If we ran into an implicit conversion constructor, can't convert.
  272. //
  273. // FIXME: The following only checks if the constructor can be used
  274. // implicitly, not if it actually was. Cases where the converting
  275. // constructor was used explicitly won't get converted.
  276. if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
  277. return;
  278. }
  279. if (!Context->hasSameType(V->getType(), E->getType()))
  280. return;
  281. }
  282. // Get the type location using the first declaration.
  283. const auto *V = cast<VarDecl>(*D->decl_begin());
  284. // WARNING: TypeLoc::getSourceRange() will include the identifier for things
  285. // like function pointers. Not a concern since this action only works with
  286. // iterators but something to keep in mind in the future.
  287. SourceRange Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
  288. diag(Range.getBegin(), "use auto when declaring iterators")
  289. << FixItHint::CreateReplacement(Range, "auto");
  290. }
  291. void UseAutoCheck::replaceExpr(
  292. const DeclStmt *D, ASTContext *Context,
  293. llvm::function_ref<QualType(const Expr *)> GetType, StringRef Message) {
  294. const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
  295. // Ensure that there is at least one VarDecl within the DeclStmt.
  296. if (!FirstDecl)
  297. return;
  298. const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
  299. std::vector<FixItHint> StarRemovals;
  300. for (const auto *Dec : D->decls()) {
  301. const auto *V = cast<VarDecl>(Dec);
  302. // Ensure that every DeclStmt child is a VarDecl.
  303. if (!V)
  304. return;
  305. const auto *Expr = V->getInit()->IgnoreParenImpCasts();
  306. // Ensure that every VarDecl has an initializer.
  307. if (!Expr)
  308. return;
  309. // If VarDecl and Initializer have mismatching unqualified types.
  310. if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
  311. return;
  312. // All subsequent variables in this declaration should have the same
  313. // canonical type. For example, we don't want to use `auto` in
  314. // `T *p = new T, **pp = new T*;`.
  315. if (FirstDeclType != V->getType().getCanonicalType())
  316. return;
  317. if (RemoveStars) {
  318. // Remove explicitly written '*' from declarations where there's more than
  319. // one declaration in the declaration list.
  320. if (Dec == *D->decl_begin())
  321. continue;
  322. auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
  323. while (!Q.isNull()) {
  324. StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
  325. Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
  326. }
  327. }
  328. }
  329. // FIXME: There is, however, one case we can address: when the VarDecl pointee
  330. // is the same as the initializer, just more CV-qualified. However, TypeLoc
  331. // information is not reliable where CV qualifiers are concerned so we can't
  332. // do anything about this case for now.
  333. TypeLoc Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
  334. if (!RemoveStars) {
  335. while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
  336. Loc.getTypeLocClass() == TypeLoc::Qualified)
  337. Loc = Loc.getNextTypeLoc();
  338. }
  339. while (Loc.getTypeLocClass() == TypeLoc::LValueReference ||
  340. Loc.getTypeLocClass() == TypeLoc::RValueReference ||
  341. Loc.getTypeLocClass() == TypeLoc::Qualified) {
  342. Loc = Loc.getNextTypeLoc();
  343. }
  344. SourceRange Range(Loc.getSourceRange());
  345. if (MinTypeNameLength != 0 &&
  346. getTypeNameLength(RemoveStars,
  347. tooling::fixit::getText(Loc.getSourceRange(),
  348. FirstDecl->getASTContext())) <
  349. MinTypeNameLength)
  350. return;
  351. auto Diag = diag(Range.getBegin(), Message);
  352. // Space after 'auto' to handle cases where the '*' in the pointer type is
  353. // next to the identifier. This avoids changing 'int *p' into 'autop'.
  354. // FIXME: This doesn't work for function pointers because the variable name
  355. // is inside the type.
  356. Diag << FixItHint::CreateReplacement(Range, RemoveStars ? "auto " : "auto")
  357. << StarRemovals;
  358. }
  359. void UseAutoCheck::check(const MatchFinder::MatchResult &Result) {
  360. if (const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
  361. replaceIterators(Decl, Result.Context);
  362. } else if (const auto *Decl =
  363. Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
  364. replaceExpr(Decl, Result.Context,
  365. [](const Expr *Expr) { return Expr->getType(); },
  366. "use auto when initializing with new to avoid "
  367. "duplicating the type name");
  368. } else if (const auto *Decl =
  369. Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
  370. replaceExpr(
  371. Decl, Result.Context,
  372. [](const Expr *Expr) {
  373. return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
  374. },
  375. "use auto when initializing with a cast to avoid duplicating the type "
  376. "name");
  377. } else if (const auto *Decl =
  378. Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
  379. replaceExpr(
  380. Decl, Result.Context,
  381. [](const Expr *Expr) {
  382. return cast<CallExpr>(Expr->IgnoreImplicit())
  383. ->getDirectCallee()
  384. ->getReturnType();
  385. },
  386. "use auto when initializing with a template cast to avoid duplicating "
  387. "the type name");
  388. } else {
  389. llvm_unreachable("Bad Callback. No node provided.");
  390. }
  391. }
  392. } // namespace clang::tidy::modernize