1//===- FileList.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#include "clang/InstallAPI/FileList.h"
10#include "clang/Basic/DiagnosticFrontend.h"
11#include "clang/InstallAPI/FileList.h"
12#include "llvm/ADT/StringSwitch.h"
13#include "llvm/Support/Error.h"
14#include "llvm/Support/JSON.h"
15#include "llvm/TextAPI/TextAPIError.h"
16#include <optional>
17
18// clang-format off
19/*
20InstallAPI JSON Input Format specification.
21
22{
23 "headers" : [ # Required: Key must exist.
24 { # Optional: May contain 0 or more header inputs.
25 "path" : "/usr/include/mach-o/dlfn.h", # Required: Path should point to destination
26 # location where applicable.
27 "type" : "public", # Required: Maps to HeaderType for header.
28 "language": "c++" # Optional: Language mode for header.
29 }
30 ],
31 "version" : "3" # Required: Version 3 supports language mode
32 & project header input.
33}
34*/
35// clang-format on
36
37using namespace llvm;
38using namespace llvm::json;
39using namespace llvm::MachO;
40using namespace clang::installapi;
41
42namespace {
43class Implementation {
44private:
45 Expected<StringRef> parseString(const Object *Obj, StringRef Key,
46 StringRef Error);
47 Expected<StringRef> parsePath(const Object *Obj);
48 Expected<HeaderType> parseType(const Object *Obj);
49 std::optional<clang::Language> parseLanguage(const Object *Obj);
50 Error parseHeaders(Array &Headers);
51
52public:
53 std::unique_ptr<MemoryBuffer> InputBuffer;
54 unsigned Version;
55 HeaderSeq HeaderList;
56
57 Error parse(StringRef Input);
58};
59
60Expected<StringRef>
61Implementation::parseString(const Object *Obj, StringRef Key, StringRef Error) {
62 auto Str = Obj->getString(K: Key);
63 if (!Str)
64 return make_error<StringError>(Args&: Error, Args: inconvertibleErrorCode());
65 return *Str;
66}
67
68Expected<HeaderType> Implementation::parseType(const Object *Obj) {
69 auto TypeStr =
70 parseString(Obj, Key: "type", Error: "required field 'type' not specified");
71 if (!TypeStr)
72 return TypeStr.takeError();
73
74 if (*TypeStr == "public")
75 return HeaderType::Public;
76 else if (*TypeStr == "private")
77 return HeaderType::Private;
78 else if (*TypeStr == "project" && Version >= 2)
79 return HeaderType::Project;
80
81 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat,
82 Args: "unsupported header type");
83}
84
85Expected<StringRef> Implementation::parsePath(const Object *Obj) {
86 auto Path = parseString(Obj, Key: "path", Error: "required field 'path' not specified");
87 if (!Path)
88 return Path.takeError();
89
90 return *Path;
91}
92
93std::optional<clang::Language>
94Implementation::parseLanguage(const Object *Obj) {
95 auto Language = Obj->getString(K: "language");
96 if (!Language)
97 return std::nullopt;
98
99 return StringSwitch<clang::Language>(*Language)
100 .Case(S: "c", Value: clang::Language::C)
101 .Case(S: "c++", Value: clang::Language::CXX)
102 .Case(S: "objective-c", Value: clang::Language::ObjC)
103 .Case(S: "objective-c++", Value: clang::Language::ObjCXX)
104 .Default(Value: clang::Language::Unknown);
105}
106
107Error Implementation::parseHeaders(Array &Headers) {
108 for (const auto &H : Headers) {
109 auto *Obj = H.getAsObject();
110 if (!Obj)
111 return make_error<StringError>(Args: "expect a JSON object",
112 Args: inconvertibleErrorCode());
113 auto Type = parseType(Obj);
114 if (!Type)
115 return Type.takeError();
116 auto Path = parsePath(Obj);
117 if (!Path)
118 return Path.takeError();
119 auto Language = parseLanguage(Obj);
120
121 StringRef PathStr = *Path;
122 if (*Type == HeaderType::Project) {
123 HeaderList.emplace_back(
124 args: HeaderFile{PathStr, *Type, /*IncludeName=*/"", Language});
125 continue;
126 }
127 auto IncludeName = createIncludeHeaderName(FullPath: PathStr);
128 HeaderList.emplace_back(args&: PathStr, args&: *Type,
129 args: IncludeName.has_value() ? IncludeName.value() : "",
130 args&: Language);
131 }
132
133 return Error::success();
134}
135
136Error Implementation::parse(StringRef Input) {
137 auto Val = json::parse(JSON: Input);
138 if (!Val)
139 return Val.takeError();
140
141 auto *Root = Val->getAsObject();
142 if (!Root)
143 return make_error<StringError>(Args: "not a JSON object",
144 Args: inconvertibleErrorCode());
145
146 auto VersionStr = Root->getString(K: "version");
147 if (!VersionStr)
148 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat,
149 Args: "required field 'version' not specified");
150 if (VersionStr->getAsInteger(Radix: 10, Result&: Version))
151 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat,
152 Args: "invalid version number");
153
154 if (Version < 1 || Version > 3)
155 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat,
156 Args: "unsupported version");
157
158 // Not specifying any header files should be atypical, but valid.
159 auto Headers = Root->getArray(K: "headers");
160 if (!Headers)
161 return Error::success();
162
163 Error Err = parseHeaders(Headers&: *Headers);
164 if (Err)
165 return Err;
166
167 return Error::success();
168}
169} // namespace
170
171llvm::Error
172FileListReader::loadHeaders(std::unique_ptr<MemoryBuffer> InputBuffer,
173 HeaderSeq &Destination) {
174 Implementation Impl;
175 Impl.InputBuffer = std::move(InputBuffer);
176
177 if (llvm::Error Err = Impl.parse(Input: Impl.InputBuffer->getBuffer()))
178 return Err;
179
180 Destination.reserve(n: Destination.size() + Impl.HeaderList.size());
181 llvm::move(Range&: Impl.HeaderList, Out: std::back_inserter(x&: Destination));
182
183 return Error::success();
184}
185

source code of clang/lib/InstallAPI/FileList.cpp