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