clang: lib/Tooling/Inclusions/HeaderIncludes.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

13#include "llvm/Support/FormatVariadic.h"

14#include "llvm/Support/Path.h"

15#include

16

18namespace tooling {

19namespace {

20

21LangOptions createLangOpts() {

22 LangOptions LangOpts;

23 LangOpts.CPlusPlus = 1;

24 LangOpts.CPlusPlus11 = 1;

25 LangOpts.CPlusPlus14 = 1;

26 LangOpts.LineComment = 1;

27 LangOpts.CXXOperatorNames = 1;

28 LangOpts.Bool = 1;

29 LangOpts.ObjC = 1;

30 LangOpts.MicrosoftExt = 1;

31 LangOpts.DeclSpecKeyword = 1;

32 LangOpts.WChar = 1;

33 return LangOpts;

34}

35

36

37

38

39

40unsigned getOffsetAfterTokenSequence(

41 StringRef FileName, StringRef Code, const IncludeStyle &Style,

42 llvm::function_ref<unsigned(const SourceManager &, Lexer &, Token &)>

43 GetOffsetAfterSequence) {

44 SourceManagerForFile VirtualSM(FileName, Code);

45 SourceManager &SM = VirtualSM.get();

46 LangOptions LangOpts = createLangOpts();

47 Lexer Lex(SM.getMainFileID(), SM.getBufferOrFake(SM.getMainFileID()), SM,

48 LangOpts);

49 Token Tok;

50

51 Lex.LexFromRawLexer(Tok);

52 return GetOffsetAfterSequence(SM, Lex, Tok);

53}

54

55

56

57

58

59bool checkAndConsumeDirectiveWithName(

60 Lexer &Lex, StringRef Name, Token &Tok,

61 std::optional RawIDName = std::nullopt) {

62 bool Matched = Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) &&

63 Tok.is(tok::raw_identifier) &&

64 Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(Tok) &&

65 Tok.is(tok::raw_identifier) &&

66 (!RawIDName || Tok.getRawIdentifier() == *RawIDName);

67 if (Matched)

68 Lex.LexFromRawLexer(Tok);

69 return Matched;

70}

71

72void skipComments(Lexer &Lex, Token &Tok) {

73 while (Tok.is(tok::comment))

74 if (Lex.LexFromRawLexer(Tok))

75 return;

76}

77

78

79

80

81

82unsigned getOffsetAfterHeaderGuardsAndComments(StringRef FileName,

83 StringRef Code,

84 const IncludeStyle &Style) {

85

86

87 auto ConsumeHeaderGuardAndComment =

88 [&](std::function<unsigned(const SourceManager &SM, Lexer &Lex,

89 Token Tok)>

90 Consume) {

91 return getOffsetAfterTokenSequence(

93 [&Consume](const SourceManager &SM, Lexer &Lex, Token Tok) {

94 skipComments(Lex, Tok);

95 unsigned InitialOffset = SM.getFileOffset(Tok.getLocation());

96 return std::max(InitialOffset, Consume(SM, Lex, Tok));

97 });

98 };

99 return std::max(

100

101 ConsumeHeaderGuardAndComment(

102 [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {

103 if (checkAndConsumeDirectiveWithName(Lex, "ifndef", Tok)) {

104 skipComments(Lex, Tok);

105 if (checkAndConsumeDirectiveWithName(Lex, "define", Tok) &&

106 Tok.isAtStartOfLine())

107 return SM.getFileOffset(Tok.getLocation());

108 }

109 return 0;

110 }),

111

112 ConsumeHeaderGuardAndComment(

113 [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {

114 if (checkAndConsumeDirectiveWithName(Lex, "pragma", Tok,

115 StringRef("once")))

116 return SM.getFileOffset(Tok.getLocation());

117 return 0;

118 }));

119}

120

121

122

123

124

125bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &Tok) {

126 auto Matched = [&]() {

127 Lex.LexFromRawLexer(Tok);

128 return true;

129 };

130 if (Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) &&

131 Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == "include") {

132 if (Lex.LexFromRawLexer(Tok))

133 return false;

134 if (Tok.is(tok::string_literal))

135 return Matched();

136 if (Tok.is(tok::less)) {

137 while (!Lex.LexFromRawLexer(Tok) && Tok.isNot(tok::greater)) {

138 }

139 if (Tok.is(tok::greater))

140 return Matched();

141 }

142 }

143 return false;

144}

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159unsigned getMaxHeaderInsertionOffset(StringRef FileName, StringRef Code,

160 const IncludeStyle &Style) {

161 return getOffsetAfterTokenSequence(

163 [](const SourceManager &SM, Lexer &Lex, Token Tok) {

164 skipComments(Lex, Tok);

165 unsigned MaxOffset = SM.getFileOffset(Tok.getLocation());

166 while (checkAndConsumeInclusiveDirective(Lex, Tok))

167 MaxOffset = SM.getFileOffset(Tok.getLocation());

169 });

170}

171

172inline StringRef trimInclude(StringRef IncludeName) {

173 return IncludeName.trim("\"<>");

174}

175

176const char IncludeRegexPattern[] =

177 R"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))";

178

179

180

181

182

183

184StringRef matchingStem(llvm::StringRef Path) {

185 StringRef Name = llvm::sys::path::filename(Path);

186 return Name.substr(0, Name.find('.', 1));

187}

188

189}

