xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp (revision 4b1dfbbc78845805e5205f5655d6f444ff05bd1b)
1 //===--- UseUsingCheck.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 "UseUsingCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/Lex/Lexer.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace modernize {
19 
20 void UseUsingCheck::registerMatchers(MatchFinder *Finder) {
21   if (!getLangOpts().CPlusPlus11)
22     return;
23   Finder->addMatcher(typedefDecl().bind("typedef"), this);
24 }
25 
26 // Checks if 'typedef' keyword can be removed - we do it only if
27 // it is the only declaration in a declaration chain.
28 static bool CheckRemoval(SourceManager &SM, SourceLocation StartLoc,
29                          ASTContext &Context) {
30   assert(StartLoc.isFileID() && "StartLoc must not be in a macro");
31   std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(StartLoc);
32   StringRef File = SM.getBufferData(LocInfo.first);
33   const char *TokenBegin = File.data() + LocInfo.second;
34   Lexer DeclLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
35                   File.begin(), TokenBegin, File.end());
36 
37   Token Tok;
38   int ParenLevel = 0;
39   bool FoundTypedef = false;
40 
41   while (!DeclLexer.LexFromRawLexer(Tok) && !Tok.is(tok::semi)) {
42     switch (Tok.getKind()) {
43     case tok::l_brace:
44     case tok::r_brace:
45       // This might be the `typedef struct {...} T;` case.
46       return false;
47     case tok::l_paren:
48       ParenLevel++;
49       break;
50     case tok::r_paren:
51       ParenLevel--;
52       break;
53     case tok::comma:
54       if (ParenLevel == 0) {
55         // If there is comma and we are not between open parenthesis then it is
56         // two or more declarations in this chain.
57         return false;
58       }
59       break;
60     case tok::raw_identifier:
61       if (Tok.getRawIdentifier() == "typedef") {
62         FoundTypedef = true;
63       }
64       break;
65     default:
66       break;
67     }
68   }
69 
70   // Sanity check against weird macro cases.
71   return FoundTypedef;
72 }
73 
74 void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
75   const auto *MatchedDecl = Result.Nodes.getNodeAs<TypedefDecl>("typedef");
76   if (MatchedDecl->getLocation().isInvalid())
77     return;
78 
79   auto &Context = *Result.Context;
80   auto &SM = *Result.SourceManager;
81 
82   if (auto *D = MatchedDecl->getUnderlyingType()->getAsCXXRecordDecl()) {
83     //TypeLoc TL = MatchedDecl->getTypeSourceInfo()->getTypeLoc();
84     llvm::errs() << D->getNameAsString() << "\n";
85   }
86 
87   auto Diag =
88       diag(MatchedDecl->getLocStart(), "use 'using' instead of 'typedef'");
89 
90   SourceLocation StartLoc = MatchedDecl->getLocStart();
91   if (StartLoc.isMacroID())
92     return;
93 
94   if (CheckRemoval(SM, StartLoc, Context)) {
95     Diag << FixItHint::CreateReplacement(
96         MatchedDecl->getSourceRange(),
97         "using " + MatchedDecl->getNameAsString() + " = " +
98             MatchedDecl->getUnderlyingType().getAsString(getLangOpts()));
99   }
100 }
101 
102 } // namespace modernize
103 } // namespace tidy
104 } // namespace clang
105