//===--- SwiftDocSupport.cpp ----------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "SwiftASTManager.h" #include "SwiftEditorDiagConsumer.h" #include "SwiftLangSupport.h" #include "SourceKit/Support/UIdent.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/ASTWalker.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CommentConversion.h" #include "swift/IDE/ModuleInterfacePrinting.h" #include "swift/IDE/SourceEntityWalker.h" #include "swift/IDE/SyntaxModel.h" // This is included only for createLazyResolver(). Move to different header ? #include "swift/Sema/IDETypeChecking.h" #include "swift/Config.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" using namespace SourceKit; using namespace swift; using namespace ide; static Module *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; std::tie(SubModuleName, ModuleName) = ModuleName.split('.'); AccessPath.push_back({ Ctx.getIdentifier(SubModuleName), SourceLoc() }); } return Ctx.getModule(AccessPath); } static Module *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) { return Ctx.getModule(std::make_pair(ModuleName, SourceLoc())); } namespace { struct TextRange { unsigned Offset; unsigned Length; }; struct TextEntity { const Decl *Dcl = nullptr; StringRef Argument; TextRange Range; unsigned LocOffset = 0; std::vector SubEntities; TextEntity(const Decl *D, unsigned StartOffset) : Dcl(D), Range{StartOffset, 0} {} TextEntity(const Decl *D, TextRange TR, unsigned LocOffset) : Dcl(D), Range(TR), LocOffset(LocOffset) {} TextEntity(const Decl *D, StringRef Arg, TextRange TR, unsigned LocOffset) : Dcl(D), Argument(Arg), Range(TR), LocOffset(LocOffset) {} }; struct TextReference { const ValueDecl *Dcl = nullptr; TextRange Range; const Type Ty; TextReference(const ValueDecl *D, unsigned Offset, unsigned Length, const Type Ty = Type()) : Dcl(D), Range{Offset, Length}, Ty(Ty) {} }; class AnnotatingPrinter : public StreamPrinter { public: std::vector TopEntities; std::vector EntitiesStack; std::vector References; using StreamPrinter::StreamPrinter; ~AnnotatingPrinter() { assert(EntitiesStack.empty()); } void printDeclPre(const Decl *D) override { if (isa(D)) return; // Parameters are handled specially in addParameters(). unsigned StartOffset = OS.tell(); EntitiesStack.emplace_back(D, StartOffset); } void printDeclLoc(const Decl *D) override { if (EntitiesStack.back().Dcl == D) { unsigned LocOffset = OS.tell(); EntitiesStack.back().LocOffset = LocOffset; } } void printDeclPost(const Decl *D) override { if (isa(D)) return; // Parameters are handled specially in addParameters(). assert(EntitiesStack.back().Dcl == D); TextEntity Entity = std::move(EntitiesStack.back()); EntitiesStack.pop_back(); unsigned EndOffset = OS.tell(); Entity.Range.Length = EndOffset - Entity.Range.Offset; if (EntitiesStack.empty()) TopEntities.push_back(std::move(Entity)); else EntitiesStack.back().SubEntities.push_back(std::move(Entity)); } void printTypeRef(Type T, const TypeDecl *TD, Identifier Name) override { unsigned StartOffset = OS.tell(); References.emplace_back(TD, StartOffset, Name.str().size()); StreamPrinter::printTypeRef(T, TD, Name); } }; struct SourceTextInfo { std::string Text; std::vector TopEntities; std::vector References; }; } static void initDocGenericParams(const Decl *D, DocEntityInfo &Info) { GenericParamList *GenParams = nullptr; if (auto *NTD = dyn_cast(D)) { GenParams = NTD->getGenericParams(); } else if (auto *AFD = dyn_cast(D)) { GenParams = AFD->getGenericParams(); } else if (auto *ExtD = dyn_cast(D)) { GenParams = ExtD->getGenericParams(); } if (!GenParams) return; for (auto *GP : GenParams->getParams()) { if (GP->isImplicit()) continue; DocGenericParam Param; Param.Name = GP->getNameStr(); if (!GP->getInherited().empty()) { llvm::raw_string_ostream OS(Param.Inherits); GP->getInherited()[0].getType().print(OS); } Info.GenericParams.push_back(Param); } for (auto &Req : GenParams->getRequirements()) { std::string ReqStr; llvm::raw_string_ostream OS(ReqStr); Req.printAsWritten(OS); OS.flush(); Info.GenericRequirements.push_back(std::move(ReqStr)); } } static bool initDocEntityInfo(const Decl *D, bool IsRef, DocEntityInfo &Info, StringRef Arg = StringRef()) { if (!D || isa(D) || (isa(D) && D->getDeclContext()->isLocalContext())) { Info.Kind = SwiftLangSupport::getUIDForLocalVar(IsRef); if (D) { llvm::raw_svector_ostream OS(Info.Name); SwiftLangSupport::printDisplayName(cast(D), OS); } else { Info.Name = "_"; } if (!Arg.empty()) Info.Argument = Arg.str(); return false; } Info.Kind = SwiftLangSupport::getUIDForDecl(D, IsRef); if (Info.Kind.isInvalid()) return true; if (const ValueDecl *VD = dyn_cast(D)) { llvm::raw_svector_ostream NameOS(Info.Name); SwiftLangSupport::printDisplayName(VD, NameOS); llvm::raw_svector_ostream OS(Info.USR); SwiftLangSupport::printUSR(VD, OS); } Info.IsUnavailable = AvailableAttr::isUnavailable(D); Info.IsDeprecated = D->getAttrs().getDeprecated(D->getASTContext()) != nullptr; if (!IsRef) { llvm::raw_svector_ostream OS(Info.DocComment); ide::getDocumentationCommentAsXML(D, OS); initDocGenericParams(D, Info); if (auto *VD = dyn_cast(D)) { llvm::raw_svector_ostream OS(Info.FullyAnnotatedDecl); SwiftLangSupport::printFullyAnnotatedDeclaration(VD, Type(), OS); } } return false; } static bool initDocEntityInfo(const TextEntity &Entity, DocEntityInfo &Info) { if (initDocEntityInfo(Entity.Dcl, /*IsRef=*/false, Info, Entity.Argument)) return true; Info.Offset = Entity.Range.Offset; Info.Length = Entity.Range.Length; return false; } static const TypeDecl *getTypeDeclFromType(Type Ty) { if (auto Alias = dyn_cast(Ty.getPointer())) return Alias->getDecl(); return Ty->getAnyNominal(); } static void passInherits(const ValueDecl *D, DocInfoConsumer &Consumer) { DocEntityInfo EntInfo; if (initDocEntityInfo(D, /*IsRef=*/true, EntInfo)) return; Consumer.handleInheritsEntity(EntInfo); } static void passConforms(const ValueDecl *D, DocInfoConsumer &Consumer) { DocEntityInfo EntInfo; if (initDocEntityInfo(D, /*IsRef=*/true, EntInfo)) return; Consumer.handleConformsToEntity(EntInfo); } static void passInherits(ArrayRef InheritedTypes, DocInfoConsumer &Consumer) { for (auto Inherited : InheritedTypes) { if (!Inherited.getType()) continue; if (auto Proto = Inherited.getType()->getAs()) { passConforms(Proto->getDecl(), Consumer); continue; } if (auto ProtoComposition = Inherited.getType()->getAs()) { for (auto T : ProtoComposition->getProtocols()) passInherits(TypeLoc::withoutLoc(T), Consumer); continue; } if (auto TD = getTypeDeclFromType(Inherited.getType())) { passInherits(TD, Consumer); continue; } } } static void passConforms(ArrayRef Dcls, DocInfoConsumer &Consumer) { for (auto D : Dcls) passConforms(D, Consumer); } static void passExtends(const ValueDecl *D, DocInfoConsumer &Consumer) { DocEntityInfo EntInfo; if (initDocEntityInfo(D, /*IsRef=*/true, EntInfo)) return; Consumer.handleExtendsEntity(EntInfo); } static void reportRelated(ASTContext &Ctx, const Decl *D, DocInfoConsumer &Consumer) { if (!D || isa(D)) return; if (const ExtensionDecl *ED = dyn_cast(D)) { if (Type T = ED->getExtendedType()) if (auto TD = getTypeDeclFromType(T)) passExtends(TD, Consumer); passInherits(ED->getInherited(), Consumer); } else if (const TypeDecl *TD = dyn_cast(D)) { passInherits(TD->getInherited(), Consumer); passConforms(TD->getSatisfiedProtocolRequirements(/*Sorted=*/true), Consumer); } else if (auto *VD = dyn_cast(D)) { if (auto Overridden = VD->getOverriddenDecl()) passInherits(Overridden, Consumer); passConforms(VD->getSatisfiedProtocolRequirements(/*Sorted=*/true), Consumer); } } // Only reports @available. // FIXME: Handle all attributes. static void reportAttributes(ASTContext &Ctx, const Decl *D, DocInfoConsumer &Consumer) { static UIdent AvailableAttrKind("source.lang.swift.attribute.availability"); static UIdent PlatformIOS("source.availability.platform.ios"); static UIdent PlatformOSX("source.availability.platform.osx"); static UIdent PlatformtvOS("source.availability.platform.tvos"); static UIdent PlatformWatchOS("source.availability.platform.watchos"); static UIdent PlatformIOSAppExt("source.availability.platform.ios_app_extension"); static UIdent PlatformOSXAppExt("source.availability.platform.osx_app_extension"); static UIdent PlatformtvOSAppExt("source.availability.platform.tvos_app_extension"); static UIdent PlatformWatchOSAppExt("source.availability.platform.watchos_app_extension"); for (auto Attr : D->getAttrs()) { if (auto Av = dyn_cast(Attr)) { UIdent PlatformUID; switch (Av->Platform) { case PlatformKind::none: PlatformUID = UIdent(); break; case PlatformKind::iOS: PlatformUID = PlatformIOS; break; case PlatformKind::OSX: PlatformUID = PlatformOSX; break; case PlatformKind::tvOS: PlatformUID = PlatformtvOS; break; case PlatformKind::watchOS: PlatformUID = PlatformWatchOS; break; case PlatformKind::iOSApplicationExtension: PlatformUID = PlatformIOSAppExt; break; case PlatformKind::OSXApplicationExtension: PlatformUID = PlatformOSXAppExt; break; case PlatformKind::tvOSApplicationExtension: PlatformUID = PlatformtvOSAppExt; break; case PlatformKind::watchOSApplicationExtension: PlatformUID = PlatformWatchOSAppExt; break; } AvailableAttrInfo Info; Info.AttrKind = AvailableAttrKind; Info.IsUnavailable = Av->isUnconditionallyUnavailable(); Info.IsDeprecated = Av->isUnconditionallyDeprecated(); Info.Platform = PlatformUID; Info.Message = Av->Message; if (Av->Introduced) Info.Introduced = *Av->Introduced; if (Av->Deprecated) Info.Deprecated = *Av->Deprecated; if (Av->Obsoleted) Info.Obsoleted = *Av->Obsoleted; Consumer.handleAvailableAttribute(Info); } } } static void reportDocEntities(ASTContext &Ctx, ArrayRef Entities, DocInfoConsumer &Consumer) { for (auto &Entity : Entities) { DocEntityInfo EntInfo; if (initDocEntityInfo(Entity, EntInfo)) continue; Consumer.startSourceEntity(EntInfo); reportRelated(Ctx, Entity.Dcl, Consumer); reportDocEntities(Ctx, Entity.SubEntities, Consumer); reportAttributes(Ctx, Entity.Dcl, Consumer); Consumer.finishSourceEntity(EntInfo.Kind); } } namespace { class DocSyntaxWalker : public SyntaxModelWalker { SourceManager &SM; unsigned BufferID; ArrayRef References; DocInfoConsumer &Consumer; SourceLoc LastArgLoc; SourceLoc LastParamLoc; public: DocSyntaxWalker(SourceManager &SM, unsigned BufferID, ArrayRef References, DocInfoConsumer &Consumer) : SM(SM), BufferID(BufferID), References(References), Consumer(Consumer) {} bool walkToNodePre(SyntaxNode Node) override { unsigned Offset = SM.getLocOffsetInBuffer(Node.Range.getStart(), BufferID); unsigned Length = Node.Range.getByteLength(); reportRefsUntil(Offset); if (!References.empty() && References.front().Range.Offset == Offset) return true; switch (Node.Kind) { case SyntaxNodeKind::EditorPlaceholder: return true; case SyntaxNodeKind::Keyword: if (Node.Range.getStart() == LastArgLoc || Node.Range.getStart() == LastParamLoc) return true; break; case SyntaxNodeKind::Identifier: case SyntaxNodeKind::DollarIdent: case SyntaxNodeKind::Integer: case SyntaxNodeKind::Floating: case SyntaxNodeKind::String: case SyntaxNodeKind::StringInterpolationAnchor: case SyntaxNodeKind::CommentLine: case SyntaxNodeKind::CommentBlock: case SyntaxNodeKind::CommentMarker: case SyntaxNodeKind::CommentURL: case SyntaxNodeKind::DocCommentLine: case SyntaxNodeKind::DocCommentBlock: case SyntaxNodeKind::DocCommentField: case SyntaxNodeKind::TypeId: case SyntaxNodeKind::BuildConfigKeyword: case SyntaxNodeKind::BuildConfigId: case SyntaxNodeKind::AttributeId: case SyntaxNodeKind::AttributeBuiltin: case SyntaxNodeKind::ObjectLiteral: break; } DocEntityInfo Info; Info.Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Node.Kind); Info.Offset = Offset; Info.Length = Length; Consumer.handleAnnotation(Info); return true; } void finished() { reportRefsUntil(std::numeric_limits::max()); } bool walkToSubStructurePre(SyntaxStructureNode Node) override { if (Node.Kind == SyntaxStructureKind::Parameter) { auto Param = dyn_cast(Node.Dcl); auto passAnnotation = [&](UIdent Kind, SourceLoc Loc, Identifier Name) { if (Loc.isInvalid()) return; unsigned Offset = SM.getLocOffsetInBuffer(Loc, BufferID); unsigned Length = Name.empty() ? 1 : Name.getLength(); reportRefsUntil(Offset); DocEntityInfo Info; Info.Kind = Kind; Info.Offset = Offset; Info.Length = Length; Consumer.handleAnnotation(Info); }; // Argument static UIdent KindArgument("source.lang.swift.syntaxtype.argument"); passAnnotation(KindArgument, Param->getArgumentNameLoc(), Param->getArgumentName()); LastArgLoc = Param->getArgumentNameLoc(); // Parameter static UIdent KindParameter("source.lang.swift.syntaxtype.parameter"); passAnnotation(KindParameter, Param->getNameLoc(), Param->getName()); LastParamLoc = Param->getNameLoc(); } return true; } private: void reportRefsUntil(unsigned Offset) { while (!References.empty() && References.front().Range.Offset < Offset) { const TextReference &Ref = References.front(); References = References.slice(1); DocEntityInfo Info; if (initDocEntityInfo(Ref.Dcl, /*IsRef=*/true, Info)) continue; Info.Offset = Ref.Range.Offset; Info.Length = Ref.Range.Length; Info.Ty = Ref.Ty; Consumer.handleAnnotation(Info); } } }; } static bool makeParserAST(CompilerInstance &CI, StringRef Text) { CompilerInvocation Invocation; Invocation.setModuleName("main"); Invocation.setInputKind(InputFileKind::IFK_Swift); std::unique_ptr Buf; Buf = llvm::MemoryBuffer::getMemBuffer(Text, ""); Invocation.addInputBuffer(Buf.get()); if (CI.setup(Invocation)) return true; CI.performParseOnly(); return false; } static void collectFuncEntities(std::vector &Ents, std::vector &FuncEntities) { for (TextEntity &Ent : Ents) { if (isa(Ent.Dcl) || isa(Ent.Dcl)) { // We are getting the entities via a pointer and later adding to their // subentities; make sure it doesn't have subentities now or we are going // to invalidate the pointers. assert(Ent.SubEntities.empty()); FuncEntities.push_back(&Ent); } collectFuncEntities(Ent.SubEntities, FuncEntities); } } static void addParameters(ArrayRef &ArgNames, const ParameterList *paramList, TextEntity &Ent, SourceManager &SM, unsigned BufferID) { for (auto ¶m : *paramList) { StringRef Arg; if (!ArgNames.empty()) { Identifier Id = ArgNames.front(); Arg = Id.empty() ? "_" : Id.str(); ArgNames = ArgNames.slice(1); } if (auto typeRepr = param->getTypeLoc().getTypeRepr()) { SourceRange TypeRange = param->getTypeLoc().getSourceRange(); if (auto InOutTyR = dyn_cast_or_null(typeRepr)) TypeRange = InOutTyR->getBase()->getSourceRange(); if (TypeRange.isInvalid()) continue; unsigned StartOffs = SM.getLocOffsetInBuffer(TypeRange.Start, BufferID); unsigned EndOffs = SM.getLocOffsetInBuffer(Lexer::getLocForEndOfToken(SM, TypeRange.End), BufferID); TextRange TR{ StartOffs, EndOffs-StartOffs }; TextEntity Param(param, Arg, TR, StartOffs); Ent.SubEntities.push_back(std::move(Param)); } } } static void addParameters(const AbstractFunctionDecl *FD, TextEntity &Ent, SourceManager &SM, unsigned BufferID) { auto params = FD->getParameterLists(); // Ignore 'self'. if (FD->getDeclContext()->isTypeContext()) params = params.slice(1); ArrayRef ArgNames; DeclName Name = FD->getFullName(); if (Name) { ArgNames = Name.getArgumentNames(); } for (auto paramList : params) { addParameters(ArgNames, paramList, Ent, SM, BufferID); } } static void addParameters(const SubscriptDecl *D, TextEntity &Ent, SourceManager &SM, unsigned BufferID) { ArrayRef ArgNames; DeclName Name = D->getFullName(); if (Name) { ArgNames = Name.getArgumentNames(); } addParameters(ArgNames, D->getIndices(), Ent, SM, BufferID); } namespace { class FuncWalker : public ASTWalker { SourceManager &SM; unsigned BufferID; llvm::MutableArrayRef FuncEnts; public: FuncWalker(SourceManager &SM, unsigned BufferID, llvm::MutableArrayRef FuncEnts) : SM(SM), BufferID(BufferID), FuncEnts(FuncEnts) {} bool walkToDeclPre(Decl *D) override { if (D->isImplicit()) return false; // Skip body. if (FuncEnts.empty()) return false; if (!isa(D) && !isa(D)) return true; unsigned Offset = SM.getLocOffsetInBuffer(D->getLoc(), BufferID); auto Found = FuncEnts.end(); if (FuncEnts.front()->LocOffset == Offset) { Found = FuncEnts.begin(); } else { Found = std::lower_bound(FuncEnts.begin(), FuncEnts.end(), Offset, [](TextEntity *Ent, unsigned Offs) { return Ent->LocOffset < Offs; }); } if (Found == FuncEnts.end() || (*Found)->LocOffset != Offset) return false; if (auto FD = dyn_cast(D)) { addParameters(FD, **Found, SM, BufferID); } else { addParameters(cast(D), **Found, SM, BufferID); } FuncEnts = llvm::MutableArrayRef(Found+1, FuncEnts.end()); return false; // skip body. } }; } static void addParameterEntities(CompilerInstance &CI, SourceTextInfo &IFaceInfo) { std::vector FuncEntities; collectFuncEntities(IFaceInfo.TopEntities, FuncEntities); llvm::MutableArrayRef FuncEnts(FuncEntities.data(), FuncEntities.size()); for (auto Unit : CI.getMainModule()->getFiles()) { auto SF = dyn_cast(Unit); if (!SF) continue; FuncWalker Walker(CI.getSourceMgr(), *SF->getBufferID(), FuncEnts); SF->walk(Walker); } } static void reportSourceAnnotations(const SourceTextInfo &IFaceInfo, CompilerInstance &CI, DocInfoConsumer &Consumer) { for (auto Unit : CI.getMainModule()->getFiles()) { auto SF = dyn_cast(Unit); if (!SF) continue; SyntaxModelContext SyntaxContext(*SF); DocSyntaxWalker SyntaxWalker(CI.getSourceMgr(), *SF->getBufferID(), IFaceInfo.References, Consumer); SyntaxContext.walk(SyntaxWalker); SyntaxWalker.finished(); } } static bool getModuleInterfaceInfo(ASTContext &Ctx, StringRef ModuleName, SourceTextInfo &Info) { // Load standard library so that Clang importer can use it. auto *Stdlib = getModuleByFullName(Ctx, Ctx.StdlibModuleName); if (!Stdlib) return true; auto *M = getModuleByFullName(Ctx, ModuleName); if (!M) return true; PrintOptions Options = PrintOptions::printDocInterface(); ModuleTraversalOptions TraversalOptions = None; TraversalOptions |= ModuleTraversal::VisitSubmodules; TraversalOptions |= ModuleTraversal::VisitHidden; SmallString<128> Text; llvm::raw_svector_ostream OS(Text); AnnotatingPrinter Printer(OS); printModuleInterface(M, TraversalOptions, Printer, Options, false); Info.Text = OS.str(); Info.TopEntities = std::move(Printer.TopEntities); Info.References = std::move(Printer.References); return false; } static bool reportModuleDocInfo(CompilerInvocation Invocation, StringRef ModuleName, DocInfoConsumer &Consumer) { CompilerInstance CI; // Display diagnostics to stderr. PrintingDiagnosticConsumer PrintDiags; CI.addDiagnosticConsumer(&PrintDiags); if (CI.setup(Invocation)) return true; ASTContext &Ctx = CI.getASTContext(); // Setup a typechecker for protocol conformance resolving. OwnedResolver TypeResolver = createLazyResolver(Ctx); SourceTextInfo IFaceInfo; if (getModuleInterfaceInfo(Ctx, ModuleName, IFaceInfo)) return true; CompilerInstance ParseCI; if (makeParserAST(ParseCI, IFaceInfo.Text)) return true; addParameterEntities(ParseCI, IFaceInfo); Consumer.handleSourceText(IFaceInfo.Text); reportDocEntities(Ctx, IFaceInfo.TopEntities, Consumer); reportSourceAnnotations(IFaceInfo, ParseCI, Consumer); return false; } namespace { class SourceDocASTWalker : public SourceEntityWalker { public: SourceManager &SM; unsigned BufferID; std::vector TopEntities; std::vector EntitiesStack; std::vector References; SourceDocASTWalker(SourceManager &SM, unsigned BufferID) : SM(SM), BufferID(BufferID) {} ~SourceDocASTWalker() { assert(EntitiesStack.empty()); } bool walkToDeclPre(Decl *D, CharSourceRange Range) override { if (!isa(D) && !isa(D)) return true; if (isLocal(D)) return true; TextRange TR = getTextRange(D->getSourceRange()); unsigned LocOffset = getOffset(Range.getStart()); EntitiesStack.emplace_back(D, TR, LocOffset); return true; } bool walkToDeclPost(Decl *D) override { if (EntitiesStack.empty() || EntitiesStack.back().Dcl != D) return true; TextEntity Entity = std::move(EntitiesStack.back()); EntitiesStack.pop_back(); if (EntitiesStack.empty()) TopEntities.push_back(Entity); else EntitiesStack.back().SubEntities.push_back(Entity); return true; } bool visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef, Type Ty) override { unsigned StartOffset = getOffset(Range.getStart()); References.emplace_back(D, StartOffset, Range.getByteLength(), Ty); return true; } bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range, bool IsOpenBracket) override { // Treat both open and close brackets equally return visitDeclReference(D, Range, nullptr, Type()); } bool isLocal(Decl *D) const { return isa(D) || D->getDeclContext()->getLocalContext(); } unsigned getOffset(SourceLoc Loc) const { return SM.getLocOffsetInBuffer(Loc, BufferID); } TextRange getTextRange(SourceRange R) const { unsigned Start = getOffset(R.Start); unsigned End = getOffset(R.End); return TextRange{ Start, End-Start }; } }; } static bool getSourceTextInfo(CompilerInstance &CI, SourceTextInfo &Info) { SourceManager &SM = CI.getSourceMgr(); unsigned BufID = CI.getInputBufferIDs().back(); SourceDocASTWalker Walker(SM, BufID); Walker.walk(*CI.getMainModule()); CharSourceRange FullRange = SM.getRangeForBuffer(BufID); Info.Text = SM.extractText(FullRange); Info.TopEntities = std::move(Walker.TopEntities); Info.References = std::move(Walker.References); return false; } static bool reportSourceDocInfo(CompilerInvocation Invocation, llvm::MemoryBuffer *InputBuf, DocInfoConsumer &Consumer) { CompilerInstance CI; // Display diagnostics to stderr. PrintingDiagnosticConsumer PrintDiags; CI.addDiagnosticConsumer(&PrintDiags); EditorDiagConsumer DiagConsumer; CI.addDiagnosticConsumer(&DiagConsumer); Invocation.addInputBuffer(InputBuf); if (CI.setup(Invocation)) return true; DiagConsumer.setInputBufferIDs(CI.getInputBufferIDs()); ASTContext &Ctx = CI.getASTContext(); CloseClangModuleFiles scopedCloseFiles(*Ctx.getClangModuleLoader()); CI.performSema(); // Setup a typechecker for protocol conformance resolving. OwnedResolver TypeResolver = createLazyResolver(Ctx); SourceTextInfo SourceInfo; if (getSourceTextInfo(CI, SourceInfo)) return true; addParameterEntities(CI, SourceInfo); reportDocEntities(Ctx, SourceInfo.TopEntities, Consumer); reportSourceAnnotations(SourceInfo, CI, Consumer); for (auto &Diag : DiagConsumer.getDiagnosticsForBuffer( CI.getInputBufferIDs().back())) Consumer.handleDiagnostic(Diag); return false; } void SwiftLangSupport::getDocInfo(llvm::MemoryBuffer *InputBuf, StringRef ModuleName, ArrayRef Args, DocInfoConsumer &Consumer) { CompilerInstance CI; // Display diagnostics to stderr. PrintingDiagnosticConsumer PrintDiags; CI.addDiagnosticConsumer(&PrintDiags); CompilerInvocation Invocation; std::string Error; bool Failed = getASTManager().initCompilerInvocation(Invocation, Args, CI.getDiags(), StringRef(), Error); if (Failed) { Consumer.failed(Error); return; } Invocation.getClangImporterOptions().ImportForwardDeclarations = true; if (!ModuleName.empty()) { bool Error = reportModuleDocInfo(Invocation, ModuleName, Consumer); if (Error) Consumer.failed("Error occurred"); return; } Failed = reportSourceDocInfo(Invocation, InputBuf, Consumer); if (Failed) Consumer.failed("Error occurred"); } void SwiftLangSupport::findModuleGroups(StringRef ModuleName, ArrayRef Args, std::function, StringRef Error)> Receiver) { CompilerInvocation Invocation; Invocation.getClangImporterOptions().ImportForwardDeclarations = true; Invocation.clearInputs(); CompilerInstance CI; // Display diagnostics to stderr. PrintingDiagnosticConsumer PrintDiags; CI.addDiagnosticConsumer(&PrintDiags); std::vector Groups; std::string Error; if (getASTManager().initCompilerInvocation(Invocation, Args, CI.getDiags(), StringRef(), Error)) { Receiver(Groups, Error); return; } if (CI.setup(Invocation)) { Error = "Compiler invocation set up fails."; Receiver(Groups, Error); return; } ASTContext &Ctx = CI.getASTContext(); // Setup a typechecker for protocol conformance resolving. OwnedResolver TypeResolver = createLazyResolver(Ctx); // Load standard library so that Clang importer can use it. auto *Stdlib = getModuleByFullName(Ctx, Ctx.StdlibModuleName); if (!Stdlib) { Error = "Cannot load stdlib."; Receiver(Groups, Error); return; } auto *M = getModuleByFullName(Ctx, ModuleName); if (!M) { Error = "Cannot find the module."; Receiver(Groups, Error); return; } std::vector Scratch; Receiver(collectModuleGroups(M, Scratch), Error); }