Coverage Report

Created: 2023-11-11 10:31

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- 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
/// \file
10
/// This file implements the ExtractAPIAction, and ASTConsumer to collect API
11
/// information.
12
///
13
//===----------------------------------------------------------------------===//
14
15
#include "clang/AST/ASTConcept.h"
16
#include "clang/AST/ASTConsumer.h"
17
#include "clang/AST/ASTContext.h"
18
#include "clang/AST/DeclObjC.h"
19
#include "clang/Basic/DiagnosticFrontend.h"
20
#include "clang/Basic/SourceLocation.h"
21
#include "clang/Basic/SourceManager.h"
22
#include "clang/Basic/TargetInfo.h"
23
#include "clang/ExtractAPI/API.h"
24
#include "clang/ExtractAPI/APIIgnoresList.h"
25
#include "clang/ExtractAPI/ExtractAPIVisitor.h"
26
#include "clang/ExtractAPI/FrontendActions.h"
27
#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
28
#include "clang/Frontend/ASTConsumers.h"
29
#include "clang/Frontend/CompilerInstance.h"
30
#include "clang/Frontend/FrontendOptions.h"
31
#include "clang/Frontend/MultiplexConsumer.h"
32
#include "clang/Lex/MacroInfo.h"
33
#include "clang/Lex/PPCallbacks.h"
34
#include "clang/Lex/Preprocessor.h"
35
#include "clang/Lex/PreprocessorOptions.h"
36
#include "llvm/ADT/DenseSet.h"
37
#include "llvm/ADT/STLExtras.h"
38
#include "llvm/ADT/SmallString.h"
39
#include "llvm/ADT/SmallVector.h"
40
#include "llvm/Support/Casting.h"
41
#include "llvm/Support/Error.h"
42
#include "llvm/Support/FileSystem.h"
43
#include "llvm/Support/MemoryBuffer.h"
44
#include "llvm/Support/Path.h"
45
#include "llvm/Support/Regex.h"
46
#include "llvm/Support/raw_ostream.h"
47
#include <memory>
48
#include <optional>
49
#include <utility>
50
51
using namespace clang;
52
using namespace extractapi;
53
54
namespace {
55
56
std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
57
                                                  StringRef File,
58
65
                                                  bool *IsQuoted = nullptr) {
59
65
  assert(CI.hasFileManager() &&
60
65
         "CompilerInstance does not have a FileNamager!");
61
62
65
  using namespace llvm::sys;
63
  // Matches framework include patterns
64
65
  const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
65
66
65
  const auto &FS = CI.getVirtualFileSystem();
67
68
65
  SmallString<128> FilePath(File.begin(), File.end());
69
65
  FS.makeAbsolute(FilePath);
70
65
  path::remove_dots(FilePath, true);
71
65
  FilePath = path::convert_to_slash(FilePath);
72
65
  File = FilePath;
73
74
  // Checks whether `Dir` is a strict path prefix of `File`. If so returns
75
  // the prefix length. Otherwise return 0.
76
114
  auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
77
114
    llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
78
114
    FS.makeAbsolute(DirPath);
79
114
    path::remove_dots(DirPath, true);
80
114
    Dir = DirPath;
81
114
    for (auto NI = path::begin(File), NE = path::end(File),
82
114
              DI = path::begin(Dir), DE = path::end(Dir);
83
667
         /*termination condition in loop*/; 
++NI, ++DI553
) {
84
      // '.' components in File are ignored.
85
667
      while (NI != NE && *NI == ".")
86
0
        ++NI;
87
667
      if (NI == NE)
88
0
        break;
89
90
      // '.' components in Dir are ignored.
91
667
      while (DI != DE && 
*DI == "."661
)
92
0
        ++DI;
93
94
      // Dir is a prefix of File, up to '.' components and choice of path
95
      // separators.
96
667
      if (DI == DE)
97
6
        return NI - path::begin(File);
98
99
      // Consider all path separators equal.
100
661
      if (NI->size() == 1 && 
DI->size() == 1114
&&
101
661
          
path::is_separator(NI->front())114
&&
path::is_separator(DI->front())114
)
102
114
        continue;
103
104
      // Special case Apple .sdk folders since the search path is typically a
105
      // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
106
      // located in `iPhoneSimulator.sdk` (the real folder).
107
547
      if (NI->endswith(".sdk") && 
DI->endswith(".sdk")0
) {
108
0
        StringRef NBasename = path::stem(*NI);
109
0
        StringRef DBasename = path::stem(*DI);
110
0
        if (DBasename.startswith(NBasename))
111
0
          continue;
112
0
      }
113
114
547
      if (*NI != *DI)
115
108
        break;
116
547
    }
117
108
    return 0;
118
114
  };
