clang 24.0.0git
HeaderIncludes.cpp
Go to the documentation of this file.
1//===--- HeaderIncludes.cpp - Insert/Delete #includes --*- C++ -*----------===//
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
10#include "clang/Basic/LLVM.h"
13#include "clang/Lex/Lexer.h"
14#include "clang/Lex/Token.h"
17#include "llvm/ADT/STLFunctionalExtras.h"
18#include "llvm/ADT/StringRef.h"
19#include "llvm/Support/Error.h"
20#include "llvm/Support/ErrorHandling.h"
21#include "llvm/Support/FormatVariadic.h"
22#include "llvm/Support/Path.h"
23#include "llvm/Support/Regex.h"
24#include <algorithm>
25#include <cassert>
26#include <climits>
27#include <functional>
28#include <optional>
29#include <string>
30#include <type_traits>
31#include <utility>
32
33namespace clang {
34namespace tooling {
35namespace {
36
37LangOptions createLangOpts() {
38 LangOptions LangOpts;
39 LangOpts.CPlusPlus = 1;
40 LangOpts.CPlusPlus11 = 1;
41 LangOpts.CPlusPlus14 = 1;
42 LangOpts.LineComment = 1;
43 LangOpts.CXXOperatorNames = 1;
44 LangOpts.Bool = 1;
45 LangOpts.ObjC = 1;
46 LangOpts.MicrosoftExt = 1; // To get kw___try, kw___finally.
47 LangOpts.DeclSpecKeyword = 1; // To get __declspec.
48 LangOpts.WChar = 1; // To get wchar_t
49 return LangOpts;
50}
51
52// Create a new lexer on the given \p Code and calls \p Callback with the
53// created source manager and lexer. \p Callback must be a callable object that
54// could be invoked with (const SourceManager &, Lexer &). This function returns
55// whatever \p Callback returns.
56template <typename F>
57auto withLexer(StringRef FileName, StringRef Code, const IncludeStyle &Style,
58 F &&Callback)
59 -> std::invoke_result_t<F, const SourceManager &, Lexer &> {
60 SourceManagerForFile VirtualSM(FileName, Code);
61 SourceManager &SM = VirtualSM.get();
62 LangOptions LangOpts = createLangOpts();
63 Lexer Lex(SM.getMainFileID(), SM.getBufferOrFake(SM.getMainFileID()), SM,
64 LangOpts);
65 return std::invoke(std::forward<F>(Callback), std::as_const(SM), Lex);
66}
67
68// Returns the offset after skipping a sequence of tokens, matched by \p
69// GetOffsetAfterSequence, from the start of the code.
70// \p GetOffsetAfterSequence should be a function that matches a sequence of
71// tokens and returns an offset after the sequence.
72unsigned getOffsetAfterTokenSequence(
73 StringRef FileName, StringRef Code, const IncludeStyle &Style,
74 llvm::function_ref<unsigned(const SourceManager &, Lexer &, Token &)>
75 GetOffsetAfterSequence) {
76 return withLexer(FileName, Code, Style,
77 [&](const SourceManager &SM, Lexer &Lex) {
78 Token Tok;
79 // Get the first token.
80 Lex.LexFromRawLexer(Tok);
81 return GetOffsetAfterSequence(SM, Lex, Tok);
82 });
83}
84
85// Check if a sequence of tokens is like "#<Name> <raw_identifier>". If it is,
86// \p Tok will be the token after this directive; otherwise, it can be any token
87// after the given \p Tok (including \p Tok). If \p RawIDName is provided, the
88// (second) raw_identifier name is checked.
89bool checkAndConsumeDirectiveWithName(
90 Lexer &Lex, StringRef Name, Token &Tok,
91 std::optional<StringRef> RawIDName = std::nullopt) {
92 bool Matched = Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) &&
93 Tok.is(tok::raw_identifier) &&
94 Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(Tok) &&
95 Tok.is(tok::raw_identifier) &&
96 (!RawIDName || Tok.getRawIdentifier() == *RawIDName);
97 if (Matched)
98 Lex.LexFromRawLexer(Tok);
99 return Matched;
100}
101
102void skipComments(Lexer &Lex, Token &Tok) {
103 while (Tok.is(tok::comment))
104 if (Lex.LexFromRawLexer(Tok))
105 return;
106}
107
108bool checkAndConsumeModuleDecl(const SourceManager &SM, Lexer &Lex,
109 Token &Tok) {
110 bool Matched = Tok.is(tok::raw_identifier) &&
111 Tok.getRawIdentifier() == "module" &&
112 !Lex.LexFromRawLexer(Tok) && Tok.is(tok::semi) &&
113 !Lex.LexFromRawLexer(Tok);
114 return Matched;
115}
116
117// Determines the minimum offset into the file where we want to insert header
118// includes. This will be put (when available):
119// - after `#pragma once`
120// - after header guards (`#ifdef` and `#define`)
121// - after opening global module (`module;`)
122// - after any comments at the start of the file or immediately following one of
123// the above constructs
124unsigned getMinHeaderInsertionOffset(StringRef FileName, StringRef Code,
125 const IncludeStyle &Style) {
126 // \p Consume returns location after header guard or 0 if no header guard is
127 // found.
128 auto ConsumeHeaderGuardAndComment =
129 [&](std::function<unsigned(const SourceManager &SM, Lexer &Lex,
130 Token Tok)>
131 Consume) {
132 return getOffsetAfterTokenSequence(
133 FileName, Code, Style,
134 [&Consume](const SourceManager &SM, Lexer &Lex, Token Tok) {
135 skipComments(Lex, Tok);
136 unsigned InitialOffset = SM.getFileOffset(Tok.getLocation());
137 return std::max(InitialOffset, Consume(SM, Lex, Tok));
138 });
139 };
140
141 auto ModuleDecl = ConsumeHeaderGuardAndComment(
142 [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {
143 if (checkAndConsumeModuleDecl(SM, Lex, Tok)) {
144 skipComments(Lex, Tok);
145 return SM.getFileOffset(Tok.getLocation());
146 }
147 return 0;
148 });
149
150 auto HeaderAndPPOffset = std::max(
151 // #ifndef/#define
152 ConsumeHeaderGuardAndComment(
153 [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {
154 if (checkAndConsumeDirectiveWithName(Lex, "ifndef", Tok)) {
155 skipComments(Lex, Tok);
156 if (checkAndConsumeDirectiveWithName(Lex, "define", Tok) &&
157 Tok.isAtStartOfLine())
158 return SM.getFileOffset(Tok.getLocation());
159 }
160 return 0;
161 }),
162 // #pragma once
163 ConsumeHeaderGuardAndComment(
164 [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {
165 if (checkAndConsumeDirectiveWithName(Lex, "pragma", Tok,
166 StringRef("once")))
167 return SM.getFileOffset(Tok.getLocation());
168 return 0;
169 }));
170 return std::max(HeaderAndPPOffset, ModuleDecl);
171}
172
173// Check if a sequence of tokens is like
174// "#(include | import) ("header.h" | <header.h>)".
175// If it is, \p Tok will be the token after this directive; otherwise, it can be
176// any token after the given \p Tok (including \p Tok).
177bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &Tok) {
178 auto Matched = [&]() {
179 Lex.LexFromRawLexer(Tok);
180 return true;
181 };
182 if (Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) &&
183 Tok.is(tok::raw_identifier) &&
184 (Tok.getRawIdentifier() == "include" ||
185 Tok.getRawIdentifier() == "import")) {
186 if (Lex.LexFromRawLexer(Tok))
187 return false;
188 if (Tok.is(tok::string_literal))
189 return Matched();
190 if (Tok.is(tok::less)) {
191 while (!Lex.LexFromRawLexer(Tok) && Tok.isNot(tok::greater)) {
192 }
193 if (Tok.is(tok::greater))
194 return Matched();
195 }
196 }
197 return false;
198}
199
200// Returns the offset of the last #include directive after which a new
201// #include can be inserted. This ignores #include's after the #include block(s)
202// in the beginning of a file to avoid inserting headers into code sections
203// where new #include's should not be added by default.
204// These code sections include:
205// - raw string literals (containing #include).
206// - #if blocks.
207// - Special #include's among declarations (e.g. functions).
208//
209// If no #include after which a new #include can be inserted, this returns the
210// offset after skipping all comments from the start of the code.
211// Inserting after an #include is not allowed if it comes after code that is not
212// #include (e.g. pre-processing directive that is not #include, declarations).
213unsigned getMaxHeaderInsertionOffset(StringRef FileName, StringRef Code,
214 const IncludeStyle &Style) {
215 return getOffsetAfterTokenSequence(
216 FileName, Code, Style,
217 [](const SourceManager &SM, Lexer &Lex, Token Tok) {
218 skipComments(Lex, Tok);
219 unsigned MaxOffset = SM.getFileOffset(Tok.getLocation());
220 while (checkAndConsumeInclusiveDirective(Lex, Tok))
221 MaxOffset = SM.getFileOffset(Tok.getLocation());
222 return MaxOffset;
223 });
224}
225
226// Check whether the first declaration in the code is a C++20 module
227// declaration, and it is not preceded by any preprocessor directives.
228bool isFirstDeclModuleDecl(StringRef FileName, StringRef Code,
229 const IncludeStyle &Style) {
230 return withLexer(
231 FileName, Code, Style, [](const SourceManager &SM, Lexer &Lex) {
232 // Let the lexer skip any comments and whitespaces for us.
233 Lex.SetKeepWhitespaceMode(false);
234 Lex.SetCommentRetentionState(false);
235
236 Token tok;
237 if (Lex.LexFromRawLexer(tok))
238 return false;
239
240 // A module declaration is made up of the following token sequence:
241 // export? module <ident> ('.' <ident>)* <partition> <attr> ;
242 //
243 // For convenience, we don't actually lex the whole declaration -- it's
244 // enough to distinguish a module declaration to just ensure an <ident>
245 // is following the "module" keyword.
246
247 // Lex the optional "export" keyword.
248 if (tok.is(tok::raw_identifier) && tok.getRawIdentifier() == "export") {
249 if (Lex.LexFromRawLexer(tok))
250 return false;
251 }
252
253 // Lex the "module" keyword.
254 if (!tok.is(tok::raw_identifier) ||
255 tok.getRawIdentifier() != "module" || Lex.LexFromRawLexer(tok))
256 return false;
257
258 // Make sure an identifier follows the "module" keyword.
259 return tok.is(tok::raw_identifier);
260 });
261}
262
263inline StringRef trimInclude(StringRef IncludeName) {
264 return IncludeName.trim("\"<>");
265}
266
267const char IncludeRegexPattern[] =
268 "^[\t ]*#[\t ]*(import|include)[^\"<]*([\"<][^\">]*[\">])";
269
270// The filename of Path excluding extension.
271// Used to match implementation with headers, this differs from sys::path::stem:
272// - in names with multiple dots (foo.cu.cc) it terminates at the *first*
273// - an empty stem is never returned: /foo/.bar.x => .bar
274// - we don't bother to handle . and .. specially
275StringRef matchingStem(llvm::StringRef Path) {
276 StringRef Name = llvm::sys::path::filename(Path);
277 return Name.substr(0, Name.find('.', 1));
278}
279
280} // anonymous namespace
281
283 StringRef FileName)
284 : Style(Style), FileName(FileName) {
285 for (const auto &Category : Style.IncludeCategories) {
286 CategoryRegexs.emplace_back(Category.Regex, Category.RegexIsCaseSensitive
287 ? llvm::Regex::NoFlags
288 : llvm::Regex::IgnoreCase);
289 }
290 IsMainFile = FileName.ends_with(".c") || FileName.ends_with(".cc") ||
291 FileName.ends_with(".cpp") || FileName.ends_with(".c++") ||
292 FileName.ends_with(".cxx") || FileName.ends_with(".m") ||
293 FileName.ends_with(".mm");
294 if (!Style.IncludeIsMainSourceRegex.empty()) {
295 llvm::Regex MainFileRegex(Style.IncludeIsMainSourceRegex);
296 IsMainFile |= MainFileRegex.match(FileName);
297 }
298}
299
301 bool CheckMainHeader) const {
302 int Ret = INT_MAX;
303 for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
304 if (CategoryRegexs[i].match(IncludeName)) {
305 Ret = Style.IncludeCategories[i].Priority;
306 break;
307 }
308 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
309 Ret = 0;
310 return Ret;
311}
312
314 bool CheckMainHeader) const {
315 int Ret = INT_MAX;
316 for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
317 if (CategoryRegexs[i].match(IncludeName)) {
318 Ret = Style.IncludeCategories[i].SortPriority;
319 if (Ret == 0)
320 Ret = Style.IncludeCategories[i].Priority;
321 break;
322 }
323 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
324 Ret = 0;
325 return Ret;
326}
327bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const {
328 switch (Style.MainIncludeChar) {
329 case IncludeStyle::MICD_Quote:
330 if (!IncludeName.starts_with("\""))
331 return false;
332 break;
334 if (!IncludeName.starts_with("<"))
335 return false;
336 break;
337 case IncludeStyle::MICD_Any:
338 break;
339 }
340
341 IncludeName =
342 IncludeName.drop_front(1).drop_back(1); // remove the surrounding "" or <>
343 // Not matchingStem: implementation files may have compound extensions but
344 // headers may not.
345 StringRef HeaderStem = llvm::sys::path::stem(IncludeName);
346 StringRef FileStem = llvm::sys::path::stem(FileName); // foo.cu for foo.cu.cc
347 StringRef MatchingFileStem = matchingStem(FileName); // foo for foo.cu.cc
348 // main-header examples:
349 // 1) foo.h => foo.cc
350 // 2) foo.h => foo.cu.cc
351 // 3) foo.proto.h => foo.proto.cc
352 //
353 // non-main-header examples:
354 // 1) foo.h => bar.cc
355 // 2) foo.proto.h => foo.cc
356 StringRef Matching;
357 if (MatchingFileStem.starts_with_insensitive(HeaderStem))
358 Matching = MatchingFileStem; // example 1), 2)
359 else if (FileStem.equals_insensitive(HeaderStem))
360 Matching = FileStem; // example 3)
361 if (!Matching.empty()) {
362 llvm::Regex MainIncludeRegex(llvm::Regex::escape(HeaderStem) +
363 Style.IncludeIsMainRegex,
364 llvm::Regex::IgnoreCase);
365 if (MainIncludeRegex.match(Matching))
366 return true;
367 }
368 return false;
369}
370
371const llvm::Regex HeaderIncludes::IncludeRegex(IncludeRegexPattern);
372
373HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code,
374 const IncludeStyle &Style)
375 : FileName(FileName), Code(Code), FirstIncludeOffset(-1),
376 MinInsertOffset(getMinHeaderInsertionOffset(FileName, Code, Style)),
377 MaxInsertOffset(MinInsertOffset +
378 getMaxHeaderInsertionOffset(
379 FileName, Code.drop_front(MinInsertOffset), Style)),
380 MainIncludeFound(false),
381 ShouldInsertGlobalModuleFragmentDecl(
382 isFirstDeclModuleDecl(FileName, Code, Style)),
383 Categories(Style, FileName) {
384 // Add 0 for main header and INT_MAX for headers that are not in any
385 // category.
386 Priorities = {0, INT_MAX};
387 for (const auto &Category : Style.IncludeCategories)
388 Priorities.insert(Category.Priority);
390 Code.drop_front(MinInsertOffset).split(Lines, "\n");
391
392 unsigned Offset = MinInsertOffset;
393 unsigned NextLineOffset;
395 for (auto Line : Lines) {
396 NextLineOffset = std::min(Code.size(), Offset + Line.size() + 1);
397 if (IncludeRegex.match(Line, &Matches)) {
398 // If this is the last line without trailing newline, we need to make
399 // sure we don't delete across the file boundary.
400 addExistingInclude(
401 Include(Matches[2],
403 Offset, std::min(Line.size() + 1, Code.size() - Offset)),
404 Matches[1] == "import" ? tooling::IncludeDirective::Import
406 NextLineOffset);
407 }
408 Offset = NextLineOffset;
409 }
410
411 // Populate CategoryEndOfssets:
412 // - Ensure that CategoryEndOffset[Highest] is always populated.
413 // - If CategoryEndOffset[Priority] isn't set, use the next higher value
414 // that is set, up to CategoryEndOffset[Highest].
415 auto Highest = Priorities.begin();
416 auto [It, Inserted] = CategoryEndOffsets.try_emplace(*Highest);
417 if (Inserted)
418 It->second = FirstIncludeOffset >= 0 ? FirstIncludeOffset : MinInsertOffset;
419 // By this point, CategoryEndOffset[Highest] is always set appropriately:
420 // - to an appropriate location before/after existing #includes, or
421 // - to right after the header guard, or
422 // - to the beginning of the file.
423 for (auto I = ++Priorities.begin(), E = Priorities.end(); I != E; ++I)
424 if (CategoryEndOffsets.find(*I) == CategoryEndOffsets.end())
425 CategoryEndOffsets[*I] = CategoryEndOffsets[*std::prev(I)];
426}
427
428// \p Offset: the start of the line following this include directive.
429void HeaderIncludes::addExistingInclude(Include IncludeToAdd,
430 unsigned NextLineOffset) {
431 auto &Incs = ExistingIncludes[trimInclude(IncludeToAdd.Name)];
432 Incs.push_back(std::move(IncludeToAdd));
433 auto &CurInclude = Incs.back();
434 // The header name with quotes or angle brackets.
435 // Only record the offset of current #include if we can insert after it.
436 if (CurInclude.R.getOffset() <= MaxInsertOffset) {
437 int Priority = Categories.getIncludePriority(
438 CurInclude.Name, /*CheckMainHeader=*/!MainIncludeFound);
439 if (Priority == 0)
440 MainIncludeFound = true;
441 CategoryEndOffsets[Priority] = NextLineOffset;
442 IncludesByPriority[Priority].push_back(&CurInclude);
443 if (FirstIncludeOffset < 0)
444 FirstIncludeOffset = CurInclude.R.getOffset();
445 }
446}
447
448std::optional<tooling::Replacement>
449HeaderIncludes::insert(llvm::StringRef Header, bool IsAngled,
451 assert(Header == trimInclude(Header));
452 // If a <header> ("header") already exists in code, "header" (<header>) with
453 // different quotation and/or directive will still be inserted.
454 // FIXME: figure out if this is the best behavior.
455 auto It = ExistingIncludes.find(Header);
456 if (It != ExistingIncludes.end()) {
457 for (const auto &Inc : It->second)
458 if (Inc.Directive == Directive &&
459 ((IsAngled && StringRef(Inc.Name).starts_with("<")) ||
460 (!IsAngled && StringRef(Inc.Name).starts_with("\""))))
461 return std::nullopt;
462 }
463 std::string Quoted =
464 std::string(llvm::formatv(IsAngled ? "<{0}>" : "\"{0}\"", Header));
465 StringRef QuotedName = Quoted;
466 int Priority = Categories.getIncludePriority(
467 QuotedName, /*CheckMainHeader=*/!MainIncludeFound);
468 auto CatOffset = CategoryEndOffsets.find(Priority);
469 assert(CatOffset != CategoryEndOffsets.end());
470 unsigned InsertOffset = CatOffset->second; // Fall back offset
471 auto Iter = IncludesByPriority.find(Priority);
472 if (Iter != IncludesByPriority.end()) {
473 for (const auto *Inc : Iter->second) {
474 if (QuotedName < Inc->Name) {
475 InsertOffset = Inc->R.getOffset();
476 break;
477 }
478 }
479 }
480 assert(InsertOffset <= Code.size());
481 llvm::StringRef DirectiveSpelling =
482 Directive == IncludeDirective::Include ? "include" : "import";
483 std::string NewInclude =
484 llvm::formatv("#{0} {1}\n", DirectiveSpelling, QuotedName);
485 // When inserting headers at end of the code, also append '\n' to the code
486 // if it does not end with '\n'.
487 // FIXME: when inserting multiple #includes at the end of code, only one
488 // newline should be added.
489 if (InsertOffset == Code.size() && (!Code.empty() && Code.back() != '\n'))
490 NewInclude = "\n" + NewInclude;
491 if (ShouldInsertGlobalModuleFragmentDecl)
492 NewInclude = "module;\n" + NewInclude;
493 return tooling::Replacement(FileName, InsertOffset, 0, NewInclude);
494}
495
497 bool IsAngled) const {
498 assert(IncludeName == trimInclude(IncludeName));
500 auto Iter = ExistingIncludes.find(IncludeName);
501 if (Iter == ExistingIncludes.end())
502 return Result;
503 for (const auto &Inc : Iter->second) {
504 if ((IsAngled && StringRef(Inc.Name).starts_with("\"")) ||
505 (!IsAngled && StringRef(Inc.Name).starts_with("<")))
506 continue;
507 llvm::Error Err = Result.add(tooling::Replacement(
508 FileName, Inc.R.getOffset(), Inc.R.getLength(), ""));
509 if (Err) {
510 auto ErrMsg = "Unexpected conflicts in #include deletions: " +
511 llvm::toString(std::move(Err));
512 llvm_unreachable(ErrMsg.c_str());
513 }
514 }
515 return Result;
516}
517
518} // namespace tooling
519} // namespace clang
Token Tok
The Token.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
#define SM(sm)
Defines the SourceManager interface.
Defines the clang::TokenKind enum and support functions.
VerifyDiagnosticConsumer::Directive Directive
tooling::Replacements remove(llvm::StringRef Header, bool IsAngled) const
Removes all existing includes and imports of Header quoted with <> if IsAngled is true or "" if IsAng...
static const llvm::Regex IncludeRegex
HeaderIncludes(llvm::StringRef FileName, llvm::StringRef Code, const IncludeStyle &Style)
std::optional< tooling::Replacement > insert(llvm::StringRef Header, bool IsAngled, IncludeDirective Directive) const
Inserts an include or import directive of Header into the code.
int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const
Returns the priority of the category which IncludeName belongs to.
IncludeCategoryManager(const IncludeStyle &Style, StringRef FileName)
int getSortIncludePriority(StringRef IncludeName, bool CheckMainHeader) const
A source range independent of the SourceManager.
Definition Replacement.h:44
A text replacement.
Definition Replacement.h:83
Maintains a set of replacements that are conflict-free.
#define INT_MAX
Definition limits.h:50
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
The JSON file list parser is used to communicate input to InstallAPI.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
@ Result
The result type of a method or function.
Definition TypeBase.h:906
for(const auto &A :T->param_types())
int const char * function
Definition c++config.h:31
#define false
Definition stdbool.h:26
Style for sorting and grouping C++ include directives.
MICD_AngleBracket
Regular expressions denoting the different #include categories used for ordering #includes.