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