blob: b3e94d9d22be1f9e96462cbe97de3136283daeed [file] [log] [blame] [edit]
//===--- swift-ide-test.cpp - IDE functionality testing application -------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "XMLValidator.h"
#include "ModuleAPIDiff.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTDemangler.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/Comment.h"
#include "swift/AST/DebuggerClient.h"
#include "swift/AST/DiagnosticConsumer.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/ImportCache.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/PrintOptions.h"
#include "swift/AST/RawComment.h"
#include "swift/AST/USRGeneration.h"
#include "swift/Demangling/Demangle.h"
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/PrimitiveParsing.h"
#include "swift/Basic/LLVMInitialize.h"
#include "swift/Driver/FrontendUtil.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/CompletionInstance.h"
#include "swift/IDE/CodeCompletion.h"
#include "swift/IDE/CommentConversion.h"
#include "swift/IDE/ConformingMethodList.h"
#include "swift/IDE/ModuleInterfacePrinting.h"
#include "swift/IDE/REPLCodeCompletion.h"
#include "swift/IDE/SourceEntityWalker.h"
#include "swift/IDE/SyntaxModel.h"
#include "swift/IDE/TypeContextInfo.h"
#include "swift/IDE/Utils.h"
#include "swift/IDE/IDERequests.h"
#include "swift/Index/Index.h"
#include "swift/Sema/IDETypeChecking.h"
#include "swift/SyntaxParse/SyntaxTreeCreator.h"
#include "swift/Markup/Markup.h"
#include "swift/Config.h"
#include "clang/Rewrite/Core/RewriteBuffer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ManagedStatic.h"
#include <system_error>
#include <random>
#include <string>
#include <chrono>
using namespace swift;
using namespace ide;
using namespace index;
namespace {
enum class ActionType {
None,
BatchCodeCompletion,
CodeCompletion,
REPLCodeCompletion,
DumpCompletionCache,
DumpImporterLookupTable,
SyntaxColoring,
DumpComments,
Structure,
Annotation,
TestInputCompleteness,
PrintASTNotTypeChecked,
PrintASTTypeChecked,
PrintModule,
PrintModuleMetadata,
PrintHeader,
PrintSwiftFileInterface,
PrintDecl,
PrintTypes,
PrintComments,
PrintModuleComments,
PrintModuleImports,
PrintModuleGroups,
PrintUSRs,
PrintLocalTypes,
PrintTypeInterface,
PrintIndexedSymbols,
PrintExpressionTypes,
TestCreateCompilerInvocation,
CompilerInvocationFromModule,
GenerateModuleAPIDescription,
DiffModuleAPI,
ReconstructType,
Range,
TypeContextInfo,
ConformingMethodList,
};
class NullDebuggerClient : public DebuggerClient {
public:
using DebuggerClient::DebuggerClient;
bool shouldGlobalize(Identifier Name, DeclKind Kind) override {
return false;
}
void didGlobalize(Decl *D) override {}
bool lookupOverrides(DeclBaseName Name, DeclContext *DC,
SourceLoc Loc, bool IsTypeLookup,
ResultVector &RV) override {
return false;
}
bool lookupAdditions(DeclBaseName Name, DeclContext *DC,
SourceLoc Loc, bool IsTypeLookup,
ResultVector &RV) override {
return false;
}
SILDebuggerClient *getAsSILDebuggerClient() override {
return nullptr;
}
};
class PrivateDiscriminatorPreferenceClient : public NullDebuggerClient {
Identifier Discriminator;
public:
PrivateDiscriminatorPreferenceClient(ASTContext &C,
StringRef DiscriminatorStr)
: NullDebuggerClient(C),
Discriminator(C.getIdentifier(DiscriminatorStr)) {}
Identifier getPreferredPrivateDiscriminator() override {
return Discriminator;
}
};
} // end anonymous namespace
namespace options {
static llvm::cl::OptionCategory Category("swift-ide-test Options");
static llvm::cl::opt<ActionType>
Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None),
llvm::cl::cat(Category),
llvm::cl::values(
clEnumValN(ActionType::BatchCodeCompletion,
"batch-code-completion", "Perform code completion in batch mode"),
clEnumValN(ActionType::CodeCompletion,
"code-completion", "Perform code completion"),
clEnumValN(ActionType::REPLCodeCompletion,
"repl-code-completion", "Perform REPL-style code completion"),
clEnumValN(ActionType::DumpCompletionCache,
"dump-completion-cache", "Dump a code completion cache file"),
clEnumValN(ActionType::DumpImporterLookupTable,
"dump-importer-lookup-table", "Dump the Clang importer's lookup tables"),
clEnumValN(ActionType::SyntaxColoring,
"syntax-coloring", "Perform syntax coloring"),
clEnumValN(ActionType::DumpComments,
"dump-comments", "Dump documentation comments attached to decls"),
clEnumValN(ActionType::Structure,
"structure", "Perform document structure annotation"),
clEnumValN(ActionType::Annotation,
"annotate", "Perform semantic annotation"),
clEnumValN(ActionType::TestInputCompleteness,
"test-input-complete", "Check if input source is complete"),
clEnumValN(ActionType::PrintASTNotTypeChecked,
"print-ast-not-typechecked", "Print the non-typechecked AST"),
clEnumValN(ActionType::PrintASTTypeChecked,
"print-ast-typechecked", "Print the typechecked AST"),
clEnumValN(ActionType::PrintModule,
"print-module", "Print visible declarations in a module"),
clEnumValN(ActionType::PrintModuleMetadata,
"print-module-metadata", "Print meta-data in a module"),
clEnumValN(ActionType::PrintHeader,
"print-header", "Print visible declarations in a header file"),
clEnumValN(ActionType::PrintSwiftFileInterface,
"print-swift-file-interface", "Print interface of a swift file"),
clEnumValN(ActionType::PrintDecl,
"print-decl", "Print interface of a decl"),
clEnumValN(ActionType::PrintTypes,
"print-types", "Print types of all subexpressions and declarations in the AST"),
clEnumValN(ActionType::PrintComments,
"print-comments", "Print documentation comments attached to decls"),
clEnumValN(ActionType::PrintModuleComments,
"print-module-comments", "Given a module, print documentation comments attached to decls"),
clEnumValN(ActionType::PrintModuleImports,
"print-module-imports", "Recursively print all imports visible from a particular module"),
clEnumValN(ActionType::PrintUSRs,
"print-usrs", "Print USRs for all decls"),
clEnumValN(ActionType::PrintLocalTypes,
"print-local-types", "Print local types and remanglings in a module"),
clEnumValN(ActionType::TestCreateCompilerInvocation,
"test-createCompilerInvocation",
"Test swift::driver::createCompilerInvocation using the "
"arguments passed to swift-ide-test (must be specified "
"before all other arguments)"),
clEnumValN(ActionType::CompilerInvocationFromModule,
"test-CompilerInvocation-from-module",
"Test CompilerInvocation::loadFromSerializedAST on the "
"\"source\" file"),
clEnumValN(ActionType::GenerateModuleAPIDescription,
"generate-module-api-description",
"Generate a machine-readable description of module API"),
clEnumValN(ActionType::DiffModuleAPI,
"diff-module-api",
"Compare machine-readable descriptions of module API"),
clEnumValN(ActionType::PrintTypeInterface,
"print-type-interface",
"Print type-specific interface decl"),
clEnumValN(ActionType::ReconstructType,
"reconstruct-type",
"Reconstruct type from mangled name"),
clEnumValN(ActionType::PrintModuleGroups,
"print-module-groups",
"Print group names in a module"),
clEnumValN(ActionType::Range,
"range",
"Print information about a given range"),
clEnumValN(ActionType::PrintIndexedSymbols,
"print-indexed-symbols",
"Print indexed symbol information"),
clEnumValN(ActionType::TypeContextInfo,
"type-context-info",
"Perform expression context info analysis"),
clEnumValN(ActionType::PrintExpressionTypes,
"print-expr-type",
"Print types for all expressions in the file"),
clEnumValN(ActionType::ConformingMethodList,
"conforming-methods",
"Perform conforming method analysis for expression")));
static llvm::cl::opt<std::string>
SourceFilename("source-filename", llvm::cl::desc("Name of the source file"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
SecondSourceFilename("second-source-filename",
llvm::cl::desc("Name of the second source file"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
InputFilenames(llvm::cl::Positional, llvm::cl::desc("[input files...]"),
llvm::cl::ZeroOrMore, llvm::cl::cat(Category));
static llvm::cl::list<std::string>
BuildConfigs("D", llvm::cl::desc("Conditional compilation flags"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
SDK("sdk", llvm::cl::desc("path to the SDK to build against"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
Triple("target", llvm::cl::desc("target triple"), llvm::cl::cat(Category));
static llvm::cl::list<std::string>
SwiftVersion("swift-version", llvm::cl::desc("Swift version"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
ModuleCachePath("module-cache-path", llvm::cl::desc("Clang module cache path"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
PCHOutputDir("pch-output-dir",
llvm::cl::desc("place autogenerated PCH files in this directory"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
CompletionCachePath("completion-cache-path",
llvm::cl::desc("Code completion cache path"),
llvm::cl::cat(Category),
llvm::cl::ZeroOrMore);
static llvm::cl::list<std::string>
ImportPaths("I", llvm::cl::desc("add a directory to the import search path"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
FrameworkPaths("F",
llvm::cl::desc("add a directory to the framework search path"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
SystemFrameworkPaths("iframework",
llvm::cl::desc("add a directory to the system framework search path"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
ResourceDir("resource-dir",
llvm::cl::desc("The directory that holds the compiler resource files"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
ImportObjCHeader("import-objc-header",
llvm::cl::desc("header to implicitly import"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
EnableSourceImport("enable-source-import", llvm::cl::Hidden,
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<bool>
EnableCrossImportOverlays("enable-cross-import-overlays",
llvm::cl::desc("Automatically import declared cross-import overlays."),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
SkipDeinit("skip-deinit",
llvm::cl::desc("Whether to skip printing destructors"),
llvm::cl::cat(Category),
llvm::cl::init(true));
static llvm::cl::opt<bool>
SkipImports("skip-imports",
llvm::cl::desc("Whether to skip printing import declarations"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
SkipOverrides("skip-overrides",
llvm::cl::desc("Whether to skip printing overrides/witnesses"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
SkipParameterNames("skip-parameter-names",
llvm::cl::desc("Whether to skip parameter names"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
AlwaysArgumentLabels("always-argument-labels",
llvm::cl::desc("Whether to always print separate argument labels"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
DisableAccessControl("disable-access-control",
llvm::cl::desc("Disables access control, like a debugger"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool> CodeCompleteInitsInPostfixExpr(
"code-complete-inits-in-postfix-expr",
llvm::cl::desc(
"Include initializers when completing a postfix expression"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool> CodeCompleteCallPatternHeuristics(
"code-complete-call-pattern-heuristics",
llvm::cl::desc(
"Use heuristics to guess whether we want call pattern completions"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
ObjCForwardDeclarations("enable-objc-forward-declarations",
llvm::cl::desc("Import Objective-C forward declarations when possible"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
InferImportAsMember("enable-infer-import-as-member",
llvm::cl::desc("Infer when a global could be imported as a member"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
EnableSwift3ObjCInference("enable-swift3-objc-inference",
llvm::cl::desc("Enable Swift 3's @objc inference rules"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
DisableObjCAttrRequiresFoundationModule(
"disable-objc-attr-requires-foundation-module",
llvm::cl::desc("Allow @objc to be used freely"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool> EnableExperimentalPrespecialization(
"enable-experimental-prespecialization",
llvm::cl::desc("Enable experimental prespecialization"),
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<bool>
PrintStats("print-stats",
llvm::cl::desc("Print statistics"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<std::string>
DebugForbidTypecheckPrefix("debug-forbid-typecheck-prefix",
llvm::cl::desc("Triggers llvm fatal_error if typechecker tries to typecheck "
"a decl with the provided prefix name"),
llvm::cl::cat(Category));
// '-batch-code-completion' options.
static llvm::cl::opt<uint64_t>
RandomSeed("random-seed", llvm::cl::value_desc("seed"),
llvm::cl::desc("Seed for the random number generator"),
llvm::cl::cat(Category),
llvm::cl::init(0));
static llvm::cl::opt<std::string>
CompletionOutputDir("completion-output-dir", llvm::cl::value_desc("path"),
llvm::cl::desc("Directory for completion output"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
FileCheckPath("filecheck", llvm::cl::value_desc("path"),
llvm::cl::desc("Path to 'FileCheck' utility"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
SkipFileCheck("skip-filecheck", llvm::cl::desc("Skip 'FileCheck' checking"),
llvm::cl::cat(Category));
// '-code-completion' options.
static llvm::cl::opt<std::string>
CodeCompletionToken("code-completion-token",
llvm::cl::desc("Code completion token name"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
CodeCompletionDiagnostics("code-completion-diagnostics",
llvm::cl::desc("Print compiler diagnostics while "
"doing code completion"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
CodeCompletionKeywords("code-completion-keywords",
llvm::cl::desc("Include keywords in code completion results"),
llvm::cl::cat(Category),
llvm::cl::init(true));
static llvm::cl::opt<bool>
CodeCompletionComments("code-completion-comments",
llvm::cl::desc("Include comments in code completion results"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
CodeCOmpletionAnnotateResults("code-completion-annotate-results",
llvm::cl::desc("annotate completion results with XML"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<std::string>
DebugClientDiscriminator("debug-client-discriminator",
llvm::cl::desc("A discriminator to prefer in lookups"),
llvm::cl::cat(Category));
// '-conforming-methods' options.
static llvm::cl::list<std::string>
ConformingMethodListExpectedTypes("conforming-methods-expected-types",
llvm::cl::desc("Set expected types for comforming method list"),
llvm::cl::cat(Category));
// '-syntax-coloring' options.
static llvm::cl::opt<bool>
TerminalOutput("terminal",
llvm::cl::desc("Use terminal color for source annotations"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
Typecheck("typecheck",
llvm::cl::desc("Type check the AST"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
Playground("playground",
llvm::cl::desc("Whether coloring in playground"),
llvm::cl::cat(Category),
llvm::cl::init(false));
// AST printing options.
static llvm::cl::opt<bool>
FunctionDefinitions("function-definitions",
llvm::cl::desc("Print function bodies"),
llvm::cl::cat(Category),
llvm::cl::init(true));
static llvm::cl::opt<bool>
AbstractAccessors("abstract-accessors",
llvm::cl::desc("Hide the concrete accessors used to "
"implement a property or subscript"),
llvm::cl::cat(Category),
llvm::cl::init(true));
static llvm::cl::opt<bool>
PreferTypeRepr("prefer-type-repr",
llvm::cl::desc("When printing types, prefer printing TypeReprs"),
llvm::cl::cat(Category),
llvm::cl::init(true));
static llvm::cl::opt<bool>
FullyQualifiedTypes("fully-qualified-types",
llvm::cl::desc("Print fully qualified types"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
ExplodePatternBindingDecls(
"explode-pattern-binding-decls",
llvm::cl::desc("Separate pattern binding decls into individual var decls"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<std::string>
MangledNameToFind("find-mangled",
llvm::cl::desc("Print the entity with the given mangled name"),
llvm::cl::cat(Category));
// Module printing options.
static llvm::cl::list<std::string>
ModuleToPrint("module-to-print",
llvm::cl::desc("Name of the module to print"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
ModuleGroupToPrint("module-group",
llvm::cl::desc("Name of the module group to print"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
ModulePrintSubmodules("module-print-submodules",
llvm::cl::desc("Recursively print submodules"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
ModulePrintHidden("module-print-hidden",
llvm::cl::desc("Print non-exported imported or submodules"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
ModulePrintSkipOverlay("module-print-skip-overlay",
llvm::cl::desc("Skip Swift overlay modules"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
FullyQualifiedTypesIfAmbiguous(
"fully-qualified-types-if-ambiguous",
llvm::cl::desc("Print types fully-qualified if they would be ambiguous "
"otherwise"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
SynthesizeSugarOnTypes(
"synthesize-sugar-on-types",
llvm::cl::desc("Always print Array and Optional with sugar"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
AnnotatePrint("annotate-print",
llvm::cl::desc("Annotate AST printing"),
llvm::cl::cat(Category),
llvm::cl::init(false));
// AST and module printing options.
static llvm::cl::opt<bool>
PrintInterface("print-interface",
llvm::cl::desc("Print with options set for interface printing, "
"overrides any other printing option"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
PrintInterfaceForDoc("print-interface-doc",
llvm::cl::desc("Print with options set for interface printing, "
"for doc support; overrides any other printing option"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
PrintImplicitAttrs("print-implicit-attrs",
llvm::cl::desc("Print implicit attributes"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
PrintAccess("print-access",
llvm::cl::desc("Print access keywords for all values"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
SkipUnavailable("skip-unavailable",
llvm::cl::desc("Don't print unavailable declarations"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<AccessLevel>
AccessFilter(
llvm::cl::desc("Access filter:"),
llvm::cl::cat(Category),
llvm::cl::init(AccessLevel::Private),
llvm::cl::values(
clEnumValN(AccessLevel::Private, "access-filter-private",
"Print all declarations"),
clEnumValN(AccessLevel::Internal, "access-filter-internal",
"Print internal and public declarations"),
clEnumValN(AccessLevel::Public, "access-filter-public",
"Print public declarations")));
static llvm::cl::opt<bool>
SynthesizeExtension("synthesize-extension",
llvm::cl::desc("Print synthesized extensions from conforming protocols."),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
SkipPrivateStdlibDecls("skip-private-stdlib-decls",
llvm::cl::desc("Don't print declarations that start with '_'"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
SkipUnderscoredStdlibProtocols("skip-underscored-stdlib-protocols",
llvm::cl::desc("Don't print protocols that start with '_'"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
SkipDocumentationComments("skip-print-doc-comments",
llvm::cl::desc("Don't print documentation comments from clang module headers"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
PrintRegularComments("print-regular-comments",
llvm::cl::desc("Print regular comments from clang module headers"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool>
PrintOriginalSourceText("print-original-source",
llvm::cl::desc("print the original source text for applicable declarations"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<std::string>
CommentsXMLSchema("comments-xml-schema",
llvm::cl::desc("Filename of the RelaxNG schema for documentation comments"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
ClangXCC("Xcc", llvm::cl::desc("option to pass to clang"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
HeaderToPrint("header-to-print",
llvm::cl::desc("Header filename to print swift interface for"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
UsrFilter("usr-filter",
llvm::cl::desc("Filter results by the given usrs"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
DeclToPrint("decl-to-print",
llvm::cl::desc("Decl name to print swift interface for"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
LineColumnPair("pos", llvm::cl::desc("Line:Column pair"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
EndLineColumnPair("end-pos", llvm::cl::desc("Line:Column pair"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
USR("usr", llvm::cl::desc("USR"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
ModuleName("module-name", llvm::cl::desc("The module name of the given test."),
llvm::cl::cat(Category), llvm::cl::init("swift_ide_test"));
static llvm::cl::opt<bool>
NoEmptyLineBetweenMembers("no-empty-line-between-members",
llvm::cl::desc("Print no empty line between members."),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<bool> DebugConstraintSolver("debug-constraints",
llvm::cl::desc("Enable verbose debugging from the constraint solver."),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
IncludeLocals("include-locals", llvm::cl::desc("Index local symbols too."),
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<bool>
EnableObjCInterop("enable-objc-interop",
llvm::cl::desc("Enable ObjC interop."),
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<bool>
DisableObjCInterop("disable-objc-interop",
llvm::cl::desc("Disable ObjC interop."),
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<bool>
EnableCxxInterop("enable-cxx-interop",
llvm::cl::desc("Enable C++ interop."),
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<std::string>
GraphVisPath("output-request-graphviz",
llvm::cl::desc("Emit GraphViz output visualizing the request graph."),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
CanonicalizeType("canonicalize-type", llvm::cl::Hidden,
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<bool>
EnableSwiftSourceInfo("enable-swiftsourceinfo",
llvm::cl::desc("Whether to consume .swiftsourceinfo files"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::opt<std::string>
ExplicitSwiftModuleMap("explicit-swift-module-map-file",
llvm::cl::desc("JSON file to include explicit Swift modules"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
EnableExperimentalConcurrency("enable-experimental-concurrency",
llvm::cl::desc("Enable experimental concurrency model"),
llvm::cl::init(false));
} // namespace options
static std::unique_ptr<llvm::MemoryBuffer>
removeCodeCompletionTokens(llvm::MemoryBuffer *Input,
StringRef TokenName,
unsigned *CodeCompletionOffset) {
std::string CleanFile =
ide::removeCodeCompletionTokens(Input->getBuffer(),
TokenName,
CodeCompletionOffset);
return std::unique_ptr<llvm::MemoryBuffer>(
llvm::MemoryBuffer::getMemBufferCopy(CleanFile,
Input->getBufferIdentifier()));
}
/// Returns true on error
static bool setBufferForFile(StringRef SourceFilename,
std::unique_ptr<llvm::MemoryBuffer> &Buffer) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
llvm::MemoryBuffer::getFile(SourceFilename);
if (!FileBufOrErr) {
llvm::errs() << "error opening input file '" << SourceFilename << "':\n"
<< " " << FileBufOrErr.getError().message() << '\n';
return true;
}
Buffer = std::move(FileBufOrErr.get());
return false;
}
static bool doCodeCompletionImpl(
CodeCompletionCallbacksFactory *callbacksFactory,
const CompilerInvocation &InitInvok,
StringRef SourceFilename,
StringRef SecondSourceFileName,
StringRef CodeCompletionToken,
bool CodeCompletionDiagnostics) {
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
if (setBufferForFile(SourceFilename, FileBuf))
return 1;
unsigned Offset;
std::unique_ptr<llvm::MemoryBuffer> CleanFile(removeCodeCompletionTokens(
FileBuf.get(), CodeCompletionToken, &Offset));
if (Offset == ~0U) {
llvm::errs() << "could not find code completion token \""
<< CodeCompletionToken << "\"\n";
return 1;
}
llvm::outs() << "found code completion token " << CodeCompletionToken
<< " at offset " << Offset << "\n";
llvm::errs() << "found code completion token " << CodeCompletionToken
<< " at offset " << Offset << "\n";
CompilerInvocation Invocation(InitInvok);
if (!SecondSourceFileName.empty()) {
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(
SecondSourceFileName);
}
std::string Error;
PrintingDiagnosticConsumer PrintDiags;
CompletionInstance CompletionInst;
auto isSuccess = CompletionInst.performOperation(
Invocation, /*Args=*/{}, llvm::vfs::getRealFileSystem(), CleanFile.get(),
Offset, Error,
CodeCompletionDiagnostics ? &PrintDiags : nullptr,
[&](CompilerInstance &CI, bool reusingASTContext) {
assert(!reusingASTContext && "reusing AST context without enabling it");
auto *SF = CI.getCodeCompletionFile();
performCodeCompletionSecondPass(*SF, *callbacksFactory);
});
return isSuccess ? 0 : 1;
}
static int doTypeContextInfo(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
StringRef SecondSourceFileName,
StringRef CodeCompletionToken,
bool CodeCompletionDiagnostics) {
// Create a CodeCompletionConsumer.
std::unique_ptr<ide::TypeContextInfoConsumer> Consumer(
new ide::PrintingTypeContextInfoConsumer(llvm::outs()));
// Create a factory for code completion callbacks that will feed the
// Consumer.
std::unique_ptr<CodeCompletionCallbacksFactory> callbacksFactory(
ide::makeTypeContextInfoCallbacksFactory(*Consumer));
return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename,
SecondSourceFileName, CodeCompletionToken,
CodeCompletionDiagnostics);
}
static int
doConformingMethodList(const CompilerInvocation &InitInvok,
StringRef SourceFilename, StringRef SecondSourceFileName,
StringRef CodeCompletionToken,
bool CodeCompletionDiagnostics,
const std::vector<std::string> expectedTypeNames) {
SmallVector<const char *, 4> typeNames;
for (auto &name : expectedTypeNames)
typeNames.push_back(name.c_str());
// Create a CodeCompletionConsumer.
std::unique_ptr<ide::ConformingMethodListConsumer> Consumer(
new ide::PrintingConformingMethodListConsumer(llvm::outs()));
// Create a factory for code completion callbacks that will feed the
// Consumer.
std::unique_ptr<CodeCompletionCallbacksFactory> callbacksFactory(
ide::makeConformingMethodListCallbacksFactory(typeNames, *Consumer));
return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename,
SecondSourceFileName, CodeCompletionToken,
CodeCompletionDiagnostics);
}
static int doCodeCompletion(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
StringRef SecondSourceFileName,
StringRef CodeCompletionToken,
bool CodeCompletionDiagnostics,
bool CodeCompletionKeywords,
bool CodeCompletionComments,
bool CodeCompletionAnnotateResults) {
std::unique_ptr<ide::OnDiskCodeCompletionCache> OnDiskCache;
if (!options::CompletionCachePath.empty()) {
OnDiskCache = std::make_unique<ide::OnDiskCodeCompletionCache>(
options::CompletionCachePath);
}
ide::CodeCompletionCache CompletionCache(OnDiskCache.get());
ide::CodeCompletionContext CompletionContext(CompletionCache);
CompletionContext.setAnnotateResult(CodeCompletionAnnotateResults);
// Create a CodeCompletionConsumer.
std::unique_ptr<ide::CodeCompletionConsumer> Consumer(
new ide::PrintingCodeCompletionConsumer(
llvm::outs(), CodeCompletionKeywords, CodeCompletionComments,
CodeCompletionAnnotateResults));
// Create a factory for code completion callbacks that will feed the
// Consumer.
std::unique_ptr<CodeCompletionCallbacksFactory> callbacksFactory(
ide::makeCodeCompletionCallbacksFactory(CompletionContext, *Consumer));
return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename,
SecondSourceFileName, CodeCompletionToken,
CodeCompletionDiagnostics);
}
namespace {
struct CompletionTestToken {
unsigned Line;
unsigned Column;
unsigned Offset;
StringRef Name;
SmallVector<StringRef, 1> CheckPrefixes;
StringRef Skip;
StringRef Xfail;
Optional<bool> IncludeKeywords = None;
Optional<bool> IncludeComments = None;
CompletionTestToken(unsigned Line, unsigned Column, unsigned Offset)
: Line(Line), Column(Column), Offset(Offset){};
static bool isStartOfToken(const char *Ptr) {
return Ptr[0] == '#' && Ptr[1] == '^';
}
static bool isEndOfToken(const char *Ptr) {
return Ptr[0] == '^' && Ptr[1] == '#';
}
static bool isValidTokenChar(char Chr) {
return (Chr >= 'A' && Chr <= 'Z') || (Chr >= 'a' && Chr <= 'z') ||
(Chr >= '0' && Chr <= '9') || Chr == '_' || Chr == '-' || Chr == '.';
}
static bool parseBooleanValue(StringRef Value, bool &Result,
std::string &Error) {
if (Value.empty() || Value == "true" || Value == "1") {
Result = true;
return false;
}
if (Value == "false" || Value == "0") {
Result = false;
return false;
}
Error = "invalid value for keywords";
return true;
}
// #^TOKEN_NAME?check-prefix=CHECK1,CHECK2&keywords=1&comments=true^#
static bool parse(const char *&InputPtr, CompletionTestToken &Result,
std::string &Error) {
auto Ptr = InputPtr;
assert(isStartOfToken(Ptr));
Ptr += 2;
// Parse the token name.
auto NameStart = Ptr;
while (isValidTokenChar(*Ptr)) { ++Ptr; }
Result.Name = StringRef(NameStart, Ptr - NameStart);
// Parse optional query string.
if (*Ptr == '?') {
++Ptr;
auto QueryStart = Ptr;
while (!isEndOfToken(Ptr) && *Ptr != 0 && *Ptr != '\n' && *Ptr != '\r')
++Ptr;
StringRef QueryString(QueryStart, Ptr - QueryStart);
while (!QueryString.empty()) {
StringRef Query, Key, Value;
std::tie(Query, QueryString) = QueryString.split(';');
std::tie(Key, Value) = Query.split('=');
if (Key == "check") {
// This value is passed to 'FileCheck --check-prefixes' as is.
Result.CheckPrefixes.push_back(Value);
continue;
}
if (Key == "keywords") {
Result.IncludeKeywords.emplace();
if (parseBooleanValue(Value, *Result.IncludeKeywords, Error))
return true;
continue;
}
if (Key == "comments") {
Result.IncludeComments.emplace();
if (parseBooleanValue(Value, *Result.IncludeComments, Error))
return true;
continue;
}
if (Key == "skip") {
Result.Skip = Value;
continue;
}
if (Key == "xfail") {
Result.Xfail = Value;
continue;
}
Error = "unknown option (" + Key.str() + ") for token";
return true;
}
}
// Default check prefix is the token name.
if (Result.CheckPrefixes.empty())
Result.CheckPrefixes.push_back(Result.Name);
// Tokens must end with '^#'.
if (!isEndOfToken(Ptr)) {
Error = "expected '^#' at the end of completion token";
return true;
}
InputPtr = Ptr + 2;
return false;;
}
};
static std::unique_ptr<llvm::MemoryBuffer>
removeCodeCompletionTokens(llvm::MemoryBuffer *Input,
SmallVectorImpl<CompletionTestToken> &Tokens,
std::string &Error) {
const char *Start = Input->getBufferStart();
const char *End = Input->getBufferEnd();
assert(*End == 0 && "buffer must be nul terminated");
std::string Out;
Out.reserve(Input->getBufferSize());
llvm::StringSet<> seenTokenName;
const char *Ptr = Start;
const char *SegmentStart = Ptr;
unsigned Removed = 0;
unsigned Line = 1;
unsigned Column = 1;
while (Ptr != End) {
if (CompletionTestToken::isStartOfToken(Ptr)) {
Out.append(SegmentStart, Ptr - SegmentStart);
// Emplace a token with the offset, and parse it.
const char *TokenStart = Ptr;
Tokens.emplace_back(Line, Column, Ptr - Start - Removed);
if (CompletionTestToken::parse(Ptr, Tokens.back(), Error)) {
Error = "while parsing a token at " +
(llvm::utostr(Line) + ":" + llvm::utostr(Column)) + ": " +
Error;
return nullptr;
}
if (!seenTokenName.insert(Tokens.back().Name).second) {
Error = "Duplicated token name '" + Tokens.back().Name.str() +
"' at " + (llvm::utostr(Line) + ":" + llvm::utostr(Column));
return nullptr;
}
auto TokLen = Ptr - TokenStart;
SegmentStart = Ptr;
Removed += TokLen;
Column += TokLen;
continue;
}
if (*Ptr == '\r' || *Ptr == '\n') {
Ptr += (Ptr[0] == '\r' && Ptr[1] == '\n') ? 2 : 1;
Line += 1;
Column = 1;
continue;
}
++Ptr;
++Column;
}
Out.append(SegmentStart, Ptr - SegmentStart);
return llvm::MemoryBuffer::getMemBufferCopy(Out,
Input->getBufferIdentifier());
}
} // namespace
static int doBatchCodeCompletion(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
bool CodeCompletionDiagnostics,
bool CodeCompletionKeywords,
bool CodeCompletionComments) {
auto FileBufOrErr = llvm::MemoryBuffer::getFile(SourceFilename);
if (!FileBufOrErr) {
llvm::errs() << "error opening input file: "
<< FileBufOrErr.getError().message() << '\n';
return 1;
}
// Completion results are output to
// '${OutputDir}/complete-{Token.Name}.result'.
SmallString<128> OutputDir;
if (!options::CompletionOutputDir.empty()) {
OutputDir = options::CompletionOutputDir;
if (auto result = llvm::sys::fs::create_directories(OutputDir))
return result.value();
} else if (!options::SkipFileCheck) {
llvm::errs() << "error: -completion-output-dir is needed unless "
"-skip-filecheck is specified.\n";
return 1;
}
std::string Error;
llvm::SmallVector<CompletionTestToken, 0> CCTokens;
auto CleanFile =
removeCodeCompletionTokens(FileBufOrErr.get().get(), CCTokens, Error);
if (!CleanFile) {
llvm::errs() << "error: " << Error << "\n";
return 1;
}
if (!options::CodeCompletionToken.empty()) {
// If `-code-completion-token` is specified, test only that token.
// TODO: Multiple tokens.
StringRef TargetTokName = options::CodeCompletionToken;
Optional<CompletionTestToken> FoundTok;
for (auto Tok : CCTokens) {
if (Tok.Name == TargetTokName) {
FoundTok = Tok;
break;
}
}
if (FoundTok) {
CCTokens = {*FoundTok};
} else {
llvm::errs() << "error: could not find code completion token \""
<< TargetTokName << "\"\n";
return 1;
}
} else {
// Shuffle tokens to detect order-dependent bugs.
if (CCTokens.empty()) {
llvm::errs()
<< "error: could not find any code completion tokens in input file\n";
return 1;
}
unsigned RandomSeed = options::RandomSeed;
if (RandomSeed == 0)
RandomSeed = std::chrono::system_clock::now().time_since_epoch().count();
llvm::errs() << "Use --random-seed=" << RandomSeed
<< " to reproduce the order of this run.\n";
std::shuffle(CCTokens.begin(), CCTokens.end(),
std::default_random_engine(RandomSeed));
}
CompilerInvocation Invocation(InitInvok);
auto FileSystem = llvm::vfs::getRealFileSystem();
CompletionInstance CompletionInst;
std::unique_ptr<ide::OnDiskCodeCompletionCache> OnDiskCache;
if (!options::CompletionCachePath.empty())
OnDiskCache = std::make_unique<ide::OnDiskCodeCompletionCache>(
options::CompletionCachePath);
ide::CodeCompletionCache CompletionCache(OnDiskCache.get());
// Process tokens.
SmallVector<StringRef, 0> FailedTokens;
SmallVector<StringRef, 0> UPassTokens;
for (const auto &Token : CCTokens) {
if (!options::CodeCompletionToken.empty() &&
options::CodeCompletionToken != Token.Name)
continue;
llvm::errs() << "----\n";
llvm::errs() << "Token: " << Token.Name << "; offset=" << Token.Offset
<< "; pos=" << Token.Line << ":" << Token.Column;
for (auto Prefix : Token.CheckPrefixes) {
llvm::errs() << "; check=" << Prefix;
}
llvm::errs() << "\n";
// Skip tokens with '?skip=${reason}'.
if (!Token.Skip.empty()) {
llvm::errs() << "Skipped: " << Token.Skip << "\n";
continue;
}
bool failureExpected = !Token.Xfail.empty();
if (failureExpected) {
llvm::errs() << "Xfail: " << Token.Xfail << "\n";
}
auto IncludeKeywords = CodeCompletionKeywords;
if (Token.IncludeKeywords)
IncludeKeywords = *Token.IncludeKeywords;
auto IncludeComments = CodeCompletionComments;
if (Token.IncludeComments)
IncludeComments = *Token.IncludeComments;
// Store the result to a string.
std::string ResultStr;
llvm::raw_string_ostream OS(ResultStr);
OS << "// Token: " << Token.Name << "\n";
auto Offset = Token.Offset;
auto completionBuffer = ide::makeCodeCompletionMemoryBuffer(
CleanFile.get(), Offset, CleanFile->getBufferIdentifier());
PrintingDiagnosticConsumer PrintDiags;
auto completionStart = std::chrono::high_resolution_clock::now();
bool wasASTContextReused = false;
bool isSuccess = CompletionInst.performOperation(
Invocation, /*Args=*/{}, FileSystem, completionBuffer.get(), Offset,
Error, CodeCompletionDiagnostics ? &PrintDiags : nullptr,
[&](CompilerInstance &CI, bool reusingASTContext) {
// Create a CodeCompletionConsumer.
std::unique_ptr<ide::CodeCompletionConsumer> Consumer(
new ide::PrintingCodeCompletionConsumer(OS, IncludeKeywords,
IncludeComments));
// Create a factory for code completion callbacks that will feed the
// Consumer.
ide::CodeCompletionContext CompletionContext(CompletionCache);
std::unique_ptr<CodeCompletionCallbacksFactory> callbacksFactory(
ide::makeCodeCompletionCallbacksFactory(CompletionContext,
*Consumer));
auto *SF = CI.getCodeCompletionFile();
performCodeCompletionSecondPass(*SF, *callbacksFactory);
wasASTContextReused = reusingASTContext;
});
auto completionEnd = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
completionEnd - completionStart);
llvm::errs() << "Elapsed: " << elapsed.count() << " msec";
if (wasASTContextReused)
llvm::errs() << " (reusing ASTContext)";
llvm::errs() << "\n";
OS.flush();
if (OutputDir.empty()) {
// If output directory is not specified, print the results to STDOUT.
llvm::outs() << ResultStr;
continue;
}
// Print to '${OutputDir}/complete-{Token.Name}.result'.
int resultFD;
SmallString<128> resultFilename(OutputDir);
llvm::sys::path::append(resultFilename,
"complete-" + Token.Name + ".result");
if (auto res = llvm::sys::fs::openFileForWrite(resultFilename, resultFD)) {
llvm::errs() << "error: failed to create output file: "
<< resultFilename << "\n";
return res.value();
}
llvm::raw_fd_ostream fileOut(resultFD, /*shouldClose=*/true);
fileOut << ResultStr;
fileOut.close();
if (!isSuccess) {
llvm::errs() << "error: " << Error << "\n";
return 1;
}
if (options::SkipFileCheck)
continue;
assert(!options::FileCheckPath.empty());
bool isFileCheckFailed = false;
for (auto Prefix : Token.CheckPrefixes) {
StringRef FileCheckArgs[] = {options::FileCheckPath, SourceFilename,
"--check-prefixes", Prefix,
"--input-file", resultFilename};
int result =
llvm::sys::ExecuteAndWait(options::FileCheckPath, FileCheckArgs,
/*Env=*/None,
/*Redirects=*/{},
/*SecondsToWait=*/0,
/*MemoryLimit=*/0,
/*ErrMsg=*/&Error);
if (result != 0) {
isFileCheckFailed = true;
if (!Error.empty())
llvm::errs() << "error: " << Error << "\n";
// Output the FileCheck invocation.
llvm::errs() << "+";
for (auto arg : FileCheckArgs)
llvm::errs() << " " << arg;
llvm::errs() << "\n";
}
}
if (isFileCheckFailed == failureExpected) {
// Success. The result may be huge. Remove the result if it's succeeded.
llvm::sys::fs::remove(resultFilename);
} else if (isFileCheckFailed) {
// Unexpectedly failed.
FailedTokens.push_back(Token.Name);
} else {
// Unexpectedly passed.
UPassTokens.push_back(Token.Name);
}
}
if (FailedTokens.empty() && UPassTokens.empty())
return 0;
llvm::errs() << "----\n";
if (!FailedTokens.empty()) {
llvm::errs() << "Unexpected failures: ";
llvm::interleave(
FailedTokens, [&](StringRef name) { llvm::errs() << name; },
[&]() { llvm::errs() << ", "; });
llvm::errs() << "\n";
}
if (!UPassTokens.empty()) {
llvm::errs() << "Unexpected passes: ";
llvm::interleave(
UPassTokens, [&](StringRef name) { llvm::errs() << name; },
[&]() { llvm::errs() << ", "; });
llvm::errs() << "\n";
}
return 1;
}
static int doREPLCodeCompletion(const CompilerInvocation &InitInvok,
StringRef SourceFilename) {
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
if (setBufferForFile(SourceFilename, FileBuf))
return 1;
StringRef BufferText = FileBuf->getBuffer();
// Drop a single newline character from the buffer.
if (BufferText.endswith("\n"))
BufferText = BufferText.drop_back(1);
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
auto &ctx = CI.getASTContext();
registerIDERequestFunctions(ctx.evaluator);
// Create an initial empty SourceFile. This only exists to feed in the
// implicit stdlib import.
ImplicitImportInfo importInfo;
importInfo.StdlibKind = ImplicitStdlibKind::Stdlib;
auto *M = ModuleDecl::create(ctx.getIdentifier(Invocation.getModuleName()),
ctx, importInfo);
auto *SF = new (ctx) SourceFile(*M, SourceFileKind::Main, /*BufferID*/ None);
M->addFile(*SF);
performImportResolution(*SF);
REPLCompletions REPLCompl;
REPLCompl.populate(*SF, BufferText);
llvm::outs() << "Begin completions\n";
for (StringRef S : REPLCompl.getCompletionList()) {
llvm::outs() << S << "\n";
}
llvm::outs() << "End completions\n";
return 0;
}
//===----------------------------------------------------------------------===//
// Syntax Coloring
//===----------------------------------------------------------------------===//
namespace {
class PrintSyntaxColorWalker : public ide::SyntaxModelWalker {
SourceManager &SM;
unsigned BufferID;
llvm::raw_ostream &OS;
bool TerminalOutput;
const char *BufStart;
const char *BufEnd;
const char *CurrBufPtr;
public:
PrintSyntaxColorWalker(SourceManager &SM,
unsigned BufferID,
llvm::raw_ostream &OS,
bool TerminalOutput)
: SM(SM), BufferID(BufferID), OS(OS), TerminalOutput(TerminalOutput) {
CharSourceRange entireRange = SM.getRangeForBuffer(BufferID);
StringRef Buffer = SM.extractText(entireRange);
BufStart = Buffer.data();
BufEnd = Buffer.data() + Buffer.size();
CurrBufPtr = BufStart;
}
bool walkToNodePre(SyntaxNode Node) override {
if (shouldIgnore(Node))
return false;
const char *LocPtr = getPtr(Node.Range.getStart());
printSourceUntil(LocPtr);
wrap(Node.Kind, /*Begin=*/true);
return true;
}
bool walkToNodePost(SyntaxNode Node) override {
if (shouldIgnore(Node))
return true;
const char *LocPtr = getPtr(Node.Range.getStart());
unsigned Length = Node.Range.getByteLength();
if (Node.Kind == SyntaxNodeKind::CommentLine) {
if (LocPtr[Length-1] == '\n')
--Length; // Wrapping should be in the same line.
}
printSourceUntil(LocPtr + Length);
wrap(Node.Kind, /*Begin=*/false);
return true;
}
void wrap(SyntaxNodeKind Kind, bool Begin) {
if (TerminalOutput) {
wrapForTerminal(Kind, Begin);
} else {
wrapForTest(Kind, Begin);
}
}
bool shouldIgnore(SyntaxNode Node) const {
const char *LocPtr = getPtr(Node.Range.getStart());
if (Node.Kind == SyntaxNodeKind::CommentLine && !TerminalOutput) {
// Ignore CHECK lines.
if (StringRef(LocPtr, BufEnd - LocPtr).startswith("// CHECK"))
return true;
}
return false;
}
const char *getPtr(SourceLoc Loc) const {
return BufStart + SM.getLocOffsetInBuffer(Loc, BufferID);
}
void printSourceUntil(const char *Ptr) {
assert(Ptr >= CurrBufPtr && Ptr <= BufEnd);
StringRef Text = StringRef(CurrBufPtr, Ptr-CurrBufPtr);
// Skip all "// CHECK" lines.
while (true) {
size_t Idx = Text.find("// CHECK");
if (Idx == StringRef::npos)
break;
OS << Text.substr(0, Idx);
Idx = Text.find('\n', Idx);
Text = Idx == StringRef::npos ? StringRef() : Text.substr(Idx+1);
}
OS << Text;
CurrBufPtr = Ptr;
}
void wrapForTest(SyntaxNodeKind Kind, bool Begin) {
const char *Id = 0;
switch (Kind) {
case SyntaxNodeKind::Keyword: Id = "kw"; break;
// Skip identifier.
case SyntaxNodeKind::Identifier: return;
case SyntaxNodeKind::DollarIdent: Id = "dollar"; break;
case SyntaxNodeKind::Integer: Id = "int"; break;
case SyntaxNodeKind::Floating: Id = "float"; break;
case SyntaxNodeKind::String: Id = "str"; break;
case SyntaxNodeKind::StringInterpolationAnchor: Id = "anchor"; break;
case SyntaxNodeKind::CommentLine: Id = "comment-line"; break;
case SyntaxNodeKind::CommentBlock: Id = "comment-block"; break;
case SyntaxNodeKind::CommentMarker: Id = "comment-marker"; break;
case SyntaxNodeKind::CommentURL: Id = "comment-url"; break;
case SyntaxNodeKind::DocCommentLine: Id = "doc-comment-line"; break;
case SyntaxNodeKind::DocCommentBlock: Id = "doc-comment-block"; break;
case SyntaxNodeKind::DocCommentField: Id = "doc-comment-field"; break;
case SyntaxNodeKind::TypeId: Id = "type"; break;
case SyntaxNodeKind::BuildConfigKeyword: Id = "#kw"; break;
case SyntaxNodeKind::BuildConfigId: Id = "#id"; break;
case SyntaxNodeKind::PoundDirectiveKeyword: Id = "#kw"; break;
case SyntaxNodeKind::AttributeId: Id = "attr-id"; break;
case SyntaxNodeKind::AttributeBuiltin: Id = "attr-builtin"; break;
case SyntaxNodeKind::EditorPlaceholder: Id = "placeholder"; break;
case SyntaxNodeKind::ObjectLiteral: Id = "object-literal"; break;
}
OS << (Begin ? "<" : "</") << Id << '>';
}
void wrapForTerminal(SyntaxNodeKind Kind, bool Begin) {
llvm::raw_ostream::Colors Col;
switch (Kind) {
case SyntaxNodeKind::Keyword: Col = llvm::raw_ostream::MAGENTA; break;
// Skip identifier.
case SyntaxNodeKind::Identifier: return;
case SyntaxNodeKind::DollarIdent: Col = llvm::raw_ostream::MAGENTA; break;
case SyntaxNodeKind::Integer: Col = llvm::raw_ostream::BLUE; break;
case SyntaxNodeKind::Floating: Col = llvm::raw_ostream::BLUE; break;
case SyntaxNodeKind::String: Col = llvm::raw_ostream::RED; break;
case SyntaxNodeKind::StringInterpolationAnchor: Col = llvm::raw_ostream::CYAN; break;
case SyntaxNodeKind::CommentLine: Col = llvm::raw_ostream::GREEN; break;
case SyntaxNodeKind::CommentBlock: Col = llvm::raw_ostream::GREEN; break;
case SyntaxNodeKind::CommentMarker: Col = llvm::raw_ostream::MAGENTA; break;
case SyntaxNodeKind::DocCommentLine: Col = llvm::raw_ostream::CYAN; break;
case SyntaxNodeKind::DocCommentBlock: Col = llvm::raw_ostream::CYAN; break;
case SyntaxNodeKind::DocCommentField: Col = llvm::raw_ostream::WHITE; break;
case SyntaxNodeKind::CommentURL: Col = llvm::raw_ostream::RED; break;
case SyntaxNodeKind::TypeId: Col = llvm::raw_ostream::CYAN; break;
case SyntaxNodeKind::BuildConfigKeyword: Col = llvm::raw_ostream::YELLOW; break;
case SyntaxNodeKind::BuildConfigId: Col = llvm::raw_ostream::YELLOW; break;
case SyntaxNodeKind::PoundDirectiveKeyword: Col = llvm::raw_ostream::YELLOW; break;
case SyntaxNodeKind::AttributeId: Col = llvm::raw_ostream::CYAN; break;
case SyntaxNodeKind::AttributeBuiltin: Col = llvm::raw_ostream::MAGENTA; break;
case SyntaxNodeKind::EditorPlaceholder: Col = llvm::raw_ostream::YELLOW; break;
case SyntaxNodeKind::ObjectLiteral: return;
}
if (Begin) {
if (const char *CStr = llvm::sys::Process::OutputColor(
static_cast<char>(Col), false, false)) {
OS << CStr;
}
} else {
OS.resetColor();
}
}
void finished() {
OS << StringRef(CurrBufPtr, BufEnd-CurrBufPtr);
}
};
} // end anonymous namespace
static int doSyntaxColoring(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
bool TerminalOutput,
bool RunTypeChecker,
bool Playground) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
Invocation.getLangOptions().DisableAvailabilityChecking = false;
Invocation.getLangOptions().Playground = Playground;
Invocation.getLangOptions().CollectParsedToken = true;
Invocation.getLangOptions().BuildSyntaxTree = true;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
if (RunTypeChecker) {
CompilerInstance CI;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
CI.performSema();
unsigned BufID = CI.getInputBufferIDs().back();
SourceFile *SF = nullptr;
for (auto Unit : CI.getMainModule()->getFiles()) {
SF = dyn_cast<SourceFile>(Unit);
if (SF)
break;
}
assert(SF && "no source file?");
ide::SyntaxModelContext ColorContext(*SF);
PrintSyntaxColorWalker ColorWalker(CI.getSourceMgr(), BufID, llvm::outs(),
TerminalOutput);
ColorContext.walk(ColorWalker);
ColorWalker.finished();
} else {
// SourceKit doesn't set up a compiler instance at all for its syntactic
// requests, just the parser. We try to mimic that setup here to help catch
// any cases where the walker might inadvertently rely on the name lookup or
// other semantic functionality via the request evaluator.
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
if (setBufferForFile(SourceFilename, FileBuf))
return 1;
SourceManager SM;
unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf));
RC<SyntaxArena> syntaxArena{new syntax::SyntaxArena()};
std::shared_ptr<SyntaxTreeCreator> SynTreeCreator =
std::make_shared<SyntaxTreeCreator>(
SM, BufferID, Invocation.getMainFileSyntaxParsingCache(),
syntaxArena);
ParserUnit Parser(SM, SourceFileKind::Main, BufferID,
Invocation.getLangOptions(),
Invocation.getTypeCheckerOptions(),
Invocation.getModuleName(),
SynTreeCreator,
Invocation.getMainFileSyntaxParsingCache());
registerParseRequestFunctions(Parser.getParser().Context.evaluator);
registerTypeCheckerRequestFunctions(Parser.getParser().Context.evaluator);
Parser.getDiagnosticEngine().addConsumer(PrintDiags);
(void)Parser.parse();
ide::SyntaxModelContext ColorContext(Parser.getSourceFile());
PrintSyntaxColorWalker ColorWalker(SM, BufferID, llvm::outs(),
TerminalOutput);
ColorContext.walk(ColorWalker);
ColorWalker.finished();
}
return 0;
}
static int doDumpImporterLookupTables(const CompilerInvocation &InitInvok,
StringRef SourceFilename) {
if (options::ImportObjCHeader.empty()) {
llvm::errs() << "implicit header required\n";
llvm::cl::PrintHelpMessage();
return 1;
}
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
auto &Context = CI.getASTContext();
auto &Importer = static_cast<ClangImporter &>(
*Context.getClangModuleLoader());
Importer.dumpSwiftLookupTables();
return 0;
}
//===----------------------------------------------------------------------===//
// Structure Annotation
//===----------------------------------------------------------------------===//
class StructureAnnotator : public ide::SyntaxModelWalker {
SourceManager &SM;
unsigned BufferID;
clang::RewriteBuffer RewriteBuf;
std::vector<SyntaxStructureNode> NodeStack;
CharSourceRange LastPoppedNodeRange;
public:
StructureAnnotator(SourceManager &SM, unsigned BufID)
: SM(SM), BufferID(BufID) {
StringRef Input = SM.getLLVMSourceMgr().getMemoryBuffer(BufID)->getBuffer();
RewriteBuf.Initialize(Input);
removeCheckLines(Input);
}
void printResult(raw_ostream &OS) {
RewriteBuf.write(OS);
}
private:
bool walkToSubStructurePre(SyntaxStructureNode Node) override {
const SyntaxStructureNode *Parent = nullptr;
if (!NodeStack.empty())
Parent = &NodeStack.back();
checkNode(Node, Parent);
NodeStack.push_back(Node);
tagRange(Node.Range, getTagName(Node.Kind), Node);
if (Node.NameRange.isValid())
tagRange(Node.NameRange, "name", Node);
for (auto &TyRange : Node.InheritedTypeRanges) {
tagRange(TyRange, "inherited", Node);
}
for (auto &Elem : Node.Elements) {
tagRange(Elem.Range, getTagName(Elem.Kind), Node);
}
if (Node.TypeRange.isValid() && Node.Range.contains(Node.TypeRange))
tagRange(Node.TypeRange, "type", Node);
return true;
}
void tagRange(CharSourceRange Range, StringRef tagName,
const SyntaxStructureNode &Node) {
checkRange(Range, &Node);
std::string BeginTag;
llvm::raw_string_ostream(BeginTag) << '<' << tagName << '>';
std::string EndTag;
llvm::raw_string_ostream(EndTag) << "</" << tagName << '>';
unsigned Offset = SM.getLocOffsetInBuffer(Range.getStart(), BufferID);
RewriteBuf.InsertTextAfter(Offset, BeginTag);
RewriteBuf.InsertTextBefore(Offset+Range.getByteLength(), EndTag);
}
static StringRef getTagName(SyntaxStructureKind K) {
switch (K) {
case SyntaxStructureKind::Argument: return "arg";
case SyntaxStructureKind::Class: return "class";
case SyntaxStructureKind::Struct: return "struct";
case SyntaxStructureKind::Protocol: return "protocol";
case SyntaxStructureKind::Enum: return "enum";
case SyntaxStructureKind::Extension: return "extension";
case SyntaxStructureKind::FreeFunction: return "ffunc";
case SyntaxStructureKind::InstanceFunction: return "ifunc";
case SyntaxStructureKind::StaticFunction: return "sfunc";
case SyntaxStructureKind::ClassFunction: return "cfunc";
case SyntaxStructureKind::GlobalVariable: return "gvar";
case SyntaxStructureKind::InstanceVariable: return "property";
case SyntaxStructureKind::StaticVariable: return "svar";
case SyntaxStructureKind::ClassVariable: return "cvar";
case SyntaxStructureKind::LocalVariable: return "lvar";
case SyntaxStructureKind::EnumCase: return "enum-case";
case SyntaxStructureKind::EnumElement: return "enum-elem";
case SyntaxStructureKind::TypeAlias: return "typealias";
case SyntaxStructureKind::Subscript: return "subscript";
case SyntaxStructureKind::AssociatedType: return "associatedtype";
case SyntaxStructureKind::GenericTypeParam: return "generic-param";
case SyntaxStructureKind::Parameter: return "param";
case SyntaxStructureKind::ForEachStatement: return "foreach";
case SyntaxStructureKind::WhileStatement: return "while";
case SyntaxStructureKind::RepeatWhileStatement: return "repeat-while";
case SyntaxStructureKind::IfStatement: return "if";
case SyntaxStructureKind::GuardStatement: return "guard";
case SyntaxStructureKind::SwitchStatement: return "switch";
case SyntaxStructureKind::CaseStatement: return "case";
case SyntaxStructureKind::BraceStatement: return "brace";
case SyntaxStructureKind::CallExpression: return "call";
case SyntaxStructureKind::ArrayExpression: return "array";
case SyntaxStructureKind::DictionaryExpression: return "dictionary";
case SyntaxStructureKind::ObjectLiteralExpression:
return "object-literal-expression";
case SyntaxStructureKind::TupleExpression: return "tuple";
case SyntaxStructureKind::ClosureExpression: return "closure";
}
llvm_unreachable("unhandled tag?");
}
static StringRef getTagName(SyntaxStructureElementKind K) {
switch (K) {
case SyntaxStructureElementKind::Id: return "elem-id";
case SyntaxStructureElementKind::Expr: return "elem-expr";
case SyntaxStructureElementKind::InitExpr: return "elem-initexpr";
case SyntaxStructureElementKind::ConditionExpr: return "elem-condexpr";
case SyntaxStructureElementKind::Pattern: return "elem-pattern";
case SyntaxStructureElementKind::TypeRef: return "elem-typeref";
}
llvm_unreachable("unhandled tag?");
}
bool walkToSubStructurePost(SyntaxStructureNode Node) override {
assert(!NodeStack.empty());
LastPoppedNodeRange = NodeStack.back().Range;
NodeStack.pop_back();
return true;
}
void checkNode(const SyntaxStructureNode &Node,
const SyntaxStructureNode *Parent) {
checkRange(Node.Range, Parent);
}
void checkRange(CharSourceRange Range, const SyntaxStructureNode *Parent) {
assert(Range.isValid());
if (Parent) {
assert(Parent->Range.contains(Range));
}
if (LastPoppedNodeRange.isValid()) {
// FIXME: Initializer expressions (like array literals) are not contained
// within the global variables nodes.
// assert(!SM.isBeforeInBuffer(Range.getStart(),
// LastPoppedNodeRange.getEnd()));
}
}
void removeCheckLines(StringRef Input) {
StringRef CheckStr = "CHECK";
size_t Pos = 0;
while (true) {
Pos = Input.find(CheckStr, Pos);
if (Pos == StringRef::npos)
break;
Pos = Input.substr(0, Pos).rfind("//");
assert(Pos != StringRef::npos);
size_t EndLine = Input.find('\n', Pos);
assert(EndLine != StringRef::npos);
++EndLine;
RewriteBuf.RemoveText(Pos, EndLine-Pos);
Pos = EndLine;
}
}
};
static int doStructureAnnotation(const CompilerInvocation &InitInvok,
StringRef SourceFilename) {
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
if (setBufferForFile(SourceFilename, FileBuf))
return 1;
CompilerInvocation Invocation(InitInvok);
Invocation.getLangOptions().BuildSyntaxTree = true;
Invocation.getLangOptions().CollectParsedToken = true;
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
// Structure annotation is run as a purely syntactic request by SourceKit. It
// doesn't set up a compiler instance at all, just the parser. We try to mimic
// that setup here to help catch any cases where the walker might inadvertently
// rely on the name lookup or other semantic functionality via the request
// evaluator.
SourceManager SM;
unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf));
RC<SyntaxArena> syntaxArena{new syntax::SyntaxArena()};
std::shared_ptr<SyntaxTreeCreator> SynTreeCreator =
std::make_shared<SyntaxTreeCreator>(
SM, BufferID, Invocation.getMainFileSyntaxParsingCache(),
syntaxArena);
ParserUnit Parser(SM, SourceFileKind::Main, BufferID,
Invocation.getLangOptions(),
Invocation.getTypeCheckerOptions(),
Invocation.getModuleName(),
SynTreeCreator,
Invocation.getMainFileSyntaxParsingCache());
registerParseRequestFunctions(Parser.getParser().Context.evaluator);
registerTypeCheckerRequestFunctions(
Parser.getParser().Context.evaluator);
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
Parser.getDiagnosticEngine().addConsumer(PrintDiags);
(void)Parser.parse();
ide::SyntaxModelContext StructureContext(Parser.getSourceFile());
StructureAnnotator Annotator(SM, BufferID);
StructureContext.walk(Annotator);
Annotator.printResult(llvm::outs());
return 0;
}
//===----------------------------------------------------------------------===//
// Semantic Annotation
//===----------------------------------------------------------------------===//
namespace {
class AnnotationPrinter : public SourceEntityWalker {
SourceManager &SM;
unsigned BufferID;
llvm::raw_ostream &OS;
bool TerminalOutput;
const char *BufStart;
const char *BufEnd;
const char *CurrBufPtr;
public:
AnnotationPrinter(SourceManager &SM,
unsigned BufferID,
llvm::raw_ostream &OS,
bool TerminalOutput)
: SM(SM), BufferID(BufferID), OS(OS), TerminalOutput(TerminalOutput) {
CharSourceRange entireRange = SM.getRangeForBuffer(BufferID);
StringRef Buffer = SM.extractText(entireRange);
BufStart = Buffer.data();
BufEnd = Buffer.data() + Buffer.size();
CurrBufPtr = BufStart;
}
void finished() {
OS << StringRef(CurrBufPtr, BufEnd-CurrBufPtr);
}
private:
struct SemanticSourceEntity {
CharSourceRange Range;
ValueDecl *Dcl = nullptr;
TypeDecl *CtorTyRef = nullptr;
ModuleEntity Mod;
Identifier ArgName;
bool IsRef = true;
SemanticSourceEntity(CharSourceRange Range,
ValueDecl *Dcl,
TypeDecl *CtorTyRef,
bool IsRef)
: Range(Range),
Dcl(Dcl),
CtorTyRef(CtorTyRef),
IsRef(IsRef) {}
SemanticSourceEntity(CharSourceRange Range,
ModuleEntity Mod)
: Range(Range),
Mod(Mod) {}
SemanticSourceEntity(CharSourceRange Range,
ValueDecl *Dcl,
Identifier argName)
: Range(Range),
Dcl(Dcl),
ArgName(argName),
IsRef(true) {}
};
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
if (Range.getByteLength() == 0)
return true;
if (auto *VD = dyn_cast<ValueDecl>(D))
annotateSourceEntity({ Range, VD, nullptr, /*IsRef=*/false});
return true;
}
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type Ty,
ReferenceMetaData Data) override {
if (!Data.isImplicit)
annotateSourceEntity({ Range, D, CtorTyRef, /*IsRef=*/true });
return true;
}
bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
ReferenceMetaData Data,
bool IsOpenBracket) override {
return visitDeclReference(D, Range, nullptr, nullptr, Type(), Data);
}
bool visitCallArgName(Identifier Name, CharSourceRange Range,
ValueDecl *D) override {
annotateSourceEntity({ Range, D, Name });
return true;
}
bool visitModuleReference(ModuleEntity Mod, CharSourceRange Range) override {
annotateSourceEntity({ Range, Mod });
return true;
}
void annotateSourceEntity(const SemanticSourceEntity &Entity) {
const char *LocPtr =
BufStart + SM.getLocOffsetInBuffer(Entity.Range.getStart(), BufferID);
unsigned Length = Entity.Range.getByteLength();
assert(LocPtr >= CurrBufPtr);
printSourceUntil(LocPtr);
StringRef NodeText = StringRef(LocPtr, Length);
if (TerminalOutput) {
if (!wrapForTerminal(Entity, NodeText))
OS << NodeText;
} else {
if (!wrapForTest(Entity, StringRef(LocPtr, Length)))
OS << NodeText;
}
CurrBufPtr = LocPtr + Length;
}
void printSourceUntil(const char *Ptr) {
StringRef Text = StringRef(CurrBufPtr, Ptr-CurrBufPtr);
// Skip all "// CHECK" lines.
while (true) {
size_t Idx = Text.find("// CHECK");
if (Idx == StringRef::npos)
break;
OS << Text.substr(0, Idx);
Idx = Text.find('\n', Idx);
Text = Idx == StringRef::npos ? StringRef() : Text.substr(Idx+1);
}
OS << Text;
}
void printLoc(SourceLoc Loc, raw_ostream &OS) {
OS << '@';
if (Loc.isValid() && SM.findBufferContainingLoc(Loc) == BufferID) {
auto LineCol = SM.getPresumedLineAndColumnForLoc(Loc, BufferID);
OS << LineCol.first << ':' << LineCol.second;
}
}
bool wrapForTest(const SemanticSourceEntity &Entity, StringRef Text) {
OS << '<';
bool IsInSystemModule = false;
ValueDecl *D = Entity.Dcl;
if (D) {
IsInSystemModule = D->getModuleContext()->isSystemModule();
if (IsInSystemModule)
OS << 'i';
if (isa<ConstructorDecl>(D) && Entity.IsRef) {
OS << "Ctor";
printLoc(D->getLoc(/*SerializedOK*/false), OS);
if (Entity.CtorTyRef) {
OS << '-';
OS << Decl::getKindName(Entity.CtorTyRef->getKind());
printLoc(Entity.CtorTyRef->getLoc(/*SerializedOK*/false), OS);
}
} else {
OS << Decl::getKindName(D->getKind());
if (Entity.IsRef)
printLoc(D->getLoc(/*SerializedOK*/false), OS);
}
} else {
if (Entity.Mod.isSystemModule())
OS << 'i';
OS << "Mod";
}
if (!Entity.ArgName.empty()) {
OS << "#" << Entity.ArgName.str();
}
OS << '>';
OS << Text;
OS << "</";
if (D) {
if (IsInSystemModule)
OS << 'i';
if (isa<ConstructorDecl>(D) && Entity.IsRef) {
OS << "Ctor";
} else {
OS << Decl::getKindName(D->getKind());
}
} else {
if (Entity.Mod.isSystemModule())
OS << 'i';
OS << "Mod";
}
OS << '>';
return true;
}
bool wrapForTerminal(const SemanticSourceEntity &Entity, StringRef Text) {
llvm::raw_ostream::Colors Col;
switch (Entity.Dcl->getKind()) {
default:
return false;
case DeclKind::Var:
Col = llvm::raw_ostream::GREEN;
break;
case DeclKind::Func:
case DeclKind::Constructor:
case DeclKind::Destructor:
Col = llvm::raw_ostream::MAGENTA;
break;
case DeclKind::Class:
Col = llvm::raw_ostream::RED;
break;
case DeclKind::Struct:
Col = llvm::raw_ostream::BLUE;
break;
case DeclKind::Protocol:
Col = llvm::raw_ostream::YELLOW;
break;
case DeclKind::TypeAlias:
case DeclKind::AssociatedType:
case DeclKind::GenericTypeParam:
Col = llvm::raw_ostream::CYAN; break;
}
if (const char *CStr = llvm::sys::Process::OutputColor(
static_cast<char>(Col), false, false)) {
OS << CStr;
}
OS << Text;
OS.resetColor();
return true;
}
};
} // unnamed namespace
static int doSemanticAnnotation(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
bool TerminalOutput) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
unsigned BufID = CI.getInputBufferIDs().back();
AnnotationPrinter AnnotPrinter(CI.getSourceMgr(), BufID, llvm::outs(),
TerminalOutput);
AnnotPrinter.walk(*CI.getMainModule());
AnnotPrinter.finished();
return 0;
}
static int doInputCompletenessTest(StringRef SourceFilename) {
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
if (setBufferForFile(SourceFilename, FileBuf))
return 1;
llvm::raw_ostream &OS = llvm::outs();
OS << SourceFilename << ": ";
if (isSourceInputComplete(std::move(FileBuf),
SourceFileKind::Main).IsComplete) {
OS << "IS_COMPLETE\n";
} else {
OS << "IS_INCOMPLETE\n";
}
return 0;
}
//===----------------------------------------------------------------------===//
// AST printing
//===----------------------------------------------------------------------===//
static ModuleDecl *getModuleByFullName(ASTContext &Context, StringRef ModuleName) {
ModuleDecl *Result = Context.getModuleByName(ModuleName);
if (!Result || Result->failedToLoad())
return nullptr;
return Result;
}
static ModuleDecl *getModuleByFullName(ASTContext &Context, Identifier ModuleName) {
ModuleDecl *Result = Context.getModuleByIdentifier(ModuleName);
if (!Result || Result->failedToLoad())
return nullptr;
return Result;
}
static int doPrintAST(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
bool RunTypeChecker,
const PrintOptions &Options,
StringRef MangledNameToFind,
StringRef DebugClientDiscriminator) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
if (!RunTypeChecker)
Invocation.getLangOptions().DisablePoundIfEvaluation = true;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
std::unique_ptr<DebuggerClient> DebuggerClient;
if (!DebugClientDiscriminator.empty()) {
DebuggerClient.reset(
new PrivateDiscriminatorPreferenceClient(CI.getASTContext(),
DebugClientDiscriminator)
);
CI.getMainModule()->setDebugClient(DebuggerClient.get());
}
if (RunTypeChecker)
CI.performSema();
if (MangledNameToFind.empty()) {
ModuleDecl *M = CI.getMainModule();
M->getMainSourceFile().print(llvm::outs(), Options);
return EXIT_SUCCESS;
}
// If we were given a mangled name, only print that declaration.
const TypeDecl *D = Demangle::getTypeDeclForMangling(CI.getASTContext(),
MangledNameToFind);
if (!D) {
llvm::errs() << "Unable to find decl for symbol: "
<< MangledNameToFind << "\n";
return EXIT_FAILURE;
}
D->print(llvm::outs(), Options);
return EXIT_SUCCESS;
}
static int doPrintExpressionTypes(const CompilerInvocation &InitInvok,
StringRef SourceFilename) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addPrimaryInputFile(SourceFilename);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return EXIT_FAILURE;
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
std::vector<ExpressionTypeInfo> Scratch;
// Buffer for where types will be printed.
llvm::SmallString<256> TypeBuffer;
llvm::raw_svector_ostream OS(TypeBuffer);
SourceFile &SF = *CI.getPrimarySourceFile();
auto Source = SF.getASTContext().SourceMgr.getRangeForBuffer(*SF.getBufferID()).str();
std::vector<std::pair<unsigned, std::string>> SortedTags;
std::vector<const char*> Usrs;
for (auto &u: options::UsrFilter)
Usrs.push_back(u.c_str());
// Collect all tags of expressions.
for (auto R: collectExpressionType(*CI.getPrimarySourceFile(), Usrs, Scratch,
options::CanonicalizeType, OS)) {
SortedTags.push_back({R.offset,
(llvm::Twine("<expr type:\"") + TypeBuffer.str().substr(R.typeOffset,
R.typeLength) + "\">").str()});
SortedTags.push_back({R.offset + R.length, "</expr>"});
}
// Sort tags by offset.
std::stable_sort(SortedTags.begin(), SortedTags.end(),
[](std::pair<unsigned, std::string> T1, std::pair<unsigned, std::string> T2) {
return T1.first < T2.first;
});
ArrayRef<std::pair<unsigned, std::string>> SortedTagsRef = SortedTags;
unsigned Cur = 0;
do {
// Print tags that are due at current offset.
while(!SortedTagsRef.empty() && SortedTagsRef.front().first == Cur) {
llvm::outs() << SortedTagsRef.front().second;
SortedTagsRef = SortedTagsRef.drop_front();
}
auto Start = Cur;
// Change current offset to the start offset of next tag.
Cur = SortedTagsRef.empty() ? Source.size() : SortedTagsRef.front().first;
// Print the source before next tag.
llvm::outs() << Source.substr(Start, Cur - Start);
} while(!SortedTagsRef.empty());
return EXIT_SUCCESS;
}
static int doPrintLocalTypes(const CompilerInvocation &InitInvok,
const std::vector<std::string> ModulesToPrint) {
using NodeKind = Demangle::Node::Kind;
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
auto &Context = CI.getASTContext();
auto *Stdlib = getModuleByFullName(Context, Context.StdlibModuleName);
if (!Stdlib)
return 1;
int ExitCode = 0;
PrintOptions Options = PrintOptions::printEverything();
for (StringRef ModuleName : ModulesToPrint) {
auto *M = getModuleByFullName(Context, ModuleName);
if (!M) {
llvm::errs() << "Couldn't find module " << ModuleName << "\n";
ExitCode = 1;
continue;
}
SmallVector<TypeDecl *, 10> LocalTypeDecls;
SmallVector<std::string, 10> MangledNames;
// Sneak into the module and get all of the local type decls
M->getLocalTypeDecls(LocalTypeDecls);
// Simulate already having mangled names
for (auto LTD : LocalTypeDecls) {
Mangle::ASTMangler Mangler;
std::string MangledName = Mangler.mangleTypeForDebugger(
LTD->getDeclaredInterfaceType(), LTD->getDeclContext());
MangledNames.push_back(MangledName);
}
// Simulate the demangling / parsing process
for (auto MangledName : MangledNames) {
// Global
Demangle::Context DCtx;
auto node = DCtx.demangleSymbolAsNode(MangledName);
// TypeMangling
node = node->getFirstChild();
// Type
node = node->getFirstChild();
auto typeNode = node;
// Nominal Type
node = node->getFirstChild();
switch (node->getKind()) {
case NodeKind::Structure:
case NodeKind::Class:
case NodeKind::Enum:
case NodeKind::OtherNominalType:
case NodeKind::TypeAlias:
break;
case NodeKind::BoundGenericStructure:
case NodeKind::BoundGenericClass:
case NodeKind::BoundGenericEnum:
case NodeKind::BoundGenericOtherNominalType:
// Base type
typeNode = node->getFirstChild();
// Nominal type
node = typeNode->getFirstChild();
assert(node->getKind() == NodeKind::Structure ||
node->getKind() == NodeKind::Class ||
node->getKind() == NodeKind::Enum ||
node->getKind() == NodeKind::OtherNominalType);
break;
default:
llvm::errs() << "Expected a nominal type node in " <<
MangledName << "\n";
return EXIT_FAILURE;
}
while (node->getKind() != NodeKind::LocalDeclName)
node = node->getChild(1); // local decl name
auto remangled = Demangle::mangleNode(typeNode);
auto LTD = M->lookupLocalType(remangled);
if (!LTD) {
llvm::errs() << "Couldn't find local type " << remangled << "\n";
return EXIT_FAILURE;
}
llvm::outs() << remangled << "\n";
auto Options = PrintOptions::printEverything();
Options.PrintAccess = false;
LTD->print(llvm::outs(), Options);
llvm::outs() << "\n";
}
}
return ExitCode;
}
namespace {
class AnnotatingPrinter : public StreamPrinter {
llvm::SmallDenseMap<ValueDecl*, ValueDecl*> DefaultImplementationMap;
bool InProtocol = false;
public:
using StreamPrinter::StreamPrinter;
void printDeclPre(const Decl *D, Optional<BracketOptions> Bracket) override {
StringRef HasDefault = "";
if (isa<ProtocolDecl>(D)) {
InProtocol = true;
DefaultImplementationMap.clear();
auto *PD = const_cast<ProtocolDecl*>(dyn_cast<ProtocolDecl>(D));
collectDefaultImplementationForProtocolMembers(PD,
DefaultImplementationMap);
}
if (InProtocol) {
if (auto *VD = const_cast<ValueDecl*>(dyn_cast<ValueDecl>(D))) {
for (auto Pair : DefaultImplementationMap) {
if (Pair.getSecond() == VD)
HasDefault = "(HasDefault)";
}
}
}
OS << "<decl:" << Decl::getKindName(D->getKind()) << HasDefault << '>';
}
void printDeclLoc(const Decl *D) override {
OS << "<loc>";
}
void printDeclNameOrSignatureEndLoc(const Decl *D) override {
OS << "</loc>";
}
void printDeclPost(const Decl *D, Optional<BracketOptions> Bracket) override {
if (isa<ProtocolDecl>(D)) {
InProtocol = false;
}
OS << "</decl>";
}
void printStructurePre(PrintStructureKind Kind, const Decl *D) override {
if (D)
printDeclPre(D, None);
}
void printStructurePost(PrintStructureKind Kind, const Decl *D) override {
if (D)
printDeclPost(D, None);
}
void printSynthesizedExtensionPre(const ExtensionDecl *ED,
TypeOrExtensionDecl Target,
Optional<BracketOptions> Bracket) override {
if (Bracket.hasValue() && !Bracket.getValue().shouldOpenExtension(ED))
return;
OS << "<synthesized>";
}
void
printSynthesizedExtensionPost(const ExtensionDecl *ED,
TypeOrExtensionDecl Target,
Optional<BracketOptions> Bracket) override {
if (Bracket.hasValue() && !Bracket.getValue().shouldCloseExtension(ED))
return;
OS << "</synthesized>";
}
void printTypeRef(
Type T, const TypeDecl *TD, Identifier Name,
PrintNameContext NameContext = PrintNameContext::Normal) override {
OS << "<ref:" << Decl::getKindName(TD->getKind()) << '>';
StreamPrinter::printTypeRef(T, TD, Name, NameContext);
OS << "</ref>";
}
void printModuleRef(ModuleEntity Mod, Identifier Name) override {
OS << "<ref:module>";
StreamPrinter::printModuleRef(Mod, Name);
OS << "</ref>";
}
};
} // end anonymous namespace
struct GroupNamesPrinter {
llvm::StringSet<> Groups;
raw_ostream &OS;
GroupNamesPrinter(raw_ostream &OS) : OS(OS) {}
~GroupNamesPrinter() {
OS << "Module groups begin:\n";
for (auto &Entry : Groups) {
OS << Entry.getKey() << "\n";
}
OS << "Module groups end.\n";
}
void addDecl(const Decl *D) {
if (auto VD = dyn_cast<ValueDecl>(D)) {
if (!VD->isImplicit() && !VD->isPrivateStdlibDecl()) {
StringRef Name = VD->getGroupName().hasValue() ?
VD->getGroupName().getValue() : "";
Groups.insert(Name.empty() ? "<NULL>" : Name);
}
}
}
};
static int doPrintModuleGroups(const CompilerInvocation &InitInvok,
const std::vector<std::string> ModulesToPrint) {
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
auto &Context = CI.getASTContext();
// Load standard library so that Clang importer can use it.
auto *Stdlib = getModuleByFullName(Context, Context.StdlibModuleName);
if (!Stdlib) {
llvm::errs() << "Failed loading stdlib\n";
return 1;
}
int ExitCode = 0;
for (StringRef ModuleToPrint : ModulesToPrint) {
if (ModuleToPrint.empty()) {
ExitCode = 1;
continue;
}
// Get the (sub)module to print.
auto *M = getModuleByFullName(Context, ModuleToPrint);
if (!M) {
ExitCode = 1;
continue;
}
{
GroupNamesPrinter Printer(llvm::outs());
llvm::SmallVector<Decl*, 256> Results;
M->getDisplayDecls(Results);
for (auto R : Results) {
Printer.addDecl(R);
}
}
}
return ExitCode;
}
static void printModuleMetadata(ModuleDecl *MD) {
auto &OS = llvm::outs();
MD->collectLinkLibraries([&](LinkLibrary lib) {
OS << "link library: " << lib.getName()
<< ", force load: " << (lib.shouldForceLoad() ? "true" : "false") << "\n";
});
}
static int doPrintModuleMetaData(const CompilerInvocation &InitInvok,
const std::vector<std::string> ModulesToPrint) {
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
auto &Context = CI.getASTContext();
// Load standard library so that Clang importer can use it.
auto *Stdlib = getModuleByFullName(Context, Context.StdlibModuleName);
if (!Stdlib) {
llvm::errs() << "Failed loading stdlib\n";
return 1;
}
int ExitCode = 0;
for (StringRef ModuleToPrint : ModulesToPrint) {
if (ModuleToPrint.empty()) {
ExitCode = 1;
continue;
}
// Get the (sub)module to print.
auto *M = getModuleByFullName(Context, ModuleToPrint);
if (!M) {
llvm::errs() << "error: could not find module '" << ModuleToPrint
<< "'\n";
ExitCode = 1;
continue;
}
// Split the module path.
std::vector<StringRef> ModuleName;
while (!ModuleToPrint.empty()) {
StringRef SubModuleName;
std::tie(SubModuleName, ModuleToPrint) = ModuleToPrint.split('.');
ModuleName.push_back(SubModuleName);
}
assert(!ModuleName.empty());
// FIXME: If ModuleToPrint is a submodule, get its top-level module, which
// will be the DeclContext for all of its Decls since we don't have first-
// class submodules.
if (ModuleName.size() > 1) {
M = getModuleByFullName(Context, ModuleName[0]);
if (!M) {
llvm::errs() << "error: could not find module '" << ModuleName[0]
<< "'\n";
ExitCode = 1;
continue;
}
}
printModuleMetadata(M);
}
return ExitCode;
}
static int doPrintModules(const CompilerInvocation &InitInvok,
const std::vector<std::string> ModulesToPrint,
const std::vector<std::string> GroupsToPrint,
ide::ModuleTraversalOptions TraversalOptions,
const PrintOptions &Options,
bool AnnotatePrint,
bool SynthesizeExtensions) {
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
auto &Context = CI.getASTContext();
// Load standard library so that Clang importer can use it.
auto *Stdlib = getModuleByFullName(Context, Context.StdlibModuleName);
if (!Stdlib) {
llvm::errs() << "Failed loading stdlib\n";
return 1;
}
int ExitCode = 0;
std::unique_ptr<ASTPrinter> Printer;
if (AnnotatePrint)
Printer.reset(new AnnotatingPrinter(llvm::outs()));
else
Printer.reset(new StreamPrinter(llvm::outs()));
for (StringRef ModuleToPrint : ModulesToPrint) {
if (ModuleToPrint.empty()) {
ExitCode = 1;
continue;
}
// Get the (sub)module to print.
auto *M = getModuleByFullName(Context, ModuleToPrint);
if (!M) {
llvm::errs() << "error: could not find module '" << ModuleToPrint
<< "'\n";
ExitCode = 1;
continue;
}
std::vector<StringRef> GroupNames;
for (StringRef G : GroupsToPrint) {
GroupNames.push_back(G);
}
printModuleInterface(M, GroupNames, TraversalOptions, *Printer, Options,
SynthesizeExtensions);
}
return ExitCode;
}
static int doPrintHeaders(const CompilerInvocation &InitInvok,
const std::vector<std::string> HeadersToPrint,
const PrintOptions &Options,
bool AnnotatePrint) {
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
auto &Context = CI.getASTContext();
// Load standard library so that Clang importer can use it.
auto *Stdlib = getModuleByFullName(Context, Context.StdlibModuleName);
if (!Stdlib) {
llvm::errs() << "Failed loading stdlib\n";
return 1;
}