xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp (revision b34b6ffa9d4aae16272cbdbf508134f98b4647d4)
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 UseUsingCheck::UseUsingCheck(StringRef Name, ClangTidyContext *Context)
21     : ClangTidyCheck(Name, Context),
22       IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
23 
24 void UseUsingCheck::registerMatchers(MatchFinder *Finder) {
25   if (!getLangOpts().CPlusPlus11)
26     return;
27   Finder->addMatcher(typedefDecl(unless(isInstantiated())).bind("typedef"),
28                      this);
29 }
30 
31 // Checks if 'typedef' keyword can be removed - we do it only if
32 // it is the only declaration in a declaration chain.
33 static bool CheckRemoval(SourceManager &SM, SourceLocation StartLoc,
34                          ASTContext &Context) {
35   assert(StartLoc.isFileID() && "StartLoc must not be in a macro");
36   std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(StartLoc);
37   StringRef File = SM.getBufferData(LocInfo.first);
38   const char *TokenBegin = File.data() + LocInfo.second;
39   Lexer DeclLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
40                   File.begin(), TokenBegin, File.end());
41 
42   Token Tok;
43   int ParenLevel = 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     case tok::r_brace:
50       // This might be the `typedef struct {...} T;` case.
51       return false;
52     case tok::l_paren:
53       ParenLevel++;
54       break;
55     case tok::r_paren:
56       ParenLevel--;
57       break;
58     case tok::comma:
59       if (ParenLevel == 0) {
60         // If there is comma and we are not between open parenthesis then it is
61         // two or more declarations in this chain.
62         return false;
63       }
64       break;
65     case tok::raw_identifier:
66       if (Tok.getRawIdentifier() == "typedef") {
67         FoundTypedef = true;
68       }
69       break;
70     default:
71       break;
72     }
73   }
74 
75   // Sanity check against weird macro cases.
76   return FoundTypedef;
77 }
78 
79 void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
80   const auto *MatchedDecl = Result.Nodes.getNodeAs<TypedefDecl>("typedef");
81   if (MatchedDecl->getLocation().isInvalid())
82     return;
83 
84   auto &Context = *Result.Context;
85   auto &SM = *Result.SourceManager;
86 
87   SourceLocation StartLoc = MatchedDecl->getBeginLoc();
88 
89   if (StartLoc.isMacroID() && IgnoreMacros)
90     return;
91 
92   auto Diag =
93       diag(StartLoc, "use 'using' instead of 'typedef'");
94 
95   // do not fix if there is macro or array
96   if (MatchedDecl->getUnderlyingType()->isArrayType() || StartLoc.isMacroID())
97     return;
98 
99   if (CheckRemoval(SM, StartLoc, Context)) {
100     auto printPolicy = PrintingPolicy(getLangOpts());
101     printPolicy.SuppressScope = true;
102     printPolicy.ConstantArraySizeAsWritten = true;
103     printPolicy.UseVoidForZeroParams = false;
104 
105     Diag << FixItHint::CreateReplacement(
106         MatchedDecl->getSourceRange(),
107         "using " + MatchedDecl->getNameAsString() + " = " +
108             MatchedDecl->getUnderlyingType().getAsString(printPolicy));
109   }
110 }
111 
112 } // namespace modernize
113 } // namespace tidy
114 } // namespace clang
115