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 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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -679,4 +679,4 @@ public:
|
|||||||
|
|
||||||
} // end namespace swift
|
} // end namespace swift
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
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,
|
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user