119
120
65
  unsigned PrefixLength = 0;
121
122
  // Go through the search paths and find the first one that is a prefix of
123
  // the header.
124
120
  for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
125
    // Note whether the match is found in a quoted entry.
126
120
    if (IsQuoted)
127
120
      *IsQuoted = Entry.Group == frontend::Quoted;
128
129
120
    if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
130
6
      if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
131
        // If this is a headermap entry, try to reverse lookup the full path
132
        // for a spelled name before mapping.
133
6
        StringRef SpelledFilename = HMap->reverseLookupFilename(File);
134
6
        if (!SpelledFilename.empty())
135
1
          return SpelledFilename.str();
136
137
        // No matching mapping in this headermap, try next search entry.
138
5
        continue;
139
6
      }
140
6
    }
141
142
    // Entry is a directory search entry, try to check if it's a prefix of File.
143
114
    PrefixLength = CheckDir(Entry.Path);
144
114
    if (PrefixLength > 0) {
145
      // The header is found in a framework path, construct the framework-style
146
      // include name `<Framework/Header.h>`
147
6
      if (Entry.IsFramework) {
148
3
        SmallVector<StringRef, 4> Matches;
149
3
        Rule.match(File, &Matches);
150
        // Returned matches are always in stable order.
151
3
        if (Matches.size() != 4)
152
0
          return std::nullopt;
153
154
3
        return path::convert_to_slash(
155
3
            (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
156
3
             Matches[3])
157
3
                .str());
158
3
      }
159
160
      // The header is found in a normal search path, strip the search path
161
      // prefix to get an include name.
162
3
      return path::convert_to_slash(File.drop_front(PrefixLength));
163
6
    }
164
114
  }
165
166
  // Couldn't determine a include name, use full path instead.
167
58
  return std::nullopt;
168
65
}
169
170
struct LocationFileChecker {
171
162
  bool operator()(SourceLocation Loc) {
172
    // If the loc refers to a macro expansion we need to first get the file
173
    // location of the expansion.
174
162
    auto &SM = CI.getSourceManager();
175
162
    auto FileLoc = SM.getFileLoc(Loc);
176
162
    FileID FID = SM.getFileID(FileLoc);
177
162
    if (FID.isInvalid())
178
0
      return false;
179
180
162
    OptionalFileEntryRef File = SM.getFileEntryRefForID(FID);
181
162
    if (!File)
182
0
      return false;
183
184
162
    if (KnownFileEntries.count(*File))
185
149
      return true;
186
187
13
    if (ExternalFileEntries.count(*File))
188
6
      return false;
189
190
7
    StringRef FileName = SM.getFileManager().getCanonicalName(*File);
191
192
    // Try to reduce the include name the same way we tried to include it.
193
7
    bool IsQuoted = false;
194
7
    if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted))
195
4
      if (llvm::any_of(KnownFiles,
196
9
                       [&IsQuoted, &IncludeName](const auto &KnownFile) {
197
9
                         return KnownFile.first.equals(*IncludeName) &&
198
9
                                
KnownFile.second == IsQuoted2
;
199
9
                       })) {
200
2
        KnownFileEntries.insert(*File);
201
2
        return true;
202
2
      }
203
204
    // Record that the file was not found to avoid future reverse lookup for
205
    // the same file.
206
5
    ExternalFileEntries.insert(*File);
207
5
    return false;
208
7
  }
209
210
  LocationFileChecker(const CompilerInstance &CI,
211
                      SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
212
53
      : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
213
53
    for (const auto &KnownFile : KnownFiles)
214
58
      if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
215
55
        KnownFileEntries.insert(*FileEntry);
216
53
  }
217
218
private:
219
  const CompilerInstance &CI;
220
  SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
221
  llvm::DenseSet<const FileEntry *> KnownFileEntries;
222
  llvm::DenseSet<const FileEntry *> ExternalFileEntries;