190

194 for (const auto &Category : Style.IncludeCategories) {

195 CategoryRegexs.emplace_back(Category.Regex, Category.RegexIsCaseSensitive

196 ? llvm::Regex::NoFlags

197 : llvm::Regex::IgnoreCase);

198 }

199 IsMainFile = FileName.ends_with(".c") || FileName.ends_with(".cc") ||

203 if (!Style.IncludeIsMainSourceRegex.empty()) {

204 llvm::Regex MainFileRegex(Style.IncludeIsMainSourceRegex);

205 IsMainFile |= MainFileRegex.match(FileName);

206 }

207}

208

210 bool CheckMainHeader) const {

212 for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)

213 if (CategoryRegexs[i].match(IncludeName)) {

214 Ret = Style.IncludeCategories[i].Priority;

215 break;

216 }

217 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))

218 Ret = 0;

219 return Ret;

220}

221

223 bool CheckMainHeader) const {

225 for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)

226 if (CategoryRegexs[i].match(IncludeName)) {

227 Ret = Style.IncludeCategories[i].SortPriority;

228 if (Ret == 0)

229 Ret = Style.IncludeCategories[i].Priority;

230 break;

231 }

232 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))

233 Ret = 0;

234 return Ret;

235}

236bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const {

237 switch (Style.MainIncludeChar) {

239 if (!IncludeName.starts_with("\""))

240 return false;

241 break;

243 if (!IncludeName.starts_with("<"))

244 return false;

245 break;

247 break;

248 }

249

250 IncludeName =

251 IncludeName.drop_front(1).drop_back(1);

252

253

254 StringRef HeaderStem = llvm::sys::path::stem(IncludeName);

255 StringRef FileStem = llvm::sys::path::stem(FileName);

256 StringRef MatchingFileStem = matchingStem(FileName);

257

258

259

260

261

262

263

264

265 StringRef Matching;

266 if (MatchingFileStem.starts_with_insensitive(HeaderStem))

267 Matching = MatchingFileStem;

268 else if (FileStem.equals_insensitive(HeaderStem))

269 Matching = FileStem;

270 if (!Matching.empty()) {

271 llvm::Regex MainIncludeRegex(HeaderStem.str() + Style.IncludeIsMainRegex,

272 llvm::Regex::IgnoreCase);

273 if (MainIncludeRegex.match(Matching))

274 return true;

275 }

276 return false;

277}

278

280

284 MinInsertOffset(

285 getOffsetAfterHeaderGuardsAndComments(FileName, Code, Style)),

286 MaxInsertOffset(MinInsertOffset +

287 getMaxHeaderInsertionOffset(

288 FileName, Code.drop_front(MinInsertOffset), Style)),

289 MainIncludeFound(false),

290 Categories(Style, FileName) {

291

292

293 Priorities = {0, INT_MAX};

294 for (const auto &Category : Style.IncludeCategories)

295 Priorities.insert(Category.Priority);

297 Code.drop_front(MinInsertOffset).split(Lines, "\n");

298

299 unsigned Offset = MinInsertOffset;

300 unsigned NextLineOffset;

302 for (auto Line : Lines) {

303 NextLineOffset = std::min(Code.size(), Offset + Line.size() + 1);

305

306

307 addExistingInclude(

310 Offset, std::min(Line.size() + 1, Code.size() - Offset)),

313 NextLineOffset);

314 }

