1 //===--- LexerUtils.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 "LexerUtils.h"
10 #include "clang/Basic/SourceManager.h"
11 
12 namespace clang {
13 namespace tidy {
14 namespace utils {
15 namespace lexer {
16 
getPreviousToken(SourceLocation Location,const SourceManager & SM,const LangOptions & LangOpts,bool SkipComments)17 Token getPreviousToken(SourceLocation Location, const SourceManager &SM,
18                        const LangOptions &LangOpts, bool SkipComments) {
19   Token Token;
20   Token.setKind(tok::unknown);
21 
22   Location = Location.getLocWithOffset(-1);
23   if (Location.isInvalid())
24       return Token;
25 
26   auto StartOfFile = SM.getLocForStartOfFile(SM.getFileID(Location));
27   while (Location != StartOfFile) {
28     Location = Lexer::GetBeginningOfToken(Location, SM, LangOpts);
29     if (!Lexer::getRawToken(Location, Token, SM, LangOpts) &&
30         (!SkipComments || !Token.is(tok::comment))) {
31       break;
32     }
33     Location = Location.getLocWithOffset(-1);
34   }
35   return Token;
36 }
37 
findPreviousTokenStart(SourceLocation Start,const SourceManager & SM,const LangOptions & LangOpts)38 SourceLocation findPreviousTokenStart(SourceLocation Start,
39                                       const SourceManager &SM,
40                                       const LangOptions &LangOpts) {
41   if (Start.isInvalid() || Start.isMacroID())
42     return SourceLocation();
43 
44   SourceLocation BeforeStart = Start.getLocWithOffset(-1);
45   if (BeforeStart.isInvalid() || BeforeStart.isMacroID())
46     return SourceLocation();
47 
48   return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts);
49 }
50 
findPreviousTokenKind(SourceLocation Start,const SourceManager & SM,const LangOptions & LangOpts,tok::TokenKind TK)51 SourceLocation findPreviousTokenKind(SourceLocation Start,
52                                      const SourceManager &SM,
53                                      const LangOptions &LangOpts,
54                                      tok::TokenKind TK) {
55   if (Start.isInvalid() || Start.isMacroID())
56     return SourceLocation();
57 
58   while (true) {
59     SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);
60     if (L.isInvalid() || L.isMacroID())
61       return SourceLocation();
62 
63     Token T;
64     if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true))
65       return SourceLocation();
66 
67     if (T.is(TK))
68       return T.getLocation();
69 
70     Start = L;
71   }
72 }
73 
findNextTerminator(SourceLocation Start,const SourceManager & SM,const LangOptions & LangOpts)74 SourceLocation findNextTerminator(SourceLocation Start, const SourceManager &SM,
75                                   const LangOptions &LangOpts) {
76   return findNextAnyTokenKind(Start, SM, LangOpts, tok::comma, tok::semi);
77 }
78 
findNextTokenSkippingComments(SourceLocation Start,const SourceManager & SM,const LangOptions & LangOpts)79 Optional<Token> findNextTokenSkippingComments(SourceLocation Start,
80                                               const SourceManager &SM,
81                                               const LangOptions &LangOpts) {
82   Optional<Token> CurrentToken;
83   do {
84     CurrentToken = Lexer::findNextToken(Start, SM, LangOpts);
85   } while (CurrentToken && CurrentToken->is(tok::comment));
86   return CurrentToken;
87 }
88 
rangeContainsExpansionsOrDirectives(SourceRange Range,const SourceManager & SM,const LangOptions & LangOpts)89 bool rangeContainsExpansionsOrDirectives(SourceRange Range,
90                                          const SourceManager &SM,
91                                          const LangOptions &LangOpts) {
92   assert(Range.isValid() && "Invalid Range for relexing provided");
93   SourceLocation Loc = Range.getBegin();
94 
95   while (Loc < Range.getEnd()) {
96     if (Loc.isMacroID())
97       return true;
98 
99     llvm::Optional<Token> Tok = Lexer::findNextToken(Loc, SM, LangOpts);
100 
101     if (!Tok)
102       return true;
103 
104     if (Tok->is(tok::hash))
105       return true;
106 
107     Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts).getLocWithOffset(1);
108   }
109 
110   return false;
111 }
112 
getQualifyingToken(tok::TokenKind TK,CharSourceRange Range,const ASTContext & Context,const SourceManager & SM)113 llvm::Optional<Token> getQualifyingToken(tok::TokenKind TK,
114                                          CharSourceRange Range,
115                                          const ASTContext &Context,
116                                          const SourceManager &SM) {
117   assert((TK == tok::kw_const || TK == tok::kw_volatile ||
118           TK == tok::kw_restrict) &&
119          "TK is not a qualifier keyword");
120   std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getBegin());
121   StringRef File = SM.getBufferData(LocInfo.first);
122   Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
123                  File.begin(), File.data() + LocInfo.second, File.end());
124   llvm::Optional<Token> LastMatchBeforeTemplate;
125   llvm::Optional<Token> LastMatchAfterTemplate;
126   bool SawTemplate = false;
127   Token Tok;
128   while (!RawLexer.LexFromRawLexer(Tok) &&
129          Range.getEnd() != Tok.getLocation() &&
130          !SM.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation())) {
131     if (Tok.is(tok::raw_identifier)) {
132       IdentifierInfo &Info = Context.Idents.get(
133           StringRef(SM.getCharacterData(Tok.getLocation()), Tok.getLength()));
134       Tok.setIdentifierInfo(&Info);
135       Tok.setKind(Info.getTokenID());
136     }
137     if (Tok.is(tok::less))
138       SawTemplate = true;
139     else if (Tok.isOneOf(tok::greater, tok::greatergreater))
140       LastMatchAfterTemplate = None;
141     else if (Tok.is(TK)) {
142       if (SawTemplate)
143         LastMatchAfterTemplate = Tok;
144       else
145         LastMatchBeforeTemplate = Tok;
146     }
147   }
148   return LastMatchAfterTemplate != None ? LastMatchAfterTemplate
149                                         : LastMatchBeforeTemplate;
150 }
151 } // namespace lexer
152 } // namespace utils
153 } // namespace tidy
154 } // namespace clang
155