[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:
Xi Ge
2015-10-12 19:14:58 +00:00
parent 5ac4fa67e6
commit 5ca83d97a5
13 changed files with 515 additions and 21 deletions

View File

@@ -22,6 +22,7 @@ namespace swift {
class Decl; class Decl;
class ModuleEntity; class ModuleEntity;
class TypeDecl; class TypeDecl;
class Type;
/// Describes the context in which a name is being printed, which /// Describes the context in which a name is being printed, which
/// affects the keywords that need to be escaped. /// affects the keywords that need to be escaped.
@@ -98,6 +99,8 @@ public:
/// To sanitize a malformatted utf8 string to a well-formatted one. /// To sanitize a malformatted utf8 string to a well-formatted one.
static std::string sanitizeUtf8(StringRef Text); static std::string sanitizeUtf8(StringRef Text);
static bool printTypeInterface(Type Ty, std::string &Result);
static bool printTypeInterface(Type Ty, llvm::raw_ostream &Out);
private: private:
virtual void anchor(); virtual void anchor();

View File

@@ -19,6 +19,7 @@
namespace swift { namespace swift {
class GenericParamList; class GenericParamList;
class CanType; class CanType;
class ExtensionDecl;
enum DeclAttrKind : unsigned; enum DeclAttrKind : unsigned;
/// Options for printing AST nodes. /// Options for printing AST nodes.
@@ -156,6 +157,11 @@ struct PrintOptions {
BothAlways, 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. /// How to print the keyword argument and parameter name in functions.
ArgAndParamPrintingMode ArgAndParamPrinting = ArgAndParamPrintingMode ArgAndParamPrinting =
ArgAndParamPrintingMode::MatchSource; ArgAndParamPrintingMode::MatchSource;

View File

@@ -679,4 +679,4 @@ public:
} // end namespace swift } // end namespace swift
#endif #endif

View File

@@ -24,7 +24,6 @@ namespace swift {
/// \brief This class manages and owns source buffers. /// \brief This class manages and owns source buffers.
class SourceManager { class SourceManager {
llvm::SourceMgr LLVMSourceMgr; llvm::SourceMgr LLVMSourceMgr;
unsigned CodeCompletionBufferID = 0U; unsigned CodeCompletionBufferID = 0U;
unsigned CodeCompletionOffset; unsigned CodeCompletionOffset;
@@ -211,6 +210,16 @@ public:
/// Verifies that all buffers are still valid. /// Verifies that all buffers are still valid.
void verifyAllBuffers() const; 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: private:
const VirtualFile *getVirtualFile(SourceLoc Loc) const; const VirtualFile *getVirtualFile(SourceLoc Loc) const;

View File

@@ -33,6 +33,7 @@ namespace swift {
class TypeDecl; class TypeDecl;
class Stmt; class Stmt;
class Expr; class Expr;
class Type;
namespace ide { namespace ide {
@@ -87,7 +88,7 @@ public:
/// \c ConstructorDecl, to point to the type declaration that the source /// \c ConstructorDecl, to point to the type declaration that the source
/// refers to. /// refers to.
virtual bool visitDeclReference(ValueDecl *D, CharSourceRange Range, 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 /// This method is called when a ValueDecl for a subscript is referenced in
/// source. If it returns false, the remaining traversal is terminated /// source. If it returns false, the remaining traversal is terminated

View File

@@ -14,6 +14,9 @@
#define SWIFT_IDE_UTILS_H #define SWIFT_IDE_UTILS_H
#include "swift/Basic/LLVM.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 "llvm/ADT/StringRef.h"
#include <memory> #include <memory>
#include <string> #include <string>
@@ -35,6 +38,13 @@ namespace swift {
class ValueDecl; class ValueDecl;
class ASTContext; class ASTContext;
class CompilerInvocation; class CompilerInvocation;
class SourceFile;
class TypeDecl;
class SourceLoc;
class Type;
class Decl;
class ClangNode;
class ClangImporter;
namespace ide { namespace ide {
struct SourceCompleteResult { struct SourceCompleteResult {
@@ -101,7 +111,72 @@ std::unique_ptr<llvm::MemoryBuffer>
replacePlaceholders(std::unique_ptr<llvm::MemoryBuffer> InputBuf, replacePlaceholders(std::unique_ptr<llvm::MemoryBuffer> InputBuf,
bool *HadPlaceholder = nullptr); 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 ide
} // namespace swift } // namespace swift

View File

@@ -69,6 +69,27 @@ std::string ASTPrinter::sanitizeUtf8(StringRef Text) {
return Builder.str(); 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::anchor() {}
void ASTPrinter::printIndent() { void ASTPrinter::printIndent() {
@@ -352,7 +373,8 @@ private:
bool shouldPrintPattern(const Pattern *P); bool shouldPrintPattern(const Pattern *P);
void printPatternType(const Pattern *P); void printPatternType(const Pattern *P);
void printAccessors(AbstractStorageDecl *ASD); 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 printNominalDeclName(NominalTypeDecl *decl);
void printInherited(const Decl *decl, void printInherited(const Decl *decl,
ArrayRef<TypeLoc> inherited, ArrayRef<TypeLoc> inherited,
@@ -641,6 +663,11 @@ void PrintAST::printPatternType(const Pattern *P) {
} }
bool PrintAST::shouldPrint(const Decl *D) { 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)) { if (Options.SkipDeinit && isa<DestructorDecl>(D)) {
return false; return false;
} }
@@ -885,7 +912,26 @@ void PrintAST::printAccessors(AbstractStorageDecl *ASD) {
Printer << "}"; 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 << " {";
Printer.printNewline(); Printer.printNewline();
{ {
@@ -1146,7 +1192,7 @@ void PrintAST::visitExtensionDecl(ExtensionDecl *decl) {
printWhereClause(GPs->getRequirements()); printWhereClause(GPs->getRequirements());
} }
if (Options.TypeDefinitions) { if (Options.TypeDefinitions) {
printMembers(decl->getMembers()); printMembersOfDecl(decl);
} }
} }
@@ -1274,7 +1320,7 @@ void PrintAST::visitEnumDecl(EnumDecl *decl) {
}); });
printInherited(decl); printInherited(decl);
if (Options.TypeDefinitions) { if (Options.TypeDefinitions) {
printMembers(decl->getMembers()); printMembersOfDecl(decl);
} }
} }
@@ -1290,7 +1336,7 @@ void PrintAST::visitStructDecl(StructDecl *decl) {
}); });
printInherited(decl); printInherited(decl);
if (Options.TypeDefinitions) { if (Options.TypeDefinitions) {
printMembers(decl->getMembers()); printMembersOfDecl(decl);
} }
} }
@@ -1308,7 +1354,7 @@ void PrintAST::visitClassDecl(ClassDecl *decl) {
printInherited(decl); printInherited(decl);
if (Options.TypeDefinitions) { if (Options.TypeDefinitions) {
printMembers(decl->getMembers()); printMembersOfDecl(decl);
} }
} }
@@ -1341,7 +1387,7 @@ void PrintAST::visitProtocolDecl(ProtocolDecl *decl) {
printInherited(decl, explicitClass); printInherited(decl, explicitClass);
if (Options.TypeDefinitions) { if (Options.TypeDefinitions) {
printMembers(decl->getMembers()); printMembersOfDecl(decl);
} }
} }

View File

@@ -280,3 +280,36 @@ void CharSourceRange::print(raw_ostream &OS, const SourceManager &SM,
void CharSourceRange::dump(const SourceManager &SM) const { void CharSourceRange::dump(const SourceManager &SM) const {
print(llvm::errs(), SM); 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;
}

View File

@@ -7,6 +7,7 @@ add_swift_library(swiftIDE
SourceEntityWalker.cpp SourceEntityWalker.cpp
SyntaxModel.cpp SyntaxModel.cpp
Utils.cpp Utils.cpp
SwiftSourceDocInfo.cpp
LINK_LIBRARIES LINK_LIBRARIES
swiftFrontend swiftFrontend
swiftClangImporter swiftClangImporter

View File

@@ -61,7 +61,7 @@ private:
bool passModulePathElements(ArrayRef<ImportDecl::AccessPathElement> Path, bool passModulePathElements(ArrayRef<ImportDecl::AccessPathElement> Path,
const clang::Module *ClangMod); 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 passReference(ModuleEntity Mod, std::pair<Identifier, SourceLoc> IdLoc);
bool passSubscriptReference(ValueDecl *D, SourceLoc Loc, bool IsOpenBracket); 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 (auto *module = dyn_cast<ModuleDecl>(DRE->getDecl())) {
if (!passReference(ModuleEntity(module), std::make_pair(module->getName(), E->getLoc()))) if (!passReference(ModuleEntity(module), std::make_pair(module->getName(), E->getLoc())))
return { false, nullptr }; return { false, nullptr };
} else if (!passReference(DRE->getDecl(), E->getLoc())) { } else if (!passReference(DRE->getDecl(), DRE->getType(), E->getLoc())) {
return { false, nullptr }; return { false, nullptr };
} }
} else if (MemberRefExpr *MRE = dyn_cast<MemberRefExpr>(E)) { } else if (MemberRefExpr *MRE = dyn_cast<MemberRefExpr>(E)) {
// Visit in source order. // Visit in source order.
if (!MRE->getBase()->walk(*this)) if (!MRE->getBase()->walk(*this))
return { false, nullptr }; return { false, nullptr };
if (!passReference(MRE->getMember().getDecl(), E->getLoc())) if (!passReference(MRE->getMember().getDecl(), MRE->getType(), E->getLoc()))
return { false, nullptr }; return { false, nullptr };
// We already visited the children. // We already visited the children.
@@ -216,7 +216,8 @@ std::pair<bool, Expr *> SemaAnnotator::walkToExprPre(Expr *E) {
return { false, E }; return { false, E };
} else if (auto OtherCtorE = dyn_cast<OtherConstructorDeclRefExpr>(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 }; return { false, nullptr };
} else if (SubscriptExpr *SE = dyn_cast<SubscriptExpr>(E)) { } 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(), return passReference(ModD, std::make_pair(IdT->getIdentifier(),
IdT->getIdLoc())); IdT->getIdLoc()));
return passReference(VD, IdT->getIdLoc()); return passReference(VD, Type(), IdT->getIdLoc());
} }
} }
return true; return true;
@@ -331,7 +332,7 @@ bool SemaAnnotator::handleImports(ImportDecl *Import) {
auto Decls = Import->getDecls(); auto Decls = Import->getDecls();
if (Decls.size() == 1) { if (Decls.size() == 1) {
if (!passReference(Decls.front(), Import->getEndLoc())) if (!passReference(Decls.front(), Type(), Import->getEndLoc()))
return false; return false;
} }
@@ -363,7 +364,7 @@ bool SemaAnnotator::passSubscriptReference(ValueDecl *D, SourceLoc Loc,
return Continue; return Continue;
} }
bool SemaAnnotator::passReference(ValueDecl *D, SourceLoc Loc) { bool SemaAnnotator::passReference(ValueDecl *D, Type Ty, SourceLoc Loc) {
unsigned NameLen = D->getName().getLength(); unsigned NameLen = D->getName().getLength();
TypeDecl *CtorTyRef = nullptr; TypeDecl *CtorTyRef = nullptr;
@@ -379,7 +380,7 @@ bool SemaAnnotator::passReference(ValueDecl *D, SourceLoc Loc) {
CharSourceRange Range = (Loc.isValid()) ? CharSourceRange(Loc, NameLen) CharSourceRange Range = (Loc.isValid()) ? CharSourceRange(Loc, NameLen)
: CharSourceRange(); : CharSourceRange();
bool Continue = SEWalker.visitDeclReference(D, Range, CtorTyRef); bool Continue = SEWalker.visitDeclReference(D, Range, CtorTyRef, Ty);
if (!Continue) if (!Continue)
Cancelled = true; Cancelled = true;
return Continue; return Continue;
@@ -450,13 +451,18 @@ bool SourceEntityWalker::walk(DeclContext *DC) {
return DC->walkContext(Annotator); return DC->walkContext(Annotator);
} }
bool SourceEntityWalker::visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, Type T) {
return true;
}
bool SourceEntityWalker::visitSubscriptReference(ValueDecl *D, bool SourceEntityWalker::visitSubscriptReference(ValueDecl *D,
CharSourceRange Range, CharSourceRange Range,
bool IsOpenBracket) { bool IsOpenBracket) {
// Most of the clients treat subscript reference the same way as a // Most of the clients treat subscript reference the same way as a
// regular reference when called on the open open bracket and // regular reference when called on the open open bracket and
// ignore the closing one. // ignore the closing one.
return IsOpenBracket ? visitDeclReference(D, Range, nullptr) : true; return IsOpenBracket ? visitDeclReference(D, Range, nullptr, Type()) : true;
} }
bool SourceEntityWalker::visitCallArgName(Identifier Name, bool SourceEntityWalker::visitCallArgName(Identifier Name,

View 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);
}
}

View 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: }

View File

@@ -86,6 +86,7 @@ enum class ActionType {
PrintModuleImports, PrintModuleImports,
PrintUSRs, PrintUSRs,
PrintLocalTypes, PrintLocalTypes,
PrintTypeInterface,
TestCreateCompilerInvocation, TestCreateCompilerInvocation,
CompilerInvocationFromModule, CompilerInvocationFromModule,
GenerateModuleAPIDescription, GenerateModuleAPIDescription,
@@ -192,6 +193,9 @@ Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None),
clEnumValN(ActionType::DiffModuleAPI, clEnumValN(ActionType::DiffModuleAPI,
"diff-module-api", "diff-module-api",
"Compare machine-readable descriptions of module API"), "Compare machine-readable descriptions of module API"),
clEnumValN(ActionType::PrintTypeInterface,
"print-type-interface",
"Print type-specific interface decl"),
clEnumValEnd)); clEnumValEnd));
static llvm::cl::opt<std::string> static llvm::cl::opt<std::string>
@@ -458,6 +462,8 @@ static llvm::cl::list<std::string>
HeaderToPrint("header-to-print", HeaderToPrint("header-to-print",
llvm::cl::desc("Header filename to print swift interface for")); 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 } // namespace options
static std::unique_ptr<llvm::MemoryBuffer> static std::unique_ptr<llvm::MemoryBuffer>
@@ -1162,14 +1168,14 @@ private:
} }
bool visitDeclReference(ValueDecl *D, CharSourceRange Range, bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef) override { TypeDecl *CtorTyRef, Type Ty) override {
annotateSourceEntity({ Range, D, CtorTyRef, /*IsRef=*/true }); annotateSourceEntity({ Range, D, CtorTyRef, /*IsRef=*/true });
return true; return true;
} }
bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range, bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
bool IsOpenBracket) override { bool IsOpenBracket) override {
return visitDeclReference(D, Range, nullptr); return visitDeclReference(D, Range, nullptr, Type());
} }
bool visitCallArgName(Identifier Name, CharSourceRange Range, bool visitCallArgName(Identifier Name, CharSourceRange Range,
@@ -2216,6 +2222,52 @@ static int doPrintModuleImports(const CompilerInvocation &InitInvok,
return ExitCode; 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 // Print USRs
//============================================================================// //============================================================================//
@@ -2634,6 +2686,11 @@ int main(int argc, char *argv[]) {
case ActionType::PrintUSRs: case ActionType::PrintUSRs:
ExitCode = doPrintUSRs(InitInvok, options::SourceFilename); ExitCode = doPrintUSRs(InitInvok, options::SourceFilename);
break; break;
case ActionType::PrintTypeInterface:
ExitCode = doPrintTypeInterface(InitInvok,
options::SourceFilename,
options::LineColumnPair);
break;
} }
if (options::PrintStats) if (options::PrintStats)