315 Offset = NextLineOffset;

316 }

317

318

319

320

321

322 auto Highest = Priorities.begin();

323 auto [It, Inserted] = CategoryEndOffsets.try_emplace(*Highest);

324 if (Inserted)

325 It->second = FirstIncludeOffset >= 0 ? FirstIncludeOffset : MinInsertOffset;

326

327

328

329

330 for (auto I = ++Priorities.begin(), E = Priorities.end(); I != E; ++I)

331 if (CategoryEndOffsets.find(*I) == CategoryEndOffsets.end())

332 CategoryEndOffsets[*I] = CategoryEndOffsets[*std::prev(I)];

333}

334

335

336void HeaderIncludes::addExistingInclude(Include IncludeToAdd,

337 unsigned NextLineOffset) {

338 auto &Incs = ExistingIncludes[trimInclude(IncludeToAdd.Name)];

339 Incs.push_back(std::move(IncludeToAdd));

340 auto &CurInclude = Incs.back();

341

342

343 if (CurInclude.R.getOffset() <= MaxInsertOffset) {

345 CurInclude.Name, !MainIncludeFound);

347 MainIncludeFound = true;

348 CategoryEndOffsets[Priority] = NextLineOffset;

349 IncludesByPriority[Priority].push_back(&CurInclude);

350 if (FirstIncludeOffset < 0)

351 FirstIncludeOffset = CurInclude.R.getOffset();

352 }

353}

354

355std::optionaltooling::Replacement

358 assert(IncludeName == trimInclude(IncludeName));

359

360

361

362 auto It = ExistingIncludes.find(IncludeName);

363 if (It != ExistingIncludes.end()) {

364 for (const auto &Inc : It->second)

365 if (Inc.Directive == Directive &&

366 ((IsAngled && StringRef(Inc.Name).starts_with("<")) ||

367 (!IsAngled && StringRef(Inc.Name).starts_with("\""))))

368 return std::nullopt;

369 }

370 std::string Quoted =

371 std::string(llvm::formatv(IsAngled ? "<{0}>" : "\"{0}\"", IncludeName));

372 StringRef QuotedName = Quoted;

374 QuotedName, !MainIncludeFound);

375 auto CatOffset = CategoryEndOffsets.find(Priority);

376 assert(CatOffset != CategoryEndOffsets.end());

377 unsigned InsertOffset = CatOffset->second;

378 auto Iter = IncludesByPriority.find(Priority);

379 if (Iter != IncludesByPriority.end()) {

380 for (const auto *Inc : Iter->second) {

381 if (QuotedName < Inc->Name) {

382 InsertOffset = Inc->R.getOffset();

383 break;

384 }

385 }

386 }

387 assert(InsertOffset <= Code.size());

388 llvm::StringRef DirectiveSpelling =

390 std::string NewInclude =

391 llvm::formatv("#{0} {1}\n", DirectiveSpelling, QuotedName);

392

393

394

395

396 if (InsertOffset == Code.size() && (!Code.empty() && Code.back() != '\n'))

397 NewInclude = "\n" + NewInclude;

399}

400

402 bool IsAngled) const {

403 assert(IncludeName == trimInclude(IncludeName));

405 auto Iter = ExistingIncludes.find(IncludeName);

406 if (Iter == ExistingIncludes.end())

408 for (const auto &Inc : Iter->second) {

409 if ((IsAngled && StringRef(Inc.Name).starts_with("\"")) ||

410 (!IsAngled && StringRef(Inc.Name).starts_with("<")))

411 continue;

413 FileName, Inc.R.getOffset(), Inc.R.getLength(), ""));

414 if (Err) {

415 auto ErrMsg = "Unexpected conflicts in #include deletions: " +

416 llvm::toString(std::move(Err));

417 llvm_unreachable(ErrMsg.c_str());

418 }

419 }

421}

422

423}

424}

Defines the clang::FileManager interface and associated types.

Defines the SourceManager interface.

Directive - Abstract class representing a parsed verify directive.

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.

@ Result

The result type of a method or function.