189f6b26fSZixu Wang //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===// 289f6b26fSZixu Wang // 389f6b26fSZixu Wang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 489f6b26fSZixu Wang // See https://llvm.org/LICENSE.txt for license information. 589f6b26fSZixu Wang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 689f6b26fSZixu Wang // 789f6b26fSZixu Wang //===----------------------------------------------------------------------===// 889f6b26fSZixu Wang /// 989f6b26fSZixu Wang /// \file 1089f6b26fSZixu Wang /// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to 1189f6b26fSZixu Wang /// collect API information. 1289f6b26fSZixu Wang /// 1389f6b26fSZixu Wang //===----------------------------------------------------------------------===// 1489f6b26fSZixu Wang 1589f6b26fSZixu Wang #include "clang/AST/ASTConsumer.h" 1689f6b26fSZixu Wang #include "clang/AST/ASTContext.h" 1789f6b26fSZixu Wang #include "clang/AST/Decl.h" 1889f6b26fSZixu Wang #include "clang/AST/DeclCXX.h" 1989f6b26fSZixu Wang #include "clang/AST/ParentMapContext.h" 2089f6b26fSZixu Wang #include "clang/AST/RawCommentList.h" 2189f6b26fSZixu Wang #include "clang/AST/RecursiveASTVisitor.h" 2289f6b26fSZixu Wang #include "clang/Basic/TargetInfo.h" 2389f6b26fSZixu Wang #include "clang/ExtractAPI/API.h" 2489f6b26fSZixu Wang #include "clang/ExtractAPI/AvailabilityInfo.h" 2589f6b26fSZixu Wang #include "clang/ExtractAPI/DeclarationFragments.h" 2689f6b26fSZixu Wang #include "clang/ExtractAPI/FrontendActions.h" 2789f6b26fSZixu Wang #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" 2889f6b26fSZixu Wang #include "clang/Frontend/ASTConsumers.h" 2989f6b26fSZixu Wang #include "clang/Frontend/CompilerInstance.h" 3089f6b26fSZixu Wang #include "llvm/Support/raw_ostream.h" 3189f6b26fSZixu Wang 3289f6b26fSZixu Wang using namespace clang; 3389f6b26fSZixu Wang using namespace extractapi; 3489f6b26fSZixu Wang 3589f6b26fSZixu Wang namespace { 3689f6b26fSZixu Wang 3789f6b26fSZixu Wang /// The RecursiveASTVisitor to traverse symbol declarations and collect API 3889f6b26fSZixu Wang /// information. 3989f6b26fSZixu Wang class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> { 4089f6b26fSZixu Wang public: 4189f6b26fSZixu Wang explicit ExtractAPIVisitor(ASTContext &Context) 4289f6b26fSZixu Wang : Context(Context), 4389f6b26fSZixu Wang API(Context.getTargetInfo().getTriple(), Context.getLangOpts()) {} 4489f6b26fSZixu Wang 4589f6b26fSZixu Wang const APISet &getAPI() const { return API; } 4689f6b26fSZixu Wang 4789f6b26fSZixu Wang bool VisitVarDecl(const VarDecl *Decl) { 4889f6b26fSZixu Wang // Skip function parameters. 4989f6b26fSZixu Wang if (isa<ParmVarDecl>(Decl)) 5089f6b26fSZixu Wang return true; 5189f6b26fSZixu Wang 5289f6b26fSZixu Wang // Skip non-global variables in records (struct/union/class). 5389f6b26fSZixu Wang if (Decl->getDeclContext()->isRecord()) 5489f6b26fSZixu Wang return true; 5589f6b26fSZixu Wang 5689f6b26fSZixu Wang // Skip local variables inside function or method. 5789f6b26fSZixu Wang if (!Decl->isDefinedOutsideFunctionOrMethod()) 5889f6b26fSZixu Wang return true; 5989f6b26fSZixu Wang 6089f6b26fSZixu Wang // If this is a template but not specialization or instantiation, skip. 6189f6b26fSZixu Wang if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) && 6289f6b26fSZixu Wang Decl->getTemplateSpecializationKind() == TSK_Undeclared) 6389f6b26fSZixu Wang return true; 6489f6b26fSZixu Wang 6589f6b26fSZixu Wang // Collect symbol information. 6689f6b26fSZixu Wang StringRef Name = Decl->getName(); 6789f6b26fSZixu Wang StringRef USR = API.recordUSR(Decl); 6889f6b26fSZixu Wang PresumedLoc Loc = 6989f6b26fSZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 7089f6b26fSZixu Wang AvailabilityInfo Availability = getAvailability(Decl); 7189f6b26fSZixu Wang LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 7289f6b26fSZixu Wang DocComment Comment; 7389f6b26fSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 7489f6b26fSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(), 7589f6b26fSZixu Wang Context.getDiagnostics()); 7689f6b26fSZixu Wang 7789f6b26fSZixu Wang // Build declaration fragments and sub-heading for the variable. 7889f6b26fSZixu Wang DeclarationFragments Declaration = 7989f6b26fSZixu Wang DeclarationFragmentsBuilder::getFragmentsForVar(Decl); 8089f6b26fSZixu Wang DeclarationFragments SubHeading = 8189f6b26fSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl); 8289f6b26fSZixu Wang 8389f6b26fSZixu Wang // Add the global variable record to the API set. 8489f6b26fSZixu Wang API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment, 8589f6b26fSZixu Wang Declaration, SubHeading); 8689f6b26fSZixu Wang return true; 8789f6b26fSZixu Wang } 8889f6b26fSZixu Wang 8989f6b26fSZixu Wang bool VisitFunctionDecl(const FunctionDecl *Decl) { 9089f6b26fSZixu Wang if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) { 9189f6b26fSZixu Wang // Skip member function in class templates. 9289f6b26fSZixu Wang if (Method->getParent()->getDescribedClassTemplate() != nullptr) 9389f6b26fSZixu Wang return true; 9489f6b26fSZixu Wang 9589f6b26fSZixu Wang // Skip methods in records. 9689f6b26fSZixu Wang for (auto P : Context.getParents(*Method)) { 9789f6b26fSZixu Wang if (P.get<CXXRecordDecl>()) 9889f6b26fSZixu Wang return true; 9989f6b26fSZixu Wang } 10089f6b26fSZixu Wang 10189f6b26fSZixu Wang // Skip ConstructorDecl and DestructorDecl. 10289f6b26fSZixu Wang if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method)) 10389f6b26fSZixu Wang return true; 10489f6b26fSZixu Wang } 10589f6b26fSZixu Wang 10689f6b26fSZixu Wang // Skip templated functions. 10789f6b26fSZixu Wang switch (Decl->getTemplatedKind()) { 10889f6b26fSZixu Wang case FunctionDecl::TK_NonTemplate: 10989f6b26fSZixu Wang break; 11089f6b26fSZixu Wang case FunctionDecl::TK_MemberSpecialization: 11189f6b26fSZixu Wang case FunctionDecl::TK_FunctionTemplateSpecialization: 11289f6b26fSZixu Wang if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) { 11389f6b26fSZixu Wang if (!TemplateInfo->isExplicitInstantiationOrSpecialization()) 11489f6b26fSZixu Wang return true; 11589f6b26fSZixu Wang } 11689f6b26fSZixu Wang break; 11789f6b26fSZixu Wang case FunctionDecl::TK_FunctionTemplate: 11889f6b26fSZixu Wang case FunctionDecl::TK_DependentFunctionTemplateSpecialization: 11989f6b26fSZixu Wang return true; 12089f6b26fSZixu Wang } 12189f6b26fSZixu Wang 12289f6b26fSZixu Wang // Collect symbol information. 12389f6b26fSZixu Wang StringRef Name = Decl->getName(); 12489f6b26fSZixu Wang StringRef USR = API.recordUSR(Decl); 12589f6b26fSZixu Wang PresumedLoc Loc = 12689f6b26fSZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 12789f6b26fSZixu Wang AvailabilityInfo Availability = getAvailability(Decl); 12889f6b26fSZixu Wang LinkageInfo Linkage = Decl->getLinkageAndVisibility(); 12989f6b26fSZixu Wang DocComment Comment; 13089f6b26fSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 13189f6b26fSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(), 13289f6b26fSZixu Wang Context.getDiagnostics()); 13389f6b26fSZixu Wang 13489f6b26fSZixu Wang // Build declaration fragments, sub-heading, and signature of the function. 13589f6b26fSZixu Wang DeclarationFragments Declaration = 13689f6b26fSZixu Wang DeclarationFragmentsBuilder::getFragmentsForFunction(Decl); 13789f6b26fSZixu Wang DeclarationFragments SubHeading = 13889f6b26fSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl); 13989f6b26fSZixu Wang FunctionSignature Signature = 14089f6b26fSZixu Wang DeclarationFragmentsBuilder::getFunctionSignature(Decl); 14189f6b26fSZixu Wang 14289f6b26fSZixu Wang // Add the function record to the API set. 14389f6b26fSZixu Wang API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration, 14489f6b26fSZixu Wang SubHeading, Signature); 14589f6b26fSZixu Wang return true; 14689f6b26fSZixu Wang } 14789f6b26fSZixu Wang 14871b4c226SZixu Wang bool VisitEnumDecl(const EnumDecl *Decl) { 14971b4c226SZixu Wang if (!Decl->isComplete()) 15071b4c226SZixu Wang return true; 15171b4c226SZixu Wang 15271b4c226SZixu Wang // Skip forward declaration. 15371b4c226SZixu Wang if (!Decl->isThisDeclarationADefinition()) 15471b4c226SZixu Wang return true; 15571b4c226SZixu Wang 15671b4c226SZixu Wang // Collect symbol information. 15771b4c226SZixu Wang StringRef Name = Decl->getName(); 15871b4c226SZixu Wang StringRef USR = API.recordUSR(Decl); 15971b4c226SZixu Wang PresumedLoc Loc = 16071b4c226SZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 16171b4c226SZixu Wang AvailabilityInfo Availability = getAvailability(Decl); 16271b4c226SZixu Wang DocComment Comment; 16371b4c226SZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 16471b4c226SZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(), 16571b4c226SZixu Wang Context.getDiagnostics()); 16671b4c226SZixu Wang 16771b4c226SZixu Wang // Build declaration fragments and sub-heading for the enum. 16871b4c226SZixu Wang DeclarationFragments Declaration = 16971b4c226SZixu Wang DeclarationFragmentsBuilder::getFragmentsForEnum(Decl); 17071b4c226SZixu Wang DeclarationFragments SubHeading = 17171b4c226SZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl); 17271b4c226SZixu Wang 17371b4c226SZixu Wang EnumRecord *EnumRecord = API.addEnum(Name, USR, Loc, Availability, Comment, 17471b4c226SZixu Wang Declaration, SubHeading); 17571b4c226SZixu Wang 17671b4c226SZixu Wang // Now collect information about the enumerators in this enum. 17771b4c226SZixu Wang recordEnumConstants(EnumRecord, Decl->enumerators()); 17871b4c226SZixu Wang 17971b4c226SZixu Wang return true; 18071b4c226SZixu Wang } 18171b4c226SZixu Wang 182*5bb5704cSZixu Wang bool VisitRecordDecl(const RecordDecl *Decl) { 183*5bb5704cSZixu Wang if (!Decl->isCompleteDefinition()) 184*5bb5704cSZixu Wang return true; 185*5bb5704cSZixu Wang 186*5bb5704cSZixu Wang // Skip C++ structs/classes/unions 187*5bb5704cSZixu Wang // TODO: support C++ records 188*5bb5704cSZixu Wang if (isa<CXXRecordDecl>(Decl)) 189*5bb5704cSZixu Wang return true; 190*5bb5704cSZixu Wang 191*5bb5704cSZixu Wang // Collect symbol information. 192*5bb5704cSZixu Wang StringRef Name = Decl->getName(); 193*5bb5704cSZixu Wang StringRef USR = API.recordUSR(Decl); 194*5bb5704cSZixu Wang PresumedLoc Loc = 195*5bb5704cSZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation()); 196*5bb5704cSZixu Wang AvailabilityInfo Availability = getAvailability(Decl); 197*5bb5704cSZixu Wang DocComment Comment; 198*5bb5704cSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) 199*5bb5704cSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(), 200*5bb5704cSZixu Wang Context.getDiagnostics()); 201*5bb5704cSZixu Wang 202*5bb5704cSZixu Wang // Build declaration fragments and sub-heading for the struct. 203*5bb5704cSZixu Wang DeclarationFragments Declaration = 204*5bb5704cSZixu Wang DeclarationFragmentsBuilder::getFragmentsForStruct(Decl); 205*5bb5704cSZixu Wang DeclarationFragments SubHeading = 206*5bb5704cSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl); 207*5bb5704cSZixu Wang 208*5bb5704cSZixu Wang StructRecord *StructRecord = API.addStruct( 209*5bb5704cSZixu Wang Name, USR, Loc, Availability, Comment, Declaration, SubHeading); 210*5bb5704cSZixu Wang 211*5bb5704cSZixu Wang // Now collect information about the fields in this struct. 212*5bb5704cSZixu Wang recordStructFields(StructRecord, Decl->fields()); 213*5bb5704cSZixu Wang 214*5bb5704cSZixu Wang return true; 215*5bb5704cSZixu Wang } 216*5bb5704cSZixu Wang 21789f6b26fSZixu Wang private: 21889f6b26fSZixu Wang /// Get availability information of the declaration \p D. 21989f6b26fSZixu Wang AvailabilityInfo getAvailability(const Decl *D) const { 22089f6b26fSZixu Wang StringRef PlatformName = Context.getTargetInfo().getPlatformName(); 22189f6b26fSZixu Wang 22289f6b26fSZixu Wang AvailabilityInfo Availability; 22389f6b26fSZixu Wang // Collect availability attributes from all redeclarations. 22489f6b26fSZixu Wang for (const auto *RD : D->redecls()) { 22589f6b26fSZixu Wang for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) { 22689f6b26fSZixu Wang if (A->getPlatform()->getName() != PlatformName) 22789f6b26fSZixu Wang continue; 22889f6b26fSZixu Wang Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(), 22989f6b26fSZixu Wang A->getObsoleted(), A->getUnavailable(), 23089f6b26fSZixu Wang /* UnconditionallyDeprecated */ false, 23189f6b26fSZixu Wang /* UnconditionallyUnavailable */ false); 23289f6b26fSZixu Wang break; 23389f6b26fSZixu Wang } 23489f6b26fSZixu Wang 23589f6b26fSZixu Wang if (const auto *A = RD->getAttr<UnavailableAttr>()) 23689f6b26fSZixu Wang if (!A->isImplicit()) { 23789f6b26fSZixu Wang Availability.Unavailable = true; 23889f6b26fSZixu Wang Availability.UnconditionallyUnavailable = true; 23989f6b26fSZixu Wang } 24089f6b26fSZixu Wang 24189f6b26fSZixu Wang if (const auto *A = RD->getAttr<DeprecatedAttr>()) 24289f6b26fSZixu Wang if (!A->isImplicit()) 24389f6b26fSZixu Wang Availability.UnconditionallyDeprecated = true; 24489f6b26fSZixu Wang } 24589f6b26fSZixu Wang 24689f6b26fSZixu Wang return Availability; 24789f6b26fSZixu Wang } 24889f6b26fSZixu Wang 24971b4c226SZixu Wang /// Collect API information for the enum constants and associate with the 25071b4c226SZixu Wang /// parent enum. 25171b4c226SZixu Wang void recordEnumConstants(EnumRecord *EnumRecord, 25271b4c226SZixu Wang const EnumDecl::enumerator_range Constants) { 25371b4c226SZixu Wang for (const auto *Constant : Constants) { 25471b4c226SZixu Wang // Collect symbol information. 25571b4c226SZixu Wang StringRef Name = Constant->getName(); 25671b4c226SZixu Wang StringRef USR = API.recordUSR(Constant); 25771b4c226SZixu Wang PresumedLoc Loc = 25871b4c226SZixu Wang Context.getSourceManager().getPresumedLoc(Constant->getLocation()); 25971b4c226SZixu Wang AvailabilityInfo Availability = getAvailability(Constant); 26071b4c226SZixu Wang DocComment Comment; 26171b4c226SZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant)) 26271b4c226SZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(), 26371b4c226SZixu Wang Context.getDiagnostics()); 26471b4c226SZixu Wang 26571b4c226SZixu Wang // Build declaration fragments and sub-heading for the enum constant. 26671b4c226SZixu Wang DeclarationFragments Declaration = 26771b4c226SZixu Wang DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant); 26871b4c226SZixu Wang DeclarationFragments SubHeading = 26971b4c226SZixu Wang DeclarationFragmentsBuilder::getSubHeading(Constant); 27071b4c226SZixu Wang 27171b4c226SZixu Wang API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment, 27271b4c226SZixu Wang Declaration, SubHeading); 27371b4c226SZixu Wang } 27471b4c226SZixu Wang } 27571b4c226SZixu Wang 276*5bb5704cSZixu Wang /// Collect API information for the struct fields and associate with the 277*5bb5704cSZixu Wang /// parent struct. 278*5bb5704cSZixu Wang void recordStructFields(StructRecord *StructRecord, 279*5bb5704cSZixu Wang const RecordDecl::field_range Fields) { 280*5bb5704cSZixu Wang for (const auto *Field : Fields) { 281*5bb5704cSZixu Wang // Collect symbol information. 282*5bb5704cSZixu Wang StringRef Name = Field->getName(); 283*5bb5704cSZixu Wang StringRef USR = API.recordUSR(Field); 284*5bb5704cSZixu Wang PresumedLoc Loc = 285*5bb5704cSZixu Wang Context.getSourceManager().getPresumedLoc(Field->getLocation()); 286*5bb5704cSZixu Wang AvailabilityInfo Availability = getAvailability(Field); 287*5bb5704cSZixu Wang DocComment Comment; 288*5bb5704cSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field)) 289*5bb5704cSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(), 290*5bb5704cSZixu Wang Context.getDiagnostics()); 291*5bb5704cSZixu Wang 292*5bb5704cSZixu Wang // Build declaration fragments and sub-heading for the struct field. 293*5bb5704cSZixu Wang DeclarationFragments Declaration = 294*5bb5704cSZixu Wang DeclarationFragmentsBuilder::getFragmentsForField(Field); 295*5bb5704cSZixu Wang DeclarationFragments SubHeading = 296*5bb5704cSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Field); 297*5bb5704cSZixu Wang 298*5bb5704cSZixu Wang API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment, 299*5bb5704cSZixu Wang Declaration, SubHeading); 300*5bb5704cSZixu Wang } 301*5bb5704cSZixu Wang } 302*5bb5704cSZixu Wang 30389f6b26fSZixu Wang ASTContext &Context; 30489f6b26fSZixu Wang APISet API; 30589f6b26fSZixu Wang }; 30689f6b26fSZixu Wang 30789f6b26fSZixu Wang class ExtractAPIConsumer : public ASTConsumer { 30889f6b26fSZixu Wang public: 3095ef2ec7eSDaniel Grumberg ExtractAPIConsumer(ASTContext &Context, StringRef ProductName, 3105ef2ec7eSDaniel Grumberg std::unique_ptr<raw_pwrite_stream> OS) 3115ef2ec7eSDaniel Grumberg : Visitor(Context), ProductName(ProductName), OS(std::move(OS)) {} 31289f6b26fSZixu Wang 31389f6b26fSZixu Wang void HandleTranslationUnit(ASTContext &Context) override { 31489f6b26fSZixu Wang // Use ExtractAPIVisitor to traverse symbol declarations in the context. 31589f6b26fSZixu Wang Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 31689f6b26fSZixu Wang 31789f6b26fSZixu Wang // Setup a SymbolGraphSerializer to write out collected API information in 31889f6b26fSZixu Wang // the Symbol Graph format. 31989f6b26fSZixu Wang // FIXME: Make the kind of APISerializer configurable. 3205ef2ec7eSDaniel Grumberg SymbolGraphSerializer SGSerializer(Visitor.getAPI(), ProductName); 32189f6b26fSZixu Wang SGSerializer.serialize(*OS); 32289f6b26fSZixu Wang } 32389f6b26fSZixu Wang 32489f6b26fSZixu Wang private: 32589f6b26fSZixu Wang ExtractAPIVisitor Visitor; 3265ef2ec7eSDaniel Grumberg std::string ProductName; 32789f6b26fSZixu Wang std::unique_ptr<raw_pwrite_stream> OS; 32889f6b26fSZixu Wang }; 32989f6b26fSZixu Wang 33089f6b26fSZixu Wang } // namespace 33189f6b26fSZixu Wang 33289f6b26fSZixu Wang std::unique_ptr<ASTConsumer> 33389f6b26fSZixu Wang ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 33489f6b26fSZixu Wang std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile); 33589f6b26fSZixu Wang if (!OS) 33689f6b26fSZixu Wang return nullptr; 3375ef2ec7eSDaniel Grumberg return std::make_unique<ExtractAPIConsumer>( 3385ef2ec7eSDaniel Grumberg CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName, 33989f6b26fSZixu Wang std::move(OS)); 34089f6b26fSZixu Wang } 34189f6b26fSZixu Wang 34289f6b26fSZixu Wang std::unique_ptr<raw_pwrite_stream> 34389f6b26fSZixu Wang ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 34489f6b26fSZixu Wang std::unique_ptr<raw_pwrite_stream> OS = 34589f6b26fSZixu Wang CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json", 34689f6b26fSZixu Wang /*RemoveFileOnSignal=*/false); 34789f6b26fSZixu Wang if (!OS) 34889f6b26fSZixu Wang return nullptr; 34989f6b26fSZixu Wang return OS; 35089f6b26fSZixu Wang } 351