xref: /freebsd/contrib/llvm-project/clang/lib/InstallAPI/FileList.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 /*
20 InstallAPI 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 
37 using namespace llvm;
38 using namespace llvm::json;
39 using namespace llvm::MachO;
40 using namespace clang::installapi;
41 
42 namespace {
43 class Implementation {
44 private:
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 
52 public:
53   std::unique_ptr<MemoryBuffer> InputBuffer;
54   clang::FileManager *FM;
55   unsigned Version;
56   HeaderSeq HeaderList;
57 
58   Error parse(StringRef Input);
59 };
60 
61 Expected<StringRef>
parseString(const Object * Obj,StringRef Key,StringRef Error)62 Implementation::parseString(const Object *Obj, StringRef Key, StringRef Error) {
63   auto Str = Obj->getString(Key);
64   if (!Str)
65     return make_error<StringError>(Error, inconvertibleErrorCode());
66   return *Str;
67 }
68 
parseType(const Object * Obj)69 Expected<HeaderType> Implementation::parseType(const Object *Obj) {
70   auto TypeStr =
71       parseString(Obj, "type", "required field 'type' not specified");
72   if (!TypeStr)
73     return TypeStr.takeError();
74 
75   if (*TypeStr == "public")
76     return HeaderType::Public;
77   else if (*TypeStr == "private")
78     return HeaderType::Private;
79   else if (*TypeStr == "project" && Version >= 2)
80     return HeaderType::Project;
81 
82   return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
83                                   "unsupported header type");
84 }
85 
parsePath(const Object * Obj)86 Expected<StringRef> Implementation::parsePath(const Object *Obj) {
87   auto Path = parseString(Obj, "path", "required field 'path' not specified");
88   if (!Path)
89     return Path.takeError();
90 
91   return *Path;
92 }
93 
94 std::optional<clang::Language>
parseLanguage(const Object * Obj)95 Implementation::parseLanguage(const Object *Obj) {
96   auto Language = Obj->getString("language");
97   if (!Language)
98     return std::nullopt;
99 
100   return StringSwitch<clang::Language>(*Language)
101       .Case("c", clang::Language::C)
102       .Case("c++", clang::Language::CXX)
103       .Case("objective-c", clang::Language::ObjC)
104       .Case("objective-c++", clang::Language::ObjCXX)
105       .Default(clang::Language::Unknown);
106 }
107 
parseHeaders(Array & Headers)108 Error Implementation::parseHeaders(Array &Headers) {
109   for (const auto &H : Headers) {
110     auto *Obj = H.getAsObject();
111     if (!Obj)
112       return make_error<StringError>("expect a JSON object",
113                                      inconvertibleErrorCode());
114     auto Type = parseType(Obj);
115     if (!Type)
116       return Type.takeError();
117     auto Path = parsePath(Obj);
118     if (!Path)
119       return Path.takeError();
120     auto Language = parseLanguage(Obj);
121 
122     StringRef PathStr = *Path;
123     if (*Type == HeaderType::Project) {
124       HeaderList.emplace_back(
125           HeaderFile{PathStr, *Type, /*IncludeName=*/"", Language});
126       continue;
127     }
128 
129     if (FM)
130       if (!FM->getOptionalFileRef(PathStr))
131         return createFileError(
132             PathStr, make_error_code(std::errc::no_such_file_or_directory));
133 
134     auto IncludeName = createIncludeHeaderName(PathStr);
135     HeaderList.emplace_back(PathStr, *Type,
136                             IncludeName.has_value() ? IncludeName.value() : "",
137                             Language);
138   }
139 
140   return Error::success();
141 }
142 
parse(StringRef Input)143 Error Implementation::parse(StringRef Input) {
144   auto Val = json::parse(Input);
145   if (!Val)
146     return Val.takeError();
147 
148   auto *Root = Val->getAsObject();
149   if (!Root)
150     return make_error<StringError>("not a JSON object",
151                                    inconvertibleErrorCode());
152 
153   auto VersionStr = Root->getString("version");
154   if (!VersionStr)
155     return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
156                                     "required field 'version' not specified");
157   if (VersionStr->getAsInteger(10, Version))
158     return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
159                                     "invalid version number");
160 
161   if (Version < 1 || Version > 3)
162     return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
163                                     "unsupported version");
164 
165   // Not specifying any header files should be atypical, but valid.
166   auto Headers = Root->getArray("headers");
167   if (!Headers)
168     return Error::success();
169 
170   Error Err = parseHeaders(*Headers);
171   if (Err)
172     return Err;
173 
174   return Error::success();
175 }
176 } // namespace
177 
178 llvm::Error
loadHeaders(std::unique_ptr<MemoryBuffer> InputBuffer,HeaderSeq & Destination,clang::FileManager * FM)179 FileListReader::loadHeaders(std::unique_ptr<MemoryBuffer> InputBuffer,
180                             HeaderSeq &Destination, clang::FileManager *FM) {
181   Implementation Impl;
182   Impl.InputBuffer = std::move(InputBuffer);
183   Impl.FM = FM;
184 
185   if (llvm::Error Err = Impl.parse(Impl.InputBuffer->getBuffer()))
186     return Err;
187 
188   Destination.reserve(Destination.size() + Impl.HeaderList.size());
189   llvm::move(Impl.HeaderList, std::back_inserter(Destination));
190 
191   return Error::success();
192 }
193