223
};
224
225
struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> {
226
142
  bool shouldDeclBeIncluded(const Decl *D) const {
227
142
    bool ShouldBeIncluded = true;
228
    // Check that we have the definition for redeclarable types.
229
142
    if (auto *TD = llvm::dyn_cast<TagDecl>(D))
230
39
      ShouldBeIncluded = TD->isThisDeclarationADefinition();
231
103
    else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D))
232
9
      ShouldBeIncluded = Interface->isThisDeclarationADefinition();
233
94
    else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D))
234
6
      ShouldBeIncluded = Protocol->isThisDeclarationADefinition();
235
236
142
    ShouldBeIncluded = ShouldBeIncluded && 
LCF(D->getLocation())139
;
237
142
    return ShouldBeIncluded;
238
142
  }
239
240
  BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context,
241
                         APISet &API)
242
53
      : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {}
243
244
private:
245
  LocationFileChecker &LCF;
246
};
247
248
class WrappingExtractAPIConsumer : public ASTConsumer {
249
public:
250
  WrappingExtractAPIConsumer(ASTContext &Context, APISet &API)
251
3
      : Visitor(Context, API) {}
252
253
3
  void HandleTranslationUnit(ASTContext &Context) override {
254
    // Use ExtractAPIVisitor to traverse symbol declarations in the context.
255
3
    Visitor.TraverseDecl(Context.getTranslationUnitDecl());
256
3
  }
257
258
private:
259
  ExtractAPIVisitor<> Visitor;
260
};
261
262
class ExtractAPIConsumer : public ASTConsumer {
263
public:
264
  ExtractAPIConsumer(ASTContext &Context,
265
                     std::unique_ptr<LocationFileChecker> LCF, APISet &API)
266
53
      : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {}
267
268
53
  void HandleTranslationUnit(ASTContext &Context) override {
269
    // Use ExtractAPIVisitor to traverse symbol declarations in the context.
270
53
    Visitor.TraverseDecl(Context.getTranslationUnitDecl());
271
53
  }
272
273
private:
274
  BatchExtractAPIVisitor Visitor;
275
  std::unique_ptr<LocationFileChecker> LCF;
276
};
277
278
class MacroCallback : public PPCallbacks {
279
public:
280
  MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP)
281
56
      : SM(SM), API(API), PP(PP) {}
282
283
  void MacroDefined(const Token &MacroNameToken,
284
23.4k
                    const MacroDirective *MD) override {
285
23.4k
    auto *MacroInfo = MD->getMacroInfo();
286
287
23.4k
    if (MacroInfo->isBuiltinMacro())
288
0
      return;
289
290
23.4k
    auto SourceLoc = MacroNameToken.getLocation();
291
23.4k
    if (SM.isWrittenInBuiltinFile(SourceLoc) ||
292
23.4k
        
SM.isWrittenInCommandLineFile(SourceLoc)52
)
293
23.4k
      return;
294
295
33
    PendingMacros.emplace_back(MacroNameToken, MD);
296
33
  }
297
298
  // If a macro gets undefined at some point during preprocessing of the inputs
299
  // it means that it isn't an exposed API and we should therefore not add a
300
  // macro definition for it.
301
  void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
302
2
                      const MacroDirective *Undef) override {
303
    // If this macro wasn't previously defined we don't need to do anything
304
    // here.
305
2
    if (!Undef)
306
1
      return;
307
308
2
    
llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) 1
{
309
2
      return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
310
2
                                              /*Syntactically*/ false);
311
2
    });
312
1
  }
313
314
56
  void EndOfMainFile() override {
315
56
    for (auto &PM : PendingMacros) {
316
      // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
317
      // file so check for it here.
318
32
      if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
319
3
        continue;
320
321
29
      if (!shouldMacroBeIncluded(PM))
322
5
        continue;
323
324
24
      StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
325
24
      PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
326
24
      StringRef USR =
327
24
          API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
328
329
24
      API.addMacroDefinition(
330
24
          Name, USR, Loc,
331
24
          DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
332
24
          DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
333
24
          SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
334
24
    }
335
336
56
    PendingMacros.clear();
337
56
  }
338
339
protected:
340
  struct PendingMacro {
341
    Token MacroNameToken;
342
    const MacroDirective *MD;
343
344
    PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
345
33
        : MacroNameToken(MacroNameToken), MD(MD) {}
346
  };
347
348
6
  virtual bool shouldMacroBeIncluded(const PendingMacro &PM) { return true; }
349
350
  const SourceManager &SM;
351
  APISet &API;
352
  Preprocessor &PP;
353
  llvm::SmallVector<PendingMacro> PendingMacros;
354
};
355
356
class APIMacroCallback : public MacroCallback {
357
public:
358
  APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP,
359
                   LocationFileChecker &LCF)
