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