mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[InterfacePrint] Enhance ASTPrinter to support type-specific interface printing.
When users try to print the interface of a specific type (most often through cursor infor query of SourceKit), we should simplify the original decls by replacing archetypes with instantiated types, hiding extension details, and omitting unfulfilled extension requirements. So the users can get the straight-to-the-point "type interface". This commit builds the testing infrastructure for this feature, and implements the first trick that wraps extension contents into the interface body. This commit also moves some generic testing support from SourceKit to Swift. Swift SVN r32630
This commit is contained in:
@@ -22,6 +22,7 @@ namespace swift {
|
||||
class Decl;
|
||||
class ModuleEntity;
|
||||
class TypeDecl;
|
||||
class Type;
|
||||
|
||||
/// Describes the context in which a name is being printed, which
|
||||
/// affects the keywords that need to be escaped.
|
||||
@@ -98,6 +99,8 @@ public:
|
||||
|
||||
/// To sanitize a malformatted utf8 string to a well-formatted one.
|
||||
static std::string sanitizeUtf8(StringRef Text);
|
||||
static bool printTypeInterface(Type Ty, std::string &Result);
|
||||
static bool printTypeInterface(Type Ty, llvm::raw_ostream &Out);
|
||||
|
||||
private:
|
||||
virtual void anchor();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
namespace swift {
|
||||
class GenericParamList;
|
||||
class CanType;
|
||||
class ExtensionDecl;
|
||||
enum DeclAttrKind : unsigned;
|
||||
|
||||
/// Options for printing AST nodes.
|
||||
@@ -156,6 +157,11 @@ struct PrintOptions {
|
||||
BothAlways,
|
||||
};
|
||||
|
||||
/// Whether to print the content of an extension decl inside the type decl where it
|
||||
/// extends from.
|
||||
std::function<bool(const ExtensionDecl *)> printExtensionContentAsMembers =
|
||||
[] (const ExtensionDecl *) { return false; };
|
||||
|
||||
/// How to print the keyword argument and parameter name in functions.
|
||||
ArgAndParamPrintingMode ArgAndParamPrinting =
|
||||
ArgAndParamPrintingMode::MatchSource;
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace swift {
|
||||
/// \brief This class manages and owns source buffers.
|
||||
class SourceManager {
|
||||
llvm::SourceMgr LLVMSourceMgr;
|
||||
|
||||
unsigned CodeCompletionBufferID = 0U;
|
||||
unsigned CodeCompletionOffset;
|
||||
|
||||
@@ -211,6 +210,16 @@ public:
|
||||
/// Verifies that all buffers are still valid.
|
||||
void verifyAllBuffers() const;
|
||||
|
||||
/// Translate line and column pair to the offset.
|
||||
llvm::Optional<unsigned> resolveFromLineCol(unsigned BufferId, unsigned Line,
|
||||
unsigned Col) const;
|
||||
|
||||
SourceLoc getLocForLineCol(unsigned BufferId, unsigned Line, unsigned Col) const {
|
||||
auto Offset = resolveFromLineCol(BufferId, Line, Col);
|
||||
return Offset.hasValue() ? getLocForOffset(BufferId, Offset.getValue()) :
|
||||
SourceLoc();
|
||||
}
|
||||
|
||||
private:
|
||||
const VirtualFile *getVirtualFile(SourceLoc Loc) const;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace swift {
|
||||
class TypeDecl;
|
||||
class Stmt;
|
||||
class Expr;
|
||||
class Type;
|
||||
|
||||
namespace ide {
|
||||
|
||||
@@ -87,7 +88,7 @@ public:
|
||||
/// \c ConstructorDecl, to point to the type declaration that the source
|
||||
/// refers to.
|
||||
virtual bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
|
||||
TypeDecl *CtorTyRef) { return true; }
|
||||
TypeDecl *CtorTyRef, Type T);
|
||||
|
||||
/// This method is called when a ValueDecl for a subscript is referenced in
|
||||
/// source. If it returns false, the remaining traversal is terminated
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#define SWIFT_IDE_UTILS_H
|
||||
|
||||
#include "swift/Basic/LLVM.h"
|
||||
#include "swift/AST/Module.h"
|
||||
#include "swift/AST/ASTPrinter.h"
|
||||
#include "swift/IDE/SourceEntityWalker.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -35,6 +38,13 @@ namespace swift {
|
||||
class ValueDecl;
|
||||
class ASTContext;
|
||||
class CompilerInvocation;
|
||||
class SourceFile;
|
||||
class TypeDecl;
|
||||
class SourceLoc;
|
||||
class Type;
|
||||
class Decl;
|
||||
class ClangNode;
|
||||
class ClangImporter;
|
||||
|
||||
namespace ide {
|
||||
struct SourceCompleteResult {
|
||||
@@ -101,7 +111,72 @@ std::unique_ptr<llvm::MemoryBuffer>
|
||||
replacePlaceholders(std::unique_ptr<llvm::MemoryBuffer> InputBuf,
|
||||
bool *HadPlaceholder = nullptr);
|
||||
|
||||
void getLocationInfo(const ValueDecl *VD,
|
||||
llvm::Optional<std::pair<unsigned, unsigned>> &DeclarationLoc,
|
||||
StringRef &Filename);
|
||||
|
||||
void getLocationInfoForClangNode(ClangNode ClangNode,
|
||||
ClangImporter *Importer,
|
||||
llvm::Optional<std::pair<unsigned, unsigned>> &DeclarationLoc,
|
||||
StringRef &Filename);
|
||||
|
||||
Optional<std::pair<unsigned, unsigned>> parseLineCol(StringRef LineCol);
|
||||
|
||||
class XMLEscapingPrinter : public StreamPrinter {
|
||||
public:
|
||||
XMLEscapingPrinter(raw_ostream &OS) : StreamPrinter(OS){};
|
||||
void printText(StringRef Text) override;
|
||||
void printXML(StringRef Text);
|
||||
};
|
||||
|
||||
struct SemaToken {
|
||||
ValueDecl *ValueD = nullptr;
|
||||
TypeDecl *CtorTyRef = nullptr;
|
||||
ModuleEntity Mod;
|
||||
SourceLoc Loc;
|
||||
bool IsRef = true;
|
||||
bool IsKeywordArgument = false;
|
||||
Type Ty;
|
||||
|
||||
SemaToken() = default;
|
||||
SemaToken(ValueDecl *ValueD, TypeDecl *CtorTyRef, SourceLoc Loc, bool IsRef,
|
||||
Type Ty) : ValueD(ValueD), CtorTyRef(CtorTyRef), Loc(Loc),
|
||||
IsRef(IsRef), Ty(Ty) { }
|
||||
SemaToken(ModuleEntity Mod, SourceLoc Loc) : Mod(Mod), Loc(Loc) { }
|
||||
|
||||
bool isValid() const { return ValueD != nullptr || Mod; }
|
||||
bool isInvalid() const { return !isValid(); }
|
||||
};
|
||||
|
||||
class SemaLocResolver : public SourceEntityWalker {
|
||||
SourceFile &SrcFile;
|
||||
SourceLoc LocToResolve;
|
||||
SemaToken SemaTok;
|
||||
|
||||
public:
|
||||
explicit SemaLocResolver(SourceFile &SrcFile) : SrcFile(SrcFile) { }
|
||||
SemaToken resolve(SourceLoc Loc);
|
||||
SourceManager &getSourceMgr() const;
|
||||
private:
|
||||
bool walkToDeclPre(Decl *D, CharSourceRange Range) override;
|
||||
bool walkToDeclPost(Decl *D) override;
|
||||
bool walkToStmtPre(Stmt *S) override;
|
||||
bool walkToStmtPost(Stmt *S) override;
|
||||
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
|
||||
TypeDecl *CtorTyRef, Type T) override;
|
||||
bool visitCallArgName(Identifier Name, CharSourceRange Range,
|
||||
ValueDecl *D) override;
|
||||
bool visitModuleReference(ModuleEntity Mod, CharSourceRange Range) override;
|
||||
bool rangeContainsLoc(SourceRange Range) const {
|
||||
return getSourceMgr().rangeContainsTokenLoc(Range, LocToResolve);
|
||||
}
|
||||
bool isDone() const { return SemaTok.isValid(); }
|
||||
bool tryResolve(ValueDecl *D, TypeDecl *CtorTyRef,
|
||||
SourceLoc Loc, bool IsRef, Type Ty = Type());
|
||||
bool tryResolve(ModuleEntity Mod, SourceLoc Loc);
|
||||
bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
|
||||
bool IsOpenBracket) override;
|
||||
};
|
||||
} // namespace ide
|
||||
} // namespace swift
|
||||
|
||||
|
||||
@@ -69,6 +69,27 @@ std::string ASTPrinter::sanitizeUtf8(StringRef Text) {
|
||||
return Builder.str();
|
||||
}
|
||||
|
||||
bool ASTPrinter::printTypeInterface(Type Ty, llvm::raw_ostream &OS) {
|
||||
if (!Ty)
|
||||
return false;
|
||||
PrintOptions Options = PrintOptions::printInterface();
|
||||
Options.printExtensionContentAsMembers = [](const ExtensionDecl *ED) {
|
||||
return true;
|
||||
};
|
||||
if (auto ND = Ty->getRValueType()->getNominalOrBoundGenericNominal()) {
|
||||
ND->print(OS, Options);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTPrinter::printTypeInterface(Type Ty, std::string &Buffer) {
|
||||
llvm::raw_string_ostream OS(Buffer);
|
||||
auto Result = printTypeInterface(Ty, OS);
|
||||
OS.str();
|
||||
return Result;
|
||||
}
|
||||
|
||||
void ASTPrinter::anchor() {}
|
||||
|
||||
void ASTPrinter::printIndent() {
|
||||
@@ -352,7 +373,8 @@ private:
|
||||
bool shouldPrintPattern(const Pattern *P);
|
||||
void printPatternType(const Pattern *P);
|
||||
void printAccessors(AbstractStorageDecl *ASD);
|
||||
void printMembers(DeclRange members, bool needComma = false);
|
||||
void printMembersOfDecl(Decl * NTD, bool needComma = false);
|
||||
void printMembers(ArrayRef<Decl *> members, bool needComma = false);
|
||||
void printNominalDeclName(NominalTypeDecl *decl);
|
||||
void printInherited(const Decl *decl,
|
||||
ArrayRef<TypeLoc> inherited,
|
||||
@@ -641,6 +663,11 @@ void PrintAST::printPatternType(const Pattern *P) {
|
||||
}
|
||||
|
||||
bool PrintAST::shouldPrint(const Decl *D) {
|
||||
if (auto *ED= dyn_cast<ExtensionDecl>(D)) {
|
||||
if (Options.printExtensionContentAsMembers(ED))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Options.SkipDeinit && isa<DestructorDecl>(D)) {
|
||||
return false;
|
||||
}
|
||||
@@ -885,7 +912,26 @@ void PrintAST::printAccessors(AbstractStorageDecl *ASD) {
|
||||
Printer << "}";
|
||||
}
|
||||
|
||||
void PrintAST::printMembers(DeclRange members, bool needComma) {
|
||||
void PrintAST::printMembersOfDecl(Decl *D, bool needComma) {
|
||||
llvm::SmallVector<Decl *, 3> Members;
|
||||
auto AddDeclFunc = [&](DeclRange Range) {
|
||||
for (auto RD : Range)
|
||||
Members.push_back(RD);
|
||||
};
|
||||
|
||||
if (auto Ext = dyn_cast<ExtensionDecl>(D)) {
|
||||
AddDeclFunc(Ext->getMembers());
|
||||
} else if (auto NTD = dyn_cast<NominalTypeDecl>(D)) {
|
||||
AddDeclFunc(NTD->getMembers());
|
||||
for (auto Ext : NTD->getExtensions()) {
|
||||
if (Options.printExtensionContentAsMembers(Ext))
|
||||
AddDeclFunc(Ext->getMembers());
|
||||
}
|
||||
}
|
||||
printMembers(Members, needComma);
|
||||
}
|
||||
|
||||
void PrintAST::printMembers(ArrayRef<Decl *> members, bool needComma) {
|
||||
Printer << " {";
|
||||
Printer.printNewline();
|
||||
{
|
||||
@@ -1146,7 +1192,7 @@ void PrintAST::visitExtensionDecl(ExtensionDecl *decl) {
|
||||
printWhereClause(GPs->getRequirements());
|
||||
}
|
||||
if (Options.TypeDefinitions) {
|
||||
printMembers(decl->getMembers());
|
||||
printMembersOfDecl(decl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1274,7 +1320,7 @@ void PrintAST::visitEnumDecl(EnumDecl *decl) {
|
||||
});
|
||||
printInherited(decl);
|
||||
if (Options.TypeDefinitions) {
|
||||
printMembers(decl->getMembers());
|
||||
printMembersOfDecl(decl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1290,7 +1336,7 @@ void PrintAST::visitStructDecl(StructDecl *decl) {
|
||||
});
|
||||
printInherited(decl);
|
||||
if (Options.TypeDefinitions) {
|
||||
printMembers(decl->getMembers());
|
||||
printMembersOfDecl(decl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1308,7 +1354,7 @@ void PrintAST::visitClassDecl(ClassDecl *decl) {
|
||||
printInherited(decl);
|
||||
|
||||
if (Options.TypeDefinitions) {
|
||||
printMembers(decl->getMembers());
|
||||
printMembersOfDecl(decl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1341,7 +1387,7 @@ void PrintAST::visitProtocolDecl(ProtocolDecl *decl) {
|
||||
|
||||
printInherited(decl, explicitClass);
|
||||
if (Options.TypeDefinitions) {
|
||||
printMembers(decl->getMembers());
|
||||
printMembersOfDecl(decl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -280,3 +280,36 @@ void CharSourceRange::print(raw_ostream &OS, const SourceManager &SM,
|
||||
void CharSourceRange::dump(const SourceManager &SM) const {
|
||||
print(llvm::errs(), SM);
|
||||
}
|
||||
|
||||
llvm::Optional<unsigned> SourceManager::resolveFromLineCol(unsigned BufferId,
|
||||
unsigned Line,
|
||||
unsigned Col) const {
|
||||
if (Line == 0 || Col == 0) {
|
||||
return None;
|
||||
}
|
||||
auto InputBuf = getLLVMSourceMgr().getMemoryBuffer(BufferId);
|
||||
const char *Ptr = InputBuf->getBufferStart();
|
||||
const char *End = InputBuf->getBufferEnd();
|
||||
const char *LineStart = Ptr;
|
||||
for (; Ptr < End; ++Ptr) {
|
||||
if (*Ptr == '\n') {
|
||||
--Line;
|
||||
if (Line == 0)
|
||||
break;
|
||||
LineStart = Ptr+1;
|
||||
}
|
||||
}
|
||||
if (Line != 0) {
|
||||
return None;
|
||||
}
|
||||
Ptr = LineStart;
|
||||
for (; Ptr < End; ++Ptr) {
|
||||
--Col;
|
||||
if (Col == 0)
|
||||
return Ptr - InputBuf->getBufferStart();
|
||||
if (*Ptr == '\n')
|
||||
break;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ add_swift_library(swiftIDE
|
||||
SourceEntityWalker.cpp
|
||||
SyntaxModel.cpp
|
||||
Utils.cpp
|
||||
SwiftSourceDocInfo.cpp
|
||||
LINK_LIBRARIES
|
||||
swiftFrontend
|
||||
swiftClangImporter
|
||||
|
||||
@@ -61,7 +61,7 @@ private:
|
||||
bool passModulePathElements(ArrayRef<ImportDecl::AccessPathElement> Path,
|
||||
const clang::Module *ClangMod);
|
||||
|
||||
bool passReference(ValueDecl *D, SourceLoc Loc);
|
||||
bool passReference(ValueDecl *D, Type Ty, SourceLoc Loc);
|
||||
bool passReference(ModuleEntity Mod, std::pair<Identifier, SourceLoc> IdLoc);
|
||||
|
||||
bool passSubscriptReference(ValueDecl *D, SourceLoc Loc, bool IsOpenBracket);
|
||||
@@ -200,14 +200,14 @@ std::pair<bool, Expr *> SemaAnnotator::walkToExprPre(Expr *E) {
|
||||
if (auto *module = dyn_cast<ModuleDecl>(DRE->getDecl())) {
|
||||
if (!passReference(ModuleEntity(module), std::make_pair(module->getName(), E->getLoc())))
|
||||
return { false, nullptr };
|
||||
} else if (!passReference(DRE->getDecl(), E->getLoc())) {
|
||||
} else if (!passReference(DRE->getDecl(), DRE->getType(), E->getLoc())) {
|
||||
return { false, nullptr };
|
||||
}
|
||||
} else if (MemberRefExpr *MRE = dyn_cast<MemberRefExpr>(E)) {
|
||||
// Visit in source order.
|
||||
if (!MRE->getBase()->walk(*this))
|
||||
return { false, nullptr };
|
||||
if (!passReference(MRE->getMember().getDecl(), E->getLoc()))
|
||||
if (!passReference(MRE->getMember().getDecl(), MRE->getType(), E->getLoc()))
|
||||
return { false, nullptr };
|
||||
|
||||
// We already visited the children.
|
||||
@@ -216,7 +216,8 @@ std::pair<bool, Expr *> SemaAnnotator::walkToExprPre(Expr *E) {
|
||||
return { false, E };
|
||||
|
||||
} else if (auto OtherCtorE = dyn_cast<OtherConstructorDeclRefExpr>(E)) {
|
||||
if (!passReference(OtherCtorE->getDecl(), OtherCtorE->getConstructorLoc()))
|
||||
if (!passReference(OtherCtorE->getDecl(), OtherCtorE->getType(),
|
||||
OtherCtorE->getConstructorLoc()))
|
||||
return { false, nullptr };
|
||||
|
||||
} else if (SubscriptExpr *SE = dyn_cast<SubscriptExpr>(E)) {
|
||||
@@ -280,7 +281,7 @@ bool SemaAnnotator::walkToTypeReprPre(TypeRepr *T) {
|
||||
return passReference(ModD, std::make_pair(IdT->getIdentifier(),
|
||||
IdT->getIdLoc()));
|
||||
|
||||
return passReference(VD, IdT->getIdLoc());
|
||||
return passReference(VD, Type(), IdT->getIdLoc());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -331,7 +332,7 @@ bool SemaAnnotator::handleImports(ImportDecl *Import) {
|
||||
|
||||
auto Decls = Import->getDecls();
|
||||
if (Decls.size() == 1) {
|
||||
if (!passReference(Decls.front(), Import->getEndLoc()))
|
||||
if (!passReference(Decls.front(), Type(), Import->getEndLoc()))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -363,7 +364,7 @@ bool SemaAnnotator::passSubscriptReference(ValueDecl *D, SourceLoc Loc,
|
||||
return Continue;
|
||||
}
|
||||
|
||||
bool SemaAnnotator::passReference(ValueDecl *D, SourceLoc Loc) {
|
||||
bool SemaAnnotator::passReference(ValueDecl *D, Type Ty, SourceLoc Loc) {
|
||||
unsigned NameLen = D->getName().getLength();
|
||||
TypeDecl *CtorTyRef = nullptr;
|
||||
|
||||
@@ -379,7 +380,7 @@ bool SemaAnnotator::passReference(ValueDecl *D, SourceLoc Loc) {
|
||||
|
||||
CharSourceRange Range = (Loc.isValid()) ? CharSourceRange(Loc, NameLen)
|
||||
: CharSourceRange();
|
||||
bool Continue = SEWalker.visitDeclReference(D, Range, CtorTyRef);
|
||||
bool Continue = SEWalker.visitDeclReference(D, Range, CtorTyRef, Ty);
|
||||
if (!Continue)
|
||||
Cancelled = true;
|
||||
return Continue;
|
||||
@@ -450,13 +451,18 @@ bool SourceEntityWalker::walk(DeclContext *DC) {
|
||||
return DC->walkContext(Annotator);
|
||||
}
|
||||
|
||||
bool SourceEntityWalker::visitDeclReference(ValueDecl *D, CharSourceRange Range,
|
||||
TypeDecl *CtorTyRef, Type T) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SourceEntityWalker::visitSubscriptReference(ValueDecl *D,
|
||||
CharSourceRange Range,
|
||||
bool IsOpenBracket) {
|
||||
// Most of the clients treat subscript reference the same way as a
|
||||
// regular reference when called on the open open bracket and
|
||||
// ignore the closing one.
|
||||
return IsOpenBracket ? visitDeclReference(D, Range, nullptr) : true;
|
||||
return IsOpenBracket ? visitDeclReference(D, Range, nullptr, Type()) : true;
|
||||
}
|
||||
|
||||
bool SourceEntityWalker::visitCallArgName(Identifier Name,
|
||||
|
||||
233
lib/IDE/SwiftSourceDocInfo.cpp
Normal file
233
lib/IDE/SwiftSourceDocInfo.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#include "swift/AST/ASTPrinter.h"
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/NameLookup.h"
|
||||
#include "swift/Basic/SourceManager.h"
|
||||
#include "swift/Frontend/Frontend.h"
|
||||
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
|
||||
#include "swift/IDE/SourceEntityWalker.h"
|
||||
#include "swift/IDE/CommentConversion.h"
|
||||
#include "swift/IDE/Utils.h"
|
||||
#include "swift/Markup/XMLUtils.h"
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/Basic/Module.h"
|
||||
#include "clang/Index/USRGeneration.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
using namespace swift;
|
||||
using namespace swift::ide;
|
||||
|
||||
Optional<std::pair<unsigned, unsigned>> swift::ide::parseLineCol(StringRef LineCol) {
|
||||
unsigned Line, Col;
|
||||
size_t ColonIdx = LineCol.find(':');
|
||||
if (ColonIdx == StringRef::npos) {
|
||||
llvm::errs() << "wrong pos format, it should be '<line>:<column'\n";
|
||||
return None;
|
||||
}
|
||||
if (LineCol.substr(0, ColonIdx).getAsInteger(10, Line)) {
|
||||
llvm::errs() << "wrong pos format, it should be '<line>:<column'\n";
|
||||
return None;
|
||||
}
|
||||
if (LineCol.substr(ColonIdx+1).getAsInteger(10, Col)) {
|
||||
llvm::errs() << "wrong pos format, it should be '<line>:<column'\n";
|
||||
return None;
|
||||
}
|
||||
|
||||
if (Line == 0 || Col == 0) {
|
||||
llvm::errs() << "wrong pos format, line/col should start from 1\n";
|
||||
return None;
|
||||
}
|
||||
|
||||
return std::make_pair(Line, Col);
|
||||
}
|
||||
|
||||
void XMLEscapingPrinter::printText(StringRef Text) {
|
||||
llvm::markup::appendWithXMLEscaping(OS, Text);
|
||||
}
|
||||
|
||||
void XMLEscapingPrinter::printXML(StringRef Text) {
|
||||
OS << Text;
|
||||
}
|
||||
|
||||
SourceManager &SemaLocResolver::getSourceMgr() const
|
||||
{
|
||||
return SrcFile.getASTContext().SourceMgr;
|
||||
}
|
||||
|
||||
bool SemaLocResolver::tryResolve(ValueDecl *D, TypeDecl *CtorTyRef,
|
||||
SourceLoc Loc, bool IsRef, Type Ty) {
|
||||
if (!D->hasName())
|
||||
return false;
|
||||
|
||||
if (Loc == LocToResolve) {
|
||||
SemaTok = { D, CtorTyRef, Loc, IsRef, Ty };
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SemaLocResolver::tryResolve(ModuleEntity Mod, SourceLoc Loc) {
|
||||
if (Loc == LocToResolve) {
|
||||
SemaTok = { Mod, Loc };
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SemaLocResolver::visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
|
||||
bool IsOpenBracket) {
|
||||
// We should treat both open and close brackets equally
|
||||
return visitDeclReference(D, Range, nullptr, Type());
|
||||
}
|
||||
|
||||
SemaToken SemaLocResolver::resolve(SourceLoc Loc) {
|
||||
assert(Loc.isValid());
|
||||
LocToResolve = Loc;
|
||||
SemaTok = SemaToken();
|
||||
walk(SrcFile);
|
||||
return SemaTok;
|
||||
}
|
||||
|
||||
bool SemaLocResolver::walkToDeclPre(Decl *D, CharSourceRange Range) {
|
||||
if (!rangeContainsLoc(D->getSourceRange()))
|
||||
return false;
|
||||
|
||||
if (isa<ExtensionDecl>(D))
|
||||
return true;
|
||||
|
||||
if (ValueDecl *VD = dyn_cast<ValueDecl>(D))
|
||||
return !tryResolve(VD, /*CtorTyRef=*/nullptr, Range.getStart(),
|
||||
/*IsRef=*/false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SemaLocResolver::walkToDeclPost(Decl *D) {
|
||||
if (isDone())
|
||||
return false;
|
||||
if (getSourceMgr().isBeforeInBuffer(LocToResolve, D->getStartLoc()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SemaLocResolver::walkToStmtPre(Stmt *S) {
|
||||
// FIXME: Even implicit Stmts should have proper ranges that include any
|
||||
// non-implicit Stmts (fix Stmts created for lazy vars).
|
||||
if (!S->isImplicit() && !rangeContainsLoc(S->getSourceRange()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SemaLocResolver::walkToStmtPost(Stmt *S) {
|
||||
if (isDone())
|
||||
return false;
|
||||
// FIXME: Even implicit Stmts should have proper ranges that include any
|
||||
// non-implicit Stmts (fix Stmts created for lazy vars).
|
||||
if (!S->isImplicit() && getSourceMgr().isBeforeInBuffer(LocToResolve,
|
||||
S->getStartLoc()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SemaLocResolver::visitDeclReference(ValueDecl *D, CharSourceRange Range,
|
||||
TypeDecl *CtorTyRef, Type T) {
|
||||
if (isDone())
|
||||
return false;
|
||||
return !tryResolve(D, CtorTyRef, Range.getStart(), /*IsRef=*/true, T);
|
||||
}
|
||||
|
||||
bool SemaLocResolver::visitCallArgName(Identifier Name, CharSourceRange Range,
|
||||
ValueDecl *D) {
|
||||
if (isDone())
|
||||
return false;
|
||||
bool Found = tryResolve(D, nullptr, Range.getStart(), /*IsRef=*/true);
|
||||
if (Found)
|
||||
SemaTok.IsKeywordArgument = true;
|
||||
return !Found;
|
||||
}
|
||||
|
||||
bool SemaLocResolver::visitModuleReference(ModuleEntity Mod,
|
||||
CharSourceRange Range) {
|
||||
if (isDone())
|
||||
return false;
|
||||
if (Mod.isBuiltinModule())
|
||||
return true; // Ignore.
|
||||
return !tryResolve(Mod, Range.getStart());
|
||||
}
|
||||
|
||||
void swift::ide::getLocationInfoForClangNode(ClangNode ClangNode,
|
||||
ClangImporter *Importer,
|
||||
llvm::Optional<std::pair<unsigned, unsigned>> &DeclarationLoc,
|
||||
StringRef &Filename) {
|
||||
clang::ASTContext &ClangCtx = Importer->getClangASTContext();
|
||||
clang::SourceManager &ClangSM = ClangCtx.getSourceManager();
|
||||
|
||||
clang::SourceRange SR = ClangNode.getLocation();
|
||||
if (auto MD = dyn_cast_or_null<clang::ObjCMethodDecl>(ClangNode.getAsDecl())) {
|
||||
SR = clang::SourceRange(MD->getSelectorStartLoc(),
|
||||
MD->getDeclaratorEndLoc());
|
||||
}
|
||||
|
||||
clang::CharSourceRange CharRange =
|
||||
clang::Lexer::makeFileCharRange(clang::CharSourceRange::getTokenRange(SR),
|
||||
ClangSM, ClangCtx.getLangOpts());
|
||||
if (CharRange.isInvalid())
|
||||
return;
|
||||
|
||||
std::pair<clang::FileID, unsigned>
|
||||
Decomp = ClangSM.getDecomposedLoc(CharRange.getBegin());
|
||||
if (!Decomp.first.isInvalid()) {
|
||||
if (auto FE = ClangSM.getFileEntryForID(Decomp.first)) {
|
||||
Filename = FE->getName();
|
||||
|
||||
std::pair<clang::FileID, unsigned>
|
||||
EndDecomp = ClangSM.getDecomposedLoc(CharRange.getEnd());
|
||||
|
||||
DeclarationLoc = { Decomp.second, EndDecomp.second-Decomp.second };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned getCharLength(SourceManager &SM, SourceRange TokenRange) {
|
||||
SourceLoc CharEndLoc = Lexer::getLocForEndOfToken(SM, TokenRange.End);
|
||||
return SM.getByteDistance(TokenRange.Start, CharEndLoc);
|
||||
}
|
||||
|
||||
void swift::ide::getLocationInfo(const ValueDecl *VD,
|
||||
llvm::Optional<std::pair<unsigned, unsigned>> &DeclarationLoc,
|
||||
StringRef &Filename) {
|
||||
ASTContext &Ctx = VD->getASTContext();
|
||||
SourceManager &SM = Ctx.SourceMgr;
|
||||
|
||||
auto ClangNode = VD->getClangNode();
|
||||
|
||||
if (VD->getLoc().isValid()) {
|
||||
unsigned NameLen;
|
||||
if (auto FD = dyn_cast<AbstractFunctionDecl>(VD)) {
|
||||
SourceRange R = FD->getSignatureSourceRange();
|
||||
if (R.isInvalid())
|
||||
return;
|
||||
NameLen = getCharLength(SM, R);
|
||||
} else {
|
||||
if (VD->hasName()) {
|
||||
NameLen = VD->getName().getLength();
|
||||
} else {
|
||||
NameLen = getCharLength(SM, VD->getLoc());
|
||||
}
|
||||
}
|
||||
|
||||
unsigned DeclBufID = SM.findBufferContainingLoc(VD->getLoc());
|
||||
DeclarationLoc = { SM.getLocOffsetInBuffer(VD->getLoc(), DeclBufID),
|
||||
NameLen };
|
||||
Filename = SM.getIdentifierForBuffer(DeclBufID);
|
||||
|
||||
} else if (ClangNode) {
|
||||
ClangImporter *Importer =
|
||||
static_cast<ClangImporter*>(Ctx.getClangModuleLoader());
|
||||
return getLocationInfoForClangNode(ClangNode, Importer,
|
||||
DeclarationLoc, Filename);
|
||||
}
|
||||
}
|
||||
24
test/IDE/print_type_interface.swift
Normal file
24
test/IDE/print_type_interface.swift
Normal file
@@ -0,0 +1,24 @@
|
||||
public struct A {
|
||||
public func fa() {}
|
||||
}
|
||||
extension A {
|
||||
public func fea1() {}
|
||||
}
|
||||
extension A {
|
||||
public func fea2() {}
|
||||
}
|
||||
class C1 {
|
||||
func f1() {
|
||||
var abcd : A
|
||||
abcd.fa()
|
||||
var intarr : [Int]
|
||||
intarr.append(1)
|
||||
}
|
||||
}
|
||||
|
||||
// RUN: %target-swift-ide-test -print-type-interface -pos=13:6 -source-filename %s | FileCheck %s -check-prefix=TYPE1
|
||||
// TYPE1: public struct A {
|
||||
// TYPE1-NEXT: public func fa()
|
||||
// TYPE1-NEXT: public func fea1()
|
||||
// TYPE1-NEXT: public func fea2()
|
||||
// TYPE1-NEXT: }
|
||||
@@ -86,6 +86,7 @@ enum class ActionType {
|
||||
PrintModuleImports,
|
||||
PrintUSRs,
|
||||
PrintLocalTypes,
|
||||
PrintTypeInterface,
|
||||
TestCreateCompilerInvocation,
|
||||
CompilerInvocationFromModule,
|
||||
GenerateModuleAPIDescription,
|
||||
@@ -192,6 +193,9 @@ Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None),
|
||||
clEnumValN(ActionType::DiffModuleAPI,
|
||||
"diff-module-api",
|
||||
"Compare machine-readable descriptions of module API"),
|
||||
clEnumValN(ActionType::PrintTypeInterface,
|
||||
"print-type-interface",
|
||||
"Print type-specific interface decl"),
|
||||
clEnumValEnd));
|
||||
|
||||
static llvm::cl::opt<std::string>
|
||||
@@ -458,6 +462,8 @@ static llvm::cl::list<std::string>
|
||||
HeaderToPrint("header-to-print",
|
||||
llvm::cl::desc("Header filename to print swift interface for"));
|
||||
|
||||
static llvm::cl::opt<std::string>
|
||||
LineColumnPair("pos", llvm::cl::desc("Line:Column pair"));
|
||||
} // namespace options
|
||||
|
||||
static std::unique_ptr<llvm::MemoryBuffer>
|
||||
@@ -1162,14 +1168,14 @@ private:
|
||||
}
|
||||
|
||||
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
|
||||
TypeDecl *CtorTyRef) override {
|
||||
TypeDecl *CtorTyRef, Type Ty) override {
|
||||
annotateSourceEntity({ Range, D, CtorTyRef, /*IsRef=*/true });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
|
||||
bool IsOpenBracket) override {
|
||||
return visitDeclReference(D, Range, nullptr);
|
||||
return visitDeclReference(D, Range, nullptr, Type());
|
||||
}
|
||||
|
||||
bool visitCallArgName(Identifier Name, CharSourceRange Range,
|
||||
@@ -2216,6 +2222,52 @@ static int doPrintModuleImports(const CompilerInvocation &InitInvok,
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================//
|
||||
// Print type interfaces.
|
||||
//============================================================================//
|
||||
static int doPrintTypeInterface(const CompilerInvocation &InitInvok,
|
||||
const StringRef FileName,
|
||||
const StringRef LCPair) {
|
||||
auto Pair = parseLineCol(LCPair);
|
||||
if (!Pair.hasValue())
|
||||
return 1;
|
||||
CompilerInvocation Invocation(InitInvok);
|
||||
Invocation.addInputFilename(FileName);
|
||||
CompilerInstance CI;
|
||||
if (CI.setup(Invocation))
|
||||
return 1;
|
||||
CI.performSema();
|
||||
SourceFile *SF = nullptr;
|
||||
unsigned BufID = CI.getInputBufferIDs().back();
|
||||
for (auto Unit : CI.getMainModule()->getFiles()) {
|
||||
SF = dyn_cast<SourceFile>(Unit);
|
||||
if (SF)
|
||||
break;
|
||||
}
|
||||
assert(SF && "no source file?");
|
||||
SemaLocResolver Resolver(*SF);
|
||||
SourceManager &SM = SF->getASTContext().SourceMgr;
|
||||
auto Offset = SM.resolveFromLineCol(BufID, Pair.getValue().first,
|
||||
Pair.getValue().second);
|
||||
if (!Offset.hasValue()) {
|
||||
llvm::errs() << "Cannot resolve source location.\n";
|
||||
return 1;
|
||||
}
|
||||
SourceLoc Loc = Lexer::getLocForStartOfToken(SM, BufID, Offset.getValue());
|
||||
auto SemaT = Resolver.resolve(Loc);
|
||||
if (SemaT.isInvalid()) {
|
||||
llvm::errs() << "Cannot find sema token at the given location.\n";
|
||||
return 1;
|
||||
}
|
||||
if (SemaT.Ty.isNull()) {
|
||||
llvm::errs() << "Cannot get type of the sema token.\n";
|
||||
return 1;
|
||||
}
|
||||
ASTPrinter::printTypeInterface(SemaT.Ty, llvm::outs());
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================//
|
||||
// Print USRs
|
||||
//============================================================================//
|
||||
@@ -2634,6 +2686,11 @@ int main(int argc, char *argv[]) {
|
||||
case ActionType::PrintUSRs:
|
||||
ExitCode = doPrintUSRs(InitInvok, options::SourceFilename);
|
||||
break;
|
||||
case ActionType::PrintTypeInterface:
|
||||
ExitCode = doPrintTypeInterface(InitInvok,
|
||||
options::SourceFilename,
|
||||
options::LineColumnPair);
|
||||
break;
|
||||
}
|
||||
|
||||
if (options::PrintStats)
|
||||
|
||||
Reference in New Issue
Block a user