360
53
      : MacroCallback(SM, API, PP), LCF(LCF) {}
361
362
23
  bool shouldMacroBeIncluded(const PendingMacro &PM) override {
363
    // Do not include macros from external files
364
23
    return LCF(PM.MacroNameToken.getLocation());
365
23
  }
366
367
private:
368
  LocationFileChecker &LCF;
369
};
370
371
} // namespace
372
373
56
void ExtractAPIActionBase::ImplEndSourceFileAction() {
374
56
  if (!OS)
375
0
    return;
376
377
  // Setup a SymbolGraphSerializer to write out collected API information in
378
  // the Symbol Graph format.
379
  // FIXME: Make the kind of APISerializer configurable.
380
56
  SymbolGraphSerializer SGSerializer(*API, IgnoresList);
381
56
  SGSerializer.serialize(*OS);
382
56
  OS.reset();
383
56
}
384
385
std::unique_ptr<raw_pwrite_stream>
386
53
ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
387
53
  std::unique_ptr<raw_pwrite_stream> OS;
388
53
  OS = CI.createDefaultOutputFile(/*Binary=*/false, InFile,
389
53
                                  /*Extension=*/"json",
390
53
                                  /*RemoveFileOnSignal=*/false);
391
53
  if (!OS)
392
0
    return nullptr;
393
53
  return OS;
394
53
}
395
396
std::unique_ptr<ASTConsumer>
397
53
ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
398
53
  OS = CreateOutputFile(CI, InFile);
399
400
53
  if (!OS)
401
0
    return nullptr;
402
403
53
  auto ProductName = CI.getFrontendOpts().ProductName;
404
405
  // Now that we have enough information about the language options and the
406
  // target triple, let's create the APISet before anyone uses it.
407
53
  API = std::make_unique<APISet>(
408
53
      CI.getTarget().getTriple(),
409
53
      CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
410
411
53
  auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
412
413
53
  CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>(
414
53
      CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF));
415
416
  // Do not include location in anonymous decls.
417
53
  PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
418
53
  Policy.AnonymousTagLocations = false;
419
53
  CI.getASTContext().setPrintingPolicy(Policy);
420
421
53
  if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
422
3
    llvm::handleAllErrors(
423
3
        APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
424
3
                               CI.getFileManager())
425
3
            .moveInto(IgnoresList),
426
3
        [&CI](const IgnoresFileNotFound &Err) {
427
1
          CI.getDiagnostics().Report(
428
1
              diag::err_extract_api_ignores_file_not_found)
429
1
              << Err.Path;
430
1
        });
431
3
  }
432
433
53
  return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
434
53
                                              std::move(LCF), *API);
435
53
}
436
437
53
bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
438
53
  auto &Inputs = CI.getFrontendOpts().Inputs;
439
53
  if (Inputs.empty())
440
0
    return true;
441
442
53
  if (!CI.hasFileManager())
443
53
    if (!CI.createFileManager())
444
0
      return false;
445
446
53
  auto Kind = Inputs[0].getKind();
447
448
  // Convert the header file inputs into a single input buffer.
449
53
  SmallString<256> HeaderContents;
450
53
  bool IsQuoted = false;
451
58
  for (const FrontendInputFile &FIF : Inputs) {
452
58
    if (Kind.isObjectiveC())
453
17
      HeaderContents += "#import";
454
41
    else
455
41
      HeaderContents += "#include";
456
457
58
    StringRef FilePath = FIF.getFile();
458
58
    if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
459
3
      if (IsQuoted)
460
1
        HeaderContents += " \"";
461
2
      else
462
2
        HeaderContents += " <";
463
464
3
      HeaderContents += *RelativeName;
465
466
3
      if (IsQuoted)
467
1
        HeaderContents += "\"\n";
468
2
      else
469
2
        HeaderContents += ">\n";
470
3
      KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
471
3
                                   IsQuoted);
