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 "../utils/LexerUtils.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace modernize {
18 
19 void UseUsingCheck::registerMatchers(MatchFinder *Finder) {
20   if (!getLangOpts().CPlusPlus11)
21     return;
22   Finder->addMatcher(typedefDecl().bind("typedef"), this);
23 }
24 
25 // Checks if 'typedef' keyword can be removed - we do it only if
26 // it is the only declaration in a declaration chain.
27 static bool CheckRemoval(SourceManager &SM, const SourceLocation &LocStart,
28                          const SourceLocation &LocEnd, ASTContext &Context,
29                          SourceRange &ResultRange) {
30   FileID FID = SM.getFileID(LocEnd);
31   llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, LocEnd);
32   Lexer DeclLexer(SM.getLocForStartOfFile(FID), Context.getLangOpts(),
33                   Buffer->getBufferStart(), SM.getCharacterData(LocStart),
34                   Buffer->getBufferEnd());
35   Token DeclToken;
36   bool result = false;
37   int parenthesisLevel = 0;
38 
39   while (!DeclLexer.LexFromRawLexer(DeclToken)) {
40     if (DeclToken.getKind() == tok::TokenKind::l_paren)
41       parenthesisLevel++;
42     if (DeclToken.getKind() == tok::TokenKind::r_paren)
43       parenthesisLevel--;
44     if (DeclToken.getKind() == tok::TokenKind::semi)
45       break;
46     // if there is comma and we are not between open parenthesis then it is
47     // two or more declatarions in this chain
48     if (parenthesisLevel == 0 && DeclToken.getKind() == tok::TokenKind::comma)
49       return false;
50 
51     if (DeclToken.isOneOf(tok::TokenKind::identifier,
52                           tok::TokenKind::raw_identifier)) {
53       auto TokenStr = DeclToken.getRawIdentifier().str();
54 
55       if (TokenStr == "typedef") {
56         ResultRange =
57             SourceRange(DeclToken.getLocation(), DeclToken.getEndLoc());
58         result = true;
59       }
60     }
61   }
62   // assert if there was keyword 'typedef' in declaration
63   assert(result && "No typedef found");
64 
65   return result;
66 }
67 
68 void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
69   const auto *MatchedDecl = Result.Nodes.getNodeAs<TypedefDecl>("typedef");
70   if (MatchedDecl->getLocation().isInvalid())
71     return;
72 
73   auto &Context = *Result.Context;
74   auto &SM = *Result.SourceManager;
75 
76   auto Diag =
77       diag(MatchedDecl->getLocStart(), "use 'using' instead of 'typedef'");
78   if (MatchedDecl->getLocStart().isMacroID()) {
79     return;
80   }
81   SourceRange RemovalRange;
82   if (CheckRemoval(SM, MatchedDecl->getLocStart(), MatchedDecl->getLocEnd(),
83                    Context, RemovalRange)) {
84     Diag << FixItHint::CreateReplacement(
85         MatchedDecl->getSourceRange(),
86         "using " + MatchedDecl->getNameAsString() + " = " +
87             MatchedDecl->getUnderlyingType().getAsString(getLangOpts()));
88   }
89 }
90 
91 } // namespace modernize
92 } // namespace tidy
93 } // namespace clang
94