clang-tools  16.0.0git
Headers.cpp
Go to the documentation of this file.
1 //===--- Headers.cpp - Include headers ---------------------------*- 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 
9 #include "Headers.h"
10 #include "Preamble.h"
11 #include "SourceCode.h"
12 #include "clang/Basic/SourceLocation.h"
13 #include "clang/Basic/SourceManager.h"
14 #include "clang/Frontend/CompilerInstance.h"
15 #include "clang/Lex/HeaderSearch.h"
16 #include "clang/Lex/PPCallbacks.h"
17 #include "clang/Lex/Preprocessor.h"
18 #include "clang/Tooling/Inclusions/HeaderAnalysis.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/Path.h"
21 #include <cstring>
22 
23 namespace clang {
24 namespace clangd {
25 
27  public CommentHandler {
28 public:
29  RecordHeaders(const CompilerInstance &CI, IncludeStructure *Out)
30  : SM(CI.getSourceManager()),
31  HeaderInfo(CI.getPreprocessor().getHeaderSearchInfo()), Out(Out) {}
32 
33  // Record existing #includes - both written and resolved paths. Only #includes
34  // in the main file are collected.
35  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
36  llvm::StringRef FileName, bool IsAngled,
37  CharSourceRange /*FilenameRange*/,
38  Optional<FileEntryRef> File,
39  llvm::StringRef /*SearchPath*/,
40  llvm::StringRef /*RelativePath*/,
41  const clang::Module * /*Imported*/,
42  SrcMgr::CharacteristicKind FileKind) override {
43  auto MainFID = SM.getMainFileID();
44  // If an include is part of the preamble patch, translate #line directives.
45  if (InBuiltinFile)
46  HashLoc = translatePreamblePatchLocation(HashLoc, SM);
47 
48  // Record main-file inclusions (including those mapped from the preamble
49  // patch).
50  if (isInsideMainFile(HashLoc, SM)) {
51  Out->MainFileIncludes.emplace_back();
52  auto &Inc = Out->MainFileIncludes.back();
53  Inc.Written =
54  (IsAngled ? "<" + FileName + ">" : "\"" + FileName + "\"").str();
55  Inc.Resolved =
56  std::string(File ? File->getFileEntry().tryGetRealPathName() : "");
57  Inc.HashOffset = SM.getFileOffset(HashLoc);
58  Inc.HashLine =
59  SM.getLineNumber(SM.getFileID(HashLoc), Inc.HashOffset) - 1;
60  Inc.FileKind = FileKind;
61  Inc.Directive = IncludeTok.getIdentifierInfo()->getPPKeywordID();
62  if (LastPragmaKeepInMainFileLine == Inc.HashLine)
63  Inc.BehindPragmaKeep = true;
64  if (File) {
65  IncludeStructure::HeaderID HID = Out->getOrCreateID(*File);
66  Inc.HeaderID = static_cast<unsigned>(HID);
67  if (IsAngled)
68  if (auto StdlibHeader = tooling::stdlib::Header::named(Inc.Written)) {
69  auto &IDs = Out->StdlibHeaders[*StdlibHeader];
70  // Few physical files for one stdlib header name, linear scan is ok.
71  if (!llvm::is_contained(IDs, HID))
72  IDs.push_back(HID);
73  }
74  }
75  }
76 
77  // Record include graph (not just for main-file includes)
78  if (File) {
79  auto IncludingFileEntry = SM.getFileEntryRefForID(SM.getFileID(HashLoc));
80  if (!IncludingFileEntry) {
81  assert(SM.getBufferName(HashLoc).startswith("<") &&
82  "Expected #include location to be a file or <built-in>");
83  // Treat as if included from the main file.
84  IncludingFileEntry = SM.getFileEntryRefForID(MainFID);
85  }
86  auto IncludingID = Out->getOrCreateID(*IncludingFileEntry),
87  IncludedID = Out->getOrCreateID(*File);
88  Out->IncludeChildren[IncludingID].push_back(IncludedID);
89  }
90  }
91 
92  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
93  SrcMgr::CharacteristicKind FileType,
94  FileID PrevFID) override {
95  switch (Reason) {
96  case PPCallbacks::EnterFile:
97  ++Level;
98  if (BuiltinFile.isInvalid() && SM.isWrittenInBuiltinFile(Loc)) {
99  BuiltinFile = SM.getFileID(Loc);
100  InBuiltinFile = true;
101  }
102  break;
103  case PPCallbacks::ExitFile: {
104  --Level;
105  if (PrevFID == BuiltinFile)
106  InBuiltinFile = false;
107  // At file exit time HeaderSearchInfo is valid and can be used to
108  // determine whether the file was a self-contained header or not.
109  if (const FileEntry *FE = SM.getFileEntryForID(PrevFID)) {
110  // isSelfContainedHeader only returns true once the full header-guard
111  // structure has been seen, i.e. when exiting the *outer* copy of the
112  // file. So last result wins.
113  if (tooling::isSelfContainedHeader(FE, SM, HeaderInfo))
114  Out->NonSelfContained.erase(*Out->getID(FE));
115  else
116  Out->NonSelfContained.insert(*Out->getID(FE));
117  }
118  break;
119  }
120  case PPCallbacks::RenameFile:
121  case PPCallbacks::SystemHeaderPragma:
122  break;
123  }
124  }
125 
126  bool HandleComment(Preprocessor &PP, SourceRange Range) override {
127  auto Pragma =
128  tooling::parseIWYUPragma(SM.getCharacterData(Range.getBegin()));
129  if (!Pragma)
130  return false;
131 
132  if (inMainFile()) {
133  // Given:
134  //
135  // #include "foo.h"
136  // #include "bar.h" // IWYU pragma: keep
137  //
138  // The order in which the callbacks will be triggered:
139  //
140  // 1. InclusionDirective("foo.h")
141  // 2. handleCommentInMainFile("// IWYU pragma: keep")
142  // 3. InclusionDirective("bar.h")
143  //
144  // This code stores the last location of "IWYU pragma: keep" (or export)
145  // comment in the main file, so that when InclusionDirective is called, it
146  // will know that the next inclusion is behind the IWYU pragma.
147  // FIXME: Support "IWYU pragma: begin_exports" and "IWYU pragma:
148  // end_exports".
149  if (!Pragma->startswith("export") && !Pragma->startswith("keep"))
150  return false;
151  unsigned Offset = SM.getFileOffset(Range.getBegin());
152  LastPragmaKeepInMainFileLine =
153  SM.getLineNumber(SM.getMainFileID(), Offset) - 1;
154  } else {
155  // Memorize headers that have export pragmas in them. Include Cleaner
156  // does not support them properly yet, so they will be not marked as
157  // unused.
158  // FIXME: Once IncludeCleaner supports export pragmas, remove this.
159  if (!Pragma->startswith("export") && !Pragma->startswith("begin_exports"))
160  return false;
161  Out->HasIWYUExport.insert(
162  *Out->getID(SM.getFileEntryForID(SM.getFileID(Range.getBegin()))));
163  }
164  return false;
165  }
166 
167 private:
168  // Keeps track of include depth for the current file. It's 1 for main file.
169  int Level = 0;
170  bool inMainFile() const { return Level == 1; }
171 
172  const SourceManager &SM;
173  HeaderSearch &HeaderInfo;
174  // Set after entering the <built-in> file.
175  FileID BuiltinFile;
176  // Indicates whether <built-in> file is part of include stack.
177  bool InBuiltinFile = false;
178 
179  IncludeStructure *Out;
180 
181  // The last line "IWYU pragma: keep" was seen in the main file, 0-indexed.
182  int LastPragmaKeepInMainFileLine = -1;
183 };
184 
185 bool isLiteralInclude(llvm::StringRef Include) {
186  return Include.startswith("<") || Include.startswith("\"");
187 }
188 
189 bool HeaderFile::valid() const {
190  return (Verbatim && isLiteralInclude(File)) ||
191  (!Verbatim && llvm::sys::path::is_absolute(File));
192 }
193 
194 llvm::Expected<HeaderFile> toHeaderFile(llvm::StringRef Header,
195  llvm::StringRef HintPath) {
196  if (isLiteralInclude(Header))
197  return HeaderFile{Header.str(), /*Verbatim=*/true};
198  auto U = URI::parse(Header);
199  if (!U)
200  return U.takeError();
201 
202  auto IncludePath = URI::includeSpelling(*U);
203  if (!IncludePath)
204  return IncludePath.takeError();
205  if (!IncludePath->empty())
206  return HeaderFile{std::move(*IncludePath), /*Verbatim=*/true};
207 
208  auto Resolved = URI::resolve(*U, HintPath);
209  if (!Resolved)
210  return Resolved.takeError();
211  return HeaderFile{std::move(*Resolved), /*Verbatim=*/false};
212 }
213 
214 llvm::SmallVector<llvm::StringRef, 1> getRankedIncludes(const Symbol &Sym) {
215  auto Includes = Sym.IncludeHeaders;
216  // Sort in descending order by reference count and header length.
217  llvm::sort(Includes, [](const Symbol::IncludeHeaderWithReferences &LHS,
219  if (LHS.References == RHS.References)
220  return LHS.IncludeHeader.size() < RHS.IncludeHeader.size();
221  return LHS.References > RHS.References;
222  });
223  llvm::SmallVector<llvm::StringRef, 1> Headers;
224  for (const auto &Include : Includes)
225  Headers.push_back(Include.IncludeHeader);
226  return Headers;
227 }
228 
229 void IncludeStructure::collect(const CompilerInstance &CI) {
230  auto &SM = CI.getSourceManager();
231  MainFileEntry = SM.getFileEntryForID(SM.getMainFileID());
232  auto Collector = std::make_unique<RecordHeaders>(CI, this);
233  CI.getPreprocessor().addCommentHandler(Collector.get());
234  CI.getPreprocessor().addPPCallbacks(std::move(Collector));
235 }
236 
237 llvm::Optional<IncludeStructure::HeaderID>
238 IncludeStructure::getID(const FileEntry *Entry) const {
239  // HeaderID of the main file is always 0;
240  if (Entry == MainFileEntry) {
241  return static_cast<IncludeStructure::HeaderID>(0u);
242  }
243  auto It = UIDToIndex.find(Entry->getUniqueID());
244  if (It == UIDToIndex.end())
245  return std::nullopt;
246  return It->second;
247 }
248 
250  // Main file's FileEntry was not known at IncludeStructure creation time.
251  if (&Entry.getFileEntry() == MainFileEntry) {
252  if (RealPathNames.front().empty())
253  RealPathNames.front() = MainFileEntry->tryGetRealPathName().str();
254  return MainFileID;
255  }
256  auto R = UIDToIndex.try_emplace(
257  Entry.getUniqueID(),
258  static_cast<IncludeStructure::HeaderID>(RealPathNames.size()));
259  if (R.second)
260  RealPathNames.emplace_back();
261  IncludeStructure::HeaderID Result = R.first->getSecond();
262  std::string &RealPathName = RealPathNames[static_cast<unsigned>(Result)];
263  if (RealPathName.empty())
264  RealPathName = Entry.getFileEntry().tryGetRealPathName().str();
265  return Result;
266 }
267 
268 llvm::DenseMap<IncludeStructure::HeaderID, unsigned>
270  // Include depth 0 is the main file only.
271  llvm::DenseMap<HeaderID, unsigned> Result;
272  assert(static_cast<unsigned>(Root) < RealPathNames.size());
273  Result[Root] = 0;
274  std::vector<IncludeStructure::HeaderID> CurrentLevel;
275  CurrentLevel.push_back(Root);
276  llvm::DenseSet<IncludeStructure::HeaderID> Seen;
277  Seen.insert(Root);
278 
279  // Each round of BFS traversal finds the next depth level.
280  std::vector<IncludeStructure::HeaderID> PreviousLevel;
281  for (unsigned Level = 1; !CurrentLevel.empty(); ++Level) {
282  PreviousLevel.clear();
283  PreviousLevel.swap(CurrentLevel);
284  for (const auto &Parent : PreviousLevel) {
285  for (const auto &Child : IncludeChildren.lookup(Parent)) {
286  if (Seen.insert(Child).second) {
287  CurrentLevel.push_back(Child);
288  Result[Child] = Level;
289  }
290  }
291  }
292  }
293  return Result;
294 }
295 
297  IncludedHeaders.insert(Inc.Written);
298  if (!Inc.Resolved.empty())
299  IncludedHeaders.insert(Inc.Resolved);
300 }
301 
302 /// FIXME(ioeric): we might not want to insert an absolute include path if the
303 /// path is not shortened.
305  PathRef DeclaringHeader, const HeaderFile &InsertedHeader) const {
306  assert(InsertedHeader.valid());
307  if (!HeaderSearchInfo && !InsertedHeader.Verbatim)
308  return false;
309  if (FileName == DeclaringHeader || FileName == InsertedHeader.File)
310  return false;
311  auto Included = [&](llvm::StringRef Header) {
312  return IncludedHeaders.find(Header) != IncludedHeaders.end();
313  };
314  return !Included(DeclaringHeader) && !Included(InsertedHeader.File);
315 }
316 
317 llvm::Optional<std::string>
319  llvm::StringRef IncludingFile) const {
320  assert(InsertedHeader.valid());
321  if (InsertedHeader.Verbatim)
322  return InsertedHeader.File;
323  bool IsSystem = false;
324  std::string Suggested;
325  if (HeaderSearchInfo) {
326  Suggested = HeaderSearchInfo->suggestPathToFileForDiagnostics(
327  InsertedHeader.File, BuildDir, IncludingFile, &IsSystem);
328  } else {
329  // Calculate include relative to including file only.
330  StringRef IncludingDir = llvm::sys::path::parent_path(IncludingFile);
331  SmallString<256> RelFile(InsertedHeader.File);
332  // Replacing with "" leaves "/RelFile" if IncludingDir doesn't end in "/".
333  llvm::sys::path::replace_path_prefix(RelFile, IncludingDir, "./");
334  Suggested = llvm::sys::path::convert_to_slash(
335  llvm::sys::path::remove_leading_dotslash(RelFile));
336  }
337  // FIXME: should we allow (some limited number of) "../header.h"?
338  if (llvm::sys::path::is_absolute(Suggested))
339  return std::nullopt;
340  if (IsSystem)
341  Suggested = "<" + Suggested + ">";
342  else
343  Suggested = "\"" + Suggested + "\"";
344  return Suggested;
345 }
346 
347 llvm::Optional<TextEdit>
348 IncludeInserter::insert(llvm::StringRef VerbatimHeader) const {
349  llvm::Optional<TextEdit> Edit;
350  if (auto Insertion = Inserter.insert(VerbatimHeader.trim("\"<>"),
351  VerbatimHeader.startswith("<")))
352  Edit = replacementToEdit(Code, *Insertion);
353  return Edit;
354 }
355 
356 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Inclusion &Inc) {
357  return OS << Inc.Written << " = "
358  << (!Inc.Resolved.empty() ? Inc.Resolved : "[unresolved]")
359  << " at line" << Inc.HashLine;
360 }
361 
362 bool operator==(const Inclusion &LHS, const Inclusion &RHS) {
363  return std::tie(LHS.Directive, LHS.FileKind, LHS.HashOffset, LHS.HashLine,
364  LHS.Resolved, LHS.Written) ==
365  std::tie(RHS.Directive, RHS.FileKind, RHS.HashOffset, RHS.HashLine,
366  RHS.Resolved, RHS.Written);
367 }
368 
369 } // namespace clangd
370 } // namespace clang
clang::clangd::IncludeStructure::collect
void collect(const CompilerInstance &CI)
Definition: Headers.cpp:229
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:45
clang::clangd::HeaderFile
Represents a header file to be #include'd.
Definition: Headers.h:39
Headers.h
clang::clangd::Edit
A set of edits generated for a single file.
Definition: SourceCode.h:184
clang::clangd::IncludeStructure::getID
llvm::Optional< HeaderID > getID(const FileEntry *Entry) const
Definition: Headers.cpp:238
CI
std::unique_ptr< CompilerInvocation > CI
Definition: TUScheduler.cpp:549
Root
ASTNode Root
Definition: DumpAST.cpp:332
Preamble.h
clang::clangd::IncludeStructure::RecordHeaders::InclusionDirective
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, llvm::StringRef FileName, bool IsAngled, CharSourceRange, Optional< FileEntryRef > File, llvm::StringRef, llvm::StringRef, const clang::Module *, SrcMgr::CharacteristicKind FileKind) override
Definition: Headers.cpp:35
clang::clangd::HeaderFile::valid
bool valid() const
Definition: Headers.cpp:189
clang::clangd::IncludeStructure::HeaderID
HeaderID
Definition: Headers.h:134
clang::clangd::IncludeStructure::RecordHeaders::RecordHeaders
RecordHeaders(const CompilerInstance &CI, IncludeStructure *Out)
Definition: Headers.cpp:29
clang::clangd::IncludeInserter::calculateIncludePath
llvm::Optional< std::string > calculateIncludePath(const HeaderFile &InsertedHeader, llvm::StringRef IncludingFile) const
Determines the preferred way to #include a file, taking into account the search path.
Definition: Headers.cpp:318
clang::clangd::IncludeStructure::MainFileIncludes
std::vector< Inclusion > MainFileIncludes
Definition: Headers.h:168
clang::clangd::URI::parse
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
Definition: URI.cpp:177
clang::clangd::replacementToEdit
TextEdit replacementToEdit(llvm::StringRef Code, const tooling::Replacement &R)
Definition: SourceCode.cpp:499
clang::clangd::IncludeInserter::shouldInsertInclude
bool shouldInsertInclude(PathRef DeclaringHeader, const HeaderFile &InsertedHeader) const
Checks whether to add an #include of the header into File.
Definition: Headers.cpp:304
clang::clangd::IncludeStructure
Definition: Headers.h:119
clang::clangd::getRankedIncludes
llvm::SmallVector< llvm::StringRef, 1 > getRankedIncludes(const Symbol &Sym)
Definition: Headers.cpp:214
clang::clangd::URI::resolve
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
Resolves the absolute path of U.
Definition: URI.cpp:245
Offset
size_t Offset
Definition: CodeComplete.cpp:1213
clang::clangd::Inclusion::Directive
tok::PPKeywordKind Directive
Definition: Headers.h:59
clang::clangd::IncludeStructure::RecordHeaders
Definition: Headers.cpp:26
clang::clangd::Inclusion::HashLine
int HashLine
Definition: Headers.h:63
clang::clangd::Inclusion::Written
std::string Written
Definition: Headers.h:60
clang::clangd::Symbol
The class presents a C++ symbol, e.g.
Definition: Symbol.h:36
clang::clangd::URI::includeSpelling
static llvm::Expected< std::string > includeSpelling(const URI &U)
Gets the preferred spelling of this file for #include, if there is one, e.g.
Definition: URI.cpp:273
FileName
StringRef FileName
Definition: KernelNameRestrictionCheck.cpp:46
clang::clangd::toHeaderFile
llvm::Expected< HeaderFile > toHeaderFile(llvm::StringRef Header, llvm::StringRef HintPath)
Creates a HeaderFile from Header which can be either a URI or a literal include.
Definition: Headers.cpp:194
clang::clangd::IncludeStructure::StdlibHeaders
llvm::DenseMap< tooling::stdlib::Header, llvm::SmallVector< HeaderID > > StdlibHeaders
Definition: Headers.h:166
clang::clangd::operator<<
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
Definition: CodeComplete.cpp:2217
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
Parent
const Node * Parent
Definition: ExtractFunction.cpp:157
Entry
Definition: Modularize.cpp:427
SourceCode.h
clang::clangd::IncludeStructure::includeDepth
llvm::DenseMap< HeaderID, unsigned > includeDepth(HeaderID Root=MainFileID) const
Definition: Headers.cpp:269
IsAngled
bool IsAngled
true if this was an include with angle brackets
Definition: IncludeOrderCheck.cpp:41
Collector
std::shared_ptr< SymbolCollector > Collector
Definition: SymbolCollectorTests.cpp:258
clang::clangd::Inclusion
Definition: Headers.h:58
clang::clangd::isLiteralInclude
bool isLiteralInclude(llvm::StringRef Include)
Returns true if Include is literal include like "path" or <path>.
Definition: Headers.cpp:185
clang::clangd::Symbol::IncludeHeaderWithReferences
Definition: Symbol.h:87
clang::clangd::IncludeInserter::addExisting
void addExisting(const Inclusion &Inc)
Definition: Headers.cpp:296
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:29
clang::clangd::Inclusion::Resolved
Path Resolved
Definition: Headers.h:61
clang::clangd::IncludeStructure::MainFileID
static const HeaderID MainFileID
Definition: Headers.h:173
clang::clangd::IncludeStructure::getOrCreateID
HeaderID getOrCreateID(FileEntryRef Entry)
Definition: Headers.cpp:249
clang::clangd::IncludeInserter::insert
llvm::Optional< TextEdit > insert(llvm::StringRef VerbatimHeader) const
Calculates an edit that inserts VerbatimHeader into code.
Definition: Headers.cpp:348
clang::clangd::Inclusion::FileKind
SrcMgr::CharacteristicKind FileKind
Definition: Headers.h:64
clang::clangd::Range
Definition: Protocol.h:185
clang::clangd::Symbol::IncludeHeaderWithReferences::References
unsigned References
The number of translation units that reference this symbol and include this header.
Definition: Symbol.h:104
PPCallbacks
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
OS
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:160
clang::clangd::isInsideMainFile
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
Definition: SourceCode.cpp:418
clang::clangd::Inclusion::HashOffset
unsigned HashOffset
Definition: Headers.h:62
clang::clangd::operator==
bool operator==(const Inclusion &LHS, const Inclusion &RHS)
Definition: Headers.cpp:362
clang::clangd::IncludeStructure::RecordHeaders::FileChanged
void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID) override
Definition: Headers.cpp:92
Out
CompiledFragmentImpl & Out
Definition: ConfigCompile.cpp:99
clang::clangd::HeaderFile::File
std::string File
Definition: Headers.h:40
clang::clangd::Symbol::IncludeHeaders
llvm::SmallVector< IncludeHeaderWithReferences, 1 > IncludeHeaders
One Symbol can potentially be included via different headers.
Definition: Symbol.h:111
clang::clangd::Symbol::IncludeHeaderWithReferences::IncludeHeader
llvm::StringRef IncludeHeader
This can be either a URI of the header to be #include'd for this symbol, or a literal header quoted w...
Definition: Symbol.h:101
clang::clangd::IncludeStructure::IncludeChildren
llvm::DenseMap< HeaderID, SmallVector< HeaderID > > IncludeChildren
Definition: Headers.h:163
clang::clangd::HeaderFile::Verbatim
bool Verbatim
If this is true, File is a literal string quoted with <> or "" that can be #included directly; otherw...
Definition: Headers.h:43
clang::clangd::translatePreamblePatchLocation
SourceLocation translatePreamblePatchLocation(SourceLocation Loc, const SourceManager &SM)
Translates locations inside preamble patch to their main-file equivalent using presumed locations.
Definition: Preamble.cpp:754
clang::clangd::IncludeStructure::RecordHeaders::HandleComment
bool HandleComment(Preprocessor &PP, SourceRange Range) override
Definition: Headers.cpp:126