472
55
    } else {
473
55
      HeaderContents += " \"";
474
55
      HeaderContents += FilePath;
475
55
      HeaderContents += "\"\n";
476
55
      KnownInputFiles.emplace_back(FilePath, true);
477
55
    }
478
58
  }
479
480
53
  if (CI.getHeaderSearchOpts().Verbose)
481
1
    CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
482
1
                                << HeaderContents << "\n";
483
484
53
  Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
485
53
                                                getInputBufferName());
486
487
  // Set that buffer up as our "real" input in the CompilerInstance.
488
53
  Inputs.clear();
489
53
  Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
490
491
53
  return true;
492
53
}
493
494
53
void ExtractAPIAction::EndSourceFileAction() { ImplEndSourceFileAction(); }
495
496
std::unique_ptr<ASTConsumer>
497
WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
498
3
                                            StringRef InFile) {
499
3
  auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
500
3
  if (!OtherConsumer)
501
0
    return nullptr;
502
503
3
  CreatedASTConsumer = true;
504
505
3
  OS = CreateOutputFile(CI, InFile);
506
3
  if (!OS)
507
0
    return nullptr;
508
509
3
  auto ProductName = CI.getFrontendOpts().ProductName;
510
511
  // Now that we have enough information about the language options and the
512
  // target triple, let's create the APISet before anyone uses it.
513
3
  API = std::make_unique<APISet>(
514
3
      CI.getTarget().getTriple(),
515
3
      CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
516
517
3
  CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
518
3
      CI.getSourceManager(), *API, CI.getPreprocessor()));
519
520
  // Do not include location in anonymous decls.
521
3
  PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
522
3
  Policy.AnonymousTagLocations = false;
523
3
  CI.getASTContext().setPrintingPolicy(Policy);
524
525
3
  if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
526
0
    llvm::handleAllErrors(
527
0
        APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
528
0
                               CI.getFileManager())
529
0
            .moveInto(IgnoresList),
530
0
        [&CI](const IgnoresFileNotFound &Err) {
531
0
          CI.getDiagnostics().Report(
532
0
              diag::err_extract_api_ignores_file_not_found)
533
0
              << Err.Path;
534
0
        });
535
0
  }
536
537
3
  auto WrappingConsumer =
538
3
      std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API);
539
3
  std::vector<std::unique_ptr<ASTConsumer>> Consumers;
540
3
  Consumers.push_back(std::move(OtherConsumer));
541
3
  Consumers.push_back(std::move(WrappingConsumer));
542
543
3
  return std::make_unique<MultiplexConsumer>(std::move(Consumers));
544
3
}
545
546
3
void WrappingExtractAPIAction::EndSourceFileAction() {
547
  // Invoke wrapped action's method.
548
3
  WrapperFrontendAction::EndSourceFileAction();
549
550
3
  if (CreatedASTConsumer) {
551
3
    ImplEndSourceFileAction();
552
3
  }
553
3
}
554
555
std::unique_ptr<raw_pwrite_stream>
556
WrappingExtractAPIAction::CreateOutputFile(CompilerInstance &CI,
557
3
                                           StringRef InFile) {
558
3
  std::unique_ptr<raw_pwrite_stream> OS;
559
3
  std::string OutputDir = CI.getFrontendOpts().SymbolGraphOutputDir;
560
561
  // The symbol graphs need to be generated as a side effect of regular
562
  // compilation so the output should be dumped in the directory provided with
563
  // the command line option.
564
3
  llvm::SmallString<128> OutFilePath(OutputDir);
565
3
  auto Seperator = llvm::sys::path::get_separator();
566
3
  auto Infilename = llvm::sys::path::filename(InFile);
567
3
  OutFilePath.append({Seperator, Infilename});
568
3
  llvm::sys::path::replace_extension(OutFilePath, "json");
569
  // StringRef outputFilePathref = *OutFilePath;
570
571
  // don't use the default output file
572
3
  OS = CI.createOutputFile(/*OutputPath=*/OutFilePath, /*Binary=*/false,
573
3
                           /*RemoveFileOnSignal=*/true,
574
3
                           /*UseTemporary=*/true,
575
3
                           /*CreateMissingDirectories=*/true);
576
3
  if (!OS)
577
0
    return nullptr;
578
3
  return OS;
579
3
}