1 //===--- UseUsingCheck.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 9 #include "UseUsingCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/Lex/Lexer.h" 12 13 using namespace clang::ast_matchers; 14 15 namespace clang { 16 namespace tidy { 17 namespace modernize { 18 19 UseUsingCheck::UseUsingCheck(StringRef Name, ClangTidyContext *Context) 20 : ClangTidyCheck(Name, Context), 21 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {} 22 23 void UseUsingCheck::registerMatchers(MatchFinder *Finder) { 24 if (!getLangOpts().CPlusPlus11) 25 return; 26 Finder->addMatcher(typedefDecl(unless(isInstantiated())).bind("typedef"), 27 this); 28 } 29 30 // Checks if 'typedef' keyword can be removed - we do it only if 31 // it is the only declaration in a declaration chain. 32 static bool CheckRemoval(SourceManager &SM, SourceLocation StartLoc, 33 ASTContext &Context) { 34 assert(StartLoc.isFileID() && "StartLoc must not be in a macro"); 35 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(StartLoc); 36 StringRef File = SM.getBufferData(LocInfo.first); 37 const char *TokenBegin = File.data() + LocInfo.second; 38 Lexer DeclLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(), 39 File.begin(), TokenBegin, File.end()); 40 41 Token Tok; 42 int NestingLevel = 0; // Parens, braces, and square brackets 43 int AngleBracketLevel = 0; 44 bool FoundTypedef = false; 45 46 while (!DeclLexer.LexFromRawLexer(Tok) && !Tok.is(tok::semi)) { 47 switch (Tok.getKind()) { 48 case tok::l_brace: 49 if (NestingLevel == 0 && AngleBracketLevel == 0) { 50 // At top level, this might be the `typedef struct {...} T;` case. 51 // Inside parens, square brackets, or angle brackets it's not. 52 return false; 53 } 54 ++NestingLevel; 55 break; 56 case tok::l_paren: 57 case tok::l_square: 58 ++NestingLevel; 59 break; 60 case tok::r_brace: 61 case tok::r_paren: 62 case tok::r_square: 63 --NestingLevel; 64 break; 65 case tok::less: 66 // If not nested in paren/brace/square bracket, treat as opening angle bracket. 67 if (NestingLevel == 0) 68 ++AngleBracketLevel; 69 break; 70 case tok::greater: 71 // Per C++ 17 Draft N4659, Section 17.2/3 72 // https://timsong-cpp.github.io/cppwp/n4659/temp.names#3: 73 // "When parsing a template-argument-list, the first non-nested > is 74 // taken as the ending delimiter rather than a greater-than operator." 75 // If not nested in paren/brace/square bracket, treat as closing angle bracket. 76 if (NestingLevel == 0) 77 --AngleBracketLevel; 78 break; 79 case tok::comma: 80 if (NestingLevel == 0 && AngleBracketLevel == 0) { 81 // If there is a non-nested comma we have two or more declarations in this chain. 82 return false; 83 } 84 break; 85 case tok::raw_identifier: 86 if (Tok.getRawIdentifier() == "typedef") { 87 FoundTypedef = true; 88 } 89 break; 90 default: 91 break; 92 } 93 } 94 95 // Sanity check against weird macro cases. 96 return FoundTypedef; 97 } 98 99 void UseUsingCheck::check(const MatchFinder::MatchResult &Result) { 100 const auto *MatchedDecl = Result.Nodes.getNodeAs<TypedefDecl>("typedef"); 101 if (MatchedDecl->getLocation().isInvalid()) 102 return; 103 104 auto &Context = *Result.Context; 105 auto &SM = *Result.SourceManager; 106 107 SourceLocation StartLoc = MatchedDecl->getBeginLoc(); 108 109 if (StartLoc.isMacroID() && IgnoreMacros) 110 return; 111 112 auto Diag = diag(StartLoc, "use 'using' instead of 'typedef'"); 113 114 // do not fix if there is macro or array 115 if (MatchedDecl->getUnderlyingType()->isArrayType() || StartLoc.isMacroID()) 116 return; 117 118 if (CheckRemoval(SM, StartLoc, Context)) { 119 auto printPolicy = PrintingPolicy(getLangOpts()); 120 printPolicy.SuppressScope = true; 121 printPolicy.ConstantArraySizeAsWritten = true; 122 printPolicy.UseVoidForZeroParams = false; 123 124 Diag << FixItHint::CreateReplacement( 125 MatchedDecl->getSourceRange(), 126 "using " + MatchedDecl->getNameAsString() + " = " + 127 MatchedDecl->getUnderlyingType().getAsString(printPolicy)); 128 } 129 } 130 131 } // namespace modernize 132 } // namespace tidy 133 } // namespace clang 134