clang-tools 18.0.0git
MacroToEnumCheck.cpp
Go to the documentation of this file.
1//===--- MacroToEnumCheck.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 "MacroToEnumCheck.h"
11
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Preprocessor.h"
15#include "llvm/ADT/STLExtras.h"
16#include <algorithm>
17#include <cassert>
18#include <cctype>
19#include <string>
20
21namespace clang::tidy::modernize {
22
23static bool hasOnlyComments(SourceLocation Loc, const LangOptions &Options,
24 StringRef Text) {
25 // Use a lexer to look for tokens; if we find something other than a single
26 // hash, then there were intervening tokens between macro definitions.
27 std::string Buffer{Text};
28 Lexer Lex(Loc, Options, Buffer.c_str(), Buffer.c_str(),
29 Buffer.c_str() + Buffer.size());
30 Token Tok;
31 bool SeenHash = false;
32 while (!Lex.LexFromRawLexer(Tok)) {
33 if (Tok.getKind() == tok::hash && !SeenHash) {
34 SeenHash = true;
35 continue;
36 }
37 return false;
38 }
39
40 // Everything in between was whitespace, so now just look for two blank lines,
41 // consisting of two consecutive EOL sequences, either '\n', '\r' or '\r\n'.
42 enum class WhiteSpace {
43 Nothing,
44 CR,
45 LF,
46 CRLF,
47 CRLFCR,
48 };
49
50 WhiteSpace State = WhiteSpace::Nothing;
51 for (char C : Text) {
52 switch (C) {
53 case '\r':
54 if (State == WhiteSpace::CR)
55 return false;
56
57 State = State == WhiteSpace::CRLF ? WhiteSpace::CRLFCR : WhiteSpace::CR;
58 break;
59
60 case '\n':
61 if (State == WhiteSpace::LF || State == WhiteSpace::CRLFCR)
62 return false;
63
64 State = State == WhiteSpace::CR ? WhiteSpace::CRLF : WhiteSpace::LF;
65 break;
66
67 default:
68 State = WhiteSpace::Nothing;
69 break;
70 }
71 }
72
73 return true;
74}
75
76static StringRef getTokenName(const Token &Tok) {
77 return Tok.is(tok::raw_identifier) ? Tok.getRawIdentifier()
78 : Tok.getIdentifierInfo()->getName();
79}
80
81namespace {
82
83struct EnumMacro {
84 EnumMacro(Token Name, const MacroDirective *Directive)
86
87 Token Name;
88 const MacroDirective *Directive;
89};
90
91using MacroList = SmallVector<EnumMacro>;
92
93enum class IncludeGuard { None, FileChanged, IfGuard, DefineGuard };
94
95struct FileState {
96 FileState() = default;
97
99 unsigned int LastLine = 0;
100 IncludeGuard GuardScanner = IncludeGuard::None;
101 SourceLocation LastMacroLocation;
102};
103
104} // namespace
105
106class MacroToEnumCallbacks : public PPCallbacks {
107public:
108 MacroToEnumCallbacks(MacroToEnumCheck *Check, const LangOptions &LangOptions,
109 const SourceManager &SM)
110 : Check(Check), LangOpts(LangOptions), SM(SM) {}
111
112 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
113 SrcMgr::CharacteristicKind FileType,
114 FileID PrevFID) override;
115
116 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
117 StringRef FileName, bool IsAngled,
118 CharSourceRange FilenameRange,
119 OptionalFileEntryRef File, StringRef SearchPath,
120 StringRef RelativePath, const Module *Imported,
121 SrcMgr::CharacteristicKind FileType) override {
122 clearCurrentEnum(HashLoc);
123 }
124
125 // Keep track of macro definitions that look like enums.
126 void MacroDefined(const Token &MacroNameTok,
127 const MacroDirective *MD) override;
128
129 // Undefining an enum-like macro results in the enum set being dropped.
130 void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD,
131 const MacroDirective *Undef) override;
132
133 // Conditional compilation clears any adjacent enum-like macros.
134 // Macros used in conditional expressions clear any adjacent enum-like
135 // macros.
136 // Include guards are either
137 // #if !defined(GUARD)
138 // or
139 // #ifndef GUARD
140 void If(SourceLocation Loc, SourceRange ConditionRange,
141 ConditionValueKind ConditionValue) override {
142 conditionStart(Loc);
143 checkCondition(ConditionRange);
144 }
145 void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
146 const MacroDefinition &MD) override {
147 conditionStart(Loc);
148 checkName(MacroNameTok);
149 }
150 void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
151 const MacroDefinition &MD) override {
152 conditionStart(Loc);
153 checkName(MacroNameTok);
154 }
155 void Elif(SourceLocation Loc, SourceRange ConditionRange,
156 ConditionValueKind ConditionValue, SourceLocation IfLoc) override {
157 checkCondition(ConditionRange);
158 }
159 void Elifdef(SourceLocation Loc, const Token &MacroNameTok,
160 const MacroDefinition &MD) override {
161 checkName(MacroNameTok);
162 }
163 void Elifdef(SourceLocation Loc, SourceRange ConditionRange,
164 SourceLocation IfLoc) override {
165 PPCallbacks::Elifdef(Loc, ConditionRange, IfLoc);
166 }
167 void Elifndef(SourceLocation Loc, const Token &MacroNameTok,
168 const MacroDefinition &MD) override {
169 checkName(MacroNameTok);
170 }
171 void Elifndef(SourceLocation Loc, SourceRange ConditionRange,
172 SourceLocation IfLoc) override {
173 PPCallbacks::Elifndef(Loc, ConditionRange, IfLoc);
174 }
175 void Endif(SourceLocation Loc, SourceLocation IfLoc) override;
176 void PragmaDirective(SourceLocation Loc,
177 PragmaIntroducerKind Introducer) override;
178
179 // After we've seen everything, issue warnings and fix-its.
180 void EndOfMainFile() override;
181
182 void invalidateRange(SourceRange Range);
183
184private:
185 void newEnum() {
186 if (Enums.empty() || !Enums.back().empty())
187 Enums.emplace_back();
188 }
189 bool insideConditional() const {
190 return (CurrentFile->GuardScanner == IncludeGuard::DefineGuard &&
191 CurrentFile->ConditionScopes > 1) ||
192 (CurrentFile->GuardScanner != IncludeGuard::DefineGuard &&
193 CurrentFile->ConditionScopes > 0);
194 }
195 bool isConsecutiveMacro(const MacroDirective *MD) const;
196 void rememberLastMacroLocation(const MacroDirective *MD) {
197 CurrentFile->LastLine = SM.getSpellingLineNumber(MD->getLocation());
198 CurrentFile->LastMacroLocation = Lexer::getLocForEndOfToken(
199 MD->getMacroInfo()->getDefinitionEndLoc(), 0, SM, LangOpts);
200 }
201 void clearLastMacroLocation() {
202 CurrentFile->LastLine = 0;
203 CurrentFile->LastMacroLocation = SourceLocation{};
204 }
205 void clearCurrentEnum(SourceLocation Loc);
206 void conditionStart(const SourceLocation &Loc);
207 void checkCondition(SourceRange ConditionRange);
208 void checkName(const Token &MacroNameTok);
209 void rememberExpressionName(const Token &Tok);
210 void rememberExpressionTokens(ArrayRef<Token> MacroTokens);
211 void invalidateExpressionNames();
212 void issueDiagnostics();
213 void warnMacroEnum(const EnumMacro &Macro) const;
214 void fixEnumMacro(const MacroList &MacroList) const;
215 bool isInitializer(ArrayRef<Token> MacroTokens);
216
217 MacroToEnumCheck *Check;
218 const LangOptions &LangOpts;
219 const SourceManager &SM;
220 SmallVector<MacroList> Enums;
221 SmallVector<FileState> Files;
222 std::vector<std::string> ExpressionNames;
223 FileState *CurrentFile = nullptr;
224};
225
226bool MacroToEnumCallbacks::isConsecutiveMacro(const MacroDirective *MD) const {
227 if (CurrentFile->LastMacroLocation.isInvalid())
228 return false;
229
230 SourceLocation Loc = MD->getLocation();
231 if (CurrentFile->LastLine + 1 == SM.getSpellingLineNumber(Loc))
232 return true;
233
234 SourceLocation Define =
235 SM.translateLineCol(SM.getFileID(Loc), SM.getSpellingLineNumber(Loc), 1);
236 CharSourceRange BetweenMacros{
237 SourceRange{CurrentFile->LastMacroLocation, Define}, true};
238 CharSourceRange CharRange =
239 Lexer::makeFileCharRange(BetweenMacros, SM, LangOpts);
240 StringRef BetweenText = Lexer::getSourceText(CharRange, SM, LangOpts);
241 return hasOnlyComments(Define, LangOpts, BetweenText);
242}
243
244void MacroToEnumCallbacks::clearCurrentEnum(SourceLocation Loc) {
245 // Only drop the most recent Enum set if the directive immediately follows.
246 if (!Enums.empty() && !Enums.back().empty() &&
247 SM.getSpellingLineNumber(Loc) == CurrentFile->LastLine + 1)
248 Enums.pop_back();
249
250 clearLastMacroLocation();
251}
252
253void MacroToEnumCallbacks::conditionStart(const SourceLocation &Loc) {
254 ++CurrentFile->ConditionScopes;
255 clearCurrentEnum(Loc);
256 if (CurrentFile->GuardScanner == IncludeGuard::FileChanged)
257 CurrentFile->GuardScanner = IncludeGuard::IfGuard;
258}
259
260void MacroToEnumCallbacks::checkCondition(SourceRange Range) {
261 CharSourceRange CharRange = Lexer::makeFileCharRange(
262 CharSourceRange::getTokenRange(Range), SM, LangOpts);
263 std::string Text = Lexer::getSourceText(CharRange, SM, LangOpts).str();
264 Lexer Lex(CharRange.getBegin(), LangOpts, Text.data(), Text.data(),
265 Text.data() + Text.size());
266 Token Tok;
267 bool End = false;
268 while (!End) {
269 End = Lex.LexFromRawLexer(Tok);
270 if (Tok.is(tok::raw_identifier) &&
271 Tok.getRawIdentifier().str() != "defined")
272 checkName(Tok);
273 }
274}
275
276void MacroToEnumCallbacks::checkName(const Token &MacroNameTok) {
277 rememberExpressionName(MacroNameTok);
278
279 StringRef Id = getTokenName(MacroNameTok);
280 llvm::erase_if(Enums, [&Id](const MacroList &MacroList) {
281 return llvm::any_of(MacroList, [&Id](const EnumMacro &Macro) {
282 return getTokenName(Macro.Name) == Id;
283 });
284 });
285}
286
287void MacroToEnumCallbacks::rememberExpressionName(const Token &Tok) {
288 std::string Id = getTokenName(Tok).str();
289 auto Pos = llvm::lower_bound(ExpressionNames, Id);
290 if (Pos == ExpressionNames.end() || *Pos != Id) {
291 ExpressionNames.insert(Pos, Id);
292 }
293}
294
295void MacroToEnumCallbacks::rememberExpressionTokens(
296 ArrayRef<Token> MacroTokens) {
297 for (Token Tok : MacroTokens) {
298 if (Tok.isAnyIdentifier())
299 rememberExpressionName(Tok);
300 }
301}
302
304 FileChangeReason Reason,
305 SrcMgr::CharacteristicKind FileType,
306 FileID PrevFID) {
307 newEnum();
308 if (Reason == EnterFile) {
309 Files.emplace_back();
310 if (!SM.isInMainFile(Loc))
311 Files.back().GuardScanner = IncludeGuard::FileChanged;
312 } else if (Reason == ExitFile) {
313 assert(CurrentFile->ConditionScopes == 0);
314 Files.pop_back();
315 }
316 CurrentFile = &Files.back();
317}
318
319bool MacroToEnumCallbacks::isInitializer(ArrayRef<Token> MacroTokens)
320{
321 IntegralLiteralExpressionMatcher Matcher(MacroTokens, LangOpts.C99 == 0);
322 bool Matched = Matcher.match();
323 bool isC = !LangOpts.CPlusPlus;
324 if (isC && (Matcher.largestLiteralSize() != LiteralSize::Int &&
325 Matcher.largestLiteralSize() != LiteralSize::UnsignedInt))
326 return false;
327
328 return Matched;
329}
330
331
332// Any defined but rejected macro is scanned for identifiers that
333// are to be excluded as enums.
334void MacroToEnumCallbacks::MacroDefined(const Token &MacroNameTok,
335 const MacroDirective *MD) {
336 // Include guards are never candidates for becoming an enum.
337 if (CurrentFile->GuardScanner == IncludeGuard::IfGuard) {
338 CurrentFile->GuardScanner = IncludeGuard::DefineGuard;
339 return;
340 }
341
342 if (insideConditional())
343 return;
344
345 if (SM.getFilename(MD->getLocation()).empty())
346 return;
347
348 const MacroInfo *Info = MD->getMacroInfo();
349 ArrayRef<Token> MacroTokens = Info->tokens();
350 if (Info->isBuiltinMacro() || MacroTokens.empty())
351 return;
352 if (Info->isFunctionLike()) {
353 rememberExpressionTokens(MacroTokens);
354 return;
355 }
356
357 if (!isInitializer(MacroTokens))
358 return;
359
360 if (!isConsecutiveMacro(MD))
361 newEnum();
362 Enums.back().emplace_back(MacroNameTok, MD);
363 rememberLastMacroLocation(MD);
364}
365
366// Any macro that is undefined removes all adjacent macros from consideration as
367// an enum and starts a new enum scan.
368void MacroToEnumCallbacks::MacroUndefined(const Token &MacroNameTok,
369 const MacroDefinition &MD,
370 const MacroDirective *Undef) {
371 rememberExpressionName(MacroNameTok);
372
373 auto MatchesToken = [&MacroNameTok](const EnumMacro &Macro) {
374 return getTokenName(Macro.Name) == getTokenName(MacroNameTok);
375 };
376
377 auto It = llvm::find_if(Enums, [MatchesToken](const MacroList &MacroList) {
378 return llvm::any_of(MacroList, MatchesToken);
379 });
380 if (It != Enums.end())
381 Enums.erase(It);
382
383 clearLastMacroLocation();
384 CurrentFile->GuardScanner = IncludeGuard::None;
385}
386
387void MacroToEnumCallbacks::Endif(SourceLocation Loc, SourceLocation IfLoc) {
388 // The if directive for the include guard isn't counted in the
389 // ConditionScopes.
390 if (CurrentFile->ConditionScopes == 0 &&
391 CurrentFile->GuardScanner == IncludeGuard::DefineGuard)
392 return;
393
394 // We don't need to clear the current enum because the start of the
395 // conditional block already took care of that.
396 assert(CurrentFile->ConditionScopes > 0);
397 --CurrentFile->ConditionScopes;
398}
399
400namespace {
401
402template <size_t N>
403bool textEquals(const char (&Needle)[N], const char *HayStack) {
404 return StringRef{HayStack, N - 1} == Needle;
405}
406
407template <size_t N> size_t len(const char (&)[N]) { return N - 1; }
408
409} // namespace
410
412 PragmaIntroducerKind Introducer) {
413 if (CurrentFile->GuardScanner != IncludeGuard::FileChanged)
414 return;
415
416 bool Invalid = false;
417 const char *Text = SM.getCharacterData(
418 Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts), &Invalid);
419 if (Invalid)
420 return;
421
422 while (*Text && std::isspace(*Text))
423 ++Text;
424
425 if (textEquals("pragma", Text))
426 return;
427
428 Text += len("pragma");
429 while (*Text && std::isspace(*Text))
430 ++Text;
431
432 if (textEquals("once", Text))
433 CurrentFile->GuardScanner = IncludeGuard::IfGuard;
434}
435
436void MacroToEnumCallbacks::invalidateExpressionNames() {
437 for (const std::string &Id : ExpressionNames) {
438 llvm::erase_if(Enums, [Id](const MacroList &MacroList) {
439 return llvm::any_of(MacroList, [&Id](const EnumMacro &Macro) {
440 return getTokenName(Macro.Name) == Id;
441 });
442 });
443 }
444}
445
447 invalidateExpressionNames();
448 issueDiagnostics();
449}
450
452 llvm::erase_if(Enums, [Range](const MacroList &MacroList) {
453 return llvm::any_of(MacroList, [Range](const EnumMacro &Macro) {
454 return Macro.Directive->getLocation() >= Range.getBegin() &&
455 Macro.Directive->getLocation() <= Range.getEnd();
456 });
457 });
458}
459
460void MacroToEnumCallbacks::issueDiagnostics() {
461 for (const MacroList &MacroList : Enums) {
462 if (MacroList.empty())
463 continue;
464
465 for (const EnumMacro &Macro : MacroList)
466 warnMacroEnum(Macro);
467
468 fixEnumMacro(MacroList);
469 }
470}
471
472void MacroToEnumCallbacks::warnMacroEnum(const EnumMacro &Macro) const {
473 Check->diag(Macro.Directive->getLocation(),
474 "macro '%0' defines an integral constant; prefer an enum instead")
475 << getTokenName(Macro.Name);
476}
477
478void MacroToEnumCallbacks::fixEnumMacro(const MacroList &MacroList) const {
479 SourceLocation Begin =
480 MacroList.front().Directive->getMacroInfo()->getDefinitionLoc();
481 Begin = SM.translateLineCol(SM.getFileID(Begin),
482 SM.getSpellingLineNumber(Begin), 1);
483 DiagnosticBuilder Diagnostic =
484 Check->diag(Begin, "replace macro with enum")
485 << FixItHint::CreateInsertion(Begin, "enum {\n");
486
487 for (size_t I = 0U; I < MacroList.size(); ++I) {
488 const EnumMacro &Macro = MacroList[I];
489 SourceLocation DefineEnd =
490 Macro.Directive->getMacroInfo()->getDefinitionLoc();
491 SourceLocation DefineBegin = SM.translateLineCol(
492 SM.getFileID(DefineEnd), SM.getSpellingLineNumber(DefineEnd), 1);
493 CharSourceRange DefineRange;
494 DefineRange.setBegin(DefineBegin);
495 DefineRange.setEnd(DefineEnd);
496 Diagnostic << FixItHint::CreateRemoval(DefineRange);
497
498 SourceLocation NameEnd = Lexer::getLocForEndOfToken(
499 Macro.Directive->getMacroInfo()->getDefinitionLoc(), 0, SM, LangOpts);
500 Diagnostic << FixItHint::CreateInsertion(NameEnd, " =");
501
502 SourceLocation ValueEnd = Lexer::getLocForEndOfToken(
503 Macro.Directive->getMacroInfo()->getDefinitionEndLoc(), 0, SM,
504 LangOpts);
505 if (I < MacroList.size() - 1)
506 Diagnostic << FixItHint::CreateInsertion(ValueEnd, ",");
507 }
508
509 SourceLocation End = Lexer::getLocForEndOfToken(
510 MacroList.back().Directive->getMacroInfo()->getDefinitionEndLoc(), 0, SM,
511 LangOpts);
512 End = SM.translateLineCol(SM.getFileID(End),
513 SM.getSpellingLineNumber(End) + 1, 1);
514 Diagnostic << FixItHint::CreateInsertion(End, "};\n");
515}
516
517void MacroToEnumCheck::registerPPCallbacks(const SourceManager &SM,
518 Preprocessor *PP,
519 Preprocessor *ModuleExpanderPP) {
520 auto Callback = std::make_unique<MacroToEnumCallbacks>(this, getLangOpts(), SM);
521 PPCallback = Callback.get();
522 PP->addPPCallbacks(std::move(Callback));
523}
524
525void MacroToEnumCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
526 using namespace ast_matchers;
527 auto TopLevelDecl = hasParent(translationUnitDecl());
528 Finder->addMatcher(decl(TopLevelDecl).bind("top"), this);
529}
530
531static bool isValid(SourceRange Range) {
532 return Range.getBegin().isValid() && Range.getEnd().isValid();
533}
534
535static bool empty(SourceRange Range) {
536 return Range.getBegin() == Range.getEnd();
537}
538
540 const ast_matchers::MatchFinder::MatchResult &Result) {
541 auto *TLDecl = Result.Nodes.getNodeAs<Decl>("top");
542 if (TLDecl == nullptr)
543 return;
544
545 SourceRange Range = TLDecl->getSourceRange();
546 if (auto *TemplateFn = Result.Nodes.getNodeAs<FunctionTemplateDecl>("top")) {
547 if (TemplateFn->isThisDeclarationADefinition() && TemplateFn->hasBody())
548 Range = SourceRange{TemplateFn->getBeginLoc(),
549 TemplateFn->getUnderlyingDecl()->getBodyRBrace()};
550 }
551
552 if (isValid(Range) && !empty(Range))
553 PPCallback->invalidateRange(Range);
554}
555
556} // namespace clang::tidy::modernize
const FunctionDecl * Decl
DiagnosticCallback Diagnostic
const Criteria C
FunctionInfo Info
std::string Text
llvm::StringRef Name
CharSourceRange Range
SourceRange for the file name.
bool IsAngled
true if this was an include with angle brackets
StringRef FileName
SourceLocation Loc
SourceLocation LastMacroLocation
const MacroDirective * Directive
unsigned int LastLine
IncludeGuard GuardScanner
int ConditionScopes
size_t Pos
clang::PPCallbacks::ConditionValueKind ConditionValue
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) override
void PragmaDirective(SourceLocation Loc, PragmaIntroducerKind Introducer) override
MacroToEnumCallbacks(MacroToEnumCheck *Check, const LangOptions &LangOptions, const SourceManager &SM)
void Elifdef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override
void If(SourceLocation Loc, SourceRange ConditionRange, ConditionValueKind ConditionValue) override
void Elif(SourceLocation Loc, SourceRange ConditionRange, ConditionValueKind ConditionValue, SourceLocation IfLoc) override
void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, const MacroDirective *Undef) override
void Ifndef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override
void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID) override
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File, StringRef SearchPath, StringRef RelativePath, const Module *Imported, SrcMgr::CharacteristicKind FileType) override
void Elifndef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override
void Endif(SourceLocation Loc, SourceLocation IfLoc) override
void Elifdef(SourceLocation Loc, SourceRange ConditionRange, SourceLocation IfLoc) override
void Elifndef(SourceLocation Loc, SourceRange ConditionRange, SourceLocation IfLoc) override
void Ifdef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override
Replaces groups of related macros with an unscoped anonymous enum.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
@ None
Mix between the two parameters is not possible.
static bool empty(SourceRange Range)
static bool hasOnlyComments(SourceLocation Loc, const LangOptions &Options, StringRef Text)
static bool isValid(SourceRange Range)
static StringRef getTokenName(const Token &Tok)