Files
swift-mirror/tools/swift-ide-test/swift-ide-test.cpp
Alastair Houghton 18496c5626 [Backtracing] Remove support for implicit import of _Backtracing.
We're going to rename the module to Runtime, and it isn't going to be an
implicit import, so we don't need any of this.

rdar://124913332
2025-01-17 10:09:36 +00:00

4901 lines
169 KiB
C++

//===--- 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 "ModuleAPIDiff.h"
#include "XMLValidator.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/PluginRegistry.h"
#include "swift/AST/PrintOptions.h"
#include "swift/AST/RawComment.h"
#include "swift/AST/USRGeneration.h"
#include "swift/Basic/BasicSourceInfo.h"
#include "swift/Basic/InitializeSwiftModules.h"
#include "swift/Basic/LLVMInitialize.h"
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/PrimitiveParsing.h"
#include "swift/Config.h"
#include "swift/Demangling/Demangle.h"
#include "swift/Driver/FrontendUtil.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/CodeCompletionResultPrinter.h"
#include "swift/IDE/CommentConversion.h"
#include "swift/IDE/ConformingMethodList.h"
#include "swift/IDE/IDERequests.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/IDETool/CompilerInvocation.h"
#include "swift/IDETool/IDEInspectionInstance.h"
#include "swift/Index/Index.h"
#include "swift/Markup/Markup.h"
#include "swift/Parse/ParseVersion.h"
#include "swift/Sema/IDETypeChecking.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/ManagedStatic.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 <system_error>
#include <algorithm>
#include <chrono>
#include <random>
#include <string>
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>
ImplicitModuleImports("import-module", llvm::cl::desc("Force import of named modules"),
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<bool>
ParseAsLibrary("parse-as-library",
llvm::cl::desc("Parse '-source-filename' as a library source file"),
llvm::cl::cat(Category),
llvm::cl::init(false));
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::opt<std::string>
ToolsDirectory("tools-directory",
llvm::cl::desc("Path to external executables (ld, clang, binutils)"),
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>
SystemImportPaths("isystem",
llvm::cl::desc("add a directory to the system 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<std::string>
InProcessPluginServerPath("in-process-plugin-server-path",
llvm::cl::desc("in-process plugin server"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
PluginPath("plugin-path",
llvm::cl::desc("plugin-path"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
LoadPluginLibrary("load-plugin-library",
llvm::cl::desc("load plugin library"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
LoadPluginExecutable("load-plugin-executable",
llvm::cl::desc("load plugin executable"),
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::list<std::string>
ModuleAliases("module-alias",
llvm::cl::desc("Use '-module-alias <name>=<binary_name>' to map a module of <name> that appears in source code to <binary_name>"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
SkipDeinit("skip-deinit",
llvm::cl::desc("Whether to skip printing destructors"),
llvm::cl::cat(Category), llvm::cl::init(false));
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>
EnableDeserializationSafety("enable-deserialization-safety",
llvm::cl::desc("Avoid reading potentially unsafe decls from swiftmodules"),
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>
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>
PrintStats("print-stats",
llvm::cl::desc("Print statistics"),
llvm::cl::cat(Category),
llvm::cl::init(false));
static llvm::cl::list<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));
static llvm::cl::opt<std::string>
FileCheckSuffix("filecheck-additional-suffix",
llvm::cl::value_desc("check-prefix-suffix"),
llvm::cl::desc("Additional suffix to add to check prefixes as an alternative"),
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>
CodeCompletionSourceText("code-completion-sourcetext",
llvm::cl::desc("Include source texts 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));
static llvm::cl::opt<bool> CodeCompletionAddCallWithNoDefaultArgs(
"code-complete-add-call-with-no-default-args",
llvm::cl::desc("Whether to include a function completion that contains no "
"default arguments"),
llvm::cl::cat(Category), llvm::cl::init(true));
// '-conforming-methods' options.
static llvm::cl::list<std::string>
ConformingMethodListExpectedTypes("conforming-methods-expected-types",
llvm::cl::desc("Set expected types for conforming 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>
Expressions("expressions",
llvm::cl::desc("Print expressions"),
llvm::cl::cat(Category),
llvm::cl::init(false));
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> SkipPrivateSystemDecls(
"skip-private-system-decls",
llvm::cl::desc("Don't print declarations that start with '_'"),
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<bool> SkipUnderscoredSystemProtocols(
"skip-underscored-system-protocols",
llvm::cl::desc("Don't print protocols that start with '_'"),
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<bool>
SkipUnsafeCXXMethods("skip-unsafe-cxx-methods",
llvm::cl::desc("Don't print unsafe C++ class methods that were renamed as unsafe"),
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>
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-experimental-cxx-interop",
llvm::cl::desc("Enable C++ interop."),
llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<std::string>
CxxInteropVersion("cxx-interoperability-mode",
llvm::cl::desc("C++ interop mode."),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
CxxInteropGettersSettersAsProperties("cxx-interop-getters-setters-as-properties",
llvm::cl::desc("Imports getters and setters as computed properties."),
llvm::cl::cat(Category), llvm::cl::init(false));
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));
static llvm::cl::opt<bool>
WarnConcurrency("warn-concurrency",
llvm::cl::desc("Additional concurrency warnings"),
llvm::cl::init(false));
static llvm::cl::opt<bool>
DisableImplicitConcurrencyImport("disable-implicit-concurrency-module-import",
llvm::cl::desc("Disable implicit import of _Concurrency module"),
llvm::cl::init(false));
static llvm::cl::opt<bool>
DisableImplicitStringProcessingImport("disable-implicit-string-processing-module-import",
llvm::cl::desc("Disable implicit import of _StringProcessing module"),
llvm::cl::init(false));
static llvm::cl::opt<bool> EnableExperimentalNamedOpaqueTypes(
"enable-experimental-named-opaque-types",
llvm::cl::desc("Enable experimental support for named opaque result types"),
llvm::cl::Hidden, llvm::cl::cat(Category), llvm::cl::init(false));
static llvm::cl::opt<bool>
EnableExperimentalDistributed("enable-experimental-distributed",
llvm::cl::desc("Enable experimental distributed actors and functions"),
llvm::cl::init(false));
static llvm::cl::opt<bool> EnableBareSlashRegexLiterals(
"enable-bare-slash-regex",
llvm::cl::desc("Enable the ability to write '/.../' regex literals"),
llvm::cl::init(false));
static llvm::cl::list<std::string>
EnableExperimentalFeatures("enable-experimental-feature",
llvm::cl::desc("Enable an experimental feature"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
EnableUpcomingFeatures("enable-upcoming-feature",
llvm::cl::desc("Enable a feature that will be introduced in an upcoming language version"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
AccessNotesPath("access-notes-path", llvm::cl::desc("Path to access notes file"),
llvm::cl::cat(Category));
static llvm::cl::opt<bool>
AllowCompilerErrors("allow-compiler-errors",
llvm::cl::desc("Whether to attempt to continue despite compiler errors"),
llvm::cl::init(false));
static llvm::cl::list<std::string>
DefineAvailability("define-availability",
llvm::cl::desc("Define a macro for @available"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
SerializedPathObfuscate("serialized-path-obfuscate", llvm::cl::desc("Path to access notes file"),
llvm::cl::cat(Category));
} // namespace options
struct CompletionTestToken {
unsigned Line;
unsigned Column;
unsigned Offset;
StringRef Name;
SmallVector<StringRef, 1> CheckPrefixes;
StringRef Skip;
StringRef Xfail;
std::optional<bool> IncludeKeywords = std::nullopt;
std::optional<bool> IncludeComments = std::nullopt;
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());
}
/// 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;
}
/// Result returned from \c performWithCompletionLikeOperationParams.
struct CompletionLikeOperationParams {
swift::CompilerInvocation &Invocation;
llvm::ArrayRef<const char *> Args;
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem;
llvm::MemoryBuffer *CompletionBuffer;
unsigned int Offset;
swift::DiagnosticConsumer *DiagC;
};
/// Run \p PerformOperation with the parameters that are needed to perform a
/// completion like operation on a \c IDEInspectionInstance. This function will
/// return the same value as \p PerformOperation.
/// In case there was an error setting up the parameters for the operation,
/// this method returns \c true and does not call \p PerformOperation.
static bool performWithCompletionLikeOperationParams(
const CompilerInvocation &InitInvok, StringRef SourceFilename,
StringRef SecondSourceFileName, StringRef CodeCompletionToken,
bool CodeCompletionDiagnostics,
llvm::function_ref<bool(CompletionLikeOperationParams Params)>
PerformOperation) {
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
if (setBufferForFile(SourceFilename, FileBuf))
return true;
SmallVector<CompletionTestToken> Tokens;
std::string Error;
auto CleanFile = removeCodeCompletionTokens(FileBuf.get(), Tokens, Error);
if (!CleanFile) {
llvm::errs() << "error: " << Error << '\n';
return true;
}
unsigned Offset = ~0U;
for (auto Token : Tokens) {
if (Token.Name == CodeCompletionToken) {
Offset = Token.Offset;
break;
}
}
if (Offset == ~0U) {
llvm::errs() << "could not find code completion token \""
<< CodeCompletionToken << "\"\n";
return true;
}
llvm::outs() << "found code completion token " << CodeCompletionToken
<< " at offset " << Offset << "\n";
auto CompletionBuffer = ide::makeCodeCompletionMemoryBuffer(CleanFile.get(), Offset, CleanFile->getBufferIdentifier());
CompilerInvocation Invocation(InitInvok);
if (!SecondSourceFileName.empty()) {
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(
SecondSourceFileName);
}
PrintingDiagnosticConsumer PrintDiags;
CompletionLikeOperationParams Params{Invocation,
/*Args=*/{},
llvm::vfs::getRealFileSystem(),
CompletionBuffer.get(),
Offset,
CodeCompletionDiagnostics ? &PrintDiags
: nullptr};
return PerformOperation(Params);
}
template <typename ResultType>
static int
printResult(CancellableResult<ResultType> Result,
llvm::function_ref<int(const ResultType &)> PrintSuccess) {
switch (Result.getKind()) {
case CancellableResultKind::Success: {
return PrintSuccess(Result.getResult());
}
case CancellableResultKind::Failure:
llvm::errs() << "error: " << Result.getError() << '\n';
return 1;
case CancellableResultKind::Cancelled:
llvm::errs() << "request cancelled\n";
return 1;
break;
}
}
static int printTypeContextInfo(
CancellableResult<TypeContextInfoResult> CancellableResult) {
return printResult<TypeContextInfoResult>(
CancellableResult, [](const TypeContextInfoResult &Result) {
llvm::outs() << "-----BEGIN TYPE CONTEXT INFO-----\n";
for (auto resultItem : Result.Results) {
llvm::outs() << "- TypeName: ";
resultItem.ExpectedTy.print(llvm::outs());
llvm::outs() << "\n";
llvm::outs() << " TypeUSR: ";
printTypeUSR(resultItem.ExpectedTy, llvm::outs());
llvm::outs() << "\n";
llvm::outs() << " ImplicitMembers:";
if (resultItem.ImplicitMembers.empty())
llvm::outs() << " []";
llvm::outs() << "\n";
for (auto VD : resultItem.ImplicitMembers) {
llvm::outs() << " - ";
llvm::outs() << "Name: ";
VD->getName().print(llvm::outs());
llvm::outs() << "\n";
StringRef BriefDoc = VD->getSemanticBriefComment();
if (!BriefDoc.empty()) {
llvm::outs() << " DocBrief: \"";
llvm::outs() << VD->getSemanticBriefComment();
llvm::outs() << "\"\n";
}
}
}
llvm::outs() << "-----END TYPE CONTEXT INFO-----\n";
return 0;
});
}
static int doTypeContextInfo(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
StringRef SecondSourceFileName,
StringRef CodeCompletionToken,
bool CodeCompletionDiagnostics) {
return performWithCompletionLikeOperationParams(
InitInvok, SourceFilename, SecondSourceFileName, CodeCompletionToken,
CodeCompletionDiagnostics,
[&](CompletionLikeOperationParams Params) -> bool {
IDEInspectionInstance IDEInspectionInst(
std::make_shared<PluginRegistry>());
int ExitCode = 2;
IDEInspectionInst.typeContextInfo(
Params.Invocation, Params.Args, Params.FileSystem,
Params.CompletionBuffer, Params.Offset, Params.DiagC,
/*CancellationFlag=*/nullptr,
[&](CancellableResult<TypeContextInfoResult> Result) {
ExitCode = printTypeContextInfo(Result);
});
return ExitCode;
});
}
static int printConformingMethodList(
CancellableResult<ConformingMethodListResults> CancellableResult) {
return printResult<ConformingMethodListResults>(
CancellableResult, [](const ConformingMethodListResults &Results) {
auto Result = Results.Result;
if (!Result) {
return 0;
}
llvm::outs() << "-----BEGIN CONFORMING METHOD LIST-----\n";
llvm::outs() << "- TypeName: ";
Result->ExprType.print(llvm::outs());
llvm::outs() << "\n";
llvm::outs() << "- Members: ";
if (Result->Members.empty())
llvm::outs() << " []";
llvm::outs() << "\n";
for (auto VD : Result->Members) {
auto resultTy = cast<FuncDecl>(VD)->getResultInterfaceType();
resultTy = resultTy.subst(
Result->ExprType->getMemberSubstitutionMap(VD));
llvm::outs() << " - Name: ";
VD->getName().print(llvm::outs());
llvm::outs() << "\n";
llvm::outs() << " TypeName: ";
resultTy.print(llvm::outs());
llvm::outs() << "\n";
StringRef BriefDoc = VD->getSemanticBriefComment();
if (!BriefDoc.empty()) {
llvm::outs() << " DocBrief: \"";
llvm::outs() << VD->getSemanticBriefComment();
llvm::outs() << "\"\n";
}
}
llvm::outs() << "-----END CONFORMING METHOD LIST-----\n";
return 0;
});
}
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());
return performWithCompletionLikeOperationParams(
InitInvok, SourceFilename, SecondSourceFileName, CodeCompletionToken,
CodeCompletionDiagnostics,
[&](CompletionLikeOperationParams Params) -> bool {
IDEInspectionInstance IDEInspectionInst(
std::make_shared<PluginRegistry>());
int ExitCode = 2;
IDEInspectionInst.conformingMethodList(
Params.Invocation, Params.Args, Params.FileSystem,
Params.CompletionBuffer, Params.Offset, Params.DiagC, typeNames,
/*CancellationFlag=*/nullptr,
[&](CancellableResult<ConformingMethodListResults> Result) {
ExitCode = printConformingMethodList(Result);
});
return ExitCode;
});
}
static void printCodeCompletionResultsImpl(
ArrayRef<CodeCompletionResult *> Results, llvm::raw_ostream &OS,
bool IncludeKeywords, bool IncludeComments, bool IncludeSourceText,
bool PrintAnnotatedDescription, const ASTContext *Ctx) {
unsigned NumResults = 0;
for (auto Result : Results) {
if (!IncludeKeywords &&
Result->getKind() == CodeCompletionResultKind::Keyword)
continue;
++NumResults;
}
if (NumResults == 0)
return;
OS << "Begin completions, " << NumResults << " items\n";
for (auto Result : Results) {
if (!IncludeKeywords &&
Result->getKind() == CodeCompletionResultKind::Keyword)
continue;
Result->printPrefix(OS);
if (PrintAnnotatedDescription) {
printCodeCompletionResultDescriptionAnnotated(
*Result, OS, /*leadingPunctuation=*/false);
OS << "; typename=";
printCodeCompletionResultTypeNameAnnotated(*Result, OS);
} else {
Result->getCompletionString()->print(OS);
}
OS << "; name=" << Result->getFilterName();
if (IncludeSourceText) {
OS << "; sourcetext=";
SmallString<64> buf;
{
llvm::raw_svector_ostream bufOS(buf);
printCodeCompletionResultSourceText(*Result, bufOS);
}
OS.write_escaped(buf);
}
StringRef comment = Result->getBriefDocComment();
if (IncludeComments && !comment.empty()) {
OS << "; comment=" << comment;
}
if (Ctx) {
// Only print diagnostics if we have an ASTContext
SmallString<256> Scratch;
auto DiagSeverityAndMessage =
Result->getDiagnosticSeverityAndMessage(Scratch, *Ctx);
if (DiagSeverityAndMessage.first !=
CodeCompletionDiagnosticSeverity::None) {
OS << "; diagnostics=" << comment;
switch (DiagSeverityAndMessage.first) {
case CodeCompletionDiagnosticSeverity::Error:
OS << "error";
break;
case CodeCompletionDiagnosticSeverity::Warning:
OS << "warning";
break;
case CodeCompletionDiagnosticSeverity::Remark:
OS << "remark";
break;
case CodeCompletionDiagnosticSeverity::Note:
OS << "note";
break;
case CodeCompletionDiagnosticSeverity::None:
llvm_unreachable("none");
}
SmallString<256> Scratch;
OS << ":" << DiagSeverityAndMessage.second;
}
}
OS << "\n";
}
OS << "End completions\n";
}
static void
printCodeCompletionLookedupTypeNames(ArrayRef<NullTerminatedStringRef> names,
llvm::raw_ostream &OS) {
if (names.empty())
return;
SmallVector<NullTerminatedStringRef, 2> sortedNames;
sortedNames.append(names.begin(), names.end());
llvm::sort(sortedNames,
[](NullTerminatedStringRef a, NullTerminatedStringRef b) {
return a.compare(b) <= 0;
});
OS << "LookedupTypeNames: [";
llvm::interleave(
sortedNames.begin(), sortedNames.end(),
[&](auto name) { OS << "'" << name << "'"; },
[&]() { OS << ", "; });
OS << "]\n";
}
static int printCodeCompletionResults(
CancellableResult<CodeCompleteResult> CancellableResult,
bool IncludeKeywords, bool IncludeComments, bool IncludeSourceText,
bool PrintAnnotatedDescription) {
llvm::raw_fd_ostream &OS = llvm::outs();
return printResult<CodeCompleteResult>(
CancellableResult, [&](const CodeCompleteResult &Result) {
printCodeCompletionResultsImpl(
Result.ResultSink.Results, OS, IncludeKeywords, IncludeComments,
IncludeSourceText, PrintAnnotatedDescription,
&Result.Info.compilerInstance->getASTContext());
printCodeCompletionLookedupTypeNames(
Result.Info.completionContext->LookedupNominalTypeNames, OS);
return 0;
});
}
static int
doCodeCompletion(const CompilerInvocation &InitInvok, StringRef SourceFilename,
StringRef SecondSourceFileName, StringRef CodeCompletionToken,
bool CodeCompletionDiagnostics, bool CodeCompletionKeywords,
bool CodeCompletionComments,
bool CodeCompletionAnnotateResults,
bool CodeCompletionAddInitsToTopLevel,
bool CodeCompletionAddCallWithNoDefaultArgs,
bool CodeCompletionSourceText) {
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);
CompletionContext.setAddInitsToTopLevel(CodeCompletionAddInitsToTopLevel);
CompletionContext.setAddCallWithNoDefaultArgs(
CodeCompletionAddCallWithNoDefaultArgs);
return performWithCompletionLikeOperationParams(
InitInvok, SourceFilename, SecondSourceFileName, CodeCompletionToken,
CodeCompletionDiagnostics,
[&](CompletionLikeOperationParams Params) -> bool {
IDEInspectionInstance Inst(std::make_shared<PluginRegistry>());
int ExitCode = 2;
Inst.codeComplete(
Params.Invocation, Params.Args, Params.FileSystem,
Params.CompletionBuffer, Params.Offset, Params.DiagC,
CompletionContext, /*CancellationFlag=*/nullptr,
[&](CancellableResult<CodeCompleteResult> Result) {
ExitCode = printCodeCompletionResults(
Result, CodeCompletionKeywords, CodeCompletionComments,
CodeCompletionSourceText, CodeCompletionAnnotateResults);
});
return ExitCode;
});
}
static int doBatchCodeCompletion(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
bool CodeCompletionDiagnostics,
bool CodeCompletionKeywords,
bool CodeCompletionComments,
bool CodeCompletionAnnotateResults,
bool CodeCompletionAddInitsToTopLevel,
bool CodeCompletionAddCallWithNoDefaultArgs,
bool CodeCompletionSourceText) {
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;
std::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();
IDEInspectionInstance IDEInspectionInst(std::make_shared<PluginRegistry>());
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;
SmallVector<std::string, 4> expandedCheckPrefixes;
llvm::errs() << "----\n";
llvm::errs() << "Token: " << Token.Name << "; offset=" << Token.Offset
<< "; pos=" << Token.Line << ":" << Token.Column;
for (auto joinedPrefix : Token.CheckPrefixes) {
if (options::FileCheckSuffix.empty()) {
// Simple case: just copy what we have
expandedCheckPrefixes.push_back(joinedPrefix.str());
} else {
// For each comma-separated prefix, insert a variant with the suffix
// added to it: "X,Y" with suffix "_FOO" -> "X,X_FOO,Y,Y_FOO"
std::string expandedPrefix;
llvm::raw_string_ostream os(expandedPrefix);
SmallVector<StringRef, 4> splitPrefix;
joinedPrefix.split(splitPrefix, ',');
llvm::interleaveComma(splitPrefix, os, [&](StringRef prefix) {
os << prefix << ',' << prefix << options::FileCheckSuffix;
});
expandedCheckPrefixes.push_back(expandedPrefix);
}
llvm::errs() << "; check=" << expandedCheckPrefixes.back();
}
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;
auto IncludeSourceText = CodeCompletionSourceText;
// TODO: Implement per token 'sourcetext' option.
// 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());
ide::CodeCompletionContext CompletionContext(CompletionCache);
CompletionContext.setAnnotateResult(CodeCompletionAnnotateResults);
CompletionContext.setAddInitsToTopLevel(CodeCompletionAddInitsToTopLevel);
CompletionContext.setAddCallWithNoDefaultArgs(
CodeCompletionAddCallWithNoDefaultArgs);
PrintingDiagnosticConsumer PrintDiags;
auto completionStart = std::chrono::high_resolution_clock::now();
bool wasASTContextReused = false;
std::string completionError = "";
bool CallbackCalled = false;
IDEInspectionInst.codeComplete(
Invocation, /*Args=*/{}, FileSystem, completionBuffer.get(), Offset,
CodeCompletionDiagnostics ? &PrintDiags : nullptr, CompletionContext,
/*CancellationFlag=*/nullptr,
[&](CancellableResult<CodeCompleteResult> Result) {
CallbackCalled = true;
switch (Result.getKind()) {
case CancellableResultKind::Success: {
wasASTContextReused =
Result->Info.completionContext->ReusingASTContext;
printCodeCompletionResultsImpl(
Result->ResultSink.Results, OS, IncludeKeywords,
IncludeComments, IncludeSourceText,
CodeCompletionAnnotateResults,
&Result->Info.compilerInstance->getASTContext());
printCodeCompletionLookedupTypeNames(
Result->Info.completionContext->LookedupNominalTypeNames, OS);
break;
}
case CancellableResultKind::Failure:
completionError = "error: " + Result.getError();
break;
case CancellableResultKind::Cancelled:
completionError = "request cancelled";
break;
}
});
assert(CallbackCalled &&
"We should always receive a callback call for code completion");
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);
if (completionError == "error: did not find code completion token") {
// Do not consider failure to find the code completion token as a critical
// failure that returns a non-zero exit code. Instead, allow the caller to
// match the error message.
fileOut << completionError;
} else if (!completionError.empty()) {
llvm::errs() << completionError << '\n';
return 1;
} else {
fileOut << ResultStr;
}
fileOut.close();
if (options::SkipFileCheck)
continue;
assert(!options::FileCheckPath.empty());
bool isFileCheckFailed = false;
for (auto Prefix : expandedCheckPrefixes) {
StringRef FileCheckArgs[] = {options::FileCheckPath, SourceFilename,
"--check-prefixes", Prefix,
"--input-file", resultFilename};
int result =
llvm::sys::ExecuteAndWait(options::FileCheckPath, FileCheckArgs,
/*Env=*/std::nullopt,
/*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";
llvm::errs() << "Code completion results:\n";
SmallVector<StringRef, 20> ResultLines;
llvm::SplitString(ResultStr, ResultLines, "\n");
for (size_t LineNo = 0;
LineNo < std::min(ResultLines.size(), static_cast<size_t>(20));
LineNo++) {
llvm::errs() << ResultLines[LineNo] << '\n';
}
if (ResultLines.size() > 20) {
llvm::errs() << " + " << (ResultLines.size() - 20) << " more lines\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::sort(FailedTokens);
llvm::interleave(
FailedTokens, [&](StringRef name) { llvm::errs() << name; },
[&]() { llvm::errs() << ", "; });
llvm::errs() << "\n";
}
if (!UPassTokens.empty()) {
llvm::errs() << "Unexpected passes: ";
llvm::sort(UPassTokens);
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.ends_with("\n"))
BufferText = BufferText.drop_back(1);
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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,
[&](ModuleDecl *M, auto addFile) {
auto bufferID = ctx.SourceMgr.addMemBufferCopy("// nothing\n");
addFile(new (ctx) SourceFile(*M, SourceFileKind::Main, bufferID));
});
auto *SF = &M->getMainSourceFile();
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).starts_with("// 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::Operator: 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::Operator: Col = llvm::raw_ostream::MAGENTA; break;
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;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
if (RunTypeChecker) {
CompilerInstance CI;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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));
ParserUnit Parser(SM, SourceFileKind::Main, BufferID,
Invocation.getLangOptions(), Invocation.getModuleName());
registerTypeCheckerRequestFunctions(Parser.getParser().Context.evaluator);
registerClangImporterRequestFunctions(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);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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().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));
ParserUnit Parser(SM, SourceFileKind::Main, BufferID,
Invocation.getLangOptions(), Invocation.getModuleName());
registerTypeCheckerRequestFunctions(
Parser.getParser().Context.evaluator);
registerClangImporterRequestFunctions(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) {
if (!Entity.Range.isValid())
return;
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()->isNonUserModule();
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.isNonUserModule())
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.isNonUserModule())
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);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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(const CompilerInvocation &InitInvok,
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,
InitInvok.getLangOptions())
.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);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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::FullyQualifiedTypes,
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);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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::printDeclarations();
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(M->getASTContext());
std::string MangledName = Mangler.mangleTypeForDebugger(
LTD->getDeclaredInterfaceType(),
LTD->getInnermostDeclContext()->getGenericSignatureOfContext());
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 mangling = Demangle::mangleNode(typeNode, Mangle::ManglingFlavor::Default);
if (!mangling.isSuccess()) {
llvm::errs() << "Couldn't remangle type (failed at Node "
<< mangling.error().node << " with error "
<< mangling.error().code << ":" << mangling.error().line
<< ")\n";
return EXIT_FAILURE;
}
auto remangled = mangling.result();
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::printDeclarations();
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,
std::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,
std::optional<BracketOptions> Bracket) override {
if (isa<ProtocolDecl>(D)) {
InProtocol = false;
}
OS << "</decl>";
}
void printStructurePre(PrintStructureKind Kind, const Decl *D) override {
if (D)
printDeclPre(D, std::nullopt);
}
void printStructurePost(PrintStructureKind Kind, const Decl *D) override {
if (D)
printDeclPost(D, std::nullopt);
}
void
printSynthesizedExtensionPre(const ExtensionDecl *ED,
TypeOrExtensionDecl Target,
std::optional<BracketOptions> Bracket) override {
if (Bracket.has_value() && !Bracket.value().shouldOpenExtension(ED))
return;
OS << "<synthesized>";
}
void printSynthesizedExtensionPost(
const ExtensionDecl *ED, TypeOrExtensionDecl Target,
std::optional<BracketOptions> Bracket) override {
if (Bracket.has_value() && !Bracket.value().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->isPrivateSystemDecl()) {
StringRef Name = VD->getGroupName().has_value() ?
VD->getGroupName().value() : "";
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);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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;
swift::getTopLevelDeclsForDisplay(M, Results);
for (auto R : Results) {
Printer.addDecl(R);
}
}
}
return ExitCode;
}
static void printModuleMetadata(ModuleDecl *MD) {
auto &OS = llvm::outs();
OS << "user module version: " << MD->getUserModuleVersion().getAsString() << "\n";
OS << "fingerprint=" << MD->getFingerprint().getRawValue() << "\n";
MD->collectLinkLibraries([&](LinkLibrary lib) {
OS << "link library: " << lib.getName()
<< ", force load: " << (lib.shouldForceLoad() ? "true" : "false") << "\n";
});
MD->collectBasicSourceFileInfo([&](const BasicSourceFileInfo &info) {
OS << "filepath=" << info.getFilePath() << "; ";
OS << "hash=" << info.getInterfaceHashIncludingTypeMembers().getRawValue() << "; ";
OS << "hashExcludingTypeMembers="
<< info.getInterfaceHashExcludingTypeMembers().getRawValue() << "; ";
OS << "mtime=" << info.getLastModified() << "; ";
OS << "size=" << info.getFileSize();
OS << "\n";
});
MD->collectSerializedSearchPath([&](StringRef path) {
OS << "searchpath=" << path << ";\n";
});
for (auto name: MD->getAllowableClientNames()) {
OS <<"allowable client: " << name << ";\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);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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);
// Read everything from loaded modules, including internal details to
// test the behavior around non-public access levels.
Invocation.getLangOptions().EnableDeserializationSafety = false;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
auto &Context = CI.getASTContext();
// Load implicit imports so that Clang importer can use it.
for (auto unloadedImport :
CI.getMainModule()->getImplicitImportInfo().AdditionalUnloadedImports) {
(void)Context.getModule(unloadedImport.module.getModulePath());
}
// If needed, load _Concurrency library so that the Clang importer can use it.
if (Context.LangOpts.EnableExperimentalConcurrency) {
if (!getModuleByFullName(Context, Context.Id_Concurrency)) {
llvm::errs() << "Failed loading _Concurrency library\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,
StringRef SourceFilename,
const std::vector<std::string> HeadersToPrint,
const PrintOptions &Options, bool AnnotatePrint) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addPrimaryInputFile(
SourceFilename);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
auto &Context = CI.getASTContext();
// Load implicit imports so that Clang importer can use it.
for (auto unloadedImport :
CI.getMainModule()->getImplicitImportInfo().AdditionalUnloadedImports) {
(void)Context.getModule(unloadedImport.module.getModulePath());
}
auto &FEOpts = Invocation.getFrontendOptions();
if (!FEOpts.ImplicitObjCHeaderPath.empty()) {
auto &Importer = static_cast<ClangImporter &>(
*Context.getClangModuleLoader());
Importer.importBridgingHeader(FEOpts.ImplicitObjCHeaderPath,
CI.getMainModule(),
/*diagLoc=*/{},
/*trackParsedSymbols=*/true);
}
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 HeaderToPrint : HeadersToPrint) {
if (HeaderToPrint.empty()) {
ExitCode = 1;
continue;
}
printHeaderInterface(HeaderToPrint, Context, *Printer, Options);
}
return ExitCode;
}
static int doPrintSwiftFileInterface(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
bool AnnotatePrint) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addPrimaryInputFile(
SourceFilename);
Invocation.getLangOptions().AttachCommentsToDecls = true;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
std::unique_ptr<ASTPrinter> Printer;
if (AnnotatePrint)
Printer.reset(new AnnotatingPrinter(llvm::outs()));
else
Printer.reset(new StreamPrinter(llvm::outs()));
PrintOptions Options = PrintOptions::printSwiftFileInterface(
InitInvok.getFrontendOptions().PrintFullConvention);
if (options::PrintOriginalSourceText)
Options.PrintOriginalSourceText = true;
printSwiftSourceInterface(*CI.getPrimarySourceFile(), *Printer, Options);
return 0;
}
static int doPrintDecls(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
ArrayRef<std::string> DeclsToPrint,
const PrintOptions &Options,
bool AnnotatePrint) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addPrimaryInputFile(
SourceFilename);
Invocation.getLangOptions().AttachCommentsToDecls = true;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
std::unique_ptr<ASTPrinter> Printer;
if (AnnotatePrint)
Printer.reset(new AnnotatingPrinter(llvm::outs()));
else
Printer.reset(new StreamPrinter(llvm::outs()));
for (const auto &name : DeclsToPrint) {
ASTContext &ctx = CI.getASTContext();
auto descriptor =
UnqualifiedLookupDescriptor(DeclNameRef(ctx.getIdentifier(name)),
CI.getPrimarySourceFile());
auto lookup = evaluateOrDefault(ctx.evaluator,
UnqualifiedLookupRequest{descriptor}, {});
for (auto result : lookup) {
result.getValueDecl()->print(*Printer, Options);
if (auto typeDecl = dyn_cast<TypeDecl>(result.getValueDecl())) {
if (auto typeAliasDecl = dyn_cast<TypeAliasDecl>(typeDecl)) {
TypeDecl *origTypeDecl = typeAliasDecl->getDeclaredInterfaceType()
->getAnyNominal();
if (origTypeDecl) {
origTypeDecl->print(*Printer, Options);
typeDecl = origTypeDecl;
}
}
// Print extensions.
if (auto nominal = dyn_cast<NominalTypeDecl>(typeDecl)) {
for (auto extension : nominal->getExtensions()) {
extension->print(*Printer, Options);
}
}
}
}
}
return 0;
}
namespace {
class ASTTypePrinter : public ASTWalker {
raw_ostream &OS;
SourceManager &SM;
const PrintOptions &Options;
unsigned IndentLevel = 0;
public:
ASTTypePrinter(SourceManager &SM, const PrintOptions &Options)
: OS(llvm::outs()), SM(SM), Options(Options) {}
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Expansion;
}
PreWalkAction walkToDeclPre(Decl *D) override {
if (auto *VD = dyn_cast<ValueDecl>(D)) {
OS.indent(IndentLevel * 2);
OS << Decl::getKindName(VD->getKind()) << "Decl '''"
<< VD->getBaseName() << "''' ";
VD->getInterfaceType().print(OS, Options);
OS << "\n";
}
IndentLevel++;
return Action::Continue();
}
PostWalkAction walkToDeclPost(Decl *D) override {
IndentLevel--;
return Action::Continue();
}
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
StringRef SourceCode{ "<unknown>" };
unsigned Line = ~0U;
SourceRange SR = E->getSourceRange();
if (SR.isValid()) {
unsigned BufferID = SM.findBufferContainingLoc(SR.Start);
SourceLoc EndCharLoc = Lexer::getLocForEndOfToken(SM, SR.End);
SourceCode = SM.extractText({ SR.Start,
SM.getByteDistance(SR.Start, EndCharLoc) });
unsigned Column;
std::tie(Line, Column) =
SM.getPresumedLineAndColumnForLoc(SR.Start, BufferID);
}
OS.indent(IndentLevel * 2);
OS << Expr::getKindName(E->getKind()) << "Expr";
if (Line != ~0U)
OS << ":" << Line;
OS << " '''" << SourceCode << "''' ";
E->getType().print(OS, Options);
OS << "\n";
IndentLevel++;
return Action::Continue(E);
}
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
IndentLevel--;
return Action::Continue(E);
}
};
} // unnamed namespace
static int doPrintTypes(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
bool FullyQualifiedTypes) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
PrintOptions Options = PrintOptions::printDeclarations();
Options.FullyQualifiedTypes = FullyQualifiedTypes;
ASTTypePrinter Printer(CI.getSourceMgr(), Options);
CI.getMainModule()->walk(Printer);
return 0;
}
namespace {
class ASTDocCommentDumper : public ASTWalker {
raw_ostream &OS;
public:
ASTDocCommentDumper() : OS(llvm::outs()) {}
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Expansion;
}
PreWalkAction walkToDeclPre(Decl *D) override {
if (D->isImplicit())
return Action::Continue();
swift::markup::MarkupContext MC;
auto DC = getSingleDocComment(MC, D);
if (DC)
swift::markup::dump(DC->getDocument(), OS);
return Action::Continue();
}
};
} // end anonymous namespace
namespace {
class ASTCommentPrinter : public ASTWalker {
raw_ostream &OS;
SourceManager &SM;
XMLValidator &TheXMLValidator;
public:
ASTCommentPrinter(SourceManager &SM, XMLValidator &TheXMLValidator)
: OS(llvm::outs()), SM(SM), TheXMLValidator(TheXMLValidator) {}
void printWithEscaping(StringRef Str) {
for (char C : Str) {
switch (C) {
case '\n': OS << "\\n"; break;
case '\r': OS << "\\r"; break;
case '\t': OS << "\\t"; break;
case '\v': OS << "\\v"; break;
case '\f': OS << "\\f"; break;
default: OS << C; break;
}
}
}
void printDeclName(const ValueDecl *VD) {
if (auto *NTD = dyn_cast<NominalTypeDecl>(VD->getDeclContext())) {
Identifier Id = NTD->getName();
if (!Id.empty())
OS << Id.str() << ".";
}
DeclBaseName Name = VD->getBaseName();
if (!Name.empty()) {
OS << Name;
return;
}
if (auto accessor = dyn_cast<AccessorDecl>(VD)) {
auto *storage = accessor->getStorage();
switch (accessor->getAccessorKind()) {
case AccessorKind::Get:
OS << "<getter for ";
break;
case AccessorKind::DistributedGet:
OS << "<_distributed_getter for ";
break;
case AccessorKind::Set:
OS << "<setter for ";
break;
case AccessorKind::WillSet:
OS << "<willSet for ";
break;
case AccessorKind::DidSet:
OS << "<didSet for ";
break;
case AccessorKind::Address:
OS << "<addressor for ";
break;
case AccessorKind::MutableAddress:
OS << "<mutableAddressor for ";
break;
case AccessorKind::Read:
OS << "<read accessor for ";
break;
case AccessorKind::Read2:
OS << "<read2 accessor for ";
break;
case AccessorKind::Modify:
OS << "<modify accessor for ";
break;
case AccessorKind::Modify2:
OS << "<modify2 accessor for ";
break;
case AccessorKind::Init:
OS << "init accessor for ";
break;
}
printDeclName(storage);
OS << ">";
return;
}
OS << "<anonymous>";
}
void printRawComment(const RawComment &RC) {
OS << "RawComment=";
if (RC.isEmpty()) {
OS << "none";
return;
}
OS << "[";
for (auto &SRC : RC.Comments)
printWithEscaping(SRC.RawText);
OS << "]";
}
void printBriefComment(StringRef Brief) {
OS << "BriefComment=";
if (Brief.empty()) {
OS << "none";
return;
}
OS << "[";
printWithEscaping(Brief);
OS << "]";
}
void printDocComment(const Decl *D) {
std::string XML;
{
llvm::raw_string_ostream OS(XML);
getDocumentationCommentAsXML(D, OS);
}
OS << "DocCommentAsXML=";
if (XML.empty()) {
OS << "none";
return;
}
OS << "[";
printWithEscaping(XML);
OS << "]";
auto Status = TheXMLValidator.validate(XML);
switch (Status.Code) {
case XMLValidator::ErrorCode::Valid:
OS << " CommentXMLValid";
break;
case XMLValidator::ErrorCode::NotCompiledIn:
OS << " ValidationSkipped=[libxml is missing]";
break;
case XMLValidator::ErrorCode::NoSchema:
OS << " ValidationSkipped=[schema is not set]";
break;
case XMLValidator::ErrorCode::BadSchema:
OS << " CommentXMLInvalid=[bad schema file]";
break;
case XMLValidator::ErrorCode::NotWellFormed:
OS << " CommentXMLInvalid=[not well-formed XML: " << Status.Message
<< "]";
break;
case XMLValidator::ErrorCode::NotValid:
OS << " CommentXMLInvalid=[not valid XML: " << Status.Message << "]";
break;
case XMLValidator::ErrorCode::InternalError:
OS << " CommentXMLInvalid=[libxml error]";
break;
}
}
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Expansion;
}
PreWalkAction walkToDeclPre(Decl *D) override {
if (D->isImplicit())
return Action::Continue();
if (auto *VD = dyn_cast<ValueDecl>(D)) {
SourceLoc Loc = D->getLoc(/*SerializedOK=*/true);
if (Loc.isValid()) {
auto LineAndColumn = SM.getPresumedLineAndColumnForLoc(Loc);
OS << SM.getDisplayNameForLoc(Loc)
<< ":" << LineAndColumn.first << ":" << LineAndColumn.second << ": ";
}
OS << Decl::getKindName(VD->getKind()) << "/";
printDeclName(VD);
OS << " ";
printRawComment(D->getRawComment());
OS << " ";
printBriefComment(D->getSemanticBriefComment());
OS << " ";
printDocComment(D);
OS << "\n";
} else if (isa<ExtensionDecl>(D)) {
SourceLoc Loc = D->getLoc(/*SerializedOK=*/true);
if (Loc.isValid()) {
auto LineAndColumn = SM.getPresumedLineAndColumnForLoc(Loc);
OS << SM.getDisplayNameForLoc(Loc)
<< ":" << LineAndColumn.first << ":" << LineAndColumn.second << ": ";
}
OS << Decl::getKindName(D->getKind()) << "/";
OS << " ";
printRawComment(D->getRawComment());
OS << " ";
printBriefComment(D->getSemanticBriefComment());
OS << " ";
printDocComment(D);
OS << "\n";
}
return Action::Continue();
}
};
} // unnamed namespace
static int doDumpComments(const CompilerInvocation &InitInvok,
StringRef SourceFilename) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
Invocation.getLangOptions().AttachCommentsToDecls = true;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
ASTDocCommentDumper Dumper;
CI.getMainModule()->walk(Dumper);
llvm::outs() << "\n";
return 0;
}
static int doPrintComments(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
StringRef CommentsXMLSchema) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
Invocation.getLangOptions().AttachCommentsToDecls = true;
Invocation.getLangOptions().EnableObjCAttrRequiresFoundation = false;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
XMLValidator TheXMLValidator;
TheXMLValidator.setSchema(CommentsXMLSchema);
ASTCommentPrinter Printer(CI.getSourceMgr(), TheXMLValidator);
CI.getMainModule()->walk(Printer);
return 0;
}
static int doPrintModuleComments(const CompilerInvocation &InitInvok,
const std::vector<std::string> ModulesToPrint,
StringRef CommentsXMLSchema) {
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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)
return 1;
XMLValidator TheXMLValidator;
TheXMLValidator.setSchema(CommentsXMLSchema);
ASTCommentPrinter Printer(CI.getSourceMgr(), TheXMLValidator);
int ExitCode = 0;
for (StringRef ModuleToPrint : ModulesToPrint) {
auto *M = getModuleByFullName(Context, ModuleToPrint);
if (!M) {
ExitCode = -1;
continue;
}
M->walk(Printer);
}
return ExitCode;
}
static int doPrintModuleImports(const CompilerInvocation &InitInvok,
const std::vector<std::string> ModulesToPrint) {
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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)
return 1;
int ExitCode = 0;
for (StringRef ModuleToPrint : ModulesToPrint) {
auto *M = getModuleByFullName(Context, ModuleToPrint);
if (!M) {
ExitCode = -1;
continue;
}
SmallVector<ImportedModule, 16> scratch;
for (auto next : namelookup::getAllImports(M)) {
llvm::outs() << next.importedModule->getName();
if (next.importedModule->isNonSwiftModule())
llvm::outs() << " (Clang)";
llvm::outs() << ":\n";
scratch.clear();
next.importedModule->getImportedModules(
scratch, ModuleDecl::ImportFilterKind::Exported);
// FIXME: ImportFilterKind::ShadowedByCrossImportOverlay?
for (auto &import : scratch) {
llvm::outs() << "\t" << import.importedModule->getName();
for (auto accessPathPiece : import.accessPath) {
llvm::outs() << "." << accessPathPiece.Item;
}
if (import.importedModule->isNonSwiftModule())
llvm::outs() << " (Clang)";
llvm::outs() << "\n";
}
}
}
return ExitCode;
}
//===----------------------------------------------------------------------===//
// Print type interfaces.
//===----------------------------------------------------------------------===//
static int doPrintTypeInterface(const CompilerInvocation &InitInvok,
const StringRef FileName,
const StringRef LCPair) {
auto Pair = parseLineCol(LCPair);
if (!Pair.has_value())
return 1;
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(FileName);
CompilerInstance CI;
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
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?");
SourceManager &SM = SF->getASTContext().SourceMgr;
auto Offset = SM.resolveFromLineCol(BufID, Pair.value().first,
Pair.value().second);
if (!Offset.has_value()) {
llvm::errs() << "Cannot resolve source location.\n";
return 1;
}
SourceLoc Loc = Lexer::getLocForStartOfToken(SM, BufID, Offset.value());
auto CursorInfo = evaluateOrDefault(
SF->getASTContext().evaluator,
CursorInfoRequest{CursorInfoOwner(SF, Loc)}, new ResolvedCursorInfo());
auto SemaT = dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo);
if (!SemaT) {
llvm::errs() << "Cannot find sema token at the given location.\n";
return 1;
}
Type Ty = SemaT->getSolutionSpecificInterfaceType();
if (Ty.isNull()) {
Ty = SemaT->getValueD()->getInterfaceType();
}
if (Ty.isNull()) {
llvm::errs() << "Cannot get type of the sema token.\n";
return 1;
}
StreamPrinter Printer(llvm::outs());
std::string Error;
std::string TypeName;
if (printTypeInterface(
SemaT->getValueD()->getDeclContext()->getParentModule(), Ty, Printer,
TypeName, Error)) {
llvm::errs() << Error;
return 1;
}
return 0;
}
static int doPrintTypeInterfaceForTypeUsr(const CompilerInvocation &InitInvok,
const StringRef FileName,
const StringRef Usr) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(FileName);
CompilerInstance CI;
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
DeclContext *DC = CI.getMainModule()->getModuleContext();
assert(DC && "no decl context?");
StreamPrinter Printer(llvm::outs());
std::string TypeName;
std::string Error;
if (printTypeInterface(DC->getParentModule(), Usr, Printer, TypeName,
Error)) {
llvm::errs() << Error;
return 1;
}
return 0;
}
//===----------------------------------------------------------------------===//
// Print USRs
//===----------------------------------------------------------------------===//
namespace {
class USRPrinter : public SourceEntityWalker {
SourceManager &SM;
unsigned BufferID;
llvm::raw_ostream &OS;
public:
USRPrinter(SourceManager &SM, unsigned BufferID, llvm::raw_ostream &OS)
: SM(SM), BufferID(BufferID), OS(OS) { }
private:
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
if (auto *VD = dyn_cast<ValueDecl>(D))
printUSR(VD, Range.getStart());
return true;
}
bool walkToExprPre(Expr *E) override {
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
printUSR(DRE->getDecl(), E->getLoc());
return true;
}
void printUSR(const ValueDecl *VD, SourceLoc Loc) {
printLoc(Loc);
OS << ' ';
if (ide::printValueDeclUSR(VD, OS))
OS << "ERROR:no-usr";
OS << '\n';
}
void printLoc(SourceLoc Loc) {
if (Loc.isValid()) {
auto LineCol = SM.getPresumedLineAndColumnForLoc(Loc, BufferID);
OS << LineCol.first << ':' << LineCol.second;
}
}
bool shouldWalkIntoGenericParams() override {
return false;
}
};
} // unnamed namespace
//===----------------------------------------------------------------------===//
// Print reconstructed type from mangled names.
//===----------------------------------------------------------------------===//
class TypeReconstructWalker : public SourceEntityWalker {
ASTContext &Ctx;
llvm::raw_ostream &Stream;
llvm::DenseSet<ValueDecl *> SeenDecls;
llvm::SmallVector<DeclContext *, 2> NestedDCs;
public:
TypeReconstructWalker(ASTContext &Ctx, llvm::raw_ostream &Stream)
: Ctx(Ctx), Stream(Stream) {}
bool walkToDeclPre(Decl *D, CharSourceRange range) override {
if (auto *VD = dyn_cast<ValueDecl>(D)) {
if (SeenDecls.insert(VD).second)
tryDemangleDecl(VD, range, /*isRef=*/false);
NestedDCs.push_back(VD->getInnermostDeclContext());
}
return true;
}
bool walkToDeclPost(Decl *D) override {
if (isa<ValueDecl>(D))
NestedDCs.pop_back();
return true;
}
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T,
ReferenceMetaData Data) override {
if (SeenDecls.insert(D).second)
tryDemangleDecl(D, Range, /*isRef=*/true);
if (T) {
T = T->getRValueType();
tryDemangleType(T->mapTypeOutOfContext(),
(NestedDCs.empty()
? D->getDeclContext()
: NestedDCs.back()),
Range);
}
return true;
}
private:
void tryDemangleType(Type T, const DeclContext *DC, CharSourceRange range) {
Mangle::ASTMangler Mangler(Ctx);
auto sig = DC->getGenericSignatureOfContext();
std::string mangledName(Mangler.mangleTypeForDebugger(T, sig));
Type ReconstructedType = DC->mapTypeIntoContext(
Demangle::getTypeForMangling(Ctx, mangledName));
Stream << "type: ";
if (ReconstructedType) {
ReconstructedType->print(Stream);
} else {
Stream << "FAILURE";
}
Stream << "\tfor '" << range.str() << "' mangled=" << mangledName << "\n";
}
void tryDemangleDecl(ValueDecl *VD, CharSourceRange range, bool isRef) {
if (!isa<TypeDecl>(VD) || isa<GenericTypeParamDecl>(VD))
return;
std::string USR;
{
llvm::raw_string_ostream OS(USR);
printValueDeclUSR(VD, OS);
}
std::string error;
if (isRef) {
Stream << "dref: ";
} else {
Stream << "decl: ";
}
if (TypeDecl *reDecl = Demangle::getTypeDeclForUSR(Ctx, USR)) {
PrintOptions POpts;
POpts.PreferTypeRepr = false;
POpts.PrintParameterSpecifiers = true;
reDecl->print(Stream, POpts);
} else {
Stream << "FAILURE";
}
Stream << "\tfor '" << range.str() << "' usr=" << USR << "\n";
}
};
static int doReconstructType(const CompilerInvocation &InitInvok,
StringRef SourceFilename) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
Invocation.getLangOptions().DisableAvailabilityChecking = false;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
SourceFile *SF = nullptr;
for (auto Unit : CI.getMainModule()->getFiles()) {
SF = dyn_cast<SourceFile>(Unit);
if (SF)
break;
}
assert(SF && "no source file?");
TypeReconstructWalker Walker(SF->getASTContext(), llvm::outs());
Walker.walk(SF);
return 0;
}
static int doPrintRangeInfo(const CompilerInvocation &InitInvok,
StringRef SourceFileName,
StringRef StartPos,
StringRef EndPos) {
auto StartOp = parseLineCol(StartPos);
auto EndOp = parseLineCol(EndPos);
if (!StartOp.has_value() || !EndOp.has_value())
return 1;
auto StartLineCol = StartOp.value();
auto EndLineCol = EndOp.value();
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFileName);
Invocation.getLangOptions().DisableAvailabilityChecking = false;
Invocation.getLangOptions().CollectParsedToken = true;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
SourceFile *SF = nullptr;
for (auto Unit : CI.getMainModule()->getFiles()) {
SF = dyn_cast<SourceFile>(Unit);
if (SF)
break;
}
assert(SF && "no source file?");
SourceManager &SM = SF->getASTContext().SourceMgr;
unsigned bufferID = SF->getBufferID();
SourceLoc StartLoc = SM.getLocForLineCol(bufferID, StartLineCol.first,
StartLineCol.second);
SourceLoc EndLoc = SM.getLocForLineCol(bufferID, EndLineCol.first,
EndLineCol.second);
ResolvedRangeInfo Result = evaluateOrDefault(SF->getASTContext().evaluator,
RangeInfoRequest(RangeInfoOwner({SF, StartLoc, EndLoc})), ResolvedRangeInfo());
Result.print(llvm::outs());
return 0;
}
namespace {
class PrintIndexDataConsumer : public IndexDataConsumer {
raw_ostream &OS;
bool shouldIndexLocals;
bool firstSourceEntity = true;
void printSymbolInfo(SymbolInfo SymInfo) {
OS << getSymbolKindString(SymInfo.Kind);
if (SymInfo.SubKind != SymbolSubKind::None)
OS << '/' << getSymbolSubKindString(SymInfo.SubKind);
if (SymInfo.Properties) {
OS << '(';
printSymbolProperties(SymInfo.Properties, OS);
OS << ')';
}
OS << '/' << getSymbolLanguageString(SymInfo.Lang);
}
public:
PrintIndexDataConsumer(raw_ostream &OS, bool indexLocals = false) :
OS(OS), shouldIndexLocals(indexLocals) {}
bool indexLocals() override { return shouldIndexLocals; }
void failed(StringRef error) override {}
bool startDependency(StringRef name, StringRef path, bool isClangModule,
bool isSystem) override {
OS << (isClangModule ? "clang-module" : "module") << " | ";
OS << (isSystem ? "system" : "user") << " | ";
OS << name << " | " << path << "\n";
return true;
}
bool finishDependency(bool isClangModule) override {
return true;
}
Action startSourceEntity(const IndexSymbol &symbol) override {
if (firstSourceEntity) {
firstSourceEntity = false;
OS << "------------\n";
}
OS << symbol.line << ':' << symbol.column << " | ";
printSymbolInfo(symbol.symInfo);
OS << " | " << symbol.name << " | " << symbol.USR << " | ";
clang::index::printSymbolRoles(symbol.roles, OS);
OS << " | rel: " << symbol.Relations.size() << "\n";
for (auto Relation : symbol.Relations) {
OS << " ";
clang::index::printSymbolRoles(Relation.roles, OS);
OS << " | ";
printSymbolInfo(Relation.symInfo);
OS << " | " << Relation.name << " | " << Relation.USR << "\n";
}
return Continue;
}
bool finishSourceEntity(SymbolInfo symInfo, SymbolRoleSet roles) override {
return true;
}
void finish() override {}
};
} // anonymous namespace
static int doPrintIndexedSymbols(const CompilerInvocation &InitInvok,
StringRef SourceFileName, bool indexLocals) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFileName);
Invocation.getLangOptions().DisableAvailabilityChecking = false;
Invocation.getLangOptions().TypoCorrectionLimit = 0;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
SourceFile *SF = nullptr;
for (auto Unit : CI.getMainModule()->getFiles()) {
SF = dyn_cast<SourceFile>(Unit);
if (SF)
break;
}
assert(SF && "no source file?");
llvm::outs() << llvm::sys::path::filename(SF->getFilename()) << '\n';
llvm::outs() << "------------\n";
PrintIndexDataConsumer consumer(llvm::outs(), indexLocals);
indexSourceFile(SF, consumer);
return 0;
}
static int doPrintIndexedSymbolsFromModule(const CompilerInvocation &InitInvok,
StringRef ModuleName) {
CompilerInvocation Invocation(InitInvok);
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
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;
}
auto *M = getModuleByFullName(Context, ModuleName);
if (!M) {
llvm::errs() << "Failed loading " << ModuleName << "\n";
return 1;
}
PrintIndexDataConsumer consumer(llvm::outs());
indexModule(M, consumer);
return 0;
}
static int doPrintUSRs(const CompilerInvocation &InitInvok,
StringRef SourceFilename) {
CompilerInvocation Invocation(InitInvok);
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
ClangImporterOptions &ImporterOpts = Invocation.getClangImporterOptions();
ImporterOpts.DetailedPreprocessingRecord = true;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
std::string InstanceSetupError;
if (CI.setup(Invocation, InstanceSetupError)) {
llvm::errs() << InstanceSetupError << '\n';
return 1;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
unsigned BufID = CI.getInputBufferIDs().back();
USRPrinter Printer(CI.getSourceMgr(), BufID, llvm::outs());
Printer.walk(*CI.getMainModule());
return 0;
}
static int doTestCreateCompilerInvocation(StringRef DriverPath,
ArrayRef<const char *> Args,
bool ForceNoOutputs) {
PrintingDiagnosticConsumer PDC;
SourceManager SM;
DiagnosticEngine Diags(SM);
Diags.addConsumer(PDC);
CompilerInvocation CI;
bool HadError = driver::getSingleFrontendInvocationFromDriverArguments(
DriverPath, Args, Diags,
[&](ArrayRef<const char *> FrontendArgs) {
llvm::outs() << "Frontend Arguments BEGIN\n";
for (const char *arg : FrontendArgs) {
llvm::outs() << arg << "\n";
}
llvm::outs() << "Frontend Arguments END\n";
return CI.parseArgs(FrontendArgs, Diags);
},
ForceNoOutputs);
if (HadError) {
llvm::errs() << "error: unable to create a CompilerInvocation\n";
return 1;
}
return 0;
}
static int doTestCompilerInvocationFromModule(StringRef ModuleFilePath) {
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
if (setBufferForFile(ModuleFilePath, FileBuf))
return 1;
CompilerInvocation CI;
StringRef Data = FileBuf->getBuffer();
static_assert(static_cast<int>(serialization::Status::Valid) == 0,
"Status::Valid should be a successful exit");
return static_cast<int>(CI.loadFromSerializedAST(Data));
}
// This function isn't referenced outside its translation unit, but it
// can't use the "static" keyword because its address is used for
// getMainExecutable (since some platforms don't support taking the
// address of main, and some platforms can't implement getMainExecutable
// without being given the address of a function in the main executable).
void anchorForGetMainExecutable() {}
// Derive 'swiftc' path from 'swift-ide-test' path.
std::string getDriverPath(StringRef MainExecutablePath) {
SmallString<256> driverPath(MainExecutablePath);
llvm::sys::path::remove_filename(driverPath); // remove 'swift-ide-test'.
llvm::sys::path::remove_filename(driverPath); // remove 'bin'.
if (llvm::sys::path::filename(driverPath) == "local") {
llvm::sys::path::remove_filename(driverPath); // remove 'local'.
}
llvm::sys::path::append(driverPath, "bin", "swiftc");
return std::string(driverPath);
}
int main(int argc, char *argv[]) {
PROGRAM_START(argc, argv);
INITIALIZE_LLVM();
initializeSwiftModules();
std::string mainExecutablePath = llvm::sys::fs::getMainExecutable(
argv[0], reinterpret_cast<void *>(&anchorForGetMainExecutable));
std::string driverPath = getDriverPath(mainExecutablePath);
if (argc > 1) {
// Handle integrated test tools which do not use
// llvm::cl::ParseCommandLineOptions.
StringRef FirstArg(argv[1]);
if (FirstArg == "-test-createCompilerInvocation") {
bool ForceNoOutputs = false;
ArrayRef<const char *> Args(argv + 2, argc - 2);
if (argc > 2 && StringRef(argv[2]) == "-force-no-outputs") {
ForceNoOutputs = true;
Args = Args.drop_front();
}
return doTestCreateCompilerInvocation(driverPath, Args, ForceNoOutputs);
}
}
// '--cc-args' separates swift-ide-test options from clang arguments.
ArrayRef<const char *> CCArgs;
for (int i = 1; i < argc; ++i) {
if (StringRef(argv[i]) == "--cc-args") {
CCArgs = llvm::ArrayRef(argv + i + 1, argc - i - 1);
argc = i;
}
}
llvm::cl::HideUnrelatedOptions(options::Category);
llvm::cl::ParseCommandLineOptions(argc, argv, "Swift IDE Test\n");
if (options::Action == ActionType::None) {
llvm::errs() << "action required\n";
llvm::cl::PrintHelpMessage();
return 1;
}
if (options::Action == ActionType::TestCreateCompilerInvocation) {
llvm::errs() << "-test-createCompilerInvocation must be specified before "
"all other arguments\n";
return 1;
}
if (options::Action == ActionType::GenerateModuleAPIDescription) {
return doGenerateModuleAPIDescription(driverPath, mainExecutablePath,
options::InputFilenames);
}
if (options::Action == ActionType::DumpCompletionCache) {
if (options::InputFilenames.empty()) {
llvm::errs() << "-dump-completion-cache requires an input file\n";
return 1;
}
for (StringRef filename : options::InputFilenames) {
auto resultsOpt = ide::OnDiskCodeCompletionCache::getFromFile(filename);
if (!resultsOpt) {
// FIXME: error?
continue;
}
// Make contextual CodeCompletionResults from the
// ContextFreeCodeCompletionResults so we can print them.
std::vector<CodeCompletionResult *> contextualResults;
contextualResults.reserve(resultsOpt->get()->Results.size());
for (auto contextFreeResult : resultsOpt->get()->Results) {
// We are leaking these results but it doesn't matter since the process
// just terminates afterwards anyway.
auto contextualResult = new CodeCompletionResult(
*contextFreeResult, SemanticContextKind::OtherModule,
CodeCompletionFlair(),
/*numBytesToErase=*/0, CodeCompletionResultTypeRelation::Unrelated,
ContextualNotRecommendedReason::None);
contextualResults.push_back(contextualResult);
}
printCodeCompletionResultsImpl(
contextualResults, llvm::outs(), options::CodeCompletionKeywords,
options::CodeCompletionComments, options::CodeCompletionSourceText,
options::CodeCompletionAnnotateResults,
/*Ctx=*/nullptr);
}
return 0;
}
if (options::SourceFilename.empty()) {
llvm::errs() << "source file required\n";
llvm::cl::PrintHelpMessage();
return 1;
}
if (options::Action == ActionType::CompilerInvocationFromModule) {
return doTestCompilerInvocationFromModule(options::SourceFilename);
}
// If no SDK was specified via -sdk, check environment variable SDKROOT.
if (options::SDK.getNumOccurrences() == 0) {
const char *SDKROOT = getenv("SDKROOT");
if (SDKROOT)
options::SDK = SDKROOT;
}
if (options::PrintStats)
llvm::EnableStatistics();
CompilerInvocation InitInvok;
InitInvok.getFrontendOptions().RequestedAction = FrontendOptions::ActionType::Typecheck;
for (auto &File : options::InputFilenames)
InitInvok.getFrontendOptions().InputsAndOutputs.addInputFile(File);
for (const auto &featureArg : options::EnableExperimentalFeatures) {
if (auto feature = getExperimentalFeature(featureArg)) {
InitInvok.getLangOptions().enableFeature(*feature);
}
}
for (const auto &featureArg : options::EnableUpcomingFeatures) {
if (auto feature = getUpcomingFeature(featureArg)) {
InitInvok.getLangOptions().enableFeature(*feature);
}
}
// NOTE: 'setMainExecutablePath' must be after 'Features' because
// 'setRuntimeResourcePath()' called from here depends on 'Features'.
InitInvok.setMainExecutablePath(mainExecutablePath);
InitInvok.setModuleName(options::ModuleName);
InitInvok.setSDKPath(options::SDK);
for (auto macro : options::DefineAvailability) {
InitInvok.getLangOptions().AvailabilityMacros.push_back(macro);
}
for (auto map: options::SerializedPathObfuscate) {
auto SplitMap = StringRef(map).split('=');
InitInvok.getFrontendOptions().serializedPathObfuscator
.addMapping(SplitMap.first, SplitMap.second);
InitInvok.getSearchPathOptions().DeserializedPathRecoverer
.addMapping(SplitMap.first, SplitMap.second);
}
InitInvok.getLangOptions().CollectParsedToken = true;
InitInvok.getLangOptions().EnableCrossImportOverlays =
options::EnableCrossImportOverlays;
if (options::DisableObjCInterop) {
InitInvok.getLangOptions().EnableObjCInterop = false;
} else if (options::EnableObjCInterop) {
InitInvok.getLangOptions().EnableObjCInterop = true;
} else if (!options::Triple.empty()) {
InitInvok.getLangOptions().EnableObjCInterop =
llvm::Triple(options::Triple).isOSDarwin();
}
if (options::EnableCxxInterop) {
InitInvok.getLangOptions().EnableCXXInterop = true;
}
if (!options::CxxInteropVersion.empty()) {
InitInvok.getLangOptions().EnableCXXInterop = true;
if (options::CxxInteropVersion == "upcoming-swift")
InitInvok.getLangOptions().cxxInteropCompatVersion =
version::Version({version::getUpcomingCxxInteropCompatVersion()});
else if (options::CxxInteropVersion == "swift-6")
InitInvok.getLangOptions().cxxInteropCompatVersion =
version::Version({6});
else if (options::CxxInteropVersion == "swift-5.9")
InitInvok.getLangOptions().cxxInteropCompatVersion =
version::Version({5, 9});
else
llvm::errs() << "invalid CxxInteropVersion\n";
}
if (options::CxxInteropGettersSettersAsProperties) {
InitInvok.getLangOptions().CxxInteropGettersSettersAsProperties = true;
}
if (options::EnableExperimentalConcurrency) {
InitInvok.getLangOptions().EnableExperimentalConcurrency = true;
}
if (options::WarnConcurrency) {
InitInvok.getLangOptions().StrictConcurrencyLevel =
StrictConcurrency::Complete;
}
if (options::DisableImplicitConcurrencyImport) {
InitInvok.getLangOptions().DisableImplicitConcurrencyModuleImport = true;
}
if (options::DisableImplicitStringProcessingImport) {
InitInvok.getLangOptions().DisableImplicitStringProcessingModuleImport = true;
}
if (options::EnableExperimentalNamedOpaqueTypes) {
InitInvok.getLangOptions().enableFeature(Feature::NamedOpaqueTypes);
}
if (options::EnableBareSlashRegexLiterals) {
InitInvok.getLangOptions().enableFeature(Feature::BareSlashRegexLiterals);
InitInvok.getLangOptions().EnableExperimentalStringProcessing = true;
}
if (!options::Triple.empty())
InitInvok.setTargetTriple(options::Triple);
if (!options::SwiftVersion.empty()) {
// Honor the *last* -swift-version specified.
const auto &LastSwiftVersion =
options::SwiftVersion[options::SwiftVersion.size()-1];
if (auto swiftVersion = VersionParser::parseVersionString(
LastSwiftVersion, SourceLoc(), nullptr)) {
if (auto actual = swiftVersion.value().getEffectiveLanguageVersion())
InitInvok.getLangOptions().EffectiveLanguageVersion = actual.value();
}
}
if (!options::ToolsDirectory.empty()) {
SmallString<128> toolsDir(options::ToolsDirectory);
llvm::sys::path::append(toolsDir, "clang");
InitInvok.getClangImporterOptions().clangPath =
std::string(toolsDir);
}
if (!options::ModuleCachePath.empty()) {
// Honor the *last* -module-cache-path specified.
InitInvok.getClangImporterOptions().ModuleCachePath =
options::ModuleCachePath[options::ModuleCachePath.size()-1];
}
if (!options::AccessNotesPath.empty()) {
InitInvok.getFrontendOptions().AccessNotesPath =
options::AccessNotesPath[options::AccessNotesPath.size()-1];
}
if (options::ParseAsLibrary) {
InitInvok.getFrontendOptions().InputMode =
swift::FrontendOptions::ParseInputMode::SwiftLibrary;
}
InitInvok.getClangImporterOptions().PrecompiledHeaderOutputDir =
options::PCHOutputDir;
std::vector<SearchPathOptions::SearchPath> ImportPaths;
for (const auto &path : options::ImportPaths) {
ImportPaths.push_back({path, /*isSystem=*/false});
}
for (const auto &path : options::SystemImportPaths) {
ImportPaths.push_back({path, /*isSystem=*/true});
}
InitInvok.setImportSearchPaths(ImportPaths);
std::vector<SearchPathOptions::SearchPath> FramePaths;
for (const auto &path : options::FrameworkPaths) {
FramePaths.push_back({path, /*isSystem=*/false});
}
for (const auto &path : options::SystemFrameworkPaths) {
FramePaths.push_back({path, /*isSystem=*/true});
}
InitInvok.setFrameworkSearchPaths(FramePaths);
InitInvok.getFrontendOptions().EnableSourceImport |=
options::EnableSourceImport;
InitInvok.getFrontendOptions().ImplicitObjCHeaderPath =
options::ImportObjCHeader;
InitInvok.getClangImporterOptions().BridgingHeader =
options::ImportObjCHeader;
InitInvok.getLangOptions().EnableAccessControl =
!options::DisableAccessControl;
InitInvok.getLangOptions().EnableDeserializationSafety =
options::EnableDeserializationSafety;
// The manner in which swift-ide-test constructs its CompilerInvocation does
// not hit the codepath in arg parsing that would normally construct
// ClangImporter options based on enabled language features etc. Explicitly
// enable them here.
InitInvok.getClangImporterOptions().ImportForwardDeclarations |=
InitInvok.getLangOptions().hasFeature(
Feature::ImportObjcForwardDeclarations);
if (!options::ResourceDir.empty()) {
InitInvok.setRuntimeResourcePath(options::ResourceDir);
}
for (auto &Arg : options::ClangXCC) {
InitInvok.getClangImporterOptions().ExtraArgs.push_back(Arg);
}
InitInvok.getLangOptions().EnableObjCAttrRequiresFoundation =
!options::DisableObjCAttrRequiresFoundationModule;
for (auto prefix : options::DebugForbidTypecheckPrefix) {
InitInvok.getTypeCheckerOptions().DebugForbidTypecheckPrefixes.push_back(
prefix);
}
InitInvok.getTypeCheckerOptions().DebugConstraintSolver =
options::DebugConstraintSolver;
for (auto ConfigName : options::BuildConfigs)
InitInvok.getLangOptions().addCustomConditionalCompilationFlag(ConfigName);
if (!options::ExplicitSwiftModuleMap.empty()) {
InitInvok.getSearchPathOptions().ExplicitSwiftModuleMapPath =
options::ExplicitSwiftModuleMap;
InitInvok.getFrontendOptions().DisableImplicitModules = true;
}
if (options::AllowCompilerErrors) {
InitInvok.getFrontendOptions().AllowModuleWithCompilerErrors = true;
InitInvok.getLangOptions().AllowModuleWithCompilerErrors = true;
}
if (!options::ModuleAliases.empty()) {
PrintingDiagnosticConsumer PDC;
SourceManager SM;
DiagnosticEngine Diags(SM);
Diags.addConsumer(PDC);
if (!InitInvok.setModuleAliasMap(options::ModuleAliases, Diags)) {
llvm::errs() << "invalid module alias arguments\n";
return 1;
}
}
if (InitInvok.getLangOptions().EnableCXXInterop) {
InitInvok.computeCXXStdlibOptions();
}
if (!options::InProcessPluginServerPath.empty()) {
InitInvok.getSearchPathOptions().InProcessPluginServerPath =
options::InProcessPluginServerPath;
}
if (!options::LoadPluginLibrary.empty()) {
std::vector<std::string> paths;
for (auto path: options::LoadPluginLibrary) {
InitInvok.getSearchPathOptions().PluginSearchOpts.emplace_back(
PluginSearchOption::LoadPluginLibrary{path});
}
}
if (!options::LoadPluginExecutable.empty()) {
std::vector<PluginExecutablePathAndModuleNames> pairs;
for (auto arg: options::LoadPluginExecutable) {
StringRef path;
StringRef modulesStr;
std::tie(path, modulesStr) = StringRef(arg).rsplit('#');
std::vector<std::string> moduleNames;
for (auto name : llvm::split(modulesStr, ',')) {
moduleNames.emplace_back(name);
}
InitInvok.getSearchPathOptions().PluginSearchOpts.emplace_back(
PluginSearchOption::LoadPluginExecutable{std::string(path),
std::move(moduleNames)});
}
}
for (auto path : options::PluginPath) {
InitInvok.getSearchPathOptions().PluginSearchOpts.emplace_back(
PluginSearchOption::PluginPath{path});
}
InitInvok.setDefaultInProcessPluginServerPathIfNecessary();
for (auto implicitImport : options::ImplicitModuleImports) {
InitInvok.getFrontendOptions().ImplicitImportModuleNames.emplace_back(
implicitImport, /*isTestable=*/false);
}
// Process the clang arguments last and allow them to override previously
// set options.
if (!CCArgs.empty()) {
std::string Error;
if (initInvocationByClangArguments(CCArgs, InitInvok, Error)) {
llvm::errs() << "error initializing invocation with clang args: "
<< Error << '\n';
return 1;
}
}
PrintOptions PrintOpts;
if (options::PrintInterface) {
PrintOpts = PrintOptions::printModuleInterface(
InitInvok.getFrontendOptions().PrintFullConvention);
} else if (options::PrintInterfaceForDoc) {
PrintOpts = PrintOptions::printDocInterface();
} else {
PrintOpts = PrintOptions::printEverything();
PrintOpts.FullyQualifiedTypes = options::FullyQualifiedTypes;
PrintOpts.FullyQualifiedTypesIfAmbiguous =
options::FullyQualifiedTypesIfAmbiguous;
PrintOpts.SynthesizeSugarOnTypes = options::SynthesizeSugarOnTypes;
PrintOpts.AbstractAccessors = options::AbstractAccessors;
PrintOpts.FunctionDefinitions = options::FunctionDefinitions;
PrintOpts.PrintExprs = options::Expressions;
PrintOpts.PreferTypeRepr = options::PreferTypeRepr;
PrintOpts.ExplodePatternBindingDecls = options::ExplodePatternBindingDecls;
PrintOpts.PrintImplicitAttrs = options::PrintImplicitAttrs;
PrintOpts.PrintAccess = options::PrintAccess;
PrintOpts.AccessFilter = options::AccessFilter;
PrintOpts.PrintDocumentationComments = !options::SkipDocumentationComments;
PrintOpts.SkipPrivateSystemDecls = options::SkipPrivateSystemDecls;
PrintOpts.SkipUnsafeCXXMethods = options::SkipUnsafeCXXMethods;
PrintOpts.SkipUnavailable = options::SkipUnavailable;
PrintOpts.SkipDeinit = options::SkipDeinit;
PrintOpts.SkipImports = options::SkipImports;
PrintOpts.SkipOverrides = options::SkipOverrides;
if (options::SkipParameterNames) {
PrintOpts.ArgAndParamPrinting
= PrintOptions::ArgAndParamPrintingMode::ArgumentOnly;
} else if (options::AlwaysArgumentLabels) {
PrintOpts.ArgAndParamPrinting
= PrintOptions::ArgAndParamPrintingMode::BothAlways;
}
}
if (options::SkipUnderscoredSystemProtocols)
PrintOpts.SkipUnderscoredSystemProtocols = true;
if (options::PrintOriginalSourceText)
PrintOpts.PrintOriginalSourceText = true;
if (PrintOpts.PrintDocumentationComments) {
InitInvok.getLangOptions().AttachCommentsToDecls = true;
}
int ExitCode;
switch (options::Action) {
case ActionType::None:
case ActionType::TestCreateCompilerInvocation:
case ActionType::CompilerInvocationFromModule:
case ActionType::GenerateModuleAPIDescription:
case ActionType::DiffModuleAPI:
case ActionType::DumpCompletionCache:
llvm_unreachable("should be handled above");
case ActionType::BatchCodeCompletion:
if (options::FileCheckPath.empty() && !options::SkipFileCheck) {
llvm::errs() << "'FileCheck' path required or explicitly specify "
<< "'-skip-filecheck'\n";
return 1;
}
ExitCode = doBatchCodeCompletion(
InitInvok, options::SourceFilename, options::CodeCompletionDiagnostics,
options::CodeCompletionKeywords, options::CodeCompletionComments,
options::CodeCompletionAnnotateResults,
options::CodeCompleteInitsInPostfixExpr,
options::CodeCompletionAddCallWithNoDefaultArgs,
options::CodeCompletionSourceText);
break;
case ActionType::CodeCompletion:
if (options::CodeCompletionToken.empty()) {
llvm::errs() << "code completion token name required\n";
return 1;
}
ExitCode = doCodeCompletion(
InitInvok, options::SourceFilename, options::SecondSourceFilename,
options::CodeCompletionToken, options::CodeCompletionDiagnostics,
options::CodeCompletionKeywords, options::CodeCompletionComments,
options::CodeCompletionAnnotateResults,
options::CodeCompleteInitsInPostfixExpr,
options::CodeCompletionAddCallWithNoDefaultArgs,
options::CodeCompletionSourceText);
break;
case ActionType::REPLCodeCompletion:
ExitCode = doREPLCodeCompletion(InitInvok, options::SourceFilename);
break;
case ActionType::TypeContextInfo:
if (options::CodeCompletionToken.empty()) {
llvm::errs() << "token name required\n";
return 1;
}
ExitCode = doTypeContextInfo(InitInvok,
options::SourceFilename,
options::SecondSourceFilename,
options::CodeCompletionToken,
options::CodeCompletionDiagnostics);
break;
case ActionType::PrintExpressionTypes:
ExitCode = doPrintExpressionTypes(InitInvok,
options::SourceFilename);
break;
case ActionType::ConformingMethodList:
if (options::CodeCompletionToken.empty()) {
llvm::errs() << "token name required\n";
return 1;
}
ExitCode = doConformingMethodList(InitInvok,
options::SourceFilename,
options::SecondSourceFilename,
options::CodeCompletionToken,
options::CodeCompletionDiagnostics,
options::ConformingMethodListExpectedTypes);
break;
case ActionType::SyntaxColoring:
ExitCode = doSyntaxColoring(InitInvok,
options::SourceFilename,
options::TerminalOutput,
options::Typecheck,
options::Playground);
break;
case ActionType::DumpImporterLookupTable:
ExitCode = doDumpImporterLookupTables(InitInvok, options::SourceFilename);
break;
case ActionType::Structure:
ExitCode = doStructureAnnotation(InitInvok, options::SourceFilename);
break;
case ActionType::Annotation:
ExitCode = doSemanticAnnotation(InitInvok,
options::SourceFilename,
options::TerminalOutput);
break;
case ActionType::TestInputCompleteness:
ExitCode = doInputCompletenessTest(InitInvok, options::SourceFilename);
break;
case ActionType::PrintASTNotTypeChecked:
case ActionType::PrintASTTypeChecked: {
bool RunTypeChecker = (options::Action == ActionType::PrintASTTypeChecked);
ExitCode = doPrintAST(InitInvok,
options::SourceFilename,
RunTypeChecker,
PrintOpts,
options::MangledNameToFind,
options::DebugClientDiscriminator);
break;
}
case ActionType::PrintLocalTypes:
ExitCode = doPrintLocalTypes(InitInvok, options::ModuleToPrint);
break;
case ActionType::PrintModuleGroups:
case ActionType::PrintModule: {
ide::ModuleTraversalOptions TraversalOptions;
if (options::ModulePrintSubmodules)
TraversalOptions |= ide::ModuleTraversal::VisitSubmodules;
if (options::ModulePrintHidden)
TraversalOptions |= ide::ModuleTraversal::VisitHidden;
if (options::ModulePrintSkipOverlay)
TraversalOptions |= ide::ModuleTraversal::SkipOverlay;
if (options::Action == ActionType::PrintModuleGroups)
ExitCode = doPrintModuleGroups(InitInvok, options::ModuleToPrint);
else {
if (options::NoEmptyLineBetweenMembers.getNumOccurrences() > 0)
PrintOpts.EmptyLineBetweenDecls = !options::NoEmptyLineBetweenMembers;
ExitCode = doPrintModules(
InitInvok, options::ModuleToPrint, options::ModuleGroupToPrint,
TraversalOptions, PrintOpts, options::AnnotatePrint,
options::SynthesizeExtension);
}
break;
}
case ActionType::PrintModuleMetadata: {
ExitCode = doPrintModuleMetaData(InitInvok, options::ModuleToPrint);
break;
}
case ActionType::PrintHeader: {
ExitCode = doPrintHeaders(InitInvok, options::SourceFilename,
options::HeaderToPrint, PrintOpts,
options::AnnotatePrint);
break;
}
case ActionType::PrintSwiftFileInterface: {
ExitCode = doPrintSwiftFileInterface(
InitInvok, options::SourceFilename,
options::AnnotatePrint);
break;
}
case ActionType::PrintDecl: {
ExitCode = doPrintDecls(
InitInvok, options::SourceFilename,
options::DeclToPrint, PrintOpts, options::AnnotatePrint);
break;
}
case ActionType::PrintTypes:
ExitCode = doPrintTypes(InitInvok, options::SourceFilename,
options::FullyQualifiedTypes);
break;
case ActionType::PrintComments:
ExitCode = doPrintComments(InitInvok, options::SourceFilename,
options::CommentsXMLSchema);
break;
case ActionType::DumpComments:
ExitCode = doDumpComments(InitInvok, options::SourceFilename);
break;
case ActionType::PrintModuleComments:
ExitCode = doPrintModuleComments(InitInvok, options::ModuleToPrint,
options::CommentsXMLSchema);
break;
case ActionType::PrintModuleImports:
ExitCode = doPrintModuleImports(InitInvok, options::ModuleToPrint);
break;
case ActionType::PrintUSRs:
ExitCode = doPrintUSRs(InitInvok, options::SourceFilename);
break;
case ActionType::PrintTypeInterface:
if (options::LineColumnPair.getNumOccurrences() == 1)
ExitCode = doPrintTypeInterface(InitInvok,
options::SourceFilename,
options::LineColumnPair);
else
ExitCode = doPrintTypeInterfaceForTypeUsr(InitInvok,
options::SourceFilename,
options::USR);
break;
case ActionType::ReconstructType:
ExitCode = doReconstructType(InitInvok, options::SourceFilename);
break;
case ActionType::Range:
ExitCode = doPrintRangeInfo(InitInvok, options::SourceFilename,
options::LineColumnPair,
options::EndLineColumnPair);
break;
case ActionType::PrintIndexedSymbols:
if (options::ModuleToPrint.empty()) {
ExitCode = doPrintIndexedSymbols(InitInvok, options::SourceFilename,
options::IncludeLocals);
} else {
if (options::ModuleToPrint.size() > 1) {
llvm::errs() << "printing symbols for the first module name, the rest "
"are ignored";
}
ExitCode = doPrintIndexedSymbolsFromModule(InitInvok,
options::ModuleToPrint.front());
}
}
if (options::PrintStats)
llvm::PrintStatistics();
return ExitCode;
}