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