blob: ea949b1075b7a5e941dda63eeb67ce8b37915e0b [file] [log] [blame]
//===--- SourceFile.h - The contents of a source file -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_SOURCEFILE_H
#define SWIFT_AST_SOURCEFILE_H
#include "swift/AST/FileUnit.h"
#include "swift/AST/Import.h"
#include "swift/AST/SynthesizedFileUnit.h"
#include "swift/Basic/Debug.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
namespace swift {
class PersistentParserState;
/// A file containing Swift source code.
///
/// This is a .swift or .sil file (or a virtual file, such as the contents of
/// the REPL). Since it contains raw source, it must be type checked for IR
/// generation.
class SourceFile final : public FileUnit {
friend class ParseSourceFileRequest;
public:
/// Flags that direct how the source file is parsed.
enum class ParsingFlags : uint8_t {
/// Whether to disable delayed parsing for nominal type, extension, and
/// function bodies.
///
/// If set, type and function bodies will be parsed eagerly. Otherwise they
/// will be lazily parsed when their contents is queried. This lets us avoid
/// building AST nodes when they're not needed.
///
/// This is set for primary files, since we want to type check all
/// declarations and function bodies anyway, so there's no benefit in lazy
/// parsing.
DisableDelayedBodies = 1 << 0,
/// Whether to disable evaluating the conditions of #if decls.
///
/// If set, #if decls are parsed as-is. Otherwise, the bodies of any active
/// clauses are hoisted such that they become sibling nodes with the #if
/// decl.
///
/// FIXME: When condition evaluation moves to a later phase, remove this
/// and the associated language option.
DisablePoundIfEvaluation = 1 << 1,
/// Whether to build a syntax tree.
BuildSyntaxTree = 1 << 2,
/// Whether to save the file's parsed tokens.
CollectParsedTokens = 1 << 3,
/// Whether to compute the interface hash of the file.
EnableInterfaceHash = 1 << 4,
/// Whether to suppress warnings when parsing. This is set for secondary
/// files, as they get parsed multiple times.
SuppressWarnings = 1 << 5,
};
using ParsingOptions = OptionSet<ParsingFlags>;
/// Retrieve the parsing options specified in the LangOptions.
static ParsingOptions getDefaultParsingOptions(const LangOptions &langOpts);
private:
std::unique_ptr<SourceLookupCache> Cache;
SourceLookupCache &getCache() const;
/// This is the list of modules that are imported by this module.
///
/// This is \c None until it is filled in by the import resolution phase.
Optional<ArrayRef<AttributedImport<ImportedModule>>> Imports;
/// A unique identifier representing this file; used to mark private decls
/// within the file to keep them from conflicting with other files in the
/// same module.
mutable Identifier PrivateDiscriminator;
/// A synthesized file corresponding to this file, created on-demand.
SynthesizedFileUnit *SynthesizedFile = nullptr;
/// The root TypeRefinementContext for this SourceFile.
///
/// This is set during type checking.
TypeRefinementContext *TRC = nullptr;
/// Either the class marked \@NS/UIApplicationMain or the synthesized FuncDecl
/// that calls main on the type marked @main.
Decl *MainDecl = nullptr;
/// The source location of the main type.
SourceLoc MainDeclDiagLoc;
/// A hash of all interface-contributing tokens that have been lexed for
/// this source file so far.
/// We only collect interface hash for primary input files.
llvm::Optional<llvm::MD5> InterfaceHash;
/// The ID for the memory buffer containing this file's source.
///
/// May be -1, to indicate no association with a buffer.
int BufferID;
/// The parsing options for the file.
ParsingOptions ParsingOpts;
/// Whether this is a primary source file which we'll be generating code for.
bool IsPrimary;
/// The scope map that describes this source file.
NullablePtr<ASTScope> Scope = nullptr;
/// The set of validated opaque return type decls in the source file.
llvm::SmallVector<OpaqueTypeDecl *, 4> OpaqueReturnTypes;
llvm::StringMap<OpaqueTypeDecl *> ValidatedOpaqueReturnTypes;
/// The set of parsed decls with opaque return types that have not yet
/// been validated.
llvm::SetVector<ValueDecl *> UnvalidatedDeclsWithOpaqueReturnTypes;
/// The list of top-level declarations in the source file. This is \c None if
/// they have not yet been parsed.
/// FIXME: Once addTopLevelDecl/prependTopLevelDecl
/// have been removed, this can become an optional ArrayRef.
Optional<std::vector<Decl *>> Decls;
/// The list of hoisted declarations. See Decl::isHoisted().
/// This is only used by lldb.
std::vector<Decl *> Hoisted;
using SeparatelyImportedOverlayMap =
llvm::SmallDenseMap<ModuleDecl *, llvm::SmallPtrSet<ModuleDecl *, 1>>;
/// Keys are modules which are shadowed by one or more separately-imported
/// overlays; values are the list of overlays shadowing them.
///
/// This is used by cross-import overlays to make their members appear to
/// be part of the underlying module. (ClangImporter overlays use a different
/// mechanism which is not SourceFile-dependent.)
SeparatelyImportedOverlayMap separatelyImportedOverlays;
/// A pointer to PersistentParserState with a function reference to its
/// deleter to handle the fact that it's forward declared.
using ParserStatePtr =
std::unique_ptr<PersistentParserState, void (*)(PersistentParserState *)>;
/// Stores delayed parser state that code completion needs to be able to
/// resume parsing at the code completion token in the file.
ParserStatePtr DelayedParserState =
ParserStatePtr(/*ptr*/ nullptr, /*deleter*/ nullptr);
friend ASTContext;
public:
/// Appends the given declaration to the end of the top-level decls list. Do
/// not add any additional uses of this function.
void addTopLevelDecl(Decl *d) {
// Force decl parsing if we haven't already.
(void)getTopLevelDecls();
Decls->push_back(d);
}
/// Prepends a declaration to the top-level decls list.
///
/// FIXME: This entrypoint exists to support LLDB. Calls to this function are
/// always a mistake, and additional uses should not be added.
///
/// See rdar://58355191
void prependTopLevelDecl(Decl *d) {
// Force decl parsing if we haven't already.
(void)getTopLevelDecls();
Decls->insert(Decls->begin(), d);
}
/// Add a hoisted declaration. See Decl::isHoisted().
void addHoistedDecl(Decl *d);
/// Retrieves an immutable view of the list of top-level decls in this file.
ArrayRef<Decl *> getTopLevelDecls() const;
/// Retrieves an immutable view of the list of hoisted decls in this file.
/// See Decl::isHoisted().
ArrayRef<Decl *> getHoistedDecls() const;
/// Retrieves an immutable view of the top-level decls if they have already
/// been parsed, or \c None if they haven't. Should only be used for dumping.
Optional<ArrayRef<Decl *>> getCachedTopLevelDecls() const {
if (!Decls)
return None;
return llvm::makeArrayRef(*Decls);
}
/// Retrieve the parsing options for the file.
ParsingOptions getParsingOptions() const { return ParsingOpts; }
/// Whether this source file is a primary file, meaning that we're generating
/// code for it. Note this method returns \c false in WMO.
bool isPrimary() const { return IsPrimary; }
/// A cache of syntax nodes that can be reused when creating the syntax tree
/// for this file.
swift::SyntaxParsingCache *SyntaxParsingCache = nullptr;
/// The list of local type declarations in the source file.
llvm::SetVector<TypeDecl *> LocalTypeDecls;
/// A set of synthesized declarations that need to be type checked.
llvm::SmallVector<Decl *, 8> SynthesizedDecls;
/// The list of functions defined in this file whose bodies have yet to be
/// typechecked. They must be held in this list instead of eagerly validated
/// because their bodies may force us to perform semantic checks of arbitrary
/// complexity, and we currently cannot handle those checks in isolation. E.g.
/// we cannot, in general, perform witness matching on singular requirements
/// unless the entire conformance has been evaluated.
std::vector<AbstractFunctionDecl *> DelayedFunctions;
/// We might perform type checking on the same source file more than once,
/// if its the main file or a REPL instance, so keep track of the last
/// checked synthesized declaration to avoid duplicating work.
unsigned LastCheckedSynthesizedDecl = 0;
/// A mapping from Objective-C selectors to the methods that have
/// those selectors.
llvm::DenseMap<ObjCSelector, llvm::TinyPtrVector<AbstractFunctionDecl *>>
ObjCMethods;
/// List of Objective-C methods, which is used for checking unintended
/// Objective-C overrides.
std::vector<AbstractFunctionDecl *> ObjCMethodList;
/// An unsatisfied, optional @objc requirement in a protocol conformance.
using ObjCUnsatisfiedOptReq = std::pair<DeclContext *, AbstractFunctionDecl *>;
/// List of optional @objc protocol requirements that have gone
/// unsatisfied, which might conflict with other Objective-C methods.
std::vector<ObjCUnsatisfiedOptReq> ObjCUnsatisfiedOptReqs;
using ObjCMethodConflict = std::tuple<ClassDecl *, ObjCSelector, bool>;
/// List of Objective-C member conflicts we have found during type checking.
std::vector<ObjCMethodConflict> ObjCMethodConflicts;
/// Describes what kind of file this is, which can affect some type checking
/// and other behavior.
const SourceFileKind Kind;
enum ASTStage_t {
/// The source file has not had its imports resolved or been type checked.
Unprocessed,
/// Import resolution has completed.
ImportsResolved,
/// Type checking has completed.
TypeChecked
};
/// Defines what phases of parsing and semantic analysis are complete for a
/// source file.
///
/// Only files that have been fully processed (i.e. type-checked) will be
/// forwarded on to IRGen.
ASTStage_t ASTStage = Unprocessed;
/// Virtual file paths declared by \c #sourceLocation(file:) declarations in
/// this source file.
llvm::SmallVector<Located<StringRef>, 0> VirtualFilePaths;
/// Returns information about the file paths used for diagnostics and magic
/// identifiers in this source file, including virtual filenames introduced by
/// \c #sourceLocation(file:) declarations.
llvm::StringMap<SourceFilePathInfo> getInfoForUsedFilePaths() const;
SourceFile(ModuleDecl &M, SourceFileKind K, Optional<unsigned> bufferID,
ParsingOptions parsingOpts = {}, bool isPrimary = false);
~SourceFile();
/// Retrieve an immutable view of the source file's imports.
ArrayRef<AttributedImport<ImportedModule>> getImports() const {
return *Imports;
}
/// Set the imports for this source file. This gets called by import
/// resolution.
void setImports(ArrayRef<AttributedImport<ImportedModule>> imports);
enum ImportQueryKind {
/// Return the results for testable or private imports.
TestableAndPrivate,
/// Return the results only for testable imports.
TestableOnly,
/// Return the results only for private imports.
PrivateOnly
};
bool
hasTestableOrPrivateImport(AccessLevel accessLevel, const ValueDecl *ofDecl,
ImportQueryKind kind = TestableAndPrivate) const;
/// Does this source file have any implementation-only imports?
/// If not, we can fast-path module checks.
bool hasImplementationOnlyImports() const;
bool isImportedImplementationOnly(const ModuleDecl *module) const;
/// Find all SPI names imported from \p importedModule by this file,
/// collecting the identifiers in \p spiGroups.
virtual void
lookupImportedSPIGroups(
const ModuleDecl *importedModule,
llvm::SmallSetVector<Identifier, 4> &spiGroups) const override;
// Is \p targetDecl accessible as an explictly imported SPI from this file?
bool isImportedAsSPI(const ValueDecl *targetDecl) const;
bool shouldCrossImport() const;
/// Register a separately-imported overlay as shadowing the module that
/// declares it.
///
/// \returns true if the overlay was added; false if it already existed.
bool addSeparatelyImportedOverlay(ModuleDecl *overlay,
ModuleDecl *declaring) {
return std::get<1>(separatelyImportedOverlays[declaring].insert(overlay));
}
/// Retrieves a list of separately imported overlays which are shadowing
/// \p declaring. If any \p overlays are returned, qualified lookups into
/// \p declaring should be performed into \p overlays instead; since they
/// are overlays, they will re-export \p declaring, but will also augment it
/// with additional symbols.
void getSeparatelyImportedOverlays(
ModuleDecl *declaring, SmallVectorImpl<ModuleDecl *> &overlays) {
auto i = separatelyImportedOverlays.find(declaring);
if (i == separatelyImportedOverlays.end()) return;
auto &value = std::get<1>(*i);
overlays.append(value.begin(), value.end());
}
SWIFT_DEBUG_DUMPER(dumpSeparatelyImportedOverlays());
void cacheVisibleDecls(SmallVectorImpl<ValueDecl *> &&globals) const;
const SmallVectorImpl<ValueDecl *> &getCachedVisibleDecls() const;
virtual void lookupValue(DeclName name, NLKind lookupKind,
SmallVectorImpl<ValueDecl*> &result) const override;
virtual void lookupVisibleDecls(ImportPath::Access accessPath,
VisibleDeclConsumer &consumer,
NLKind lookupKind) const override;
virtual void lookupClassMembers(ImportPath::Access accessPath,
VisibleDeclConsumer &consumer) const override;
virtual void
lookupClassMember(ImportPath::Access accessPath, DeclName name,
SmallVectorImpl<ValueDecl*> &results) const override;
void lookupObjCMethods(
ObjCSelector selector,
SmallVectorImpl<AbstractFunctionDecl *> &results) const override;
protected:
virtual void
lookupOperatorDirect(Identifier name, OperatorFixity fixity,
TinyPtrVector<OperatorDecl *> &results) const override;
virtual void lookupPrecedenceGroupDirect(
Identifier name,
TinyPtrVector<PrecedenceGroupDecl *> &results) const override;
public:
virtual void getTopLevelDecls(SmallVectorImpl<Decl*> &results) const override;
virtual void
getOperatorDecls(SmallVectorImpl<OperatorDecl *> &results) const override;
virtual void
getPrecedenceGroups(SmallVectorImpl<PrecedenceGroupDecl*> &results) const override;
virtual TypeDecl *lookupLocalType(llvm::StringRef MangledName) const override;
virtual void
getLocalTypeDecls(SmallVectorImpl<TypeDecl*> &results) const override;
virtual void
getOpaqueReturnTypeDecls(SmallVectorImpl<OpaqueTypeDecl*> &results) const override;
virtual void
getImportedModules(SmallVectorImpl<ImportedModule> &imports,
ModuleDecl::ImportFilter filter) const override;
virtual void
collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const override;
Identifier getDiscriminatorForPrivateValue(const ValueDecl *D) const override;
Identifier getPrivateDiscriminator() const { return PrivateDiscriminator; }
Optional<BasicDeclLocs> getBasicLocsForDecl(const Decl *D) const override;
/// Returns the synthesized file for this source file, if it exists.
SynthesizedFileUnit *getSynthesizedFile() const { return SynthesizedFile; };
SynthesizedFileUnit &getOrCreateSynthesizedFile();
virtual bool walk(ASTWalker &walker) override;
/// The buffer ID for the file that was imported, or None if there
/// is no associated buffer.
Optional<unsigned> getBufferID() const {
if (BufferID == -1)
return None;
return BufferID;
}
/// If this buffer corresponds to a file on disk, returns the path.
/// Otherwise, return an empty string.
StringRef getFilename() const;
/// Retrieve the scope that describes this source file.
ASTScope &getScope();
void clearScope() {
Scope = nullptr;
}
/// Retrieves the previously set delayed parser state, asserting that it
/// exists.
PersistentParserState *getDelayedParserState() {
// Force parsing of the top-level decls, which will set DelayedParserState
// if necessary.
// FIXME: Ideally the parser state should be an output of
// ParseSourceFileRequest, but the evaluator doesn't currently support
// move-only outputs for cached requests.
(void)getTopLevelDecls();
auto *state = DelayedParserState.get();
assert(state && "Didn't set any delayed parser state!");
return state;
}
/// Record delayed parser state for the source file. This is needed for code
/// completion's second pass.
void setDelayedParserState(ParserStatePtr &&state) {
DelayedParserState = std::move(state);
}
SWIFT_DEBUG_DUMP;
void dump(raw_ostream &os, bool parseIfNeeded = false) const;
/// Pretty-print the contents of this source file.
///
/// \param Printer The AST printer used for printing the contents.
/// \param PO Options controlling the printing process.
void print(ASTPrinter &Printer, const PrintOptions &PO);
void print(raw_ostream &OS, const PrintOptions &PO);
static bool classof(const FileUnit *file) {
return file->getKind() == FileUnitKind::Source;
}
static bool classof(const DeclContext *DC) {
return isa<FileUnit>(DC) && classof(cast<FileUnit>(DC));
}
/// True if this is a "script mode" source file that admits top-level code.
bool isScriptMode() const {
switch (Kind) {
case SourceFileKind::Main:
return true;
case SourceFileKind::Library:
case SourceFileKind::Interface:
case SourceFileKind::SIL:
return false;
}
llvm_unreachable("bad SourceFileKind");
}
Decl *getMainDecl() const override { return MainDecl; }
SourceLoc getMainDeclDiagLoc() const {
assert(hasMainDecl());
return MainDeclDiagLoc;
}
SourceLoc getMainClassDiagLoc() const {
assert(hasMainClass());
return getMainDeclDiagLoc();
}
/// Register a "main" class for the module, complaining if there is more than
/// one.
///
/// Should only be called during type-checking.
bool registerMainDecl(Decl *mainDecl, SourceLoc diagLoc);
/// True if this source file has an application entry point.
///
/// This is true if the source file either is in script mode or contains
/// a designated main class.
bool hasEntryPoint() const override {
return isScriptMode() || hasMainDecl();
}
/// Get the root refinement context for the file. The root context may be
/// null if the context hierarchy has not been built yet. Use
/// TypeChecker::getOrBuildTypeRefinementContext() to get a built
/// root of the hierarchy.
TypeRefinementContext *getTypeRefinementContext();
/// Set the root refinement context for the file.
void setTypeRefinementContext(TypeRefinementContext *TRC);
/// Whether this file has an interface hash available.
bool hasInterfaceHash() const {
return ParsingOpts.contains(ParsingFlags::EnableInterfaceHash);
}
/// Output this file's interface hash into the provided string buffer.
Fingerprint getInterfaceHash() const;
void dumpInterfaceHash(llvm::raw_ostream &out) {
out << getInterfaceHash() << '\n';
}
/// If this source file has been told to collect its parsed tokens, retrieve
/// those tokens.
ArrayRef<Token> getAllTokens() const;
/// Whether the parsed tokens of this source file should be saved, allowing
/// them to be accessed from \c getAllTokens.
bool shouldCollectTokens() const;
bool shouldBuildSyntaxTree() const;
/// Whether the bodies of types and functions within this file can be lazily
/// parsed.
bool hasDelayedBodyParsing() const;
syntax::SourceFileSyntax getSyntaxRoot() const;
OpaqueTypeDecl *lookupOpaqueResultType(StringRef MangledName) override;
/// Do not call when inside an inactive clause (\c
/// InInactiveClauseEnvironment)) because it will later on result in a lookup
/// to something that won't be in the ASTScope tree.
void addUnvalidatedDeclWithOpaqueResultType(ValueDecl *vd) {
UnvalidatedDeclsWithOpaqueReturnTypes.insert(vd);
}
ArrayRef<OpaqueTypeDecl *> getOpaqueReturnTypeDecls();
private:
/// If not \c None, the underlying vector contains the parsed tokens of this
/// source file.
Optional<ArrayRef<Token>> AllCollectedTokens;
/// The root of the syntax tree representing the source file.
std::unique_ptr<syntax::SourceFileSyntax> SyntaxRoot;
};
inline SourceFile::ParsingOptions operator|(SourceFile::ParsingFlags lhs,
SourceFile::ParsingFlags rhs) {
return SourceFile::ParsingOptions(lhs) | rhs;
}
inline SourceFile &ModuleDecl::getMainSourceFile() const {
assert(!Files.empty() && "No files added yet");
return *cast<SourceFile>(Files.front());
}
inline FileUnit *ModuleDecl::EntryPointInfoTy::getEntryPointFile() const {
return storage.getPointer();
}
inline void ModuleDecl::EntryPointInfoTy::setEntryPointFile(FileUnit *file) {
assert(!storage.getPointer());
storage.setPointer(file);
}
inline bool ModuleDecl::EntryPointInfoTy::hasEntryPoint() const {
return storage.getPointer();
}
inline bool ModuleDecl::EntryPointInfoTy::markDiagnosedMultipleMainClasses() {
bool res = storage.getInt().contains(Flags::DiagnosedMultipleMainClasses);
storage.setInt(storage.getInt() | Flags::DiagnosedMultipleMainClasses);
return !res;
}
inline bool ModuleDecl::EntryPointInfoTy::markDiagnosedMainClassWithScript() {
bool res = storage.getInt().contains(Flags::DiagnosedMainClassWithScript);
storage.setInt(storage.getInt() | Flags::DiagnosedMainClassWithScript);
return !res;
}
inline void simple_display(llvm::raw_ostream &out, const SourceFile *SF) {
assert(SF && "Cannot display null source file!");
out << "source_file " << '\"' << SF->getFilename() << '\"';
}
} // end namespace swift
#endif