Propagating prior merge from 'llvm.org/release_40'.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..a0c1644
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,14 @@
+By submitting a pull request, you represent that you have the right to license
+your contribution to Apple and the community, and agree by submitting the patch
+that your contributions are licensed under the [Swift
+license](https://swift.org/LICENSE.txt).
+
+---
+
+Changes to this repository follow special considerations as described on
+Swift.org under "[LLVM and Swift](https://swift.org/contributing/#llvm-and-swift)".
+Please make sure your change is appropriate for this repository.
+
+Before submitting a pull request, please make sure you have tested your
+changes and that they follow the Swift project [guidelines for contributing
+code](https://swift.org/contributing/#contributing-code).
diff --git a/include/clang/APINotes/APINotesManager.h b/include/clang/APINotes/APINotesManager.h
new file mode 100644
index 0000000..2adc29c
--- /dev/null
+++ b/include/clang/APINotes/APINotesManager.h
@@ -0,0 +1,147 @@
+//===--- APINotesManager.h - Manage API Notes Files -------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the APINotesManager interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APINOTES_APINOTESMANAGER_H
+#define LLVM_CLANG_APINOTES_APINOTESMANAGER_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/Module.h"
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+#include <string>
+
+namespace clang {
+
+class DirectoryEntry;
+class FileEntry;
+class LangOptions;
+class SourceManager;
+
+namespace api_notes {
+
+class APINotesReader;
+
+/// The API notes manager helps find API notes associated with declarations.
+///
+/// API notes are externally-provided annotations for declarations that can
+/// introduce new attributes (covering availability, nullability of
+/// parameters/results, and so on) for specific declarations without directly
+/// modifying the headers that contain those declarations.
+///
+/// The API notes manager is responsible for finding and loading the
+/// external API notes files that correspond to a given header. Its primary
+/// operation is \c findAPINotes(), which finds the API notes reader that
+/// provides information about the declarations at that location.
+class APINotesManager {
+ typedef llvm::PointerUnion<const DirectoryEntry *, APINotesReader *>
+ ReaderEntry;
+
+ SourceManager &SourceMgr;
+
+ /// Whether to implicitly search for API notes files based on the
+ /// source file from which an entity was declared.
+ bool ImplicitAPINotes;
+
+ /// The Swift version to use when interpreting versioned API notes.
+ VersionTuple SwiftVersion;
+
+ /// API notes readers for the current module.
+ ///
+ /// There can be up to two of these, one for public headers and one
+ /// for private headers.
+ APINotesReader *CurrentModuleReaders[2] = { nullptr, nullptr };
+
+ /// Whether we have already pruned the API notes cache.
+ bool PrunedCache;
+
+ /// A mapping from header file directories to the API notes reader for
+ /// that directory, or a redirection to another directory entry that may
+ /// have more information, or NULL to indicate that there is no API notes
+ /// reader for this directory.
+ llvm::DenseMap<const DirectoryEntry *, ReaderEntry> Readers;
+
+ /// Load the API notes associated with the given file, whether it is
+ /// the binary or source form of API notes.
+ ///
+ /// \returns the API notes reader for this file, or null if there is
+ /// a failure.
+ std::unique_ptr<APINotesReader> loadAPINotes(const FileEntry *apiNotesFile);
+
+ /// Load the given API notes file for the given header directory.
+ ///
+ /// \param HeaderDir The directory at which we
+ ///
+ /// \returns true if an error occurred.
+ bool loadAPINotes(const DirectoryEntry *HeaderDir,
+ const FileEntry *APINotesFile);
+
+ /// Look for API notes in the given directory.
+ ///
+ /// This might find either a binary or source API notes.
+ const FileEntry *findAPINotesFile(const DirectoryEntry *directory,
+ StringRef filename,
+ bool wantPublic = true);
+
+ /// Attempt to load API notes for the given framework.
+ ///
+ /// \param FrameworkPath The path to the framework.
+ /// \param Public Whether to load the public API notes. Otherwise, attempt
+ /// to load the private API notes.
+ ///
+ /// \returns the header directory entry (e.g., for Headers or PrivateHeaders)
+ /// for which the API notes were successfully loaded, or NULL if API notes
+ /// could not be loaded for any reason.
+ const DirectoryEntry *loadFrameworkAPINotes(llvm::StringRef FrameworkPath,
+ llvm::StringRef FrameworkName,
+ bool Public);
+
+public:
+ APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts);
+ ~APINotesManager();
+
+ /// Set the Swift version to use when filtering API notes.
+ void setSwiftVersion(VersionTuple swiftVersion) {
+ SwiftVersion = swiftVersion;
+ }
+
+ /// Load the API notes for the current module.
+ ///
+ /// \param module The current module.
+ /// \param lookInModule Whether to look inside the module itself.
+ /// \param searchPaths The paths in which we should search for API notes
+ /// for the current module.
+ ///
+ /// \returns true if API notes were successfully loaded, \c false otherwise.
+ bool loadCurrentModuleAPINotes(const Module *module,
+ bool lookInModule,
+ ArrayRef<std::string> searchPaths);
+
+ /// Retrieve the set of API notes readers for the current module.
+ ArrayRef<APINotesReader *> getCurrentModuleReaders() const {
+ unsigned numReaders = static_cast<unsigned>(CurrentModuleReaders[0] != nullptr) +
+ static_cast<unsigned>(CurrentModuleReaders[1] != nullptr);
+ return llvm::makeArrayRef(CurrentModuleReaders).slice(0, numReaders);
+ }
+
+ /// Find the API notes readers that correspond to the given source location.
+ llvm::SmallVector<APINotesReader *, 2> findAPINotes(SourceLocation Loc);
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+#endif
diff --git a/include/clang/APINotes/APINotesOptions.h b/include/clang/APINotes/APINotesOptions.h
new file mode 100644
index 0000000..24bb913
--- /dev/null
+++ b/include/clang/APINotes/APINotesOptions.h
@@ -0,0 +1,41 @@
+//===--- APINotesOptions.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the APINotesOptions class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APINOTES_APINOTESOPTIONS_H
+#define LLVM_CLANG_APINOTES_APINOTESOPTIONS_H
+
+#include "clang/Basic/VersionTuple.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+
+/// APINotesOptions - Track various options which control how API
+/// notes are found and handled.
+class APINotesOptions {
+public:
+ /// The Swift version which should be used for API notes.
+ VersionTuple SwiftVersion;
+
+ /// The set of search paths where we API notes can be found for
+ /// particular modules.
+ ///
+ /// The API notes in this directory are stored as
+ /// <ModuleName>.apinotes or <ModuleName>.apinotesc, and are only
+ /// applied when building the module <ModuleName>.
+ std::vector<std::string> ModuleSearchPaths;
+};
+
+} // end namespace clang
+
+#endif
diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h
new file mode 100644
index 0000000..2b985c6
--- /dev/null
+++ b/include/clang/APINotes/APINotesReader.h
@@ -0,0 +1,291 @@
+//===--- APINotesReader.h - API Notes Reader ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the \c APINotesReader class that reads source
+// API notes data providing additional information about source code as
+// a separate input, such as the non-nil/nilable annotations for
+// method parameters.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_API_NOTES_READER_H
+#define LLVM_CLANG_API_NOTES_READER_H
+
+#include "clang/APINotes/Types.h"
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <memory>
+
+namespace clang {
+namespace api_notes {
+
+/// Describes the role of a specific bit of versioned information.
+enum class VersionedInfoRole : unsigned {
+ /// Augment the AST, but do not override information explicitly specified
+ /// in the source code.
+ AugmentSource,
+
+ /// Replace information that may have been explicitly specified in the source
+ /// code.
+ ReplaceSource,
+
+ /// Describes an alternate version of this information.
+ Versioned,
+};
+
+/// A class that reads API notes data from a binary file that was written by
+/// the \c APINotesWriter.
+class APINotesReader {
+ class Implementation;
+
+ Implementation &Impl;
+
+ APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer,
+ VersionTuple swiftVersion, bool &failed);
+
+public:
+ /// Create a new API notes reader from the given member buffer, which
+ /// contains the contents of a binary API notes file.
+ ///
+ /// \returns the new API notes reader, or null if an error occurred.
+ static std::unique_ptr<APINotesReader>
+ get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer,
+ VersionTuple swiftVersion);
+
+ /// Create a new API notes reader from the given member buffer, which
+ /// contains the contents of a binary API notes file.
+ ///
+ /// \returns the new API notes reader, or null if an error occurred.
+ static std::unique_ptr<APINotesReader>
+ getUnmanaged(llvm::MemoryBuffer *inputBuffer,
+ VersionTuple swiftVersion);
+
+ ~APINotesReader();
+
+ APINotesReader(const APINotesReader &) = delete;
+ APINotesReader &operator=(const APINotesReader &) = delete;
+
+ /// Retrieve the name of the module for which this reader is providing API
+ /// notes.
+ StringRef getModuleName() const;
+
+ /// Retrieve the size and modification time of the source file from
+ /// which this API notes file was created, if known.
+ Optional<std::pair<off_t, time_t>> getSourceFileSizeAndModTime() const;
+
+ /// Retrieve the module options
+ ModuleOptions getModuleOptions() const;
+
+ /// Captures the completed versioned information for a particular part of
+ /// API notes, including both unversioned API notes and each versioned API
+ /// note for that particular entity.
+ template<typename T>
+ class VersionedInfo {
+ /// The complete set of results.
+ SmallVector<std::pair<VersionTuple, T>, 1> Results;
+
+ /// The index of the result that is the "selected" set based on the desired
+ /// Swift version, or \c Results.size() if nothing matched.
+ unsigned Selected;
+
+ /// The role of the selected index.
+ VersionedInfoRole SelectedRole;
+
+ public:
+ /// Form an empty set of versioned information.
+ VersionedInfo(llvm::NoneType) : Selected(0) { }
+
+ /// Form a versioned info set given the desired version and a set of
+ /// results.
+ VersionedInfo(VersionTuple version,
+ SmallVector<std::pair<VersionTuple, T>, 1> results);
+
+ /// Determine whether there is a result that should be applied directly
+ /// to the AST.
+ explicit operator bool() const { return Selected != size(); }
+
+ /// Retrieve the information to apply directly to the AST.
+ const T& operator*() const {
+ assert(*this && "No result to apply directly");
+ return (*this)[Selected].second;
+ }
+
+ /// Retrieve the selected index in the result set.
+ Optional<unsigned> getSelected() const {
+ if (Selected == Results.size()) return None;
+ return Selected;
+ }
+
+ /// Describes the role of the selected entity.
+ VersionedInfoRole getSelectedRole() const {
+ return SelectedRole;
+ }
+
+ /// Return the number of versioned results we know about.
+ unsigned size() const { return Results.size(); }
+
+ /// Access all versioned results.
+ const std::pair<VersionTuple, T> *begin() const { return Results.begin(); }
+ const std::pair<VersionTuple, T> *end() const { return Results.end(); }
+
+ /// Access a specific versioned result.
+ const std::pair<VersionTuple, T> &operator[](unsigned index) const {
+ return Results[index];
+ }
+ };
+
+ /// Look for the context ID of the given Objective-C class.
+ ///
+ /// \param name The name of the class we're looking for.
+ ///
+ /// \returns The ID, if known.
+ Optional<ContextID> lookupObjCClassID(StringRef name);
+
+ /// Look for information regarding the given Objective-C class.
+ ///
+ /// \param name The name of the class we're looking for.
+ ///
+ /// \returns The information about the class, if known.
+ VersionedInfo<ObjCContextInfo> lookupObjCClassInfo(StringRef name);
+
+ /// Look for the context ID of the given Objective-C protocol.
+ ///
+ /// \param name The name of the protocol we're looking for.
+ ///
+ /// \returns The ID of the protocol, if known.
+ Optional<ContextID> lookupObjCProtocolID(StringRef name);
+
+ /// Look for information regarding the given Objective-C protocol.
+ ///
+ /// \param name The name of the protocol we're looking for.
+ ///
+ /// \returns The information about the protocol, if known.
+ VersionedInfo<ObjCContextInfo> lookupObjCProtocolInfo(StringRef name);
+
+ /// Look for information regarding the given Objective-C property in
+ /// the given context.
+ ///
+ /// \param contextID The ID that references the context we are looking for.
+ /// \param name The name of the property we're looking for.
+ /// \param isInstance Whether we are looking for an instance property (vs.
+ /// a class property).
+ ///
+ /// \returns Information about the property, if known.
+ VersionedInfo<ObjCPropertyInfo> lookupObjCProperty(ContextID contextID,
+ StringRef name,
+ bool isInstance);
+
+ /// Look for information regarding the given Objective-C method in
+ /// the given context.
+ ///
+ /// \param contextID The ID that references the context we are looking for.
+ /// \param selector The selector naming the method we're looking for.
+ /// \param isInstanceMethod Whether we are looking for an instance method.
+ ///
+ /// \returns Information about the method, if known.
+ VersionedInfo<ObjCMethodInfo> lookupObjCMethod(ContextID contextID,
+ ObjCSelectorRef selector,
+ bool isInstanceMethod);
+
+ /// Look for information regarding the given global variable.
+ ///
+ /// \param name The name of the global variable.
+ ///
+ /// \returns information about the global variable, if known.
+ VersionedInfo<GlobalVariableInfo> lookupGlobalVariable(StringRef name);
+
+ /// Look for information regarding the given global function.
+ ///
+ /// \param name The name of the global function.
+ ///
+ /// \returns information about the global function, if known.
+ VersionedInfo<GlobalFunctionInfo> lookupGlobalFunction(StringRef name);
+
+ /// Look for information regarding the given enumerator.
+ ///
+ /// \param name The name of the enumerator.
+ ///
+ /// \returns information about the enumerator, if known.
+ VersionedInfo<EnumConstantInfo> lookupEnumConstant(StringRef name);
+
+ /// Look for information regarding the given tag
+ /// (struct/union/enum/C++ class).
+ ///
+ /// \param name The name of the tag.
+ ///
+ /// \returns information about the tag, if known.
+ VersionedInfo<TagInfo> lookupTag(StringRef name);
+
+ /// Look for information regarding the given typedef.
+ ///
+ /// \param name The name of the typedef.
+ ///
+ /// \returns information about the typedef, if known.
+ VersionedInfo<TypedefInfo> lookupTypedef(StringRef name);
+
+ /// Visitor used when walking the contents of the API notes file.
+ class Visitor {
+ public:
+ virtual ~Visitor();
+
+ /// Visit an Objective-C class.
+ virtual void visitObjCClass(ContextID contextID, StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit an Objective-C protocol.
+ virtual void visitObjCProtocol(ContextID contextID, StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit an Objective-C method.
+ virtual void visitObjCMethod(ContextID contextID, StringRef selector,
+ bool isInstanceMethod,
+ const ObjCMethodInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit an Objective-C property.
+ virtual void visitObjCProperty(ContextID contextID, StringRef name,
+ bool isInstance,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit a global variable.
+ virtual void visitGlobalVariable(StringRef name,
+ const GlobalVariableInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit a global function.
+ virtual void visitGlobalFunction(StringRef name,
+ const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit an enumerator.
+ virtual void visitEnumConstant(StringRef name,
+ const EnumConstantInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit a tag.
+ virtual void visitTag(StringRef name, const TagInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit a typedef.
+ virtual void visitTypedef(StringRef name, const TypedefInfo &info,
+ VersionTuple swiftVersion);
+ };
+
+ /// Visit the contents of the API notes file, passing each entity to the
+ /// given visitor.
+ void visit(Visitor &visitor);
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+#endif // LLVM_CLANG_API_NOTES_READER_H
diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h
new file mode 100644
index 0000000..62defc1
--- /dev/null
+++ b/include/clang/APINotes/APINotesWriter.h
@@ -0,0 +1,126 @@
+//===--- APINotesWriter.h - API Notes Writer ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the \c APINotesWriter class that writes out source
+// API notes data providing additional information about source code as
+// a separate input, such as the non-nil/nilable annotations for
+// method parameters.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_API_NOTES_WRITER_H
+#define LLVM_CLANG_API_NOTES_WRITER_H
+
+#include "clang/Basic/VersionTuple.h"
+#include "clang/APINotes/Types.h"
+
+namespace llvm {
+ class raw_ostream;
+}
+
+namespace clang {
+
+class FileEntry;
+
+namespace api_notes {
+
+/// A class that writes API notes data to a binary representation that can be
+/// read by the \c APINotesReader.
+class APINotesWriter {
+ class Implementation;
+ Implementation &Impl;
+
+public:
+ /// Create a new API notes writer with the given module name and
+ /// (optional) source file.
+ APINotesWriter(StringRef moduleName, const FileEntry *sourceFile);
+ ~APINotesWriter();
+
+ APINotesWriter(const APINotesWriter &) = delete;
+ APINotesWriter &operator=(const APINotesWriter &) = delete;
+
+ /// Write the API notes data to the given stream.
+ void writeToStream(llvm::raw_ostream &os);
+
+ /// Add information about a specific Objective-C class or protocol.
+ ///
+ /// \param name The name of this class/protocol.
+ /// \param isClass Whether this is a class (vs. a protocol).
+ /// \param info Information about this class/protocol.
+ ///
+ /// \returns the ID of the class or protocol, which can be used to add
+ /// properties and methods to the class/protocol.
+ ContextID addObjCContext(StringRef name, bool isClass,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a specific Objective-C property.
+ ///
+ /// \param contextID The context in which this property resides.
+ /// \param name The name of this property.
+ /// \param info Information about this property.
+ void addObjCProperty(ContextID contextID, StringRef name,
+ bool isInstanceProperty,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a specific Objective-C method.
+ ///
+ /// \param contextID The context in which this method resides.
+ /// \param selector The selector that names this method.
+ /// \param isInstanceMethod Whether this method is an instance method
+ /// (vs. a class method).
+ /// \param info Information about this method.
+ void addObjCMethod(ContextID contextID, ObjCSelectorRef selector,
+ bool isInstanceMethod, const ObjCMethodInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a global variable.
+ ///
+ /// \param name The name of this global variable.
+ /// \param info Information about this global variable.
+ void addGlobalVariable(StringRef name, const GlobalVariableInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a global function.
+ ///
+ /// \param name The name of this global function.
+ /// \param info Information about this global function.
+ void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about an enumerator.
+ ///
+ /// \param name The name of this enumerator.
+ /// \param info Information about this enumerator.
+ void addEnumConstant(StringRef name, const EnumConstantInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a tag (struct/union/enum/C++ class).
+ ///
+ /// \param name The name of this tag.
+ /// \param info Information about this tag.
+ void addTag(StringRef name, const TagInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a typedef.
+ ///
+ /// \param name The name of this typedef.
+ /// \param info Information about this typedef.
+ void addTypedef(StringRef name, const TypedefInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add module options
+ void addModuleOptions(ModuleOptions opts);
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+#endif // LLVM_CLANG_API_NOTES_WRITER_H
+
diff --git a/include/clang/APINotes/APINotesYAMLCompiler.h b/include/clang/APINotes/APINotesYAMLCompiler.h
new file mode 100644
index 0000000..508da65
--- /dev/null
+++ b/include/clang/APINotes/APINotesYAMLCompiler.h
@@ -0,0 +1,62 @@
+//=== APINotesYAMLCompiler.h - API Notes YAML to binary compiler *- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file reads sidecar API notes specified in YAML format.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_API_NOTES_YAML_COMPILER_H
+#define LLVM_CLANG_API_NOTES_YAML_COMPILER_H
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/SourceMgr.h"
+#include <memory>
+
+namespace llvm {
+ class raw_ostream;
+ class MemoryBuffer;
+}
+
+namespace clang {
+
+class FileEntry;
+
+namespace api_notes {
+
+ enum class ActionType {
+ None,
+ YAMLToBinary,
+ BinaryToYAML,
+ Dump,
+ };
+
+ enum class OSType {
+ OSX,
+ IOS,
+ TvOS,
+ WatchOS,
+ Absent
+ };
+
+ /// Converts API notes from YAML format to binary format.
+ bool compileAPINotes(llvm::StringRef yamlInput,
+ const FileEntry *sourceFile,
+ llvm::raw_ostream &os,
+ OSType targetOS,
+ llvm::SourceMgr::DiagHandlerTy diagHandler = nullptr,
+ void *diagHandlerCtxt = nullptr);
+
+ bool parseAndDumpAPINotes(llvm::StringRef yamlInput);
+
+ /// Converts API notes from the compiled binary format to the YAML format.
+ bool decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input,
+ llvm::raw_ostream &os);
+} // end namespace api_notes
+} // end namespace clang
+
+#endif // LLVM_CLANG_API_NOTES_YAML_COMPILER_H
diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h
new file mode 100644
index 0000000..6fbd5d8
--- /dev/null
+++ b/include/clang/APINotes/Types.h
@@ -0,0 +1,658 @@
+//===--- Types.h - API Notes Data Types --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines data types used in the representation of API notes data.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_API_NOTES_TYPES_H
+#define LLVM_CLANG_API_NOTES_TYPES_H
+#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include <cassert>
+#include <climits>
+
+namespace llvm {
+ class raw_ostream;
+}
+
+namespace clang {
+namespace api_notes {
+
+/// The file extension used for the source representation of API notes.
+static const char SOURCE_APINOTES_EXTENSION[] = "apinotes";
+
+/// The file extension used for the binary representation of API notes.
+static const char BINARY_APINOTES_EXTENSION[] = "apinotesc";
+
+using llvm::ArrayRef;
+using llvm::StringRef;
+using llvm::Optional;
+using llvm::None;
+
+/// Describes whether to classify a factory method as an initializer.
+enum class FactoryAsInitKind {
+ /// Infer based on name and type (the default).
+ Infer,
+ /// Treat as a class method.
+ AsClassMethod,
+ /// Treat as an initializer.
+ AsInitializer
+};
+
+/// Opaque context ID used to refer to an Objective-C class or protocol.
+class ContextID {
+public:
+ unsigned Value;
+
+ explicit ContextID(unsigned value) : Value(value) { }
+};
+
+/// Describes API notes data for any entity.
+///
+/// This is used as the base of all API notes.
+class CommonEntityInfo {
+public:
+ /// Message to use when this entity is unavailable.
+ std::string UnavailableMsg;
+
+ /// Whether this entity is marked unavailable.
+ unsigned Unavailable : 1;
+
+ /// Whether this entity is marked unavailable in Swift.
+ unsigned UnavailableInSwift : 1;
+
+private:
+ /// Whether SwiftPrivate was specified.
+ unsigned SwiftPrivateSpecified : 1;
+
+ /// Whether this entity is considered "private" to a Swift overlay.
+ unsigned SwiftPrivate : 1;
+
+public:
+ /// Swift name of this entity.
+ std::string SwiftName;
+
+ CommonEntityInfo()
+ : Unavailable(0), UnavailableInSwift(0), SwiftPrivateSpecified(0),
+ SwiftPrivate(0) { }
+
+ Optional<bool> isSwiftPrivate() const {
+ if (!SwiftPrivateSpecified) return None;
+ return SwiftPrivate;
+ }
+
+ void setSwiftPrivate(Optional<bool> swiftPrivate) {
+ if (swiftPrivate) {
+ SwiftPrivateSpecified = 1;
+ SwiftPrivate = *swiftPrivate;
+ } else {
+ SwiftPrivateSpecified = 0;
+ SwiftPrivate = 0;
+ }
+ }
+
+ friend bool operator==(const CommonEntityInfo &lhs,
+ const CommonEntityInfo &rhs) {
+ return lhs.UnavailableMsg == rhs.UnavailableMsg &&
+ lhs.Unavailable == rhs.Unavailable &&
+ lhs.UnavailableInSwift == rhs.UnavailableInSwift &&
+ lhs.SwiftPrivateSpecified == rhs.SwiftPrivateSpecified &&
+ lhs.SwiftPrivate == rhs.SwiftPrivate &&
+ lhs.SwiftName == rhs.SwiftName;
+ }
+
+ friend bool operator!=(const CommonEntityInfo &lhs,
+ const CommonEntityInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+ friend CommonEntityInfo &operator|=(CommonEntityInfo &lhs,
+ const CommonEntityInfo &rhs) {
+ // Merge unavailability.
+ if (rhs.Unavailable) {
+ lhs.Unavailable = true;
+ if (rhs.UnavailableMsg.length() != 0 &&
+ lhs.UnavailableMsg.length() == 0) {
+ lhs.UnavailableMsg = rhs.UnavailableMsg;
+ }
+ }
+
+ if (rhs.UnavailableInSwift) {
+ lhs.UnavailableInSwift = true;
+ if (rhs.UnavailableMsg.length() != 0 &&
+ lhs.UnavailableMsg.length() == 0) {
+ lhs.UnavailableMsg = rhs.UnavailableMsg;
+ }
+ }
+
+ if (rhs.SwiftPrivateSpecified && !lhs.SwiftPrivateSpecified) {
+ lhs.SwiftPrivateSpecified = 1;
+ lhs.SwiftPrivate = rhs.SwiftPrivate;
+ }
+
+ if (rhs.SwiftName.length() != 0 &&
+ lhs.SwiftName.length() == 0)
+ lhs.SwiftName = rhs.SwiftName;
+
+ return lhs;
+ }
+};
+
+/// Describes API notes for types.
+class CommonTypeInfo : public CommonEntityInfo {
+ /// The Swift type to which a given type is bridged.
+ ///
+ /// Reflects the swift_bridge attribute.
+ Optional<std::string> SwiftBridge;
+
+ /// The NS error domain for this type.
+ Optional<std::string> NSErrorDomain;
+
+public:
+ CommonTypeInfo() : CommonEntityInfo() { }
+
+ const Optional<std::string> &getSwiftBridge() const { return SwiftBridge; }
+
+ void setSwiftBridge(const Optional<std::string> &swiftType) {
+ SwiftBridge = swiftType;
+ }
+
+ void setSwiftBridge(const Optional<StringRef> &swiftType) {
+ if (swiftType)
+ SwiftBridge = *swiftType;
+ else
+ SwiftBridge = None;
+ }
+
+ const Optional<std::string> &getNSErrorDomain() const {
+ return NSErrorDomain;
+ }
+
+ void setNSErrorDomain(const Optional<std::string> &domain) {
+ NSErrorDomain = domain;
+ }
+
+ void setNSErrorDomain(const Optional<StringRef> &domain) {
+ if (domain)
+ NSErrorDomain = *domain;
+ else
+ NSErrorDomain = None;
+ }
+
+ friend CommonTypeInfo &operator|=(CommonTypeInfo &lhs,
+ const CommonTypeInfo &rhs) {
+ static_cast<CommonEntityInfo &>(lhs) |= rhs;
+ if (!lhs.SwiftBridge && rhs.SwiftBridge)
+ lhs.SwiftBridge = rhs.SwiftBridge;
+ if (!lhs.NSErrorDomain && rhs.NSErrorDomain)
+ lhs.NSErrorDomain = rhs.NSErrorDomain;
+ return lhs;
+ }
+
+ friend bool operator==(const CommonTypeInfo &lhs,
+ const CommonTypeInfo &rhs) {
+ return static_cast<const CommonEntityInfo &>(lhs) == rhs &&
+ lhs.SwiftBridge == rhs.SwiftBridge &&
+ lhs.NSErrorDomain == rhs.NSErrorDomain;
+ }
+
+ friend bool operator!=(const CommonTypeInfo &lhs,
+ const CommonTypeInfo &rhs) {
+ return !(lhs == rhs);
+ }
+};
+
+/// Describes API notes data for an Objective-C class or protocol.
+class ObjCContextInfo : public CommonTypeInfo {
+ /// Whether this class has a default nullability.
+ unsigned HasDefaultNullability : 1;
+
+ /// The default nullability.
+ unsigned DefaultNullability : 2;
+
+ /// Whether this class has designated initializers recorded.
+ unsigned HasDesignatedInits : 1;
+
+public:
+ ObjCContextInfo()
+ : CommonTypeInfo(),
+ HasDefaultNullability(0),
+ DefaultNullability(0),
+ HasDesignatedInits(0)
+ { }
+
+ /// Determine the default nullability for properties and methods of this
+ /// class.
+ ///
+ /// \returns the default nullability, if implied, or None if there is no
+ Optional<NullabilityKind> getDefaultNullability() const {
+ if (HasDefaultNullability)
+ return static_cast<NullabilityKind>(DefaultNullability);
+
+ return None;
+ }
+
+ /// Set the default nullability for properties and methods of this class.
+ void setDefaultNullability(NullabilityKind kind) {
+ HasDefaultNullability = true;
+ DefaultNullability = static_cast<unsigned>(kind);
+ }
+
+ bool hasDesignatedInits() const { return HasDesignatedInits; }
+ void setHasDesignatedInits(bool value) { HasDesignatedInits = value; }
+
+ /// Strip off any information within the class information structure that is
+ /// module-local, such as 'audited' flags.
+ void stripModuleLocalInfo() {
+ HasDefaultNullability = false;
+ DefaultNullability = 0;
+ }
+
+ friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) {
+ return static_cast<const CommonTypeInfo &>(lhs) == rhs &&
+ lhs.HasDefaultNullability == rhs.HasDefaultNullability &&
+ lhs.DefaultNullability == rhs.DefaultNullability &&
+ lhs.HasDesignatedInits == rhs.HasDesignatedInits;
+ }
+
+ friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+ friend ObjCContextInfo &operator|=(ObjCContextInfo &lhs,
+ const ObjCContextInfo &rhs) {
+ // Merge inherited info.
+ static_cast<CommonTypeInfo &>(lhs) |= rhs;
+
+ // Merge nullability.
+ if (!lhs.getDefaultNullability()) {
+ if (auto nullable = rhs.getDefaultNullability()) {
+ lhs.setDefaultNullability(*nullable);
+ }
+ }
+
+ lhs.HasDesignatedInits |= rhs.HasDesignatedInits;
+
+ return lhs;
+ }
+
+ void dump(llvm::raw_ostream &os);
+};
+
+/// API notes for a variable/property.
+class VariableInfo : public CommonEntityInfo {
+ /// Whether this property has been audited for nullability.
+ unsigned NullabilityAudited : 1;
+
+ /// The kind of nullability for this property. Only valid if the nullability
+ /// has been audited.
+ unsigned Nullable : 2;
+
+ /// The C type of the variable, as a string.
+ std::string Type;
+
+public:
+ VariableInfo()
+ : CommonEntityInfo(),
+ NullabilityAudited(false),
+ Nullable(0) { }
+
+ Optional<NullabilityKind> getNullability() const {
+ if (NullabilityAudited)
+ return static_cast<NullabilityKind>(Nullable);
+
+ return None;
+ }
+
+ void setNullabilityAudited(NullabilityKind kind) {
+ NullabilityAudited = true;
+ Nullable = static_cast<unsigned>(kind);
+ }
+
+ const std::string &getType() const { return Type; }
+ void setType(const std::string &type) { Type = type; }
+
+ friend bool operator==(const VariableInfo &lhs, const VariableInfo &rhs) {
+ return static_cast<const CommonEntityInfo &>(lhs) == rhs &&
+ lhs.NullabilityAudited == rhs.NullabilityAudited &&
+ lhs.Nullable == rhs.Nullable &&
+ lhs.Type == rhs.Type;
+ }
+
+ friend bool operator!=(const VariableInfo &lhs, const VariableInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+ friend VariableInfo &operator|=(VariableInfo &lhs,
+ const VariableInfo &rhs) {
+ static_cast<CommonEntityInfo &>(lhs) |= rhs;
+ if (!lhs.NullabilityAudited && rhs.NullabilityAudited)
+ lhs.setNullabilityAudited(*rhs.getNullability());
+ if (lhs.Type.empty() && !rhs.Type.empty())
+ lhs.Type = rhs.Type;
+ return lhs;
+ }
+};
+
+/// Describes API notes data for an Objective-C property.
+class ObjCPropertyInfo : public VariableInfo {
+ unsigned SwiftImportAsAccessorsSpecified : 1;
+ unsigned SwiftImportAsAccessors : 1;
+
+public:
+ ObjCPropertyInfo()
+ : VariableInfo(), SwiftImportAsAccessorsSpecified(false),
+ SwiftImportAsAccessors(false) {}
+
+ /// Merge class-wide information into the given property.
+ friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs,
+ const ObjCContextInfo &rhs) {
+ static_cast<VariableInfo &>(lhs) |= rhs;
+
+ // Merge nullability.
+ if (!lhs.getNullability()) {
+ if (auto nullable = rhs.getDefaultNullability()) {
+ lhs.setNullabilityAudited(*nullable);
+ }
+ }
+
+ return lhs;
+ }
+
+ Optional<bool> getSwiftImportAsAccessors() const {
+ if (SwiftImportAsAccessorsSpecified)
+ return SwiftImportAsAccessors;
+ return None;
+ }
+ void setSwiftImportAsAccessors(Optional<bool> value) {
+ if (value.hasValue()) {
+ SwiftImportAsAccessorsSpecified = true;
+ SwiftImportAsAccessors = value.getValue();
+ } else {
+ SwiftImportAsAccessorsSpecified = false;
+ SwiftImportAsAccessors = false;
+ }
+ }
+
+ friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs,
+ const ObjCPropertyInfo &rhs) {
+ lhs |= static_cast<const VariableInfo &>(rhs);
+ if (!lhs.SwiftImportAsAccessorsSpecified &&
+ rhs.SwiftImportAsAccessorsSpecified) {
+ lhs.SwiftImportAsAccessorsSpecified = true;
+ lhs.SwiftImportAsAccessors = rhs.SwiftImportAsAccessors;
+ }
+ return lhs;
+ }
+};
+
+/// Describes a function or method parameter.
+class ParamInfo : public VariableInfo {
+ /// Whether noescape was specified.
+ unsigned NoEscapeSpecified : 1;
+
+ /// Whether the this parameter has the 'noescape' attribute.
+ unsigned NoEscape : 1;
+
+public:
+ ParamInfo() : VariableInfo(), NoEscapeSpecified(false), NoEscape(false) { }
+
+ Optional<bool> isNoEscape() const {
+ if (!NoEscapeSpecified) return None;
+ return NoEscape;
+ }
+ void setNoEscape(Optional<bool> noescape) {
+ if (noescape) {
+ NoEscapeSpecified = true;
+ NoEscape = *noescape;
+ } else {
+ NoEscapeSpecified = false;
+ NoEscape = false;
+ }
+ }
+
+ friend ParamInfo &operator|=(ParamInfo &lhs, const ParamInfo &rhs) {
+ static_cast<VariableInfo &>(lhs) |= rhs;
+ if (!lhs.NoEscapeSpecified && rhs.NoEscapeSpecified) {
+ lhs.NoEscapeSpecified = true;
+ lhs.NoEscape = rhs.NoEscape;
+ }
+ return lhs;
+ }
+
+ friend bool operator==(const ParamInfo &lhs, const ParamInfo &rhs) {
+ return static_cast<const VariableInfo &>(lhs) == rhs &&
+ lhs.NoEscapeSpecified == rhs.NoEscapeSpecified &&
+ lhs.NoEscape == rhs.NoEscape;
+ }
+
+ friend bool operator!=(const ParamInfo &lhs, const ParamInfo &rhs) {
+ return !(lhs == rhs);
+ }
+};
+
+/// A temporary reference to an Objective-C selector, suitable for
+/// referencing selector data on the stack.
+///
+/// Instances of this struct do not store references to any of the
+/// data they contain; it is up to the user to ensure that the data
+/// referenced by the identifier list persists.
+struct ObjCSelectorRef {
+ unsigned NumPieces;
+ ArrayRef<StringRef> Identifiers;
+};
+
+/// API notes for a function or method.
+class FunctionInfo : public CommonEntityInfo {
+private:
+ static unsigned const NullabilityKindMask = 0x3;
+ static unsigned const NullabilityKindSize = 2;
+
+public:
+ /// Whether the signature has been audited with respect to nullability.
+ /// If yes, we consider all types to be non-nullable unless otherwise noted.
+ /// If this flag is not set, the pointer types are considered to have
+ /// unknown nullability.
+ unsigned NullabilityAudited : 1;
+
+ /// Number of types whose nullability is encoded with the NullabilityPayload.
+ unsigned NumAdjustedNullable : 8;
+
+ /// Stores the nullability of the return type and the parameters.
+ // NullabilityKindSize bits are used to encode the nullability. The info
+ // about the return type is stored at position 0, followed by the nullability
+ // of the parameters.
+ uint64_t NullabilityPayload = 0;
+
+ /// The result type of this function, as a C type.
+ std::string ResultType;
+
+ /// The function parameters.
+ std::vector<ParamInfo> Params;
+
+ FunctionInfo()
+ : CommonEntityInfo(),
+ NullabilityAudited(false),
+ NumAdjustedNullable(0) { }
+
+ static unsigned getMaxNullabilityIndex() {
+ return ((sizeof(NullabilityPayload) * CHAR_BIT)/NullabilityKindSize);
+ }
+
+ void addTypeInfo(unsigned index, NullabilityKind kind) {
+ assert(index <= getMaxNullabilityIndex());
+ assert(static_cast<unsigned>(kind) < NullabilityKindMask);
+ NullabilityAudited = true;
+ if (NumAdjustedNullable < index + 1)
+ NumAdjustedNullable = index + 1;
+
+ // Mask the bits.
+ NullabilityPayload &= ~(NullabilityKindMask << (index * NullabilityKindSize));
+
+ // Set the value.
+ unsigned kindValue =
+ (static_cast<unsigned>(kind)) << (index * NullabilityKindSize);
+ NullabilityPayload |= kindValue;
+ }
+
+ /// Adds the return type info.
+ void addReturnTypeInfo(NullabilityKind kind) {
+ addTypeInfo(0, kind);
+ }
+
+ /// Adds the parameter type info.
+ void addParamTypeInfo(unsigned index, NullabilityKind kind) {
+ addTypeInfo(index + 1, kind);
+ }
+
+private:
+ NullabilityKind getTypeInfo(unsigned index) const {
+ assert(NullabilityAudited &&
+ "Checking the type adjustment on non-audited method.");
+ // If we don't have info about this parameter, return the default.
+ if (index > NumAdjustedNullable)
+ return NullabilityKind::NonNull;
+ return static_cast<NullabilityKind>(( NullabilityPayload
+ >> (index * NullabilityKindSize) )
+ & NullabilityKindMask);
+ }
+
+public:
+ NullabilityKind getParamTypeInfo(unsigned index) const {
+ return getTypeInfo(index + 1);
+ }
+
+ NullabilityKind getReturnTypeInfo() const {
+ return getTypeInfo(0);
+ }
+
+ friend bool operator==(const FunctionInfo &lhs, const FunctionInfo &rhs) {
+ return static_cast<const CommonEntityInfo &>(lhs) == rhs &&
+ lhs.NullabilityAudited == rhs.NullabilityAudited &&
+ lhs.NumAdjustedNullable == rhs.NumAdjustedNullable &&
+ lhs.NullabilityPayload == rhs.NullabilityPayload &&
+ lhs.ResultType == rhs.ResultType &&
+ lhs.Params == rhs.Params;
+ }
+
+ friend bool operator!=(const FunctionInfo &lhs, const FunctionInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+};
+
+/// Describes API notes data for an Objective-C method.
+class ObjCMethodInfo : public FunctionInfo {
+public:
+ /// Whether this is a designated initializer of its class.
+ unsigned DesignatedInit : 1;
+
+ /// Whether to treat this method as a factory or initializer.
+ unsigned FactoryAsInit : 2;
+
+ /// Whether this is a required initializer.
+ unsigned Required : 1;
+
+ ObjCMethodInfo()
+ : FunctionInfo(),
+ DesignatedInit(false),
+ FactoryAsInit(static_cast<unsigned>(FactoryAsInitKind::Infer)),
+ Required(false) { }
+
+ FactoryAsInitKind getFactoryAsInitKind() const {
+ return static_cast<FactoryAsInitKind>(FactoryAsInit);
+ }
+
+ void setFactoryAsInitKind(FactoryAsInitKind kind) {
+ FactoryAsInit = static_cast<unsigned>(kind);
+ }
+
+ friend bool operator==(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) {
+ return static_cast<const FunctionInfo &>(lhs) == rhs &&
+ lhs.DesignatedInit == rhs.DesignatedInit &&
+ lhs.FactoryAsInit == rhs.FactoryAsInit &&
+ lhs.Required == rhs.Required;
+ }
+
+ friend bool operator!=(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+ void mergePropInfoIntoSetter(const ObjCPropertyInfo &pInfo);
+
+ void mergePropInfoIntoGetter(const ObjCPropertyInfo &pInfo);
+
+ /// Merge class-wide information into the given method.
+ friend ObjCMethodInfo &operator|=(ObjCMethodInfo &lhs,
+ const ObjCContextInfo &rhs) {
+ // Merge nullability.
+ if (!lhs.NullabilityAudited) {
+ if (auto nullable = rhs.getDefaultNullability()) {
+ lhs.NullabilityAudited = true;
+ lhs.addTypeInfo(0, *nullable);
+ }
+ }
+
+ return lhs;
+ }
+
+ void dump(llvm::raw_ostream &os);
+};
+
+/// Describes API notes data for a global variable.
+class GlobalVariableInfo : public VariableInfo {
+public:
+ GlobalVariableInfo() : VariableInfo() { }
+};
+
+/// Describes API notes data for a global function.
+class GlobalFunctionInfo : public FunctionInfo {
+public:
+ GlobalFunctionInfo() : FunctionInfo() { }
+};
+
+/// Describes API notes data for an enumerator.
+class EnumConstantInfo : public CommonEntityInfo {
+public:
+ EnumConstantInfo() : CommonEntityInfo() { }
+};
+
+/// Describes API notes data for a tag.
+class TagInfo : public CommonTypeInfo {
+public:
+ TagInfo() : CommonTypeInfo() { }
+};
+
+/// The kind of a swift_wrapper/swift_newtype.
+enum class SwiftWrapperKind {
+ None,
+ Struct,
+ Enum
+};
+
+/// Describes API notes data for a typedef.
+class TypedefInfo : public CommonTypeInfo {
+public:
+ Optional<SwiftWrapperKind> SwiftWrapper;
+
+ TypedefInfo() : CommonTypeInfo() { }
+};
+
+/// Descripts a series of options for a module
+struct ModuleOptions {
+ bool SwiftInferImportAsMember = false;
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+#endif // LLVM_CLANG_API_NOTES_TYPES_H
diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h
index bbe320c..58566db 100644
--- a/include/clang/AST/Attr.h
+++ b/include/clang/AST/Attr.h
@@ -24,6 +24,7 @@
#include "clang/Basic/Sanitizers.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/PointerEmbeddedInt.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
diff --git a/include/clang/AST/AttrIterator.h b/include/clang/AST/AttrIterator.h
index fb9b049..5ef250c 100644
--- a/include/clang/AST/AttrIterator.h
+++ b/include/clang/AST/AttrIterator.h
@@ -108,6 +108,8 @@
specific_attr_iterator Right) {
return !(Left == Right);
}
+
+ Iterator getCurrent() const { return Current; }
};
template <typename SpecificAttr, typename Container>
diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h
index 2e99f39..127c324 100644
--- a/include/clang/AST/ExternalASTSource.h
+++ b/include/clang/AST/ExternalASTSource.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_EXTERNALASTSOURCE_H
#define LLVM_CLANG_AST_EXTERNALASTSOURCE_H
+#include "clang/Basic/Module.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclBase.h"
#include "llvm/ADT/DenseMap.h"
@@ -149,20 +150,20 @@
StringRef PCHModuleName;
StringRef Path;
StringRef ASTFile;
- uint64_t Signature = 0;
+ ASTFileSignature Signature;
const Module *ClangModule = nullptr;
public:
ASTSourceDescriptor(){};
ASTSourceDescriptor(StringRef Name, StringRef Path, StringRef ASTFile,
- uint64_t Signature)
+ ASTFileSignature Signature)
: PCHModuleName(std::move(Name)), Path(std::move(Path)),
ASTFile(std::move(ASTFile)), Signature(Signature){};
ASTSourceDescriptor(const Module &M);
std::string getModuleName() const;
StringRef getPath() const { return Path; }
StringRef getASTFile() const { return ASTFile; }
- uint64_t getSignature() const { return Signature; }
+ ASTFileSignature getSignature() const { return Signature; }
const Module *getModuleOrNull() const { return ClangModule; }
};
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
index fa60d51..a4a88d0 100644
--- a/include/clang/Basic/Attr.td
+++ b/include/clang/Basic/Attr.td
@@ -87,6 +87,9 @@
def NonBitField : SubsetSubject<Field,
[{!S->isBitField()}]>;
+def ObjCClassMethod : SubsetSubject<ObjCMethod,
+ [{!S->isInstanceMethod()}]>;
+
def ObjCInstanceMethod : SubsetSubject<ObjCMethod,
[{S->isInstanceMethod()}]>;
@@ -192,6 +195,9 @@
list<string> Enums = enums;
}
+// Represents an attribute wrapped by another attribute.
+class AttrArgument<string name, bit opt = 0> : Argument<name, opt>;
+
// This handles one spelling of an attribute.
class Spelling<string name, string variety> {
string Name = name;
@@ -505,6 +511,7 @@
.Case("macos_app_extension", "macOS (App Extension)")
.Case("tvos_app_extension", "tvOS (App Extension)")
.Case("watchos_app_extension", "watchOS (App Extension)")
+ .Case("swift", "Swift")
.Default(llvm::StringRef());
} }];
let HasCustomParsing = 1;
@@ -1171,6 +1178,12 @@
let Documentation = [Undocumented];
}
+def NoEscape : InheritableAttr {
+ let Spellings = [GCC<"noescape">];
+ let Subjects = SubjectList<[ParmVar], WarnDiag, "ExpectedParameter">;
+ let Documentation = [Undocumented];
+}
+
def AssumeAligned : InheritableAttr {
let Spellings = [GCC<"assume_aligned">];
let Subjects = SubjectList<[ObjCMethod, Function]>;
@@ -1233,6 +1246,12 @@
let Documentation = [Undocumented];
}
+def NSErrorDomain : Attr {
+ let Spellings = [GNU<"ns_error_domain">];
+ let Args = [IdentifierArgument<"ErrorDomain">];
+ let Documentation = [NSErrorDomainDocs];
+}
+
def NSReturnsRetained : InheritableAttr {
let Spellings = [GNU<"ns_returns_retained">];
// let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>;
@@ -1319,6 +1338,12 @@
let Documentation = [ObjCSubclassingRestrictedDocs];
}
+def ObjCCompleteDefinition : InheritableAttr {
+ let Spellings = [GNU<"objc_complete_definition">];
+ let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
+ let Documentation = [Undocumented];
+}
+
def ObjCExplicitProtocolImpl : InheritableAttr {
let Spellings = [GNU<"objc_protocol_requires_explicit_implementation">];
let Subjects = SubjectList<[ObjCProtocol], ErrorDiag>;
@@ -1421,6 +1446,86 @@
let Documentation = [RegparmDocs];
}
+def SwiftBridge : Attr {
+ let Spellings = [GNU<"swift_bridge">];
+ let Subjects = SubjectList<[Tag, TypedefName, ObjCInterface, ObjCProtocol],
+ ErrorDiag, "ExpectedType">;
+ let Args = [StringArgument<"SwiftType">];
+ let Documentation = [SwiftBridgeDocs];
+}
+
+def SwiftError : InheritableAttr {
+ let Spellings = [GCC<"swift_error">];
+ let Args = [EnumArgument<"Convention", "ConventionKind",
+ ["none", "nonnull_error", "null_result", "zero_result", "nonzero_result"],
+ ["None", "NonNullError", "NullResult", "ZeroResult", "NonZeroResult"]>];
+ let Subjects = SubjectList<[ObjCMethod, Function], ErrorDiag>;
+ let Documentation = [SwiftErrorDocs];
+}
+
+def SwiftName : InheritableAttr {
+ let Spellings = [GCC<"swift_name">];
+ let Args = [StringArgument<"Name">];
+ // Proper subject list disabled because of the custom error needed.
+ // Let's avoid merge conflicts for now.
+// let Subjects = SubjectList<[EnumConstant, ObjCProtocol, ObjCClassMethod],
+// ErrorDiag, "ExpectedSwiftNameSubjects">;
+ let Documentation = [Undocumented];
+}
+
+def SwiftNewtype : Attr {
+ let Spellings = [GNU<"swift_newtype">, GNU<"swift_wrapper">];
+ let Subjects = SubjectList<[TypedefName], ErrorDiag, "ExpectedType">;
+ let Args = [EnumArgument<"NewtypeKind", "NewtypeKind",
+ ["struct", "enum"],
+ ["NK_Struct", "NK_Enum"]>];
+ let Documentation = [SwiftNewtypeDocs];
+}
+
+def SwiftPrivate : InheritableAttr {
+ let Spellings = [GCC<"swift_private">];
+ let Documentation = [Undocumented];
+}
+
+def SwiftSuppressFactoryAsInit : InheritableAttr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let SemaHandler = 0;
+ let Documentation = [Undocumented];
+}
+
+def SwiftImportPropertyAsAccessors : InheritableAttr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let SemaHandler = 0;
+ let Documentation = [Undocumented];
+}
+
+def SwiftVersioned : Attr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let Args = [VersionArgument<"Version">, AttrArgument<"AttrToAdd">];
+ let SemaHandler = 0;
+ let Documentation = [Undocumented];
+}
+
+def SwiftVersionedRemoval : Attr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">];
+ let SemaHandler = 0;
+ let Documentation = [Undocumented];
+ let AdditionalMembers = [{
+ attr::Kind getAttrKindToRemove() const {
+ return static_cast<attr::Kind>(getRawKind());
+ }
+ }];
+}
+
def ReqdWorkGroupSize : InheritableAttr {
let Spellings = [GNU<"reqd_work_group_size">];
let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">,
diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td
index 8f6a7ea..ccf4778 100644
--- a/include/clang/Basic/AttrDocs.td
+++ b/include/clang/Basic/AttrDocs.td
@@ -2322,6 +2322,58 @@
}];
}
+def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> {
+ let Content = [{
+Clang supports additional attributes for controlling how APIs are imported into Swift.
+ }];
+}
+
+def NSErrorDomainDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+The ``ns_error_domain`` attribute indicates a global constant representing the error domain.
+ }];
+}
+
+def SwiftBridgeDocs : Documentation {
+ let Category = SwiftDocs;
+ let Content = [{
+The ``swift_bridge`` attribute indicates that the type to which the attribute appertains is bridged to the named Swift type.
+ }];
+}
+
+def SwiftErrorDocs : Documentation {
+ let Category = SwiftDocs;
+ let Heading = "swift_error";
+ let Content = [{
+The ``swift_error`` attribute controls whether a particular function (or Objective-C method) is imported into Swift as a throwing function, and if so, the dynamic convention it uses.
+
+All of these conventions except ``none`` require the function to have an error parameter. Currently, the error parameter is always the last parameter of type ``NSError**`` or ``CFErrorRef*``. Swift will remove the error parameter from the imported API, and dynamically will always pass a valid address initialized to a null pointer.
+
+* ``swift_error(none)`` means that the function should not be imported as throwing. The error parameter and result type will be left alone.
+
+* ``swift_error(null_result)`` means that calls to the function should be considered to have thrown if they return a null value. The return type must be a pointer type, and it will be imported into Swift with a non-optional type. This is the default error convention for Objective-C methods that return pointers.
+
+* ``swift_error(zero_result)`` means that calls to the function should be considered to have thrown if they return a zero result. The return type must be an integral type. If the return type would have been imported as ``Bool``, it is instead imported as ``Void``. This is the default error convention for Objective-C methods that return a type that would be imported as ``Bool``.
+
+* ``swift_error(nonzero_result)`` means that calls to the function should be considered to have thrown if they return a non-zero result. The return type must be an integral type. If the return type would have been imported as ``Bool``, it is instead imported as ``Void``.
+
+* ``swift_error(nonnull_error)`` means that calls to the function should be considered to have thrown if they leave a non-null error in the error parameter. The return type is left unmodified.
+
+}];
+}
+
+def SwiftNewtypeDocs : Documentation {
+ let Category = SwiftDocs;
+ let Heading = "swift_newtype";
+ let Content = [{
+The ``swift_newtype`` attribute indicates that the typedef to which the attribute appertains is imported as a new Swift type of the typedef's name.
+* ``swift_newtype(struct)`` means that a Swift struct will be created for this typedef.
+* ``swift_newtype(enum)`` means that a Swift enum will be created for this typedef.
+ }];
+}
+
+
def OMPDeclareTargetDocs : Documentation {
let Category = DocCatFunction;
let Heading = "#pragma omp declare target";
diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h
index b83ef4d..ffce99b 100644
--- a/include/clang/Basic/Diagnostic.h
+++ b/include/clang/Basic/Diagnostic.h
@@ -29,6 +29,7 @@
#include <cassert>
#include <cstdint>
#include <list>
+#include <map>
#include <memory>
#include <string>
#include <type_traits>
@@ -232,59 +233,97 @@
/// \brief Keeps and automatically disposes all DiagStates that we create.
std::list<DiagState> DiagStates;
- /// \brief Represents a point in source where the diagnostic state was
- /// modified because of a pragma.
- ///
- /// 'Loc' can be null if the point represents the diagnostic state
- /// modifications done through the command-line.
- struct DiagStatePoint {
- DiagState *State;
- FullSourceLoc Loc;
- DiagStatePoint(DiagState *State, FullSourceLoc Loc)
- : State(State), Loc(Loc) { }
-
- bool operator<(const DiagStatePoint &RHS) const {
- // If Loc is invalid it means it came from <command-line>, in which case
- // we regard it as coming before any valid source location.
- if (RHS.Loc.isInvalid())
- return false;
- if (Loc.isInvalid())
- return true;
- return Loc.isBeforeInTranslationUnitThan(RHS.Loc);
+ /// A mapping from files to the diagnostic states for those files. Lazily
+ /// built on demand for files in which the diagnostic state has not changed.
+ class DiagStateMap {
+ public:
+ /// Add an initial diagnostic state.
+ void appendFirst(DiagState *State);
+ /// Add a new latest state point.
+ void append(SourceManager &SrcMgr, SourceLocation Loc, DiagState *State);
+ /// Look up the diagnostic state at a given source location.
+ DiagState *lookup(SourceManager &SrcMgr, SourceLocation Loc) const;
+ /// Determine whether this map is empty.
+ bool empty() const { return Files.empty(); }
+ /// Clear out this map.
+ void clear() {
+ Files.clear();
+ FirstDiagState = CurDiagState = nullptr;
+ CurDiagStateLoc = SourceLocation();
}
+
+ /// Grab the most-recently-added state point.
+ DiagState *getCurDiagState() const { return CurDiagState; }
+ /// Get the location at which a diagnostic state was last added.
+ SourceLocation getCurDiagStateLoc() const { return CurDiagStateLoc; }
+
+ private:
+ /// \brief Represents a point in source where the diagnostic state was
+ /// modified because of a pragma.
+ ///
+ /// 'Loc' can be null if the point represents the diagnostic state
+ /// modifications done through the command-line.
+ struct DiagStatePoint {
+ DiagState *State;
+ unsigned Offset;
+ DiagStatePoint(DiagState *State, unsigned Offset)
+ : State(State), Offset(Offset) { }
+ };
+
+ /// Description of the diagnostic states and state transitions for a
+ /// particular FileID.
+ struct File {
+ /// The diagnostic state for the parent file. This is strictly redundant,
+ /// as looking up the DecomposedIncludedLoc for the FileID in the Files
+ /// map would give us this, but we cache it here for performance.
+ File *Parent = nullptr;
+ /// The offset of this file within its parent.
+ unsigned ParentOffset = 0;
+ /// Whether this file has any local (not imported from an AST file)
+ /// diagnostic state transitions.
+ bool HasLocalTransitions = false;
+ /// The points within the file where the state changes. There will always
+ /// be at least one of these (the state on entry to the file).
+ llvm::SmallVector<DiagStatePoint, 4> StateTransitions;
+
+ DiagState *lookup(unsigned Offset) const;
+ };
+
+ /// The diagnostic states for each file.
+ mutable std::map<FileID, File> Files;
+
+ /// The initial diagnostic state.
+ DiagState *FirstDiagState;
+ /// The current diagnostic state.
+ DiagState *CurDiagState;
+ /// The location at which the current diagnostic state was established.
+ SourceLocation CurDiagStateLoc;
+
+ /// Get the diagnostic state information for a file.
+ File *getFile(SourceManager &SrcMgr, FileID ID) const;
+
+ friend class ASTReader;
+ friend class ASTWriter;
};
- /// \brief A sorted vector of all DiagStatePoints representing changes in
- /// diagnostic state due to diagnostic pragmas.
- ///
- /// The vector is always sorted according to the SourceLocation of the
- /// DiagStatePoint.
- typedef std::vector<DiagStatePoint> DiagStatePointsTy;
- mutable DiagStatePointsTy DiagStatePoints;
+ DiagStateMap DiagStatesByLoc;
/// \brief Keeps the DiagState that was active during each diagnostic 'push'
/// so we can get back at it when we 'pop'.
std::vector<DiagState *> DiagStateOnPushStack;
DiagState *GetCurDiagState() const {
- assert(!DiagStatePoints.empty());
- return DiagStatePoints.back().State;
+ return DiagStatesByLoc.getCurDiagState();
}
- void PushDiagStatePoint(DiagState *State, SourceLocation L) {
- FullSourceLoc Loc(L, getSourceManager());
- // Make sure that DiagStatePoints is always sorted according to Loc.
- assert(Loc.isValid() && "Adding invalid loc point");
- assert(!DiagStatePoints.empty() &&
- (DiagStatePoints.back().Loc.isInvalid() ||
- DiagStatePoints.back().Loc.isBeforeInTranslationUnitThan(Loc)) &&
- "Previous point loc comes after or is the same as new one");
- DiagStatePoints.push_back(DiagStatePoint(State, Loc));
- }
+ void PushDiagStatePoint(DiagState *State, SourceLocation L);
/// \brief Finds the DiagStatePoint that contains the diagnostic state of
/// the given source location.
- DiagStatePointsTy::iterator GetDiagStatePointForLoc(SourceLocation Loc) const;
+ DiagState *GetDiagStateForLoc(SourceLocation Loc) const {
+ return SourceMgr ? DiagStatesByLoc.lookup(*SourceMgr, Loc)
+ : DiagStatesByLoc.getCurDiagState();
+ }
/// \brief Sticky flag set to \c true when an error is emitted.
bool ErrorOccurred;
@@ -390,7 +429,11 @@
assert(SourceMgr && "SourceManager not set!");
return *SourceMgr;
}
- void setSourceManager(SourceManager *SrcMgr) { SourceMgr = SrcMgr; }
+ void setSourceManager(SourceManager *SrcMgr) {
+ assert(DiagStatesByLoc.empty() &&
+ "Leftover diag state from a different SourceManager.");
+ SourceMgr = SrcMgr;
+ }
//===--------------------------------------------------------------------===//
// DiagnosticsEngine characterization methods, used by a client to customize
diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td
index af0612a..aeaa824 100644
--- a/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/include/clang/Basic/DiagnosticCommonKinds.td
@@ -88,10 +88,12 @@
"module '%0' %select{is incompatible with|requires}1 feature '%2'">;
def err_module_header_missing : Error<
"%select{|umbrella }0header '%1' not found">;
-def err_module_lock_failure : Error<
- "could not acquire lock file for module '%0': %1">, DefaultFatal;
-def err_module_lock_timeout : Error<
- "timed out waiting to acquire lock file for module '%0'">, DefaultFatal;
+def err_module_shadowed : Error<
+ "import of shadowed module '%0'">;
+def remark_module_lock_failure : Remark<
+ "could not acquire lock file for module '%0': %1">, InGroup<ModuleBuild>;
+def remark_module_lock_timeout : Remark<
+ "timed out waiting to acquire lock file for module '%0'">, InGroup<ModuleBuild>;
def err_module_cycle : Error<"cyclic dependency in module '%0': %1">,
DefaultFatal;
def err_module_prebuilt : Error<
@@ -222,6 +224,11 @@
def warn_arcmt_nsalloc_realloc : Warning<"[rewriter] call returns pointer to GC managed memory; it will become unmanaged in ARC">;
def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to be used with an object with ownership other than __unsafe_unretained">;
+// API notes
+def err_apinotes_message : Error<"%0">;
+def warn_apinotes_message : Warning<"%0">, InGroup<DiagGroup<"apinotes">>;
+def note_apinotes_message : Note<"%0">;
+
// OpenMP
def err_omp_more_one_clause : Error<
"directive '#pragma omp %0' cannot contain more than one '%1' clause%select{| with '%3' name modifier| with 'source' dependence}2">;
diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td
index 1267f8d..257448e 100644
--- a/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -222,6 +222,10 @@
def err_invalid_vfs_overlay : Error<
"invalid virtual filesystem overlay file '%0'">, DefaultFatal;
+def err_no_apinotes_cache_path : Error<
+ "-fapinotes was provided without -fapinotes-cache-path=<directory>">,
+ DefaultFatal;
+
def warn_option_invalid_ocl_version : Warning<
"OpenCL version %0 does not support the option '%1'">, InGroup<Deprecated>;
}
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index 4173d03..6129b54 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -219,7 +219,6 @@
def DanglingElse: DiagGroup<"dangling-else">;
def DanglingField : DiagGroup<"dangling-field">;
def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
-def ExpansionToDefined : DiagGroup<"expansion-to-defined">;
def FlagEnum : DiagGroup<"flag-enum">;
def IncrementBool : DiagGroup<"increment-bool", [DeprecatedIncrementBool]>;
def InfiniteRecursion : DiagGroup<"infinite-recursion">;
@@ -386,6 +385,9 @@
def StringPlusInt : DiagGroup<"string-plus-int">;
def StringPlusChar : DiagGroup<"string-plus-char">;
def StrncatSize : DiagGroup<"strncat-size">;
+
+def SwiftNameAttribute : DiagGroup<"swift-name-attribute">;
+
def TautologicalOutOfRangeCompare : DiagGroup<"tautological-constant-out-of-range-compare">;
def TautologicalPointerCompare : DiagGroup<"tautological-pointer-compare">;
def TautologicalOverlapCompare : DiagGroup<"tautological-overlap-compare">;
diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h
index fcd04a0..d13ae4c 100644
--- a/include/clang/Basic/DiagnosticIDs.h
+++ b/include/clang/Basic/DiagnosticIDs.h
@@ -36,7 +36,7 @@
DIAG_START_AST = DIAG_START_PARSE + 500,
DIAG_START_COMMENT = DIAG_START_AST + 110,
DIAG_START_SEMA = DIAG_START_COMMENT + 100,
- DIAG_START_ANALYSIS = DIAG_START_SEMA + 3500,
+ DIAG_START_ANALYSIS = DIAG_START_SEMA + 4000,
DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + 100
};
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index 7f7022b..aaabe7f 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -679,13 +679,6 @@
def note_header_guard : Note<
"%0 is defined here; did you mean %1?">;
-def warn_defined_in_object_type_macro : Warning<
- "macro expansion producing 'defined' has undefined behavior">,
- InGroup<ExpansionToDefined>;
-def warn_defined_in_function_type_macro : Extension<
- "macro expansion producing 'defined' has undefined behavior">,
- InGroup<ExpansionToDefined>;
-
let CategoryName = "Nullability Issue" in {
def err_pp_assume_nonnull_syntax : Error<"expected 'begin' or 'end'">;
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index 0943fea..76e776c 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -1039,6 +1039,9 @@
def err_pragma_invalid_keyword : Error<
"invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">;
+// API notes.
+def err_type_unparsed : Error<"unparsed tokens following type">;
+
// Pragma unroll support.
def warn_pragma_unroll_cuda_value_in_parens : Warning<
"argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">,
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index c934351..c28465a 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2735,6 +2735,9 @@
InGroup<Availability>;
def note_overridden_method : Note<
"overridden method is here">;
+def warn_availability_swift_unavailable_deprecated_only : Warning<
+ "only 'unavailable' and 'deprecated' are supported for Swift availability">,
+ InGroup<Availability>;
def note_protocol_method : Note<
"protocol method is here">;
@@ -3103,6 +3106,9 @@
def warn_attribute_nonnull_parm_no_args : Warning<
"'nonnull' attribute when used on parameters takes no arguments">,
InGroup<IgnoredAttributes>;
+def warn_attribute_noescape_non_pointer : Warning<
+ "'noescape' attribute ignored on parameter of non-pointer type %0">,
+ InGroup<IgnoredAttributes>;
def note_declared_nonnull : Note<
"declared %select{'returns_nonnull'|'nonnull'}0 here">;
def warn_attribute_sentinel_named_arguments : Warning<
@@ -3225,6 +3231,68 @@
def err_objc_attr_protocol_requires_definition : Error<
"attribute %0 can only be applied to @protocol definitions, not forward declarations">;
+// Swift attributes
+def warn_attr_swift_name_decl_kind : Warning<
+ "%0 attribute cannot be applied to this declaration">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_function : Warning<
+ "parameter of %0 attribute must be a Swift function name string">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_function_no_prototype : Warning<
+ "%0 attribute can only be applied to function declarations with prototypes">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_context_name_invalid_identifier : Warning<
+ "%0 attribute has invalid identifier for context name">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_basename_invalid_identifier : Warning<
+ "%0 attribute has invalid identifier for base name">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_parameter_invalid_identifier : Warning<
+ "%0 attribute has invalid identifier for parameter name">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_missing_parameters : Warning<
+ "%0 attribute is missing parameter label clause">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_not_accessor : Warning<
+ "%0 attribute for 'subscript' must be a getter or setter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_no_parameter : Warning<
+ "%0 attribute for 'subscript' must take at least one parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_getter_newValue : Warning<
+ "%0 attribute for 'subscript' getter cannot take a 'newValue:' parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_setter_no_newValue : Warning<
+ "%0 attribute for 'subscript' setter must take a 'newValue:' parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_setter_multiple_newValues : Warning<
+ "%0 attribute for 'subscript' setter cannot take multiple 'newValue:' parameters">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_getter_parameters : Warning<
+ "%0 attribute for getter must not take any parameters besides 'self:'">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_setter_parameters : Warning<
+ "%0 attribute for setter must take one parameter for new value">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_multiple_selfs : Warning<
+ "%0 attribute cannot specify more than one 'self:' parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_static_subscript : Warning<
+ "%0 attribute for 'subscript' must take a 'self:' parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_num_params : Warning<
+ "too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">,
+ InGroup<SwiftNameAttribute>;
+def err_attr_swift_error_no_error_parameter : Error<
+ "%0 attribute can only be applied to a %select{function|method}1 "
+ "with an error parameter">;
+def err_attr_swift_error_return_type : Error<
+ "%0 attribute with '%1' convention can only be applied to a "
+ "%select{function|method}2 returning %select{an integral type|a pointer}3">;
+def warn_swift_newtype_attribute_non_typedef : Warning<
+ "'swift_newtype' attribute may be put on a typedef only; "
+ "attribute is ignored">, InGroup<DiagGroup<"swift-newtype-attribute">>;
+
// Function Parameter Semantic Analysis.
def err_param_with_void_type : Error<"argument may not have 'void' type">;
def err_void_only_param : Error<
@@ -5164,7 +5232,7 @@
def warn_block_capture_autoreleasing : Warning<
"block captures an autoreleasing out-parameter, which may result in "
"use-after-free bugs">,
- InGroup<BlockCaptureAutoReleasing>, DefaultIgnore;
+ InGroup<BlockCaptureAutoReleasing>;
def note_declare_parameter_autoreleasing : Note<
"declare the parameter __autoreleasing explicitly to suppress this warning">;
def note_declare_parameter_strong : Note<
@@ -7979,6 +8047,14 @@
def err_nsreturns_retained_attribute_mismatch : Error<
"overriding method has mismatched ns_returns_%select{not_retained|retained}0"
" attributes">;
+
+def err_nserrordomain_not_tagdecl : Error<
+ "ns_error_domain attribute only valid on "
+ "%select{enums, structs, and unions|enums, structs, unions, and classes}0">;
+def err_nserrordomain_invalid_decl : Error<
+ "domain argument %0 does not refer to global constant">;
+def err_nserrordomain_requires_identifier : Error<
+ "domain argument must be an identifier">;
def note_getter_unavailable : Note<
"or because setter is declared here, but no getter method %0 is found">;
@@ -8237,6 +8313,13 @@
InGroup<OpenCLUnsupportedRGBA>;
} // end of sema category
+let CategoryName = "API Notes Issue" in {
+
+def err_incompatible_replacement_type : Error<
+ "API notes replacement type %0 has a different size from original type %1">;
+
+}
+
let CategoryName = "OpenMP Issue" in {
// OpenMP support.
def err_omp_expected_var_arg : Error<
diff --git a/include/clang/Basic/DiagnosticSerializationKinds.td b/include/clang/Basic/DiagnosticSerializationKinds.td
index 066a1f5..46adf2c 100644
--- a/include/clang/Basic/DiagnosticSerializationKinds.td
+++ b/include/clang/Basic/DiagnosticSerializationKinds.td
@@ -125,6 +125,11 @@
"duplicate module file extension block name '%0'">,
InGroup<ModuleFileExtension>;
+def warn_module_system_bit_conflict : Warning<
+ "module file '%0' was validated as a system module and is now being imported "
+ "as a non-system module; any difference in diagnostic options will be ignored">,
+ InGroup<ModuleConflict>;
+
} // let CategoryName
} // let Component
diff --git a/include/clang/Basic/FileSystemOptions.h b/include/clang/Basic/FileSystemOptions.h
index 38f1346..1beb2e2 100644
--- a/include/clang/Basic/FileSystemOptions.h
+++ b/include/clang/Basic/FileSystemOptions.h
@@ -25,6 +25,9 @@
/// \brief If set, paths are resolved as if the working directory was
/// set to the value of WorkingDir.
std::string WorkingDir;
+
+ /// The path to the API notes cache.
+ std::string APINotesCachePath;
};
} // end namespace clang
diff --git a/include/clang/Basic/IdentifierTable.h b/include/clang/Basic/IdentifierTable.h
index 3001d0b..52238f8 100644
--- a/include/clang/Basic/IdentifierTable.h
+++ b/include/clang/Basic/IdentifierTable.h
@@ -73,9 +73,6 @@
// partially) from an AST file.
bool ChangedAfterLoad : 1; // True if identifier has changed from the
// definition loaded from an AST file.
- bool FEChangedAfterLoad : 1; // True if identifier's frontend information
- // has changed from the definition loaded
- // from an AST file.
bool RevertedTokenID : 1; // True if revertTokenIDToIdentifier was
// called.
bool OutOfDate : 1; // True if there may be additional
@@ -83,7 +80,7 @@
// stored externally.
bool IsModulesImport : 1; // True if this is the 'import' contextual
// keyword.
- // 29 bit left in 64-bit word.
+ // 30 bit left in 64-bit word.
void *FETokenInfo; // Managed by the language front-end.
llvm::StringMapEntry<IdentifierInfo*> *Entry;
@@ -313,18 +310,6 @@
ChangedAfterLoad = true;
}
- /// \brief Determine whether the frontend token information for this
- /// identifier has changed since it was loaded from an AST file.
- bool hasFETokenInfoChangedSinceDeserialization() const {
- return FEChangedAfterLoad;
- }
-
- /// \brief Note that the frontend token information for this identifier has
- /// changed since it was loaded from an AST file.
- void setFETokenInfoChangedSinceDeserialization() {
- FEChangedAfterLoad = true;
- }
-
/// \brief Determine whether the information for this identifier is out of
/// date with respect to the external source.
bool isOutOfDate() const { return OutOfDate; }
diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def
index d944a9d..19a081c 100644
--- a/include/clang/Basic/LangOptions.def
+++ b/include/clang/Basic/LangOptions.def
@@ -256,6 +256,8 @@
LANGOPT(ApplePragmaPack, 1, 0, "Apple gcc-compatible #pragma pack handling")
LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST")
+LANGOPT(APINotes, 1, 0, "use external API notes")
+LANGOPT(APINotesModules, 1, 0, "use external API notes")
LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan "
"field padding (0: none, 1:least "
diff --git a/include/clang/Basic/MemoryBufferCache.h b/include/clang/Basic/MemoryBufferCache.h
new file mode 100644
index 0000000..c79c3c4
--- /dev/null
+++ b/include/clang/Basic/MemoryBufferCache.h
@@ -0,0 +1,80 @@
+//===- MemoryBufferCache.h - Cache for loaded memory buffers ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_MEMORYBUFFERCACHE_H
+#define LLVM_CLANG_BASIC_MEMORYBUFFERCACHE_H
+
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringMap.h"
+#include <memory>
+
+namespace llvm {
+class MemoryBuffer;
+} // end namespace llvm
+
+namespace clang {
+
+/// Manage memory buffers across multiple users.
+///
+/// Ensures that multiple users have a consistent view of each buffer. This is
+/// used by \a CompilerInstance when building PCMs to ensure that each \a
+/// ModuleManager sees the same files.
+///
+/// \a finalizeCurrentBuffers() should be called before creating a new user.
+/// This locks in the current buffers, ensuring that no buffer that has already
+/// been accessed can be purged, preventing use-after-frees.
+class MemoryBufferCache : public llvm::RefCountedBase<MemoryBufferCache> {
+ struct BufferEntry {
+ std::unique_ptr<llvm::MemoryBuffer> Buffer;
+
+ /// Track the timeline of when this was added to the cache.
+ unsigned Index;
+ };
+
+ /// Cache of buffers.
+ llvm::StringMap<BufferEntry> Buffers;
+
+ /// Monotonically increasing index.
+ unsigned NextIndex = 0;
+
+ /// Bumped to prevent "older" buffers from being removed.
+ unsigned FirstRemovableIndex = 0;
+
+public:
+ /// Store the Buffer under the Filename.
+ ///
+ /// \pre There is not already buffer is not already in the cache.
+ /// \return a reference to the buffer as a convenience.
+ llvm::MemoryBuffer &addBuffer(llvm::StringRef Filename,
+ std::unique_ptr<llvm::MemoryBuffer> Buffer);
+
+ /// Try to remove a buffer from the cache.
+ ///
+ /// \return false on success, iff \c !isBufferFinal().
+ bool tryToRemoveBuffer(llvm::StringRef Filename);
+
+ /// Get a pointer to the buffer if it exists; else nullptr.
+ llvm::MemoryBuffer *lookupBuffer(llvm::StringRef Filename);
+
+ /// Check whether the buffer is final.
+ ///
+ /// \return true iff \a finalizeCurrentBuffers() has been called since the
+ /// buffer was added. This prevents buffers from being removed.
+ bool isBufferFinal(llvm::StringRef Filename);
+
+ /// Finalize the current buffers in the cache.
+ ///
+ /// Should be called when creating a new user to ensure previous uses aren't
+ /// invalidated.
+ void finalizeCurrentBuffers();
+};
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_BASIC_MEMORYBUFFERCACHE_H
diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h
index 31c5c7e..38004fc 100644
--- a/include/clang/Basic/Module.h
+++ b/include/clang/Basic/Module.h
@@ -42,7 +42,17 @@
/// \brief Describes the name of a module.
typedef SmallVector<std::pair<std::string, SourceLocation>, 2> ModuleId;
-
+
+/// The signature of a module, which is a hash of the AST content.
+struct ASTFileSignature : std::array<uint32_t, 5> {
+ ASTFileSignature(std::array<uint32_t, 5> S = {{0}})
+ : std::array<uint32_t, 5>(std::move(S)) {}
+
+ explicit operator bool() const {
+ return *this != std::array<uint32_t, 5>({{0}});
+ }
+};
+
/// \brief Describes a module or submodule.
class Module {
public:
@@ -65,7 +75,7 @@
llvm::PointerUnion<const DirectoryEntry *, const FileEntry *> Umbrella;
/// \brief The module signature.
- uint64_t Signature;
+ ASTFileSignature Signature;
/// \brief The name of the umbrella entry, as written in the module map.
std::string UmbrellaAsWritten;
@@ -147,6 +157,9 @@
/// will be false to indicate that this (sub)module is not available.
SmallVector<Requirement, 2> Requirements;
+ /// \brief A module with the same name that shadows this module.
+ Module *ShadowingModule = nullptr;
+
/// \brief Whether this module is missing a feature from \c Requirements.
unsigned IsMissingRequirement : 1;
@@ -180,6 +193,9 @@
/// \brief Whether this is an inferred submodule (module * { ... }).
unsigned IsInferred : 1;
+ /// \brief Whether this is a module who has its swift_names inferred.
+ unsigned IsSwiftInferImportAsMember : 1;
+
/// \brief Whether we should infer submodules for this module based on
/// the headers.
///
@@ -325,13 +341,20 @@
///
/// \param Target The target options used for the current translation unit.
///
- /// \param Req If this module is unavailable, this parameter
- /// will be set to one of the requirements that is not met for use of
- /// this module.
+ /// \param Req If this module is unavailable because of a missing requirement,
+ /// this parameter will be set to one of the requirements that is not met for
+ /// use of this module.
+ ///
+ /// \param MissingHeader If this module is unavailable because of a missing
+ /// header, this parameter will be set to one of the missing headers.
+ ///
+ /// \param ShadowingModule If this module is unavailable because it is
+ /// shadowed, this parameter will be set to the shadowing module.
bool isAvailable(const LangOptions &LangOpts,
const TargetInfo &Target,
Requirement &Req,
- UnresolvedHeaderDirective &MissingHeader) const;
+ UnresolvedHeaderDirective &MissingHeader,
+ Module *&ShadowingModule) const;
/// \brief Determine whether this module is a submodule.
bool isSubModule() const { return Parent != nullptr; }
diff --git a/include/clang/Basic/SourceMgrAdapter.h b/include/clang/Basic/SourceMgrAdapter.h
new file mode 100644
index 0000000..dd7b83f
--- /dev/null
+++ b/include/clang/Basic/SourceMgrAdapter.h
@@ -0,0 +1,85 @@
+//=== SourceMgrAdapter.h - SourceMgr to SourceManager Adapter ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides an adapter that maps diagnostics from llvm::SourceMgr
+// to Clang's SourceManager.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SOURCEMGRADAPTER_H
+#define LLVM_CLANG_SOURCEMGRADAPTER_H
+
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/SourceMgr.h"
+#include <string>
+#include <utility>
+
+namespace clang {
+
+class DiagnosticsEngine;
+class FileEntry;
+
+/// An adapter that can be used to translate diagnostics from one or more
+/// llvm::SourceMgr instances to a ,
+class SourceMgrAdapter {
+ /// Clang source manager.
+ SourceManager &SrcMgr;
+
+ /// Clang diagnostics engine.
+ DiagnosticsEngine &Diag;
+
+ /// Diagnostic IDs for errors, warnings, and notes.
+ unsigned ErrorDiagID, WarningDiagID, NoteDiagID;
+
+ /// The default file to use when mapping buffers.
+ const FileEntry *DefaultFile;
+
+ /// A mapping from (LLVM source manager, buffer ID) pairs to the
+ /// corresponding file ID within the Clang source manager.
+ llvm::DenseMap<std::pair<const llvm::SourceMgr *, unsigned>, FileID>
+ FileIDMapping;
+
+ /// Diagnostic handler.
+ static void handleDiag(const llvm::SMDiagnostic &diag, void *context);
+
+public:
+ /// Create a new \c SourceMgr adaptor that maps to the given source
+ /// manager and diagnostics engine.
+ SourceMgrAdapter(SourceManager &srcMgr, DiagnosticsEngine &diag,
+ unsigned errorDiagID, unsigned warningDiagID,
+ unsigned noteDiagID, const FileEntry *defaultFile = nullptr);
+
+ ~SourceMgrAdapter();
+
+ /// Map a source location in the given LLVM source manager to its
+ /// corresponding location in the Clang source manager.
+ SourceLocation mapLocation(const llvm::SourceMgr &llvmSrcMgr,llvm::SMLoc loc);
+
+ /// Map a source range in the given LLVM source manager to its corresponding
+ /// range in the Clang source manager.
+ SourceRange mapRange(const llvm::SourceMgr &llvmSrcMgr, llvm::SMRange range);
+
+ /// Handle the given diagnostic from an LLVM source manager.
+ void handleDiag(const llvm::SMDiagnostic &diag);
+
+ /// Retrieve the diagnostic handler to use with the underlying SourceMgr.
+ llvm::SourceMgr::DiagHandlerTy getDiagHandler() {
+ return &SourceMgrAdapter::handleDiag;
+ }
+
+ /// Retrieve the context to use with the diagnostic handler produced by
+ /// \c getDiagHandler().
+ void *getDiagContext() { return this; }
+};
+
+
+} // end namespace clang
+
+#endif
diff --git a/include/clang/Basic/VersionTuple.h b/include/clang/Basic/VersionTuple.h
index da3b019..07315f0 100644
--- a/include/clang/Basic/VersionTuple.h
+++ b/include/clang/Basic/VersionTuple.h
@@ -17,6 +17,7 @@
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/DenseMapInfo.h"
#include <string>
#include <tuple>
@@ -70,6 +71,9 @@
return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0;
}
+ /// Whether this is a non-empty version tuple.
+ explicit operator bool () const { return !empty(); }
+
/// \brief Retrieve the major version number.
unsigned getMajor() const { return Major; }
@@ -165,4 +169,35 @@
raw_ostream& operator<<(raw_ostream &Out, const VersionTuple &V);
} // end namespace clang
+
+namespace llvm {
+ // Provide DenseMapInfo for version tuples.
+ template<>
+ struct DenseMapInfo<clang::VersionTuple> {
+ static inline clang::VersionTuple getEmptyKey() {
+ return clang::VersionTuple(0x7FFFFFFF);
+ }
+ static inline clang::VersionTuple getTombstoneKey() {
+ return clang::VersionTuple(0x7FFFFFFE);
+ }
+ static unsigned getHashValue(const clang::VersionTuple& value) {
+ unsigned result = value.getMajor();
+ if (auto minor = value.getMinor())
+ result = combineHashValue(result, *minor);
+ if (auto subminor = value.getSubminor())
+ result = combineHashValue(result, *subminor);
+ if (auto build = value.getBuild())
+ result = combineHashValue(result, *build);
+
+ return result;
+ }
+
+ static bool isEqual(const clang::VersionTuple &lhs,
+ const clang::VersionTuple &rhs) {
+ return lhs == rhs;
+ }
+ };
+
+} // end namespace llvm
+
#endif // LLVM_CLANG_BASIC_VERSIONTUPLE_H
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index ab296eb..cd446ec 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -658,6 +658,8 @@
HelpText<"Disable standard system #include directories">;
def fdisable_module_hash : Flag<["-"], "fdisable-module-hash">,
HelpText<"Disable the module hash">;
+def fmodules_hash_content : Flag<["-"], "fmodules-hash-content">,
+ HelpText<"Enable hashing the content of a module file">;
def c_isystem : JoinedOrSeparate<["-"], "c-isystem">, MetaVarName<"<directory>">,
HelpText<"Add directory to the C SYSTEM include search path">;
def objc_isystem : JoinedOrSeparate<["-"], "objc-isystem">,
diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h
index 0ce461c..d38e63b 100644
--- a/include/clang/Driver/Driver.h
+++ b/include/clang/Driver/Driver.h
@@ -304,13 +304,8 @@
bool isSaveTempsObj() const { return SaveTemps == SaveTempsObj; }
bool embedBitcodeEnabled() const { return BitcodeEmbed != EmbedNone; }
- bool embedBitcodeInObject() const {
- // LTO has no object file output so ignore embed bitcode option in LTO.
- return (BitcodeEmbed == EmbedBitcode) && !isUsingLTO();
- }
- bool embedBitcodeMarkerOnly() const {
- return (BitcodeEmbed == EmbedMarker) && !isUsingLTO();
- }
+ bool embedBitcodeInObject() const { return (BitcodeEmbed == EmbedBitcode); }
+ bool embedBitcodeMarkerOnly() const { return (BitcodeEmbed == EmbedMarker); }
/// Compute the desired OpenMP runtime from the flags provided.
OpenMPRuntimeKind getOpenMPRuntime(const llvm::opt::ArgList &Args) const;
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index 6be159f..77e6cf3 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -556,6 +556,21 @@
def fno_profile_use : Flag<["-"], "fno-profile-use">,
Alias<fno_profile_instr_use>;
+def fapinotes : Flag<["-"], "fapinotes">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Enable external API notes support">;
+def fapinotes_modules : Flag<["-"], "fapinotes-modules">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Enable module-based external API notes support">;
+def fno_apinotes : Flag<["-"], "fno-apinotes">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Disable external API notes support">;
+def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Disable module-based external API notes support">;
+def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">,
+ Group<i_Group>, Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">,
+ HelpText<"Specify the API notes cache path">;
+def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">,
+ Group<f_clang_Group>, Flags<[CC1Option]>, MetaVarName<"<version>">,
+ HelpText<"Specify the Swift version to use when filtering API notes">;
+
def fblocks : Flag<["-"], "fblocks">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Enable the 'blocks' language feature">;
def fbootclasspath_EQ : Joined<["-"], "fbootclasspath=">, Group<f_Group>;
@@ -1407,6 +1422,8 @@
HelpText<"Display available options">;
def index_header_map : Flag<["-"], "index-header-map">, Flags<[CC1Option]>,
HelpText<"Make the next included directory (-I or -F) an indexer header map">;
+def iapinotes_modules : JoinedOrSeparate<["-"], "iapinotes-modules">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Add directory to the API notes search path referenced by module name">, MetaVarName<"<directory>">;
def idirafter : JoinedOrSeparate<["-"], "idirafter">, Group<clang_i_Group>, Flags<[CC1Option]>,
HelpText<"Add directory to AFTER include search path">;
def iframework : JoinedOrSeparate<["-"], "iframework">, Group<clang_i_Group>, Flags<[CC1Option]>,
diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h
index 6c6458b..d34ca2f 100644
--- a/include/clang/Format/Format.h
+++ b/include/clang/Format/Format.h
@@ -465,8 +465,6 @@
LK_Java,
/// Should be used for JavaScript.
LK_JavaScript,
- /// Should be used for ObjC code.
- LK_ObjC,
/// Should be used for Protocol Buffers
/// (https://developers.google.com/protocol-buffers/).
LK_Proto,
@@ -854,16 +852,13 @@
/// == "file".
/// \param[in] FallbackStyle The name of a predefined style used to fallback to
/// in case the style can't be determined from \p StyleName.
-/// \param[in] Code The actual code to be formatted. Used to determine the
-/// language if the filename isn't sufficient.
/// \param[in] FS The underlying file system, in which the file resides. By
/// default, the file system is the real file system.
///
/// \returns FormatStyle as specified by ``StyleName``. If no style could be
/// determined, the default is LLVM Style (see ``getLLVMStyle()``).
FormatStyle getStyle(StringRef StyleName, StringRef FileName,
- StringRef FallbackStyle, StringRef Code = "",
- vfs::FileSystem *FS = nullptr);
+ StringRef FallbackStyle, vfs::FileSystem *FS = nullptr);
// \brief Returns a string representation of ``Language``.
inline StringRef getLanguageName(FormatStyle::LanguageKind Language) {
diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h
index b1cdb46..2a8df1b 100644
--- a/include/clang/Frontend/ASTUnit.h
+++ b/include/clang/Frontend/ASTUnit.h
@@ -51,6 +51,7 @@
class FileEntry;
class FileManager;
class HeaderSearch;
+class MemoryBufferCache;
class Preprocessor;
class PCHContainerOperations;
class PCHContainerReader;
@@ -84,6 +85,7 @@
IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics;
IntrusiveRefCntPtr<FileManager> FileMgr;
IntrusiveRefCntPtr<SourceManager> SourceMgr;
+ IntrusiveRefCntPtr<MemoryBufferCache> PCMCache;
std::unique_ptr<HeaderSearch> HeaderInfo;
IntrusiveRefCntPtr<TargetInfo> Target;
std::shared_ptr<Preprocessor> PP;
@@ -519,6 +521,8 @@
const FileSystemOptions &getFileSystemOpts() const { return FileSystemOpts; }
+ IntrusiveRefCntPtr<ASTReader> getASTReader() const;
+
StringRef getOriginalSourceFileName() {
return OriginalSourceFile;
}
diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h
index 3ebbc61..62a5fd2 100644
--- a/include/clang/Frontend/CompilerInstance.h
+++ b/include/clang/Frontend/CompilerInstance.h
@@ -44,6 +44,7 @@
class FileEntry;
class FileManager;
class FrontendAction;
+class MemoryBufferCache;
class Module;
class Preprocessor;
class Sema;
@@ -90,6 +91,9 @@
/// The source manager.
IntrusiveRefCntPtr<SourceManager> SourceMgr;
+ /// The cache of PCM files.
+ IntrusiveRefCntPtr<MemoryBufferCache> PCMCache;
+
/// The preprocessor.
std::shared_ptr<Preprocessor> PP;
@@ -142,13 +146,13 @@
/// \brief Whether we should (re)build the global module index once we
/// have finished with this translation unit.
- bool BuildGlobalModuleIndex;
+ bool BuildGlobalModuleIndex = false;
/// \brief We have a full global module index, with all modules.
- bool HaveFullGlobalModuleIndex;
+ bool HaveFullGlobalModuleIndex = false;
/// \brief One or more modules failed to build.
- bool ModuleBuildFailed;
+ bool ModuleBuildFailed = false;
/// \brief Holds information about the output file.
///
@@ -178,7 +182,7 @@
explicit CompilerInstance(
std::shared_ptr<PCHContainerOperations> PCHContainerOps =
std::make_shared<PCHContainerOperations>(),
- bool BuildingModule = false);
+ MemoryBufferCache *SharedPCMCache = nullptr);
~CompilerInstance() override;
/// @name High-Level Operations
@@ -292,6 +296,13 @@
return Invocation->getHeaderSearchOptsPtr();
}
+ APINotesOptions &getAPINotesOpts() {
+ return Invocation->getAPINotesOpts();
+ }
+ const APINotesOptions &getAPINotesOpts() const {
+ return Invocation->getAPINotesOpts();
+ }
+
LangOptions &getLangOpts() {
return *Invocation->getLangOpts();
}
@@ -783,6 +794,8 @@
}
void setExternalSemaSource(IntrusiveRefCntPtr<ExternalSemaSource> ESS);
+
+ MemoryBufferCache &getPCMCache() const { return *PCMCache; }
};
} // end namespace clang
diff --git a/include/clang/Frontend/CompilerInvocation.h b/include/clang/Frontend/CompilerInvocation.h
index cef7f73..239991d 100644
--- a/include/clang/Frontend/CompilerInvocation.h
+++ b/include/clang/Frontend/CompilerInvocation.h
@@ -10,6 +10,7 @@
#ifndef LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_
#define LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_
+#include "clang/APINotes/APINotesOptions.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/LangOptions.h"
@@ -113,6 +114,9 @@
MigratorOptions MigratorOpts;
+ /// Options controlling API notes.
+ APINotesOptions APINotesOpts;
+
/// Options controlling IRgen and the backend.
CodeGenOptions CodeGenOpts;
@@ -184,6 +188,11 @@
const MigratorOptions &getMigratorOpts() const {
return MigratorOpts;
}
+
+ APINotesOptions &getAPINotesOpts() { return APINotesOpts; }
+ const APINotesOptions &getAPINotesOpts() const {
+ return APINotesOpts;
+ }
CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; }
const CodeGenOptions &getCodeGenOpts() const {
diff --git a/include/clang/Frontend/PCHContainerOperations.h b/include/clang/Frontend/PCHContainerOperations.h
index d323fb3..f9a7350 100644
--- a/include/clang/Frontend/PCHContainerOperations.h
+++ b/include/clang/Frontend/PCHContainerOperations.h
@@ -10,6 +10,7 @@
#ifndef LLVM_CLANG_PCH_CONTAINER_OPERATIONS_H
#define LLVM_CLANG_PCH_CONTAINER_OPERATIONS_H
+#include "clang/Basic/Module.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -29,7 +30,7 @@
class CompilerInstance;
struct PCHBuffer {
- uint64_t Signature;
+ ASTFileSignature Signature;
llvm::SmallVector<char, 0> Data;
bool IsComplete;
};
diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h
index d19e5eb..16c3026 100644
--- a/include/clang/Index/IndexSymbol.h
+++ b/include/clang/Index/IndexSymbol.h
@@ -57,6 +57,7 @@
C,
ObjC,
CXX,
+ Swift,
};
/// Language specific sub-kinds.
@@ -66,6 +67,26 @@
CXXMoveConstructor,
AccessorGetter,
AccessorSetter,
+
+ // Swift sub-kinds
+
+ SwiftAccessorWillSet,
+ SwiftAccessorDidSet,
+ SwiftAccessorAddressor,
+ SwiftAccessorMutableAddressor,
+
+ SwiftExtensionOfStruct,
+ SwiftExtensionOfClass,
+ SwiftExtensionOfEnum,
+ SwiftExtensionOfProtocol,
+
+ SwiftPrefixOperator,
+ SwiftPostfixOperator,
+ SwiftInfixOperator,
+
+ SwiftSubscript,
+ SwiftAssociatedType,
+ SwiftGenericTypeParam,
};
/// Set of properties that provide additional info about a symbol.
diff --git a/include/clang/Index/IndexingAction.h b/include/clang/Index/IndexingAction.h
index e2e63dc..8eed33c 100644
--- a/include/clang/Index/IndexingAction.h
+++ b/include/clang/Index/IndexingAction.h
@@ -14,9 +14,14 @@
#include <memory>
namespace clang {
+ class ASTReader;
class ASTUnit;
class FrontendAction;
+namespace serialization {
+ class ModuleFile;
+}
+
namespace index {
class IndexDataConsumer;
@@ -42,6 +47,11 @@
std::shared_ptr<IndexDataConsumer> DataConsumer,
IndexingOptions Opts);
+void indexModuleFile(serialization::ModuleFile &Mod,
+ ASTReader &Reader,
+ std::shared_ptr<IndexDataConsumer> DataConsumer,
+ IndexingOptions Opts);
+
} // namespace index
} // namespace clang
diff --git a/include/clang/Index/USRGeneration.h b/include/clang/Index/USRGeneration.h
index be89068..61f2c9d 100644
--- a/include/clang/Index/USRGeneration.h
+++ b/include/clang/Index/USRGeneration.h
@@ -16,6 +16,7 @@
namespace clang {
class Decl;
class MacroDefinitionRecord;
+class SourceLocation;
class SourceManager;
namespace index {
@@ -54,6 +55,8 @@
/// \returns true on error, false on success.
bool generateUSRForMacro(const MacroDefinitionRecord *MD,
const SourceManager &SM, SmallVectorImpl<char> &Buf);
+bool generateUSRForMacro(StringRef MacroName, SourceLocation Loc,
+ const SourceManager &SM, SmallVectorImpl<char> &Buf);
} // namespace index
} // namespace clang
diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h
index 51983b9..4df3e78 100644
--- a/include/clang/Lex/HeaderSearch.h
+++ b/include/clang/Lex/HeaderSearch.h
@@ -406,8 +406,7 @@
/// \return false if \#including the file will have no effect or true
/// if we should include it.
bool ShouldEnterIncludeFile(Preprocessor &PP, const FileEntry *File,
- bool isImport, bool ModulesEnabled,
- Module *CorrespondingModule);
+ bool isImport, Module *CorrespondingModule);
/// \brief Return whether the specified file is a normal header,
/// a system header, or a C++ friendly system header.
diff --git a/include/clang/Lex/HeaderSearchOptions.h b/include/clang/Lex/HeaderSearchOptions.h
index e999805..ca3a84e 100644
--- a/include/clang/Lex/HeaderSearchOptions.h
+++ b/include/clang/Lex/HeaderSearchOptions.h
@@ -178,6 +178,8 @@
unsigned ModulesValidateDiagnosticOptions : 1;
+ unsigned ModulesHashContent : 1;
+
HeaderSearchOptions(StringRef _Sysroot = "/")
: Sysroot(_Sysroot), ModuleFormat("raw"), DisableModuleHash(0),
ImplicitModuleMaps(0), ModuleMapFileHomeIsCwd(0),
@@ -186,8 +188,8 @@
UseBuiltinIncludes(true), UseStandardSystemIncludes(true),
UseStandardCXXIncludes(true), UseLibcxx(false), Verbose(false),
ModulesValidateOncePerBuildSession(false),
- ModulesValidateSystemHeaders(false),
- UseDebugInfo(false), ModulesValidateDiagnosticOptions(true) {}
+ ModulesValidateSystemHeaders(false), UseDebugInfo(false),
+ ModulesValidateDiagnosticOptions(true), ModulesHashContent(false) {}
/// AddPath - Add the \p Path path to the specified \p Group list.
void AddPath(StringRef Path, frontend::IncludeDirGroup Group,
diff --git a/include/clang/Lex/Lexer.h b/include/clang/Lex/Lexer.h
index 830c25a..6f1f7c6 100644
--- a/include/clang/Lex/Lexer.h
+++ b/include/clang/Lex/Lexer.h
@@ -133,15 +133,17 @@
/// from. Currently this is only used by _Pragma handling.
SourceLocation getFileLoc() const { return FileLoc; }
-private:
/// Lex - Return the next token in the file. If this is the end of file, it
/// return the tok::eof token. This implicitly involves the preprocessor.
bool Lex(Token &Result);
-public:
/// isPragmaLexer - Returns true if this Lexer is being used to lex a pragma.
bool isPragmaLexer() const { return Is_PragmaLexer; }
+ /// Note that this Lexer is being used to lex a pragma, or something like it
+ /// that has simple end-of-file behavior.
+ void setIsPragmaLexer(bool value) { Is_PragmaLexer = value; }
+
private:
/// IndirectLex - An indirect call to 'Lex' that can be invoked via
/// the PreprocessorLexer interface.
diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h
index 4613672..e5400c6 100644
--- a/include/clang/Lex/ModuleMap.h
+++ b/include/clang/Lex/ModuleMap.h
@@ -92,7 +92,7 @@
// named LangOpts::CurrentModule, if we've loaded it).
Module *SourceModule;
- /// \brief The top-level modules that are known.
+ /// \brief The unshadowed top-level modules that are known.
llvm::StringMap<Module *> Modules;
/// \brief The number of modules we have created in total.
@@ -174,6 +174,15 @@
/// header.
llvm::DenseMap<const DirectoryEntry *, Module *> UmbrellaDirs;
+ /// \brief A generation counter that is used to test whether modules of the
+ /// same name may shadow or are illegal redefintions.
+ ///
+ /// Modules from earlier scopes may shadow modules from later ones.
+ /// Modules from the same scope may not have the same name.
+ unsigned CurrentModuleScopeID = 0;
+
+ llvm::DenseMap<Module *, unsigned> ModuleScopeIDs;
+
/// \brief The set of attributes that can be attached to a module.
struct Attributes {
Attributes()
@@ -188,6 +197,9 @@
/// \brief Whether this is an exhaustive set of configuration macros.
unsigned IsExhaustive : 1;
+ /// \brief Whether this is a module who has its swift_names inferred.
+ unsigned IsSwiftInferImportAsMember : 1;
+
/// \brief Whether files in this module can only include non-modular headers
/// and headers from used modules.
unsigned NoUndeclaredIncludes : 1;
@@ -316,14 +328,6 @@
BuiltinIncludeDir = Dir;
}
- /// \brief Get the directory that contains Clang-supplied include files.
- const DirectoryEntry *getBuiltinDir() const {
- return BuiltinIncludeDir;
- }
-
- /// \brief Is this a compiler builtin header?
- static bool isBuiltinHeader(StringRef FileName);
-
/// \brief Add a module map callback.
void addModuleMapCallbacks(std::unique_ptr<ModuleMapCallbacks> Callback) {
Callbacks.push_back(std::move(Callback));
@@ -440,6 +444,24 @@
Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir,
bool IsSystem, Module *Parent);
+ /// \brief Create a new top-level module that is shadowed by
+ /// \p ShadowingModule.
+ Module *createShadowedModule(StringRef Name, bool IsFramework,
+ Module *ShadowingModule);
+
+ /// \brief Creates a new declaration scope for module names, allowing
+ /// previously defined modules to shadow definitions from the new scope.
+ ///
+ /// \note Module names from earlier scopes will shadow names from the new
+ /// scope, which is the opposite of how shadowing works for variables.
+ void finishModuleDeclarationScope() { CurrentModuleScopeID += 1; }
+
+ bool mayShadowNewModule(Module *ExistingModule) {
+ assert(!ExistingModule->Parent && "expected top-level module");
+ assert(ModuleScopeIDs.count(ExistingModule) && "unknown module");
+ return ModuleScopeIDs[ExistingModule] < CurrentModuleScopeID;
+ }
+
/// \brief Retrieve the module map file containing the definition of the given
/// module.
///
diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h
index 7ce1aad..ee4b3c8 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/include/clang/Lex/Preprocessor.h
@@ -47,6 +47,7 @@
class FileManager;
class FileEntry;
class HeaderSearch;
+class MemoryBufferCache;
class PragmaNamespace;
class PragmaHandler;
class CommentHandler;
@@ -102,6 +103,7 @@
const TargetInfo *AuxTarget;
FileManager &FileMgr;
SourceManager &SourceMgr;
+ MemoryBufferCache &PCMCache;
std::unique_ptr<ScratchBuffer> ScratchBuf;
HeaderSearch &HeaderInfo;
ModuleLoader &TheModuleLoader;
@@ -652,6 +654,7 @@
public:
Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
DiagnosticsEngine &diags, LangOptions &opts, SourceManager &SM,
+ MemoryBufferCache &PCMCache,
HeaderSearch &Headers, ModuleLoader &TheModuleLoader,
IdentifierInfoLookup *IILookup = nullptr,
bool OwnsHeaderSearch = false,
@@ -691,6 +694,7 @@
const TargetInfo *getAuxTargetInfo() const { return AuxTarget; }
FileManager &getFileManager() const { return FileMgr; }
SourceManager &getSourceManager() const { return SourceMgr; }
+ MemoryBufferCache &getPCMCache() const { return PCMCache; }
HeaderSearch &getHeaderSearchInfo() const { return HeaderInfo; }
IdentifierTable &getIdentifierTable() { return Identifiers; }
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index fe15902..5fae263 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -2282,6 +2282,14 @@
SourceLocation ScopeLoc,
AttributeList::Syntax Syntax);
+ void ParseSwiftNewtypeAttribute(IdentifierInfo &SwiftNewtype,
+ SourceLocation SwiftNewtypeLoc,
+ ParsedAttributes &attrs,
+ SourceLocation *endLoc,
+ IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc,
+ AttributeList::Syntax Syntax);
+
void ParseAttributeWithTypeArg(IdentifierInfo &AttrName,
SourceLocation AttrNameLoc,
ParsedAttributes &Attrs,
@@ -2365,10 +2373,10 @@
AR_DeclspecAttributesParsed
};
- void ParseTypeQualifierListOpt(DeclSpec &DS,
- unsigned AttrReqs = AR_AllAttributesParsed,
- bool AtomicAllowed = true,
- bool IdentifierRequired = false);
+ void ParseTypeQualifierListOpt(
+ DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed,
+ bool AtomicAllowed = true, bool IdentifierRequired = false,
+ Optional<llvm::function_ref<void()>> CodeCompletionHandler = None);
void ParseDirectDeclarator(Declarator &D);
void ParseDecompositionDeclarator(Declarator &D);
void ParseParenDeclarator(Declarator &D);
@@ -2706,7 +2714,19 @@
//===--------------------------------------------------------------------===//
// C++11/G++: Type Traits [Type-Traits.html in the GCC manual]
ExprResult ParseTypeTrait();
-
+
+ /// Parse the given string as a type.
+ ///
+ /// This is a dangerous utility function currently employed only by API notes.
+ /// It is not a general entry-point for safely parsing types from strings.
+ ///
+ /// \param typeStr The string to be parsed as a type.
+ /// \param context The name of the context in which this string is being
+ /// parsed, which will be used in diagnostics.
+ /// \param includeLoc The location at which this parse was triggered.
+ TypeResult parseTypeFromString(StringRef typeStr, StringRef context,
+ SourceLocation includeLoc);
+
//===--------------------------------------------------------------------===//
// Embarcadero: Arary and Expression Traits
ExprResult ParseArrayTypeTrait();
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 63d0784..4f46fbf 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -27,6 +27,7 @@
#include "clang/AST/NSAPI.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/TypeLoc.h"
+#include "clang/APINotes/APINotesManager.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/ExpressionTraits.h"
#include "clang/Basic/LangOptions.h"
@@ -54,6 +55,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TinyPtrVector.h"
#include <deque>
+#include <functional>
#include <memory>
#include <string>
#include <vector>
@@ -304,6 +306,7 @@
ASTConsumer &Consumer;
DiagnosticsEngine &Diags;
SourceManager &SourceMgr;
+ api_notes::APINotesManager APINotes;
/// \brief Flag indicating whether or not to collect detailed statistics.
bool CollectStats;
@@ -573,6 +576,10 @@
OpaqueParser = P;
}
+ /// \brief Callback to the parser to parse a type expressed as a string.
+ std::function<TypeResult(StringRef, StringRef, SourceLocation)>
+ ParseTypeFromStringCallback;
+
class DelayedDiagnostics;
class DelayedDiagnosticsState {
@@ -1409,6 +1416,24 @@
}
};
+ /// Do a check to make sure \p Name looks like a legal swift_name
+ /// attribute for the decl \p D. Raise a diagnostic if the name is invalid
+ /// for the given declaration.
+ ///
+ /// For a function, this will validate a compound Swift name,
+ /// e.g. <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>,
+ /// and the function will output the number of parameter names, and whether
+ /// this is a single-arg initializer.
+ ///
+ /// For a type, enum constant, property, or variable declaration, this will
+ /// validate either a simple identifier, or a qualified
+ /// <code>context.identifier</code> name.
+ ///
+ /// \returns true if the name is a valid swift name for \p D, false otherwise.
+ bool DiagnoseSwiftName(Decl *D, StringRef Name,
+ SourceLocation ArgLoc,
+ IdentifierInfo *AttrName);
+
private:
bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
TypeDiagnoser *Diagnoser);
@@ -1777,6 +1802,8 @@
ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC,
SourceLocation Loc,
QualType T);
+ QualType adjustParameterTypeForObjCAutoRefCount(QualType T,
+ SourceLocation Loc);
ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc,
SourceLocation NameLoc, IdentifierInfo *Name,
QualType T, TypeSourceInfo *TSInfo,
@@ -2263,6 +2290,9 @@
unsigned AttrSpellingListIndex);
OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, SourceRange Range,
unsigned AttrSpellingListIndex);
+ SwiftNameAttr *mergeSwiftNameAttr(Decl *D, SourceRange Range,
+ StringRef Name, bool Override,
+ unsigned AttrSpellingListIndex);
InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, SourceRange Range,
IdentifierInfo *Ident,
unsigned AttrSpellingListIndex);
@@ -3105,6 +3135,12 @@
void checkUnusedDeclAttributes(Declarator &D);
+ /// Map any API notes provided for this declaration to attributes on the
+ /// declaration.
+ ///
+ /// Triggered by declaration-attribute processing.
+ void ProcessAPINotes(Decl *D);
+
/// Determine if type T is a valid subject for a nonnull and similar
/// attributes. By default, we look through references (the behavior used by
/// nonnull), but if the second parameter is true, then we treat a reference
@@ -3159,11 +3195,16 @@
/// \param allowArrayTypes Whether to accept nullability specifiers on an
/// array type (e.g., because it will decay to a pointer).
///
+ /// \param overrideExisting Whether to override an existing, locally-specified
+ /// nullability specifier rather than complaining about the conflict.
+ ///
/// \returns true if nullability cannot be applied, false otherwise.
bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability,
SourceLocation nullabilityLoc,
bool isContextSensitive,
- bool allowArrayTypes);
+ bool allowArrayTypes,
+ bool implicit,
+ bool overrideExisting = false);
/// \brief Stmt attributes - this routine is the top level dispatcher.
StmtResult ProcessStmtAttributes(Stmt *Stmt, AttributeList *Attrs,
@@ -7928,6 +7969,12 @@
RTC_Unknown
};
+ /// Check whether the declared result type of the given Objective-C
+ /// method declaration is compatible with the method's class.
+ ResultTypeCompatibilityKind
+ checkRelatedResultTypeCompatibility(const ObjCMethodDecl *Method,
+ const ObjCInterfaceDecl *CurrentClass);
+
void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
ObjCInterfaceDecl *CurrentClass,
ResultTypeCompatibilityKind RTC);
@@ -9777,6 +9824,8 @@
void CodeCompletePostfixExpression(Scope *S, ExprResult LHS);
void CodeCompleteTag(Scope *S, unsigned TagSpec);
void CodeCompleteTypeQualifiers(DeclSpec &DS);
+ void CodeCompleteFunctionQualifiers(DeclSpec &DS, Declarator &D,
+ const VirtSpecifiers *VS = nullptr);
void CodeCompleteBracketDeclarator(Scope *S);
void CodeCompleteCase(Scope *S);
void CodeCompleteCall(Scope *S, Expr *Fn, ArrayRef<Expr *> Args);
@@ -10107,6 +10156,7 @@
/// The struct behind the CFErrorRef pointer.
RecordDecl *CFError = nullptr;
+ bool isCFError(RecordDecl *D);
/// Retrieve the identifier "NSError".
IdentifierInfo *getNSErrorIdent();
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index acbd6d1..d431c15 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -226,7 +226,7 @@
/// \brief The block containing the detailed preprocessing record.
PREPROCESSOR_DETAIL_BLOCK_ID,
-
+
/// \brief The block containing the submodule structure.
SUBMODULE_BLOCK_ID,
@@ -253,6 +253,10 @@
/// \brief A block containing a module file extension.
EXTENSION_BLOCK_ID,
+
+ /// A block containing unhashed contents. It currently holds Diagnostic
+ /// Options and Signature.
+ UNHASHED_CONTROL_BLOCK_ID,
};
/// \brief Record types that occur within the control block.
@@ -288,9 +292,6 @@
/// AST file.
MODULE_MAP_FILE,
- /// \brief Record code for the signature that identifiers this AST file.
- SIGNATURE,
-
/// \brief Record code for the module build directory.
MODULE_DIRECTORY,
};
@@ -309,9 +310,6 @@
/// \brief Record code for the target options table.
TARGET_OPTIONS,
- /// \brief Record code for the diagnostic options table.
- DIAGNOSTIC_OPTIONS,
-
/// \brief Record code for the filesystem options table.
FILE_SYSTEM_OPTIONS,
@@ -322,6 +320,18 @@
PREPROCESSOR_OPTIONS,
};
+ /// Record codes for the unhashed control block.
+ enum UnhashedControlBlockRecordTypes {
+ /// Record code for the signature that identifiers this AST file.
+ SIGNATURE = 1,
+
+ /// Record code for the diagnostic options table.
+ DIAGNOSTIC_OPTIONS,
+
+ /// Record code for \#pragma diagnostic mappings.
+ DIAG_PRAGMA_MAPPINGS,
+ };
+
/// \brief Record code for extension blocks.
enum ExtensionBlockRecordTypes {
/// Metadata describing this particular extension.
@@ -493,8 +503,7 @@
// ID 31 used to be a list of offsets to DECL_CXX_BASE_SPECIFIERS records.
- /// \brief Record code for \#pragma diagnostic mappings.
- DIAG_PRAGMA_MAPPINGS = 32,
+ // ID 32 used to be the code for \#pragma diagnostic mappings.
/// \brief Record code for special CUDA declarations.
CUDA_SPECIAL_DECL_REFS = 33,
diff --git a/include/clang/Serialization/ASTDeserializationListener.h b/include/clang/Serialization/ASTDeserializationListener.h
index 4b10c39..c26f3e0 100644
--- a/include/clang/Serialization/ASTDeserializationListener.h
+++ b/include/clang/Serialization/ASTDeserializationListener.h
@@ -26,6 +26,7 @@
class MacroDefinitionRecord;
class MacroInfo;
class Module;
+class SourceLocation;
class ASTDeserializationListener {
public:
@@ -52,6 +53,9 @@
MacroDefinitionRecord *MD) {}
/// \brief A module definition was read from the AST file.
virtual void ModuleRead(serialization::SubmoduleID ID, Module *Mod) {}
+ /// \brief A module import was read from the AST file.
+ virtual void ModuleImportRead(serialization::SubmoduleID ID,
+ SourceLocation ImportLoc) {}
};
}
diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h
index 93994e2..e174517 100644
--- a/include/clang/Serialization/ASTReader.h
+++ b/include/clang/Serialization/ASTReader.h
@@ -408,6 +408,9 @@
/// \brief The module manager which manages modules and their dependencies
ModuleManager ModuleMgr;
+ /// The cache that manages memory buffers for PCM files.
+ MemoryBufferCache &PCMCache;
+
/// \brief A dummy identifier resolver used to merge TU-scope declarations in
/// C, for the cases where we don't have a Sema object to provide a real
/// identifier resolver.
@@ -1174,7 +1177,7 @@
SourceLocation ImportLoc, ModuleFile *ImportedBy,
SmallVectorImpl<ImportedModule> &Loaded,
off_t ExpectedSize, time_t ExpectedModTime,
- serialization::ASTFileSignature ExpectedSignature,
+ ASTFileSignature ExpectedSignature,
unsigned ClientLoadCapabilities);
ASTReadResult ReadControlBlock(ModuleFile &F,
SmallVectorImpl<ImportedModule> &Loaded,
@@ -1183,7 +1186,22 @@
static ASTReadResult ReadOptionsBlock(
llvm::BitstreamCursor &Stream, unsigned ClientLoadCapabilities,
bool AllowCompatibleConfigurationMismatch, ASTReaderListener &Listener,
- std::string &SuggestedPredefines, bool ValidateDiagnosticOptions);
+ std::string &SuggestedPredefines);
+
+ /// Read the unhashed control block.
+ ///
+ /// This has no effect on \c F.Stream, instead creating a fresh cursor from
+ /// \c F.Data and reading ahead.
+ ASTReadResult readUnhashedControlBlock(ModuleFile &F, bool WasImportedBy,
+ unsigned ClientLoadCapabilities);
+
+ static ASTReadResult
+ readUnhashedControlBlockImpl(ModuleFile *F, llvm::StringRef StreamData,
+ unsigned ClientLoadCapabilities,
+ bool AllowCompatibleConfigurationMismatch,
+ ASTReaderListener *Listener,
+ bool ValidateDiagnosticOptions);
+
ASTReadResult ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities);
ASTReadResult ReadExtensionBlock(ModuleFile &F);
bool ParseLineTable(ModuleFile &F, const RecordData &Record);
@@ -1268,6 +1286,7 @@
llvm::iterator_range<PreprocessingRecord::iterator>
getModulePreprocessedEntities(ModuleFile &Mod) const;
+public:
class ModuleDeclIterator
: public llvm::iterator_adaptor_base<
ModuleDeclIterator, const serialization::LocalDeclID *,
@@ -1298,6 +1317,7 @@
llvm::iterator_range<ModuleDeclIterator>
getModuleFileLevelDecls(ModuleFile &Mod);
+private:
void PassInterestingDeclsToConsumer();
void PassInterestingDeclToConsumer(Decl *D);
@@ -1633,7 +1653,7 @@
unsigned Result = 0;
for (ModuleConstIterator I = ModuleMgr.begin(),
E = ModuleMgr.end(); I != E; ++I) {
- Result += (*I)->NumPreprocessedEntities;
+ Result += I->NumPreprocessedEntities;
}
return Result;
@@ -2189,6 +2209,12 @@
/// \brief Loads comments ranges.
void ReadComments() override;
+ /// Visit all the input files of the given module file.
+ void visitInputFiles(serialization::ModuleFile &MF,
+ bool IncludeSystem, bool Complain,
+ llvm::function_ref<void(const serialization::InputFile &IF,
+ bool isSystem)> Visitor);
+
bool isProcessingUpdateRecords() { return ProcessingUpdateRecords; }
};
diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h
index 0d6b026..57b0a65 100644
--- a/include/clang/Serialization/ASTWriter.h
+++ b/include/clang/Serialization/ASTWriter.h
@@ -54,6 +54,7 @@
class OpaqueValueExpr;
class OpenCLOptions;
class ASTReader;
+class MemoryBufferCache;
class Module;
class ModuleFileExtension;
class ModuleFileExtensionWriter;
@@ -106,6 +107,12 @@
/// \brief The bitstream writer used to emit this precompiled header.
llvm::BitstreamWriter &Stream;
+ /// The buffer associated with the bitstream.
+ const SmallVectorImpl<char> &Buffer;
+
+ /// \brief The PCM manager which manages memory buffers for pcm files.
+ MemoryBufferCache &PCMCache;
+
/// \brief The ASTContext we're writing.
ASTContext *Context = nullptr;
@@ -424,8 +431,16 @@
void WriteSubStmt(Stmt *S);
void WriteBlockInfoBlock();
- uint64_t WriteControlBlock(Preprocessor &PP, ASTContext &Context,
- StringRef isysroot, const std::string &OutputFile);
+ void WriteControlBlock(Preprocessor &PP, ASTContext &Context,
+ StringRef isysroot, const std::string &OutputFile);
+
+ /// Write out the signature and diagnostic options, and return the signature.
+ ASTFileSignature writeUnhashedControlBlock(Preprocessor &PP,
+ ASTContext &Context);
+
+ /// Calculate hash of the pcm content.
+ static ASTFileSignature createSignature(StringRef Bytes);
+
void WriteInputFiles(SourceManager &SourceMgr, HeaderSearchOptions &HSOpts,
bool Modules);
void WriteSourceManagerBlock(SourceManager &SourceMgr,
@@ -492,14 +507,15 @@
void WriteDeclAbbrevs();
void WriteDecl(ASTContext &Context, Decl *D);
- uint64_t WriteASTCore(Sema &SemaRef,
- StringRef isysroot, const std::string &OutputFile,
- Module *WritingModule);
+ ASTFileSignature WriteASTCore(Sema &SemaRef, StringRef isysroot,
+ const std::string &OutputFile,
+ Module *WritingModule);
public:
/// \brief Create a new precompiled header writer that outputs to
/// the given bitstream.
- ASTWriter(llvm::BitstreamWriter &Stream,
+ ASTWriter(llvm::BitstreamWriter &Stream, SmallVectorImpl<char> &Buffer,
+ MemoryBufferCache &PCMCache,
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
bool IncludeTimestamps = true);
~ASTWriter() override;
@@ -525,9 +541,9 @@
///
/// \return the module signature, which eventually will be a hash of
/// the module but currently is merely a random 32-bit number.
- uint64_t WriteAST(Sema &SemaRef, const std::string &OutputFile,
- Module *WritingModule, StringRef isysroot,
- bool hasErrors = false);
+ ASTFileSignature WriteAST(Sema &SemaRef, const std::string &OutputFile,
+ Module *WritingModule, StringRef isysroot,
+ bool hasErrors = false);
/// \brief Emit a token.
void AddToken(const Token &Tok, RecordDataImpl &Record);
diff --git a/include/clang/Serialization/Module.h b/include/clang/Serialization/Module.h
index 58b3149..d28a2f3 100644
--- a/include/clang/Serialization/Module.h
+++ b/include/clang/Serialization/Module.h
@@ -16,6 +16,7 @@
#define LLVM_CLANG_SERIALIZATION_MODULE_H
#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Serialization/ASTBitCodes.h"
#include "clang/Serialization/ContinuousRangeMap.h"
@@ -89,8 +90,6 @@
bool isNotFound() const { return Val.getInt() == NotFound; }
};
-typedef unsigned ASTFileSignature;
-
/// \brief Information about a module that has been loaded by the ASTReader.
///
/// Each instance of the Module class corresponds to a single AST file, which
@@ -100,13 +99,14 @@
/// other modules.
class ModuleFile {
public:
- ModuleFile(ModuleKind Kind, unsigned Generation);
+ ModuleFile(ModuleKind Kind, unsigned Generation)
+ : Kind(Kind), Generation(Generation) {}
~ModuleFile();
// === General information ===
/// \brief The index of this module in the list of modules.
- unsigned Index;
+ unsigned Index = 0;
/// \brief The type of this module.
ModuleKind Kind;
@@ -144,34 +144,34 @@
std::string ModuleMapPath;
/// \brief Whether this precompiled header is a relocatable PCH file.
- bool RelocatablePCH;
+ bool RelocatablePCH = false;
/// \brief Whether timestamps are included in this module file.
- bool HasTimestamps;
+ bool HasTimestamps = false;
/// \brief The file entry for the module file.
- const FileEntry *File;
+ const FileEntry *File = nullptr;
- /// \brief The signature of the module file, which may be used along with size
+ /// The signature of the module file, which may be used instead of the size
/// and modification time to identify this particular file.
ASTFileSignature Signature;
/// \brief Whether this module has been directly imported by the
/// user.
- bool DirectlyImported;
+ bool DirectlyImported = false;
/// \brief The generation of which this module file is a part.
unsigned Generation;
- /// \brief The memory buffer that stores the data associated with
- /// this AST file.
- std::unique_ptr<llvm::MemoryBuffer> Buffer;
+ /// The memory buffer that stores the data associated with
+ /// this AST file, owned by the PCMCache in the ModuleManager.
+ llvm::MemoryBuffer *Buffer;
/// \brief The size of this file, in bits.
- uint64_t SizeInBits;
+ uint64_t SizeInBits = 0;
/// \brief The global bit offset (or base) of this module
- uint64_t GlobalBitOffset;
+ uint64_t GlobalBitOffset = 0;
/// \brief The serialized bitstream data for this file.
StringRef Data;
@@ -205,16 +205,20 @@
llvm::BitstreamCursor InputFilesCursor;
/// \brief Offsets for all of the input file entries in the AST file.
- const llvm::support::unaligned_uint64_t *InputFileOffsets;
+ const llvm::support::unaligned_uint64_t *InputFileOffsets = nullptr;
/// \brief The input files that have been loaded from this AST file.
std::vector<InputFile> InputFilesLoaded;
+ // All user input files reside at the index range [0, NumUserInputFiles), and
+ // system input files reside at [NumUserInputFiles, InputFilesLoaded.size()).
+ unsigned NumUserInputFiles = 0;
+
/// \brief If non-zero, specifies the time when we last validated input
/// files. Zero means we never validated them.
///
/// The time is specified in seconds since the start of the Epoch.
- uint64_t InputFilesValidationTimestamp;
+ uint64_t InputFilesValidationTimestamp = 0;
// === Source Locations ===
@@ -222,17 +226,17 @@
llvm::BitstreamCursor SLocEntryCursor;
/// \brief The number of source location entries in this AST file.
- unsigned LocalNumSLocEntries;
+ unsigned LocalNumSLocEntries = 0;
/// \brief The base ID in the source manager's view of this module.
- int SLocEntryBaseID;
+ int SLocEntryBaseID = 0;
/// \brief The base offset in the source manager's view of this module.
- unsigned SLocEntryBaseOffset;
+ unsigned SLocEntryBaseOffset = 0;
/// \brief Offsets for all of the source location entries in the
/// AST file.
- const uint32_t *SLocEntryOffsets;
+ const uint32_t *SLocEntryOffsets = nullptr;
/// \brief SLocEntries that we're going to preload.
SmallVector<uint64_t, 4> PreloadSLocEntries;
@@ -243,17 +247,17 @@
// === Identifiers ===
/// \brief The number of identifiers in this AST file.
- unsigned LocalNumIdentifiers;
+ unsigned LocalNumIdentifiers = 0;
/// \brief Offsets into the identifier table data.
///
/// This array is indexed by the identifier ID (-1), and provides
/// the offset into IdentifierTableData where the string data is
/// stored.
- const uint32_t *IdentifierOffsets;
+ const uint32_t *IdentifierOffsets = nullptr;
/// \brief Base identifier ID for identifiers local to this module.
- serialization::IdentID BaseIdentifierID;
+ serialization::IdentID BaseIdentifierID = 0;
/// \brief Remapping table for identifier IDs in this module.
ContinuousRangeMap<uint32_t, int, 2> IdentifierRemap;
@@ -262,11 +266,11 @@
///
/// This pointer points into a memory buffer, where the on-disk hash
/// table for identifiers actually lives.
- const char *IdentifierTableData;
+ const char *IdentifierTableData = nullptr;
/// \brief A pointer to an on-disk hash table of opaque type
/// IdentifierHashTable.
- void *IdentifierLookupTable;
+ void *IdentifierLookupTable = nullptr;
/// \brief Offsets of identifiers that we're going to preload within
/// IdentifierTableData.
@@ -279,23 +283,23 @@
llvm::BitstreamCursor MacroCursor;
/// \brief The number of macros in this AST file.
- unsigned LocalNumMacros;
+ unsigned LocalNumMacros = 0;
/// \brief Offsets of macros in the preprocessor block.
///
/// This array is indexed by the macro ID (-1), and provides
/// the offset into the preprocessor block where macro definitions are
/// stored.
- const uint32_t *MacroOffsets;
+ const uint32_t *MacroOffsets = nullptr;
/// \brief Base macro ID for macros local to this module.
- serialization::MacroID BaseMacroID;
+ serialization::MacroID BaseMacroID = 0;
/// \brief Remapping table for macro IDs in this module.
ContinuousRangeMap<uint32_t, int, 2> MacroRemap;
/// \brief The offset of the start of the set of defined macros.
- uint64_t MacroStartOffset;
+ uint64_t MacroStartOffset = 0;
// === Detailed PreprocessingRecord ===
@@ -304,40 +308,40 @@
llvm::BitstreamCursor PreprocessorDetailCursor;
/// \brief The offset of the start of the preprocessor detail cursor.
- uint64_t PreprocessorDetailStartOffset;
+ uint64_t PreprocessorDetailStartOffset = 0;
/// \brief Base preprocessed entity ID for preprocessed entities local to
/// this module.
- serialization::PreprocessedEntityID BasePreprocessedEntityID;
+ serialization::PreprocessedEntityID BasePreprocessedEntityID = 0;
/// \brief Remapping table for preprocessed entity IDs in this module.
ContinuousRangeMap<uint32_t, int, 2> PreprocessedEntityRemap;
- const PPEntityOffset *PreprocessedEntityOffsets;
- unsigned NumPreprocessedEntities;
+ const PPEntityOffset *PreprocessedEntityOffsets = nullptr;
+ unsigned NumPreprocessedEntities = 0;
// === Header search information ===
/// \brief The number of local HeaderFileInfo structures.
- unsigned LocalNumHeaderFileInfos;
+ unsigned LocalNumHeaderFileInfos = 0;
/// \brief Actual data for the on-disk hash table of header file
/// information.
///
/// This pointer points into a memory buffer, where the on-disk hash
/// table for header file information actually lives.
- const char *HeaderFileInfoTableData;
+ const char *HeaderFileInfoTableData = nullptr;
/// \brief The on-disk hash table that contains information about each of
/// the header files.
- void *HeaderFileInfoTable;
+ void *HeaderFileInfoTable = nullptr;
// === Submodule information ===
/// \brief The number of submodules in this module.
- unsigned LocalNumSubmodules;
+ unsigned LocalNumSubmodules = 0;
/// \brief Base submodule ID for submodules local to this module.
- serialization::SubmoduleID BaseSubmoduleID;
+ serialization::SubmoduleID BaseSubmoduleID = 0;
/// \brief Remapping table for submodule IDs in this module.
ContinuousRangeMap<uint32_t, int, 2> SubmoduleRemap;
@@ -347,14 +351,14 @@
/// \brief The number of selectors new to this file.
///
/// This is the number of entries in SelectorOffsets.
- unsigned LocalNumSelectors;
+ unsigned LocalNumSelectors = 0;
/// \brief Offsets into the selector lookup table's data array
/// where each selector resides.
- const uint32_t *SelectorOffsets;
+ const uint32_t *SelectorOffsets = nullptr;
/// \brief Base selector ID for selectors local to this module.
- serialization::SelectorID BaseSelectorID;
+ serialization::SelectorID BaseSelectorID = 0;
/// \brief Remapping table for selector IDs in this module.
ContinuousRangeMap<uint32_t, int, 2> SelectorRemap;
@@ -362,14 +366,14 @@
/// \brief A pointer to the character data that comprises the selector table
///
/// The SelectorOffsets table refers into this memory.
- const unsigned char *SelectorLookupTableData;
+ const unsigned char *SelectorLookupTableData = nullptr;
/// \brief A pointer to an on-disk hash table of opaque type
/// ASTSelectorLookupTable.
///
/// This hash table provides the IDs of all selectors, and the associated
/// instance and factory methods.
- void *SelectorLookupTable;
+ void *SelectorLookupTable = nullptr;
// === Declarations ===
@@ -379,14 +383,14 @@
llvm::BitstreamCursor DeclsCursor;
/// \brief The number of declarations in this AST file.
- unsigned LocalNumDecls;
+ unsigned LocalNumDecls = 0;
/// \brief Offset of each declaration within the bitstream, indexed
/// by the declaration ID (-1).
- const DeclOffset *DeclOffsets;
+ const DeclOffset *DeclOffsets = nullptr;
/// \brief Base declaration ID for declarations local to this module.
- serialization::DeclID BaseDeclID;
+ serialization::DeclID BaseDeclID = 0;
/// \brief Remapping table for declaration IDs in this module.
ContinuousRangeMap<uint32_t, int, 2> DeclRemap;
@@ -401,15 +405,15 @@
llvm::DenseMap<ModuleFile *, serialization::DeclID> GlobalToLocalDeclIDs;
/// \brief Array of file-level DeclIDs sorted by file.
- const serialization::DeclID *FileSortedDecls;
- unsigned NumFileSortedDecls;
+ const serialization::DeclID *FileSortedDecls = nullptr;
+ unsigned NumFileSortedDecls = 0;
/// \brief Array of category list location information within this
/// module file, sorted by the definition ID.
- const serialization::ObjCCategoriesInfo *ObjCCategoriesMap;
+ const serialization::ObjCCategoriesInfo *ObjCCategoriesMap = nullptr;
/// \brief The number of redeclaration info entries in ObjCCategoriesMap.
- unsigned LocalNumObjCCategoriesInMap;
+ unsigned LocalNumObjCCategoriesInMap = 0;
/// \brief The Objective-C category lists for categories known to this
/// module.
@@ -418,15 +422,15 @@
// === Types ===
/// \brief The number of types in this AST file.
- unsigned LocalNumTypes;
+ unsigned LocalNumTypes = 0;
/// \brief Offset of each type within the bitstream, indexed by the
/// type ID, or the representation of a Type*.
- const uint32_t *TypeOffsets;
+ const uint32_t *TypeOffsets = nullptr;
/// \brief Base type ID for types local to this module as represented in
/// the global type ID space.
- serialization::TypeID BaseTypeIndex;
+ serialization::TypeID BaseTypeIndex = 0;
/// \brief Remapping table for type IDs in this module.
ContinuousRangeMap<uint32_t, int, 2> TypeRemap;
diff --git a/include/clang/Serialization/ModuleManager.h b/include/clang/Serialization/ModuleManager.h
index 1c4d88e..da6bedc 100644
--- a/include/clang/Serialization/ModuleManager.h
+++ b/include/clang/Serialization/ModuleManager.h
@@ -19,10 +19,12 @@
#include "clang/Serialization/Module.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/iterator.h"
namespace clang {
class GlobalModuleIndex;
+class MemoryBufferCache;
class ModuleMap;
class PCHContainerReader;
@@ -32,7 +34,7 @@
class ModuleManager {
/// \brief The chain of AST files, in the order in which we started to load
/// them (this order isn't really useful for anything).
- SmallVector<ModuleFile *, 2> Chain;
+ SmallVector<std::unique_ptr<ModuleFile>, 2> Chain;
/// \brief The chain of non-module PCH files. The first entry is the one named
/// by the user, the last one is the one that doesn't depend on anything
@@ -50,6 +52,9 @@
/// FileEntry *.
FileManager &FileMgr;
+ /// Cache of PCM files.
+ IntrusiveRefCntPtr<MemoryBufferCache> PCMCache;
+
/// \brief Knows how to unwrap module containers.
const PCHContainerReader &PCHContainerRdr;
@@ -111,12 +116,18 @@
void returnVisitState(VisitState *State);
public:
- typedef SmallVectorImpl<ModuleFile*>::iterator ModuleIterator;
- typedef SmallVectorImpl<ModuleFile*>::const_iterator ModuleConstIterator;
- typedef SmallVectorImpl<ModuleFile*>::reverse_iterator ModuleReverseIterator;
+ typedef llvm::pointee_iterator<
+ SmallVectorImpl<std::unique_ptr<ModuleFile>>::iterator>
+ ModuleIterator;
+ typedef llvm::pointee_iterator<
+ SmallVectorImpl<std::unique_ptr<ModuleFile>>::const_iterator>
+ ModuleConstIterator;
+ typedef llvm::pointee_iterator<
+ SmallVectorImpl<std::unique_ptr<ModuleFile>>::reverse_iterator>
+ ModuleReverseIterator;
typedef std::pair<uint32_t, StringRef> ModuleOffset;
- explicit ModuleManager(FileManager &FileMgr,
+ explicit ModuleManager(FileManager &FileMgr, MemoryBufferCache &PCMCache,
const PCHContainerReader &PCHContainerRdr);
~ModuleManager();
@@ -136,7 +147,8 @@
ModuleReverseIterator rend() { return Chain.rend(); }
/// \brief A range covering the PCH and preamble module files loaded.
- llvm::iterator_range<ModuleConstIterator> pch_modules() const {
+ llvm::iterator_range<SmallVectorImpl<ModuleFile *>::const_iterator>
+ pch_modules() const {
return llvm::make_range(PCHChain.begin(), PCHChain.end());
}
@@ -220,8 +232,8 @@
ModuleFile *&Module,
std::string &ErrorStr);
- /// \brief Remove the given set of modules.
- void removeModules(ModuleIterator first, ModuleIterator last,
+ /// \brief Remove the modules starting from First (to the end).
+ void removeModules(ModuleIterator First,
llvm::SmallPtrSetImpl<ModuleFile *> &LoadedSuccessfully,
ModuleMap *modMap);
@@ -282,6 +294,8 @@
/// \brief View the graphviz representation of the module graph.
void viewGraph();
+
+ MemoryBufferCache &getPCMCache() const { return *PCMCache; }
};
} } // end namespace clang::serialization
diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h
index dd7a6c8..f947776 100644
--- a/include/clang/StaticAnalyzer/Core/Checker.h
+++ b/include/clang/StaticAnalyzer/Core/Checker.h
@@ -321,9 +321,11 @@
const InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> Explicits,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
const CallEvent *Call) {
- return ((const CHECKER *)checker)->checkRegionChanges(state, invalidated,
- Explicits, Regions, Call);
+ return ((const CHECKER *) checker)->checkRegionChanges(state, invalidated,
+ Explicits, Regions,
+ LCtx, Call);
}
public:
diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h
index 0316c8f..52ed260 100644
--- a/include/clang/StaticAnalyzer/Core/CheckerManager.h
+++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h
@@ -338,6 +338,7 @@
const InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
const CallEvent *Call);
/// \brief Run checkers when pointers escape.
@@ -443,10 +444,11 @@
typedef CheckerFn<void (ProgramStateRef,SymbolReaper &)> CheckLiveSymbolsFunc;
typedef CheckerFn<ProgramStateRef (ProgramStateRef,
- const InvalidatedSymbols *symbols,
- ArrayRef<const MemRegion *> ExplicitRegions,
- ArrayRef<const MemRegion *> Regions,
- const CallEvent *Call)>
+ const InvalidatedSymbols *symbols,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call)>
CheckRegionChangesFunc;
typedef CheckerFn<ProgramStateRef (ProgramStateRef,
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index 89610ef..aaecf38 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -55,6 +55,7 @@
class CallDescription {
friend CallEvent;
mutable IdentifierInfo *II;
+ mutable bool IsLookupDone;
StringRef FuncName;
unsigned RequiredArgs;
@@ -68,7 +69,8 @@
/// call. Omit this parameter to match every occurance of call with a given
/// name regardless the number of arguments.
CallDescription(StringRef FuncName, unsigned RequiredArgs = NoArgRequirement)
- : II(nullptr), FuncName(FuncName), RequiredArgs(RequiredArgs) {}
+ : II(nullptr), IsLookupDone(false), FuncName(FuncName),
+ RequiredArgs(RequiredArgs) {}
/// \brief Get the name of the function that this object matches.
StringRef getFunctionName() const { return FuncName; }
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 591b112..fa7769b 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -293,6 +293,7 @@
const InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
const CallEvent *Call) override;
/// printState - Called by ProgramStateManager to print checker-specific data.
@@ -522,7 +523,9 @@
/// Call PointerEscape callback when a value escapes as a result of bind.
ProgramStateRef processPointerEscapedOnBind(ProgramStateRef State,
- SVal Loc, SVal Val) override;
+ SVal Loc,
+ SVal Val,
+ const LocationContext *LCtx) override;
/// Call PointerEscape callback when a value escapes as a result of
/// region invalidation.
/// \param[in] ITraits Specifies invalidation traits for regions/symbols.
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index 463b375..2910ef4 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -229,11 +229,12 @@
ProgramStateRef bindLoc(Loc location,
SVal V,
+ const LocationContext *LCtx,
bool notifyChanges = true) const;
- ProgramStateRef bindLoc(SVal location, SVal V) const;
+ ProgramStateRef bindLoc(SVal location, SVal V, const LocationContext *LCtx) const;
- ProgramStateRef bindDefault(SVal loc, SVal V) const;
+ ProgramStateRef bindDefault(SVal loc, SVal V, const LocationContext *LCtx) const;
ProgramStateRef killBinding(Loc LV) const;
@@ -681,9 +682,9 @@
this, Val.castAs<NonLoc>(), From, To);
}
-inline ProgramStateRef ProgramState::bindLoc(SVal LV, SVal V) const {
+inline ProgramStateRef ProgramState::bindLoc(SVal LV, SVal V, const LocationContext *LCtx) const {
if (Optional<Loc> L = LV.getAs<Loc>())
- return bindLoc(*L, V);
+ return bindLoc(*L, V, LCtx);
return this;
}
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
index 581ef20..e16df13 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
@@ -131,17 +131,19 @@
const InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
const CallEvent *Call) = 0;
inline ProgramStateRef
processRegionChange(ProgramStateRef state,
- const MemRegion* MR) {
- return processRegionChanges(state, nullptr, MR, MR, nullptr);
+ const MemRegion* MR,
+ const LocationContext *LCtx) {
+ return processRegionChanges(state, nullptr, MR, MR, LCtx, nullptr);
}
virtual ProgramStateRef
- processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, SVal Val) = 0;
+ processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, SVal Val, const LocationContext *LCtx) = 0;
virtual ProgramStateRef
notifyCheckersOfPointerEscape(ProgramStateRef State,
diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h
new file mode 100644
index 0000000..ef3ef73
--- /dev/null
+++ b/lib/APINotes/APINotesFormat.h
@@ -0,0 +1,308 @@
+//===--- APINotesFormat.h - The internals of API notes files ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Contains various constants and helper types to deal with API notes
+/// files.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_API_NOTES_FORMAT_H
+#define LLVM_CLANG_API_NOTES_FORMAT_H
+
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/PointerEmbeddedInt.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Bitcode/RecordLayout.h"
+
+namespace clang {
+namespace api_notes {
+
+using namespace llvm;
+
+/// Magic number for API notes files.
+const unsigned char API_NOTES_SIGNATURE[] = { 0xE2, 0x9C, 0xA8, 0x01 };
+
+/// API notes file major version number.
+///
+const uint16_t VERSION_MAJOR = 0;
+
+/// API notes file minor version number.
+///
+/// When the format changes IN ANY WAY, this number should be incremented.
+const uint16_t VERSION_MINOR = 21; // Override types
+
+using IdentifierID = PointerEmbeddedInt<unsigned, 31>;
+using IdentifierIDField = BCVBR<16>;
+
+using SelectorID = PointerEmbeddedInt<unsigned, 31>;
+using SelectorIDField = BCVBR<16>;
+
+using StoredContextID = PointerEmbeddedInt<unsigned, 31>;
+
+/// The various types of blocks that can occur within a API notes file.
+///
+/// These IDs must \em not be renumbered or reordered without incrementing
+/// VERSION_MAJOR.
+enum BlockID {
+ /// The control block, which contains all of the information that needs to
+ /// be validated prior to committing to loading the API notes file.
+ ///
+ /// \sa control_block
+ CONTROL_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID,
+
+ /// The identifier data block, which maps identifier strings to IDs.
+ IDENTIFIER_BLOCK_ID,
+
+ /// The Objective-C context data block, which contains information about
+ /// Objective-C classes and protocols.
+ OBJC_CONTEXT_BLOCK_ID,
+
+ /// The Objective-C property data block, which maps Objective-C
+ /// (class name, property name) pairs to information about the
+ /// property.
+ OBJC_PROPERTY_BLOCK_ID,
+
+ /// The Objective-C property data block, which maps Objective-C
+ /// (class name, selector, is_instance_method) tuples to information
+ /// about the method.
+ OBJC_METHOD_BLOCK_ID,
+
+ /// The Objective-C selector data block, which maps Objective-C
+ /// selector names (# of pieces, identifier IDs) to the selector ID
+ /// used in other tables.
+ OBJC_SELECTOR_BLOCK_ID,
+
+ /// The global variables data block, which maps global variable names to
+ /// information about the global variable.
+ GLOBAL_VARIABLE_BLOCK_ID,
+
+ /// The (global) functions data block, which maps global function names to
+ /// information about the global function.
+ GLOBAL_FUNCTION_BLOCK_ID,
+
+ /// The tag data block, which maps tag names to information about
+ /// the tags.
+ TAG_BLOCK_ID,
+
+ /// The typedef data block, which maps typedef names to information about
+ /// the typedefs.
+ TYPEDEF_BLOCK_ID,
+
+ /// The enum constant data block, which maps enumerator names to
+ /// information about the enumerators.
+ ENUM_CONSTANT_BLOCK_ID,
+};
+
+namespace control_block {
+ // These IDs must \em not be renumbered or reordered without incrementing
+ // VERSION_MAJOR.
+ enum {
+ METADATA = 1,
+ MODULE_NAME = 2,
+ MODULE_OPTIONS = 3,
+ SOURCE_FILE = 4,
+ };
+
+ using MetadataLayout = BCRecordLayout<
+ METADATA, // ID
+ BCFixed<16>, // Module format major version
+ BCFixed<16> // Module format minor version
+ >;
+
+ using ModuleNameLayout = BCRecordLayout<
+ MODULE_NAME,
+ BCBlob // Module name
+ >;
+
+ using ModuleOptionsLayout = BCRecordLayout<
+ MODULE_OPTIONS,
+ BCFixed<1> // SwiftInferImportAsMember
+ >;
+
+ using SourceFileLayout = BCRecordLayout<
+ SOURCE_FILE,
+ BCVBR<16>, // file size
+ BCVBR<16> // creation time
+ >;
+}
+
+namespace identifier_block {
+ enum {
+ IDENTIFIER_DATA = 1,
+ };
+
+ using IdentifierDataLayout = BCRecordLayout<
+ IDENTIFIER_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from identifier strings to decl kinds / decl IDs
+ >;
+}
+
+namespace objc_context_block {
+ enum {
+ OBJC_CONTEXT_ID_DATA = 1,
+ OBJC_CONTEXT_INFO_DATA = 2,
+ };
+
+ using ObjCContextIDLayout = BCRecordLayout<
+ OBJC_CONTEXT_ID_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from ObjC class names/protocol (as IDs) to context IDs
+ >;
+
+ using ObjCContextInfoLayout = BCRecordLayout<
+ OBJC_CONTEXT_INFO_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from ObjC context IDs to context information.
+ >;
+}
+
+namespace objc_property_block {
+ enum {
+ OBJC_PROPERTY_DATA = 1,
+ };
+
+ using ObjCPropertyDataLayout = BCRecordLayout<
+ OBJC_PROPERTY_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from ObjC (class name, property name) pairs to ObjC
+ // property information
+ >;
+}
+
+namespace objc_method_block {
+ enum {
+ OBJC_METHOD_DATA = 1,
+ };
+
+ using ObjCMethodDataLayout = BCRecordLayout<
+ OBJC_METHOD_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from ObjC (class names, selector,
+ // is-instance-method) tuples to ObjC method information
+ >;
+}
+
+namespace objc_selector_block {
+ enum {
+ OBJC_SELECTOR_DATA = 1,
+ };
+
+ using ObjCSelectorDataLayout = BCRecordLayout<
+ OBJC_SELECTOR_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from (# pieces, identifier IDs) to Objective-C selector ID.
+ >;
+}
+
+namespace global_variable_block {
+ enum {
+ GLOBAL_VARIABLE_DATA = 1
+ };
+
+ using GlobalVariableDataLayout = BCRecordLayout<
+ GLOBAL_VARIABLE_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to global variable information
+ >;
+}
+
+namespace global_function_block {
+ enum {
+ GLOBAL_FUNCTION_DATA = 1
+ };
+
+ using GlobalFunctionDataLayout = BCRecordLayout<
+ GLOBAL_FUNCTION_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to global function information
+ >;
+}
+
+namespace tag_block {
+ enum {
+ TAG_DATA = 1
+ };
+
+ using TagDataLayout = BCRecordLayout<
+ TAG_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to tag information
+ >;
+};
+
+namespace typedef_block {
+ enum {
+ TYPEDEF_DATA = 1
+ };
+
+ using TypedefDataLayout = BCRecordLayout<
+ TYPEDEF_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to typedef information
+ >;
+};
+
+namespace enum_constant_block {
+ enum {
+ ENUM_CONSTANT_DATA = 1
+ };
+
+ using EnumConstantDataLayout = BCRecordLayout<
+ ENUM_CONSTANT_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to enumerator information
+ >;
+}
+
+/// A stored Objective-C selector.
+struct StoredObjCSelector {
+ unsigned NumPieces;
+ llvm::SmallVector<IdentifierID, 2> Identifiers;
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+namespace llvm {
+ template<>
+ struct DenseMapInfo<clang::api_notes::StoredObjCSelector> {
+ typedef DenseMapInfo<unsigned> UnsignedInfo;
+
+ static inline clang::api_notes::StoredObjCSelector getEmptyKey() {
+ return clang::api_notes::StoredObjCSelector{
+ UnsignedInfo::getEmptyKey(), { } };
+ }
+
+ static inline clang::api_notes::StoredObjCSelector getTombstoneKey() {
+ return clang::api_notes::StoredObjCSelector{
+ UnsignedInfo::getTombstoneKey(), { } };
+ }
+
+ static unsigned getHashValue(
+ const clang::api_notes::StoredObjCSelector& value) {
+ auto hash = llvm::hash_value(value.NumPieces);
+ hash = hash_combine(hash, value.Identifiers.size());
+ for (auto piece : value.Identifiers)
+ hash = hash_combine(hash, static_cast<unsigned>(piece));
+ // FIXME: Mix upper/lower 32-bit values together to produce
+ // unsigned rather than truncating.
+ return hash;
+ }
+
+ static bool isEqual(const clang::api_notes::StoredObjCSelector &lhs,
+ const clang::api_notes::StoredObjCSelector &rhs) {
+ return lhs.NumPieces == rhs.NumPieces &&
+ lhs.Identifiers == rhs.Identifiers;
+ }
+ };
+}
+
+#endif // LLVM_CLANG_API_NOTES_FORMAT_H
diff --git a/lib/APINotes/APINotesManager.cpp b/lib/APINotes/APINotesManager.cpp
new file mode 100644
index 0000000..832f454
--- /dev/null
+++ b/lib/APINotes/APINotesManager.cpp
@@ -0,0 +1,587 @@
+//===--- APINotesManager.cpp - Manage API Notes Files ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the APINotesManager class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/APINotes/APINotesManager.h"
+#include "clang/APINotes/APINotesOptions.h"
+#include "clang/APINotes/APINotesReader.h"
+#include "clang/APINotes/APINotesYAMLCompiler.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/SourceMgrAdapter.h"
+#include "clang/Basic/Version.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include <sys/stat.h>
+
+using namespace clang;
+using namespace api_notes;
+
+#define DEBUG_TYPE "API Notes"
+STATISTIC(NumHeaderAPINotes,
+ "non-framework API notes files loaded");
+STATISTIC(NumPublicFrameworkAPINotes,
+ "framework public API notes loaded");
+STATISTIC(NumPrivateFrameworkAPINotes,
+ "framework private API notes loaded");
+STATISTIC(NumFrameworksSearched,
+ "frameworks searched");
+STATISTIC(NumDirectoriesSearched,
+ "header directories searched");
+STATISTIC(NumDirectoryCacheHits,
+ "directory cache hits");
+STATISTIC(NumBinaryCacheHits,
+ "binary form cache hits");
+STATISTIC(NumBinaryCacheMisses,
+ "binary form cache misses");
+STATISTIC(NumBinaryCacheRebuilds,
+ "binary form cache rebuilds");
+
+namespace {
+ /// Prints two successive strings, which much be kept alive as long as the
+ /// PrettyStackTrace entry.
+ class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {
+ StringRef First, Second;
+ public:
+ PrettyStackTraceDoubleString(StringRef first, StringRef second)
+ : First(first), Second(second) {}
+ void print(raw_ostream &OS) const override {
+ OS << First << Second;
+ }
+ };
+}
+
+APINotesManager::APINotesManager(SourceManager &sourceMgr,
+ const LangOptions &langOpts)
+ : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes),
+ PrunedCache(false) { }
+
+APINotesManager::~APINotesManager() {
+ // Free the API notes readers.
+ for (const auto &entry : Readers) {
+ if (auto reader = entry.second.dyn_cast<APINotesReader *>()) {
+ delete reader;
+ }
+ }
+
+ delete CurrentModuleReaders[0];
+ delete CurrentModuleReaders[1];
+}
+
+/// \brief Write a new timestamp file with the given path.
+static void writeTimestampFile(StringRef TimestampFile) {
+ std::error_code EC;
+ llvm::raw_fd_ostream Out(TimestampFile.str(), EC, llvm::sys::fs::F_None);
+}
+
+/// \brief Prune the API notes cache of API notes that haven't been accessed in
+/// a long time.
+static void pruneAPINotesCache(StringRef APINotesCachePath) {
+ struct stat StatBuf;
+ llvm::SmallString<128> TimestampFile;
+ TimestampFile = APINotesCachePath;
+ llvm::sys::path::append(TimestampFile, "APINotes.timestamp");
+
+ // Try to stat() the timestamp file.
+ if (::stat(TimestampFile.c_str(), &StatBuf)) {
+ // If the timestamp file wasn't there, create one now.
+ if (errno == ENOENT) {
+ llvm::sys::fs::create_directories(APINotesCachePath);
+ writeTimestampFile(TimestampFile);
+ }
+ return;
+ }
+
+ const unsigned APINotesCachePruneInterval = 7 * 24 * 60 * 60;
+ const unsigned APINotesCachePruneAfter = 31 * 24 * 60 * 60;
+
+ // Check whether the time stamp is older than our pruning interval.
+ // If not, do nothing.
+ time_t TimeStampModTime = StatBuf.st_mtime;
+ time_t CurrentTime = time(nullptr);
+ if (CurrentTime - TimeStampModTime <= time_t(APINotesCachePruneInterval))
+ return;
+
+ // Write a new timestamp file so that nobody else attempts to prune.
+ // There is a benign race condition here, if two Clang instances happen to
+ // notice at the same time that the timestamp is out-of-date.
+ writeTimestampFile(TimestampFile);
+
+ // Walk the entire API notes cache, looking for unused compiled API notes.
+ std::error_code EC;
+ SmallString<128> APINotesCachePathNative;
+ llvm::sys::path::native(APINotesCachePath, APINotesCachePathNative);
+ for (llvm::sys::fs::directory_iterator
+ File(APINotesCachePathNative.str(), EC), DirEnd;
+ File != DirEnd && !EC; File.increment(EC)) {
+ StringRef Extension = llvm::sys::path::extension(File->path());
+ if (Extension.empty())
+ continue;
+
+ if (Extension.substr(1) != BINARY_APINOTES_EXTENSION)
+ continue;
+
+ // Look at this file. If we can't stat it, there's nothing interesting
+ // there.
+ if (::stat(File->path().c_str(), &StatBuf))
+ continue;
+
+ // If the file has been used recently enough, leave it there.
+ time_t FileAccessTime = StatBuf.st_atime;
+ if (CurrentTime - FileAccessTime <= time_t(APINotesCachePruneAfter)) {
+ continue;
+ }
+
+ // Remove the file.
+ llvm::sys::fs::remove(File->path());
+ }
+}
+
+std::unique_ptr<APINotesReader>
+APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) {
+ FileManager &fileMgr = SourceMgr.getFileManager();
+ PrettyStackTraceDoubleString trace("Loading API notes from ",
+ apiNotesFile->getName());
+
+ // If the API notes file is already in the binary form, load it directly.
+ StringRef apiNotesFileName = apiNotesFile->getName();
+ StringRef apiNotesFileExt = llvm::sys::path::extension(apiNotesFileName);
+ if (!apiNotesFileExt.empty() &&
+ apiNotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) {
+ auto compiledFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User);
+
+ // Load the file.
+ auto buffer = SourceMgr.getBuffer(compiledFileID, SourceLocation());
+ if (!buffer) return nullptr;
+
+ // Load the binary form.
+ return APINotesReader::getUnmanaged(buffer, SwiftVersion);
+ }
+
+ // If we haven't pruned the API notes cache yet during this execution, do
+ // so now.
+ if (!PrunedCache) {
+ pruneAPINotesCache(fileMgr.getFileSystemOpts().APINotesCachePath);
+ PrunedCache = true;
+ }
+
+ // Compute a hash of the API notes file's directory and the Clang version,
+ // to be used as part of the filename for the cached binary copy.
+ auto code = llvm::hash_value(StringRef(apiNotesFile->getDir()->getName()));
+ code = hash_combine(code, getClangFullRepositoryVersion());
+
+ // Determine the file name for the cached binary form.
+ SmallString<128> compiledFileName;
+ compiledFileName += fileMgr.getFileSystemOpts().APINotesCachePath;
+ assert(!compiledFileName.empty() && "No API notes cache path provided?");
+ llvm::sys::path::append(compiledFileName,
+ (llvm::Twine(llvm::sys::path::stem(apiNotesFileName)) + "-"
+ + llvm::APInt(64, code).toString(36, /*Signed=*/false) + "."
+ + BINARY_APINOTES_EXTENSION));
+
+ // Try to open the cached binary form.
+ if (const FileEntry *compiledFile = fileMgr.getFile(compiledFileName,
+ /*openFile=*/true,
+ /*cacheFailure=*/false)) {
+ // Load the file contents.
+ if (auto buffer = fileMgr.getBufferForFile(compiledFile)) {
+ // Load the file.
+ if (auto reader = APINotesReader::get(std::move(buffer.get()),
+ SwiftVersion)) {
+ bool outOfDate = false;
+ if (auto sizeAndModTime = reader->getSourceFileSizeAndModTime()) {
+ if (sizeAndModTime->first != apiNotesFile->getSize() ||
+ sizeAndModTime->second != apiNotesFile->getModificationTime())
+ outOfDate = true;
+ }
+
+ if (!outOfDate) {
+ // Success.
+ ++NumBinaryCacheHits;
+ return reader;
+ }
+ }
+ }
+
+ // The cache entry was somehow broken; delete this one so we can build a
+ // new one below.
+ llvm::sys::fs::remove(compiledFileName.str());
+ ++NumBinaryCacheRebuilds;
+ } else {
+ ++NumBinaryCacheMisses;
+ }
+
+ // Open the source file.
+ auto sourceFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User);
+ auto sourceBuffer = SourceMgr.getBuffer(sourceFileID, SourceLocation());
+ if (!sourceBuffer) return nullptr;
+
+ // Compile the API notes source into a buffer.
+ // FIXME: Either propagate OSType through or, better yet, improve the binary
+ // APINotes format to maintain complete availability information.
+ llvm::SmallVector<char, 1024> apiNotesBuffer;
+ std::unique_ptr<llvm::MemoryBuffer> compiledBuffer;
+ {
+ SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(),
+ diag::err_apinotes_message,
+ diag::warn_apinotes_message,
+ diag::note_apinotes_message,
+ apiNotesFile);
+ llvm::raw_svector_ostream OS(apiNotesBuffer);
+ if (api_notes::compileAPINotes(sourceBuffer->getBuffer(),
+ SourceMgr.getFileEntryForID(sourceFileID),
+ OS,
+ api_notes::OSType::Absent,
+ srcMgrAdapter.getDiagHandler(),
+ srcMgrAdapter.getDiagContext()))
+ return nullptr;
+
+ // Make a copy of the compiled form into the buffer.
+ compiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
+ StringRef(apiNotesBuffer.data(), apiNotesBuffer.size()));
+ }
+
+ // Save the binary form into the cache. Perform this operation
+ // atomically.
+ SmallString<64> temporaryBinaryFileName = compiledFileName.str();
+ temporaryBinaryFileName.erase(
+ temporaryBinaryFileName.end()
+ - llvm::sys::path::extension(temporaryBinaryFileName).size(),
+ temporaryBinaryFileName.end());
+ temporaryBinaryFileName += "-%%%%%%.";
+ temporaryBinaryFileName += BINARY_APINOTES_EXTENSION;
+
+ int temporaryFD;
+ llvm::sys::fs::create_directories(
+ fileMgr.getFileSystemOpts().APINotesCachePath);
+ if (!llvm::sys::fs::createUniqueFile(temporaryBinaryFileName.str(),
+ temporaryFD, temporaryBinaryFileName)) {
+ // Write the contents of the buffer.
+ bool hadError;
+ {
+ llvm::raw_fd_ostream out(temporaryFD, /*shouldClose=*/true);
+ out.write(compiledBuffer.get()->getBufferStart(),
+ compiledBuffer.get()->getBufferSize());
+ out.flush();
+
+ hadError = out.has_error();
+ }
+
+ if (!hadError) {
+ // Rename the temporary file to the actual compiled file.
+ llvm::sys::fs::rename(temporaryBinaryFileName.str(),
+ compiledFileName.str());
+ }
+ }
+
+ // Load the binary form we just compiled.
+ auto reader = APINotesReader::get(std::move(compiledBuffer), SwiftVersion);
+ assert(reader && "Could not load the API notes we just generated?");
+ return reader;
+}
+
+bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir,
+ const FileEntry *APINotesFile) {
+ assert(Readers.find(HeaderDir) == Readers.end());
+ if (auto reader = loadAPINotes(APINotesFile)) {
+ Readers[HeaderDir] = reader.release();
+ return false;
+ }
+
+ Readers[HeaderDir] = nullptr;
+ return true;
+}
+
+const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directory,
+ StringRef basename,
+ bool wantPublic) {
+ FileManager &fileMgr = SourceMgr.getFileManager();
+
+ llvm::SmallString<128> path;
+ path += directory->getName();
+
+ unsigned pathLen = path.size();
+
+ StringRef basenameSuffix = "";
+ if (!wantPublic) basenameSuffix = "_private";
+
+ // Look for a binary API notes file.
+ llvm::sys::path::append(path,
+ llvm::Twine(basename) + basenameSuffix + "." + BINARY_APINOTES_EXTENSION);
+ if (const FileEntry *binaryFile = fileMgr.getFile(path))
+ return binaryFile;
+
+ // Go back to the original path.
+ path.resize(pathLen);
+
+ // Look for the source API notes file.
+ llvm::sys::path::append(path,
+ llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION);
+ return fileMgr.getFile(path);
+}
+
+const DirectoryEntry *APINotesManager::loadFrameworkAPINotes(
+ llvm::StringRef FrameworkPath,
+ llvm::StringRef FrameworkName,
+ bool Public) {
+ FileManager &FileMgr = SourceMgr.getFileManager();
+
+ llvm::SmallString<128> Path;
+ Path += FrameworkPath;
+ unsigned FrameworkNameLength = Path.size();
+
+ // Form the path to the APINotes file.
+ llvm::sys::path::append(Path, "APINotes");
+ if (Public)
+ llvm::sys::path::append(Path,
+ (llvm::Twine(FrameworkName) + "."
+ + SOURCE_APINOTES_EXTENSION));
+ else
+ llvm::sys::path::append(Path,
+ (llvm::Twine(FrameworkName) + "_private."
+ + SOURCE_APINOTES_EXTENSION));
+
+ // Try to open the APINotes file.
+ const FileEntry *APINotesFile = FileMgr.getFile(Path);
+ if (!APINotesFile)
+ return nullptr;
+
+ // Form the path to the corresponding header directory.
+ Path.resize(FrameworkNameLength);
+ if (Public)
+ llvm::sys::path::append(Path, "Headers");
+ else
+ llvm::sys::path::append(Path, "PrivateHeaders");
+
+ // Try to access the header directory.
+ const DirectoryEntry *HeaderDir = FileMgr.getDirectory(Path);
+ if (!HeaderDir)
+ return nullptr;
+
+ // Try to load the API notes.
+ if (loadAPINotes(HeaderDir, APINotesFile))
+ return nullptr;
+
+ // Success: return the header directory.
+ if (Public)
+ ++NumPublicFrameworkAPINotes;
+ else
+ ++NumPrivateFrameworkAPINotes;
+ return HeaderDir;
+}
+
+bool APINotesManager::loadCurrentModuleAPINotes(
+ const Module *module,
+ bool lookInModule,
+ ArrayRef<std::string> searchPaths) {
+ assert(!CurrentModuleReaders[0] &&
+ "Already loaded API notes for the current module?");
+
+ FileManager &fileMgr = SourceMgr.getFileManager();
+ auto moduleName = module->getTopLevelModuleName();
+
+ // First, look relative to the module itself.
+ if (lookInModule) {
+ bool foundAny = false;
+ unsigned numReaders = 0;
+
+ // Local function to try loading an API notes file in the given directory.
+ auto tryAPINotes = [&](const DirectoryEntry *dir, bool wantPublic) {
+ if (auto file = findAPINotesFile(dir, moduleName, wantPublic)) {
+ foundAny = true;
+
+ // Try to load the API notes file.
+ CurrentModuleReaders[numReaders] = loadAPINotes(file).release();
+ if (CurrentModuleReaders[numReaders])
+ ++numReaders;
+ }
+ };
+
+ if (module->IsFramework) {
+ // For frameworks, we search in the "Headers" or "PrivateHeaders"
+ // subdirectory.
+ llvm::SmallString<128> path;
+ path += module->Directory->getName();
+ unsigned pathLen = path.size();
+
+ llvm::sys::path::append(path, "Headers");
+ if (auto apinotesDir = fileMgr.getDirectory(path))
+ tryAPINotes(apinotesDir, /*wantPublic=*/true);
+
+ path.resize(pathLen);
+ llvm::sys::path::append(path, "PrivateHeaders");
+ if (auto privateAPINotesDir = fileMgr.getDirectory(path))
+ tryAPINotes(privateAPINotesDir, /*wantPublic=*/false);
+ } else {
+ tryAPINotes(module->Directory, /*wantPublic=*/true);
+ tryAPINotes(module->Directory, /*wantPublic=*/false);
+ }
+
+ if (foundAny)
+ return numReaders > 0;
+ }
+
+ // Second, look for API notes for this module in the module API
+ // notes search paths.
+ for (const auto &searchPath : searchPaths) {
+ if (auto searchDir = fileMgr.getDirectory(searchPath)) {
+ if (auto file = findAPINotesFile(searchDir, moduleName)) {
+ CurrentModuleReaders[0] = loadAPINotes(file).release();
+ return !getCurrentModuleReaders().empty();
+ }
+ }
+ }
+
+ // Didn't find any API notes.
+ return false;
+}
+
+llvm::SmallVector<APINotesReader *, 2> APINotesManager::findAPINotes(SourceLocation Loc) {
+ llvm::SmallVector<APINotesReader *, 2> Results;
+
+ // If there are readers for the current module, return them.
+ if (!getCurrentModuleReaders().empty()) {
+ Results.append(getCurrentModuleReaders().begin(), getCurrentModuleReaders().end());
+ return Results;
+ }
+
+ // If we're not allowed to implicitly load API notes files, we're done.
+ if (!ImplicitAPINotes) return Results;
+
+ // If we don't have source location information, we're done.
+ if (Loc.isInvalid()) return Results;
+
+ // API notes are associated with the expansion location. Retrieve the
+ // file for this location.
+ SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc);
+ FileID ID = SourceMgr.getFileID(ExpansionLoc);
+ if (ID.isInvalid()) return Results;
+ const FileEntry *File = SourceMgr.getFileEntryForID(ID);
+ if (!File) return Results;
+
+ // Look for API notes in the directory corresponding to this file, or one of
+ // its its parent directories.
+ const DirectoryEntry *Dir = File->getDir();
+ FileManager &FileMgr = SourceMgr.getFileManager();
+ llvm::SetVector<const DirectoryEntry *,
+ SmallVector<const DirectoryEntry *, 4>,
+ llvm::SmallPtrSet<const DirectoryEntry *, 4>> DirsVisited;
+ do {
+ // Look for an API notes reader for this header search directory.
+ auto Known = Readers.find(Dir);
+
+ // If we already know the answer, chase it.
+ if (Known != Readers.end()) {
+ ++NumDirectoryCacheHits;
+
+ // We've been redirected to another directory for answers. Follow it.
+ if (auto OtherDir = Known->second.dyn_cast<const DirectoryEntry *>()) {
+ DirsVisited.insert(Dir);
+ Dir = OtherDir;
+ continue;
+ }
+
+ // We have the answer.
+ if (auto Reader = Known->second.dyn_cast<APINotesReader *>())
+ Results.push_back(Reader);
+ break;
+ }
+
+ // Look for API notes corresponding to this directory.
+ StringRef Path = Dir->getName();
+ if (llvm::sys::path::extension(Path) == ".framework") {
+ // If this is a framework directory, check whether there are API notes
+ // in the APINotes subdirectory.
+ auto FrameworkName = llvm::sys::path::stem(Path);
+ ++NumFrameworksSearched;
+
+ // Look for API notes for both the public and private headers.
+ const DirectoryEntry *PublicDir
+ = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true);
+ const DirectoryEntry *PrivateDir
+ = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false);
+
+ if (PublicDir || PrivateDir) {
+ // We found API notes: don't ever look past the framework directory.
+ Readers[Dir] = nullptr;
+
+ // Pretend we found the result in the public or private directory,
+ // as appropriate. All headers should be in one of those two places,
+ // but be defensive here.
+ if (!DirsVisited.empty()) {
+ if (DirsVisited.back() == PublicDir) {
+ DirsVisited.pop_back();
+ Dir = PublicDir;
+ } else if (DirsVisited.back() == PrivateDir) {
+ DirsVisited.pop_back();
+ Dir = PrivateDir;
+ }
+ }
+
+ // Grab the result.
+ if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
+ Results.push_back(Reader);
+ break;
+ }
+ } else {
+ // Look for an APINotes file in this directory.
+ llvm::SmallString<128> APINotesPath;
+ APINotesPath += Dir->getName();
+ llvm::sys::path::append(APINotesPath,
+ (llvm::Twine("APINotes.")
+ + SOURCE_APINOTES_EXTENSION));
+
+ // If there is an API notes file here, try to load it.
+ ++NumDirectoriesSearched;
+ if (const FileEntry *APINotesFile = FileMgr.getFile(APINotesPath)) {
+ if (!loadAPINotes(Dir, APINotesFile)) {
+ ++NumHeaderAPINotes;
+ if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
+ Results.push_back(Reader);
+ break;
+ }
+ }
+ }
+
+ // We didn't find anything. Look at the parent directory.
+ if (!DirsVisited.insert(Dir)) {
+ Dir = 0;
+ break;
+ }
+
+ StringRef ParentPath = llvm::sys::path::parent_path(Path);
+ while (llvm::sys::path::stem(ParentPath) == "..") {
+ ParentPath = llvm::sys::path::parent_path(ParentPath);
+ }
+ if (ParentPath.empty()) {
+ Dir = nullptr;
+ } else {
+ Dir = FileMgr.getDirectory(ParentPath);
+ }
+ } while (Dir);
+
+ // Path compression for all of the directories we visited, redirecting
+ // them to the directory we ended on. If no API notes were found, the
+ // resulting directory will be NULL, indicating no API notes.
+ for (const auto Visited : DirsVisited) {
+ Readers[Visited] = Dir;
+ }
+
+ return Results;
+}
diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp
new file mode 100644
index 0000000..8fbe2a1
--- /dev/null
+++ b/lib/APINotes/APINotesReader.cpp
@@ -0,0 +1,1864 @@
+//===--- APINotesReader.cpp - Side Car Reader --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the \c APINotesReader class that reads source
+// API notes data providing additional information about source code as
+// a separate input, such as the non-nil/nilable annotations for
+// method parameters.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/APINotesReader.h"
+#include "APINotesFormat.h"
+#include "llvm/Bitcode/BitstreamReader.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/OnDiskHashTable.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace clang;
+using namespace api_notes;
+using namespace llvm::support;
+using namespace llvm;
+
+namespace {
+ /// Deserialize a version tuple.
+ VersionTuple readVersionTuple(const uint8_t *&data) {
+ uint8_t numVersions = (*data++) & 0x03;
+
+ unsigned major = endian::readNext<uint32_t, little, unaligned>(data);
+ if (numVersions == 0)
+ return VersionTuple(major);
+
+ unsigned minor = endian::readNext<uint32_t, little, unaligned>(data);
+ if (numVersions == 1)
+ return VersionTuple(major, minor);
+
+ unsigned subminor = endian::readNext<uint32_t, little, unaligned>(data);
+ if (numVersions == 2)
+ return VersionTuple(major, minor, subminor);
+
+ unsigned build = endian::readNext<uint32_t, little, unaligned>(data);
+ return VersionTuple(major, minor, subminor, build);
+ }
+
+ /// An on-disk hash table whose data is versioned based on the Swift version.
+ template<typename Derived, typename KeyType, typename UnversionedDataType>
+ class VersionedTableInfo {
+ public:
+ using internal_key_type = KeyType;
+ using external_key_type = KeyType;
+ using data_type = SmallVector<std::pair<VersionTuple, UnversionedDataType>, 1>;
+ using hash_value_type = size_t;
+ using offset_type = unsigned;
+
+ internal_key_type GetInternalKey(external_key_type key) {
+ return key;
+ }
+
+ external_key_type GetExternalKey(internal_key_type key) {
+ return key;
+ }
+
+ hash_value_type ComputeHash(internal_key_type key) {
+ return static_cast<size_t>(llvm::hash_value(key));
+ }
+
+ static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
+ return lhs == rhs;
+ }
+
+ static std::pair<unsigned, unsigned>
+ ReadKeyDataLength(const uint8_t *&data) {
+ unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
+ return { keyLength, dataLength };
+ }
+
+ static data_type ReadData(internal_key_type key, const uint8_t *data,
+ unsigned length) {
+ unsigned numElements = endian::readNext<uint16_t, little, unaligned>(data);
+ data_type result;
+ result.reserve(numElements);
+ for (unsigned i = 0; i != numElements; ++i) {
+ auto version = readVersionTuple(data);
+ auto dataBefore = data; (void)dataBefore;
+ auto unversionedData = Derived::readUnversioned(key, data);
+ assert(data != dataBefore
+ && "Unversioned data reader didn't move pointer");
+ result.push_back({version, unversionedData});
+ }
+ return result;
+ }
+ };
+
+
+ /// Read serialized CommonEntityInfo.
+ void readCommonEntityInfo(const uint8_t *&data, CommonEntityInfo &info) {
+ uint8_t unavailableBits = *data++;
+ info.Unavailable = (unavailableBits >> 1) & 0x01;
+ info.UnavailableInSwift = unavailableBits & 0x01;
+ if ((unavailableBits >> 2) & 0x01)
+ info.setSwiftPrivate(static_cast<bool>((unavailableBits >> 3) & 0x01));
+
+ unsigned msgLength = endian::readNext<uint16_t, little, unaligned>(data);
+ info.UnavailableMsg
+ = std::string(reinterpret_cast<const char *>(data),
+ reinterpret_cast<const char *>(data) + msgLength);
+ data += msgLength;
+
+ unsigned swiftNameLength
+ = endian::readNext<uint16_t, little, unaligned>(data);
+ info.SwiftName
+ = std::string(reinterpret_cast<const char *>(data),
+ reinterpret_cast<const char *>(data) + swiftNameLength);
+ data += swiftNameLength;
+ }
+
+ /// Read serialized CommonTypeInfo.
+ void readCommonTypeInfo(const uint8_t *&data, CommonTypeInfo &info) {
+ readCommonEntityInfo(data, info);
+
+ unsigned swiftBridgeLength =
+ endian::readNext<uint16_t, little, unaligned>(data);
+ if (swiftBridgeLength > 0) {
+ info.setSwiftBridge(
+ std::string(reinterpret_cast<const char *>(data), swiftBridgeLength-1));
+ data += swiftBridgeLength-1;
+ }
+
+ unsigned errorDomainLength =
+ endian::readNext<uint16_t, little, unaligned>(data);
+ if (errorDomainLength > 0) {
+ info.setNSErrorDomain(
+ std::string(reinterpret_cast<const char *>(data), errorDomainLength-1));
+ data += errorDomainLength-1;
+ }
+ }
+
+ /// Used to deserialize the on-disk identifier table.
+ class IdentifierTableInfo {
+ public:
+ using internal_key_type = StringRef;
+ using external_key_type = StringRef;
+ using data_type = IdentifierID;
+ using hash_value_type = uint32_t;
+ using offset_type = unsigned;
+
+ internal_key_type GetInternalKey(external_key_type key) {
+ return key;
+ }
+
+ external_key_type GetExternalKey(internal_key_type key) {
+ return key;
+ }
+
+ hash_value_type ComputeHash(internal_key_type key) {
+ return llvm::HashString(key);
+ }
+
+ static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
+ return lhs == rhs;
+ }
+
+ static std::pair<unsigned, unsigned>
+ ReadKeyDataLength(const uint8_t *&data) {
+ unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
+ return { keyLength, dataLength };
+ }
+
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ return StringRef(reinterpret_cast<const char *>(data), length);
+ }
+
+ static data_type ReadData(internal_key_type key, const uint8_t *data,
+ unsigned length) {
+ return endian::readNext<uint32_t, little, unaligned>(data);
+ }
+ };
+
+ /// Used to deserialize the on-disk Objective-C class table.
+ class ObjCContextIDTableInfo {
+ public:
+ // identifier ID, is-protocol
+ using internal_key_type = std::pair<unsigned, char>;
+ using external_key_type = internal_key_type;
+ using data_type = unsigned;
+ using hash_value_type = size_t;
+ using offset_type = unsigned;
+
+ internal_key_type GetInternalKey(external_key_type key) {
+ return key;
+ }
+
+ external_key_type GetExternalKey(internal_key_type key) {
+ return key;
+ }
+
+ hash_value_type ComputeHash(internal_key_type key) {
+ return static_cast<size_t>(llvm::hash_value(key));
+ }
+
+ static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
+ return lhs == rhs;
+ }
+
+ static std::pair<unsigned, unsigned>
+ ReadKeyDataLength(const uint8_t *&data) {
+ unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
+ return { keyLength, dataLength };
+ }
+
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID
+ = endian::readNext<uint32_t, little, unaligned>(data);
+ auto isProtocol = endian::readNext<uint8_t, little, unaligned>(data);
+ return { nameID, isProtocol };
+ }
+
+ static data_type ReadData(internal_key_type key, const uint8_t *data,
+ unsigned length) {
+ return endian::readNext<uint32_t, little, unaligned>(data);
+ }
+ };
+
+ /// Used to deserialize the on-disk Objective-C property table.
+ class ObjCContextInfoTableInfo
+ : public VersionedTableInfo<ObjCContextInfoTableInfo,
+ unsigned,
+ ObjCContextInfo>
+ {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ return endian::readNext<uint32_t, little, unaligned>(data);
+ }
+
+ static ObjCContextInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ ObjCContextInfo info;
+ readCommonTypeInfo(data, info);
+ uint8_t payload = *data++;
+
+ if (payload & 0x01)
+ info.setHasDesignatedInits(true);
+ payload = payload >> 1;
+
+ if (payload & 0x4)
+ info.setDefaultNullability(static_cast<NullabilityKind>(payload&0x03));
+
+ return info;
+ }
+ };
+
+ /// Read serialized VariableInfo.
+ void readVariableInfo(const uint8_t *&data, VariableInfo &info) {
+ readCommonEntityInfo(data, info);
+ if (*data++) {
+ info.setNullabilityAudited(static_cast<NullabilityKind>(*data));
+ }
+ ++data;
+
+ auto typeLen
+ = endian::readNext<uint16_t, little, unaligned>(data);
+ info.setType(std::string(data, data + typeLen));
+ data += typeLen;
+ }
+
+ /// Used to deserialize the on-disk Objective-C property table.
+ class ObjCPropertyTableInfo
+ : public VersionedTableInfo<ObjCPropertyTableInfo,
+ std::tuple<unsigned, unsigned, char>,
+ ObjCPropertyInfo>
+ {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto classID = endian::readNext<uint32_t, little, unaligned>(data);
+ auto nameID = endian::readNext<uint32_t, little, unaligned>(data);
+ char isInstance = endian::readNext<uint8_t, little, unaligned>(data);
+ return std::make_tuple(classID, nameID, isInstance);
+ }
+
+ static ObjCPropertyInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ ObjCPropertyInfo info;
+ readVariableInfo(data, info);
+ uint8_t flags = *data++;
+ if (flags & (1 << 0))
+ info.setSwiftImportAsAccessors(flags & (1 << 1));
+ return info;
+ }
+ };
+
+ /// Read serialized ParamInfo.
+ void readParamInfo(const uint8_t *&data, ParamInfo &info) {
+ readVariableInfo(data, info);
+
+ uint8_t payload = endian::readNext<uint8_t, little, unaligned>(data);
+ if (payload & 0x01) {
+ info.setNoEscape(payload & 0x02);
+ }
+ payload >>= 2; assert(payload == 0 && "Bad API notes");
+ }
+
+ /// Read serialized FunctionInfo.
+ void readFunctionInfo(const uint8_t *&data, FunctionInfo &info) {
+ readCommonEntityInfo(data, info);
+ info.NullabilityAudited
+ = endian::readNext<uint8_t, little, unaligned>(data);
+ info.NumAdjustedNullable
+ = endian::readNext<uint8_t, little, unaligned>(data);
+ info.NullabilityPayload
+ = endian::readNext<uint64_t, little, unaligned>(data);
+
+ unsigned numParams = endian::readNext<uint16_t, little, unaligned>(data);
+ while (numParams > 0) {
+ ParamInfo pi;
+ readParamInfo(data, pi);
+ info.Params.push_back(pi);
+ --numParams;
+ }
+
+ unsigned resultTypeLen
+ = endian::readNext<uint16_t, little, unaligned>(data);
+ info.ResultType = std::string(data, data + resultTypeLen);
+ data += resultTypeLen;
+ }
+
+ /// Used to deserialize the on-disk Objective-C method table.
+ class ObjCMethodTableInfo
+ : public VersionedTableInfo<ObjCMethodTableInfo,
+ std::tuple<unsigned, unsigned, char>,
+ ObjCMethodInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto classID = endian::readNext<uint32_t, little, unaligned>(data);
+ auto selectorID = endian::readNext<uint32_t, little, unaligned>(data);
+ auto isInstance = endian::readNext<uint8_t, little, unaligned>(data);
+ return internal_key_type{ classID, selectorID, isInstance };
+ }
+
+ static ObjCMethodInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ ObjCMethodInfo info;
+ uint8_t payload = *data++;
+ info.Required = payload & 0x01;
+ payload >>= 1;
+ info.DesignatedInit = payload & 0x01;
+ payload >>= 1;
+ info.FactoryAsInit = payload & 0x03;
+ payload >>= 2;
+
+ readFunctionInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk Objective-C selector table.
+ class ObjCSelectorTableInfo {
+ public:
+ using internal_key_type = StoredObjCSelector;
+ using external_key_type = internal_key_type;
+ using data_type = SelectorID;
+ using hash_value_type = unsigned;
+ using offset_type = unsigned;
+
+ internal_key_type GetInternalKey(external_key_type key) {
+ return key;
+ }
+
+ external_key_type GetExternalKey(internal_key_type key) {
+ return key;
+ }
+
+ hash_value_type ComputeHash(internal_key_type key) {
+ return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(key);
+ }
+
+ static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
+ return llvm::DenseMapInfo<StoredObjCSelector>::isEqual(lhs, rhs);
+ }
+
+ static std::pair<unsigned, unsigned>
+ ReadKeyDataLength(const uint8_t *&data) {
+ unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
+ return { keyLength, dataLength };
+ }
+
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ internal_key_type key;
+ key.NumPieces = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned numIdents = (length - sizeof(uint16_t)) / sizeof(uint32_t);
+ for (unsigned i = 0; i != numIdents; ++i) {
+ key.Identifiers.push_back(
+ endian::readNext<uint32_t, little, unaligned>(data));
+ }
+ return key;
+ }
+
+ static data_type ReadData(internal_key_type key, const uint8_t *data,
+ unsigned length) {
+ return endian::readNext<uint32_t, little, unaligned>(data);
+ }
+ };
+
+ /// Used to deserialize the on-disk global variable table.
+ class GlobalVariableTableInfo
+ : public VersionedTableInfo<GlobalVariableTableInfo, unsigned,
+ GlobalVariableInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<uint32_t, little, unaligned>(data);
+ return nameID;
+ }
+
+ static GlobalVariableInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ GlobalVariableInfo info;
+ readVariableInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk global function table.
+ class GlobalFunctionTableInfo
+ : public VersionedTableInfo<GlobalFunctionTableInfo, unsigned,
+ GlobalFunctionInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<uint32_t, little, unaligned>(data);
+ return nameID;
+ }
+
+ static GlobalFunctionInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ GlobalFunctionInfo info;
+ readFunctionInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk enumerator table.
+ class EnumConstantTableInfo
+ : public VersionedTableInfo<EnumConstantTableInfo, unsigned,
+ EnumConstantInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<uint32_t, little, unaligned>(data);
+ return nameID;
+ }
+
+ static EnumConstantInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ EnumConstantInfo info;
+ readCommonEntityInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk tag table.
+ class TagTableInfo
+ : public VersionedTableInfo<TagTableInfo, unsigned, TagInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<IdentifierID, little, unaligned>(data);
+ return nameID;
+ }
+
+ static TagInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ TagInfo info;
+ readCommonTypeInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk typedef table.
+ class TypedefTableInfo
+ : public VersionedTableInfo<TypedefTableInfo, unsigned, TypedefInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<IdentifierID, little, unaligned>(data);
+ return nameID;
+ }
+
+ static TypedefInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ TypedefInfo info;
+
+ uint8_t payload = *data++;
+ if (payload > 0) {
+ info.SwiftWrapper = static_cast<SwiftWrapperKind>((payload & 0x3) - 1);
+ }
+
+ readCommonTypeInfo(data, info);
+ return info;
+ }
+ };
+} // end anonymous namespace
+
+class APINotesReader::Implementation {
+public:
+ /// The input buffer for the API notes data.
+ llvm::MemoryBuffer *InputBuffer;
+
+ /// Whether we own the input buffer.
+ bool OwnsInputBuffer;
+
+ /// The Swift version to use for filtering.
+ VersionTuple SwiftVersion;
+
+ /// The name of the module that we read from the control block.
+ std::string ModuleName;
+
+ // The size and modification time of the source file from
+ // which this API notes file was created, if known.
+ Optional<std::pair<off_t, time_t>> SourceFileSizeAndModTime;
+
+ /// Various options and attributes for the module
+ ModuleOptions ModuleOpts;
+
+ using SerializedIdentifierTable =
+ llvm::OnDiskIterableChainedHashTable<IdentifierTableInfo>;
+
+ /// The identifier table.
+ std::unique_ptr<SerializedIdentifierTable> IdentifierTable;
+
+ using SerializedObjCContextIDTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCContextIDTableInfo>;
+
+ /// The Objective-C context ID table.
+ std::unique_ptr<SerializedObjCContextIDTable> ObjCContextIDTable;
+
+ using SerializedObjCContextInfoTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCContextInfoTableInfo>;
+
+ /// The Objective-C context info table.
+ std::unique_ptr<SerializedObjCContextInfoTable> ObjCContextInfoTable;
+
+ using SerializedObjCPropertyTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCPropertyTableInfo>;
+
+ /// The Objective-C property table.
+ std::unique_ptr<SerializedObjCPropertyTable> ObjCPropertyTable;
+
+ using SerializedObjCMethodTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCMethodTableInfo>;
+
+ /// The Objective-C method table.
+ std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable;
+
+ using SerializedObjCSelectorTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>;
+
+ /// The Objective-C selector table.
+ std::unique_ptr<SerializedObjCSelectorTable> ObjCSelectorTable;
+
+ using SerializedGlobalVariableTable =
+ llvm::OnDiskIterableChainedHashTable<GlobalVariableTableInfo>;
+
+ /// The global variable table.
+ std::unique_ptr<SerializedGlobalVariableTable> GlobalVariableTable;
+
+ using SerializedGlobalFunctionTable =
+ llvm::OnDiskIterableChainedHashTable<GlobalFunctionTableInfo>;
+
+ /// The global function table.
+ std::unique_ptr<SerializedGlobalFunctionTable> GlobalFunctionTable;
+
+ using SerializedEnumConstantTable =
+ llvm::OnDiskIterableChainedHashTable<EnumConstantTableInfo>;
+
+ /// The enumerator table.
+ std::unique_ptr<SerializedEnumConstantTable> EnumConstantTable;
+
+ using SerializedTagTable =
+ llvm::OnDiskIterableChainedHashTable<TagTableInfo>;
+
+ /// The tag table.
+ std::unique_ptr<SerializedTagTable> TagTable;
+
+ using SerializedTypedefTable =
+ llvm::OnDiskIterableChainedHashTable<TypedefTableInfo>;
+
+ /// The typedef table.
+ std::unique_ptr<SerializedTypedefTable> TypedefTable;
+
+ /// Retrieve the identifier ID for the given string, or an empty
+ /// optional if the string is unknown.
+ Optional<IdentifierID> getIdentifier(StringRef str);
+
+ /// Retrieve the selector ID for the given selector, or an empty
+ /// optional if the string is unknown.
+ Optional<SelectorID> getSelector(ObjCSelectorRef selector);
+
+ bool readControlBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readIdentifierBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readObjCContextBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readObjCPropertyBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readObjCMethodBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readObjCSelectorBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readGlobalVariableBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readEnumConstantBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readTagBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readTypedefBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+};
+
+Optional<IdentifierID> APINotesReader::Implementation::getIdentifier(
+ StringRef str) {
+ if (!IdentifierTable)
+ return None;
+
+ if (str.empty())
+ return IdentifierID(0);
+
+ auto known = IdentifierTable->find(str);
+ if (known == IdentifierTable->end())
+ return None;
+
+ return *known;
+}
+
+Optional<SelectorID> APINotesReader::Implementation::getSelector(
+ ObjCSelectorRef selector) {
+ if (!ObjCSelectorTable || !IdentifierTable)
+ return None;
+
+ // Translate the identifiers.
+ StoredObjCSelector key;
+ key.NumPieces = selector.NumPieces;
+ for (auto ident : selector.Identifiers) {
+ if (auto identID = getIdentifier(ident)) {
+ key.Identifiers.push_back(*identID);
+ } else {
+ return None;
+ }
+ }
+
+ auto known = ObjCSelectorTable->find(key);
+ if (known == ObjCSelectorTable->end())
+ return None;
+
+ return *known;
+
+}
+
+bool APINotesReader::Implementation::readControlBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(CONTROL_BLOCK_ID))
+ return true;
+
+ bool sawMetadata = false;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown metadata sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case control_block::METADATA:
+ // Already saw metadata.
+ if (sawMetadata)
+ return true;
+
+ if (scratch[0] != VERSION_MAJOR || scratch[1] != VERSION_MINOR)
+ return true;
+
+ sawMetadata = true;
+ break;
+
+ case control_block::MODULE_NAME:
+ ModuleName = blobData.str();
+ break;
+
+ case control_block::MODULE_OPTIONS:
+ ModuleOpts.SwiftInferImportAsMember = (scratch.front() & 1) != 0;
+ break;
+
+ case control_block::SOURCE_FILE:
+ SourceFileSizeAndModTime = { scratch[0], scratch[1] };
+ break;
+
+ default:
+ // Unknown metadata record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return !sawMetadata;
+}
+
+bool APINotesReader::Implementation::readIdentifierBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case identifier_block::IDENTIFIER_DATA: {
+ // Already saw identifier table.
+ if (IdentifierTable)
+ return true;
+
+ uint32_t tableOffset;
+ identifier_block::IdentifierDataLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ IdentifierTable.reset(
+ SerializedIdentifierTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readObjCContextBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case objc_context_block::OBJC_CONTEXT_ID_DATA: {
+ // Already saw Objective-C context ID table.
+ if (ObjCContextIDTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_context_block::ObjCContextIDLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCContextIDTable.reset(
+ SerializedObjCContextIDTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ case objc_context_block::OBJC_CONTEXT_INFO_DATA: {
+ // Already saw Objective-C context info table.
+ if (ObjCContextInfoTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_context_block::ObjCContextInfoLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCContextInfoTable.reset(
+ SerializedObjCContextInfoTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readObjCPropertyBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case objc_property_block::OBJC_PROPERTY_DATA: {
+ // Already saw Objective-C property table.
+ if (ObjCPropertyTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_property_block::ObjCPropertyDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCPropertyTable.reset(
+ SerializedObjCPropertyTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readObjCMethodBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case objc_method_block::OBJC_METHOD_DATA: {
+ // Already saw Objective-C method table.
+ if (ObjCMethodTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_method_block::ObjCMethodDataLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCMethodTable.reset(
+ SerializedObjCMethodTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readObjCSelectorBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case objc_selector_block::OBJC_SELECTOR_DATA: {
+ // Already saw Objective-C selector table.
+ if (ObjCSelectorTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_selector_block::ObjCSelectorDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCSelectorTable.reset(
+ SerializedObjCSelectorTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readGlobalVariableBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case global_variable_block::GLOBAL_VARIABLE_DATA: {
+ // Already saw global variable table.
+ if (GlobalVariableTable)
+ return true;
+
+ uint32_t tableOffset;
+ global_variable_block::GlobalVariableDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ GlobalVariableTable.reset(
+ SerializedGlobalVariableTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readGlobalFunctionBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case global_function_block::GLOBAL_FUNCTION_DATA: {
+ // Already saw global function table.
+ if (GlobalFunctionTable)
+ return true;
+
+ uint32_t tableOffset;
+ global_function_block::GlobalFunctionDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ GlobalFunctionTable.reset(
+ SerializedGlobalFunctionTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readEnumConstantBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case enum_constant_block::ENUM_CONSTANT_DATA: {
+ // Already saw enumerator table.
+ if (EnumConstantTable)
+ return true;
+
+ uint32_t tableOffset;
+ enum_constant_block::EnumConstantDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ EnumConstantTable.reset(
+ SerializedEnumConstantTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readTagBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(TAG_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case tag_block::TAG_DATA: {
+ // Already saw tag table.
+ if (TagTable)
+ return true;
+
+ uint32_t tableOffset;
+ tag_block::TagDataLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ TagTable.reset(
+ SerializedTagTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readTypedefBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(TYPEDEF_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case typedef_block::TYPEDEF_DATA: {
+ // Already saw typedef table.
+ if (TypedefTable)
+ return true;
+
+ uint32_t tableOffset;
+ typedef_block::TypedefDataLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ TypedefTable.reset(
+ SerializedTypedefTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer,
+ bool ownsInputBuffer,
+ VersionTuple swiftVersion,
+ bool &failed)
+ : Impl(*new Implementation)
+{
+ failed = false;
+
+ // Initialize the input buffer.
+ Impl.InputBuffer = inputBuffer;
+ Impl.OwnsInputBuffer = ownsInputBuffer;
+ Impl.SwiftVersion = swiftVersion;
+ llvm::BitstreamCursor cursor(*Impl.InputBuffer);
+
+ // Validate signature.
+ for (auto byte : API_NOTES_SIGNATURE) {
+ if (cursor.AtEndOfStream() || cursor.Read(8) != byte) {
+ failed = true;
+ return;
+ }
+ }
+
+ // Look at all of the blocks.
+ bool hasValidControlBlock = false;
+ SmallVector<uint64_t, 64> scratch;
+ while (!cursor.AtEndOfStream()) {
+ auto topLevelEntry = cursor.advance();
+ if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock)
+ break;
+
+ switch (topLevelEntry.ID) {
+ case llvm::bitc::BLOCKINFO_BLOCK_ID:
+ if (!cursor.ReadBlockInfoBlock()) {
+ failed = true;
+ break;
+ }
+ break;
+
+ case CONTROL_BLOCK_ID:
+ // Only allow a single control block.
+ if (hasValidControlBlock || Impl.readControlBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+
+ hasValidControlBlock = true;
+ break;
+
+ case IDENTIFIER_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readIdentifierBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case OBJC_CONTEXT_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readObjCContextBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+
+ break;
+
+ case OBJC_PROPERTY_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readObjCPropertyBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case OBJC_METHOD_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readObjCMethodBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case OBJC_SELECTOR_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readObjCSelectorBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case GLOBAL_VARIABLE_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readGlobalVariableBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case GLOBAL_FUNCTION_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readGlobalFunctionBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case ENUM_CONSTANT_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readEnumConstantBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case TAG_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readTagBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case TYPEDEF_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readTypedefBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ default:
+ // Unknown top-level block, possibly for use by a future version of the
+ // module format.
+ if (cursor.SkipBlock()) {
+ failed = true;
+ return;
+ }
+ break;
+ }
+ }
+
+ if (!cursor.AtEndOfStream()) {
+ failed = true;
+ return;
+ }
+}
+
+APINotesReader::~APINotesReader() {
+ if (Impl.OwnsInputBuffer)
+ delete Impl.InputBuffer;
+
+ delete &Impl;
+}
+
+std::unique_ptr<APINotesReader>
+APINotesReader::get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer,
+ VersionTuple swiftVersion) {
+ bool failed = false;
+ std::unique_ptr<APINotesReader>
+ reader(new APINotesReader(inputBuffer.release(), /*ownsInputBuffer=*/true,
+ swiftVersion, failed));
+ if (failed)
+ return nullptr;
+
+ return reader;
+}
+
+std::unique_ptr<APINotesReader>
+APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer,
+ VersionTuple swiftVersion) {
+ bool failed = false;
+ std::unique_ptr<APINotesReader>
+ reader(new APINotesReader(inputBuffer, /*ownsInputBuffer=*/false,
+ swiftVersion, failed));
+ if (failed)
+ return nullptr;
+
+ return reader;
+}
+
+StringRef APINotesReader::getModuleName() const {
+ return Impl.ModuleName;
+}
+
+Optional<std::pair<off_t, time_t>>
+APINotesReader::getSourceFileSizeAndModTime() const {
+ return Impl.SourceFileSizeAndModTime;
+}
+
+ModuleOptions APINotesReader::getModuleOptions() const {
+ return Impl.ModuleOpts;
+}
+
+template<typename T>
+APINotesReader::VersionedInfo<T>::VersionedInfo(
+ VersionTuple version,
+ SmallVector<std::pair<VersionTuple, T>, 1> results)
+ : Results(std::move(results)) {
+
+ // Look for an exact version match.
+ Optional<unsigned> unversioned;
+ Selected = Results.size();
+ SelectedRole = VersionedInfoRole::Versioned;
+
+ for (unsigned i = 0, n = Results.size(); i != n; ++i) {
+ if (Results[i].first == version) {
+ Selected = i;
+
+ if (version) SelectedRole = VersionedInfoRole::ReplaceSource;
+ else SelectedRole = VersionedInfoRole::AugmentSource;
+ break;
+ }
+
+ if (!Results[i].first) {
+ assert(!unversioned && "Two unversioned entries?");
+ unversioned = i;
+ }
+ }
+
+ // If we didn't find a match but we have an unversioned result, use the
+ // unversioned result.
+ if (Selected == Results.size() && unversioned) {
+ Selected = *unversioned;
+ SelectedRole = VersionedInfoRole::AugmentSource;
+ }
+ }
+
+auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional<ContextID> {
+ if (!Impl.ObjCContextIDTable)
+ return None;
+
+ Optional<IdentifierID> classID = Impl.getIdentifier(name);
+ if (!classID)
+ return None;
+
+ auto knownID = Impl.ObjCContextIDTable->find({*classID, '\0'});
+ if (knownID == Impl.ObjCContextIDTable->end())
+ return None;
+
+ return ContextID(*knownID);
+}
+
+auto APINotesReader::lookupObjCClassInfo(StringRef name)
+ -> VersionedInfo<ObjCContextInfo> {
+ if (!Impl.ObjCContextInfoTable)
+ return None;
+
+ Optional<ContextID> contextID = lookupObjCClassID(name);
+ if (!contextID)
+ return None;
+
+ auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value);
+ if (knownInfo == Impl.ObjCContextInfoTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *knownInfo };
+}
+
+auto APINotesReader::lookupObjCProtocolID(StringRef name)
+ -> Optional<ContextID> {
+ if (!Impl.ObjCContextIDTable)
+ return None;
+
+ Optional<IdentifierID> classID = Impl.getIdentifier(name);
+ if (!classID)
+ return None;
+
+ auto knownID = Impl.ObjCContextIDTable->find({*classID, '\1'});
+ if (knownID == Impl.ObjCContextIDTable->end())
+ return None;
+
+ return ContextID(*knownID);
+}
+
+auto APINotesReader::lookupObjCProtocolInfo(StringRef name)
+ -> VersionedInfo<ObjCContextInfo> {
+ if (!Impl.ObjCContextInfoTable)
+ return None;
+
+ Optional<ContextID> contextID = lookupObjCProtocolID(name);
+ if (!contextID)
+ return None;
+
+ auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value);
+ if (knownInfo == Impl.ObjCContextInfoTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *knownInfo };
+}
+
+
+auto APINotesReader::lookupObjCProperty(ContextID contextID,
+ StringRef name,
+ bool isInstance)
+ -> VersionedInfo<ObjCPropertyInfo> {
+ if (!Impl.ObjCPropertyTable)
+ return None;
+
+ Optional<IdentifierID> propertyID = Impl.getIdentifier(name);
+ if (!propertyID)
+ return None;
+
+ auto known = Impl.ObjCPropertyTable->find(std::make_tuple(contextID.Value,
+ *propertyID,
+ (char)isInstance));
+ if (known == Impl.ObjCPropertyTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupObjCMethod(
+ ContextID contextID,
+ ObjCSelectorRef selector,
+ bool isInstanceMethod)
+ -> VersionedInfo<ObjCMethodInfo> {
+ if (!Impl.ObjCMethodTable)
+ return None;
+
+ Optional<SelectorID> selectorID = Impl.getSelector(selector);
+ if (!selectorID)
+ return None;
+
+ auto known = Impl.ObjCMethodTable->find(
+ ObjCMethodTableInfo::internal_key_type{
+ contextID.Value, *selectorID, isInstanceMethod});
+ if (known == Impl.ObjCMethodTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupGlobalVariable(
+ StringRef name)
+ -> VersionedInfo<GlobalVariableInfo> {
+ if (!Impl.GlobalVariableTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.GlobalVariableTable->find(*nameID);
+ if (known == Impl.GlobalVariableTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupGlobalFunction(StringRef name)
+ -> VersionedInfo<GlobalFunctionInfo> {
+ if (!Impl.GlobalFunctionTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.GlobalFunctionTable->find(*nameID);
+ if (known == Impl.GlobalFunctionTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupEnumConstant(StringRef name)
+ -> VersionedInfo<EnumConstantInfo> {
+ if (!Impl.EnumConstantTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.EnumConstantTable->find(*nameID);
+ if (known == Impl.EnumConstantTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupTag(StringRef name) -> VersionedInfo<TagInfo> {
+ if (!Impl.TagTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.TagTable->find(*nameID);
+ if (known == Impl.TagTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupTypedef(StringRef name)
+ -> VersionedInfo<TypedefInfo> {
+ if (!Impl.TypedefTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.TypedefTable->find(*nameID);
+ if (known == Impl.TypedefTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+APINotesReader::Visitor::~Visitor() { }
+
+void APINotesReader::Visitor::visitObjCClass(
+ ContextID contextID,
+ StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitObjCProtocol(
+ ContextID contextID,
+ StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitObjCMethod(
+ ContextID contextID,
+ StringRef selector,
+ bool isInstanceMethod,
+ const ObjCMethodInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitObjCProperty(
+ ContextID contextID,
+ StringRef name,
+ bool isInstance,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitGlobalVariable(
+ StringRef name,
+ const GlobalVariableInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitGlobalFunction(
+ StringRef name,
+ const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitEnumConstant(
+ StringRef name,
+ const EnumConstantInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitTag(
+ StringRef name,
+ const TagInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitTypedef(
+ StringRef name,
+ const TypedefInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::visit(Visitor &visitor) {
+ // FIXME: All of these iterations would be significantly more efficient if we
+ // could get the keys and data together, but OnDiskIterableHashTable doesn't
+ // support that.
+
+ // Build an identifier ID -> string mapping, which we'll need when visiting
+ // any of the tables.
+ llvm::DenseMap<unsigned, StringRef> identifiers;
+ if (Impl.IdentifierTable) {
+ for (auto key : Impl.IdentifierTable->keys()) {
+ unsigned ID = *Impl.IdentifierTable->find(key);
+ assert(identifiers.count(ID) == 0);
+ identifiers[ID] = key;
+ }
+ }
+
+ // Visit classes and protocols.
+ if (Impl.ObjCContextIDTable && Impl.ObjCContextInfoTable) {
+ for (auto key : Impl.ObjCContextIDTable->keys()) {
+ auto name = identifiers[key.first];
+ auto contextID = *Impl.ObjCContextIDTable->find(key);
+
+ auto knownInfo = Impl.ObjCContextInfoTable->find(contextID);
+ if (knownInfo == Impl.ObjCContextInfoTable->end()) continue;
+
+ for (const auto &versioned : *knownInfo) {
+ if (key.second)
+ visitor.visitObjCProtocol(ContextID(contextID), name,
+ versioned.second, versioned.first);
+ else
+ visitor.visitObjCClass(ContextID(contextID), name, versioned.second,
+ versioned.first);
+ }
+ }
+ }
+
+ // Build a selector ID -> stored Objective-C selector mapping, which we need
+ // when visiting the method tables.
+ llvm::DenseMap<unsigned, std::string> selectors;
+ if (Impl.ObjCSelectorTable) {
+ for (auto key : Impl.ObjCSelectorTable->keys()) {
+ std::string selector;
+ if (key.NumPieces == 0)
+ selector = identifiers[key.Identifiers[0]];
+ else {
+ for (auto identID : key.Identifiers) {
+ selector += identifiers[identID];
+ selector += ':';
+ }
+ }
+
+ unsigned selectorID = *Impl.ObjCSelectorTable->find(key);
+ selectors[selectorID] = selector;
+ }
+ }
+
+ // Visit methods.
+ if (Impl.ObjCMethodTable) {
+ for (auto key : Impl.ObjCMethodTable->keys()) {
+ ContextID contextID(std::get<0>(key));
+ const auto &selector = selectors[std::get<1>(key)];
+ for (const auto &versioned : *Impl.ObjCMethodTable->find(key))
+ visitor.visitObjCMethod(contextID, selector, std::get<2>(key),
+ versioned.second, versioned.first);
+ }
+ }
+
+ // Visit properties.
+ if (Impl.ObjCPropertyTable) {
+ for (auto key : Impl.ObjCPropertyTable->keys()) {
+ ContextID contextID(std::get<0>(key));
+ auto name = identifiers[std::get<1>(key)];
+ char isInstance = std::get<2>(key);
+ for (const auto &versioned : *Impl.ObjCPropertyTable->find(key)) {
+ visitor.visitObjCProperty(contextID, name, isInstance, versioned.second,
+ versioned.first);
+ }
+ }
+ }
+
+ // Visit global functions.
+ if (Impl.GlobalFunctionTable) {
+ for (auto key : Impl.GlobalFunctionTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.GlobalFunctionTable->find(key))
+ visitor.visitGlobalFunction(name, versioned.second, versioned.first);
+ }
+ }
+
+ // Visit global variables.
+ if (Impl.GlobalVariableTable) {
+ for (auto key : Impl.GlobalVariableTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.GlobalVariableTable->find(key))
+ visitor.visitGlobalVariable(name, versioned.second, versioned.first);
+ }
+ }
+
+ // Visit global variables.
+ if (Impl.EnumConstantTable) {
+ for (auto key : Impl.EnumConstantTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.EnumConstantTable->find(key))
+ visitor.visitEnumConstant(name, versioned.second, versioned.first);
+ }
+ }
+
+ // Visit tags.
+ if (Impl.TagTable) {
+ for (auto key : Impl.TagTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.TagTable->find(key))
+ visitor.visitTag(name, versioned.second, versioned.first);
+ }
+ }
+
+ // Visit typedefs.
+ if (Impl.TypedefTable) {
+ for (auto key : Impl.TypedefTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.TypedefTable->find(key))
+ visitor.visitTypedef(name, versioned.second, versioned.first);
+ }
+ }
+}
+
diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp
new file mode 100644
index 0000000..86b6abd
--- /dev/null
+++ b/lib/APINotes/APINotesWriter.cpp
@@ -0,0 +1,1289 @@
+//===--- APINotesWriter.cpp - API Notes Writer --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the \c APINotesWriter class that writes out
+// source API notes data providing additional information about source
+// code as a separate input, such as the non-nil/nilable annotations
+// for method parameters.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/APINotesWriter.h"
+#include "APINotesFormat.h"
+#include "clang/Basic/FileManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/OnDiskHashTable.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/DataTypes.h"
+#include <tuple>
+#include <vector>
+using namespace clang;
+using namespace api_notes;
+using namespace llvm::support;
+
+namespace {
+ template<typename T> using VersionedSmallVector =
+ SmallVector<std::pair<VersionTuple, T>, 1>;
+}
+
+class APINotesWriter::Implementation {
+ /// Mapping from strings to identifier IDs.
+ llvm::StringMap<IdentifierID> IdentifierIDs;
+
+ /// Mapping from selectors to selector ID.
+ llvm::DenseMap<StoredObjCSelector, SelectorID> SelectorIDs;
+
+ /// Scratch space for bitstream writing.
+ SmallVector<uint64_t, 64> ScratchRecord;
+
+public:
+ /// The name of the module
+ std::string ModuleName;
+
+ /// The source file from which this binary representation was
+ /// created, if known.
+ const FileEntry *SourceFile;
+
+ bool SwiftInferImportAsMember = false;
+
+ /// Information about Objective-C contexts (classes or protocols).
+ ///
+ /// Indexed by the identifier ID and a bit indication whether we're looking
+ /// for a class (0) or protocol (1) and provides both the context ID and
+ /// information describing the context within that module.
+ llvm::DenseMap<std::pair<unsigned, char>,
+ std::pair<unsigned, VersionedSmallVector<ObjCContextInfo>>>
+ ObjCContexts;
+
+ /// Mapping from context IDs to the identifier ID holding the name.
+ llvm::DenseMap<unsigned, unsigned> ObjCContextNames;
+
+ /// Information about Objective-C properties.
+ ///
+ /// Indexed by the context ID, property name, and whether this is an
+ /// instance property.
+ llvm::DenseMap<std::tuple<unsigned, unsigned, char>,
+ llvm::SmallVector<std::pair<VersionTuple, ObjCPropertyInfo>,
+ 1>>
+ ObjCProperties;
+
+ /// Information about Objective-C methods.
+ ///
+ /// Indexed by the context ID, selector ID, and Boolean (stored as a
+ /// char) indicating whether this is a class or instance method.
+ llvm::DenseMap<std::tuple<unsigned, unsigned, char>,
+ llvm::SmallVector<std::pair<VersionTuple, ObjCMethodInfo>, 1>>
+ ObjCMethods;
+
+ /// Information about global variables.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, GlobalVariableInfo>,
+ 1>>
+ GlobalVariables;
+
+ /// Information about global functions.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, GlobalFunctionInfo>,
+ 1>>
+ GlobalFunctions;
+
+ /// Information about enumerators.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, EnumConstantInfo>,
+ 1>>
+ EnumConstants;
+
+ /// Information about tags.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>>
+ Tags;
+
+ /// Information about typedefs.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>>
+ Typedefs;
+
+ /// Retrieve the ID for the given identifier.
+ IdentifierID getIdentifier(StringRef identifier) {
+ if (identifier.empty())
+ return 0;
+
+ auto known = IdentifierIDs.find(identifier);
+ if (known != IdentifierIDs.end())
+ return known->second;
+
+ // Add to the identifier table.
+ known = IdentifierIDs.insert({identifier, IdentifierIDs.size() + 1}).first;
+ return known->second;
+ }
+
+ /// Retrieve the ID for the given selector.
+ SelectorID getSelector(ObjCSelectorRef selectorRef) {
+ // Translate the selector reference into a stored selector.
+ StoredObjCSelector selector;
+ selector.NumPieces = selectorRef.NumPieces;
+ selector.Identifiers.reserve(selectorRef.Identifiers.size());
+ for (auto piece : selectorRef.Identifiers) {
+ selector.Identifiers.push_back(getIdentifier(piece));
+ }
+
+ // Look for the stored selector.
+ auto known = SelectorIDs.find(selector);
+ if (known != SelectorIDs.end())
+ return known->second;
+
+ // Add to the selector table.
+ known = SelectorIDs.insert({selector, SelectorIDs.size()}).first;
+ return known->second;
+ }
+
+ void writeToStream(llvm::raw_ostream &os);
+
+private:
+ void writeBlockInfoBlock(llvm::BitstreamWriter &writer);
+ void writeControlBlock(llvm::BitstreamWriter &writer);
+ void writeIdentifierBlock(llvm::BitstreamWriter &writer);
+ void writeObjCContextBlock(llvm::BitstreamWriter &writer);
+ void writeObjCPropertyBlock(llvm::BitstreamWriter &writer);
+ void writeObjCMethodBlock(llvm::BitstreamWriter &writer);
+ void writeObjCSelectorBlock(llvm::BitstreamWriter &writer);
+ void writeGlobalVariableBlock(llvm::BitstreamWriter &writer);
+ void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer);
+ void writeEnumConstantBlock(llvm::BitstreamWriter &writer);
+ void writeTagBlock(llvm::BitstreamWriter &writer);
+ void writeTypedefBlock(llvm::BitstreamWriter &writer);
+};
+
+/// Record the name of a block.
+static void emitBlockID(llvm::BitstreamWriter &out, unsigned ID,
+ StringRef name,
+ SmallVectorImpl<unsigned char> &nameBuffer) {
+ SmallVector<unsigned, 1> idBuffer;
+ idBuffer.push_back(ID);
+ out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, idBuffer);
+
+ // Emit the block name if present.
+ if (name.empty())
+ return;
+ nameBuffer.resize(name.size());
+ memcpy(nameBuffer.data(), name.data(), name.size());
+ out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, nameBuffer);
+}
+
+/// Record the name of a record within a block.
+static void emitRecordID(llvm::BitstreamWriter &out, unsigned ID,
+ StringRef name,
+ SmallVectorImpl<unsigned char> &nameBuffer) {
+ assert(ID < 256 && "can't fit record ID in next to name");
+ nameBuffer.resize(name.size()+1);
+ nameBuffer[0] = ID;
+ memcpy(nameBuffer.data()+1, name.data(), name.size());
+ out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, nameBuffer);
+}
+
+void APINotesWriter::Implementation::writeBlockInfoBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, llvm::bitc::BLOCKINFO_BLOCK_ID, 2);
+
+ SmallVector<unsigned char, 64> nameBuffer;
+#define BLOCK(X) emitBlockID(writer, X ## _ID, #X, nameBuffer)
+#define BLOCK_RECORD(K, X) emitRecordID(writer, K::X, #X, nameBuffer)
+
+ BLOCK(CONTROL_BLOCK);
+ BLOCK_RECORD(control_block, METADATA);
+ BLOCK_RECORD(control_block, MODULE_NAME);
+
+ BLOCK(IDENTIFIER_BLOCK);
+ BLOCK_RECORD(identifier_block, IDENTIFIER_DATA);
+
+ BLOCK(OBJC_CONTEXT_BLOCK);
+ BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_ID_DATA);
+
+ BLOCK(OBJC_PROPERTY_BLOCK);
+ BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA);
+
+ BLOCK(OBJC_METHOD_BLOCK);
+ BLOCK_RECORD(objc_method_block, OBJC_METHOD_DATA);
+
+ BLOCK(OBJC_SELECTOR_BLOCK);
+ BLOCK_RECORD(objc_selector_block, OBJC_SELECTOR_DATA);
+
+ BLOCK(GLOBAL_VARIABLE_BLOCK);
+ BLOCK_RECORD(global_variable_block, GLOBAL_VARIABLE_DATA);
+
+ BLOCK(GLOBAL_FUNCTION_BLOCK);
+ BLOCK_RECORD(global_function_block, GLOBAL_FUNCTION_DATA);
+#undef BLOCK
+#undef BLOCK_RECORD
+}
+
+void APINotesWriter::Implementation::writeControlBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, CONTROL_BLOCK_ID, 3);
+ control_block::MetadataLayout metadata(writer);
+ metadata.emit(ScratchRecord, VERSION_MAJOR, VERSION_MINOR);
+
+ control_block::ModuleNameLayout moduleName(writer);
+ moduleName.emit(ScratchRecord, ModuleName);
+
+ if (SwiftInferImportAsMember) {
+ control_block::ModuleOptionsLayout moduleOptions(writer);
+ moduleOptions.emit(ScratchRecord, SwiftInferImportAsMember);
+ }
+
+ if (SourceFile) {
+ control_block::SourceFileLayout sourceFile(writer);
+ sourceFile.emit(ScratchRecord, SourceFile->getSize(),
+ SourceFile->getModificationTime());
+ }
+}
+
+namespace {
+ /// Used to serialize the on-disk identifier table.
+ class IdentifierTableInfo {
+ public:
+ using key_type = StringRef;
+ using key_type_ref = key_type;
+ using data_type = IdentifierID;
+ using data_type_ref = const data_type &;
+ using hash_value_type = uint32_t;
+ using offset_type = unsigned;
+
+ hash_value_type ComputeHash(key_type_ref key) {
+ return llvm::HashString(key);
+ }
+
+ std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
+ key_type_ref key,
+ data_type_ref data) {
+ uint32_t keyLength = key.size();
+ uint32_t dataLength = sizeof(uint32_t);
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(keyLength);
+ writer.write<uint16_t>(dataLength);
+ return { keyLength, dataLength };
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ out << key;
+ }
+
+ void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
+ unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(data);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeIdentifierBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, IDENTIFIER_BLOCK_ID, 3);
+
+ if (IdentifierIDs.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<IdentifierTableInfo> generator;
+ for (auto &entry : IdentifierIDs)
+ generator.insert(entry.first(), entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ identifier_block::IdentifierDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Retrieve the serialized size of the given CommonEntityInfo, for use in
+ /// on-disk hash tables.
+ static unsigned getCommonEntityInfoSize(const CommonEntityInfo &info) {
+ return 5 + info.UnavailableMsg.size() + info.SwiftName.size();
+ }
+
+ /// Emit a serialized representation of the common entity information.
+ static void emitCommonEntityInfo(raw_ostream &out,
+ const CommonEntityInfo &info) {
+ endian::Writer<little> writer(out);
+ uint8_t payload = 0;
+ if (auto swiftPrivate = info.isSwiftPrivate()) {
+ payload |= 0x01;
+ if (*swiftPrivate) payload |= 0x02;
+ }
+ payload <<= 1;
+ payload |= info.Unavailable;
+ payload <<= 1;
+ payload |= info.UnavailableInSwift;
+
+ writer.write<uint8_t>(payload);
+
+ writer.write<uint16_t>(info.UnavailableMsg.size());
+ out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size());
+ writer.write<uint16_t>(info.SwiftName.size());
+ out.write(info.SwiftName.c_str(), info.SwiftName.size());
+ }
+
+ // Retrieve the serialized size of the given CommonTypeInfo, for use
+ // in on-disk hash tables.
+ static unsigned getCommonTypeInfoSize(const CommonTypeInfo &info) {
+ return 2 + (info.getSwiftBridge() ? info.getSwiftBridge()->size() : 0) +
+ 2 + (info.getNSErrorDomain() ? info.getNSErrorDomain()->size() : 0) +
+ getCommonEntityInfoSize(info);
+ }
+
+ /// Emit a serialized representation of the common type information.
+ static void emitCommonTypeInfo(raw_ostream &out, const CommonTypeInfo &info) {
+ emitCommonEntityInfo(out, info);
+ endian::Writer<little> writer(out);
+ if (auto swiftBridge = info.getSwiftBridge()) {
+ writer.write<uint16_t>(swiftBridge->size() + 1);
+ out.write(swiftBridge->c_str(), swiftBridge->size());
+ } else {
+ writer.write<uint16_t>(0);
+ }
+ if (auto nsErrorDomain = info.getNSErrorDomain()) {
+ writer.write<uint16_t>(nsErrorDomain->size() + 1);
+ out.write(nsErrorDomain->c_str(), info.getNSErrorDomain()->size());
+ } else {
+ writer.write<uint16_t>(0);
+ }
+ }
+
+ /// Used to serialize the on-disk Objective-C context table.
+ class ObjCContextIDTableInfo {
+ public:
+ using key_type = std::pair<unsigned, char>; // identifier ID, is-protocol
+ using key_type_ref = key_type;
+ using data_type = unsigned;
+ using data_type_ref = const data_type &;
+ using hash_value_type = size_t;
+ using offset_type = unsigned;
+
+ hash_value_type ComputeHash(key_type_ref key) {
+ return static_cast<size_t>(llvm::hash_value(key));
+ }
+
+ std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
+ key_type_ref key,
+ data_type_ref data) {
+ uint32_t keyLength = sizeof(uint32_t) + 1;
+ uint32_t dataLength = sizeof(uint32_t);
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(keyLength);
+ writer.write<uint16_t>(dataLength);
+ return { keyLength, dataLength };
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key.first);
+ writer.write<uint8_t>(key.second);
+ }
+
+ void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
+ unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(data);
+ }
+ };
+} // end anonymous namespace
+
+namespace {
+ /// Retrieve the serialized size of the given VersionTuple, for use in
+ /// on-disk hash tables.
+ unsigned getVersionTupleSize(const VersionTuple &version) {
+ unsigned size = sizeof(uint8_t) + /*major*/sizeof(uint32_t);
+ if (version.getMinor()) size += sizeof(uint32_t);
+ if (version.getSubminor()) size += sizeof(uint32_t);
+ if (version.getBuild()) size += sizeof(uint32_t);
+ return size;
+ }
+
+ /// Emit a serialized representation of a version tuple.
+ void emitVersionTuple(raw_ostream &out, const VersionTuple &version) {
+ endian::Writer<little> writer(out);
+
+ // First byte contains the number of components beyond the 'major'
+ // component.
+ uint8_t descriptor;
+ if (version.getBuild()) descriptor = 3;
+ else if (version.getSubminor()) descriptor = 2;
+ else if (version.getMinor()) descriptor = 1;
+ else descriptor = 0;
+ assert(!version.usesUnderscores() && "Not a serializable version");
+ writer.write<uint8_t>(descriptor);
+
+ // Write the components.
+ writer.write<uint32_t>(version.getMajor());
+ if (auto minor = version.getMinor())
+ writer.write<uint32_t>(*minor);
+ if (auto subminor = version.getSubminor())
+ writer.write<uint32_t>(*subminor);
+ if (auto build = version.getBuild())
+ writer.write<uint32_t>(*build);
+ }
+
+ /// Localized helper to make a type dependent, thwarting template argument
+ /// deduction.
+ template<typename T>
+ struct MakeDependent {
+ typedef T Type;
+ };
+
+ /// Determine the size of an array of versioned information,
+ template<typename T>
+ unsigned getVersionedInfoSize(
+ const SmallVectorImpl<std::pair<VersionTuple, T>> &infoArray,
+ llvm::function_ref<unsigned(const typename MakeDependent<T>::Type&)>
+ getInfoSize) {
+ unsigned result = sizeof(uint16_t); // # of elements
+ for (const auto &element : infoArray) {
+ result += getVersionTupleSize(element.first);
+ result += getInfoSize(element.second);
+ }
+
+ return result;
+ }
+
+ /// Emit versioned information.
+ template<typename T>
+ void emitVersionedInfo(
+ raw_ostream &out,
+ const SmallVectorImpl<std::pair<VersionTuple, T>> &infoArray,
+ llvm::function_ref<void(raw_ostream &out,
+ const typename MakeDependent<T>::Type& info)>
+ emitInfo) {
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(infoArray.size());
+ for (const auto &element : infoArray) {
+ emitVersionTuple(out, element.first);
+ emitInfo(out, element.second);
+ }
+ }
+
+ /// Retrieve the serialized size of the given VariableInfo, for use in
+ /// on-disk hash tables.
+ unsigned getVariableInfoSize(const VariableInfo &info) {
+ return 2 + getCommonEntityInfoSize(info) + 2 + info.getType().size();
+ }
+
+ /// Emit a serialized representation of the variable information.
+ void emitVariableInfo(raw_ostream &out, const VariableInfo &info) {
+ emitCommonEntityInfo(out, info);
+
+ uint8_t bytes[2] = { 0, 0 };
+ if (auto nullable = info.getNullability()) {
+ bytes[0] = 1;
+ bytes[1] = static_cast<uint8_t>(*nullable);
+ } else {
+ // Nothing to do.
+ }
+
+ out.write(reinterpret_cast<const char *>(bytes), 2);
+
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(info.getType().size());
+ out.write(info.getType().data(), info.getType().size());
+ }
+
+ /// On-dish hash table info key base for handling versioned data.
+ template<typename Derived, typename KeyType, typename UnversionedDataType>
+ class VersionedTableInfo {
+ Derived &asDerived() {
+ return *static_cast<Derived *>(this);
+ }
+
+ const Derived &asDerived() const {
+ return *static_cast<const Derived *>(this);
+ }
+
+ public:
+ using key_type = KeyType;
+ using key_type_ref = key_type;
+ using data_type =
+ SmallVector<std::pair<VersionTuple, UnversionedDataType>, 1>;
+ using data_type_ref = const data_type &;
+ using hash_value_type = size_t;
+ using offset_type = unsigned;
+
+ hash_value_type ComputeHash(key_type_ref key) {
+ return llvm::hash_value(key);
+ }
+
+ std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
+ key_type_ref key,
+ data_type_ref data) {
+ uint32_t keyLength = asDerived().getKeyLength(key);
+ uint32_t dataLength = getVersionedInfoSize(data,
+ [this](const UnversionedDataType &unversionedInfo) {
+ return asDerived().getUnversionedInfoSize(unversionedInfo);
+ });
+
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(keyLength);
+ writer.write<uint16_t>(dataLength);
+ return { keyLength, dataLength };
+ }
+
+ void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
+ unsigned len) {
+ emitVersionedInfo(out, data,
+ [this](llvm::raw_ostream &out,
+ const UnversionedDataType &unversionedInfo) {
+ asDerived().emitUnversionedInfo(out, unversionedInfo);
+ });
+ }
+ };
+
+ /// Used to serialize the on-disk Objective-C property table.
+ class ObjCContextInfoTableInfo
+ : public VersionedTableInfo<ObjCContextInfoTableInfo,
+ unsigned,
+ ObjCContextInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const ObjCContextInfo &info) {
+ return getCommonTypeInfoSize(info) + 1;
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const ObjCContextInfo &info) {
+ emitCommonTypeInfo(out, info);
+
+ uint8_t payload = 0;
+ if (auto nullable = info.getDefaultNullability()) {
+ payload = (0x01 << 2) | static_cast<uint8_t>(*nullable);
+ }
+ payload = (payload << 1) | (info.hasDesignatedInits() ? 1 : 0);
+ out << payload;
+ }
+ };
+
+ /// Used to serialize the on-disk Objective-C property table.
+ class ObjCPropertyTableInfo
+ : public VersionedTableInfo<ObjCPropertyTableInfo,
+ std::tuple<unsigned, unsigned, char>,
+ ObjCPropertyInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(std::get<0>(key));
+ writer.write<uint32_t>(std::get<1>(key));
+ writer.write<uint8_t>(std::get<2>(key));
+ }
+
+ unsigned getUnversionedInfoSize(const ObjCPropertyInfo &info) {
+ return getVariableInfoSize(info) + 1;
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const ObjCPropertyInfo &info) {
+ emitVariableInfo(out, info);
+ uint8_t flags = 0;
+ if (Optional<bool> value = info.getSwiftImportAsAccessors()) {
+ flags |= 1 << 0;
+ flags |= value.getValue() << 1;
+ }
+ out << flags;
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeObjCContextBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, OBJC_CONTEXT_BLOCK_ID, 3);
+
+ if (ObjCContexts.empty())
+ return;
+
+ {
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCContextIDTableInfo> generator;
+ for (auto &entry : ObjCContexts)
+ generator.insert(entry.first, entry.second.first);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_context_block::ObjCContextIDLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+ }
+
+ {
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCContextInfoTableInfo>
+ generator;
+ for (auto &entry : ObjCContexts)
+ generator.insert(entry.second.first, entry.second.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_context_block::ObjCContextInfoLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+ }
+}
+
+void APINotesWriter::Implementation::writeObjCPropertyBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, OBJC_PROPERTY_BLOCK_ID, 3);
+
+ if (ObjCProperties.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCPropertyTableInfo> generator;
+ for (auto &entry : ObjCProperties)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_property_block::ObjCPropertyDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ static unsigned getParamInfoSize(const ParamInfo &info) {
+ return getVariableInfoSize(info) + 1;
+ }
+
+ static void emitParamInfo(raw_ostream &out, const ParamInfo &info) {
+ emitVariableInfo(out, info);
+
+ endian::Writer<little> writer(out);
+
+ uint8_t payload = 0;
+ if (auto noescape = info.isNoEscape()) {
+ payload |= 0x01;
+ if (*noescape)
+ payload |= 0x02;
+ }
+ writer.write<uint8_t>(payload);
+ }
+
+ /// Retrieve the serialized size of the given FunctionInfo, for use in
+ /// on-disk hash tables.
+ static unsigned getFunctionInfoSize(const FunctionInfo &info) {
+ unsigned size = 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info) + 2;
+
+ for (const auto ¶m : info.Params)
+ size += getParamInfoSize(param);
+
+ size += 2 + info.ResultType.size();
+ return size;
+ }
+
+ /// Emit a serialized representation of the function information.
+ static void emitFunctionInfo(raw_ostream &out, const FunctionInfo &info) {
+ emitCommonEntityInfo(out, info);
+
+ endian::Writer<little> writer(out);
+ writer.write<uint8_t>(info.NullabilityAudited);
+ writer.write<uint8_t>(info.NumAdjustedNullable);
+ writer.write<uint64_t>(info.NullabilityPayload);
+
+ // Parameters.
+ writer.write<uint16_t>(info.Params.size());
+ for (const auto &pi : info.Params)
+ emitParamInfo(out, pi);
+
+ // Result type.
+ writer.write<uint16_t>(info.ResultType.size());
+ out.write(info.ResultType.data(), info.ResultType.size());
+ }
+
+ /// Used to serialize the on-disk Objective-C method table.
+ class ObjCMethodTableInfo
+ : public VersionedTableInfo<ObjCMethodTableInfo,
+ std::tuple<unsigned, unsigned, char>,
+ ObjCMethodInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t) + sizeof(uint32_t) + 1;
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(std::get<0>(key));
+ writer.write<uint32_t>(std::get<1>(key));
+ writer.write<uint8_t>(std::get<2>(key));
+ }
+
+ unsigned getUnversionedInfoSize(const ObjCMethodInfo &info) {
+ return 1 + getFunctionInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const ObjCMethodInfo &info) {
+ uint8_t payload = info.FactoryAsInit;
+ payload = (payload << 1) | info.DesignatedInit;
+ payload = (payload << 1) | info.Required;
+ endian::Writer<little> writer(out);
+ writer.write<uint8_t>(payload);
+
+ emitFunctionInfo(out, info);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeObjCMethodBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, OBJC_METHOD_BLOCK_ID, 3);
+
+ if (ObjCMethods.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCMethodTableInfo> generator;
+ for (auto &entry : ObjCMethods) {
+ generator.insert(entry.first, entry.second);
+ }
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_method_block::ObjCMethodDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk Objective-C selector table.
+ class ObjCSelectorTableInfo {
+ public:
+ using key_type = StoredObjCSelector;
+ using key_type_ref = const key_type &;
+ using data_type = SelectorID;
+ using data_type_ref = data_type;
+ using hash_value_type = unsigned;
+ using offset_type = unsigned;
+
+ hash_value_type ComputeHash(key_type_ref key) {
+ return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(key);
+ }
+
+ std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
+ key_type_ref key,
+ data_type_ref data) {
+ uint32_t keyLength = sizeof(uint16_t)
+ + sizeof(uint32_t) * key.Identifiers.size();
+ uint32_t dataLength = sizeof(uint32_t);
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(keyLength);
+ writer.write<uint16_t>(dataLength);
+ return { keyLength, dataLength };
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(key.NumPieces);
+ for (auto piece : key.Identifiers) {
+ writer.write<uint32_t>(piece);
+ }
+ }
+
+ void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
+ unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(data);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeObjCSelectorBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, OBJC_SELECTOR_BLOCK_ID, 3);
+
+ if (SelectorIDs.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCSelectorTableInfo> generator;
+ for (auto &entry : SelectorIDs)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_selector_block::ObjCSelectorDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk global variable table.
+ class GlobalVariableTableInfo
+ : public VersionedTableInfo<GlobalVariableTableInfo,
+ unsigned,
+ GlobalVariableInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref key) {
+ return sizeof(uint32_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const GlobalVariableInfo &info) {
+ return getVariableInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out,
+ const GlobalVariableInfo &info) {
+ emitVariableInfo(out, info);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeGlobalVariableBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, GLOBAL_VARIABLE_BLOCK_ID, 3);
+
+ if (GlobalVariables.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<GlobalVariableTableInfo> generator;
+ for (auto &entry : GlobalVariables)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ global_variable_block::GlobalVariableDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk global function table.
+ class GlobalFunctionTableInfo
+ : public VersionedTableInfo<GlobalFunctionTableInfo,
+ unsigned,
+ GlobalFunctionInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const GlobalFunctionInfo &info) {
+ return getFunctionInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out,
+ const GlobalFunctionInfo &info) {
+ emitFunctionInfo(out, info);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeGlobalFunctionBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, GLOBAL_FUNCTION_BLOCK_ID, 3);
+
+ if (GlobalFunctions.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<GlobalFunctionTableInfo> generator;
+ for (auto &entry : GlobalFunctions) {
+ generator.insert(entry.first, entry.second);
+ }
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ global_function_block::GlobalFunctionDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk global enum constant.
+ class EnumConstantTableInfo
+ : public VersionedTableInfo<EnumConstantTableInfo,
+ unsigned,
+ EnumConstantInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const EnumConstantInfo &info) {
+ return getCommonEntityInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const EnumConstantInfo &info) {
+ emitCommonEntityInfo(out, info);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeEnumConstantBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, ENUM_CONSTANT_BLOCK_ID, 3);
+
+ if (EnumConstants.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<EnumConstantTableInfo> generator;
+ for (auto &entry : EnumConstants)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ enum_constant_block::EnumConstantDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ template<typename Derived, typename UnversionedDataType>
+ class CommonTypeTableInfo
+ : public VersionedTableInfo<Derived, unsigned, UnversionedDataType> {
+ public:
+ using key_type_ref = typename CommonTypeTableInfo::key_type_ref;
+
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(IdentifierID);
+ }
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<IdentifierID>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const UnversionedDataType &info) {
+ return getCommonTypeInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out,
+ const UnversionedDataType &info) {
+ emitCommonTypeInfo(out, info);
+ }
+ };
+
+ /// Used to serialize the on-disk tag table.
+ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> { };
+
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeTagBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, TAG_BLOCK_ID, 3);
+
+ if (Tags.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<TagTableInfo> generator;
+ for (auto &entry : Tags)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ tag_block::TagDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk typedef table.
+ class TypedefTableInfo
+ : public CommonTypeTableInfo<TypedefTableInfo, TypedefInfo> {
+
+ public:
+ unsigned getUnversionedInfoSize(const TypedefInfo &info) {
+ return 1 + getCommonTypeInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const TypedefInfo &info) {
+ endian::Writer<little> writer(out);
+
+ uint8_t payload = 0;
+ if (auto swiftWrapper = info.SwiftWrapper) {
+ payload |= static_cast<uint8_t>(*swiftWrapper) + 1;
+ }
+
+ writer.write<uint8_t>(payload);
+
+ emitCommonTypeInfo(out, info);
+ }
+
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeTypedefBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, TYPEDEF_BLOCK_ID, 3);
+
+ if (Typedefs.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<TypedefTableInfo> generator;
+ for (auto &entry : Typedefs)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ typedef_block::TypedefDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) {
+ // Write the API notes file into a buffer.
+ SmallVector<char, 0> buffer;
+ {
+ llvm::BitstreamWriter writer(buffer);
+
+ // Emit the signature.
+ for (unsigned char byte : API_NOTES_SIGNATURE)
+ writer.Emit(byte, 8);
+
+ // Emit the blocks.
+ writeBlockInfoBlock(writer);
+ writeControlBlock(writer);
+ writeIdentifierBlock(writer);
+ writeObjCContextBlock(writer);
+ writeObjCPropertyBlock(writer);
+ writeObjCMethodBlock(writer);
+ writeObjCSelectorBlock(writer);
+ writeGlobalVariableBlock(writer);
+ writeGlobalFunctionBlock(writer);
+ writeEnumConstantBlock(writer);
+ writeTagBlock(writer);
+ writeTypedefBlock(writer);
+ }
+
+ // Write the buffer to the stream.
+ os.write(buffer.data(), buffer.size());
+ os.flush();
+}
+
+APINotesWriter::APINotesWriter(StringRef moduleName, const FileEntry *sourceFile)
+ : Impl(*new Implementation)
+{
+ Impl.ModuleName = moduleName;
+ Impl.SourceFile = sourceFile;
+}
+
+APINotesWriter::~APINotesWriter() {
+ delete &Impl;
+}
+
+
+void APINotesWriter::writeToStream(raw_ostream &os) {
+ Impl.writeToStream(os);
+}
+
+ContextID APINotesWriter::addObjCContext(StringRef name, bool isClass,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID nameID = Impl.getIdentifier(name);
+
+ std::pair<unsigned, char> key(nameID, isClass ? 0 : 1);
+ auto known = Impl.ObjCContexts.find(key);
+ if (known == Impl.ObjCContexts.end()) {
+ unsigned nextID = Impl.ObjCContexts.size() + 1;
+
+ VersionedSmallVector<ObjCContextInfo> emptyVersionedInfo;
+ known = Impl.ObjCContexts.insert(
+ std::make_pair(key, std::make_pair(nextID, emptyVersionedInfo)))
+ .first;
+
+ Impl.ObjCContextNames[nextID] = nameID;
+ }
+
+ // Add this version information.
+ auto &versionedVec = known->second.second;
+ bool found = false;
+ for (auto &versioned : versionedVec){
+ if (versioned.first == swiftVersion) {
+ versioned.second |= info;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ versionedVec.push_back({swiftVersion, info});
+
+ return ContextID(known->second.first);
+}
+
+void APINotesWriter::addObjCProperty(ContextID contextID, StringRef name,
+ bool isInstance,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID nameID = Impl.getIdentifier(name);
+ Impl.ObjCProperties[std::make_tuple(contextID.Value, nameID, isInstance)]
+ .push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addObjCMethod(ContextID contextID,
+ ObjCSelectorRef selector,
+ bool isInstanceMethod,
+ const ObjCMethodInfo &info,
+ VersionTuple swiftVersion) {
+ SelectorID selectorID = Impl.getSelector(selector);
+ auto key = std::tuple<unsigned, unsigned, char>{
+ contextID.Value, selectorID, isInstanceMethod};
+ Impl.ObjCMethods[key].push_back({swiftVersion, info});
+
+ // If this method is a designated initializer, update the class to note that
+ // it has designated initializers.
+ if (info.DesignatedInit) {
+ assert(Impl.ObjCContexts.count({Impl.ObjCContextNames[contextID.Value],
+ (char)0}));
+ auto &versionedVec =
+ Impl.ObjCContexts[{Impl.ObjCContextNames[contextID.Value], (char)0}]
+ .second;
+ bool found = false;
+ for (auto &versioned : versionedVec) {
+ if (versioned.first == swiftVersion) {
+ versioned.second.setHasDesignatedInits(true);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ versionedVec.push_back({swiftVersion, ObjCContextInfo()});
+ versionedVec.back().second.setHasDesignatedInits(true);
+ }
+ }
+}
+
+void APINotesWriter::addGlobalVariable(llvm::StringRef name,
+ const GlobalVariableInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID variableID = Impl.getIdentifier(name);
+ Impl.GlobalVariables[variableID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addGlobalFunction(llvm::StringRef name,
+ const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID nameID = Impl.getIdentifier(name);
+ Impl.GlobalFunctions[nameID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addEnumConstant(llvm::StringRef name,
+ const EnumConstantInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID enumConstantID = Impl.getIdentifier(name);
+ Impl.EnumConstants[enumConstantID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID tagID = Impl.getIdentifier(name);
+ Impl.Tags[tagID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID typedefID = Impl.getIdentifier(name);
+ Impl.Typedefs[typedefID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addModuleOptions(ModuleOptions opts) {
+ Impl.SwiftInferImportAsMember = opts.SwiftInferImportAsMember;
+}
+
diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp
new file mode 100644
index 0000000..0ac0b6d
--- /dev/null
+++ b/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -0,0 +1,1411 @@
+//===--- APINotesYAMLCompiler.cpp - API Notes YAML format reader *- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file reads API notes specified in YAML format.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/APINotesYAMLCompiler.h"
+#include "clang/APINotes/APINotesReader.h"
+#include "clang/APINotes/Types.h"
+#include "clang/APINotes/APINotesWriter.h"
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <algorithm>
+
+/*
+
+ YAML Format specification.
+
+ Nullability should be expressed using one of the following values:
+ O - Optional (or Nullable)
+ N - Not Optional
+ S - Scalar
+ U - Unknown
+ Note, the API is considered 'audited' when at least the return value or a
+ parameter has a nullability value. For 'audited' APIs, we assume the default
+ nullability for any underspecified type.
+
+ FactoryAsInit can have the following values:
+ C - Treat as class method.
+ I - Treat as initializer.
+ A - Automatically infer based on the name and type (default).
+
+---
+ Name: AppKit # The name of the framework
+
+ Availability: OSX # Optional: Specifies which platform the API is
+ # available on. [OSX / iOS / none/
+ # available / nonswift]
+
+ AvailabilityMsg: "" # Optional: Custom availability message to display to
+ # the user, when API is not available.
+
+ Classes: # List of classes
+ ...
+ Protocols: # List of protocols
+ ...
+ Functions: # List of functions
+ ...
+ Globals: # List of globals
+ ...
+ Enumerators: # List of enumerators
+ ...
+ Tags: # List of tags (struct/union/enum/C++ class)
+ ...
+ Typedefs: # List of typedef-names and C++11 type aliases
+ ...
+
+ Each class and protocol is defined as following:
+
+ - Name: NSView # The name of the class
+
+ AuditedForNullability: false # Optional: Specifies if the whole class
+ # has been audited for nullability.
+ # If yes, we assume all the methods and
+ # properties of the class have default
+ # nullability unless it is overwritten by
+ # a method/property specific info below.
+ # This applies to all classes, extensions,
+ # and categories of the class defined in
+ # the current framework/module.
+ # (false/true)
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+ Methods:
+ - Selector: "setSubviews:" # Full name
+
+ MethodKind: Instance # [Class/Instance]
+
+ Nullability: [N, N, O, S] # The nullability of parameters in
+ # the signature.
+
+ NullabilityOfRet: O # The nullability of the return value.
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+ FactoryAsInit: C # Optional: Specifies if this method is a
+ # factory initializer (false/true)
+ DesignatedInit: false # Optional: Specifies if this method is a
+ # designated initializer (false/true)
+
+ Required: false # Optional: Specifies if this method is a
+ # required initializer (false/true)
+
+ Properties:
+ - Name: window
+
+ Nullability: O
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+ The protocol definition format is the same as the class definition.
+
+ Each function definition is of the following form:
+
+ - Name: "myGlobalFunction" # Full name
+
+ Nullability: [N, N, O, S] # The nullability of parameters in
+ # the signature.
+
+ NullabilityOfRet: O # The nullability of the return value.
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+Each global variable definition is of the following form:
+
+ - Name: MyGlobalVar
+
+ Nullability: O
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+*/
+
+using llvm::StringRef;
+using namespace clang;
+namespace {
+ enum class APIAvailability {
+ Available = 0,
+ OSX,
+ IOS,
+ None,
+ NonSwift,
+ };
+
+ enum class MethodKind {
+ Class,
+ Instance,
+ };
+
+ struct AvailabilityItem {
+ APIAvailability Mode = APIAvailability::Available;
+ StringRef Msg;
+ AvailabilityItem() : Mode(APIAvailability::Available), Msg("") {}
+ };
+
+ static llvm::Optional<NullabilityKind> AbsentNullability = llvm::None;
+ static llvm::Optional<NullabilityKind> DefaultNullability =
+ NullabilityKind::NonNull;
+ typedef std::vector<clang::NullabilityKind> NullabilitySeq;
+
+ struct Param {
+ unsigned Position;
+ Optional<bool> NoEscape = false;
+ llvm::Optional<NullabilityKind> Nullability;
+ StringRef Type;
+ };
+ typedef std::vector<Param> ParamsSeq;
+
+ struct Method {
+ StringRef Selector;
+ MethodKind Kind;
+ ParamsSeq Params;
+ NullabilitySeq Nullability;
+ llvm::Optional<NullabilityKind> NullabilityOfRet;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ api_notes::FactoryAsInitKind FactoryAsInit
+ = api_notes::FactoryAsInitKind::Infer;
+ bool DesignatedInit = false;
+ bool Required = false;
+ StringRef ResultType;
+ };
+ typedef std::vector<Method> MethodsSeq;
+
+ struct Property {
+ StringRef Name;
+ llvm::Optional<MethodKind> Kind;
+ llvm::Optional<NullabilityKind> Nullability;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ Optional<bool> SwiftImportAsAccessors;
+ StringRef Type;
+ };
+ typedef std::vector<Property> PropertiesSeq;
+
+ struct Class {
+ StringRef Name;
+ bool AuditedForNullability = false;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ Optional<StringRef> SwiftBridge;
+ Optional<StringRef> NSErrorDomain;
+ MethodsSeq Methods;
+ PropertiesSeq Properties;
+ };
+ typedef std::vector<Class> ClassesSeq;
+
+ struct Function {
+ StringRef Name;
+ ParamsSeq Params;
+ NullabilitySeq Nullability;
+ llvm::Optional<NullabilityKind> NullabilityOfRet;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ StringRef Type;
+ StringRef ResultType;
+ };
+ typedef std::vector<Function> FunctionsSeq;
+
+ struct GlobalVariable {
+ StringRef Name;
+ llvm::Optional<NullabilityKind> Nullability;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ StringRef Type;
+ };
+ typedef std::vector<GlobalVariable> GlobalVariablesSeq;
+
+ struct EnumConstant {
+ StringRef Name;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ };
+ typedef std::vector<EnumConstant> EnumConstantsSeq;
+
+ struct Tag {
+ StringRef Name;
+ AvailabilityItem Availability;
+ StringRef SwiftName;
+ Optional<bool> SwiftPrivate;
+ Optional<StringRef> SwiftBridge;
+ Optional<StringRef> NSErrorDomain;
+ };
+ typedef std::vector<Tag> TagsSeq;
+
+ struct Typedef {
+ StringRef Name;
+ AvailabilityItem Availability;
+ StringRef SwiftName;
+ Optional<bool> SwiftPrivate;
+ Optional<StringRef> SwiftBridge;
+ Optional<StringRef> NSErrorDomain;
+ Optional<api_notes::SwiftWrapperKind> SwiftWrapper;
+ };
+ typedef std::vector<Typedef> TypedefsSeq;
+
+ struct TopLevelItems {
+ ClassesSeq Classes;
+ ClassesSeq Protocols;
+ FunctionsSeq Functions;
+ GlobalVariablesSeq Globals;
+ EnumConstantsSeq EnumConstants;
+ TagsSeq Tags;
+ TypedefsSeq Typedefs;
+ };
+
+ struct Versioned {
+ VersionTuple Version;
+ TopLevelItems Items;
+ };
+
+ typedef std::vector<Versioned> VersionedSeq;
+
+ struct Module {
+ StringRef Name;
+ AvailabilityItem Availability;
+ TopLevelItems TopLevel;
+ VersionedSeq SwiftVersions;
+
+ llvm::Optional<bool> SwiftInferImportAsMember = {llvm::None};
+
+ LLVM_ATTRIBUTE_DEPRECATED(
+ void dump() LLVM_ATTRIBUTE_USED,
+ "only for use within the debugger");
+ };
+}
+
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(clang::NullabilityKind)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Method)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Property)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Param)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Class)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Function)
+LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable)
+LLVM_YAML_IS_SEQUENCE_VECTOR(EnumConstant)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Tag)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Versioned)
+
+namespace llvm {
+ namespace yaml {
+
+ template <>
+ struct ScalarEnumerationTraits<NullabilityKind > {
+ static void enumeration(IO &io, NullabilityKind &value) {
+ io.enumCase(value, "N", NullabilityKind::NonNull);
+ io.enumCase(value, "O", NullabilityKind::Nullable);
+ io.enumCase(value, "U", NullabilityKind::Unspecified);
+ // TODO: Mapping this to it's own value would allow for better cross
+ // checking. Also the default should be Unknown.
+ io.enumCase(value, "S", NullabilityKind::Unspecified);
+ }
+ };
+
+ template <>
+ struct ScalarEnumerationTraits<api_notes::FactoryAsInitKind > {
+ static void enumeration(IO &io, api_notes::FactoryAsInitKind &value) {
+ io.enumCase(value, "A", api_notes::FactoryAsInitKind::Infer);
+ io.enumCase(value, "C", api_notes::FactoryAsInitKind::AsClassMethod);
+ io.enumCase(value, "I", api_notes::FactoryAsInitKind::AsInitializer);
+ }
+ };
+
+ template <>
+ struct ScalarEnumerationTraits<MethodKind> {
+ static void enumeration(IO &io, MethodKind &value) {
+ io.enumCase(value, "Class", MethodKind::Class);
+ io.enumCase(value, "Instance", MethodKind::Instance);
+ }
+ };
+
+ template <>
+ struct ScalarEnumerationTraits<APIAvailability> {
+ static void enumeration(IO &io, APIAvailability &value) {
+ io.enumCase(value, "OSX", APIAvailability::OSX);
+ io.enumCase(value, "iOS", APIAvailability::IOS);
+ io.enumCase(value, "none", APIAvailability::None);
+ io.enumCase(value, "nonswift", APIAvailability::NonSwift);
+ io.enumCase(value, "available", APIAvailability::Available);
+ }
+ };
+
+ template<>
+ struct ScalarEnumerationTraits<api_notes::SwiftWrapperKind> {
+ static void enumeration(IO &io, api_notes::SwiftWrapperKind &value) {
+ io.enumCase(value, "none", api_notes::SwiftWrapperKind::None);
+ io.enumCase(value, "struct", api_notes::SwiftWrapperKind::Struct);
+ io.enumCase(value, "enum", api_notes::SwiftWrapperKind::Enum);
+ }
+ };
+
+ template <>
+ struct ScalarTraits<VersionTuple> {
+ static void output(const VersionTuple &value, void*,
+ llvm::raw_ostream &out) {
+ out << value;
+ }
+ static StringRef input(StringRef scalar, void*, VersionTuple &value) {
+ if (value.tryParse(scalar))
+ return "not a version number in the form XX.YY";
+
+ // Canonicalize on '.' as a separator.
+ value.UseDotAsSeparator();
+ return StringRef();
+ }
+
+ static bool mustQuote(StringRef) { return false; }
+ };
+
+ template <>
+ struct MappingTraits<Param> {
+ static void mapping(IO &io, Param& p) {
+ io.mapRequired("Position", p.Position);
+ io.mapOptional("Nullability", p.Nullability,
+ AbsentNullability);
+ io.mapOptional("NoEscape", p.NoEscape);
+ io.mapOptional("Type", p.Type, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<Property> {
+ static void mapping(IO &io, Property& p) {
+ io.mapRequired("Name", p.Name);
+ io.mapOptional("PropertyKind", p.Kind);
+ io.mapOptional("Nullability", p.Nullability,
+ AbsentNullability);
+ io.mapOptional("Availability", p.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", p.Availability.Msg);
+ io.mapOptional("SwiftPrivate", p.SwiftPrivate);
+ io.mapOptional("SwiftName", p.SwiftName);
+ io.mapOptional("SwiftImportAsAccessors", p.SwiftImportAsAccessors);
+ io.mapOptional("Type", p.Type, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<Method> {
+ static void mapping(IO &io, Method& m) {
+ io.mapRequired("Selector", m.Selector);
+ io.mapRequired("MethodKind", m.Kind);
+ io.mapOptional("Parameters", m.Params);
+ io.mapOptional("Nullability", m.Nullability);
+ io.mapOptional("NullabilityOfRet", m.NullabilityOfRet,
+ AbsentNullability);
+ io.mapOptional("Availability", m.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", m.Availability.Msg);
+ io.mapOptional("SwiftPrivate", m.SwiftPrivate);
+ io.mapOptional("SwiftName", m.SwiftName);
+ io.mapOptional("FactoryAsInit", m.FactoryAsInit,
+ api_notes::FactoryAsInitKind::Infer);
+ io.mapOptional("DesignatedInit", m.DesignatedInit, false);
+ io.mapOptional("Required", m.Required, false);
+ io.mapOptional("ResultType", m.ResultType, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<Class> {
+ static void mapping(IO &io, Class& c) {
+ io.mapRequired("Name", c.Name);
+ io.mapOptional("AuditedForNullability", c.AuditedForNullability, false);
+ io.mapOptional("Availability", c.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", c.Availability.Msg);
+ io.mapOptional("SwiftPrivate", c.SwiftPrivate);
+ io.mapOptional("SwiftName", c.SwiftName);
+ io.mapOptional("SwiftBridge", c.SwiftBridge);
+ io.mapOptional("NSErrorDomain", c.NSErrorDomain);
+ io.mapOptional("Methods", c.Methods);
+ io.mapOptional("Properties", c.Properties);
+ }
+ };
+
+ template <>
+ struct MappingTraits<Function> {
+ static void mapping(IO &io, Function& f) {
+ io.mapRequired("Name", f.Name);
+ io.mapOptional("Parameters", f.Params);
+ io.mapOptional("Nullability", f.Nullability);
+ io.mapOptional("NullabilityOfRet", f.NullabilityOfRet,
+ AbsentNullability);
+ io.mapOptional("Availability", f.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", f.Availability.Msg);
+ io.mapOptional("SwiftPrivate", f.SwiftPrivate);
+ io.mapOptional("SwiftName", f.SwiftName);
+ io.mapOptional("ResultType", f.ResultType, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<GlobalVariable> {
+ static void mapping(IO &io, GlobalVariable& v) {
+ io.mapRequired("Name", v.Name);
+ io.mapOptional("Nullability", v.Nullability,
+ AbsentNullability);
+ io.mapOptional("Availability", v.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", v.Availability.Msg);
+ io.mapOptional("SwiftPrivate", v.SwiftPrivate);
+ io.mapOptional("SwiftName", v.SwiftName);
+ io.mapOptional("Type", v.Type, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<EnumConstant> {
+ static void mapping(IO &io, EnumConstant& v) {
+ io.mapRequired("Name", v.Name);
+ io.mapOptional("Availability", v.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", v.Availability.Msg);
+ io.mapOptional("SwiftPrivate", v.SwiftPrivate);
+ io.mapOptional("SwiftName", v.SwiftName);
+ }
+ };
+
+ template <>
+ struct MappingTraits<Tag> {
+ static void mapping(IO &io, Tag& t) {
+ io.mapRequired("Name", t.Name);
+ io.mapOptional("Availability", t.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", t.Availability.Msg);
+ io.mapOptional("SwiftPrivate", t.SwiftPrivate);
+ io.mapOptional("SwiftName", t.SwiftName);
+ io.mapOptional("SwiftBridge", t.SwiftBridge);
+ io.mapOptional("NSErrorDomain", t.NSErrorDomain);
+ }
+ };
+
+ template <>
+ struct MappingTraits<Typedef> {
+ static void mapping(IO &io, Typedef& t) {
+ io.mapRequired("Name", t.Name);
+ io.mapOptional("Availability", t.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", t.Availability.Msg);
+ io.mapOptional("SwiftPrivate", t.SwiftPrivate);
+ io.mapOptional("SwiftName", t.SwiftName);
+ io.mapOptional("SwiftBridge", t.SwiftBridge);
+ io.mapOptional("NSErrorDomain", t.NSErrorDomain);
+ io.mapOptional("SwiftWrapper", t.SwiftWrapper);
+ }
+ };
+
+ static void mapTopLevelItems(IO &io, TopLevelItems &i) {
+ io.mapOptional("Classes", i.Classes);
+ io.mapOptional("Protocols", i.Protocols);
+ io.mapOptional("Functions", i.Functions);
+ io.mapOptional("Globals", i.Globals);
+ io.mapOptional("Enumerators", i.EnumConstants);
+ io.mapOptional("Tags", i.Tags);
+ io.mapOptional("Typedefs", i.Typedefs);
+ }
+
+ template <>
+ struct MappingTraits<Versioned> {
+ static void mapping(IO &io, Versioned& v) {
+ io.mapRequired("Version", v.Version);
+ mapTopLevelItems(io, v.Items);
+ }
+ };
+
+ template <>
+ struct MappingTraits<Module> {
+ static void mapping(IO &io, Module& m) {
+ io.mapRequired("Name", m.Name);
+ io.mapOptional("Availability", m.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", m.Availability.Msg);
+ io.mapOptional("SwiftInferImportAsMember", m.SwiftInferImportAsMember);
+
+ mapTopLevelItems(io, m.TopLevel);
+
+ io.mapOptional("SwiftVersions", m.SwiftVersions);
+ }
+ };
+ }
+}
+
+using llvm::yaml::Input;
+using llvm::yaml::Output;
+
+void Module::dump() {
+ Output yout(llvm::errs());
+ yout << *this;
+}
+
+static bool parseAPINotes(StringRef yamlInput, Module &module,
+ llvm::SourceMgr::DiagHandlerTy diagHandler,
+ void *diagHandlerCtxt) {
+ Input yin(yamlInput, nullptr, diagHandler, diagHandlerCtxt);
+ yin >> module;
+
+ return static_cast<bool>(yin.error());
+}
+
+namespace {
+ using namespace api_notes;
+
+ class YAMLConverter {
+ const Module &TheModule;
+ const FileEntry *SourceFile;
+ APINotesWriter *Writer;
+ OSType TargetOS;
+ llvm::raw_ostream &OS;
+ llvm::SourceMgr::DiagHandlerTy DiagHandler;
+ void *DiagHandlerCtxt;
+ bool ErrorOccured;
+
+ /// Emit a diagnostic
+ bool emitError(llvm::Twine message) {
+ DiagHandler(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error,
+ message.str()),
+ DiagHandlerCtxt);
+ ErrorOccured = true;
+ return true;
+ }
+
+ public:
+ YAMLConverter(const Module &module,
+ const FileEntry *sourceFile,
+ OSType targetOS,
+ llvm::raw_ostream &os,
+ llvm::SourceMgr::DiagHandlerTy diagHandler,
+ void *diagHandlerCtxt) :
+ TheModule(module), SourceFile(sourceFile), Writer(0), TargetOS(targetOS), OS(os),
+ DiagHandler(diagHandler), DiagHandlerCtxt(diagHandlerCtxt),
+ ErrorOccured(false) {}
+
+ bool isAvailable(const AvailabilityItem &in) {
+ // Check if the API is available on the OS for which we are building.
+ if (in.Mode == APIAvailability::OSX && TargetOS != OSType::OSX)
+ return false;
+ if (in.Mode == APIAvailability::IOS && TargetOS != OSType::IOS)
+ return false;
+ return true;
+ }
+
+ bool convertAvailability(const AvailabilityItem &in,
+ CommonEntityInfo &outInfo,
+ llvm::StringRef apiName) {
+ // Populate the unavailability information.
+ outInfo.Unavailable = (in.Mode == APIAvailability::None);
+ outInfo.UnavailableInSwift = (in.Mode == APIAvailability::NonSwift);
+ if (outInfo.Unavailable || outInfo.UnavailableInSwift) {
+ outInfo.UnavailableMsg = in.Msg;
+ } else {
+ if (!in.Msg.empty()) {
+ emitError("availability message for available API '" +
+ apiName + "' will not be used");
+ }
+ }
+ return false;
+ }
+
+ void convertParams(const ParamsSeq ¶ms, FunctionInfo &outInfo) {
+ for (const auto &p : params) {
+ ParamInfo pi;
+ if (p.Nullability)
+ pi.setNullabilityAudited(*p.Nullability);
+ pi.setNoEscape(p.NoEscape);
+ pi.setType(p.Type);
+ while (outInfo.Params.size() <= p.Position) {
+ outInfo.Params.push_back(ParamInfo());
+ }
+ outInfo.Params[p.Position] |= pi;
+ }
+ }
+
+ void convertNullability(const NullabilitySeq &nullability,
+ Optional<NullabilityKind> nullabilityOfRet,
+ FunctionInfo &outInfo,
+ llvm::StringRef apiName) {
+ if (nullability.size() > FunctionInfo::getMaxNullabilityIndex()) {
+ emitError("nullability info for " + apiName + " does not fit");
+ return;
+ }
+
+ bool audited = false;
+ unsigned int idx = 1;
+ for (auto i = nullability.begin(),
+ e = nullability.end(); i != e; ++i, ++idx){
+ outInfo.addTypeInfo(idx, *i);
+ audited = true;
+ }
+ if (nullabilityOfRet) {
+ outInfo.addTypeInfo(0, *nullabilityOfRet);
+ audited = true;
+ } else if (audited) {
+ outInfo.addTypeInfo(0, *DefaultNullability);
+ }
+ if (audited) {
+ outInfo.NullabilityAudited = audited;
+ outInfo.NumAdjustedNullable = idx;
+ }
+ }
+
+ /// Convert the common parts of an entity from YAML.
+ template<typename T>
+ bool convertCommon(const T& common, CommonEntityInfo &info,
+ StringRef apiName) {
+ if (!isAvailable(common.Availability))
+ return true;
+
+ convertAvailability(common.Availability, info, apiName);
+ info.setSwiftPrivate(common.SwiftPrivate);
+ info.SwiftName = common.SwiftName;
+ return false;
+ }
+
+ /// Convert the common parts of a type entity from YAML.
+ template<typename T>
+ bool convertCommonType(const T& common, CommonTypeInfo &info,
+ StringRef apiName) {
+ if (convertCommon(common, info, apiName))
+ return true;
+
+ info.setSwiftBridge(common.SwiftBridge);
+ info.setNSErrorDomain(common.NSErrorDomain);
+ return false;
+ }
+
+ // Translate from Method into ObjCMethodInfo and write it out.
+ void convertMethod(const Method &meth,
+ ContextID classID, StringRef className,
+ VersionTuple swiftVersion) {
+ ObjCMethodInfo mInfo;
+
+ if (convertCommon(meth, mInfo, meth.Selector))
+ return;
+
+ // Check if the selector ends with ':' to determine if it takes arguments.
+ bool takesArguments = meth.Selector.endswith(":");
+
+ // Split the selector into pieces.
+ llvm::SmallVector<StringRef, 4> a;
+ meth.Selector.split(a, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+ if (!takesArguments && a.size() > 1 ) {
+ emitError("selector " + meth.Selector + "is missing a ':' at the end");
+ return;
+ }
+
+ // Construct ObjCSelectorRef.
+ api_notes::ObjCSelectorRef selectorRef;
+ selectorRef.NumPieces = !takesArguments ? 0 : a.size();
+ selectorRef.Identifiers = a;
+
+ // Translate the initializer info.
+ mInfo.DesignatedInit = meth.DesignatedInit;
+ mInfo.Required = meth.Required;
+ if (meth.FactoryAsInit != FactoryAsInitKind::Infer)
+ mInfo.setFactoryAsInitKind(meth.FactoryAsInit);
+ mInfo.ResultType = meth.ResultType;
+
+ // Translate parameter information.
+ convertParams(meth.Params, mInfo);
+
+ // Translate nullability info.
+ convertNullability(meth.Nullability, meth.NullabilityOfRet,
+ mInfo, meth.Selector);
+
+ // Write it.
+ Writer->addObjCMethod(classID, selectorRef,
+ meth.Kind == MethodKind::Instance,
+ mInfo, swiftVersion);
+ }
+
+ void convertContext(const Class &cl, bool isClass,
+ VersionTuple swiftVersion) {
+ // Write the class.
+ ObjCContextInfo cInfo;
+
+ if (convertCommonType(cl, cInfo, cl.Name))
+ return;
+
+ if (cl.AuditedForNullability)
+ cInfo.setDefaultNullability(*DefaultNullability);
+
+ ContextID clID = Writer->addObjCContext(cl.Name, isClass, cInfo,
+ swiftVersion);
+
+ // Write all methods.
+ llvm::StringMap<std::pair<bool, bool>> knownMethods;
+ for (const auto &method : cl.Methods) {
+ // Check for duplicate method definitions.
+ bool isInstanceMethod = method.Kind == MethodKind::Instance;
+ bool &known = isInstanceMethod ? knownMethods[method.Selector].first
+ : knownMethods[method.Selector].second;
+ if (known) {
+ emitError(llvm::Twine("duplicate definition of method '") +
+ (isInstanceMethod? "-" : "+") + "[" + cl.Name + " " +
+ method.Selector + "]'");
+ continue;
+ }
+ known = true;
+
+ convertMethod(method, clID, cl.Name, swiftVersion);
+ }
+
+ // Write all properties.
+ llvm::StringSet<> knownInstanceProperties;
+ llvm::StringSet<> knownClassProperties;
+ for (const auto &prop : cl.Properties) {
+ // Check for duplicate property definitions.
+ if ((!prop.Kind || *prop.Kind == MethodKind::Instance) &&
+ !knownInstanceProperties.insert(prop.Name).second) {
+ emitError("duplicate definition of instance property '" + cl.Name +
+ "." + prop.Name + "'");
+ continue;
+ }
+
+ if ((!prop.Kind || *prop.Kind == MethodKind::Class) &&
+ !knownClassProperties.insert(prop.Name).second) {
+ emitError("duplicate definition of class property '" + cl.Name + "." +
+ prop.Name + "'");
+ continue;
+ }
+
+ // Translate from Property into ObjCPropertyInfo.
+ ObjCPropertyInfo pInfo;
+ if (!isAvailable(prop.Availability))
+ continue;
+ convertAvailability(prop.Availability, pInfo, prop.Name);
+ pInfo.setSwiftPrivate(prop.SwiftPrivate);
+ pInfo.SwiftName = prop.SwiftName;
+ if (prop.Nullability)
+ pInfo.setNullabilityAudited(*prop.Nullability);
+ if (prop.SwiftImportAsAccessors)
+ pInfo.setSwiftImportAsAccessors(*prop.SwiftImportAsAccessors);
+ pInfo.setType(prop.Type);
+ if (prop.Kind) {
+ Writer->addObjCProperty(clID, prop.Name,
+ *prop.Kind == MethodKind::Instance, pInfo,
+ swiftVersion);
+ } else {
+ // Add both instance and class properties with this name.
+ Writer->addObjCProperty(clID, prop.Name, true, pInfo, swiftVersion);
+ Writer->addObjCProperty(clID, prop.Name, false, pInfo, swiftVersion);
+ }
+ }
+ }
+
+ void convertTopLevelItems(const TopLevelItems &items,
+ VersionTuple swiftVersion) {
+ // Write all classes.
+ llvm::StringSet<> knownClasses;
+ for (const auto &cl : items.Classes) {
+ // Check for duplicate class definitions.
+ if (!knownClasses.insert(cl.Name).second) {
+ emitError("multiple definitions of class '" + cl.Name + "'");
+ continue;
+ }
+
+ convertContext(cl, /*isClass*/ true, swiftVersion);
+ }
+
+ // Write all protocols.
+ llvm::StringSet<> knownProtocols;
+ for (const auto &pr : items.Protocols) {
+ // Check for duplicate protocol definitions.
+ if (!knownProtocols.insert(pr.Name).second) {
+ emitError("multiple definitions of protocol '" + pr.Name + "'");
+ continue;
+ }
+
+ convertContext(pr, /*isClass*/ false, swiftVersion);
+ }
+
+ // Write all global variables.
+ llvm::StringSet<> knownGlobals;
+ for (const auto &global : items.Globals) {
+ // Check for duplicate global variables.
+ if (!knownGlobals.insert(global.Name).second) {
+ emitError("multiple definitions of global variable '" +
+ global.Name + "'");
+ continue;
+ }
+
+ GlobalVariableInfo info;
+ if (!isAvailable(global.Availability))
+ continue;
+ convertAvailability(global.Availability, info, global.Name);
+ info.setSwiftPrivate(global.SwiftPrivate);
+ info.SwiftName = global.SwiftName;
+ if (global.Nullability)
+ info.setNullabilityAudited(*global.Nullability);
+ info.setType(global.Type);
+ Writer->addGlobalVariable(global.Name, info, swiftVersion);
+ }
+
+ // Write all global functions.
+ llvm::StringSet<> knownFunctions;
+ for (const auto &function : items.Functions) {
+ // Check for duplicate global functions.
+ if (!knownFunctions.insert(function.Name).second) {
+ emitError("multiple definitions of global function '" +
+ function.Name + "'");
+ continue;
+ }
+
+ GlobalFunctionInfo info;
+ if (!isAvailable(function.Availability))
+ continue;
+ convertAvailability(function.Availability, info, function.Name);
+ info.setSwiftPrivate(function.SwiftPrivate);
+ info.SwiftName = function.SwiftName;
+ convertParams(function.Params, info);
+ convertNullability(function.Nullability,
+ function.NullabilityOfRet,
+ info, function.Name);
+ info.ResultType = function.ResultType;
+ Writer->addGlobalFunction(function.Name, info, swiftVersion);
+ }
+
+ // Write all enumerators.
+ llvm::StringSet<> knownEnumConstants;
+ for (const auto &enumConstant : items.EnumConstants) {
+ // Check for duplicate enumerators
+ if (!knownEnumConstants.insert(enumConstant.Name).second) {
+ emitError("multiple definitions of enumerator '" +
+ enumConstant.Name + "'");
+ continue;
+ }
+
+ EnumConstantInfo info;
+ if (!isAvailable(enumConstant.Availability))
+ continue;
+ convertAvailability(enumConstant.Availability, info, enumConstant.Name);
+ info.setSwiftPrivate(enumConstant.SwiftPrivate);
+ info.SwiftName = enumConstant.SwiftName;
+ Writer->addEnumConstant(enumConstant.Name, info, swiftVersion);
+ }
+
+ // Write all tags.
+ llvm::StringSet<> knownTags;
+ for (const auto &t : items.Tags) {
+ // Check for duplicate tag definitions.
+ if (!knownTags.insert(t.Name).second) {
+ emitError("multiple definitions Of tag '" + t.Name + "'");
+ continue;
+ }
+
+ TagInfo tagInfo;
+ if (convertCommonType(t, tagInfo, t.Name))
+ continue;
+
+ Writer->addTag(t.Name, tagInfo, swiftVersion);
+ }
+
+ // Write all typedefs.
+ llvm::StringSet<> knownTypedefs;
+ for (const auto &t : items.Typedefs) {
+ // Check for duplicate typedef definitions.
+ if (!knownTypedefs.insert(t.Name).second) {
+ emitError("multiple definitions of typedef '" + t.Name + "'");
+ continue;
+ }
+
+ TypedefInfo typedefInfo;
+ if (convertCommonType(t, typedefInfo, t.Name))
+ continue;
+ typedefInfo.SwiftWrapper = t.SwiftWrapper;
+
+ Writer->addTypedef(t.Name, typedefInfo, swiftVersion);
+ }
+ }
+
+ bool convertModule() {
+ if (!isAvailable(TheModule.Availability))
+ return false;
+
+ // Set up the writer.
+ // FIXME: This is kindof ugly.
+ APINotesWriter writer(TheModule.Name, SourceFile);
+ Writer = &writer;
+
+ // Write the top-level items.
+ convertTopLevelItems(TheModule.TopLevel, VersionTuple());
+
+ if (TheModule.SwiftInferImportAsMember) {
+ ModuleOptions opts;
+ opts.SwiftInferImportAsMember = true;
+ Writer->addModuleOptions(opts);
+ }
+
+ // Convert the versioned information.
+ for (const auto &versioned : TheModule.SwiftVersions) {
+ convertTopLevelItems(versioned.Items, versioned.Version);
+ }
+
+ if (!ErrorOccured)
+ Writer->writeToStream(OS);
+
+ return ErrorOccured;
+ }
+ };
+}
+
+static bool compile(const Module &module,
+ const FileEntry *sourceFile,
+ llvm::raw_ostream &os,
+ api_notes::OSType targetOS,
+ llvm::SourceMgr::DiagHandlerTy diagHandler,
+ void *diagHandlerCtxt){
+ using namespace api_notes;
+
+ YAMLConverter c(module, sourceFile, targetOS, os, diagHandler, diagHandlerCtxt);
+ return c.convertModule();
+}
+
+bool api_notes::parseAndDumpAPINotes(StringRef yamlInput) {
+ Module module;
+
+ if (parseAPINotes(yamlInput, module, nullptr, nullptr))
+ return true;
+
+ Output yout(llvm::outs());
+ yout << module;
+
+ return false;
+}
+
+/// Simple diagnostic handler that prints diagnostics to standard error.
+static void printDiagnostic(const llvm::SMDiagnostic &diag, void *context) {
+ diag.print(nullptr, llvm::errs());
+}
+
+bool api_notes::compileAPINotes(StringRef yamlInput,
+ const FileEntry *sourceFile,
+ llvm::raw_ostream &os,
+ OSType targetOS,
+ llvm::SourceMgr::DiagHandlerTy diagHandler,
+ void *diagHandlerCtxt) {
+ Module module;
+
+ if (!diagHandler) {
+ diagHandler = &printDiagnostic;
+ }
+
+ if (parseAPINotes(yamlInput, module, diagHandler, diagHandlerCtxt))
+ return true;
+
+ return compile(module, sourceFile, os, targetOS, diagHandler, diagHandlerCtxt);
+}
+
+namespace {
+ // Deserialize the API notes file into a module.
+ class DecompileVisitor : public APINotesReader::Visitor {
+ /// Allocator used to clone those strings that need it.
+ llvm::BumpPtrAllocator Allocator;
+
+ /// The module we're building.
+ Module TheModule;
+
+ /// A known context, which tracks what we know about a context ID.
+ struct KnownContext {
+ /// Whether this is a protocol (vs. a class).
+ bool isProtocol;
+
+ /// The indices into the top-level items for this context at each
+ /// Swift version.
+ SmallVector<std::pair<VersionTuple, unsigned>, 1> indices;
+
+ Class &getContext(const VersionTuple &swiftVersion,
+ TopLevelItems &items) {
+ ClassesSeq &seq = isProtocol ? items.Protocols : items.Classes;
+
+ for (auto &index : indices) {
+ if (index.first == swiftVersion)
+ return seq[index.second];
+ }
+
+ indices.push_back({swiftVersion, seq.size()});
+ seq.push_back(Class());
+ return seq.back();
+ }
+ };
+
+ /// A mapping from context ID to a pair (index, is-protocol) that indicates
+ /// the index of that class or protocol in the global "classes" or
+ /// "protocols" list.
+ llvm::DenseMap<unsigned, KnownContext> knownContexts;
+
+ /// Copy a string into allocated memory so it does disappear on us.
+ StringRef copyString(StringRef string) {
+ if (string.empty()) return StringRef();
+
+ void *ptr = Allocator.Allocate(string.size(), 1);
+ memcpy(ptr, string.data(), string.size());
+ return StringRef(reinterpret_cast<const char *>(ptr), string.size());
+ }
+
+ /// Copy an optional string into allocated memory so it does disappear on us.
+ Optional<StringRef> maybeCopyString(Optional<StringRef> string) {
+ if (!string) return None;
+
+ return copyString(*string);
+ }
+
+ /// Copy an optional string into allocated memory so it does disappear on us.
+ Optional<StringRef> maybeCopyString(Optional<std::string> string) {
+ if (!string) return None;
+
+ return copyString(*string);
+ }
+
+ template<typename T>
+ void handleCommon(T &record, const CommonEntityInfo &info) {
+ handleAvailability(record.Availability, info);
+ record.SwiftPrivate = info.isSwiftPrivate();
+ record.SwiftName = copyString(info.SwiftName);
+ }
+
+ template<typename T>
+ void handleCommonType(T &record, const CommonTypeInfo &info) {
+ handleCommon(record, info);
+ record.SwiftBridge = maybeCopyString(info.getSwiftBridge());
+ record.NSErrorDomain = maybeCopyString(info.getNSErrorDomain());
+ }
+
+ /// Map Objective-C context info.
+ void handleObjCContext(Class &record, StringRef name,
+ const ObjCContextInfo &info) {
+ record.Name = name;
+
+ handleCommonType(record, info);
+
+ if (info.getDefaultNullability()) {
+ record.AuditedForNullability = true;
+ }
+ }
+
+ /// Map availability information, if present.
+ void handleAvailability(AvailabilityItem &availability,
+ const CommonEntityInfo &info) {
+ if (info.Unavailable) {
+ availability.Mode = APIAvailability::None;
+ availability.Msg = copyString(info.UnavailableMsg);
+ }
+
+ if (info.UnavailableInSwift) {
+ availability.Mode = APIAvailability::NonSwift;
+ availability.Msg = copyString(info.UnavailableMsg);
+ }
+ }
+
+ /// Map parameter information for a function.
+ void handleParameters(ParamsSeq ¶ms,
+ const FunctionInfo &info) {
+ unsigned position = 0;
+ for (const auto &pi: info.Params) {
+ Param p;
+ p.Position = position++;
+ p.Nullability = pi.getNullability();
+ p.NoEscape = pi.isNoEscape();
+ p.Type = copyString(pi.getType());
+ params.push_back(p);
+ }
+ }
+
+ /// Map nullability information for a function.
+ void handleNullability(NullabilitySeq &nullability,
+ llvm::Optional<NullabilityKind> &nullabilityOfRet,
+ const FunctionInfo &info,
+ unsigned numParams) {
+ if (info.NullabilityAudited) {
+ nullabilityOfRet = info.getReturnTypeInfo();
+
+ // Figure out the number of parameters from the selector.
+ for (unsigned i = 0; i != numParams; ++i)
+ nullability.push_back(info.getParamTypeInfo(i));
+ }
+ }
+
+ TopLevelItems &getTopLevelItems(VersionTuple swiftVersion) {
+ if (!swiftVersion) return TheModule.TopLevel;
+
+ for (auto &versioned : TheModule.SwiftVersions) {
+ if (versioned.Version == swiftVersion)
+ return versioned.Items;
+ }
+
+ TheModule.SwiftVersions.push_back(Versioned());
+ TheModule.SwiftVersions.back().Version = swiftVersion;
+ return TheModule.SwiftVersions.back().Items;
+ }
+
+ public:
+ virtual void visitObjCClass(ContextID contextID, StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) {
+ // Record this known context.
+ auto &items = getTopLevelItems(swiftVersion);
+ auto &known = knownContexts[contextID.Value];
+ known.isProtocol = false;
+
+ handleObjCContext(known.getContext(swiftVersion, items), name, info);
+ }
+
+ virtual void visitObjCProtocol(ContextID contextID, StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) {
+ // Record this known context.
+ auto &items = getTopLevelItems(swiftVersion);
+ auto &known = knownContexts[contextID.Value];
+ known.isProtocol = true;
+
+ handleObjCContext(known.getContext(swiftVersion, items), name, info);
+ }
+
+ virtual void visitObjCMethod(ContextID contextID, StringRef selector,
+ bool isInstanceMethod,
+ const ObjCMethodInfo &info,
+ VersionTuple swiftVersion) {
+ Method method;
+ method.Selector = copyString(selector);
+ method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class;
+
+ handleCommon(method, info);
+ handleParameters(method.Params, info);
+ handleNullability(method.Nullability, method.NullabilityOfRet, info,
+ selector.count(':'));
+ method.FactoryAsInit = info.getFactoryAsInitKind();
+ method.DesignatedInit = info.DesignatedInit;
+ method.Required = info.Required;
+ method.ResultType = copyString(info.ResultType);
+ auto &items = getTopLevelItems(swiftVersion);
+ knownContexts[contextID.Value].getContext(swiftVersion, items)
+ .Methods.push_back(method);
+ }
+
+ virtual void visitObjCProperty(ContextID contextID, StringRef name,
+ bool isInstance,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion) {
+ Property property;
+ property.Name = name;
+ property.Kind = isInstance ? MethodKind::Instance : MethodKind::Class;
+ handleCommon(property, info);
+
+ // FIXME: No way to represent "not audited for nullability".
+ if (auto nullability = info.getNullability()) {
+ property.Nullability = *nullability;
+ }
+
+ property.SwiftImportAsAccessors = info.getSwiftImportAsAccessors();
+
+ property.Type = copyString(info.getType());
+
+ auto &items = getTopLevelItems(swiftVersion);
+ knownContexts[contextID.Value].getContext(swiftVersion, items)
+ .Properties.push_back(property);
+ }
+
+ virtual void visitGlobalFunction(StringRef name,
+ const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion) {
+ Function function;
+ function.Name = name;
+ handleCommon(function, info);
+ handleParameters(function.Params, info);
+ if (info.NumAdjustedNullable > 0)
+ handleNullability(function.Nullability, function.NullabilityOfRet,
+ info, info.NumAdjustedNullable-1);
+ function.ResultType = copyString(info.ResultType);
+ auto &items = getTopLevelItems(swiftVersion);
+ items.Functions.push_back(function);
+ }
+
+ virtual void visitGlobalVariable(StringRef name,
+ const GlobalVariableInfo &info,
+ VersionTuple swiftVersion) {
+ GlobalVariable global;
+ global.Name = name;
+ handleCommon(global, info);
+
+ // FIXME: No way to represent "not audited for nullability".
+ if (auto nullability = info.getNullability()) {
+ global.Nullability = *nullability;
+ }
+ global.Type = copyString(info.getType());
+
+ auto &items = getTopLevelItems(swiftVersion);
+ items.Globals.push_back(global);
+ }
+
+ virtual void visitEnumConstant(StringRef name,
+ const EnumConstantInfo &info,
+ VersionTuple swiftVersion) {
+ EnumConstant enumConstant;
+ enumConstant.Name = name;
+ handleCommon(enumConstant, info);
+
+ auto &items = getTopLevelItems(swiftVersion);
+ items.EnumConstants.push_back(enumConstant);
+ }
+
+ virtual void visitTag(StringRef name, const TagInfo &info,
+ VersionTuple swiftVersion) {
+ Tag tag;
+ tag.Name = name;
+ handleCommonType(tag, info);
+ auto &items = getTopLevelItems(swiftVersion);
+ items.Tags.push_back(tag);
+ }
+
+ virtual void visitTypedef(StringRef name, const TypedefInfo &info,
+ VersionTuple swiftVersion) {
+ Typedef td;
+ td.Name = name;
+ handleCommonType(td, info);
+ td.SwiftWrapper = info.SwiftWrapper;
+ auto &items = getTopLevelItems(swiftVersion);
+ items.Typedefs.push_back(td);
+ }
+
+ /// Retrieve the module.
+ Module &getModule() { return TheModule; }
+ };
+}
+
+/// Produce a flattened, numeric value for optional method/property kinds.
+static unsigned flattenPropertyKind(llvm::Optional<MethodKind> kind) {
+ return kind ? (*kind == MethodKind::Instance ? 2 : 1) : 0;
+}
+
+/// Sort the items in the given block of "top-level" items.
+static void sortTopLevelItems(TopLevelItems &items) {
+ // Sort classes.
+ std::sort(items.Classes.begin(), items.Classes.end(),
+ [](const Class &lhs, const Class &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort protocols.
+ std::sort(items.Protocols.begin(), items.Protocols.end(),
+ [](const Class &lhs, const Class &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort methods and properties within each class and protocol.
+ auto sortMembers = [](Class &record) {
+ // Sort properties.
+ std::sort(record.Properties.begin(), record.Properties.end(),
+ [](const Property &lhs, const Property &rhs) -> bool {
+ return lhs.Name < rhs.Name ||
+ (lhs.Name == rhs.Name &&
+ flattenPropertyKind(lhs.Kind) <
+ flattenPropertyKind(rhs.Kind));
+ });
+
+ // Sort methods.
+ std::sort(record.Methods.begin(), record.Methods.end(),
+ [](const Method &lhs, const Method &rhs) -> bool {
+ return lhs.Selector < rhs.Selector ||
+ (lhs.Selector == rhs.Selector &&
+ static_cast<unsigned>(lhs.Kind)
+ < static_cast<unsigned>(rhs.Kind));
+ });
+ };
+ std::for_each(items.Classes.begin(), items.Classes.end(), sortMembers);
+ std::for_each(items.Protocols.begin(), items.Protocols.end(), sortMembers);
+
+ // Sort functions.
+ std::sort(items.Functions.begin(), items.Functions.end(),
+ [](const Function &lhs, const Function &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort global variables.
+ std::sort(items.Globals.begin(), items.Globals.end(),
+ [](const GlobalVariable &lhs, const GlobalVariable &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort enum constants.
+ std::sort(items.EnumConstants.begin(), items.EnumConstants.end(),
+ [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort tags.
+ std::sort(items.Tags.begin(), items.Tags.end(),
+ [](const Tag &lhs, const Tag &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort typedefs.
+ std::sort(items.Typedefs.begin(), items.Typedefs.end(),
+ [](const Typedef &lhs, const Typedef &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+}
+
+bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input,
+ llvm::raw_ostream &os) {
+ // Try to read the file.
+ auto reader = APINotesReader::get(std::move(input), VersionTuple());
+ if (!reader) {
+ llvm::errs() << "not a well-formed API notes binary file\n";
+ return true;
+ }
+
+ DecompileVisitor decompileVisitor;
+ reader->visit(decompileVisitor);
+
+ // Sort the data in the module, because the API notes reader doesn't preserve
+ // order.
+ auto &module = decompileVisitor.getModule();
+
+ // Set module name.
+ module.Name = reader->getModuleName();
+
+ // Set module options
+ auto opts = reader->getModuleOptions();
+ if (opts.SwiftInferImportAsMember)
+ module.SwiftInferImportAsMember = true;
+
+ // Sort the top-level items.
+ sortTopLevelItems(module.TopLevel);
+
+ // Sort the Swift versions.
+ std::sort(module.SwiftVersions.begin(), module.SwiftVersions.end(),
+ [](const Versioned &lhs, const Versioned &rhs) -> bool {
+ return lhs.Version < rhs.Version;
+ });
+
+ // Sort the top-level items within each Swift version.
+ for (auto &versioned : module.SwiftVersions)
+ sortTopLevelItems(versioned.Items);
+
+ // Output the YAML representation.
+ Output yout(os);
+ yout << module;
+
+ return false;
+}
+
diff --git a/lib/APINotes/CMakeLists.txt b/lib/APINotes/CMakeLists.txt
new file mode 100644
index 0000000..da9d0d1
--- /dev/null
+++ b/lib/APINotes/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+ BitReader
+ Support
+ )
+
+add_clang_library(clangAPINotes
+ APINotesManager.cpp
+ APINotesWriter.cpp
+ APINotesReader.cpp
+ APINotesYAMLCompiler.cpp
+ Types.cpp
+
+ LINK_LIBS
+ clangBasic
+)
diff --git a/lib/APINotes/Types.cpp b/lib/APINotes/Types.cpp
new file mode 100644
index 0000000..963780f
--- /dev/null
+++ b/lib/APINotes/Types.cpp
@@ -0,0 +1,55 @@
+//===--- Types.cpp - API Notes Data Types ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines data types used in the representation of API notes data.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/Types.h"
+#include "llvm/Support/raw_ostream.h"
+
+void clang::api_notes::ObjCMethodInfo::dump(llvm::raw_ostream &os) {
+ os << DesignatedInit << " " << FactoryAsInit << " " << Unavailable << " "
+ << NullabilityAudited << " " << NumAdjustedNullable << " "
+ << NullabilityPayload << " " << UnavailableMsg << "\n";
+}
+
+void clang::api_notes::ObjCContextInfo::dump(llvm::raw_ostream &os) {
+ os << HasDefaultNullability << " " << DefaultNullability << " "
+ << HasDesignatedInits << "\n";
+}
+
+void clang::api_notes::ObjCMethodInfo::mergePropInfoIntoSetter(
+ const ObjCPropertyInfo &pInfo) {
+ // Set the type of the first argument of the the setter or check that the
+ // value we have is consistent with the property.
+ // TODO: Can we provide proper error handling here?
+ if (auto pNullability = pInfo.getNullability()) {
+ if (!NullabilityAudited) {
+ addParamTypeInfo(0, *pNullability);
+ assert(NumAdjustedNullable == 2);
+ } else {
+ assert(getParamTypeInfo(0) == *pNullability);
+ }
+ }
+}
+
+void clang::api_notes::ObjCMethodInfo::mergePropInfoIntoGetter(
+ const ObjCPropertyInfo &pInfo) {
+ // Set the return type of the getter or check that the value we have is
+ // consistent with the property.
+ // TODO: Can we provide proper error handling here?
+ if (auto pNullability = pInfo.getNullability()) {
+ if (!NullabilityAudited) {
+ addReturnTypeInfo(*pNullability);
+ assert(NumAdjustedNullable == 1);
+ } else {
+ assert(getReturnTypeInfo() == *pNullability);
+ }
+ }
+}
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index b531a66..c8cdc72 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -4078,11 +4078,6 @@
bool allowOnPointerType) const {
hasError = false;
- if (const ObjCTypeParamType *objT =
- dyn_cast<ObjCTypeParamType>(type.getTypePtr())) {
- return getObjCTypeParamType(objT->getDecl(), protocols);
- }
-
// Apply protocol qualifiers to ObjCObjectPointerType.
if (allowOnPointerType) {
if (const ObjCObjectPointerType *objPtr =
@@ -8870,22 +8865,30 @@
return GVA_Internal;
if (VD->isStaticLocal()) {
- GVALinkage StaticLocalLinkage = GVA_DiscardableODR;
const DeclContext *LexicalContext = VD->getParentFunctionOrMethod();
while (LexicalContext && !isa<FunctionDecl>(LexicalContext))
LexicalContext = LexicalContext->getLexicalParent();
- // Let the static local variable inherit its linkage from the nearest
- // enclosing function.
- if (LexicalContext)
- StaticLocalLinkage =
- Context.GetGVALinkageForFunction(cast<FunctionDecl>(LexicalContext));
+ // ObjC Blocks can create local variables that don't have a FunctionDecl
+ // LexicalContext.
+ if (!LexicalContext)
+ return GVA_DiscardableODR;
- // GVA_StrongODR function linkage is stronger than what we need,
- // downgrade to GVA_DiscardableODR.
- // This allows us to discard the variable if we never end up needing it.
- return StaticLocalLinkage == GVA_StrongODR ? GVA_DiscardableODR
- : StaticLocalLinkage;
+ // Otherwise, let the static local variable inherit its linkage from the
+ // nearest enclosing function.
+ auto StaticLocalLinkage =
+ Context.GetGVALinkageForFunction(cast<FunctionDecl>(LexicalContext));
+
+ // Itanium ABI 5.2.2: "Each COMDAT group [for a static local variable] must
+ // be emitted in any object with references to the symbol for the object it
+ // contains, whether inline or out-of-line."
+ // Similar behavior is observed with MSVC. An alternative ABI could use
+ // StrongODR/AvailableExternally to match the function, but none are
+ // known/supported currently.
+ if (StaticLocalLinkage == GVA_StrongODR ||
+ StaticLocalLinkage == GVA_AvailableExternally)
+ return GVA_DiscardableODR;
+ return StaticLocalLinkage;
}
// MSVC treats in-class initialized static data members as definitions.
diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp
index 60d05f6..5d894c5 100644
--- a/lib/AST/DeclObjC.cpp
+++ b/lib/AST/DeclObjC.cpp
@@ -1329,12 +1329,8 @@
IdentifierInfo *name,
SourceLocation colonLoc,
TypeSourceInfo *boundInfo) {
- auto *TPDecl =
- new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index,
- nameLoc, name, colonLoc, boundInfo);
- QualType TPType = ctx.getObjCTypeParamType(TPDecl, {});
- TPDecl->setTypeForDecl(TPType.getTypePtr());
- return TPDecl;
+ return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index,
+ nameLoc, name, colonLoc, boundInfo);
}
ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx,
diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp
index b8ebe1c..b8d90b0 100644
--- a/lib/AST/DeclPrinter.cpp
+++ b/lib/AST/DeclPrinter.cpp
@@ -1231,6 +1231,9 @@
return;
}
bool eolnOut = false;
+ prettyPrintAttributes(OID);
+ if (OID->hasAttrs()) Out << "\n";
+
Out << "@interface " << I;
if (auto TypeParams = OID->getTypeParamListAsWritten()) {
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index 0d0cd2e..f3724aa 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -1081,24 +1081,13 @@
// Replace an Objective-C type parameter reference with the corresponding
// type argument.
- if (const auto *OTPTy = dyn_cast<ObjCTypeParamType>(splitType.Ty)) {
- if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(OTPTy->getDecl())) {
+ if (const auto *typedefTy = dyn_cast<TypedefType>(splitType.Ty)) {
+ if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(typedefTy->getDecl())) {
// If we have type arguments, use them.
if (!typeArgs.empty()) {
+ // FIXME: Introduce SubstObjCTypeParamType ?
QualType argType = typeArgs[typeParam->getIndex()];
- if (OTPTy->qual_empty())
- return ctx.getQualifiedType(argType, splitType.Quals);
-
- // Apply protocol lists if exists.
- bool hasError;
- SmallVector<ObjCProtocolDecl*, 8> protocolsVec;
- protocolsVec.append(OTPTy->qual_begin(),
- OTPTy->qual_end());
- ArrayRef<ObjCProtocolDecl *> protocolsToApply = protocolsVec;
- QualType resultTy = ctx.applyObjCProtocolQualifiers(argType,
- protocolsToApply, hasError, true/*allowOnPointerType*/);
-
- return ctx.getQualifiedType(resultTy, splitType.Quals);
+ return ctx.getQualifiedType(argType, splitType.Quals);
}
switch (context) {
diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp
index 8c126b0..6d9530b 100644
--- a/lib/Analysis/CallGraph.cpp
+++ b/lib/Analysis/CallGraph.cpp
@@ -62,6 +62,7 @@
void VisitCallExpr(CallExpr *CE) {
if (Decl *D = getDeclFromCall(CE))
addCalledDecl(D);
+ VisitChildren(CE);
}
// Adds may-call edges for the ObjC message sends.
diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt
index 8929ec3..b6a289a 100644
--- a/lib/Basic/CMakeLists.txt
+++ b/lib/Basic/CMakeLists.txt
@@ -74,6 +74,7 @@
FileSystemStatCache.cpp
IdentifierTable.cpp
LangOptions.cpp
+ MemoryBufferCache.cpp
Module.cpp
ObjCRuntime.cpp
OpenMPKinds.cpp
@@ -82,6 +83,7 @@
Sanitizers.cpp
SourceLocation.cpp
SourceManager.cpp
+ SourceMgrAdapter.cpp
TargetInfo.cpp
Targets.cpp
TokenKinds.cpp
diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp
index 7529c47..0c27ae4 100644
--- a/lib/Basic/Diagnostic.cpp
+++ b/lib/Basic/Diagnostic.cpp
@@ -16,6 +16,7 @@
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/PartialDiagnostic.h"
+#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/CrashRecoveryContext.h"
@@ -131,13 +132,13 @@
// Clear state related to #pragma diagnostic.
DiagStates.clear();
- DiagStatePoints.clear();
+ DiagStatesByLoc.clear();
DiagStateOnPushStack.clear();
// Create a DiagState and DiagStatePoint representing diagnostic changes
// through command-line.
DiagStates.emplace_back();
- DiagStatePoints.push_back(DiagStatePoint(&DiagStates.back(), FullSourceLoc()));
+ DiagStatesByLoc.appendFirst(&DiagStates.back());
}
void DiagnosticsEngine::SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1,
@@ -157,27 +158,94 @@
DelayedDiagArg2.clear();
}
-DiagnosticsEngine::DiagStatePointsTy::iterator
-DiagnosticsEngine::GetDiagStatePointForLoc(SourceLocation L) const {
- assert(!DiagStatePoints.empty());
- assert(DiagStatePoints.front().Loc.isInvalid() &&
- "Should have created a DiagStatePoint for command-line");
+void DiagnosticsEngine::DiagStateMap::appendFirst(
+ DiagState *State) {
+ assert(Files.empty() && "not first");
+ FirstDiagState = CurDiagState = State;
+ CurDiagStateLoc = SourceLocation();
+}
- if (!SourceMgr)
- return DiagStatePoints.end() - 1;
+void DiagnosticsEngine::DiagStateMap::append(SourceManager &SrcMgr,
+ SourceLocation Loc,
+ DiagState *State) {
+ CurDiagState = State;
+ CurDiagStateLoc = Loc;
- FullSourceLoc Loc(L, *SourceMgr);
- if (Loc.isInvalid())
- return DiagStatePoints.end() - 1;
+ std::pair<FileID, unsigned> Decomp = SrcMgr.getDecomposedLoc(Loc);
+ unsigned Offset = Decomp.second;
+ for (File *F = getFile(SrcMgr, Decomp.first); F;
+ Offset = F->ParentOffset, F = F->Parent) {
+ F->HasLocalTransitions = true;
+ auto &Last = F->StateTransitions.back();
+ assert(Last.Offset <= Offset && "state transitions added out of order");
- DiagStatePointsTy::iterator Pos = DiagStatePoints.end();
- FullSourceLoc LastStateChangePos = DiagStatePoints.back().Loc;
- if (LastStateChangePos.isValid() &&
- Loc.isBeforeInTranslationUnitThan(LastStateChangePos))
- Pos = std::upper_bound(DiagStatePoints.begin(), DiagStatePoints.end(),
- DiagStatePoint(nullptr, Loc));
- --Pos;
- return Pos;
+ if (Last.Offset == Offset) {
+ if (Last.State == State)
+ break;
+ Last.State = State;
+ continue;
+ }
+
+ F->StateTransitions.push_back({State, Offset});
+ }
+}
+
+DiagnosticsEngine::DiagState *
+DiagnosticsEngine::DiagStateMap::lookup(SourceManager &SrcMgr,
+ SourceLocation Loc) const {
+ // Common case: we have not seen any diagnostic pragmas.
+ if (Files.empty())
+ return FirstDiagState;
+
+ std::pair<FileID, unsigned> Decomp = SrcMgr.getDecomposedLoc(Loc);
+ const File *F = getFile(SrcMgr, Decomp.first);
+ return F->lookup(Decomp.second);
+}
+
+DiagnosticsEngine::DiagState *
+DiagnosticsEngine::DiagStateMap::File::lookup(unsigned Offset) const {
+ auto OnePastIt = std::upper_bound(
+ StateTransitions.begin(), StateTransitions.end(), Offset,
+ [](unsigned Offset, const DiagStatePoint &P) {
+ return Offset < P.Offset;
+ });
+ assert(OnePastIt != StateTransitions.begin() && "missing initial state");
+ return OnePastIt[-1].State;
+}
+
+DiagnosticsEngine::DiagStateMap::File *
+DiagnosticsEngine::DiagStateMap::getFile(SourceManager &SrcMgr,
+ FileID ID) const {
+ // Get or insert the File for this ID.
+ auto Range = Files.equal_range(ID);
+ if (Range.first != Range.second)
+ return &Range.first->second;
+ auto &F = Files.insert(Range.first, std::make_pair(ID, File()))->second;
+
+ // We created a new File; look up the diagnostic state at the start of it and
+ // initialize it.
+ if (ID.isValid()) {
+ std::pair<FileID, unsigned> Decomp = SrcMgr.getDecomposedIncludedLoc(ID);
+ F.Parent = getFile(SrcMgr, Decomp.first);
+ F.ParentOffset = Decomp.second;
+ F.StateTransitions.push_back({F.Parent->lookup(Decomp.second), 0});
+ } else {
+ // This is the (imaginary) root file into which we pretend all top-level
+ // files are included; it descends from the initial state.
+ //
+ // FIXME: This doesn't guarantee that we use the same ordering as
+ // isBeforeInTranslationUnit in the cases where someone invented another
+ // top-level file and added diagnostic pragmas to it. See the code at the
+ // end of isBeforeInTranslationUnit for the quirks it deals with.
+ F.StateTransitions.push_back({FirstDiagState, 0});
+ }
+ return &F;
+}
+
+void DiagnosticsEngine::PushDiagStatePoint(DiagState *State,
+ SourceLocation Loc) {
+ assert(Loc.isValid() && "Adding invalid loc point");
+ DiagStatesByLoc.append(*SourceMgr, Loc, State);
}
void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map,
@@ -187,11 +255,8 @@
assert((Diags->isBuiltinWarningOrExtension(Diag) ||
(Map == diag::Severity::Fatal || Map == diag::Severity::Error)) &&
"Cannot map errors into warnings!");
- assert(!DiagStatePoints.empty());
assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location");
- FullSourceLoc Loc = SourceMgr? FullSourceLoc(L, *SourceMgr) : FullSourceLoc();
- FullSourceLoc LastStateChangePos = DiagStatePoints.back().Loc;
// Don't allow a mapping to a warning override an error/fatal mapping.
if (Map == diag::Severity::Warning) {
DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
@@ -202,50 +267,22 @@
DiagnosticMapping Mapping = makeUserMapping(Map, L);
// Common case; setting all the diagnostics of a group in one place.
- if (Loc.isInvalid() || Loc == LastStateChangePos) {
- GetCurDiagState()->setMapping(Diag, Mapping);
+ if ((L.isInvalid() || L == DiagStatesByLoc.getCurDiagStateLoc()) &&
+ DiagStatesByLoc.getCurDiagState()) {
+ // FIXME: This is theoretically wrong: if the current state is shared with
+ // some other location (via push/pop) we will change the state for that
+ // other location as well. This cannot currently happen, as we can't update
+ // the diagnostic state at the same location at which we pop.
+ DiagStatesByLoc.getCurDiagState()->setMapping(Diag, Mapping);
return;
}
- // Another common case; modifying diagnostic state in a source location
- // after the previous one.
- if ((Loc.isValid() && LastStateChangePos.isInvalid()) ||
- LastStateChangePos.isBeforeInTranslationUnitThan(Loc)) {
- // A diagnostic pragma occurred, create a new DiagState initialized with
- // the current one and a new DiagStatePoint to record at which location
- // the new state became active.
- DiagStates.push_back(*GetCurDiagState());
- PushDiagStatePoint(&DiagStates.back(), Loc);
- GetCurDiagState()->setMapping(Diag, Mapping);
- return;
- }
-
- // We allow setting the diagnostic state in random source order for
- // completeness but it should not be actually happening in normal practice.
-
- DiagStatePointsTy::iterator Pos = GetDiagStatePointForLoc(Loc);
- assert(Pos != DiagStatePoints.end());
-
- // Update all diagnostic states that are active after the given location.
- for (DiagStatePointsTy::iterator
- I = Pos+1, E = DiagStatePoints.end(); I != E; ++I) {
- I->State->setMapping(Diag, Mapping);
- }
-
- // If the location corresponds to an existing point, just update its state.
- if (Pos->Loc == Loc) {
- Pos->State->setMapping(Diag, Mapping);
- return;
- }
-
- // Create a new state/point and fit it into the vector of DiagStatePoints
- // so that the vector is always ordered according to location.
- assert(Pos->Loc.isBeforeInTranslationUnitThan(Loc));
- DiagStates.push_back(*Pos->State);
- DiagState *NewState = &DiagStates.back();
- NewState->setMapping(Diag, Mapping);
- DiagStatePoints.insert(Pos+1, DiagStatePoint(NewState,
- FullSourceLoc(Loc, *SourceMgr)));
+ // A diagnostic pragma occurred, create a new DiagState initialized with
+ // the current one and a new DiagStatePoint to record at which location
+ // the new state became active.
+ DiagStates.push_back(*GetCurDiagState());
+ DiagStates.back().setMapping(Diag, Mapping);
+ PushDiagStatePoint(&DiagStates.back(), L);
}
bool DiagnosticsEngine::setSeverityForGroup(diag::Flavor Flavor,
diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp
index 3c370f6..e0580af 100644
--- a/lib/Basic/DiagnosticIDs.cpp
+++ b/lib/Basic/DiagnosticIDs.cpp
@@ -411,11 +411,8 @@
// to error. Errors can only be mapped to fatal.
diag::Severity Result = diag::Severity::Fatal;
- DiagnosticsEngine::DiagStatePointsTy::iterator
- Pos = Diag.GetDiagStatePointForLoc(Loc);
- DiagnosticsEngine::DiagState *State = Pos->State;
-
// Get the mapping information, or compute it lazily.
+ DiagnosticsEngine::DiagState *State = Diag.GetDiagStateForLoc(Loc);
DiagnosticMapping &Mapping = State->getOrAddMapping((diag::kind)DiagID);
// TODO: Can a null severity really get here?
diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp
index af424cd..851b3ab 100644
--- a/lib/Basic/IdentifierTable.cpp
+++ b/lib/Basic/IdentifierTable.cpp
@@ -42,7 +42,6 @@
NeedsHandleIdentifier = false;
IsFromAST = false;
ChangedAfterLoad = false;
- FEChangedAfterLoad = false;
RevertedTokenID = false;
OutOfDate = false;
IsModulesImport = false;
diff --git a/lib/Basic/MemoryBufferCache.cpp b/lib/Basic/MemoryBufferCache.cpp
new file mode 100644
index 0000000..f4b751c
--- /dev/null
+++ b/lib/Basic/MemoryBufferCache.cpp
@@ -0,0 +1,48 @@
+//===- MemoryBufferCache.cpp - Cache for loaded memory buffers ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/MemoryBufferCache.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace clang;
+
+llvm::MemoryBuffer &
+MemoryBufferCache::addBuffer(llvm::StringRef Filename,
+ std::unique_ptr<llvm::MemoryBuffer> Buffer) {
+ auto Insertion = Buffers.insert(std::make_pair(
+ Filename, BufferEntry{std::move(Buffer), NextIndex++}));
+ assert(Insertion.second && "Already has a buffer");
+ return *Insertion.first->second.Buffer;
+}
+
+llvm::MemoryBuffer *MemoryBufferCache::lookupBuffer(llvm::StringRef Filename) {
+ auto I = Buffers.find(Filename);
+ if (I == Buffers.end())
+ return nullptr;
+ return I->second.Buffer.get();
+}
+
+bool MemoryBufferCache::isBufferFinal(llvm::StringRef Filename) {
+ auto I = Buffers.find(Filename);
+ if (I == Buffers.end())
+ return false;
+ return I->second.Index < FirstRemovableIndex;
+}
+
+bool MemoryBufferCache::tryToRemoveBuffer(llvm::StringRef Filename) {
+ auto I = Buffers.find(Filename);
+ assert(I != Buffers.end() && "No buffer to remove...");
+ if (I->second.Index < FirstRemovableIndex)
+ return true;
+
+ Buffers.erase(I);
+ return false;
+}
+
+void MemoryBufferCache::finalizeCurrentBuffers() { FirstRemovableIndex = NextIndex; }
diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp
index 80bbc24..a410a72 100644
--- a/lib/Basic/Module.cpp
+++ b/lib/Basic/Module.cpp
@@ -27,11 +27,12 @@
Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
bool IsFramework, bool IsExplicit, unsigned VisibilityID)
: Name(Name), DefinitionLoc(DefinitionLoc), Parent(Parent), Directory(),
- Umbrella(), Signature(0), ASTFile(nullptr), VisibilityID(VisibilityID),
+ Umbrella(), ASTFile(nullptr), VisibilityID(VisibilityID),
IsMissingRequirement(false), HasIncompatibleModuleFile(false),
IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework),
IsExplicit(IsExplicit), IsSystem(false), IsExternC(false),
- IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false),
+ IsInferred(false), IsSwiftInferImportAsMember(false),
+ InferSubmodules(false), InferExplicitSubmodules(false),
InferExportWildcard(false), ConfigMacrosExhaustive(false),
NoUndeclaredIncludes(false), NameVisibility(Hidden) {
if (Parent) {
@@ -83,11 +84,16 @@
bool Module::isAvailable(const LangOptions &LangOpts, const TargetInfo &Target,
Requirement &Req,
- UnresolvedHeaderDirective &MissingHeader) const {
+ UnresolvedHeaderDirective &MissingHeader,
+ Module *&ShadowingModule) const {
if (IsAvailable)
return true;
for (const Module *Current = this; Current; Current = Current->Parent) {
+ if (Current->ShadowingModule) {
+ ShadowingModule = Current->ShadowingModule;
+ return false;
+ }
for (unsigned I = 0, N = Current->Requirements.size(); I != N; ++I) {
if (hasFeature(Current->Requirements[I].first, LangOpts, Target) !=
Current->Requirements[I].second) {
@@ -341,6 +347,8 @@
OS << " [system]";
if (IsExternC)
OS << " [extern_c]";
+ if (IsSwiftInferImportAsMember)
+ OS << " [swift_infer_import_as_member]";
}
OS << " {\n";
diff --git a/lib/Basic/SourceMgrAdapter.cpp b/lib/Basic/SourceMgrAdapter.cpp
new file mode 100644
index 0000000..1d52a24
--- /dev/null
+++ b/lib/Basic/SourceMgrAdapter.cpp
@@ -0,0 +1,137 @@
+//=== SourceMgrAdapter.cpp - SourceMgr to SourceManager Adapter -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the adapter that maps diagnostics from llvm::SourceMgr
+// to Clang's SourceManager.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceMgrAdapter.h"
+#include "clang/Basic/Diagnostic.h"
+
+using namespace clang;
+
+void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &diag,
+ void *context) {
+ static_cast<SourceMgrAdapter *>(context)->handleDiag(diag);
+}
+
+SourceMgrAdapter::SourceMgrAdapter(SourceManager &srcMgr,
+ DiagnosticsEngine &diag,
+ unsigned errorDiagID,
+ unsigned warningDiagID,
+ unsigned noteDiagID,
+ const FileEntry *defaultFile)
+ : SrcMgr(srcMgr), Diag(diag), ErrorDiagID(errorDiagID),
+ WarningDiagID(warningDiagID), NoteDiagID(noteDiagID),
+ DefaultFile(defaultFile) { }
+
+SourceMgrAdapter::~SourceMgrAdapter() { }
+
+SourceLocation SourceMgrAdapter::mapLocation(const llvm::SourceMgr &llvmSrcMgr,
+ llvm::SMLoc loc) {
+ // Map invalid locations.
+ if (!loc.isValid())
+ return SourceLocation();
+
+ // Find the buffer containing the location.
+ unsigned bufferID = llvmSrcMgr.FindBufferContainingLoc(loc);
+ if (!bufferID)
+ return SourceLocation();
+
+
+ // If we haven't seen this buffer before, copy it over.
+ auto buffer = llvmSrcMgr.getMemoryBuffer(bufferID);
+ auto knownBuffer = FileIDMapping.find(std::make_pair(&llvmSrcMgr, bufferID));
+ if (knownBuffer == FileIDMapping.end()) {
+ FileID fileID;
+ if (DefaultFile) {
+ // Map to the default file.
+ fileID = SrcMgr.createFileID(DefaultFile, SourceLocation(),
+ SrcMgr::C_User);
+
+ // Only do this once.
+ DefaultFile = nullptr;
+ } else {
+ // Make a copy of the memory buffer.
+ StringRef bufferName = buffer->getBufferIdentifier();
+ auto bufferCopy
+ = std::unique_ptr<llvm::MemoryBuffer>(
+ llvm::MemoryBuffer::getMemBufferCopy(buffer->getBuffer(),
+ bufferName));
+
+ // Add this memory buffer to the Clang source manager.
+ fileID = SrcMgr.createFileID(std::move(bufferCopy));
+ }
+
+ // Save the mapping.
+ knownBuffer = FileIDMapping.insert(
+ std::make_pair(std::make_pair(&llvmSrcMgr, bufferID),
+ fileID)).first;
+ }
+
+ // Translate the offset into the file.
+ unsigned offset = loc.getPointer() - buffer->getBufferStart();
+ return SrcMgr.getLocForStartOfFile(knownBuffer->second)
+ .getLocWithOffset(offset);
+}
+
+SourceRange SourceMgrAdapter::mapRange(const llvm::SourceMgr &llvmSrcMgr,
+ llvm::SMRange range) {
+ if (!range.isValid())
+ return SourceRange();
+
+ SourceLocation start = mapLocation(llvmSrcMgr, range.Start);
+ SourceLocation end = mapLocation(llvmSrcMgr, range.End);
+ return SourceRange(start, end);
+}
+
+void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &diag) {
+ // Map the location.
+ SourceLocation loc;
+ if (auto *llvmSrcMgr = diag.getSourceMgr())
+ loc = mapLocation(*llvmSrcMgr, diag.getLoc());
+
+ // Extract the message.
+ StringRef message = diag.getMessage();
+
+ // Map the diagnostic kind.
+ unsigned diagID;
+ switch (diag.getKind()) {
+ case llvm::SourceMgr::DK_Error:
+ diagID = ErrorDiagID;
+ break;
+
+ case llvm::SourceMgr::DK_Warning:
+ diagID = WarningDiagID;
+ break;
+
+ case llvm::SourceMgr::DK_Note:
+ diagID = NoteDiagID;
+ break;
+ }
+
+ // Report the diagnostic.
+ DiagnosticBuilder builder = Diag.Report(loc, diagID) << message;
+
+ if (auto *llvmSrcMgr = diag.getSourceMgr()) {
+ // Translate ranges.
+ SourceLocation startOfLine = loc.getLocWithOffset(-diag.getColumnNo());
+ for (auto range : diag.getRanges()) {
+ builder << SourceRange(startOfLine.getLocWithOffset(range.first),
+ startOfLine.getLocWithOffset(range.second));
+ }
+
+ // Translate Fix-Its.
+ for (const llvm::SMFixIt &fixIt : diag.getFixIts()) {
+ CharSourceRange range(mapRange(*llvmSrcMgr, fixIt.getRange()), false);
+ builder << FixItHint::CreateReplacement(range, fixIt.getText());
+ }
+ }
+}
diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp
index 1a95ff2..fdedd91 100644
--- a/lib/Basic/Targets.cpp
+++ b/lib/Basic/Targets.cpp
@@ -8976,11 +8976,19 @@
return new SPIR64TargetInfo(Triple, Opts);
}
case llvm::Triple::wasm32:
- if (!(Triple == llvm::Triple("wasm32-unknown-unknown")))
+ if (Triple.getSubArch() != llvm::Triple::NoSubArch ||
+ Triple.getVendor() != llvm::Triple::UnknownVendor ||
+ Triple.getOS() != llvm::Triple::UnknownOS ||
+ Triple.getEnvironment() != llvm::Triple::UnknownEnvironment ||
+ !(Triple.isOSBinFormatELF() || Triple.isOSBinFormatWasm()))
return nullptr;
return new WebAssemblyOSTargetInfo<WebAssembly32TargetInfo>(Triple, Opts);
case llvm::Triple::wasm64:
- if (!(Triple == llvm::Triple("wasm64-unknown-unknown")))
+ if (Triple.getSubArch() != llvm::Triple::NoSubArch ||
+ Triple.getVendor() != llvm::Triple::UnknownVendor ||
+ Triple.getOS() != llvm::Triple::UnknownOS ||
+ Triple.getEnvironment() != llvm::Triple::UnknownEnvironment ||
+ !(Triple.isOSBinFormatELF() || Triple.isOSBinFormatWasm()))
return nullptr;
return new WebAssemblyOSTargetInfo<WebAssembly64TargetInfo>(Triple, Opts);
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index dfd819a..574c511 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,5 +1,6 @@
add_subdirectory(Headers)
add_subdirectory(Basic)
+add_subdirectory(APINotes)
add_subdirectory(Lex)
add_subdirectory(Parse)
add_subdirectory(AST)
diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp
index d2ce6ea..328ac5b 100644
--- a/lib/CodeGen/BackendUtil.cpp
+++ b/lib/CodeGen/BackendUtil.cpp
@@ -996,6 +996,7 @@
return "__LLVM,__bitcode";
case Triple::COFF:
case Triple::ELF:
+ case Triple::Wasm:
case Triple::UnknownObjectFormat:
return ".llvmbc";
}
@@ -1008,6 +1009,7 @@
return "__LLVM,__cmdline";
case Triple::COFF:
case Triple::ELF:
+ case Triple::Wasm:
case Triple::UnknownObjectFormat:
return ".llvmcmd";
}
diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp
index 12a6803..c82e778 100644
--- a/lib/CodeGen/CGDebugInfo.cpp
+++ b/lib/CodeGen/CGDebugInfo.cpp
@@ -2009,7 +2009,11 @@
if (CreateSkeletonCU && IsRootModule) {
// PCH files don't have a signature field in the control block,
// but LLVM detects skeleton CUs by looking for a non-zero DWO id.
- uint64_t Signature = Mod.getSignature() ? Mod.getSignature() : ~1ULL;
+ // We use the lower 64 bits for debug info.
+ uint64_t Signature =
+ Mod.getSignature()
+ ? (uint64_t)Mod.getSignature()[1] << 32 | Mod.getSignature()[0]
+ : ~1ULL;
llvm::DIBuilder DIB(CGM.getModule());
DIB.createCompileUnit(TheCU->getSourceLanguage(),
DIB.createFile(Mod.getModuleName(), Mod.getPath()),
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp
index 0a88b23..b7c1743 100644
--- a/lib/CodeGen/CGDecl.cpp
+++ b/lib/CodeGen/CGDecl.cpp
@@ -1022,11 +1022,21 @@
// Emit a lifetime intrinsic if meaningful. There's no point in doing this
// if we don't have a valid insertion point (?).
if (HaveInsertPoint() && !IsMSCatchParam) {
- // goto or switch-case statements can break lifetime into several
- // regions which need more efforts to handle them correctly. PR28267
- // This is rare case, but it's better just omit intrinsics than have
- // them incorrectly placed.
- if (!Bypasses.IsBypassed(&D)) {
+ // If there's a jump into the lifetime of this variable, its lifetime
+ // gets broken up into several regions in IR, which requires more work
+ // to handle correctly. For now, just omit the intrinsics; this is a
+ // rare case, and it's better to just be conservatively correct.
+ // PR28267.
+ //
+ // We have to do this in all language modes if there's a jump past the
+ // declaration. We also have to do it in C if there's a jump to an
+ // earlier point in the current block because non-VLA lifetimes begin as
+ // soon as the containing block is entered, not when its variables
+ // actually come into scope; suppressing the lifetime annotations
+ // completely in this case is unnecessarily pessimistic, but again, this
+ // is rare.
+ if (!Bypasses.IsBypassed(&D) &&
+ !(!getLangOpts().CPlusPlus && hasLabelBeenSeenInCurrentScope())) {
uint64_t size = CGM.getDataLayout().getTypeAllocSize(allocaTy);
emission.SizeForLifetimeMarkers =
EmitLifetimeStart(size, address.getPointer());
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index e5e34a5..d2cd9ed 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -3335,7 +3335,9 @@
AlignmentSource AlignSource;
Address Addr = EmitPointerWithAlignment(BaseExpr, &AlignSource);
QualType PtrTy = BaseExpr->getType()->getPointeeType();
- EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr.getPointer(), PtrTy);
+ bool SkipNullCheck = isa<llvm::ConstantPointerNull>(Addr.getPointer());
+ EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr.getPointer(), PtrTy,
+ /*Alignment=*/CharUnits::Zero(), SkipNullCheck);
BaseLV = MakeAddrLValue(Addr, PtrTy, AlignSource);
} else
BaseLV = EmitCheckedLValue(BaseExpr, TCK_MemberAccess);
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index 1b85c45..12b14be 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -2751,8 +2751,8 @@
isa<llvm::IntegerType>(Ops.LHS->getType())) {
CodeGenFunction::SanitizerScope SanScope(&CGF);
SmallVector<std::pair<Value *, SanitizerMask>, 2> Checks;
- llvm::Value *WidthMinusOne = GetWidthMinusOneValue(Ops.LHS, RHS);
- llvm::Value *ValidExponent = Builder.CreateICmpULE(RHS, WidthMinusOne);
+ llvm::Value *WidthMinusOne = GetWidthMinusOneValue(Ops.LHS, Ops.RHS);
+ llvm::Value *ValidExponent = Builder.CreateICmpULE(Ops.RHS, WidthMinusOne);
if (SanitizeExponent) {
Checks.push_back(
@@ -2767,12 +2767,14 @@
llvm::BasicBlock *Cont = CGF.createBasicBlock("cont");
llvm::BasicBlock *CheckShiftBase = CGF.createBasicBlock("check");
Builder.CreateCondBr(ValidExponent, CheckShiftBase, Cont);
+ llvm::Value *PromotedWidthMinusOne =
+ (RHS == Ops.RHS) ? WidthMinusOne
+ : GetWidthMinusOneValue(Ops.LHS, RHS);
CGF.EmitBlock(CheckShiftBase);
- llvm::Value *BitsShiftedOff =
- Builder.CreateLShr(Ops.LHS,
- Builder.CreateSub(WidthMinusOne, RHS, "shl.zeros",
- /*NUW*/true, /*NSW*/true),
- "shl.check");
+ llvm::Value *BitsShiftedOff = Builder.CreateLShr(
+ Ops.LHS, Builder.CreateSub(PromotedWidthMinusOne, RHS, "shl.zeros",
+ /*NUW*/ true, /*NSW*/ true),
+ "shl.check");
if (CGF.getLangOpts().CPlusPlus) {
// In C99, we are not permitted to shift a 1 bit into the sign bit.
// Under C++11's rules, shifting a 1 bit into the sign bit is
diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp
index 7219592..01793e2 100644
--- a/lib/CodeGen/CGObjCMac.cpp
+++ b/lib/CodeGen/CGObjCMac.cpp
@@ -7147,7 +7147,12 @@
}
assert(GV->getLinkage() == L);
- return GV;
+
+ if (IsForDefinition ||
+ GV->getValueType() == ObjCTypes.ClassnfABITy)
+ return GV;
+
+ return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.ClassnfABIPtrTy);
}
llvm::Value *
diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp
index e142a21..798d5b4 100644
--- a/lib/CodeGen/CodeGenFunction.cpp
+++ b/lib/CodeGen/CodeGenFunction.cpp
@@ -707,6 +707,11 @@
return false;
}
+static void markAsIgnoreThreadCheckingAtRuntime(llvm::Function *Fn) {
+ Fn->addFnAttr("sanitize_thread_no_checking_at_run_time");
+ Fn->removeFnAttr(llvm::Attribute::SanitizeThread);
+}
+
void CodeGenFunction::StartFunction(GlobalDecl GD,
QualType RetTy,
llvm::Function *Fn,
@@ -750,16 +755,19 @@
Fn->addFnAttr(llvm::Attribute::SafeStack);
// Ignore TSan memory acesses from within ObjC/ObjC++ dealloc, initialize,
- // .cxx_destruct and all of their calees at run time.
+ // .cxx_destruct, __destroy_helper_block_ and all of their calees at run time.
if (SanOpts.has(SanitizerKind::Thread)) {
if (const auto *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) {
IdentifierInfo *II = OMD->getSelector().getIdentifierInfoForSlot(0);
if (OMD->getMethodFamily() == OMF_dealloc ||
OMD->getMethodFamily() == OMF_initialize ||
(OMD->getSelector().isUnarySelector() && II->isStr(".cxx_destruct"))) {
- Fn->addFnAttr("sanitize_thread_no_checking_at_run_time");
- Fn->removeFnAttr(llvm::Attribute::SanitizeThread);
+ markAsIgnoreThreadCheckingAtRuntime(Fn);
}
+ } else if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) {
+ IdentifierInfo *II = FD->getIdentifier();
+ if (II && II->isStr("__destroy_helper_block_"))
+ markAsIgnoreThreadCheckingAtRuntime(Fn);
}
}
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index 5861340..11a2b95 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -212,6 +212,13 @@
/// value. This is invalid iff the function has no return value.
Address ReturnValue;
+ /// Return true if a label was seen in the current scope.
+ bool hasLabelBeenSeenInCurrentScope() const {
+ if (CurLexicalScope)
+ return CurLexicalScope->hasLabels();
+ return !LabelMap.empty();
+ }
+
/// AllocaInsertPoint - This is an instruction in the entry block before which
/// we prefer to insert allocas.
llvm::AssertingVH<llvm::Instruction> AllocaInsertPt;
@@ -620,6 +627,10 @@
rescopeLabels();
}
+ bool hasLabels() const {
+ return !Labels.empty();
+ }
+
void rescopeLabels();
};
diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp
index 3600543..a6b2559 100644
--- a/lib/CodeGen/CodeGenModule.cpp
+++ b/lib/CodeGen/CodeGenModule.cpp
@@ -3341,6 +3341,7 @@
llvm_unreachable("unknown file format");
case llvm::Triple::COFF:
case llvm::Triple::ELF:
+ case llvm::Triple::Wasm:
GV->setSection("cfstring");
break;
case llvm::Triple::MachO:
diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp
index f7a8dd6..490fadb 100644
--- a/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/lib/CodeGen/ItaniumCXXABI.cpp
@@ -2015,10 +2015,11 @@
// The ABI says: "It is suggested that it be emitted in the same COMDAT
// group as the associated data object." In practice, this doesn't work for
- // non-ELF object formats, so only do it for ELF.
+ // non-ELF and non-Wasm object formats, so only do it for ELF and Wasm.
llvm::Comdat *C = var->getComdat();
if (!D.isLocalVarDecl() && C &&
- CGM.getTarget().getTriple().isOSBinFormatELF()) {
+ (CGM.getTarget().getTriple().isOSBinFormatELF() ||
+ CGM.getTarget().getTriple().isOSBinFormatWasm())) {
guard->setComdat(C);
// An inline variable's guard function is run from the per-TU
// initialization function, not via a dedicated global ctor function, so
@@ -3534,8 +3535,9 @@
return StructorCodegen::RAUW;
if (llvm::GlobalValue::isWeakForLinker(Linkage)) {
- // Only ELF supports COMDATs with arbitrary names (C5/D5).
- if (CGM.getTarget().getTriple().isOSBinFormatELF())
+ // Only ELF and wasm support COMDATs with arbitrary names (C5/D5).
+ if (CGM.getTarget().getTriple().isOSBinFormatELF() ||
+ CGM.getTarget().getTriple().isOSBinFormatWasm())
return StructorCodegen::COMDAT;
return StructorCodegen::Emit;
}
diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp
index 754f996..37ecc05a 100644
--- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp
+++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp
@@ -171,7 +171,8 @@
// Prepare CGDebugInfo to emit debug info for a clang module.
auto *DI = Builder->getModuleDebugInfo();
StringRef ModuleName = llvm::sys::path::filename(MainFileName);
- DI->setPCHDescriptor({ModuleName, "", OutputFileName, ~1ULL});
+ DI->setPCHDescriptor({ModuleName, "", OutputFileName,
+ ASTFileSignature{{{~0U, ~0U, ~0U, ~0U, ~1U}}}});
DI->setModuleMap(MMap);
}
@@ -241,7 +242,11 @@
// PCH files don't have a signature field in the control block,
// but LLVM detects DWO CUs by looking for a non-zero DWO id.
- uint64_t Signature = Buffer->Signature ? Buffer->Signature : ~1ULL;
+ // We use the lower 64 bits for debug info.
+ uint64_t Signature =
+ Buffer->Signature
+ ? (uint64_t)Buffer->Signature[1] << 32 | Buffer->Signature[0]
+ : ~1ULL;
Builder->getModuleDebugInfo()->setDwoId(Signature);
// Finalize the Builder.
diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp
index 15f830d..d8f5bfc 100644
--- a/lib/Driver/Driver.cpp
+++ b/lib/Driver/Driver.cpp
@@ -3186,7 +3186,8 @@
const JobAction *JA = cast<JobAction>(A);
ActionList CollapsedOffloadActions;
- ToolSelector TS(JA, *TC, C, isSaveTempsEnabled(), embedBitcodeInObject());
+ ToolSelector TS(JA, *TC, C, isSaveTempsEnabled(),
+ embedBitcodeInObject() && !isUsingLTO());
const Tool *T = TS.getTool(Inputs, CollapsedOffloadActions);
if (!T)
diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp
index 9fd8808..595d6e6 100644
--- a/lib/Driver/Job.cpp
+++ b/lib/Driver/Job.cpp
@@ -88,6 +88,8 @@
return HaveCrashVFS ? false : true;
if (FlagRef.startswith("-fmodules-cache-path="))
return true;
+ if (FlagRef.startswith("-fapinotes-cache-path="))
+ return true;
SkipNum = 0;
return false;
diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
index 9bc9ae4..7e52514 100644
--- a/lib/Driver/ToolChains.cpp
+++ b/lib/Driver/ToolChains.cpp
@@ -1109,10 +1109,6 @@
options::OPT_fno_omit_frame_pointer, false))
getDriver().Diag(clang::diag::warn_drv_unsupported_opt_for_target)
<< "-fomit-frame-pointer" << BoundArch;
- if (Args.hasFlag(options::OPT_momit_leaf_frame_pointer,
- options::OPT_mno_omit_leaf_frame_pointer, false))
- getDriver().Diag(clang::diag::warn_drv_unsupported_opt_for_target)
- << "-momit-leaf-frame-pointer" << BoundArch;
}
return DAL;
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index b4a8334..30d48b7 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1363,9 +1363,9 @@
options::OPT_mno_global_merge)) {
CmdArgs.push_back("-backend-option");
if (A->getOption().matches(options::OPT_mno_global_merge))
- CmdArgs.push_back("-aarch64-global-merge=false");
+ CmdArgs.push_back("-aarch64-enable-global-merge=false");
else
- CmdArgs.push_back("-aarch64-global-merge=true");
+ CmdArgs.push_back("-aarch64-enable-global-merge=true");
}
}
@@ -3409,7 +3409,7 @@
return false;
}
-static bool mustUseFramePointerForTarget(const llvm::Triple &Triple) {
+static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) {
switch (Triple.getArch()){
default:
return false;
@@ -3475,7 +3475,7 @@
if (Arg *A = Args.getLastArg(options::OPT_fno_omit_frame_pointer,
options::OPT_fomit_frame_pointer))
return A->getOption().matches(options::OPT_fno_omit_frame_pointer) ||
- mustUseFramePointerForTarget(Triple);
+ mustUseNonLeafFramePointerForTarget(Triple);
if (Args.hasArg(options::OPT_pg))
return true;
@@ -3487,8 +3487,7 @@
const llvm::Triple &Triple) {
if (Arg *A = Args.getLastArg(options::OPT_mno_omit_leaf_frame_pointer,
options::OPT_momit_leaf_frame_pointer))
- return A->getOption().matches(options::OPT_mno_omit_leaf_frame_pointer) ||
- mustUseFramePointerForTarget(Triple);
+ return A->getOption().matches(options::OPT_mno_omit_leaf_frame_pointer);
if (Args.hasArg(options::OPT_pg))
return true;
@@ -4250,14 +4249,14 @@
}
// Embed-bitcode option.
- if (C.getDriver().embedBitcodeInObject() &&
+ if (C.getDriver().embedBitcodeInObject() && !C.getDriver().isUsingLTO() &&
(isa<BackendJobAction>(JA) || isa<AssembleJobAction>(JA))) {
// Add flags implied by -fembed-bitcode.
Args.AddLastArg(CmdArgs, options::OPT_fembed_bitcode_EQ);
// Disable all llvm IR level optimizations.
CmdArgs.push_back("-disable-llvm-passes");
}
- if (C.getDriver().embedBitcodeMarkerOnly())
+ if (C.getDriver().embedBitcodeMarkerOnly() && !C.getDriver().isUsingLTO())
CmdArgs.push_back("-fembed-bitcode=marker");
// We normally speed up the clang process a bit by skipping destructors at
@@ -5638,6 +5637,42 @@
options::OPT_fno_assume_sane_operator_new))
CmdArgs.push_back("-fno-assume-sane-operator-new");
+ if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes,
+ false) ||
+ Args.hasFlag(options::OPT_fapinotes_modules,
+ options::OPT_fno_apinotes_modules, false) ||
+ Args.hasArg(options::OPT_iapinotes_modules)) {
+ if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false))
+ CmdArgs.push_back("-fapinotes");
+ if (Args.hasFlag(options::OPT_fapinotes_modules,
+ options::OPT_fno_apinotes_modules, false))
+ CmdArgs.push_back("-fapinotes-modules");
+
+ SmallString<128> APINotesCachePath;
+ if (Arg *A = Args.getLastArg(options::OPT_fapinotes_cache_path)) {
+ APINotesCachePath = A->getValue();
+ }
+
+ if (C.isForDiagnostics()) {
+ // When generating crash reports, we want to emit the API notes along with
+ // the reproduction sources, so we ignore any provided API notes path.
+ APINotesCachePath = Output.getFilename();
+ llvm::sys::path::replace_extension(APINotesCachePath, ".cache");
+ llvm::sys::path::append(APINotesCachePath, "apinotes");
+ } else if (APINotesCachePath.empty()) {
+ // No API notes path was provided: use the default.
+ llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false,
+ APINotesCachePath);
+ llvm::sys::path::append(APINotesCachePath, "org.llvm.clang");
+ llvm::sys::path::append(APINotesCachePath, "APINotesCache");
+ }
+ const char Arg[] = "-fapinotes-cache-path=";
+ APINotesCachePath.insert(APINotesCachePath.begin(), Arg, Arg + strlen(Arg));
+ CmdArgs.push_back(Args.MakeArgString(APINotesCachePath));
+
+ Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version);
+ }
+
// -fblocks=0 is default.
if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks,
getToolChain().IsBlocksDefault()) ||
@@ -6449,7 +6484,8 @@
// pristine IR generated by the frontend. Ideally, a new compile action should
// be added so both IR can be captured.
if (C.getDriver().isSaveTempsEnabled() &&
- !C.getDriver().embedBitcodeInObject() && isa<CompileJobAction>(JA))
+ !C.getDriver().embedBitcodeInObject() && !C.getDriver().isUsingLTO() &&
+ isa<CompileJobAction>(JA))
CmdArgs.push_back("-disable-llvm-passes");
if (Output.getType() == types::TY_Dependencies) {
@@ -8422,9 +8458,13 @@
// for embed-bitcode, use -bitcode_bundle in linker command
if (C.getDriver().embedBitcodeEnabled()) {
// Check if the toolchain supports bitcode build flow.
- if (MachOTC.SupportsEmbeddedBitcode())
+ if (MachOTC.SupportsEmbeddedBitcode()) {
CmdArgs.push_back("-bitcode_bundle");
- else
+ if (C.getDriver().embedBitcodeMarkerOnly() && Version[0] >= 278) {
+ CmdArgs.push_back("-bitcode_process_mode");
+ CmdArgs.push_back("marker");
+ }
+ } else
D.Diag(diag::err_drv_bitcode_unsupported_on_toolchain);
}
diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp
index 6bb6fb3..2f8446e 100644
--- a/lib/Format/ContinuationIndenter.cpp
+++ b/lib/Format/ContinuationIndenter.cpp
@@ -560,7 +560,6 @@
// and we need to avoid bin packing there.
bool NestedBlockSpecialCase =
Style.Language != FormatStyle::LK_Cpp &&
- Style.Language != FormatStyle::LK_ObjC &&
Current.is(tok::r_brace) && State.Stack.size() > 1 &&
State.Stack[State.Stack.size() - 2].NestedBlockInlined;
if (!NestedBlockSpecialCase)
diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp
index 389761d..f08b386 100644
--- a/lib/Format/Format.cpp
+++ b/lib/Format/Format.cpp
@@ -52,7 +52,6 @@
IO.enumCase(Value, "Cpp", FormatStyle::LK_Cpp);
IO.enumCase(Value, "Java", FormatStyle::LK_Java);
IO.enumCase(Value, "JavaScript", FormatStyle::LK_JavaScript);
- IO.enumCase(Value, "ObjC", FormatStyle::LK_ObjC);
IO.enumCase(Value, "Proto", FormatStyle::LK_Proto);
IO.enumCase(Value, "TableGen", FormatStyle::LK_TableGen);
}
@@ -624,8 +623,6 @@
} else if (Language == FormatStyle::LK_Proto) {
GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
GoogleStyle.SpacesInContainerLiterals = false;
- } else if (Language == FormatStyle::LK_ObjC) {
- GoogleStyle.ColumnLimit = 100;
}
return GoogleStyle;
@@ -1872,8 +1869,6 @@
return FormatStyle::LK_Java;
if (FileName.endswith_lower(".js") || FileName.endswith_lower(".ts"))
return FormatStyle::LK_JavaScript; // JavaScript or TypeScript.
- if (FileName.endswith(".m") || FileName.endswith(".mm"))
- return FormatStyle::LK_ObjC;
if (FileName.endswith_lower(".proto") ||
FileName.endswith_lower(".protodevel"))
return FormatStyle::LK_Proto;
@@ -1883,21 +1878,12 @@
}
FormatStyle getStyle(StringRef StyleName, StringRef FileName,
- StringRef FallbackStyle, StringRef Code,
- vfs::FileSystem *FS) {
+ StringRef FallbackStyle, vfs::FileSystem *FS) {
if (!FS) {
FS = vfs::getRealFileSystem().get();
}
FormatStyle Style = getLLVMStyle();
Style.Language = getLanguageByFileName(FileName);
-
- // This is a very crude detection of whether a header contains ObjC code that
- // should be improved over time and probably be done on tokens, not one the
- // bare content of the file.
- if (Style.Language == FormatStyle::LK_Cpp && FileName.endswith(".h") &&
- (Code.contains("\n- (") || Code.contains("\n+ (")))
- Style.Language = FormatStyle::LK_ObjC;
-
if (!getPredefinedStyle(FallbackStyle, Style.Language, &Style)) {
llvm::errs() << "Invalid fallback style \"" << FallbackStyle
<< "\" using LLVM style\n";
diff --git a/lib/Format/FormatTokenLexer.cpp b/lib/Format/FormatTokenLexer.cpp
index 46a32a9..2aa48e3 100644
--- a/lib/Format/FormatTokenLexer.cpp
+++ b/lib/Format/FormatTokenLexer.cpp
@@ -558,8 +558,7 @@
Column = FormatTok->LastLineColumnWidth;
}
- if (Style.Language == FormatStyle::LK_Cpp ||
- Style.Language == FormatStyle::LK_ObjC) {
+ if (Style.Language == FormatStyle::LK_Cpp) {
if (!(Tokens.size() > 0 && Tokens.back()->Tok.getIdentifierInfo() &&
Tokens.back()->Tok.getIdentifierInfo()->getPPKeywordID() ==
tok::pp_define) &&
diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp
index b5f7de2..d1a3952 100644
--- a/lib/Format/TokenAnnotator.cpp
+++ b/lib/Format/TokenAnnotator.cpp
@@ -317,8 +317,7 @@
Contexts.back().InTemplateArgument);
bool StartsObjCMethodExpr =
- !CppArrayTemplates && (Style.Language == FormatStyle::LK_Cpp ||
- Style.Language == FormatStyle::LK_ObjC) &&
+ !CppArrayTemplates && Style.Language == FormatStyle::LK_Cpp &&
Contexts.back().CanBeExpression && Left->isNot(TT_LambdaLSquare) &&
CurrentToken->isNot(tok::l_brace) &&
(!Parent ||
@@ -434,8 +433,7 @@
FormatToken *Previous = CurrentToken->getPreviousNonComment();
if (((CurrentToken->is(tok::colon) &&
(!Contexts.back().ColonIsDictLiteral ||
- (Style.Language != FormatStyle::LK_Cpp &&
- Style.Language != FormatStyle::LK_ObjC))) ||
+ Style.Language != FormatStyle::LK_Cpp)) ||
Style.Language == FormatStyle::LK_Proto) &&
(Previous->Tok.getIdentifierInfo() ||
Previous->is(tok::string_literal)))
@@ -1176,7 +1174,6 @@
bool rParenEndsCast(const FormatToken &Tok) {
// C-style casts are only used in C++ and Java.
if (Style.Language != FormatStyle::LK_Cpp &&
- Style.Language != FormatStyle::LK_ObjC &&
Style.Language != FormatStyle::LK_Java)
return false;
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index d892996..aba662b 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -18,6 +18,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/MemoryBufferCache.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/VirtualFileSystem.h"
@@ -185,7 +186,8 @@
llvm::BitstreamWriter Stream;
ASTWriter Writer;
- ASTWriterData() : Stream(Buffer), Writer(Stream, { }) { }
+ ASTWriterData(MemoryBufferCache &PCMCache)
+ : Stream(Buffer), Writer(Stream, Buffer, PCMCache, {}) {}
};
void ASTUnit::clearFileLevelDecls() {
@@ -619,6 +621,10 @@
StoredDiags.emplace_back(Level, Info);
}
+IntrusiveRefCntPtr<ASTReader> ASTUnit::getASTReader() const {
+ return Reader;
+}
+
ASTMutationListener *ASTUnit::getASTMutationListener() {
if (WriterData)
return &WriterData->Writer;
@@ -677,6 +683,7 @@
AST->SourceMgr = new SourceManager(AST->getDiagnostics(),
AST->getFileManager(),
UserFilesAreVolatile);
+ AST->PCMCache = new MemoryBufferCache;
AST->HSOpts = std::make_shared<HeaderSearchOptions>();
AST->HSOpts->ModuleFormat = PCHContainerRdr.getFormat();
AST->HeaderInfo.reset(new HeaderSearch(AST->HSOpts,
@@ -697,7 +704,7 @@
AST->PP = std::make_shared<Preprocessor>(
std::move(PPOpts), AST->getDiagnostics(), AST->ASTFileLangOpts,
- AST->getSourceManager(), HeaderInfo, *AST,
+ AST->getSourceManager(), *AST->PCMCache, HeaderInfo, *AST,
/*IILookup=*/nullptr,
/*OwnsHeaderSearch=*/false);
Preprocessor &PP = *AST->PP;
@@ -1723,6 +1730,7 @@
AST->UserFilesAreVolatile = UserFilesAreVolatile;
AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr,
UserFilesAreVolatile);
+ AST->PCMCache = new MemoryBufferCache;
return AST;
}
@@ -1990,6 +1998,7 @@
if (!VFS)
return nullptr;
AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS);
+ AST->PCMCache = new MemoryBufferCache;
AST->OnlyLocalDecls = OnlyLocalDecls;
AST->CaptureDiagnostics = CaptureDiagnostics;
AST->TUKind = TUKind;
@@ -2001,7 +2010,7 @@
AST->StoredDiagnostics.swap(StoredDiagnostics);
AST->Invocation = CI;
if (ForSerialization)
- AST->WriterData.reset(new ASTWriterData());
+ AST->WriterData.reset(new ASTWriterData(*AST->PCMCache));
// Zero out now to ease cleanup during crash recovery.
CI = nullptr;
Diags = nullptr;
@@ -2516,7 +2525,8 @@
SmallString<128> Buffer;
llvm::BitstreamWriter Stream(Buffer);
- ASTWriter Writer(Stream, { });
+ MemoryBufferCache PCMCache;
+ ASTWriter Writer(Stream, Buffer, PCMCache, {});
return serializeUnit(Writer, Buffer, getSema(), hasErrors, OS);
}
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index afcaa6e..af0f94b 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -8,11 +8,13 @@
//===----------------------------------------------------------------------===//
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/APINotes/APINotesReader.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Basic/MemoryBufferCache.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
@@ -55,12 +57,15 @@
CompilerInstance::CompilerInstance(
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
- bool BuildingModule)
- : ModuleLoader(BuildingModule), Invocation(new CompilerInvocation()),
- ModuleManager(nullptr),
- ThePCHContainerOperations(std::move(PCHContainerOps)),
- BuildGlobalModuleIndex(false), HaveFullGlobalModuleIndex(false),
- ModuleBuildFailed(false) {}
+ MemoryBufferCache *SharedPCMCache)
+ : ModuleLoader(/* BuildingModule = */ SharedPCMCache),
+ Invocation(new CompilerInvocation()),
+ PCMCache(SharedPCMCache ? SharedPCMCache : new MemoryBufferCache),
+ ThePCHContainerOperations(std::move(PCHContainerOps)) {
+ // Don't allow this to invalidate buffers in use by others.
+ if (SharedPCMCache)
+ getPCMCache().finalizeCurrentBuffers();
+}
CompilerInstance::~CompilerInstance() {
assert(OutputFiles.empty() && "Still output files in flight?");
@@ -131,6 +136,8 @@
return ModuleManager;
}
void CompilerInstance::setModuleManager(IntrusiveRefCntPtr<ASTReader> Reader) {
+ assert(PCMCache.get() == &Reader->getModuleManager().getPCMCache() &&
+ "Expected ASTReader to use the same PCM cache");
ModuleManager = std::move(Reader);
}
@@ -373,7 +380,7 @@
getDiagnostics(), getLangOpts(), &getTarget());
PP = std::make_shared<Preprocessor>(
Invocation->getPreprocessorOptsPtr(), getDiagnostics(), getLangOpts(),
- getSourceManager(), *HeaderInfo, *this, PTHMgr,
+ getSourceManager(), getPCMCache(), *HeaderInfo, *this, PTHMgr,
/*OwnsHeaderSearch=*/true, TUKind);
PP->Initialize(getTarget(), getAuxTarget());
@@ -615,6 +622,27 @@
CodeCompleteConsumer *CompletionConsumer) {
TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(),
TUKind, CompletionConsumer));
+
+ // Set up API notes.
+ TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion);
+
+ // If we're building a module and are supposed to load API notes,
+ // notify the API notes manager.
+ if (auto currentModule = getPreprocessor().getCurrentModule()) {
+ (void)TheSema->APINotes.loadCurrentModuleAPINotes(
+ currentModule,
+ getLangOpts().APINotesModules,
+ getAPINotesOpts().ModuleSearchPaths);
+ // Check for any attributes we should add to the module
+ for (auto reader : TheSema->APINotes.getCurrentModuleReaders()) {
+ // swift_infer_import_as_member
+ if (reader->getModuleOptions().SwiftInferImportAsMember) {
+ currentModule->IsSwiftInferImportAsMember = true;
+ break;
+ }
+ }
+ }
+
// Attach the external sema source if there is any.
if (ExternalSemaSrc) {
TheSema->addExternalSource(ExternalSemaSrc.get());
@@ -1016,7 +1044,7 @@
SourceLocation ImportLoc,
Module *Module,
StringRef ModuleFileName) {
- ModuleMap &ModMap
+ ModuleMap &ModMap
= ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
// Construct a compiler invocation for creating this module.
@@ -1032,7 +1060,7 @@
// Remove any macro definitions that are explicitly ignored by the module.
// They aren't supposed to affect how the module is built anyway.
- const HeaderSearchOptions &HSOpts = Invocation->getHeaderSearchOpts();
+ HeaderSearchOptions &HSOpts = Invocation->getHeaderSearchOpts();
PPOpts.Macros.erase(
std::remove_if(PPOpts.Macros.begin(), PPOpts.Macros.end(),
[&HSOpts](const std::pair<std::string, bool> &def) {
@@ -1063,6 +1091,8 @@
FrontendOpts.DisableFree = false;
FrontendOpts.GenerateGlobalModuleIndex = false;
FrontendOpts.BuildingImplicitModule = true;
+ // Force implicitly-built modules to hash the content of the module file.
+ HSOpts.ModulesHashContent = true;
FrontendOpts.Inputs.clear();
InputKind IK = getSourceInputKindFromOptions(*Invocation->getLangOpts());
@@ -1074,9 +1104,11 @@
Invocation->getModuleHash() && "Module hash mismatch!");
// Construct a compiler instance that will be used to actually create the
- // module.
+ // module. Since we're sharing a PCMCache,
+ // CompilerInstance::CompilerInstance is responsible for finalizing the
+ // buffers to prevent use-after-frees.
CompilerInstance Instance(ImportingInstance.getPCHContainerOperations(),
- /*BuildingModule=*/true);
+ &ImportingInstance.getPreprocessor().getPCMCache());
auto &Inv = *Invocation;
Instance.setInvocation(std::move(Invocation));
@@ -1180,10 +1212,14 @@
llvm::LockFileManager Locked(ModuleFileName);
switch (Locked) {
case llvm::LockFileManager::LFS_Error:
- Diags.Report(ModuleNameLoc, diag::err_module_lock_failure)
+ // PCMCache takes care of correctness and locks are only necessary for
+ // performance. If there are errors creating the lock, do not use it
+ // and fallback to building the module ourselves.
+ Diags.Report(ModuleNameLoc, diag::remark_module_lock_failure)
<< Module->Name << Locked.getErrorMessage();
- return false;
-
+ // Clear the lock file in case there's some leftover around.
+ Locked.unsafeRemoveLockFile();
+ // FALLTHROUGH
case llvm::LockFileManager::LFS_Owned:
// We're responsible for building the module ourselves.
if (!compileModuleImpl(ImportingInstance, ModuleNameLoc, Module,
@@ -1203,11 +1239,14 @@
case llvm::LockFileManager::Res_OwnerDied:
continue; // try again to get the lock.
case llvm::LockFileManager::Res_Timeout:
- Diags.Report(ModuleNameLoc, diag::err_module_lock_timeout)
+ // Since PCMCache takes care of correctness, we try waiting for another
+ // process to complete the build so that this isn't done twice. If we
+ // reach a timeout, it's not a problem, try to build it ourselves then.
+ Diags.Report(ModuleNameLoc, diag::remark_module_lock_timeout)
<< Module->Name;
// Clear the lock file so that future invokations can make progress.
Locked.unsafeRemoveLockFile();
- return false;
+ continue;
}
break;
}
@@ -1794,8 +1833,13 @@
// Check whether this module is available.
clang::Module::Requirement Requirement;
clang::Module::UnresolvedHeaderDirective MissingHeader;
+ clang::Module *ShadowingModule = nullptr;
if (!Module->isAvailable(getLangOpts(), getTarget(), Requirement,
- MissingHeader)) {
+ MissingHeader, ShadowingModule)) {
+
+ assert(!ShadowingModule &&
+ "lookup of module by name should never find shadowed module");
+
if (MissingHeader.FileNameLoc.isValid()) {
getDiagnostics().Report(MissingHeader.FileNameLoc,
diag::err_module_header_missing)
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 36f6b0a..f0cf3d4 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1085,6 +1085,7 @@
static void ParseFileSystemArgs(FileSystemOptions &Opts, ArgList &Args) {
Opts.WorkingDir = Args.getLastArgValue(OPT_working_directory);
+ Opts.APINotesCachePath = Args.getLastArgValue(OPT_fapinotes_cache_path);
}
/// Parse the argument to the -ftest-module-file-extension
@@ -1407,7 +1408,8 @@
return P.str();
}
-static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) {
+static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args,
+ const std::string &WorkingDir) {
using namespace options;
Opts.Sysroot = Args.getLastArgValue(OPT_isysroot, "/");
Opts.Verbose = Args.hasArg(OPT_v);
@@ -1417,11 +1419,23 @@
if (const Arg *A = Args.getLastArg(OPT_stdlib_EQ))
Opts.UseLibcxx = (strcmp(A->getValue(), "libc++") == 0);
Opts.ResourceDir = Args.getLastArgValue(OPT_resource_dir);
- Opts.ModuleCachePath = Args.getLastArgValue(OPT_fmodules_cache_path);
+
+ // Canonicalize -fmodules-cache-path before storing it.
+ SmallString<128> P(Args.getLastArgValue(OPT_fmodules_cache_path));
+ if (!(P.empty() || llvm::sys::path::is_absolute(P))) {
+ if (WorkingDir.empty())
+ llvm::sys::fs::make_absolute(P);
+ else
+ llvm::sys::fs::make_absolute(WorkingDir, P);
+ }
+ llvm::sys::path::remove_dots(P);
+ Opts.ModuleCachePath = P.str();
+
Opts.ModuleUserBuildPath = Args.getLastArgValue(OPT_fmodules_user_build_path);
for (const Arg *A : Args.filtered(OPT_fprebuilt_module_path))
Opts.AddPrebuiltModulePath(A->getValue());
Opts.DisableModuleHash = Args.hasArg(OPT_fdisable_module_hash);
+ Opts.ModulesHashContent = Args.hasArg(OPT_fmodules_hash_content);
Opts.ModulesValidateDiagnosticOptions =
!Args.hasArg(OPT_fmodules_disable_diagnostic_validation);
Opts.ImplicitModuleMaps = Args.hasArg(OPT_fimplicit_module_maps);
@@ -1525,6 +1539,18 @@
Opts.AddVFSOverlayFile(A->getValue());
}
+static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args,
+ DiagnosticsEngine &diags) {
+ using namespace options;
+ if (const Arg *A = Args.getLastArg(OPT_fapinotes_swift_version)) {
+ if (Opts.SwiftVersion.tryParse(A->getValue()))
+ diags.Report(diag::err_drv_invalid_value)
+ << A->getAsString(Args) << A->getValue();
+ }
+ for (const Arg *A : Args.filtered(OPT_iapinotes_modules))
+ Opts.ModuleSearchPaths.push_back(A->getValue());
+}
+
static bool isOpenCL(LangStandard::Kind LangStd) {
return LangStd == LangStandard::lang_opencl ||
LangStd == LangStandard::lang_opencl11 ||
@@ -2031,6 +2057,8 @@
// is enabled.
Opts.HalfArgsAndReturns = Args.hasArg(OPT_fallow_half_arguments_and_returns)
| Opts.NativeHalfArgsAndReturns;
+ Opts.APINotes = Args.hasArg(OPT_fapinotes);
+ Opts.APINotesModules = Args.hasArg(OPT_fapinotes_modules);
Opts.GNUAsm = !Args.hasArg(OPT_fno_gnu_inline_asm);
// __declspec is enabled by default for the PS4 by the driver, and also
@@ -2440,7 +2468,10 @@
ParseTargetArgs(Res.getTargetOpts(), Args, Diags);
Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags,
Res.getTargetOpts());
- ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args);
+ ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args,
+ Res.getFileSystemOpts().WorkingDir);
+ ParseAPINotesArgs(Res.getAPINotesOpts(), Args, Diags);
+
if (DashX == IK_AST || DashX == IK_LLVM_IR) {
// ObjCAAutoRefCount and Sanitize LangOpts are used to setup the
// PassManager in BackendUtil.cpp. They need to be initializd no matter
@@ -2459,6 +2490,13 @@
Res.getPreprocessorOpts(), Diags);
if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC)
LangOpts.ObjCExceptions = 1;
+
+ // -fapinotes and -fapinotes-modules requires -fapinotes-cache-path=<directory>.
+ if ((LangOpts.APINotes || LangOpts.APINotesModules) &&
+ Res.getFileSystemOpts().APINotesCachePath.empty()) {
+ Diags.Report(diag::err_no_apinotes_cache_path);
+ Success = false;
+ }
}
if (LangOpts.CUDA) {
@@ -2566,7 +2604,19 @@
// Extend the signature with the module file extensions.
const FrontendOptions &frontendOpts = getFrontendOpts();
for (const auto &ext : frontendOpts.ModuleFileExtensions) {
- code = ext->hashExtension(code);
+ code = hash_combine(code, ext->hashExtension(code));
+ }
+
+ // Extend the signature with the SWift version for API notes.
+ const APINotesOptions &apiNotesOpts = getAPINotesOpts();
+ if (apiNotesOpts.SwiftVersion) {
+ code = hash_combine(code, apiNotesOpts.SwiftVersion.getMajor());
+ if (auto minor = apiNotesOpts.SwiftVersion.getMinor())
+ code = hash_combine(code, *minor);
+ if (auto subminor = apiNotesOpts.SwiftVersion.getSubminor())
+ code = hash_combine(code, *subminor);
+ if (auto build = apiNotesOpts.SwiftVersion.getBuild())
+ code = hash_combine(code, *build);
}
// Darwin-specific hack: if we have a sysroot, use the contents and
diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp
index 39fc137..f9ad97c 100644
--- a/lib/Frontend/FrontendAction.cpp
+++ b/lib/Frontend/FrontendAction.cpp
@@ -414,6 +414,13 @@
CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename;
}
+ // Add a module declaration scope so that modules from -fmodule-map-file
+ // arguments may shadow modules found implicitly in search paths.
+ CI.getPreprocessor()
+ .getHeaderSearchInfo()
+ .getModuleMap()
+ .finishModuleDeclarationScope();
+
// If we were asked to load any module files, do so now.
for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles)
if (!CI.loadModuleFile(ModuleFile))
diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp
index f795a1d..bfea1df 100644
--- a/lib/Frontend/FrontendActions.cpp
+++ b/lib/Frontend/FrontendActions.cpp
@@ -341,8 +341,13 @@
// Check whether we can build this module at all.
clang::Module::Requirement Requirement;
clang::Module::UnresolvedHeaderDirective MissingHeader;
+ clang::Module *ShadowingModule = nullptr;
if (!Module->isAvailable(CI.getLangOpts(), CI.getTarget(), Requirement,
- MissingHeader)) {
+ MissingHeader, ShadowingModule)) {
+
+ assert(!ShadowingModule &&
+ "lookup of module by name should never find shadowed module");
+
if (MissingHeader.FileNameLoc.isValid()) {
CI.getDiagnostics().Report(MissingHeader.FileNameLoc,
diag::err_module_header_missing)
diff --git a/lib/Frontend/InitPreprocessor.cpp b/lib/Frontend/InitPreprocessor.cpp
index 4502c92..e5a0796 100644
--- a/lib/Frontend/InitPreprocessor.cpp
+++ b/lib/Frontend/InitPreprocessor.cpp
@@ -593,9 +593,6 @@
Builder.defineMacro("OBJC_ZEROCOST_EXCEPTIONS");
}
- Builder.defineMacro("__OBJC_BOOL_IS_BOOL",
- Twine(TI.useSignedCharForObjCBool() ? "0" : "1"));
-
if (LangOpts.getGC() != LangOptions::NonGC)
Builder.defineMacro("__OBJC_GC__");
@@ -626,6 +623,11 @@
Builder.defineMacro("IB_DESIGNABLE", "");
}
+ // Define a macro that describes the Objective-C boolean type even for C
+ // and C++ since BOOL can be used from non Objective-C code.
+ Builder.defineMacro("__OBJC_BOOL_IS_BOOL",
+ Twine(TI.useSignedCharForObjCBool() ? "0" : "1"));
+
if (LangOpts.CPlusPlus)
InitializeCPlusPlusFeatureTestMacros(LangOpts, Builder);
diff --git a/lib/Index/CMakeLists.txt b/lib/Index/CMakeLists.txt
index 8f51ccb..c9fbfaf 100644
--- a/lib/Index/CMakeLists.txt
+++ b/lib/Index/CMakeLists.txt
@@ -24,5 +24,6 @@
clangFormat
clangFrontend
clangRewrite
+ clangSerialization
clangToolingCore
)
diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp
index 84984fc..f3de472 100644
--- a/lib/Index/IndexSymbol.cpp
+++ b/lib/Index/IndexSymbol.cpp
@@ -389,6 +389,20 @@
case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor";
case SymbolSubKind::AccessorGetter: return "acc-get";
case SymbolSubKind::AccessorSetter: return "acc-set";
+ case SymbolSubKind::SwiftAccessorWillSet: return "acc-willset";
+ case SymbolSubKind::SwiftAccessorDidSet: return "acc-didset";
+ case SymbolSubKind::SwiftAccessorAddressor: return "acc-addr";
+ case SymbolSubKind::SwiftAccessorMutableAddressor: return "acc-mutaddr";
+ case SymbolSubKind::SwiftExtensionOfStruct: return "ext-struct";
+ case SymbolSubKind::SwiftExtensionOfClass: return "ext-class";
+ case SymbolSubKind::SwiftExtensionOfEnum: return "ext-enum";
+ case SymbolSubKind::SwiftExtensionOfProtocol: return "ext-protocol";
+ case SymbolSubKind::SwiftPrefixOperator: return "prefix-operator";
+ case SymbolSubKind::SwiftPostfixOperator: return "postfix-operator";
+ case SymbolSubKind::SwiftInfixOperator: return "infix-operator";
+ case SymbolSubKind::SwiftSubscript: return "subscript";
+ case SymbolSubKind::SwiftAssociatedType: return "associated-type";
+ case SymbolSubKind::SwiftGenericTypeParam: return "generic-type-param";
}
llvm_unreachable("invalid symbol subkind");
}
@@ -398,6 +412,7 @@
case SymbolLanguage::C: return "C";
case SymbolLanguage::ObjC: return "ObjC";
case SymbolLanguage::CXX: return "C++";
+ case SymbolLanguage::Swift: return "Swift";
}
llvm_unreachable("invalid symbol language kind");
}
diff --git a/lib/Index/IndexingAction.cpp b/lib/Index/IndexingAction.cpp
index d744293..cac24d4 100644
--- a/lib/Index/IndexingAction.cpp
+++ b/lib/Index/IndexingAction.cpp
@@ -13,6 +13,7 @@
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Serialization/ASTReader.h"
using namespace clang;
using namespace clang::index;
@@ -173,4 +174,20 @@
IndexCtx.setASTContext(Unit.getASTContext());
DataConsumer->initialize(Unit.getASTContext());
indexTranslationUnit(Unit, IndexCtx);
+ DataConsumer->finish();
+}
+
+void index::indexModuleFile(serialization::ModuleFile &Mod,
+ ASTReader &Reader,
+ std::shared_ptr<IndexDataConsumer> DataConsumer,
+ IndexingOptions Opts) {
+ ASTContext &Ctx = Reader.getContext();
+ IndexingContext IndexCtx(Opts, *DataConsumer);
+ IndexCtx.setASTContext(Ctx);
+ DataConsumer->initialize(Ctx);
+
+ for (const Decl *D :Reader.getModuleFileLevelDecls(Mod)) {
+ IndexCtx.indexTopLevelDecl(D);
+ }
+ DataConsumer->finish();
}
diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp
index 58f61c3..f9ed3c4 100644
--- a/lib/Index/USRGeneration.cpp
+++ b/lib/Index/USRGeneration.cpp
@@ -911,21 +911,30 @@
bool clang::index::generateUSRForMacro(const MacroDefinitionRecord *MD,
const SourceManager &SM,
SmallVectorImpl<char> &Buf) {
+ if (!MD)
+ return true;
+ return generateUSRForMacro(MD->getName()->getName(), MD->getLocation(),
+ SM, Buf);
+
+}
+
+bool clang::index::generateUSRForMacro(StringRef MacroName, SourceLocation Loc,
+ const SourceManager &SM,
+ SmallVectorImpl<char> &Buf) {
// Don't generate USRs for things with invalid locations.
- if (!MD || MD->getLocation().isInvalid())
+ if (MacroName.empty() || Loc.isInvalid())
return true;
llvm::raw_svector_ostream Out(Buf);
// Assume that system headers are sane. Don't put source location
// information into the USR if the macro comes from a system header.
- SourceLocation Loc = MD->getLocation();
bool ShouldGenerateLocation = !SM.isInSystemHeader(Loc);
Out << getUSRSpacePrefix();
if (ShouldGenerateLocation)
printLoc(Out, Loc, SM, /*IncludeOffset=*/true);
Out << "@macro@";
- Out << MD->getName()->getName();
+ Out << MacroName;
return false;
}
diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp
index c667f4b..fa2a76e 100644
--- a/lib/Lex/HeaderSearch.cpp
+++ b/lib/Lex/HeaderSearch.cpp
@@ -1092,51 +1092,13 @@
}
bool HeaderSearch::ShouldEnterIncludeFile(Preprocessor &PP,
- const FileEntry *File, bool isImport,
- bool ModulesEnabled, Module *M) {
+ const FileEntry *File,
+ bool isImport, Module *M) {
++NumIncluded; // Count # of attempted #includes.
// Get information about this file.
HeaderFileInfo &FileInfo = getFileInfo(File);
- // FIXME: this is a workaround for the lack of proper modules-aware support
- // for #import / #pragma once
- auto TryEnterImported = [&](void) -> bool {
- if (!ModulesEnabled)
- return false;
- // Modules with builtins are special; multiple modules use builtins as
- // modular headers, example:
- //
- // module stddef { header "stddef.h" export * }
- //
- // After module map parsing, this expands to:
- //
- // module stddef {
- // header "/path_to_builtin_dirs/stddef.h"
- // textual "stddef.h"
- // }
- //
- // It's common that libc++ and system modules will both define such
- // submodules. Make sure cached results for a builtin header won't
- // prevent other builtin modules to potentially enter the builtin header.
- // Note that builtins are header guarded and the decision to actually
- // enter them is postponed to the controlling macros logic below.
- bool TryEnterHdr = false;
- if (FileInfo.isCompilingModuleHeader && FileInfo.isModuleHeader)
- TryEnterHdr = File->getDir() == ModMap.getBuiltinDir() &&
- ModuleMap::isBuiltinHeader(
- llvm::sys::path::filename(File->getName()));
-
- // Textual headers can be #imported from different modules. Since ObjC
- // headers find in the wild might rely only on #import and do not contain
- // controlling macros, be conservative and only try to enter textual headers
- // if such macro is present.
- if (!FileInfo.isModuleHeader &&
- FileInfo.getControllingMacro(ExternalLookup))
- TryEnterHdr = true;
- return TryEnterHdr;
- };
-
// If this is a #import directive, check that we have not already imported
// this header.
if (isImport) {
@@ -1144,12 +1106,11 @@
FileInfo.isImport = true;
// Has this already been #import'ed or #include'd?
- if (FileInfo.NumIncludes && !TryEnterImported())
- return false;
+ if (FileInfo.NumIncludes) return false;
} else {
// Otherwise, if this is a #include of a file that was previously #import'd
// or if this is the second #include of a #pragma once file, ignore it.
- if (FileInfo.isImport && !TryEnterImported())
+ if (FileInfo.isImport)
return false;
}
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp
index 1488f62..12dbd6e 100644
--- a/lib/Lex/ModuleMap.cpp
+++ b/lib/Lex/ModuleMap.cpp
@@ -144,7 +144,7 @@
/// \brief Determine whether the given file name is the name of a builtin
/// header, supplied by Clang to replace, override, or augment existing system
/// headers.
-bool ModuleMap::isBuiltinHeader(StringRef FileName) {
+static bool isBuiltinHeader(StringRef FileName) {
return llvm::StringSwitch<bool>(FileName)
.Case("float.h", true)
.Case("iso646.h", true)
@@ -165,7 +165,7 @@
HeadersMap::iterator Known = Headers.find(File);
if (HeaderInfo.getHeaderSearchOpts().ImplicitModuleMaps &&
Known == Headers.end() && File->getDir() == BuiltinIncludeDir &&
- ModuleMap::isBuiltinHeader(llvm::sys::path::filename(File->getName()))) {
+ isBuiltinHeader(llvm::sys::path::filename(File->getName()))) {
HeaderInfo.loadTopLevelSystemModules();
return Headers.find(File);
}
@@ -568,10 +568,25 @@
if (LangOpts.CurrentModule == Name)
SourceModule = Result;
Modules[Name] = Result;
+ ModuleScopeIDs[Result] = CurrentModuleScopeID;
}
return std::make_pair(Result, true);
}
+Module *ModuleMap::createShadowedModule(StringRef Name, bool IsFramework,
+ Module *ShadowingModule) {
+
+ // Create a new module with this name.
+ Module *Result =
+ new Module(Name, SourceLocation(), /*Parent=*/nullptr, IsFramework,
+ /*IsExplicit=*/false, NumCreatedModules++);
+ Result->ShadowingModule = ShadowingModule;
+ Result->IsAvailable = false;
+ ModuleScopeIDs[Result] = CurrentModuleScopeID;
+
+ return Result;
+}
+
Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc,
StringRef Name) {
assert(LangOpts.CurrentModule == Name && "module name mismatch");
@@ -713,6 +728,8 @@
Module *Result = new Module(ModuleName, SourceLocation(), Parent,
/*IsFramework=*/true, /*IsExplicit=*/false,
NumCreatedModules++);
+ if (!Parent)
+ ModuleScopeIDs[Result] = CurrentModuleScopeID;
InferredModuleAllowedBy[Result] = ModuleMapFile;
Result->IsInferred = true;
if (!Parent) {
@@ -1324,6 +1341,8 @@
AT_extern_c,
/// \brief The 'exhaustive' attribute.
AT_exhaustive,
+ // \brief The 'swift_infer_import_as_member' attribute.
+ AT_swift_infer_import_as_member,
/// \brief The 'no_undeclared_includes' attribute.
AT_no_undeclared_includes
};
@@ -1460,6 +1479,7 @@
SourceLocation LBraceLoc = consumeToken();
// Determine whether this (sub)module has already been defined.
+ Module *ShadowingModule = nullptr;
if (Module *Existing = Map.lookupModuleQualified(ModuleName, ActiveModule)) {
if (Existing->DefinitionLoc.isInvalid() && !ActiveModule) {
// Skip the module definition.
@@ -1473,23 +1493,35 @@
}
return;
}
-
- Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition)
- << ModuleName;
- Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition);
-
- // Skip the module definition.
- skipUntil(MMToken::RBrace);
- if (Tok.is(MMToken::RBrace))
- consumeToken();
-
- HadError = true;
- return;
+
+ if (!Existing->Parent && Map.mayShadowNewModule(Existing)) {
+ ShadowingModule = Existing;
+ } else {
+ // This is not a shawdowed module decl, it is an illegal redefinition.
+ Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition)
+ << ModuleName;
+ Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition);
+
+ // Skip the module definition.
+ skipUntil(MMToken::RBrace);
+ if (Tok.is(MMToken::RBrace))
+ consumeToken();
+
+ HadError = true;
+ return;
+ }
}
// Start defining this module.
- ActiveModule = Map.findOrCreateModule(ModuleName, ActiveModule, Framework,
- Explicit).first;
+ if (ShadowingModule) {
+ ActiveModule =
+ Map.createShadowedModule(ModuleName, Framework, ShadowingModule);
+ } else {
+ ActiveModule =
+ Map.findOrCreateModule(ModuleName, ActiveModule, Framework, Explicit)
+ .first;
+ }
+
ActiveModule->DefinitionLoc = ModuleNameLoc;
if (Attrs.IsSystem || IsSystem)
ActiveModule->IsSystem = true;
@@ -1889,7 +1921,7 @@
// supplied by Clang. Find that builtin header.
if (ActiveModule->IsSystem && LeadingToken != MMToken::UmbrellaKeyword &&
BuiltinIncludeDir && BuiltinIncludeDir != Directory &&
- ModuleMap::isBuiltinHeader(Header.FileName)) {
+ isBuiltinHeader(Header.FileName)) {
SmallString<128> BuiltinPathName(BuiltinIncludeDir->getName());
llvm::sys::path::append(BuiltinPathName, Header.FileName);
BuiltinFile = SourceMgr.getFileManager().getFile(BuiltinPathName);
@@ -2428,6 +2460,7 @@
.Case("extern_c", AT_extern_c)
.Case("no_undeclared_includes", AT_no_undeclared_includes)
.Case("system", AT_system)
+ .Case("swift_infer_import_as_member", AT_swift_infer_import_as_member)
.Default(AT_unknown);
switch (Attribute) {
case AT_unknown:
@@ -2443,6 +2476,10 @@
Attrs.IsExternC = true;
break;
+ case AT_swift_infer_import_as_member:
+ Attrs.IsSwiftInferImportAsMember = true;
+ break;
+
case AT_exhaustive:
Attrs.IsExhaustive = true;
break;
diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp
index 322c580..7b65f64 100644
--- a/lib/Lex/PPDirectives.cpp
+++ b/lib/Lex/PPDirectives.cpp
@@ -1869,13 +1869,17 @@
if (!SuggestedModule.getModule()->isAvailable()) {
Module::Requirement Requirement;
Module::UnresolvedHeaderDirective MissingHeader;
+ Module *ShadowingModule = nullptr;
Module *M = SuggestedModule.getModule();
// Identify the cause.
(void)M->isAvailable(getLangOpts(), getTargetInfo(), Requirement,
- MissingHeader);
+ MissingHeader, ShadowingModule);
if (MissingHeader.FileNameLoc.isValid()) {
Diag(MissingHeader.FileNameLoc, diag::err_module_header_missing)
<< MissingHeader.IsUmbrella << MissingHeader.FileName;
+ } else if (ShadowingModule) {
+ Diag(M->DefinitionLoc, diag::err_module_shadowed) << M->Name;
+ Diag(ShadowingModule->DefinitionLoc, diag::note_previous_definition);
} else {
Diag(M->DefinitionLoc, diag::err_module_unavailable)
<< M->getFullModuleName() << Requirement.second << Requirement.first;
@@ -1999,7 +2003,6 @@
bool SkipHeader = false;
if (ShouldEnter &&
!HeaderInfo.ShouldEnterIncludeFile(*this, File, isImport,
- getLangOpts().Modules,
SuggestedModule.getModule())) {
ShouldEnter = false;
SkipHeader = true;
diff --git a/lib/Lex/PPExpressions.cpp b/lib/Lex/PPExpressions.cpp
index 862a471..2e1b5de 100644
--- a/lib/Lex/PPExpressions.cpp
+++ b/lib/Lex/PPExpressions.cpp
@@ -157,51 +157,6 @@
PP.LexNonComment(PeekTok);
}
- // [cpp.cond]p4:
- // Prior to evaluation, macro invocations in the list of preprocessing
- // tokens that will become the controlling constant expression are replaced
- // (except for those macro names modified by the 'defined' unary operator),
- // just as in normal text. If the token 'defined' is generated as a result
- // of this replacement process or use of the 'defined' unary operator does
- // not match one of the two specified forms prior to macro replacement, the
- // behavior is undefined.
- // This isn't an idle threat, consider this program:
- // #define FOO
- // #define BAR defined(FOO)
- // #if BAR
- // ...
- // #else
- // ...
- // #endif
- // clang and gcc will pick the #if branch while Visual Studio will take the
- // #else branch. Emit a warning about this undefined behavior.
- if (beginLoc.isMacroID()) {
- bool IsFunctionTypeMacro =
- PP.getSourceManager()
- .getSLocEntry(PP.getSourceManager().getFileID(beginLoc))
- .getExpansion()
- .isFunctionMacroExpansion();
- // For object-type macros, it's easy to replace
- // #define FOO defined(BAR)
- // with
- // #if defined(BAR)
- // #define FOO 1
- // #else
- // #define FOO 0
- // #endif
- // and doing so makes sense since compilers handle this differently in
- // practice (see example further up). But for function-type macros,
- // there is no good way to write
- // # define FOO(x) (defined(M_ ## x) && M_ ## x)
- // in a different way, and compilers seem to agree on how to behave here.
- // So warn by default on object-type macros, but only warn in -pedantic
- // mode on function-type macros.
- if (IsFunctionTypeMacro)
- PP.Diag(beginLoc, diag::warn_defined_in_function_type_macro);
- else
- PP.Diag(beginLoc, diag::warn_defined_in_object_type_macro);
- }
-
// Invoke the 'defined' callback.
if (PPCallbacks *Callbacks = PP.getPPCallbacks()) {
Callbacks->Defined(macroToken, Macro,
@@ -241,8 +196,8 @@
if (IdentifierInfo *II = PeekTok.getIdentifierInfo()) {
// Handle "defined X" and "defined(X)".
if (II->isStr("defined"))
- return EvaluateDefined(Result, PeekTok, DT, ValueLive, PP);
-
+ return(EvaluateDefined(Result, PeekTok, DT, ValueLive, PP));
+
// If this identifier isn't 'defined' or one of the special
// preprocessor keywords and it wasn't macro expanded, it turns
// into a simple 0, unless it is the C++ keyword "true", in which case it
diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp
index de166c7..822ac8f 100644
--- a/lib/Lex/PPMacroExpansion.cpp
+++ b/lib/Lex/PPMacroExpansion.cpp
@@ -1106,6 +1106,7 @@
.Case("attribute_availability_with_version_underscores", true)
.Case("attribute_availability_tvos", true)
.Case("attribute_availability_watchos", true)
+ .Case("attribute_availability_swift", true)
.Case("attribute_availability_with_strict", true)
.Case("attribute_availability_with_replacement", true)
.Case("attribute_availability_in_templates", true)
@@ -1130,6 +1131,7 @@
.Case("cxx_exceptions", LangOpts.CXXExceptions)
.Case("cxx_rtti", LangOpts.RTTI && LangOpts.RTTIData)
.Case("enumerator_attributes", true)
+ .Case("generalized_swift_name", true)
.Case("nullability", true)
.Case("nullability_on_arrays", true)
.Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory))
diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp
index 91319be..babef5d 100644
--- a/lib/Lex/Preprocessor.cpp
+++ b/lib/Lex/Preprocessor.cpp
@@ -70,15 +70,15 @@
Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
DiagnosticsEngine &diags, LangOptions &opts,
- SourceManager &SM, HeaderSearch &Headers,
- ModuleLoader &TheModuleLoader,
+ SourceManager &SM, MemoryBufferCache &PCMCache,
+ HeaderSearch &Headers, ModuleLoader &TheModuleLoader,
IdentifierInfoLookup *IILookup, bool OwnsHeaders,
TranslationUnitKind TUKind)
: PPOpts(std::move(PPOpts)), Diags(&diags), LangOpts(opts), Target(nullptr),
AuxTarget(nullptr), FileMgr(Headers.getFileMgr()), SourceMgr(SM),
- ScratchBuf(new ScratchBuffer(SourceMgr)), HeaderInfo(Headers),
- TheModuleLoader(TheModuleLoader), ExternalSource(nullptr),
- Identifiers(opts, IILookup),
+ PCMCache(PCMCache), ScratchBuf(new ScratchBuffer(SourceMgr)),
+ HeaderInfo(Headers), TheModuleLoader(TheModuleLoader),
+ ExternalSource(nullptr), Identifiers(opts, IILookup),
PragmaHandlers(new PragmaNamespace(StringRef())),
IncrementalProcessing(false), TUKind(TUKind), CodeComplete(nullptr),
CodeCompletionFile(nullptr), CodeCompletionOffset(0),
diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp
index c52b61e..a456c9b 100644
--- a/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/lib/Parse/ParseCXXInlineMethods.cpp
@@ -47,6 +47,7 @@
VS, ICIS_NoInit);
if (FnD) {
Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs);
+ Actions.ProcessAPINotes(FnD);
if (PureSpecLoc.isValid())
Actions.ActOnPureSpecifier(FnD, PureSpecLoc);
}
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 2d32087..b89419d 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -244,6 +244,38 @@
return IL;
}
+void Parser::ParseSwiftNewtypeAttribute(
+ IdentifierInfo &SwiftNewtype, SourceLocation SwiftNewtypeLoc,
+ ParsedAttributes &attrs, SourceLocation *endLoc, IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc, AttributeList::Syntax Syntax) {
+
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ Parens.consumeOpen();
+
+ if (Tok.is(tok::r_paren)) {
+ Diag(Tok.getLocation(), diag::err_argument_required_after_attribute);
+ Parens.consumeClose();
+ return;
+ }
+ if (Tok.isNot(tok::kw_struct) && Tok.isNot(tok::kw_enum)) {
+ Diag(Tok.getLocation(), diag::warn_attribute_type_not_supported)
+ << &SwiftNewtype << Tok.getIdentifierInfo();
+ if (!isTokenSpecial())
+ ConsumeToken();
+ Parens.consumeClose();
+ return;
+ }
+ auto IL = IdentifierLoc::create(Actions.Context, Tok.getLocation(),
+ Tok.getIdentifierInfo());
+ ConsumeToken();
+ auto identLoc = ArgsUnion(IL);
+
+ attrs.addNew(&SwiftNewtype,
+ SourceRange(SwiftNewtypeLoc, Parens.getCloseLocation()),
+ ScopeName, ScopeLoc, &identLoc, 1, Syntax);
+ Parens.consumeClose();
+}
+
void Parser::ParseAttributeWithTypeArg(IdentifierInfo &AttrName,
SourceLocation AttrNameLoc,
ParsedAttributes &Attrs,
@@ -364,6 +396,10 @@
ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
ScopeName, ScopeLoc, Syntax);
return;
+ } else if (AttrKind == AttributeList::AT_SwiftNewtype) {
+ ParseSwiftNewtypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ return;
} else if (attributeIsTypeArgAttr(*AttrName)) {
ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
ScopeLoc, Syntax);
@@ -855,7 +891,7 @@
///
/// version-arg:
/// 'introduced' '=' version
-/// 'deprecated' '=' version
+/// 'deprecated' ['=' version]
/// 'obsoleted' = version
/// 'unavailable'
/// opt-replacement:
@@ -943,6 +979,21 @@
continue;
}
+ if (Keyword == Ident_deprecated && Platform->Ident &&
+ Platform->Ident->getName() == "swift") {
+ // For swift, we deprecate for all versions.
+ if (!Changes[Deprecated].KeywordLoc.isInvalid()) {
+ Diag(KeywordLoc, diag::err_availability_redundant)
+ << Keyword
+ << SourceRange(Changes[Deprecated].KeywordLoc);
+ }
+
+ Changes[Deprecated].KeywordLoc = KeywordLoc;
+ // Use a fake version here.
+ Changes[Deprecated].Version = VersionTuple(1);
+ continue;
+ }
+
if (Tok.isNot(tok::equal)) {
Diag(Tok, diag::err_expected_after) << Keyword << tok::equal;
SkipUntil(tok::r_paren, StopAtSemi);
@@ -4806,9 +4857,10 @@
/// [ only if AttReqs & AR_CXX11AttributesParsed ]
/// Note: vendor can be GNU, MS, etc and can be explicitly controlled via
/// AttrRequirements bitmask values.
-void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, unsigned AttrReqs,
- bool AtomicAllowed,
- bool IdentifierRequired) {
+void Parser::ParseTypeQualifierListOpt(
+ DeclSpec &DS, unsigned AttrReqs, bool AtomicAllowed,
+ bool IdentifierRequired,
+ Optional<llvm::function_ref<void()>> CodeCompletionHandler) {
if (getLangOpts().CPlusPlus11 && (AttrReqs & AR_CXX11AttributesParsed) &&
isCXX11AttributeSpecifier()) {
ParsedAttributesWithRange attrs(AttrFactory);
@@ -4826,7 +4878,10 @@
switch (Tok.getKind()) {
case tok::code_completion:
- Actions.CodeCompleteTypeQualifiers(DS);
+ if (CodeCompletionHandler)
+ (*CodeCompletionHandler)();
+ else
+ Actions.CodeCompleteTypeQualifiers(DS);
return cutOffParsing();
case tok::kw_const:
@@ -5744,7 +5799,11 @@
// Parse cv-qualifier-seq[opt].
ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed,
- /*AtomicAllowed*/ false);
+ /*AtomicAllowed*/ false,
+ /*IdentifierRequired=*/false,
+ llvm::function_ref<void()>([&]() {
+ Actions.CodeCompleteFunctionQualifiers(DS, D);
+ }));
if (!DS.getSourceRange().getEnd().isInvalid()) {
EndLoc = DS.getSourceRange().getEnd();
ConstQualifierLoc = DS.getConstSpecLoc();
@@ -6553,3 +6612,68 @@
}
return false;
}
+
+TypeResult Parser::parseTypeFromString(StringRef typeStr, StringRef context,
+ SourceLocation includeLoc) {
+ // Consume (unexpanded) tokens up to the end-of-directive.
+ SmallVector<Token, 4> tokens;
+ {
+ // Create a new buffer from which we will parse the type.
+ auto &sourceMgr = PP.getSourceManager();
+ FileID fileID = sourceMgr.createFileID(
+ llvm::MemoryBuffer::getMemBufferCopy(typeStr, context),
+ SrcMgr::C_User, 0, 0, includeLoc);
+
+ // Form a new lexer that references the buffer.
+ Lexer lexer(fileID, sourceMgr.getBuffer(fileID), PP);
+ lexer.setParsingPreprocessorDirective(true);
+ lexer.setIsPragmaLexer(true);
+
+ // Lex the tokens from that buffer.
+ Token tok;
+ do {
+ lexer.Lex(tok);
+ tokens.push_back(tok);
+ } while (tok.isNot(tok::eod));
+ }
+
+ // Replace the "eod" token with an "eof" token identifying the end of
+ // the provided string.
+ Token &endToken = tokens.back();
+ endToken.startToken();
+ endToken.setKind(tok::eof);
+ endToken.setLocation(Tok.getLocation());
+ endToken.setEofData(typeStr.data());
+
+ // Add the current token back.
+ tokens.push_back(Tok);
+
+ // Enter the tokens into the token stream.
+ PP.EnterTokenStream(tokens, /*DisableMacroExpansion=*/false);
+
+ // Consume the current token so that we'll start parsing the tokens we
+ // added to the stream.
+ ConsumeAnyToken();
+
+ // Enter a new scope.
+ ParseScope localScope(this, 0);
+
+ // Parse the type.
+ TypeResult result = ParseTypeName(nullptr);
+
+ // Check if we parsed the whole thing.
+ if (result.isUsable() &&
+ (Tok.isNot(tok::eof) || Tok.getEofData() != typeStr.data())) {
+ Diag(Tok.getLocation(), diag::err_type_unparsed);
+ }
+
+ // There could be leftover tokens (e.g. because of an error).
+ // Skip through until we reach the 'end of directive' token.
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ // Consume the end token.
+ if (Tok.is(tok::eof) && Tok.getEofData() == typeStr.data())
+ ConsumeAnyToken();
+ return result;
+}
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 3f1fe7e..d2165d8 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -2283,7 +2283,11 @@
// GNU-style and C++11 attributes are not allowed here, but they will be
// handled by the caller. Diagnose everything else.
- ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed, false);
+ ParseTypeQualifierListOpt(
+ DS, AR_NoAttributesParsed, false,
+ /*IdentifierRequired=*/false, llvm::function_ref<void()>([&]() {
+ Actions.CodeCompleteFunctionQualifiers(DS, D, &VS);
+ }));
D.ExtendWithDeclSpec(DS);
if (D.isFunctionDeclarator()) {
@@ -2724,8 +2728,10 @@
// initialize it.
ThisDecl = VT->getTemplatedDecl();
- if (ThisDecl && AccessAttrs)
+ if (ThisDecl) {
Actions.ProcessDeclAttributeList(getCurScope(), ThisDecl, AccessAttrs);
+ Actions.ProcessAPINotes(ThisDecl);
+ }
}
// Error recovery might have converted a non-static member into a static
diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp
index 81761bf..3a24ad9 100644
--- a/lib/Parse/ParseObjc.cpp
+++ b/lib/Parse/ParseObjc.cpp
@@ -212,6 +212,8 @@
/// __attribute__((unavailable))
/// __attribute__((objc_exception)) - used by NSException on 64-bit
/// __attribute__((objc_root_class))
+/// __attribute__((objc_subclassing_restricted))
+/// __attribute__((objc_complete_definition))
///
Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
ParsedAttributes &attrs) {
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index 52e5194..e1a99fd 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -87,6 +87,11 @@
PP.addCommentHandler(CommentSemaHandler.get());
PP.setCodeCompletionHandler(*this);
+
+ Actions.ParseTypeFromStringCallback =
+ [this](StringRef typeStr, StringRef context, SourceLocation includeLoc) {
+ return this->parseTypeFromString(typeStr, context, includeLoc);
+ };
}
DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) {
@@ -419,6 +424,9 @@
//===----------------------------------------------------------------------===//
Parser::~Parser() {
+ // Clear out the parse-type-from-string callback.
+ Actions.ParseTypeFromStringCallback = nullptr;
+
// If we still have scopes active, delete the scope tree.
delete getCurScope();
Actions.CurScope = nullptr;
diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt
index 7a59732..c92879a 100644
--- a/lib/Sema/CMakeLists.txt
+++ b/lib/Sema/CMakeLists.txt
@@ -20,6 +20,7 @@
Sema.cpp
SemaAccess.cpp
SemaAttr.cpp
+ SemaAPINotes.cpp
SemaCXXScopeSpec.cpp
SemaCast.cpp
SemaChecking.cpp
@@ -61,4 +62,5 @@
clangBasic
clangEdit
clangLex
+ clangAPINotes
)
diff --git a/lib/Sema/IdentifierResolver.cpp b/lib/Sema/IdentifierResolver.cpp
index 0bdb194..53263ba 100644
--- a/lib/Sema/IdentifierResolver.cpp
+++ b/lib/Sema/IdentifierResolver.cpp
@@ -381,7 +381,7 @@
PP.getExternalSource()->updateOutOfDateIdentifier(II);
if (II.isFromAST())
- II.setFETokenInfoChangedSinceDeserialization();
+ II.setChangedSinceDeserialization();
}
//===----------------------------------------------------------------------===//
diff --git a/lib/Sema/ScopeInfo.cpp b/lib/Sema/ScopeInfo.cpp
index 3970b41..58d44ba 100644
--- a/lib/Sema/ScopeInfo.cpp
+++ b/lib/Sema/ScopeInfo.cpp
@@ -184,7 +184,7 @@
}
// Has this weak object been seen before?
- FunctionScopeInfo::WeakObjectUseMap::iterator Uses;
+ FunctionScopeInfo::WeakObjectUseMap::iterator Uses = WeakObjectUses.end();
if (const ObjCPropertyRefExpr *RefExpr = dyn_cast<ObjCPropertyRefExpr>(E)) {
if (!RefExpr->isObjectReceiver())
return;
@@ -197,10 +197,10 @@
}
else if (const ObjCIvarRefExpr *IvarE = dyn_cast<ObjCIvarRefExpr>(E))
Uses = WeakObjectUses.find(WeakObjectProfileTy(IvarE));
- else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
- Uses = WeakObjectUses.find(WeakObjectProfileTy(DRE));
- else if (const ObjCMessageExpr *MsgE = dyn_cast<ObjCMessageExpr>(E)) {
- Uses = WeakObjectUses.end();
+ else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (isa<VarDecl>(DRE->getDecl()))
+ Uses = WeakObjectUses.find(WeakObjectProfileTy(DRE));
+ } else if (const ObjCMessageExpr *MsgE = dyn_cast<ObjCMessageExpr>(E)) {
if (const ObjCMethodDecl *MD = MsgE->getMethodDecl()) {
if (const ObjCPropertyDecl *Prop = MD->findPropertyDecl()) {
Uses =
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 412f944..221058c 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -77,7 +77,8 @@
isMultiplexExternalSource(false), FPFeatures(pp.getLangOpts()),
LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer),
Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
- CollectStats(false), CodeCompleter(CodeCompleter),
+ APINotes(SourceMgr, LangOpts), CollectStats(false),
+ CodeCompleter(CodeCompleter),
CurContext(nullptr), OriginalLexicalContext(nullptr),
MSStructPragmaOn(false),
MSPointerToMemberRepresentationMethod(
diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp
new file mode 100644
index 0000000..078445e
--- /dev/null
+++ b/lib/Sema/SemaAPINotes.cpp
@@ -0,0 +1,840 @@
+//===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the mapping from API notes to declaration attributes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Sema/SemaInternal.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/APINotes/APINotesReader.h"
+using namespace clang;
+using clang::api_notes::VersionedInfoRole;
+
+namespace {
+ struct VersionedInfoMetadata {
+ VersionTuple Version;
+ VersionedInfoRole Role;
+
+ /*implicit*/ VersionedInfoMetadata(VersionedInfoRole role) : Role(role) {
+ assert(role != VersionedInfoRole::Versioned &&
+ "explicit version required");
+ }
+
+ /*implicit*/ VersionedInfoMetadata(VersionTuple version)
+ : Version(version), Role(VersionedInfoRole::Versioned) {}
+ };
+} // end anonymous namespace
+
+/// Determine whether this is a multi-level pointer type.
+static bool isMultiLevelPointerType(QualType type) {
+ QualType pointee = type->getPointeeType();
+ if (pointee.isNull())
+ return false;
+
+ return pointee->isAnyPointerType() || pointee->isObjCObjectPointerType() ||
+ pointee->isMemberPointerType();
+}
+
+// Apply nullability to the given declaration.
+static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability,
+ VersionedInfoMetadata metadata) {
+ bool overrideExisting;
+ switch (metadata.Role) {
+ case VersionedInfoRole::AugmentSource:
+ overrideExisting = false;
+ break;
+
+ case VersionedInfoRole::ReplaceSource:
+ overrideExisting = true;
+ break;
+
+ case VersionedInfoRole::Versioned:
+ // FIXME: Record versioned info?
+ return;
+ }
+
+ QualType type;
+
+ // Nullability for a function/method appertains to the retain type.
+ if (auto function = dyn_cast<FunctionDecl>(decl)) {
+ type = function->getReturnType();
+ } else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) {
+ type = method->getReturnType();
+ } else if (auto value = dyn_cast<ValueDecl>(decl)) {
+ type = value->getType();
+ } else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) {
+ type = property->getType();
+ } else {
+ return;
+ }
+
+ // Check the nullability specifier on this type.
+ QualType origType = type;
+ S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(),
+ /*isContextSensitive=*/false,
+ isa<ParmVarDecl>(decl), /*implicit=*/true,
+ overrideExisting);
+ if (type.getTypePtr() == origType.getTypePtr())
+ return;
+
+ if (auto function = dyn_cast<FunctionDecl>(decl)) {
+ const FunctionType *fnType = function->getType()->castAs<FunctionType>();
+ if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType)) {
+ function->setType(S.Context.getFunctionType(type, proto->getParamTypes(),
+ proto->getExtProtoInfo()));
+ } else {
+ function->setType(S.Context.getFunctionNoProtoType(type,
+ fnType->getExtInfo()));
+ }
+ } else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) {
+ method->setReturnType(type);
+
+ // Make it a context-sensitive keyword if we can.
+ if (!isMultiLevelPointerType(type)) {
+ method->setObjCDeclQualifier(
+ Decl::ObjCDeclQualifier(method->getObjCDeclQualifier() |
+ Decl::OBJC_TQ_CSNullability));
+ }
+ } else if (auto value = dyn_cast<ValueDecl>(decl)) {
+ value->setType(type);
+
+ // Make it a context-sensitive keyword if we can.
+ if (auto parm = dyn_cast<ParmVarDecl>(decl)) {
+ if (parm->isObjCMethodParameter() && !isMultiLevelPointerType(type)) {
+ parm->setObjCDeclQualifier(
+ Decl::ObjCDeclQualifier(parm->getObjCDeclQualifier() |
+ Decl::OBJC_TQ_CSNullability));
+ }
+ }
+ } else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) {
+ property->setType(type, property->getTypeSourceInfo());
+
+ // Make it a property attribute if we can.
+ if (!isMultiLevelPointerType(type)) {
+ property->setPropertyAttributes(
+ ObjCPropertyDecl::OBJC_PR_null_resettable);
+ }
+ } else {
+ llvm_unreachable("cannot handle nullability here");
+ }
+}
+
+/// Copy a string into ASTContext-allocated memory.
+static StringRef CopyString(ASTContext &ctx, StringRef string) {
+ void *mem = ctx.Allocate(string.size(), alignof(char));
+ memcpy(mem, string.data(), string.size());
+ return StringRef(static_cast<char *>(mem), string.size());
+}
+
+namespace {
+ template <typename A>
+ struct AttrKindFor {};
+
+#define ATTR(X) \
+ template <> struct AttrKindFor<X##Attr> { \
+ static const attr::Kind value = attr::X; \
+ };
+#include "clang/Basic/AttrList.inc"
+
+ /// Handle an attribute introduced by API notes.
+ ///
+ /// \param shouldAddAttribute Whether we should add a new attribute
+ /// (otherwise, we might remove an existing attribute).
+ /// \param createAttr Create the new attribute to be added.
+ template<typename A>
+ void handleAPINotedAttribute(
+ Sema &S, Decl *D, bool shouldAddAttribute,
+ VersionedInfoMetadata metadata,
+ llvm::function_ref<A *()> createAttr,
+ llvm::function_ref<specific_attr_iterator<A>(Decl*)> getExistingAttr) {
+ switch (metadata.Role) {
+ case VersionedInfoRole::AugmentSource:
+ // If we're not adding an attribute, there's nothing to do.
+ if (!shouldAddAttribute) return;
+
+ // If the attribute is already present, we're done.
+ if (getExistingAttr(D) != D->specific_attr_end<A>()) return;
+
+ // Add the attribute.
+ if (auto attr = createAttr())
+ D->addAttr(attr);
+ break;
+
+ case VersionedInfoRole::ReplaceSource: {
+ auto end = D->specific_attr_end<A>();
+ auto existing = getExistingAttr(D);
+ if (existing != end) {
+ // Remove the existing attribute, and treat it as a superseded
+ // non-versioned attribute.
+ auto *versioned =
+ SwiftVersionedAttr::CreateImplicit(S.Context, clang::VersionTuple(),
+ *existing);
+
+ D->getAttrs().erase(existing.getCurrent());
+ D->addAttr(versioned);
+ }
+
+ // If we're supposed to add a new attribute, do so.
+ if (shouldAddAttribute) {
+ if (auto attr = createAttr()) {
+ D->addAttr(attr);
+ }
+ }
+ break;
+ }
+
+ case VersionedInfoRole::Versioned:
+ // FIXME: Include the actual version instead of making one up.
+ if (shouldAddAttribute) {
+ if (auto attr = createAttr()) {
+ auto *versioned =
+ SwiftVersionedAttr::CreateImplicit(S.Context, metadata.Version,
+ attr);
+ D->addAttr(versioned);
+ }
+ } else {
+ // FIXME: This isn't preserving enough information for things like
+ // availability, where we're trying to remove a /specific/ kind of
+ // attribute.
+ auto *versioned =
+ SwiftVersionedRemovalAttr::CreateImplicit(S.Context,
+ metadata.Version,
+ AttrKindFor<A>::value);
+ D->addAttr(versioned);
+ }
+ break;
+ }
+ }
+
+ template<typename A>
+ void handleAPINotedAttribute(
+ Sema &S, Decl *D, bool shouldAddAttribute,
+ VersionedInfoMetadata metadata,
+ llvm::function_ref<A *()> createAttr) {
+ handleAPINotedAttribute<A>(S, D, shouldAddAttribute, metadata, createAttr,
+ [](Decl *decl) {
+ return decl->specific_attr_begin<A>();
+ });
+ }
+}
+
+static void ProcessAPINotes(Sema &S, Decl *D,
+ const api_notes::CommonEntityInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Availability
+ if (info.Unavailable) {
+ handleAPINotedAttribute<UnavailableAttr>(S, D, true, metadata,
+ [&] {
+ return UnavailableAttr::CreateImplicit(S.Context,
+ CopyString(S.Context,
+ info.UnavailableMsg));
+ });
+ }
+
+ if (info.UnavailableInSwift) {
+ handleAPINotedAttribute<AvailabilityAttr>(S, D, true, metadata, [&] {
+ return AvailabilityAttr::CreateImplicit(
+ S.Context,
+ &S.Context.Idents.get("swift"),
+ VersionTuple(),
+ VersionTuple(),
+ VersionTuple(),
+ /*Unavailable=*/true,
+ CopyString(S.Context, info.UnavailableMsg),
+ /*Strict=*/false,
+ /*Replacement=*/StringRef());
+ },
+ [](Decl *decl) {
+ auto existing = decl->specific_attr_begin<AvailabilityAttr>(),
+ end = decl->specific_attr_end<AvailabilityAttr>();
+ while (existing != end) {
+ if (auto platform = (*existing)->getPlatform()) {
+ if (platform->isStr("swift"))
+ break;
+ }
+
+ ++existing;
+ }
+
+ return existing;
+ });
+ }
+
+ // swift_private
+ if (auto swiftPrivate = info.isSwiftPrivate()) {
+ handleAPINotedAttribute<SwiftPrivateAttr>(S, D, *swiftPrivate, metadata,
+ [&] {
+ return SwiftPrivateAttr::CreateImplicit(S.Context);
+ });
+ }
+
+ // swift_name
+ if (!info.SwiftName.empty()) {
+ handleAPINotedAttribute<SwiftNameAttr>(S, D, true, metadata,
+ [&]() -> SwiftNameAttr * {
+ auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note");
+
+ if (!S.DiagnoseSwiftName(D, info.SwiftName, D->getLocation(),
+ &APINoteName)) {
+ return nullptr;
+ }
+
+ return SwiftNameAttr::CreateImplicit(S.Context,
+ CopyString(S.Context,
+ info.SwiftName));
+ });
+ }
+}
+
+static void ProcessAPINotes(Sema &S, Decl *D,
+ const api_notes::CommonTypeInfo &info,
+ VersionedInfoMetadata metadata) {
+ // swift_bridge
+ if (auto swiftBridge = info.getSwiftBridge()) {
+ handleAPINotedAttribute<SwiftBridgeAttr>(S, D, !swiftBridge->empty(),
+ metadata, [&] {
+ return SwiftBridgeAttr::CreateImplicit(S.Context,
+ CopyString(S.Context,
+ *swiftBridge));
+ });
+ }
+
+ // ns_error_domain
+ if (auto nsErrorDomain = info.getNSErrorDomain()) {
+ handleAPINotedAttribute<NSErrorDomainAttr>(S, D, !nsErrorDomain->empty(),
+ metadata, [&] {
+ return NSErrorDomainAttr::CreateImplicit(
+ S.Context,
+ &S.Context.Idents.get(*nsErrorDomain));
+ });
+ }
+
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info),
+ metadata);
+}
+
+/// Check that the replacement type provided by API notes is reasonable.
+///
+/// This is a very weak form of ABI check.
+static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc,
+ QualType origType,
+ QualType replacementType) {
+ if (S.Context.getTypeSize(origType) !=
+ S.Context.getTypeSize(replacementType)) {
+ S.Diag(loc, diag::err_incompatible_replacement_type)
+ << replacementType << origType;
+ return true;
+ }
+
+ return false;
+}
+
+/// Process API notes for a variable or property.
+static void ProcessAPINotes(Sema &S, Decl *D,
+ const api_notes::VariableInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Type override.
+ if (metadata.Role != VersionedInfoRole::Versioned &&
+ !info.getType().empty() && S.ParseTypeFromStringCallback) {
+ auto parsedType = S.ParseTypeFromStringCallback(info.getType(),
+ "<API Notes>",
+ D->getLocation());
+ if (parsedType.isUsable()) {
+ QualType type = Sema::GetTypeFromParser(parsedType.get());
+ auto typeInfo =
+ S.Context.getTrivialTypeSourceInfo(type, D->getLocation());
+
+ if (auto var = dyn_cast<VarDecl>(D)) {
+ // Make adjustments to parameter types.
+ if (isa<ParmVarDecl>(var)) {
+ type = S.adjustParameterTypeForObjCAutoRefCount(type,
+ D->getLocation());
+ type = S.Context.getAdjustedParameterType(type);
+ }
+
+ if (!checkAPINotesReplacementType(S, var->getLocation(), var->getType(),
+ type)) {
+ var->setType(type);
+ var->setTypeSourceInfo(typeInfo);
+ }
+ } else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) {
+ if (!checkAPINotesReplacementType(S, property->getLocation(),
+ property->getType(),
+ type)) {
+ property->setType(type, typeInfo);
+ }
+ } else {
+ llvm_unreachable("API notes allowed a type on an unknown declaration");
+ }
+ }
+ }
+
+ // Nullability.
+ if (auto Nullability = info.getNullability()) {
+ applyNullability(S, D, *Nullability, metadata);
+ }
+
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for a parameter.
+static void ProcessAPINotes(Sema &S, ParmVarDecl *D,
+ const api_notes::ParamInfo &info,
+ VersionedInfoMetadata metadata) {
+ // noescape
+ if (auto noescape = info.isNoEscape()) {
+ handleAPINotedAttribute<NoEscapeAttr>(S, D, *noescape, metadata, [&] {
+ return NoEscapeAttr::CreateImplicit(S.Context);
+ });
+ }
+
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for a global variable.
+static void ProcessAPINotes(Sema &S, VarDecl *D,
+ const api_notes::GlobalVariableInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for an Objective-C property.
+static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D,
+ const api_notes::ObjCPropertyInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info),
+ metadata);
+ if (auto asAccessors = info.getSwiftImportAsAccessors()) {
+ handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(S, D,
+ *asAccessors,
+ metadata, [&] {
+ return SwiftImportPropertyAsAccessorsAttr::CreateImplicit(S.Context);
+ });
+ }
+}
+
+namespace {
+ typedef llvm::PointerUnion<FunctionDecl *, ObjCMethodDecl *> FunctionOrMethod;
+}
+
+/// Process API notes for a function or method.
+static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
+ const api_notes::FunctionInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Find the declaration itself.
+ FunctionDecl *FD = AnyFunc.dyn_cast<FunctionDecl *>();
+ Decl *D = FD;
+ ObjCMethodDecl *MD = 0;
+ if (!D) {
+ MD = AnyFunc.get<ObjCMethodDecl *>();
+ D = MD;
+ }
+
+ // Nullability of return type.
+ if (info.NullabilityAudited) {
+ applyNullability(S, D, info.getReturnTypeInfo(), metadata);
+ }
+
+ // Parameters.
+ unsigned NumParams;
+ if (FD)
+ NumParams = FD->getNumParams();
+ else
+ NumParams = MD->param_size();
+
+ bool anyTypeChanged = false;
+ for (unsigned I = 0; I != NumParams; ++I) {
+ ParmVarDecl *Param;
+ if (FD)
+ Param = FD->getParamDecl(I);
+ else
+ Param = MD->param_begin()[I];
+
+ QualType paramTypeBefore = Param->getType();
+
+ if (I < info.Params.size()) {
+ ProcessAPINotes(S, Param, info.Params[I], metadata);
+ }
+
+ // Nullability.
+ if (info.NullabilityAudited)
+ applyNullability(S, Param, info.getParamTypeInfo(I), metadata);
+
+ if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr())
+ anyTypeChanged = true;
+ }
+
+ // Result type override.
+ QualType overriddenResultType;
+ if (metadata.Role != VersionedInfoRole::Versioned &&
+ !info.ResultType.empty() && S.ParseTypeFromStringCallback) {
+ auto parsedType = S.ParseTypeFromStringCallback(info.ResultType,
+ "<API Notes>",
+ D->getLocation());
+ if (parsedType.isUsable()) {
+ QualType resultType = Sema::GetTypeFromParser(parsedType.get());
+
+ if (MD) {
+ if (!checkAPINotesReplacementType(S, D->getLocation(),
+ MD->getReturnType(), resultType)) {
+ auto resultTypeInfo =
+ S.Context.getTrivialTypeSourceInfo(resultType, D->getLocation());
+ MD->setReturnType(resultType);
+ MD->setReturnTypeSourceInfo(resultTypeInfo);
+ }
+ } else if (!checkAPINotesReplacementType(S, FD->getLocation(),
+ FD->getReturnType(),
+ resultType)) {
+ overriddenResultType = resultType;
+ anyTypeChanged = true;
+ }
+ }
+ }
+
+ // If the result type or any of the parameter types changed for a function
+ // declaration, we have to rebuild the type.
+ if (FD && anyTypeChanged) {
+ if (const auto *fnProtoType = FD->getType()->getAs<FunctionProtoType>()) {
+ if (overriddenResultType.isNull())
+ overriddenResultType = fnProtoType->getReturnType();
+
+ SmallVector<QualType, 4> paramTypes;
+ for (auto param : FD->parameters()) {
+ paramTypes.push_back(param->getType());
+ }
+ FD->setType(S.Context.getFunctionType(overriddenResultType,
+ paramTypes,
+ fnProtoType->getExtProtoInfo()));
+ } else if (!overriddenResultType.isNull()) {
+ const auto *fnNoProtoType = FD->getType()->castAs<FunctionNoProtoType>();
+ FD->setType(
+ S.Context.getFunctionNoProtoType(overriddenResultType,
+ fnNoProtoType->getExtInfo()));
+ }
+ }
+
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for a global function.
+static void ProcessAPINotes(Sema &S, FunctionDecl *D,
+ const api_notes::GlobalFunctionInfo &info,
+ VersionedInfoMetadata metadata) {
+
+ // Handle common function information.
+ ProcessAPINotes(S, FunctionOrMethod(D),
+ static_cast<const api_notes::FunctionInfo &>(info), metadata);
+}
+
+/// Process API notes for an enumerator.
+static void ProcessAPINotes(Sema &S, EnumConstantDecl *D,
+ const api_notes::EnumConstantInfo &info,
+ VersionedInfoMetadata metadata) {
+
+ // Handle common information.
+ ProcessAPINotes(S, D,
+ static_cast<const api_notes::CommonEntityInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for an Objective-C method.
+static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D,
+ const api_notes::ObjCMethodInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Designated initializers.
+ if (info.DesignatedInit) {
+ handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(S, D, true, metadata,
+ [&] {
+ if (ObjCInterfaceDecl *IFace = D->getClassInterface()) {
+ IFace->setHasDesignatedInitializers();
+ }
+ return ObjCDesignatedInitializerAttr::CreateImplicit(S.Context);
+ });
+ }
+
+ // FIXME: This doesn't work well with versioned API notes.
+ if (metadata.Role == VersionedInfoRole::AugmentSource &&
+ info.getFactoryAsInitKind()
+ == api_notes::FactoryAsInitKind::AsClassMethod &&
+ !D->getAttr<SwiftNameAttr>()) {
+ D->addAttr(SwiftSuppressFactoryAsInitAttr::CreateImplicit(S.Context));
+ }
+
+ // Handle common function information.
+ ProcessAPINotes(S, FunctionOrMethod(D),
+ static_cast<const api_notes::FunctionInfo &>(info), metadata);
+}
+
+/// Process API notes for a tag.
+static void ProcessAPINotes(Sema &S, TagDecl *D,
+ const api_notes::TagInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Handle common type information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for a typedef.
+static void ProcessAPINotes(Sema &S, TypedefNameDecl *D,
+ const api_notes::TypedefInfo &info,
+ VersionedInfoMetadata metadata) {
+ // swift_wrapper
+ using SwiftWrapperKind = api_notes::SwiftWrapperKind;
+
+ if (auto swiftWrapper = info.SwiftWrapper) {
+ handleAPINotedAttribute<SwiftNewtypeAttr>(S, D,
+ *swiftWrapper != SwiftWrapperKind::None, metadata,
+ [&] {
+ SwiftNewtypeAttr::NewtypeKind kind;
+ switch (*swiftWrapper) {
+ case SwiftWrapperKind::None:
+ llvm_unreachable("Shouldn't build an attribute");
+
+ case SwiftWrapperKind::Struct:
+ kind = SwiftNewtypeAttr::NK_Struct;
+ break;
+
+ case SwiftWrapperKind::Enum:
+ kind = SwiftNewtypeAttr::NK_Enum;
+ break;
+ }
+ return SwiftNewtypeAttr::CreateImplicit(
+ S.Context,
+ SwiftNewtypeAttr::GNU_swift_wrapper,
+ kind);
+ });
+ }
+
+ // Handle common type information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for an Objective-C class or protocol.
+static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D,
+ const api_notes::ObjCContextInfo &info,
+ VersionedInfoMetadata metadata) {
+
+ // Handle common type information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for an Objective-C class.
+static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D,
+ const api_notes::ObjCContextInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Handle information common to Objective-C classes and protocols.
+ ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info,
+ metadata);
+}
+
+/// Processes all versions of versioned API notes.
+///
+/// Just dispatches to the various ProcessAPINotes functions in this file.
+template <typename SpecificDecl, typename SpecificInfo>
+static void ProcessVersionedAPINotes(
+ Sema &S, SpecificDecl *D,
+ const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
+ unsigned Selected = Info.getSelected().getValueOr(Info.size());
+
+ VersionTuple Version;
+ SpecificInfo InfoSlice;
+ for (unsigned i = 0, e = Info.size(); i != e; ++i) {
+ std::tie(Version, InfoSlice) = Info[i];
+ if (i != Selected) {
+ ProcessAPINotes(S, D, InfoSlice, Version);
+ } else {
+ ProcessAPINotes(S, D, InfoSlice, Info.getSelectedRole());
+ }
+ }
+}
+
+/// Process API notes that are associated with this declaration, mapping them
+/// to attributes as appropriate.
+void Sema::ProcessAPINotes(Decl *D) {
+ if (!D)
+ return;
+
+ // Globals.
+ if (D->getDeclContext()->isFileContext()) {
+ // Global variables.
+ if (auto VD = dyn_cast<VarDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupGlobalVariable(VD->getName());
+ ProcessVersionedAPINotes(*this, VD, Info);
+ }
+
+ return;
+ }
+
+ // Global functions.
+ if (auto FD = dyn_cast<FunctionDecl>(D)) {
+ if (FD->getDeclName().isIdentifier()) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupGlobalFunction(FD->getName());
+ ProcessVersionedAPINotes(*this, FD, Info);
+ }
+ }
+
+ return;
+ }
+
+ // Objective-C classes.
+ if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupObjCClassInfo(Class->getName());
+ ProcessVersionedAPINotes(*this, Class, Info);
+ }
+
+ return;
+ }
+
+ // Objective-C protocols.
+ if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName());
+ ProcessVersionedAPINotes(*this, Protocol, Info);
+ }
+
+ return;
+ }
+
+ // Tags
+ if (auto Tag = dyn_cast<TagDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupTag(Tag->getName());
+ ProcessVersionedAPINotes(*this, Tag, Info);
+ }
+
+ return;
+ }
+
+ // Typedefs
+ if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupTypedef(Typedef->getName());
+ ProcessVersionedAPINotes(*this, Typedef, Info);
+ }
+
+ return;
+ }
+
+ return;
+ }
+
+ // Enumerators.
+ if (D->getDeclContext()->getRedeclContext()->isFileContext()) {
+ if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupEnumConstant(EnumConstant->getName());
+ ProcessVersionedAPINotes(*this, EnumConstant, Info);
+ }
+
+ return;
+ }
+ }
+
+ if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(D->getDeclContext())) {
+ // Location function that looks up an Objective-C context.
+ auto GetContext = [&](api_notes::APINotesReader *Reader)
+ -> Optional<api_notes::ContextID> {
+ if (auto Protocol = dyn_cast<ObjCProtocolDecl>(ObjCContainer)) {
+ if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName()))
+ return *Found;
+
+ return None;
+ }
+
+ if (auto Impl = dyn_cast<ObjCCategoryImplDecl>(ObjCContainer)) {
+ if (auto Cat = Impl->getCategoryDecl())
+ ObjCContainer = Cat;
+ else
+ return None;
+ }
+
+ if (auto Category = dyn_cast<ObjCCategoryDecl>(ObjCContainer)) {
+ if (Category->getClassInterface())
+ ObjCContainer = Category->getClassInterface();
+ else
+ return None;
+ }
+
+ if (auto Impl = dyn_cast<ObjCImplDecl>(ObjCContainer)) {
+ if (Impl->getClassInterface())
+ ObjCContainer = Impl->getClassInterface();
+ else
+ return None;
+ }
+
+ if (auto Class = dyn_cast<ObjCInterfaceDecl>(ObjCContainer)) {
+ if (auto Found = Reader->lookupObjCClassID(Class->getName()))
+ return *Found;
+
+ return None;
+
+ }
+
+ return None;
+ };
+
+ // Objective-C methods.
+ if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ if (auto Context = GetContext(Reader)) {
+ // Map the selector.
+ Selector Sel = Method->getSelector();
+ SmallVector<StringRef, 2> SelPieces;
+ if (Sel.isUnarySelector())
+ SelPieces.push_back(Sel.getNameForSlot(0));
+ else {
+ for (unsigned i = 0, n = Sel.getNumArgs(); i != n; ++i)
+ SelPieces.push_back(Sel.getNameForSlot(i));
+ }
+
+ api_notes::ObjCSelectorRef SelectorRef;
+ SelectorRef.NumPieces = Sel.getNumArgs();
+ SelectorRef.Identifiers = SelPieces;
+
+ auto Info = Reader->lookupObjCMethod(*Context, SelectorRef,
+ Method->isInstanceMethod());
+ ProcessVersionedAPINotes(*this, Method, Info);
+ }
+ }
+ }
+
+ // Objective-C properties.
+ if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ if (auto Context = GetContext(Reader)) {
+ bool isInstanceProperty =
+ (Property->getPropertyAttributesAsWritten() &
+ ObjCPropertyDecl::OBJC_PR_class) == 0;
+ auto Info = Reader->lookupObjCProperty(*Context, Property->getName(),
+ isInstanceProperty);
+ ProcessVersionedAPINotes(*this, Property, Info);
+ }
+ }
+
+ return;
+ }
+
+ return;
+ }
+}
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 3aedb2a..2565f0c 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -4623,20 +4623,6 @@
return SLCT_NotALiteral;
}
- case Stmt::ObjCMessageExprClass: {
- const auto *ME = cast<ObjCMessageExpr>(E);
- if (const auto *ND = ME->getMethodDecl()) {
- if (const auto *FA = ND->getAttr<FormatArgAttr>()) {
- unsigned ArgIndex = FA->getFormatIdx();
- const Expr *Arg = ME->getArg(ArgIndex - 1);
- return checkFormatStringExpr(
- S, Arg, Args, HasVAListArg, format_idx, firstDataArg, Type,
- CallType, InFunctionCall, CheckedVarArgs, UncoveredArg, Offset);
- }
- }
-
- return SLCT_NotALiteral;
- }
case Stmt::ObjCStringLiteralClass:
case Stmt::StringLiteralClass: {
const StringLiteral *StrE = nullptr;
diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp
index 94cfc4b..c85fda0 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -1334,8 +1334,9 @@
Builder.AddChunk(CodeCompletionString::CK_RightParen);
Results.AddResult(Result(Builder.TakeString()));
}
- }
-
+ } else
+ Results.AddResult(Result("__auto_type", CCP_Type));
+
// GNU extensions
if (LangOpts.GNUMode) {
// FIXME: Enable when we actually support decimal floating point.
@@ -1370,6 +1371,21 @@
// in C++0x as a type specifier.
Results.AddResult(Result("extern"));
Results.AddResult(Result("static"));
+
+ if (LangOpts.CPlusPlus11) {
+ CodeCompletionAllocator &Allocator = Results.getAllocator();
+ CodeCompletionBuilder Builder(Allocator, Results.getCodeCompletionTUInfo());
+
+ // alignas
+ Builder.AddTypedTextChunk("alignas");
+ Builder.AddChunk(CodeCompletionString::CK_LeftParen);
+ Builder.AddPlaceholderChunk("expression");
+ Builder.AddChunk(CodeCompletionString::CK_RightParen);
+ Results.AddResult(Result(Builder.TakeString()));
+
+ Results.AddResult(Result("constexpr"));
+ Results.AddResult(Result("thread_local"));
+ }
}
static void AddFunctionSpecifiers(Sema::ParserCompletionContext CCC,
@@ -1527,6 +1543,21 @@
Results.AddResult(CodeCompletionResult(Builder.TakeString()));
}
+static void AddStaticAssertResult(CodeCompletionBuilder &Builder,
+ ResultBuilder &Results,
+ const LangOptions &LangOpts) {
+ if (!LangOpts.CPlusPlus11)
+ return;
+
+ Builder.AddTypedTextChunk("static_assert");
+ Builder.AddChunk(CodeCompletionString::CK_LeftParen);
+ Builder.AddPlaceholderChunk("expression");
+ Builder.AddChunk(CodeCompletionString::CK_Comma);
+ Builder.AddPlaceholderChunk("message");
+ Builder.AddChunk(CodeCompletionString::CK_RightParen);
+ Results.AddResult(CodeCompletionResult(Builder.TakeString()));
+}
+
/// \brief Add language constructs that show up for "ordinary" names.
static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC,
Scope *S,
@@ -1611,6 +1642,8 @@
Results.AddResult(Result(Builder.TakeString()));
}
+ AddStaticAssertResult(Builder, Results, SemaRef.getLangOpts());
+
if (CCC == Sema::PCC_Class) {
AddTypedefResult(Results);
@@ -1824,6 +1857,8 @@
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("identifier");
Results.AddResult(Result(Builder.TakeString()));
+
+ AddStaticAssertResult(Builder, Results, SemaRef.getLangOpts());
}
// Fall through (for statement expressions).
@@ -3482,6 +3517,11 @@
Results.AddResult(Result("restrict"));
if (getLangOpts().CPlusPlus) {
+ if (getLangOpts().CPlusPlus11 &&
+ (DS.getTypeSpecType() == DeclSpec::TST_class ||
+ DS.getTypeSpecType() == DeclSpec::TST_struct))
+ Results.AddResult("final");
+
if (AllowNonIdentifiers) {
Results.AddResult(Result("operator"));
}
@@ -4012,30 +4052,54 @@
Results.data(),Results.size());
}
+static void AddTypeQualifierResults(DeclSpec &DS, ResultBuilder &Results,
+ const LangOptions &LangOpts) {
+ if (!(DS.getTypeQualifiers() & DeclSpec::TQ_const))
+ Results.AddResult("const");
+ if (!(DS.getTypeQualifiers() & DeclSpec::TQ_volatile))
+ Results.AddResult("volatile");
+ if (LangOpts.C99 && !(DS.getTypeQualifiers() & DeclSpec::TQ_restrict))
+ Results.AddResult("restrict");
+ if (LangOpts.C11 && !(DS.getTypeQualifiers() & DeclSpec::TQ_atomic))
+ Results.AddResult("_Atomic");
+ if (LangOpts.MSVCCompat && !(DS.getTypeQualifiers() & DeclSpec::TQ_unaligned))
+ Results.AddResult("__unaligned");
+}
+
void Sema::CodeCompleteTypeQualifiers(DeclSpec &DS) {
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
CodeCompleter->getCodeCompletionTUInfo(),
CodeCompletionContext::CCC_TypeQualifiers);
Results.EnterNewScope();
- if (!(DS.getTypeQualifiers() & DeclSpec::TQ_const))
- Results.AddResult("const");
- if (!(DS.getTypeQualifiers() & DeclSpec::TQ_volatile))
- Results.AddResult("volatile");
- if (getLangOpts().C99 &&
- !(DS.getTypeQualifiers() & DeclSpec::TQ_restrict))
- Results.AddResult("restrict");
- if (getLangOpts().C11 &&
- !(DS.getTypeQualifiers() & DeclSpec::TQ_atomic))
- Results.AddResult("_Atomic");
- if (getLangOpts().MSVCCompat &&
- !(DS.getTypeQualifiers() & DeclSpec::TQ_unaligned))
- Results.AddResult("__unaligned");
+ AddTypeQualifierResults(DS, Results, LangOpts);
Results.ExitScope();
HandleCodeCompleteResults(this, CodeCompleter,
Results.getCompletionContext(),
Results.data(), Results.size());
}
+void Sema::CodeCompleteFunctionQualifiers(DeclSpec &DS, Declarator &D,
+ const VirtSpecifiers *VS) {
+ ResultBuilder Results(*this, CodeCompleter->getAllocator(),
+ CodeCompleter->getCodeCompletionTUInfo(),
+ CodeCompletionContext::CCC_TypeQualifiers);
+ Results.EnterNewScope();
+ AddTypeQualifierResults(DS, Results, LangOpts);
+ if (LangOpts.CPlusPlus11) {
+ Results.AddResult("noexcept");
+ if (D.getContext() == Declarator::MemberContext && !D.isCtorOrDtor() &&
+ !D.isStaticMember()) {
+ if (!VS || !VS->isFinalSpecified())
+ Results.AddResult("final");
+ if (!VS || !VS->isOverrideSpecified())
+ Results.AddResult("override");
+ }
+ }
+ Results.ExitScope();
+ HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
+ Results.data(), Results.size());
+}
+
void Sema::CodeCompleteBracketDeclarator(Scope *S) {
CodeCompleteExpression(S, QualType(getASTContext().getSizeType()));
}
@@ -5230,24 +5294,22 @@
/// when it has the same number of parameters as we have selector identifiers.
///
/// \param Results the structure into which we'll add results.
-static void AddObjCMethods(ObjCContainerDecl *Container,
- bool WantInstanceMethods,
- ObjCMethodKind WantKind,
+static void AddObjCMethods(ObjCContainerDecl *Container,
+ bool WantInstanceMethods, ObjCMethodKind WantKind,
ArrayRef<IdentifierInfo *> SelIdents,
DeclContext *CurContext,
- VisitedSelectorSet &Selectors,
- bool AllowSameLength,
- ResultBuilder &Results,
- bool InOriginalClass = true) {
+ VisitedSelectorSet &Selectors, bool AllowSameLength,
+ ResultBuilder &Results, bool InOriginalClass = true,
+ bool IsRootClass = false) {
typedef CodeCompletionResult Result;
Container = getContainerDef(Container);
ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(Container);
- bool isRootClass = IFace && !IFace->getSuperClass();
+ IsRootClass = IsRootClass || (IFace && !IFace->getSuperClass());
for (auto *M : Container->methods()) {
// The instance methods on the root class can be messaged via the
// metaclass.
if (M->isInstanceMethod() == WantInstanceMethods ||
- (isRootClass && !WantInstanceMethods)) {
+ (IsRootClass && !WantInstanceMethods)) {
// Check whether the selector identifiers we've been given are a
// subset of the identifiers for this particular method.
if (!isAcceptableObjCMethod(M, WantKind, SelIdents, AllowSameLength))
@@ -5273,8 +5335,8 @@
for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
E = Protocols.end();
I != E; ++I)
- AddObjCMethods(*I, WantInstanceMethods, WantKind, SelIdents,
- CurContext, Selectors, AllowSameLength, Results, false);
+ AddObjCMethods(*I, WantInstanceMethods, WantKind, SelIdents, CurContext,
+ Selectors, AllowSameLength, Results, false, IsRootClass);
}
}
@@ -5283,43 +5345,43 @@
// Add methods in protocols.
for (auto *I : IFace->protocols())
- AddObjCMethods(I, WantInstanceMethods, WantKind, SelIdents,
- CurContext, Selectors, AllowSameLength, Results, false);
-
+ AddObjCMethods(I, WantInstanceMethods, WantKind, SelIdents, CurContext,
+ Selectors, AllowSameLength, Results, false, IsRootClass);
+
// Add methods in categories.
for (auto *CatDecl : IFace->known_categories()) {
AddObjCMethods(CatDecl, WantInstanceMethods, WantKind, SelIdents,
- CurContext, Selectors, AllowSameLength,
- Results, InOriginalClass);
-
+ CurContext, Selectors, AllowSameLength, Results,
+ InOriginalClass, IsRootClass);
+
// Add a categories protocol methods.
const ObjCList<ObjCProtocolDecl> &Protocols
= CatDecl->getReferencedProtocols();
for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
E = Protocols.end();
I != E; ++I)
- AddObjCMethods(*I, WantInstanceMethods, WantKind, SelIdents,
- CurContext, Selectors, AllowSameLength,
- Results, false);
-
+ AddObjCMethods(*I, WantInstanceMethods, WantKind, SelIdents, CurContext,
+ Selectors, AllowSameLength, Results, false, IsRootClass);
+
// Add methods in category implementations.
if (ObjCCategoryImplDecl *Impl = CatDecl->getImplementation())
- AddObjCMethods(Impl, WantInstanceMethods, WantKind, SelIdents,
- CurContext, Selectors, AllowSameLength,
- Results, InOriginalClass);
+ AddObjCMethods(Impl, WantInstanceMethods, WantKind, SelIdents, CurContext,
+ Selectors, AllowSameLength, Results, InOriginalClass,
+ IsRootClass);
}
// Add methods in superclass.
+ // Avoid passing in IsRootClass since root classes won't have super classes.
if (IFace->getSuperClass())
- AddObjCMethods(IFace->getSuperClass(), WantInstanceMethods, WantKind,
- SelIdents, CurContext, Selectors,
- AllowSameLength, Results, false);
+ AddObjCMethods(IFace->getSuperClass(), WantInstanceMethods, WantKind,
+ SelIdents, CurContext, Selectors, AllowSameLength, Results,
+ /*IsRootClass=*/false);
// Add methods in our implementation, if any.
if (ObjCImplementationDecl *Impl = IFace->getImplementation())
- AddObjCMethods(Impl, WantInstanceMethods, WantKind, SelIdents,
- CurContext, Selectors, AllowSameLength,
- Results, InOriginalClass);
+ AddObjCMethods(Impl, WantInstanceMethods, WantKind, SelIdents, CurContext,
+ Selectors, AllowSameLength, Results, InOriginalClass,
+ IsRootClass);
}
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index adcf2ee..eff3aeb 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -2307,6 +2307,10 @@
NewAttr = S.mergeMinSizeAttr(D, MA->getRange(), AttrSpellingListIndex);
else if (const auto *OA = dyn_cast<OptimizeNoneAttr>(Attr))
NewAttr = S.mergeOptimizeNoneAttr(D, OA->getRange(), AttrSpellingListIndex);
+ else if (const auto *SNA = dyn_cast<SwiftNameAttr>(Attr))
+ NewAttr = S.mergeSwiftNameAttr(D, SNA->getRange(), SNA->getName(),
+ AMK == Sema::AMK_Override,
+ AttrSpellingListIndex);
else if (const auto *InternalLinkageA = dyn_cast<InternalLinkageAttr>(Attr))
NewAttr = S.mergeInternalLinkageAttr(
D, InternalLinkageA->getRange(),
@@ -2324,6 +2328,8 @@
(AMK == Sema::AMK_Override ||
AMK == Sema::AMK_ProtocolImplementation))
NewAttr = nullptr;
+ else if (isa<SwiftPrivateAttr>(Attr) && AMK == Sema::AMK_Override)
+ NewAttr = nullptr;
else if (const auto *UA = dyn_cast<UuidAttr>(Attr))
NewAttr = S.mergeUuidAttr(D, UA->getRange(), AttrSpellingListIndex,
UA->getGuid());
@@ -11370,10 +11376,8 @@
}
}
-ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
- SourceLocation NameLoc, IdentifierInfo *Name,
- QualType T, TypeSourceInfo *TSInfo,
- StorageClass SC) {
+QualType Sema::adjustParameterTypeForObjCAutoRefCount(QualType T,
+ SourceLocation Loc) {
// In ARC, infer a lifetime qualifier for appropriate parameter types.
if (getLangOpts().ObjCAutoRefCount &&
T.getObjCLifetime() == Qualifiers::OCL_None &&
@@ -11388,7 +11392,7 @@
if (!T.isConstQualified()) {
DelayedDiagnostics.add(
sema::DelayedDiagnostic::makeForbiddenType(
- NameLoc, diag::err_arc_array_param_no_ownership, T, false));
+ Loc, diag::err_arc_array_param_no_ownership, T, false));
}
lifetime = Qualifiers::OCL_ExplicitNone;
} else {
@@ -11397,6 +11401,16 @@
T = Context.getLifetimeQualifiedType(T, lifetime);
}
+ return T;
+}
+
+ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
+ SourceLocation NameLoc, IdentifierInfo *Name,
+ QualType T, TypeSourceInfo *TSInfo,
+ StorageClass SC) {
+ // Perform Objective-C ARC adjustments.
+ T = adjustParameterTypeForObjCAutoRefCount(T, NameLoc);
+
ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name,
Context.getAdjustedParameterType(T),
TSInfo, SC, nullptr);
@@ -12149,7 +12163,9 @@
// Always attach attributes to the underlying decl.
if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D))
D = TD->getTemplatedDecl();
- ProcessDeclAttributeList(S, D, Attrs.getList());
+
+ ProcessDeclAttributeList(S, D, Attrs.getList());
+ ProcessAPINotes(D);
if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D))
if (Method->isStatic())
@@ -13491,6 +13507,7 @@
if (Attr)
ProcessDeclAttributeList(S, New, Attr);
+ ProcessAPINotes(New);
// Set the lexical context. If the tag has a C++ scope specifier, the
// lexical context will be different from the semantic context.
@@ -14721,6 +14738,7 @@
if (Attr)
ProcessDeclAttributeList(S, Record, Attr);
+ ProcessAPINotes(Record);
}
/// \brief Determine whether the given integral value is representable within
@@ -15020,6 +15038,8 @@
// Process attributes.
if (Attr) ProcessDeclAttributeList(S, New, Attr);
+ ProcessAPINotes(New);
+
// Register this decl in the current scope stack.
New->setAccess(TheEnumDecl->getAccess());
PushOnScopeChains(New, S);
@@ -15243,6 +15263,7 @@
if (Attr)
ProcessDeclAttributeList(S, Enum, Attr);
+ ProcessAPINotes(Enum);
if (Enum->isDependentType()) {
for (unsigned i = 0, e = Elements.size(); i != e; ++i) {
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index c6a5bc7..fba9aea 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -1476,6 +1476,26 @@
Attr.getAttributeSpellingListIndex()));
}
+static void handleNoEscapeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ ParmVarDecl *PD = dyn_cast<ParmVarDecl>(D);
+ if (!PD)
+ return;
+
+ // noescape only applies to pointer types.
+ QualType T = PD->getType();
+ if (!T->isAnyPointerType() && !T->isBlockPointerType() &&
+ !T->isReferenceType() && !T->isArrayType() &&
+ !T->isMemberPointerType()) {
+ S.Diag(Attr.getLoc(), diag::warn_attribute_noescape_non_pointer)
+ << T;
+ return;
+ }
+
+ D->addAttr(::new (S.Context) NoEscapeAttr(
+ Attr.getRange(), S.Context,
+ Attr.getAttributeSpellingListIndex()));
+}
+
static void handleAssumeAlignedAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
Expr *E = Attr.getArgAsExpr(0),
@@ -2322,6 +2342,15 @@
dyn_cast_or_null<StringLiteral>(Attr.getReplacementExpr()))
Replacement = SE->getString();
+ if (II->getName() == "swift") {
+ if (Introduced.isValid() || Obsoleted.isValid() ||
+ (!IsUnavailable && !Deprecated.isValid())) {
+ S.Diag(Attr.getLoc(),
+ diag::warn_availability_swift_unavailable_deprecated_only);
+ return;
+ }
+ }
+
AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, Attr.getRange(), II,
false/*Implicit*/,
Introduced.Version,
@@ -3845,6 +3874,27 @@
AttrSpellingListIndex);
}
+SwiftNameAttr *Sema::mergeSwiftNameAttr(Decl *D, SourceRange Range,
+ StringRef Name, bool Override,
+ unsigned AttrSpellingListIndex) {
+ if (SwiftNameAttr *Inline = D->getAttr<SwiftNameAttr>()) {
+ if (Override) {
+ // FIXME: Warn about an incompatible override.
+ return nullptr;
+ }
+
+ if (Inline->getName() != Name && !Inline->isImplicit()) {
+ Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline;
+ Diag(Range.getBegin(), diag::note_conflicting_attribute);
+ }
+
+ D->dropAttr<SwiftNameAttr>();
+ }
+
+ return ::new (Context) SwiftNameAttr(Range, Context, Name,
+ AttrSpellingListIndex);
+}
+
static void handleAlwaysInlineAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
if (checkAttrMutualExclusion<NotTailCalledAttr>(S, D, Attr.getRange(),
@@ -4622,6 +4672,40 @@
attr.getAttributeSpellingListIndex()));
}
+static void handleNSErrorDomain(Sema &S, Decl *D, const AttributeList &Attr) {
+ if (!isa<TagDecl>(D)) {
+ S.Diag(D->getLocStart(), diag::err_nserrordomain_not_tagdecl)
+ << S.getLangOpts().CPlusPlus;
+ return;
+ }
+ IdentifierLoc *identLoc =
+ Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr;
+ if (!identLoc || !identLoc->Ident) {
+ // Try to locate the argument directly
+ SourceLocation loc = Attr.getLoc();
+ if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0))
+ loc = Attr.getArgAsExpr(0)->getLocStart();
+
+ S.Diag(loc, diag::err_nserrordomain_requires_identifier);
+ return;
+ }
+
+ // Verify that the identifier is a valid decl in the C decl namespace
+ LookupResult lookupResult(S, DeclarationName(identLoc->Ident),
+ SourceLocation(),
+ Sema::LookupNameKind::LookupOrdinaryName);
+ if (!S.LookupName(lookupResult, S.TUScope) ||
+ !lookupResult.getAsSingle<VarDecl>()) {
+ S.Diag(identLoc->Loc, diag::err_nserrordomain_invalid_decl)
+ << identLoc->Ident;
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ NSErrorDomainAttr(Attr.getRange(), S.Context, identLoc->Ident,
+ Attr.getAttributeSpellingListIndex()));
+}
+
static void handleCFAuditedTransferAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
if (checkAttrMutualExclusion<CFUnknownTransferAttr>(S, D, Attr.getRange(),
@@ -4811,6 +4895,421 @@
Attr.getAttributeSpellingListIndex()));
}
+static Optional<unsigned>
+validateSwiftFunctionName(StringRef Name,
+ unsigned &SwiftParamCount,
+ bool &IsSingleParamInit) {
+ SwiftParamCount = 0;
+
+ // Check whether this will be mapped to a getter or setter of a
+ // property.
+ bool isGetter = false;
+ bool isSetter = false;
+ if (Name.startswith("getter:")) {
+ isGetter = true;
+ Name = Name.substr(7);
+ } else if (Name.startswith("setter:")) {
+ isSetter = true;
+ Name = Name.substr(7);
+ }
+
+ if (Name.back() != ')')
+ return diag::warn_attr_swift_name_function;
+
+ StringRef BaseName, Parameters;
+ std::tie(BaseName, Parameters) = Name.split('(');
+
+ // Split at the first '.', if it exists, which separates the context
+ // name from the base name.
+ StringRef ContextName;
+ bool IsMember = false;
+ std::tie(ContextName, BaseName) = BaseName.split('.');
+ if (BaseName.empty()) {
+ BaseName = ContextName;
+ ContextName = StringRef();
+ } else if (ContextName.empty() || !isValidIdentifier(ContextName)) {
+ return diag::warn_attr_swift_name_context_name_invalid_identifier;
+ } else {
+ IsMember = true;
+ }
+
+ if (!isValidIdentifier(BaseName) || BaseName == "_")
+ return diag::warn_attr_swift_name_basename_invalid_identifier;
+
+ bool IsSubscript = BaseName == "subscript";
+ // A subscript accessor must be a getter or setter.
+ if (IsSubscript && !isGetter && !isSetter)
+ return diag::warn_attr_swift_name_subscript_not_accessor;
+
+ if (Parameters.empty())
+ return diag::warn_attr_swift_name_missing_parameters;
+ Parameters = Parameters.drop_back(); // ')'
+
+ if (Parameters.empty()) {
+ // Setters and subscripts must have at least one parameter.
+ if (IsSubscript)
+ return diag::warn_attr_swift_name_subscript_no_parameter;
+ if (isSetter)
+ return diag::warn_attr_swift_name_setter_parameters;
+
+ return None;
+ }
+
+ if (Parameters.back() != ':')
+ return diag::warn_attr_swift_name_function;
+
+ Optional<unsigned> SelfLocation;
+ Optional<unsigned> NewValueLocation;
+ unsigned NewValueCount = 0;
+ StringRef NextParam;
+ do {
+ std::tie(NextParam, Parameters) = Parameters.split(':');
+
+ if (!isValidIdentifier(NextParam))
+ return diag::warn_attr_swift_name_parameter_invalid_identifier;
+
+ // "self" indicates the "self" argument for a member.
+ if (IsMember && NextParam == "self") {
+ // More than one "self"?
+ if (SelfLocation) return diag::warn_attr_swift_name_multiple_selfs;
+
+ // The "self" location is the current parameter.
+ SelfLocation = SwiftParamCount;
+ }
+
+ // "newValue" indicates the "newValue" argument for a setter.
+ if (NextParam == "newValue") {
+ // There should only be one 'newValue', but it's only significant for
+ // subscript accessors, so don't error right away.
+ ++NewValueCount;
+
+ NewValueLocation = SwiftParamCount;
+ }
+ ++SwiftParamCount;
+ } while (!Parameters.empty());
+
+ // Only instance subscripts are currently supported.
+ if (IsSubscript && !SelfLocation)
+ return diag::warn_attr_swift_name_static_subscript;
+
+ IsSingleParamInit =
+ (SwiftParamCount == 1 && BaseName == "init" && NextParam != "_");
+
+ // Check the number of parameters for a getter/setter.
+ if (isGetter || isSetter) {
+ // Setters have one parameter for the new value.
+ unsigned NumExpectedParams;
+ unsigned ParamDiag;
+
+ if (isSetter) {
+ NumExpectedParams = 1;
+ ParamDiag = diag::warn_attr_swift_name_setter_parameters;
+ } else {
+ NumExpectedParams = 0;
+ ParamDiag = diag::warn_attr_swift_name_getter_parameters;
+ }
+
+ // Instance methods have one parameter for "self".
+ if (SelfLocation) ++NumExpectedParams;
+
+ // Subscripts may have additional parameters beyond the expected params for
+ // the index.
+ if (IsSubscript) {
+ if (SwiftParamCount < NumExpectedParams)
+ return ParamDiag;
+ // A subscript setter must explicitly label its newValue parameter to
+ // distinguish it from index parameters.
+ if (isSetter) {
+ if (!NewValueLocation)
+ return diag::warn_attr_swift_name_subscript_setter_no_newValue;
+ // There can only be one.
+ if (NewValueCount > 1)
+ return diag::warn_attr_swift_name_subscript_setter_multiple_newValues;
+ } else {
+ // Subscript getters should have no 'newValue:' parameter.
+ if (NewValueLocation)
+ return diag::warn_attr_swift_name_subscript_getter_newValue;
+ }
+ } else {
+ // Property accessors must have exactly the number of expected params.
+ if (SwiftParamCount != NumExpectedParams)
+ return ParamDiag;
+ }
+ }
+
+ return None;
+}
+
+/// Do a check to make sure \p Name looks like a legal swift_name
+/// attribute for the decl \p D. Raise a diagnostic if the name is invalid
+/// for the given declaration.
+///
+/// For a function, this will validate a compound Swift name,
+/// e.g. <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>,
+/// and the function will output the number of parameter names, and whether this
+/// is a single-arg initializer.
+///
+/// For a type, enum constant, property, or variable declaration, this will
+/// validate either a simple identifier, or a qualified
+/// <code>context.identifier</code> name.
+///
+/// \returns true if the name is a valid swift name for \p D, false otherwise.
+bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name,
+ SourceLocation ArgLoc,
+ IdentifierInfo *AttrName) {
+ if (isa<ObjCMethodDecl>(D) || isa<FunctionDecl>(D)) {
+ ArrayRef<ParmVarDecl*> Params;
+ unsigned ParamCount;
+
+ if (const auto *Method = dyn_cast<ObjCMethodDecl>(D)) {
+ ParamCount = Method->getSelector().getNumArgs();
+ Params = Method->parameters().slice(0, ParamCount);
+ } else {
+ const auto *Function = cast<FunctionDecl>(D);
+ ParamCount = Function->getNumParams();
+ Params = Function->parameters();
+
+ if (!Function->hasWrittenPrototype()) {
+ Diag(ArgLoc, diag::warn_attr_swift_name_function_no_prototype)
+ << AttrName;
+ return false;
+ }
+ }
+
+ unsigned SwiftParamCount;
+ bool IsSingleParamInit;
+ if (auto diagID = validateSwiftFunctionName(Name, SwiftParamCount,
+ IsSingleParamInit)) {
+ Diag(ArgLoc, *diagID) << AttrName;
+ return false;
+ }
+
+ bool ParamsOK;
+ if (SwiftParamCount == ParamCount) {
+ ParamsOK = true;
+ } else if (SwiftParamCount > ParamCount) {
+ ParamsOK = IsSingleParamInit && ParamCount == 0;
+ } else {
+ // We have fewer Swift parameters than Objective-C parameters, but that
+ // might be because we've transformed some of them. Check for potential
+ // "out" parameters and err on the side of not warning.
+ unsigned MaybeOutParamCount =
+ std::count_if(Params.begin(), Params.end(),
+ [](const ParmVarDecl *Param) -> bool {
+ QualType ParamTy = Param->getType();
+ if (ParamTy->isReferenceType() || ParamTy->isPointerType())
+ return !ParamTy->getPointeeType().isConstQualified();
+ return false;
+ });
+ ParamsOK = (SwiftParamCount + MaybeOutParamCount >= ParamCount);
+ }
+
+ if (!ParamsOK) {
+ Diag(ArgLoc, diag::warn_attr_swift_name_num_params)
+ << (SwiftParamCount > ParamCount) << AttrName
+ << ParamCount << SwiftParamCount;
+ return false;
+ }
+
+ } else if (isa<EnumConstantDecl>(D) || isa<ObjCProtocolDecl>(D) ||
+ isa<ObjCInterfaceDecl>(D) || isa<ObjCPropertyDecl>(D) ||
+ isa<VarDecl>(D) || isa<TypedefNameDecl>(D) || isa<TagDecl>(D) ||
+ isa<IndirectFieldDecl>(D) || isa<FieldDecl>(D)) {
+ StringRef ContextName, BaseName;
+ std::tie(ContextName, BaseName) = Name.split('.');
+ if (BaseName.empty()) {
+ BaseName = ContextName;
+ ContextName = StringRef();
+ } else if (!isValidIdentifier(ContextName)) {
+ Diag(ArgLoc, diag::warn_attr_swift_name_context_name_invalid_identifier)
+ << AttrName;
+ return false;
+ }
+
+ if (!isValidIdentifier(BaseName)) {
+ Diag(ArgLoc, diag::warn_attr_swift_name_basename_invalid_identifier)
+ << AttrName;
+ return false;
+ }
+
+ } else {
+ Diag(ArgLoc, diag::warn_attr_swift_name_decl_kind) << AttrName;
+ return false;
+ }
+ return true;
+}
+
+static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) {
+ StringRef Name;
+ SourceLocation ArgLoc;
+ if (!S.checkStringLiteralArgumentAttr(Attr, 0, Name, &ArgLoc))
+ return;
+
+ if (!S.DiagnoseSwiftName(D, Name, ArgLoc, Attr.getName()))
+ return;
+
+ D->addAttr(::new (S.Context) SwiftNameAttr(Attr.getRange(), S.Context, Name,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+static bool isErrorParameter(Sema &S, QualType paramType) {
+ if (auto ptr = paramType->getAs<PointerType>()) {
+ auto outerPointee = ptr->getPointeeType();
+
+ // NSError**.
+ if (auto objcPtr = outerPointee->getAs<ObjCObjectPointerType>()) {
+ if (auto iface = objcPtr->getInterfaceDecl())
+ if (iface->getIdentifier() == S.getNSErrorIdent())
+ return true;
+ }
+
+ // CFErrorRef*.
+ if (auto cPtr = outerPointee->getAs<PointerType>()) {
+ auto innerPointee = cPtr->getPointeeType();
+ if (auto recordType = innerPointee->getAs<RecordType>()) {
+ if (S.isCFError(recordType->getDecl()))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static void handleSwiftError(Sema &S, Decl *D, const AttributeList &attr) {
+ SwiftErrorAttr::ConventionKind convention;
+ IdentifierLoc *conventionLoc = attr.getArgAsIdent(0);
+ StringRef conventionStr = conventionLoc->Ident->getName();
+ if (!SwiftErrorAttr::ConvertStrToConventionKind(conventionStr, convention)) {
+ S.Diag(attr.getLoc(), diag::warn_attribute_type_not_supported)
+ << attr.getName() << conventionLoc->Ident;
+ return;
+ }
+
+ auto requireErrorParameter = [&]() -> bool {
+ if (D->isInvalidDecl()) return true;
+
+ for (unsigned i = 0, e = getFunctionOrMethodNumParams(D); i != e; ++i) {
+ if (isErrorParameter(S, getFunctionOrMethodParamType(D, i)))
+ return true;
+ }
+
+ S.Diag(attr.getLoc(), diag::err_attr_swift_error_no_error_parameter)
+ << attr.getName() << isa<ObjCMethodDecl>(D);
+ return false;
+ };
+
+ auto requirePointerResult = [&] {
+ if (D->isInvalidDecl()) return true;
+
+ // C, ObjC, and block pointers are definitely okay.
+ // References are definitely not okay.
+ // nullptr_t is weird but acceptable.
+ QualType returnType = getFunctionOrMethodResultType(D);
+ if (returnType->hasPointerRepresentation() &&
+ !returnType->isReferenceType()) return true;
+
+ S.Diag(attr.getLoc(), diag::err_attr_swift_error_return_type)
+ << attr.getName() << conventionStr
+ << isa<ObjCMethodDecl>(D) << /*pointer*/ 1;
+ return false;
+ };
+
+ auto requireIntegerResult = [&] {
+ if (D->isInvalidDecl()) return true;
+
+ QualType returnType = getFunctionOrMethodResultType(D);
+ if (returnType->isIntegralType(S.Context)) return true;
+
+ S.Diag(attr.getLoc(), diag::err_attr_swift_error_return_type)
+ << attr.getName() << conventionStr
+ << isa<ObjCMethodDecl>(D) << /*integral*/ 0;
+ return false;
+ };
+
+ switch (convention) {
+ case SwiftErrorAttr::None:
+ // No additional validation required.
+ break;
+
+ case SwiftErrorAttr::NonNullError:
+ if (!requireErrorParameter()) return;
+ break;
+
+ case SwiftErrorAttr::NullResult:
+ if (!requireErrorParameter()) return;
+ if (!requirePointerResult()) return;
+ break;
+
+ case SwiftErrorAttr::NonZeroResult:
+ case SwiftErrorAttr::ZeroResult:
+ if (!requireErrorParameter()) return;
+ if (!requireIntegerResult()) return;
+ break;
+ }
+
+ D->addAttr(::new (S.Context)
+ SwiftErrorAttr(attr.getRange(), S.Context, convention,
+ attr.getAttributeSpellingListIndex()));
+}
+
+static void handleSwiftBridgeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ // Make sure that there is a string literal as the annotation's single
+ // argument.
+ StringRef Str;
+ if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str))
+ return;
+
+ // Don't duplicate annotations that are already set.
+ if (D->hasAttr<SwiftBridgeAttr>()) {
+ S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr.getName();
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ SwiftBridgeAttr(Attr.getRange(), S.Context, Str,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleSwiftNewtypeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ // Make sure that there is an identifier as the annotation's single
+ // argument.
+ if (Attr.getNumArgs() != 1) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments)
+ << Attr.getName() << 1;
+ Attr.setInvalid();
+ return;
+ }
+ if (!Attr.isArgIdent(0)) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_type)
+ << Attr.getName() << AANT_ArgumentIdentifier;
+ Attr.setInvalid();
+ return;
+ }
+
+ IdentifierInfo *II = Attr.getArgAsIdent(0)->Ident;
+ SwiftNewtypeAttr::NewtypeKind Kind;
+ if (II->isStr("struct"))
+ Kind = SwiftNewtypeAttr::NK_Struct;
+ else if (II->isStr("enum"))
+ Kind = SwiftNewtypeAttr::NK_Enum;
+ else {
+ S.Diag(Attr.getLoc(), diag::warn_attribute_type_not_supported)
+ << Attr.getName() << II;
+ Attr.setInvalid();
+ return;
+ }
+
+ if (!isa<TypedefNameDecl>(D)) {
+ S.Diag(Attr.getLoc(), diag::warn_swift_newtype_attribute_non_typedef);
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ SwiftNewtypeAttr(Attr.getRange(), S.Context, Kind,
+ Attr.getAttributeSpellingListIndex()));
+}
+
//===----------------------------------------------------------------------===//
// Microsoft specific attribute handlers.
//===----------------------------------------------------------------------===//
@@ -5834,6 +6333,9 @@
case AttributeList::AT_ReturnsNonNull:
handleReturnsNonNullAttr(S, D, Attr);
break;
+ case AttributeList::AT_NoEscape:
+ handleNoEscapeAttr(S, D, Attr);
+ break;
case AttributeList::AT_AssumeAligned:
handleAssumeAlignedAttr(S, D, Attr);
break;
@@ -5897,6 +6399,9 @@
case AttributeList::AT_ObjCBoxable:
handleObjCBoxable(S, D, Attr);
break;
+ case AttributeList::AT_NSErrorDomain:
+ handleNSErrorDomain(S, D, Attr);
+ break;
case AttributeList::AT_CFAuditedTransfer:
handleCFAuditedTransferAttr(S, D, Attr);
break;
@@ -5953,6 +6458,9 @@
case AttributeList::AT_ObjCSubclassingRestricted:
handleSimpleAttribute<ObjCSubclassingRestrictedAttr>(S, D, Attr);
break;
+ case AttributeList::AT_ObjCCompleteDefinition:
+ handleSimpleAttribute<ObjCCompleteDefinitionAttr>(S, D, Attr);
+ break;
case AttributeList::AT_ObjCExplicitProtocolImpl:
handleObjCSuppresProtocolAttr(S, D, Attr);
break;
@@ -6219,6 +6727,22 @@
case AttributeList::AT_RenderScriptKernel:
handleSimpleAttribute<RenderScriptKernelAttr>(S, D, Attr);
break;
+ // Swift attributes.
+ case AttributeList::AT_SwiftPrivate:
+ handleSimpleAttribute<SwiftPrivateAttr>(S, D, Attr);
+ break;
+ case AttributeList::AT_SwiftName:
+ handleSwiftName(S, D, Attr);
+ break;
+ case AttributeList::AT_SwiftError:
+ handleSwiftError(S, D, Attr);
+ break;
+ case AttributeList::AT_SwiftBridge:
+ handleSwiftBridgeAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_SwiftNewtype:
+ handleSwiftNewtypeAttr(S, D, Attr);
+ break;
// XRay attributes.
case AttributeList::AT_XRayInstrument:
handleSimpleAttribute<XRayInstrumentAttr>(S, D, Attr);
@@ -6443,6 +6967,9 @@
// Finally, apply any attributes on the decl itself.
if (const AttributeList *Attrs = PD.getAttributes())
ProcessDeclAttributeList(S, D, Attrs);
+
+ // Look for API notes that map to attributes.
+ ProcessAPINotes(D);
}
/// Is the given declaration allowed to use a forbidden type?
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index f265f4c..b1bec1c 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -8161,6 +8161,7 @@
Namespc->setInvalidDecl();
ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList);
+ ProcessAPINotes(Namespc);
// FIXME: Should we be merging attributes?
if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>())
@@ -8550,6 +8551,7 @@
if (UDir)
ProcessDeclAttributeList(S, UDir, AttrList);
+ ProcessAPINotes(UDir);
return UDir;
}
@@ -9641,6 +9643,7 @@
NewTD->setInvalidDecl();
ProcessDeclAttributeList(S, NewTD, AttrList);
+ ProcessAPINotes(NewTD);
CheckTypedefForVariablyModifiedType(S, NewTD);
Invalid |= NewTD->isInvalidDecl();
diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp
index b43e5b9..8885b09 100644
--- a/lib/Sema/SemaDeclObjC.cpp
+++ b/lib/Sema/SemaDeclObjC.cpp
@@ -980,6 +980,10 @@
ObjCInterfaceDecl *IDecl
= ObjCInterfaceDecl::Create(Context, CurContext, AtInterfaceLoc, ClassName,
typeParamList, PrevIDecl, ClassLoc);
+ if (AttrList)
+ ProcessDeclAttributeList(TUScope, IDecl, AttrList);
+ ProcessAPINotes(IDecl);
+
if (PrevIDecl) {
// Class already seen. Was it a definition?
if (ObjCInterfaceDecl *Def = PrevIDecl->getDefinition()) {
@@ -990,8 +994,6 @@
}
}
- if (AttrList)
- ProcessDeclAttributeList(TUScope, IDecl, AttrList);
PushOnScopeChains(IDecl, TUScope);
// Start the definition of this class. If we're in a redefinition case, there
@@ -1175,6 +1177,7 @@
if (AttrList)
ProcessDeclAttributeList(TUScope, PDecl, AttrList);
+ ProcessAPINotes(PDecl);
// Merge attributes from previous declarations.
if (PrevDecl)
@@ -1699,12 +1702,14 @@
= ObjCProtocolDecl::Create(Context, CurContext, Ident,
IdentPair.second, AtProtocolLoc,
PrevDecl);
-
+ ProcessAPINotes(PDecl);
+
PushOnScopeChains(PDecl, TUScope);
CheckObjCDeclScope(PDecl);
if (attrList)
ProcessDeclAttributeList(TUScope, PDecl, attrList);
+ ProcessAPINotes(PDecl);
if (PrevDecl)
mergeDeclAttributes(PDecl, PrevDecl);
@@ -3037,7 +3042,8 @@
ClassName, TypeParams, PrevIDecl,
IdentLocs[i]);
IDecl->setAtEndRange(IdentLocs[i]);
-
+ ProcessAPINotes(IDecl);
+
PushOnScopeChains(IDecl, TUScope);
CheckObjCDeclScope(IDecl);
DeclsInGroup.push_back(IDecl);
@@ -3837,7 +3843,7 @@
if (IDecl->getSuperClass() == nullptr) {
// This class has no superclass, so check that it has been marked with
// __attribute((objc_root_class)).
- if (!HasRootClassAttr) {
+ if (!HasRootClassAttr && !IDecl->hasAttr<ObjCCompleteDefinitionAttr>()) {
SourceLocation DeclLoc(IDecl->getLocation());
SourceLocation SuperClassLoc(getLocForEndOfToken(DeclLoc));
Diag(DeclLoc, diag::warn_objc_root_class_missing)
@@ -3932,12 +3938,10 @@
return (Decl::ObjCDeclQualifier) (unsigned) PQTVal;
}
-/// \brief Check whether the declared result type of the given Objective-C
-/// method declaration is compatible with the method's class.
-///
-static Sema::ResultTypeCompatibilityKind
-CheckRelatedResultTypeCompatibility(Sema &S, ObjCMethodDecl *Method,
- ObjCInterfaceDecl *CurrentClass) {
+Sema::ResultTypeCompatibilityKind
+Sema::checkRelatedResultTypeCompatibility(
+ const ObjCMethodDecl *Method,
+ const ObjCInterfaceDecl *CurrentClass) {
QualType ResultType = Method->getReturnType();
// If an Objective-C method inherits its related result type, then its
@@ -4393,6 +4397,7 @@
// Apply the attributes to the parameter.
ProcessDeclAttributeList(TUScope, Param, ArgInfo[i].ArgAttrs);
+ ProcessAPINotes(Param);
if (Param->hasAttr<BlocksAttr>()) {
Diag(Param->getLocation(), diag::err_block_on_nonlocal);
@@ -4423,6 +4428,7 @@
if (AttrList)
ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList);
+ ProcessAPINotes(ObjCMethod);
// Add the method now.
const ObjCMethodDecl *PrevMethod = nullptr;
@@ -4478,7 +4484,7 @@
}
ResultTypeCompatibilityKind RTC
- = CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass);
+ = checkRelatedResultTypeCompatibility(ObjCMethod, CurrentClass);
CheckObjCMethodOverrides(ObjCMethod, CurrentClass, RTC);
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 0077d6c..267dbe4 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -7531,22 +7531,6 @@
return IncompatibleVectors;
}
}
-
- // When the RHS comes from another lax conversion (e.g. binops between
- // scalars and vectors) the result is canonicalized as a vector. When the
- // LHS is also a vector, the lax is allowed by the condition above. Handle
- // the case where LHS is a scalar.
- if (LHSType->isScalarType()) {
- const VectorType *VecType = RHSType->getAs<VectorType>();
- if (VecType && VecType->getNumElements() == 1 &&
- isLaxVectorConversion(RHSType, LHSType)) {
- ExprResult *VecExpr = &RHS;
- *VecExpr = ImpCastExprToType(VecExpr->get(), LHSType, CK_BitCast);
- Kind = CK_BitCast;
- return Compatible;
- }
- }
-
return Incompatible;
}
@@ -8081,7 +8065,6 @@
// If there's an ext-vector type and a scalar, try to convert the scalar to
// the vector element type and splat.
- // FIXME: this should also work for regular vector types as supported in GCC.
if (!RHSVecType && isa<ExtVectorType>(LHSVecType)) {
if (!tryVectorConvertAndSplat(*this, &RHS, RHSType,
LHSVecType->getElementType(), LHSType))
@@ -8094,31 +8077,14 @@
return RHSType;
}
- // FIXME: The code below also handles convertion between vectors and
- // non-scalars, we should break this down into fine grained specific checks
- // and emit proper diagnostics.
- QualType VecType = LHSVecType ? LHSType : RHSType;
- const VectorType *VT = LHSVecType ? LHSVecType : RHSVecType;
- QualType OtherType = LHSVecType ? RHSType : LHSType;
- ExprResult *OtherExpr = LHSVecType ? &RHS : &LHS;
- if (isLaxVectorConversion(OtherType, VecType)) {
- // If we're allowing lax vector conversions, only the total (data) size
- // needs to be the same. For non compound assignment, if one of the types is
- // scalar, the result is always the vector type.
- if (!IsCompAssign) {
- *OtherExpr = ImpCastExprToType(OtherExpr->get(), VecType, CK_BitCast);
- return VecType;
- // In a compound assignment, lhs += rhs, 'lhs' is a lvalue src, forbidding
- // any implicit cast. Here, the 'rhs' should be implicit casted to 'lhs'
- // type. Note that this is already done by non-compound assignments in
- // CheckAssignmentConstraints. If it's a scalar type, only bitcast for
- // <1 x T> -> T. The result is also a vector type.
- } else if (OtherType->isExtVectorType() ||
- (OtherType->isScalarType() && VT->getNumElements() == 1)) {
- ExprResult *RHSExpr = &RHS;
- *RHSExpr = ImpCastExprToType(RHSExpr->get(), LHSType, CK_BitCast);
- return VecType;
- }
+ // If we're allowing lax vector conversions, only the total (data) size
+ // needs to be the same.
+ // FIXME: Should we really be allowing this?
+ // FIXME: We really just pick the LHS type arbitrarily?
+ if (isLaxVectorConversion(RHSType, LHSType)) {
+ QualType resultType = LHSType;
+ RHS = ImpCastExprToType(RHS.get(), resultType, CK_BitCast);
+ return resultType;
}
// Okay, the expression is invalid.
@@ -9724,7 +9690,7 @@
}
// Return a signed type for the vector.
- return GetSignedVectorType(vType);
+ return GetSignedVectorType(LHSType);
}
QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS,
@@ -13577,11 +13543,28 @@
}
// Warn about implicitly autoreleasing indirect parameters captured by blocks.
- if (auto *PT = dyn_cast<PointerType>(CaptureType)) {
+ if (const auto *PT = CaptureType->getAs<PointerType>()) {
+ // This function finds out whether there is an AttributedType of kind
+ // attr_objc_ownership in Ty. The existence of AttributedType of kind
+ // attr_objc_ownership implies __autoreleasing was explicitly specified
+ // rather than being added implicitly by the compiler.
+ auto IsObjCOwnershipAttributedType = [](QualType Ty) {
+ while (const auto *AttrTy = Ty->getAs<AttributedType>()) {
+ if (AttrTy->getAttrKind() == AttributedType::attr_objc_ownership)
+ return true;
+
+ // Peel off AttributedTypes that are not of kind objc_ownership.
+ Ty = AttrTy->getModifiedType();
+ }
+
+ return false;
+ };
+
QualType PointeeTy = PT->getPointeeType();
- if (isa<ObjCObjectPointerType>(PointeeTy.getCanonicalType()) &&
+
+ if (PointeeTy->getAs<ObjCObjectPointerType>() &&
PointeeTy.getObjCLifetime() == Qualifiers::OCL_Autoreleasing &&
- !isa<AttributedType>(PointeeTy)) {
+ !IsObjCOwnershipAttributedType(PointeeTy)) {
if (BuildAndDiagnose) {
SourceLocation VarLoc = Var->getLocation();
S.Diag(Loc, diag::warn_block_capture_autoreleasing);
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 3afa95f..9b5fe9a 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -6402,6 +6402,23 @@
return false;
}
+/// \brief Check if it's ok to try and recover dot pseudo destructor calls on
+/// pointer objects.
+static bool
+canRecoverDotPseudoDestructorCallsOnPointerObjects(Sema &SemaRef,
+ QualType DestructedType) {
+ // If this is a record type, check if its destructor is callable.
+ if (auto *RD = DestructedType->getAsCXXRecordDecl()) {
+ if (CXXDestructorDecl *D = SemaRef.LookupDestructor(RD))
+ return SemaRef.CanUseDecl(D, /*TreatUnavailableAsInvalid=*/false);
+ return false;
+ }
+
+ // Otherwise, check if it's a type for which it's valid to use a pseudo-dtor.
+ return DestructedType->isDependentType() || DestructedType->isScalarType() ||
+ DestructedType->isVectorType();
+}
+
ExprResult Sema::BuildPseudoDestructorExpr(Expr *Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
@@ -6436,15 +6453,36 @@
= DestructedTypeInfo->getTypeLoc().getLocalSourceRange().getBegin();
if (!DestructedType->isDependentType() && !ObjectType->isDependentType()) {
if (!Context.hasSameUnqualifiedType(DestructedType, ObjectType)) {
- Diag(DestructedTypeStart, diag::err_pseudo_dtor_type_mismatch)
- << ObjectType << DestructedType << Base->getSourceRange()
- << DestructedTypeInfo->getTypeLoc().getLocalSourceRange();
+ // Detect dot pseudo destructor calls on pointer objects, e.g.:
+ // Foo *foo;
+ // foo.~Foo();
+ if (OpKind == tok::period && ObjectType->isPointerType() &&
+ Context.hasSameUnqualifiedType(DestructedType,
+ ObjectType->getPointeeType())) {
+ auto Diagnostic =
+ Diag(OpLoc, diag::err_typecheck_member_reference_suggestion)
+ << ObjectType << /*IsArrow=*/0 << Base->getSourceRange();
- // Recover by setting the destructed type to the object type.
- DestructedType = ObjectType;
- DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType,
- DestructedTypeStart);
- Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo);
+ // Issue a fixit only when the destructor is valid.
+ if (canRecoverDotPseudoDestructorCallsOnPointerObjects(
+ *this, DestructedType))
+ Diagnostic << FixItHint::CreateReplacement(OpLoc, "->");
+
+ // Recover by setting the object type to the destructed type and the
+ // operator to '->'.
+ ObjectType = DestructedType;
+ OpKind = tok::arrow;
+ } else {
+ Diag(DestructedTypeStart, diag::err_pseudo_dtor_type_mismatch)
+ << ObjectType << DestructedType << Base->getSourceRange()
+ << DestructedTypeInfo->getTypeLoc().getLocalSourceRange();
+
+ // Recover by setting the destructed type to the object type.
+ DestructedType = ObjectType;
+ DestructedTypeInfo =
+ Context.getTrivialTypeSourceInfo(ObjectType, DestructedTypeStart);
+ Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo);
+ }
} else if (DestructedType.getObjCLifetime() !=
ObjectType.getObjCLifetime()) {
diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp
index b053c83..e5d368f 100644
--- a/lib/Sema/SemaInit.cpp
+++ b/lib/Sema/SemaInit.cpp
@@ -2237,6 +2237,10 @@
}
unsigned FieldIndex = 0;
+
+ if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RT->getDecl()))
+ FieldIndex = CXXRD->getNumBases();
+
for (auto *FI : RT->getDecl()->fields()) {
if (FI->isUnnamedBitfield())
continue;
@@ -3977,6 +3981,8 @@
ImplicitConversionSequence ICS;
ICS.setStandard();
ICS.Standard.setAsIdentityConversion();
+ if (!E->isRValue())
+ ICS.Standard.First = ICK_Lvalue_To_Rvalue;
// If E is of a floating-point type, then the conversion is ill-formed
// due to narrowing, but go through the motions in order to produce the
// right diagnostic.
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index e2cb2c8..f751d8b 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -3430,6 +3430,12 @@
SM == ShadowMaps.rbegin())
continue;
+ // A shadow declaration that's created by a resolved using declaration
+ // is not hidden by the same using declaration.
+ if (isa<UsingShadowDecl>(ND) && isa<UsingDecl>(D) &&
+ cast<UsingShadowDecl>(ND)->getUsingDecl() == D)
+ continue;
+
// We've found a declaration that hides this one.
return D;
}
diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp
index 3481b82..eea44eb 100644
--- a/lib/Sema/SemaObjCProperty.cpp
+++ b/lib/Sema/SemaObjCProperty.cpp
@@ -636,8 +636,6 @@
PDecl->setInvalidDecl();
}
- ProcessDeclAttributes(S, PDecl, FD.D);
-
// Regardless of setter/getter attribute, we save the default getter/setter
// selector names in anticipation of declaration of setter/getter methods.
PDecl->setGetterName(GetterSel);
@@ -645,6 +643,8 @@
PDecl->setPropertyAttributesAsWritten(
makePropertyAttributesAsWritten(AttributesAsWritten));
+ ProcessDeclAttributes(S, PDecl, FD.D);
+
if (Attributes & ObjCDeclSpec::DQ_PR_readonly)
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readonly);
@@ -2250,6 +2250,8 @@
SectionAttr::CreateImplicit(Context, SectionAttr::GNU_section,
SA->getName(), Loc));
+ ProcessAPINotes(GetterMethod);
+
if (getLangOpts().ObjCAutoRefCount)
CheckARCMethodDecl(GetterMethod);
} else
@@ -2315,6 +2317,9 @@
SetterMethod->addAttr(
SectionAttr::CreateImplicit(Context, SectionAttr::GNU_section,
SA->getName(), Loc));
+
+ ProcessAPINotes(SetterMethod);
+
// It's possible for the user to have set a very odd custom
// setter selector that causes it to have a method family.
if (getLangOpts().ObjCAutoRefCount)
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index ad1e89a..7dfac4e 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -1288,6 +1288,7 @@
if (Attr)
ProcessDeclAttributeList(S, NewClass, Attr);
+ ProcessAPINotes(NewClass);
if (PrevClassTemplate)
mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl());
@@ -6763,6 +6764,7 @@
if (Attr)
ProcessDeclAttributeList(S, Specialization, Attr);
+ ProcessAPINotes(Specialization);
// Add alignment attributes if necessary; these attributes are checked when
// the ASTContext lays out the structure.
@@ -7796,6 +7798,7 @@
bool PreviouslyDLLExported = Specialization->hasAttr<DLLExportAttr>();
if (Attr)
ProcessDeclAttributeList(S, Specialization, Attr);
+ ProcessAPINotes(Specialization);
// Add the explicit instantiation into its lexical context. However,
// since explicit instantiations are never found by name lookup, we
@@ -8215,6 +8218,7 @@
// Merge attributes.
if (AttributeList *Attr = D.getDeclSpec().getAttributes().getList())
ProcessDeclAttributeList(S, Prev, Attr);
+ ProcessAPINotes(Prev);
}
if (TSK == TSK_ExplicitInstantiationDefinition)
InstantiateVariableDefinition(D.getIdentifierLoc(), Prev);
@@ -8374,6 +8378,7 @@
Specialization->setTemplateSpecializationKind(TSK, D.getIdentifierLoc());
if (Attr)
ProcessDeclAttributeList(S, Specialization, Attr);
+ ProcessAPINotes(Specialization);
if (Specialization->isDefined()) {
// Let the ASTConsumer know that this function has been explicitly
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 29b2142..ffc965e 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -1159,20 +1159,6 @@
ResultTL = ObjCObjectPointerTL.getPointeeLoc();
}
- if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) {
- // Protocol qualifier information.
- if (OTPTL.getNumProtocols() > 0) {
- assert(OTPTL.getNumProtocols() == Protocols.size());
- OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc);
- OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc);
- for (unsigned i = 0, n = Protocols.size(); i != n; ++i)
- OTPTL.setProtocolLoc(i, ProtocolLocs[i]);
- }
-
- // We're done. Return the completed type to the parser.
- return CreateParsedType(Result, ResultTInfo);
- }
-
auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>();
// Type argument information.
@@ -3355,25 +3341,9 @@
if (auto recordType = type->getAs<RecordType>()) {
RecordDecl *recordDecl = recordType->getDecl();
- bool isCFError = false;
- if (S.CFError) {
- // If we already know about CFError, test it directly.
- isCFError = (S.CFError == recordDecl);
- } else {
- // Check whether this is CFError, which we identify based on its bridge
- // to NSError.
- if (recordDecl->getTagKind() == TTK_Struct && numNormalPointers > 0) {
- if (auto bridgeAttr = recordDecl->getAttr<ObjCBridgeAttr>()) {
- if (bridgeAttr->getBridgedType() == S.getNSErrorIdent()) {
- S.CFError = recordDecl;
- isCFError = true;
- }
- }
- }
- }
-
// If this is CFErrorRef*, report it as such.
- if (isCFError && numNormalPointers == 2 && numTypeSpecifierPointers < 2) {
+ if (numNormalPointers == 2 && numTypeSpecifierPointers < 2 &&
+ S.isCFError(recordDecl)) {
return PointerDeclaratorKind::CFErrorRefPointer;
}
break;
@@ -3397,6 +3367,26 @@
}
}
+bool Sema::isCFError(RecordDecl *recordDecl) {
+ // If we already know about CFError, test it directly.
+ if (CFError) {
+ return (CFError == recordDecl);
+ }
+
+ // Check whether this is CFError, which we identify based on being
+ // bridged to NSError.
+ if (recordDecl->getTagKind() == TTK_Struct) {
+ if (auto bridgeAttr = recordDecl->getAttr<ObjCBridgeAttr>()) {
+ if (bridgeAttr->getBridgedType() == getNSErrorIdent()) {
+ CFError = recordDecl;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
static FileID getNullabilityCompletenessCheckFileID(Sema &S,
SourceLocation loc) {
// If we're anywhere in a function, method, or closure context, don't perform
@@ -5979,12 +5969,34 @@
return false;
}
+/// Rebuild an attributed type without the nullability attribute on it.
+static QualType rebuildAttributedTypeWithoutNullability(ASTContext &ctx,
+ QualType type) {
+ auto attributed = dyn_cast<AttributedType>(type.getTypePtr());
+ if (!attributed) return type;
+
+ // Skip the nullability attribute; we're done.
+ if (attributed->getImmediateNullability()) {
+ return attributed->getModifiedType();
+ }
+
+ // Build the modified type.
+ auto modified = rebuildAttributedTypeWithoutNullability(
+ ctx, attributed->getModifiedType());
+ assert(modified.getTypePtr() != attributed->getModifiedType().getTypePtr());
+ return ctx.getAttributedType(attributed->getAttrKind(), modified,
+ attributed->getEquivalentType());
+}
+
bool Sema::checkNullabilityTypeSpecifier(QualType &type,
NullabilityKind nullability,
SourceLocation nullabilityLoc,
bool isContextSensitive,
- bool allowOnArrayType) {
- recordNullabilitySeen(*this, nullabilityLoc);
+ bool allowOnArrayType,
+ bool implicit,
+ bool overrideExisting) {
+ if (!implicit)
+ recordNullabilitySeen(*this, nullabilityLoc);
// Check for existing nullability attributes on the type.
QualType desugared = type;
@@ -5993,6 +6005,9 @@
if (auto existingNullability = attributed->getImmediateNullability()) {
// Duplicated nullability.
if (nullability == *existingNullability) {
+ if (implicit)
+ break;
+
Diag(nullabilityLoc, diag::warn_nullability_duplicate)
<< DiagNullabilityKind(nullability, isContextSensitive)
<< FixItHint::CreateRemoval(nullabilityLoc);
@@ -6000,11 +6015,16 @@
break;
}
- // Conflicting nullability.
- Diag(nullabilityLoc, diag::err_nullability_conflicting)
- << DiagNullabilityKind(nullability, isContextSensitive)
- << DiagNullabilityKind(*existingNullability, false);
- return true;
+ if (!overrideExisting) {
+ // Conflicting nullability.
+ Diag(nullabilityLoc, diag::err_nullability_conflicting)
+ << DiagNullabilityKind(nullability, isContextSensitive)
+ << DiagNullabilityKind(*existingNullability, false);
+ return true;
+ }
+
+ // Rebuild the attributed type, dropping the existing nullability.
+ type = rebuildAttributedTypeWithoutNullability(Context, type);
}
desugared = attributed->getModifiedType();
@@ -6015,7 +6035,7 @@
// have nullability specifiers on them, which means we cannot
// provide a useful Fix-It.
if (auto existingNullability = desugared->getNullability(Context)) {
- if (nullability != *existingNullability) {
+ if (nullability != *existingNullability && !implicit) {
Diag(nullabilityLoc, diag::err_nullability_conflicting)
<< DiagNullabilityKind(nullability, isContextSensitive)
<< DiagNullabilityKind(*existingNullability, false);
@@ -6040,15 +6060,16 @@
// If this definitely isn't a pointer type, reject the specifier.
if (!desugared->canHaveNullability() &&
!(allowOnArrayType && desugared->isArrayType())) {
- Diag(nullabilityLoc, diag::err_nullability_nonpointer)
- << DiagNullabilityKind(nullability, isContextSensitive) << type;
+ if (!implicit) {
+ Diag(nullabilityLoc, diag::err_nullability_nonpointer)
+ << DiagNullabilityKind(nullability, isContextSensitive) << type;
+ }
return true;
}
// For the context-sensitive keywords/Objective-C property
// attributes, require that the type be a single-level pointer.
if (isContextSensitive) {
- // Make sure that the pointee isn't itself a pointer type.
const Type *pointeeType;
if (desugared->isArrayType())
pointeeType = desugared->getArrayElementTypeNoTypeQual();
@@ -6077,13 +6098,6 @@
}
bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) {
- if (isa<ObjCTypeParamType>(type)) {
- // Build the attributed type to record where __kindof occurred.
- type = Context.getAttributedType(AttributedType::attr_objc_kindof,
- type, type);
- return false;
- }
-
// Find out if it's an Objective-C object or object pointer type;
const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>();
const ObjCObjectType *objType = ptrType ? ptrType->getObjectType()
@@ -6829,7 +6843,7 @@
mapNullabilityAttrKind(attr.getKind()),
attr.getLoc(),
attr.isContextSensitiveKeywordAttribute(),
- allowOnArrayType)) {
+ allowOnArrayType, /*implicit=*/false)) {
attr.setInvalid();
}
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index 53224e2..3061aed 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -36,6 +36,7 @@
#include "clang/Basic/FileManager.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/MemoryBufferCache.h"
#include "clang/Basic/ObjCRuntime.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/Sanitizers.h"
@@ -461,6 +462,30 @@
return checkDiagnosticGroupMappings(StoredDiags, Diags, Complain);
}
+/// Return the top import module if it is implicit, nullptr otherwise.
+static Module *getTopImportImplicitModule(ModuleManager &ModuleMgr,
+ Preprocessor &PP) {
+ // If the original import came from a file explicitly generated by the user,
+ // don't check the diagnostic mappings.
+ // FIXME: currently this is approximated by checking whether this is not a
+ // module import of an implicitly-loaded module file.
+ // Note: ModuleMgr.rbegin() may not be the current module, but it must be in
+ // the transitive closure of its imports, since unrelated modules cannot be
+ // imported until after this module finishes validation.
+ ModuleFile *TopImport = &*ModuleMgr.rbegin();
+ while (!TopImport->ImportedBy.empty())
+ TopImport = TopImport->ImportedBy[0];
+ if (TopImport->Kind != MK_ImplicitModule)
+ return nullptr;
+
+ StringRef ModuleName = TopImport->ModuleName;
+ assert(!ModuleName.empty() && "diagnostic options read before module name");
+
+ Module *M = PP.getHeaderSearchInfo().lookupModule(ModuleName);
+ assert(M && "missing module");
+ return M;
+}
+
bool PCHValidator::ReadDiagnosticOptions(
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts, bool Complain) {
DiagnosticsEngine &ExistingDiags = PP.getDiagnostics();
@@ -474,28 +499,14 @@
ModuleManager &ModuleMgr = Reader.getModuleManager();
assert(ModuleMgr.size() >= 1 && "what ASTFile is this then");
- // If the original import came from a file explicitly generated by the user,
- // don't check the diagnostic mappings.
- // FIXME: currently this is approximated by checking whether this is not a
- // module import of an implicitly-loaded module file.
- // Note: ModuleMgr.rbegin() may not be the current module, but it must be in
- // the transitive closure of its imports, since unrelated modules cannot be
- // imported until after this module finishes validation.
- ModuleFile *TopImport = *ModuleMgr.rbegin();
- while (!TopImport->ImportedBy.empty())
- TopImport = TopImport->ImportedBy[0];
- if (TopImport->Kind != MK_ImplicitModule)
+ Module *TopM = getTopImportImplicitModule(ModuleMgr, PP);
+ if (!TopM)
return false;
- StringRef ModuleName = TopImport->ModuleName;
- assert(!ModuleName.empty() && "diagnostic options read before module name");
-
- Module *M = PP.getHeaderSearchInfo().lookupModule(ModuleName);
- assert(M && "missing module");
-
// FIXME: if the diagnostics are incompatible, save a DiagnosticOptions that
// contains the union of their flags.
- return checkDiagnosticMappings(*Diags, ExistingDiags, M->IsSystem, Complain);
+ return checkDiagnosticMappings(*Diags, ExistingDiags, TopM->IsSystem,
+ Complain);
}
/// \brief Collect the macro definitions provided by the given preprocessor
@@ -1707,15 +1718,15 @@
// Note that we are loading defined macros.
Deserializing Macros(this);
- for (auto &I : llvm::reverse(ModuleMgr)) {
- BitstreamCursor &MacroCursor = I->MacroCursor;
+ for (ModuleFile &I : llvm::reverse(ModuleMgr)) {
+ BitstreamCursor &MacroCursor = I.MacroCursor;
// If there was no preprocessor block, skip this file.
if (MacroCursor.getBitcodeBytes().empty())
continue;
BitstreamCursor Cursor = MacroCursor;
- Cursor.JumpToBit(I->MacroStartOffset);
+ Cursor.JumpToBit(I.MacroStartOffset);
RecordData Record;
while (true) {
@@ -1737,7 +1748,7 @@
case PP_MACRO_OBJECT_LIKE:
case PP_MACRO_FUNCTION_LIKE: {
- IdentifierInfo *II = getLocalIdentifier(*I, Record[0]);
+ IdentifierInfo *II = getLocalIdentifier(I, Record[0]);
if (II->isOutOfDate())
updateOutOfDateIdentifier(*II);
break;
@@ -2149,7 +2160,7 @@
ASTReader::ASTReadResult ASTReader::ReadOptionsBlock(
BitstreamCursor &Stream, unsigned ClientLoadCapabilities,
bool AllowCompatibleConfigurationMismatch, ASTReaderListener &Listener,
- std::string &SuggestedPredefines, bool ValidateDiagnosticOptions) {
+ std::string &SuggestedPredefines) {
if (Stream.EnterSubBlock(OPTIONS_BLOCK_ID))
return Failure;
@@ -2191,15 +2202,6 @@
break;
}
- case DIAGNOSTIC_OPTIONS: {
- bool Complain = (ClientLoadCapabilities & ARR_OutOfDate) == 0;
- if (ValidateDiagnosticOptions &&
- !AllowCompatibleConfigurationMismatch &&
- ParseDiagnosticOptions(Record, Complain, Listener))
- return OutOfDate;
- break;
- }
-
case FILE_SYSTEM_OPTIONS: {
bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0;
if (!AllowCompatibleConfigurationMismatch &&
@@ -2240,6 +2242,18 @@
return Failure;
}
+ // Lambda to read the unhashed control block the first time it's called.
+ bool HasReadUnhashedControlBlock = false;
+ auto readUnhashedControlBlockOnce = [&]() {
+ if (!HasReadUnhashedControlBlock) {
+ HasReadUnhashedControlBlock = true;
+ if (ASTReadResult Result =
+ readUnhashedControlBlock(F, ImportedBy, ClientLoadCapabilities))
+ return Result;
+ }
+ return Success;
+ };
+
// Read all of the records and blocks in the control block.
RecordData Record;
unsigned NumInputs = 0;
@@ -2252,6 +2266,11 @@
Error("malformed block record in AST file");
return Failure;
case llvm::BitstreamEntry::EndBlock: {
+ // Validate the module before returning. This call catches an AST with
+ // no module name and no imports.
+ if (ASTReadResult Result = readUnhashedControlBlockOnce())
+ return Result;
+
// Validate input files.
const HeaderSearchOptions &HSOpts =
PP.getHeaderSearchInfo().getHeaderSearchOpts();
@@ -2323,13 +2342,10 @@
// FIXME: Allow this for files explicitly specified with -include-pch.
bool AllowCompatibleConfigurationMismatch =
F.Kind == MK_ExplicitModule || F.Kind == MK_PrebuiltModule;
- const HeaderSearchOptions &HSOpts =
- PP.getHeaderSearchInfo().getHeaderSearchOpts();
Result = ReadOptionsBlock(Stream, ClientLoadCapabilities,
AllowCompatibleConfigurationMismatch,
- *Listener, SuggestedPredefines,
- HSOpts.ModulesValidateDiagnosticOptions);
+ *Listener, SuggestedPredefines);
if (Result == Failure) {
Error("malformed block record in AST file");
return Result;
@@ -2403,12 +2419,13 @@
break;
}
- case SIGNATURE:
- assert((!F.Signature || F.Signature == Record[0]) && "signature changed");
- F.Signature = Record[0];
- break;
-
case IMPORTS: {
+ // Validate the AST before processing any imports (otherwise, untangling
+ // them can be error-prone and expensive). A module will have a name and
+ // will already have been validated, but this catches the PCH case.
+ if (ASTReadResult Result = readUnhashedControlBlockOnce())
+ return Result;
+
// Load each of the imported PCH files.
unsigned Idx = 0, N = Record.size();
while (Idx < N) {
@@ -2421,7 +2438,10 @@
ReadUntranslatedSourceLocation(Record[Idx++]);
off_t StoredSize = (off_t)Record[Idx++];
time_t StoredModTime = (time_t)Record[Idx++];
- ASTFileSignature StoredSignature = Record[Idx++];
+ ASTFileSignature StoredSignature = {
+ {{(uint32_t)Record[Idx++], (uint32_t)Record[Idx++],
+ (uint32_t)Record[Idx++], (uint32_t)Record[Idx++],
+ (uint32_t)Record[Idx++]}}};
auto ImportedFile = ReadPath(F, Record, Idx);
// If our client can't cope with us being out of date, we can't cope with
@@ -2473,6 +2493,12 @@
F.ModuleName = Blob;
if (Listener)
Listener->ReadModuleName(F.ModuleName);
+
+ // Validate the AST as soon as we have a name so we can exit early on
+ // failure.
+ if (ASTReadResult Result = readUnhashedControlBlockOnce())
+ return Result;
+
break;
case MODULE_DIRECTORY: {
@@ -2513,6 +2539,7 @@
F.InputFileOffsets =
(const llvm::support::unaligned_uint64_t *)Blob.data();
F.InputFilesLoaded.resize(NumInputs);
+ F.NumUserInputFiles = NumUserInputs;
break;
}
}
@@ -3123,14 +3150,6 @@
F.ObjCCategories.swap(Record);
break;
- case DIAG_PRAGMA_MAPPINGS:
- if (F.PragmaDiagMappings.empty())
- F.PragmaDiagMappings.swap(Record);
- else
- F.PragmaDiagMappings.insert(F.PragmaDiagMappings.end(),
- Record.begin(), Record.end());
- break;
-
case CUDA_SPECIAL_DECL_REFS:
// Later tables overwrite earlier ones.
// FIXME: Modules will have trouble with this.
@@ -3245,8 +3264,11 @@
for (unsigned I = 0, N = Record.size(); I != N; /**/) {
unsigned GlobalID = getGlobalSubmoduleID(F, Record[I++]);
SourceLocation Loc = ReadSourceLocation(F, Record, I);
- if (GlobalID)
+ if (GlobalID) {
ImportedModules.push_back(ImportedSubmodule(GlobalID, Loc));
+ if (DeserializationListener)
+ DeserializationListener->ModuleImportRead(GlobalID, Loc);
+ }
}
}
break;
@@ -3342,8 +3364,7 @@
// usable header search context.
assert(!F.ModuleName.empty() &&
"MODULE_NAME should come before MODULE_MAP_FILE");
- if (F.Kind == MK_ImplicitModule &&
- (*ModuleMgr.begin())->Kind != MK_MainFile) {
+ if (F.Kind == MK_ImplicitModule && ModuleMgr.begin()->Kind != MK_MainFile) {
// An implicitly-loaded module file should have its module listed in some
// module map file that we've already loaded.
Module *M = PP.getHeaderSearchInfo().lookupModule(F.ModuleName);
@@ -3621,10 +3642,10 @@
unsigned NumModules = ModuleMgr.size();
SmallVector<ImportedModule, 4> Loaded;
- switch(ASTReadResult ReadResult = ReadASTCore(FileName, Type, ImportLoc,
- /*ImportedBy=*/nullptr, Loaded,
- 0, 0, 0,
- ClientLoadCapabilities)) {
+ switch (ASTReadResult ReadResult =
+ ReadASTCore(FileName, Type, ImportLoc,
+ /*ImportedBy=*/nullptr, Loaded, 0, 0,
+ ASTFileSignature(), ClientLoadCapabilities)) {
case Failure:
case Missing:
case OutOfDate:
@@ -3635,11 +3656,10 @@
for (const ImportedModule &IM : Loaded)
LoadedSet.insert(IM.Mod);
- ModuleMgr.removeModules(ModuleMgr.begin() + NumModules, ModuleMgr.end(),
- LoadedSet,
+ ModuleMgr.removeModules(ModuleMgr.begin() + NumModules, LoadedSet,
Context.getLangOpts().Modules
- ? &PP.getHeaderSearchInfo().getModuleMap()
- : nullptr);
+ ? &PP.getHeaderSearchInfo().getModuleMap()
+ : nullptr);
// If we find that any modules are unusable, the global index is going
// to be out-of-date. Just remove it.
@@ -3986,6 +4006,11 @@
Loaded.push_back(ImportedModule(M, ImportedBy, ImportLoc));
return Success;
+ case UNHASHED_CONTROL_BLOCK_ID:
+ // This block is handled using look-ahead during ReadControlBlock. We
+ // shouldn't get here!
+ return Failure;
+
default:
if (Stream.SkipBlock()) {
Error("malformed block record in AST file");
@@ -3998,6 +4023,122 @@
return Success;
}
+ASTReader::ASTReadResult
+ASTReader::readUnhashedControlBlock(ModuleFile &F, bool WasImportedBy,
+ unsigned ClientLoadCapabilities) {
+ const HeaderSearchOptions &HSOpts =
+ PP.getHeaderSearchInfo().getHeaderSearchOpts();
+ bool AllowCompatibleConfigurationMismatch =
+ F.Kind == MK_ExplicitModule || F.Kind == MK_PrebuiltModule;
+
+ ASTReadResult Result = readUnhashedControlBlockImpl(
+ &F, F.Data, ClientLoadCapabilities, AllowCompatibleConfigurationMismatch,
+ Listener.get(),
+ WasImportedBy ? false : HSOpts.ModulesValidateDiagnosticOptions);
+
+ // If F was directly imported by another module, it's implicitly validated by
+ // the importing module.
+ if (DisableValidation || WasImportedBy ||
+ (AllowConfigurationMismatch && Result == ConfigurationMismatch))
+ return Success;
+
+ if (Result == Failure) {
+ Error("malformed block record in AST file");
+ return Failure;
+ }
+
+ if (Result == OutOfDate && F.Kind == MK_ImplicitModule) {
+ // If this module has already been finalized in the PCMCache, we're stuck
+ // with it; we can only load a single version of each module.
+ //
+ // This can happen when a module is imported in two contexts: in one, as a
+ // user module; in another, as a system module (due to an import from
+ // another module marked with the [system] flag). It usually indicates a
+ // bug in the module map: this module should also be marked with [system].
+ //
+ // If -Wno-system-headers (the default), and the first import is as a
+ // system module, then validation will fail during the as-user import,
+ // since -Werror flags won't have been validated. However, it's reasonable
+ // to treat this consistently as a system module.
+ //
+ // If -Wsystem-headers, the PCM on disk was built with
+ // -Wno-system-headers, and the first import is as a user module, then
+ // validation will fail during the as-system import since the PCM on disk
+ // doesn't guarantee that -Werror was respected. However, the -Werror
+ // flags were checked during the initial as-user import.
+ if (PCMCache.isBufferFinal(F.FileName)) {
+ Diag(diag::warn_module_system_bit_conflict) << F.FileName;
+ return Success;
+ }
+ }
+
+ return Result;
+}
+
+ASTReader::ASTReadResult ASTReader::readUnhashedControlBlockImpl(
+ ModuleFile *F, llvm::StringRef StreamData, unsigned ClientLoadCapabilities,
+ bool AllowCompatibleConfigurationMismatch, ASTReaderListener *Listener,
+ bool ValidateDiagnosticOptions) {
+ // Initialize a stream.
+ BitstreamCursor Stream(StreamData);
+
+ // Sniff for the signature.
+ if (!startsWithASTFileMagic(Stream))
+ return Failure;
+
+ // Scan for the UNHASHED_CONTROL_BLOCK_ID block.
+ if (SkipCursorToBlock(Stream, UNHASHED_CONTROL_BLOCK_ID))
+ return Failure;
+
+ // Read all of the records in the options block.
+ RecordData Record;
+ ASTReadResult Result = Success;
+ while (1) {
+ llvm::BitstreamEntry Entry = Stream.advance();
+
+ switch (Entry.Kind) {
+ case llvm::BitstreamEntry::Error:
+ case llvm::BitstreamEntry::SubBlock:
+ return Failure;
+
+ case llvm::BitstreamEntry::EndBlock:
+ return Result;
+
+ case llvm::BitstreamEntry::Record:
+ // The interesting case.
+ break;
+ }
+
+ // Read and process a record.
+ Record.clear();
+ switch (
+ (UnhashedControlBlockRecordTypes)Stream.readRecord(Entry.ID, Record)) {
+ case SIGNATURE: {
+ if (F)
+ std::copy(Record.begin(), Record.end(), F->Signature.data());
+ break;
+ }
+ case DIAGNOSTIC_OPTIONS: {
+ bool Complain = (ClientLoadCapabilities & ARR_OutOfDate) == 0;
+ if (Listener && ValidateDiagnosticOptions &&
+ !AllowCompatibleConfigurationMismatch &&
+ ParseDiagnosticOptions(Record, Complain, *Listener))
+ Result = OutOfDate; // Don't return early. Read the signature.
+ break;
+ }
+ case DIAG_PRAGMA_MAPPINGS:
+ if (!F)
+ break;
+ if (F->PragmaDiagMappings.empty())
+ F->PragmaDiagMappings.swap(Record);
+ else
+ F->PragmaDiagMappings.insert(F->PragmaDiagMappings.end(),
+ Record.begin(), Record.end());
+ break;
+ }
+ }
+}
+
/// Parse a record and blob containing module file extension metadata.
static bool parseModuleFileExtensionMetadata(
const SmallVectorImpl<uint64_t> &Record,
@@ -4214,23 +4355,24 @@
static ASTFileSignature readASTFileSignature(StringRef PCH) {
BitstreamCursor Stream(PCH);
if (!startsWithASTFileMagic(Stream))
- return 0;
+ return ASTFileSignature();
- // Scan for the CONTROL_BLOCK_ID block.
- if (SkipCursorToBlock(Stream, CONTROL_BLOCK_ID))
- return 0;
+ // Scan for the UNHASHED_CONTROL_BLOCK_ID block.
+ if (SkipCursorToBlock(Stream, UNHASHED_CONTROL_BLOCK_ID))
+ return ASTFileSignature();
- // Scan for SIGNATURE inside the control block.
+ // Scan for SIGNATURE inside the diagnostic options block.
ASTReader::RecordData Record;
while (true) {
llvm::BitstreamEntry Entry = Stream.advanceSkippingSubblocks();
if (Entry.Kind != llvm::BitstreamEntry::Record)
- return 0;
+ return ASTFileSignature();
Record.clear();
StringRef Blob;
if (SIGNATURE == Stream.readRecord(Entry.ID, Record, &Blob))
- return Record[0];
+ return {{{(uint32_t)Record[0], (uint32_t)Record[1], (uint32_t)Record[2],
+ (uint32_t)Record[3], (uint32_t)Record[4]}}};
}
}
@@ -4349,7 +4491,8 @@
}
// Initialize the stream
- BitstreamCursor Stream(PCHContainerRdr.ExtractPCH(**Buffer));
+ StringRef Bytes = PCHContainerRdr.ExtractPCH(**Buffer);
+ BitstreamCursor Stream(Bytes);
// Sniff for the signature.
if (!startsWithASTFileMagic(Stream))
@@ -4377,8 +4520,7 @@
std::string IgnoredSuggestedPredefines;
if (ReadOptionsBlock(Stream, ARR_ConfigurationMismatch | ARR_OutOfDate,
/*AllowCompatibleConfigurationMismatch*/ false,
- Listener, IgnoredSuggestedPredefines,
- ValidateDiagnosticOptions) != Success)
+ Listener, IgnoredSuggestedPredefines) != Success)
return true;
break;
}
@@ -4499,6 +4641,7 @@
// Look for module file extension blocks, if requested.
if (FindModuleFileExtensions) {
+ BitstreamCursor SavedStream = Stream;
while (!SkipCursorToBlock(Stream, EXTENSION_BLOCK_ID)) {
bool DoneWithExtensionBlock = false;
while (!DoneWithExtensionBlock) {
@@ -4537,8 +4680,16 @@
}
}
}
+ Stream = SavedStream;
}
+ // Scan for the UNHASHED_CONTROL_BLOCK_ID block.
+ if (readUnhashedControlBlockImpl(
+ nullptr, Bytes, ARR_ConfigurationMismatch | ARR_OutOfDate,
+ /*AllowCompatibleConfigurationMismatch*/ false, &Listener,
+ ValidateDiagnosticOptions) != Success)
+ return true;
+
return false;
}
@@ -4617,6 +4768,7 @@
bool IsExplicit = Record[Idx++];
bool IsSystem = Record[Idx++];
bool IsExternC = Record[Idx++];
+ bool IsSwiftInferImportAsMember = Record[Idx++];
bool InferSubmodules = Record[Idx++];
bool InferExplicitSubmodules = Record[Idx++];
bool InferExportWildcard = Record[Idx++];
@@ -4661,6 +4813,7 @@
CurrentModule->IsFromModuleFile = true;
CurrentModule->IsSystem = IsSystem || CurrentModule->IsSystem;
CurrentModule->IsExternC = IsExternC;
+ CurrentModule->IsSwiftInferImportAsMember = IsSwiftInferImportAsMember;
CurrentModule->InferSubmodules = InferSubmodules;
CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules;
CurrentModule->InferExportWildcard = InferExportWildcard;
@@ -5288,48 +5441,83 @@
}
void ASTReader::ReadPragmaDiagnosticMappings(DiagnosticsEngine &Diag) {
- // FIXME: Make it work properly with modules.
- SmallVector<DiagnosticsEngine::DiagState *, 32> DiagStates;
- for (ModuleIterator I = ModuleMgr.begin(), E = ModuleMgr.end(); I != E; ++I) {
- ModuleFile &F = *(*I);
- unsigned Idx = 0;
- DiagStates.clear();
- assert(!Diag.DiagStates.empty());
- DiagStates.push_back(&Diag.DiagStates.front()); // the command-line one.
- while (Idx < F.PragmaDiagMappings.size()) {
- SourceLocation Loc = ReadSourceLocation(F, F.PragmaDiagMappings[Idx++]);
- unsigned DiagStateID = F.PragmaDiagMappings[Idx++];
- if (DiagStateID != 0) {
- Diag.DiagStatePoints.push_back(
- DiagnosticsEngine::DiagStatePoint(DiagStates[DiagStateID-1],
- FullSourceLoc(Loc, SourceMgr)));
- continue;
- }
+ using DiagState = DiagnosticsEngine::DiagState;
+ SmallVector<DiagState *, 32> DiagStates;
- assert(DiagStateID == 0);
+ for (ModuleFile &F : ModuleMgr) {
+ unsigned Idx = 0;
+ auto &Record = F.PragmaDiagMappings;
+ if (Record.empty())
+ continue;
+
+ DiagStates.clear();
+
+ auto ReadDiagState =
+ [&](const DiagState &BasedOn, SourceLocation Loc,
+ bool IncludeNonPragmaStates) -> DiagnosticsEngine::DiagState * {
+ unsigned BackrefID = Record[Idx++];
+ if (BackrefID != 0)
+ return DiagStates[BackrefID - 1];
+
// A new DiagState was created here.
- Diag.DiagStates.push_back(*Diag.GetCurDiagState());
- DiagnosticsEngine::DiagState *NewState = &Diag.DiagStates.back();
+ Diag.DiagStates.push_back(BasedOn);
+ DiagState *NewState = &Diag.DiagStates.back();
DiagStates.push_back(NewState);
- Diag.DiagStatePoints.push_back(
- DiagnosticsEngine::DiagStatePoint(NewState,
- FullSourceLoc(Loc, SourceMgr)));
- while (true) {
- assert(Idx < F.PragmaDiagMappings.size() &&
- "Invalid data, didn't find '-1' marking end of diag/map pairs");
- if (Idx >= F.PragmaDiagMappings.size()) {
- break; // Something is messed up but at least avoid infinite loop in
- // release build.
- }
- unsigned DiagID = F.PragmaDiagMappings[Idx++];
- if (DiagID == (unsigned)-1) {
- break; // no more diag/map pairs for this location.
- }
- diag::Severity Map = (diag::Severity)F.PragmaDiagMappings[Idx++];
+ while (Idx + 1 < Record.size() && Record[Idx] != unsigned(-1)) {
+ unsigned DiagID = Record[Idx++];
+ diag::Severity Map = (diag::Severity)Record[Idx++];
DiagnosticMapping Mapping = Diag.makeUserMapping(Map, Loc);
- Diag.GetCurDiagState()->setMapping(DiagID, Mapping);
+ if (Mapping.isPragma() || IncludeNonPragmaStates)
+ NewState->setMapping(DiagID, Mapping);
+ }
+ assert(Idx != Record.size() && Record[Idx] == unsigned(-1) &&
+ "Invalid data, didn't find '-1' marking end of diag/map pairs");
+ ++Idx;
+ return NewState;
+ };
+
+ auto *FirstState = ReadDiagState(
+ F.isModule() ? DiagState() : *Diag.DiagStatesByLoc.CurDiagState,
+ SourceLocation(), F.isModule());
+ SourceLocation CurStateLoc =
+ ReadSourceLocation(F, F.PragmaDiagMappings[Idx++]);
+ auto *CurState = ReadDiagState(*FirstState, CurStateLoc, false);
+
+ if (!F.isModule()) {
+ Diag.DiagStatesByLoc.CurDiagState = CurState;
+ Diag.DiagStatesByLoc.CurDiagStateLoc = CurStateLoc;
+
+ // Preserve the property that the imaginary root file describes the
+ // current state.
+ auto &T = Diag.DiagStatesByLoc.Files[FileID()].StateTransitions;
+ if (T.empty())
+ T.push_back({CurState, 0});
+ else
+ T[0].State = CurState;
+ }
+
+ while (Idx < Record.size()) {
+ SourceLocation Loc = ReadSourceLocation(F, Record[Idx++]);
+ auto IDAndOffset = SourceMgr.getDecomposedLoc(Loc);
+ assert(IDAndOffset.second == 0 && "not a start location for a FileID");
+ unsigned Transitions = Record[Idx++];
+
+ // Note that we don't need to set up Parent/ParentOffset here, because
+ // we won't be changing the diagnostic state within imported FileIDs
+ // (other than perhaps appending to the main source file, which has no
+ // parent).
+ auto &F = Diag.DiagStatesByLoc.Files[IDAndOffset.first];
+ F.StateTransitions.reserve(F.StateTransitions.size() + Transitions);
+ for (unsigned I = 0; I != Transitions; ++I) {
+ unsigned Offset = Record[Idx++];
+ auto *State =
+ ReadDiagState(*FirstState, Loc.getLocWithOffset(Offset), false);
+ F.StateTransitions.push_back({State, Offset});
}
}
+
+ // Don't try to read these mappings again.
+ Record.clear();
}
}
@@ -7092,18 +7280,15 @@
GlobalPreprocessedEntityMap);
llvm::errs() << "\n*** PCH/Modules Loaded:";
- for (ModuleManager::ModuleConstIterator M = ModuleMgr.begin(),
- MEnd = ModuleMgr.end();
- M != MEnd; ++M)
- (*M)->dump();
+ for (ModuleFile &M : ModuleMgr)
+ M.dump();
}
/// Return the amount of memory used by memory buffers, breaking down
/// by heap-backed versus mmap'ed memory.
void ASTReader::getMemoryBufferSizes(MemoryBufferSizes &sizes) const {
- for (ModuleConstIterator I = ModuleMgr.begin(),
- E = ModuleMgr.end(); I != E; ++I) {
- if (llvm::MemoryBuffer *buf = (*I)->Buffer.get()) {
+ for (ModuleFile &I : ModuleMgr) {
+ if (llvm::MemoryBuffer *buf = I.Buffer) {
size_t bytes = buf->getBufferSize();
switch (buf->getBufferKind()) {
case llvm::MemoryBuffer::MemoryBuffer_Malloc:
@@ -8493,6 +8678,21 @@
}
}
+void ASTReader::visitInputFiles(serialization::ModuleFile &MF,
+ bool IncludeSystem, bool Complain,
+ llvm::function_ref<void(const serialization::InputFile &IF,
+ bool isSystem)> Visitor) {
+ unsigned NumUserInputs = MF.NumUserInputFiles;
+ unsigned NumInputs = MF.InputFilesLoaded.size();
+ assert(NumUserInputs <= NumInputs);
+ unsigned N = IncludeSystem ? NumInputs : NumUserInputs;
+ for (unsigned I = 0; I < N; ++I) {
+ bool IsSystem = I >= NumUserInputs;
+ InputFile IF = getInputFile(MF, I+1, Complain);
+ Visitor(IF, IsSystem);
+ }
+}
+
std::string ASTReader::getOwningModuleNameForDiagnostic(const Decl *D) {
// If we know the owning module, use it.
if (Module *M = D->getImportedOwningModule())
@@ -8905,8 +9105,10 @@
: cast<ASTReaderListener>(new PCHValidator(PP, *this))),
SourceMgr(PP.getSourceManager()), FileMgr(PP.getFileManager()),
PCHContainerRdr(PCHContainerRdr), Diags(PP.getDiagnostics()), PP(PP),
- Context(Context), ModuleMgr(PP.getFileManager(), PCHContainerRdr),
- DummyIdResolver(PP), ReadTimer(std::move(ReadTimer)), isysroot(isysroot),
+ Context(Context),
+ ModuleMgr(PP.getFileManager(), PP.getPCMCache(), PCHContainerRdr),
+ PCMCache(PP.getPCMCache()), DummyIdResolver(PP),
+ ReadTimer(std::move(ReadTimer)), isysroot(isysroot),
DisableValidation(DisableValidation),
AllowASTWithCompilerErrors(AllowASTWithCompilerErrors),
AllowConfigurationMismatch(AllowConfigurationMismatch),
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index c691919..707a924 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -2513,8 +2513,8 @@
// An ImportDecl or VarDecl imported from a module will get emitted when
// we import the relevant module.
- if ((isa<ImportDecl>(D) || isa<VarDecl>(D)) && Ctx.DeclMustBeEmitted(D) &&
- D->getImportedOwningModule())
+ if ((isa<ImportDecl>(D) || isa<VarDecl>(D)) && D->getImportedOwningModule() &&
+ Ctx.DeclMustBeEmitted(D))
return false;
if (isa<FileScopeAsmDecl>(D) ||
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 886523e..a44c9a8 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -18,8 +18,8 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTUnresolvedSet.h"
#include "clang/AST/Decl.h"
-#include "clang/AST/DeclContextInternals.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclContextInternals.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
@@ -33,8 +33,9 @@
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/FileSystemOptions.h"
-#include "clang/Basic/LangOptions.h"
#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/MemoryBufferCache.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/ObjCRuntime.h"
#include "clang/Basic/SourceManager.h"
@@ -64,9 +65,9 @@
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Bitcode/BitCodes.h"
#include "llvm/Bitcode/BitstreamWriter.h"
@@ -78,6 +79,7 @@
#include "llvm/Support/OnDiskHashTable.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/SHA1.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
@@ -1001,7 +1003,6 @@
// Control Block.
BLOCK(CONTROL_BLOCK);
RECORD(METADATA);
- RECORD(SIGNATURE);
RECORD(MODULE_NAME);
RECORD(MODULE_DIRECTORY);
RECORD(MODULE_MAP_FILE);
@@ -1014,7 +1015,6 @@
BLOCK(OPTIONS_BLOCK);
RECORD(LANGUAGE_OPTIONS);
RECORD(TARGET_OPTIONS);
- RECORD(DIAGNOSTIC_OPTIONS);
RECORD(FILE_SYSTEM_OPTIONS);
RECORD(HEADER_SEARCH_OPTIONS);
RECORD(PREPROCESSOR_OPTIONS);
@@ -1049,7 +1049,6 @@
RECORD(UPDATE_VISIBLE);
RECORD(DECL_UPDATE_OFFSETS);
RECORD(DECL_UPDATES);
- RECORD(DIAG_PRAGMA_MAPPINGS);
RECORD(CUDA_SPECIAL_DECL_REFS);
RECORD(HEADER_SEARCH_TABLE);
RECORD(FP_PRAGMA_OPTIONS);
@@ -1242,6 +1241,11 @@
BLOCK(EXTENSION_BLOCK);
RECORD(EXTENSION_METADATA);
+ BLOCK(UNHASHED_CONTROL_BLOCK);
+ RECORD(SIGNATURE);
+ RECORD(DIAGNOSTIC_OPTIONS);
+ RECORD(DIAG_PRAGMA_MAPPINGS);
+
#undef RECORD
#undef BLOCK
Stream.ExitBlock();
@@ -1304,21 +1308,73 @@
return Filename + Pos;
}
-static ASTFileSignature getSignature() {
- while (true) {
- if (ASTFileSignature S = llvm::sys::Process::GetRandomNumber())
- return S;
- // Rely on GetRandomNumber to eventually return non-zero...
+ASTFileSignature ASTWriter::createSignature(StringRef Bytes) {
+ // Calculate the hash till start of UNHASHED_CONTROL_BLOCK.
+ llvm::SHA1 Hasher;
+ Hasher.update(ArrayRef<uint8_t>(Bytes.bytes_begin(), Bytes.size()));
+ auto Hash = Hasher.result();
+
+ // Convert to an array [5*i32].
+ ASTFileSignature Signature;
+ auto LShift = [&](unsigned char Val, unsigned Shift) {
+ return (uint32_t)Val << Shift;
+ };
+ for (int I = 0; I != 5; ++I)
+ Signature[I] = LShift(Hash[I * 4 + 0], 24) | LShift(Hash[I * 4 + 1], 16) |
+ LShift(Hash[I * 4 + 2], 8) | LShift(Hash[I * 4 + 3], 0);
+
+ return Signature;
+}
+
+ASTFileSignature ASTWriter::writeUnhashedControlBlock(Preprocessor &PP,
+ ASTContext &Context) {
+ // Flush first to prepare the PCM hash (signature).
+ Stream.FlushToWord();
+ auto StartOfUnhashedControl = Stream.GetCurrentBitNo() >> 3;
+
+ // Enter the block and prepare to write records.
+ RecordData Record;
+ Stream.EnterSubblock(UNHASHED_CONTROL_BLOCK_ID, 5);
+
+ // For implicit modules, write the hash of the PCM as its signature.
+ ASTFileSignature Signature;
+ if (WritingModule &&
+ PP.getHeaderSearchInfo().getHeaderSearchOpts().ModulesHashContent) {
+ Signature = createSignature(StringRef(Buffer.begin(), StartOfUnhashedControl));
+ Record.append(Signature.begin(), Signature.end());
+ Stream.EmitRecord(SIGNATURE, Record);
+ Record.clear();
}
+
+ // Diagnostic options.
+ const auto &Diags = Context.getDiagnostics();
+ const DiagnosticOptions &DiagOpts = Diags.getDiagnosticOptions();
+#define DIAGOPT(Name, Bits, Default) Record.push_back(DiagOpts.Name);
+#define ENUM_DIAGOPT(Name, Type, Bits, Default) \
+ Record.push_back(static_cast<unsigned>(DiagOpts.get##Name()));
+#include "clang/Basic/DiagnosticOptions.def"
+ Record.push_back(DiagOpts.Warnings.size());
+ for (unsigned I = 0, N = DiagOpts.Warnings.size(); I != N; ++I)
+ AddString(DiagOpts.Warnings[I], Record);
+ Record.push_back(DiagOpts.Remarks.size());
+ for (unsigned I = 0, N = DiagOpts.Remarks.size(); I != N; ++I)
+ AddString(DiagOpts.Remarks[I], Record);
+ // Note: we don't serialize the log or serialization file names, because they
+ // are generally transient files and will almost always be overridden.
+ Stream.EmitRecord(DIAGNOSTIC_OPTIONS, Record);
+
+ // Write out the diagnostic/pragma mappings.
+ WritePragmaDiagnosticMappings(Diags, /* IsModule = */ WritingModule);
+
+ // Leave the options block.
+ Stream.ExitBlock();
+ return Signature;
}
/// \brief Write the control block.
-uint64_t ASTWriter::WriteControlBlock(Preprocessor &PP,
- ASTContext &Context,
- StringRef isysroot,
- const std::string &OutputFile) {
- ASTFileSignature Signature = 0;
-
+void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context,
+ StringRef isysroot,
+ const std::string &OutputFile) {
using namespace llvm;
Stream.EnterSubblock(CONTROL_BLOCK_ID, 5);
RecordData Record;
@@ -1346,15 +1402,6 @@
getClangFullRepositoryVersion());
}
if (WritingModule) {
- // For implicit modules we output a signature that we can use to ensure
- // duplicate module builds don't collide in the cache as their output order
- // is non-deterministic.
- // FIXME: Remove this when output is deterministic.
- if (Context.getLangOpts().ImplicitModules) {
- Signature = getSignature();
- RecordData::value_type Record[] = {Signature};
- Stream.EmitRecord(SIGNATURE, Record);
- }
// Module name
auto Abbrev = std::make_shared<BitCodeAbbrev>();
@@ -1420,17 +1467,23 @@
serialization::ModuleManager &Mgr = Chain->getModuleManager();
Record.clear();
- for (auto *M : Mgr) {
+ for (ModuleFile &M : Mgr) {
// Skip modules that weren't directly imported.
- if (!M->isDirectlyImported())
+ if (!M.isDirectlyImported())
continue;
- Record.push_back((unsigned)M->Kind); // FIXME: Stable encoding
- AddSourceLocation(M->ImportLoc, Record);
- Record.push_back(M->File->getSize());
- Record.push_back(getTimestampForOutput(M->File));
- Record.push_back(M->Signature);
- AddPath(M->FileName, Record);
+ Record.push_back((unsigned)M.Kind); // FIXME: Stable encoding
+ AddSourceLocation(M.ImportLoc, Record);
+
+ // If we have calculated signature, there is no need to store
+ // the size or timestamp.
+ Record.push_back(M.Signature ? 0 : M.File->getSize());
+ Record.push_back(M.Signature ? 0 : getTimestampForOutput(M.File));
+
+ for (auto I : M.Signature)
+ Record.push_back(I);
+
+ AddPath(M.FileName, Record);
}
Stream.EmitRecord(IMPORTS, Record);
}
@@ -1492,24 +1545,6 @@
}
Stream.EmitRecord(TARGET_OPTIONS, Record);
- // Diagnostic options.
- Record.clear();
- const DiagnosticOptions &DiagOpts
- = Context.getDiagnostics().getDiagnosticOptions();
-#define DIAGOPT(Name, Bits, Default) Record.push_back(DiagOpts.Name);
-#define ENUM_DIAGOPT(Name, Type, Bits, Default) \
- Record.push_back(static_cast<unsigned>(DiagOpts.get##Name()));
-#include "clang/Basic/DiagnosticOptions.def"
- Record.push_back(DiagOpts.Warnings.size());
- for (unsigned I = 0, N = DiagOpts.Warnings.size(); I != N; ++I)
- AddString(DiagOpts.Warnings[I], Record);
- Record.push_back(DiagOpts.Remarks.size());
- for (unsigned I = 0, N = DiagOpts.Remarks.size(); I != N; ++I)
- AddString(DiagOpts.Remarks[I], Record);
- // Note: we don't serialize the log or serialization file names, because they
- // are generally transient files and will almost always be overridden.
- Stream.EmitRecord(DIAGNOSTIC_OPTIONS, Record);
-
// File system options.
Record.clear();
const FileSystemOptions &FSOpts =
@@ -1623,7 +1658,6 @@
PP.getHeaderSearchInfo().getHeaderSearchOpts(),
PP.getLangOpts().Modules);
Stream.ExitBlock();
- return Signature;
}
namespace {
@@ -2516,7 +2550,8 @@
auto *Top = Mod->getTopLevelModule();
if (Top != WritingModule &&
- !Top->fullModuleNameIs(StringRef(getLangOpts().CurrentModule)))
+ (getLangOpts().CompilingPCH ||
+ !Top->fullModuleNameIs(StringRef(getLangOpts().CurrentModule))))
return 0;
return SubmoduleIDs[Mod] = NextSubmoduleID++;
@@ -2558,6 +2593,7 @@
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExplicit
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExternC
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSwiftInferIAM...
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferSubmodules...
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExplicit...
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExportWild...
@@ -2652,9 +2688,9 @@
{
RecordData::value_type Record[] = {
SUBMODULE_DEFINITION, ID, ParentID, Mod->IsFramework, Mod->IsExplicit,
- Mod->IsSystem, Mod->IsExternC, Mod->InferSubmodules,
- Mod->InferExplicitSubmodules, Mod->InferExportWildcard,
- Mod->ConfigMacrosExhaustive};
+ Mod->IsSystem, Mod->IsExternC, Mod->IsSwiftInferImportAsMember,
+ Mod->InferSubmodules, Mod->InferExplicitSubmodules,
+ Mod->InferExportWildcard, Mod->ConfigMacrosExhaustive};
Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name);
}
@@ -2789,38 +2825,43 @@
void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag,
bool isModule) {
- // Make sure set diagnostic pragmas don't affect the translation unit that
- // imports the module.
- // FIXME: Make diagnostic pragma sections work properly with modules.
- if (isModule)
- return;
-
llvm::SmallDenseMap<const DiagnosticsEngine::DiagState *, unsigned, 64>
DiagStateIDMap;
unsigned CurrID = 0;
- DiagStateIDMap[&Diag.DiagStates.front()] = ++CurrID; // the command-line one.
RecordData Record;
- for (DiagnosticsEngine::DiagStatePointsTy::const_iterator
- I = Diag.DiagStatePoints.begin(), E = Diag.DiagStatePoints.end();
- I != E; ++I) {
- const DiagnosticsEngine::DiagStatePoint &point = *I;
- if (point.Loc.isInvalid())
- continue;
- AddSourceLocation(point.Loc, Record);
- unsigned &DiagStateID = DiagStateIDMap[point.State];
+ auto AddDiagState = [&](const DiagnosticsEngine::DiagState *State,
+ bool IncludeNonPragmaStates) {
+ unsigned &DiagStateID = DiagStateIDMap[State];
Record.push_back(DiagStateID);
-
+
if (DiagStateID == 0) {
DiagStateID = ++CurrID;
- for (const auto &I : *(point.State)) {
- if (I.second.isPragma()) {
+ for (const auto &I : *State) {
+ if (I.second.isPragma() || IncludeNonPragmaStates) {
Record.push_back(I.first);
Record.push_back((unsigned)I.second.getSeverity());
}
}
- Record.push_back(-1); // mark the end of the diag/map pairs for this
- // location.
+ // Add a sentinel to mark the end of the diag IDs.
+ Record.push_back(unsigned(-1));
+ }
+ };
+
+ AddDiagState(Diag.DiagStatesByLoc.FirstDiagState, isModule);
+ AddSourceLocation(Diag.DiagStatesByLoc.CurDiagStateLoc, Record);
+ AddDiagState(Diag.DiagStatesByLoc.CurDiagState, false);
+
+ for (auto &FileIDAndFile : Diag.DiagStatesByLoc.Files) {
+ if (!FileIDAndFile.first.isValid() ||
+ !FileIDAndFile.second.HasLocalTransitions)
+ continue;
+ AddSourceLocation(Diag.SourceMgr->getLocForStartOfFile(FileIDAndFile.first),
+ Record);
+ Record.push_back(FileIDAndFile.second.StateTransitions.size());
+ for (auto &StatePoint : FileIDAndFile.second.StateTransitions) {
+ Record.push_back(StatePoint.Offset);
+ AddDiagState(StatePoint.State, false);
}
}
@@ -3283,8 +3324,6 @@
NeedDecls(!IsModule || !Writer.getLangOpts().CPlusPlus),
InterestingIdentifierOffsets(InterestingIdentifierOffsets) {}
- bool needDecls() const { return NeedDecls; }
-
static hash_value_type ComputeHash(const IdentifierInfo* II) {
return llvm::HashString(II->getName());
}
@@ -3434,10 +3473,8 @@
assert(II && "NULL identifier in identifier table");
// Write out identifiers if either the ID is local or the identifier has
// changed since it was loaded.
- if (ID >= FirstIdentID || !Chain || !II->isFromAST()
- || II->hasChangedSinceDeserialization() ||
- (Trait.needDecls() &&
- II->hasFETokenInfoChangedSinceDeserialization()))
+ if (ID >= FirstIdentID || !Chain || !II->isFromAST() ||
+ II->hasChangedSinceDeserialization())
Generator.insert(II, ID, Trait);
}
@@ -4223,9 +4260,11 @@
}
ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream,
+ SmallVectorImpl<char> &Buffer, MemoryBufferCache &PCMCache,
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
bool IncludeTimestamps)
- : Stream(Stream), IncludeTimestamps(IncludeTimestamps) {
+ : Stream(Stream), Buffer(Buffer), PCMCache(PCMCache),
+ IncludeTimestamps(IncludeTimestamps) {
for (const auto &Ext : Extensions) {
if (auto Writer = Ext->createExtensionWriter(*this))
ModuleFileExtensionWriters.push_back(std::move(Writer));
@@ -4245,9 +4284,10 @@
return IncludeTimestamps ? E->getModificationTime() : 0;
}
-uint64_t ASTWriter::WriteAST(Sema &SemaRef, const std::string &OutputFile,
- Module *WritingModule, StringRef isysroot,
- bool hasErrors) {
+ASTFileSignature ASTWriter::WriteAST(Sema &SemaRef,
+ const std::string &OutputFile,
+ Module *WritingModule, StringRef isysroot,
+ bool hasErrors) {
WritingAST = true;
ASTHasCompilerErrors = hasErrors;
@@ -4271,6 +4311,12 @@
this->BaseDirectory.clear();
WritingAST = false;
+ if (SemaRef.Context.getLangOpts().ImplicitModules && WritingModule) {
+ // Construct MemoryBuffer and update buffer manager.
+ PCMCache.addBuffer(OutputFile,
+ llvm::MemoryBuffer::getMemBufferCopy(
+ StringRef(Buffer.begin(), Buffer.size())));
+ }
return Signature;
}
@@ -4283,9 +4329,9 @@
}
}
-uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
- const std::string &OutputFile,
- Module *WritingModule) {
+ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
+ const std::string &OutputFile,
+ Module *WritingModule) {
using namespace llvm;
bool isModule = WritingModule != nullptr;
@@ -4433,7 +4479,7 @@
}
// Write the control block
- uint64_t Signature = WriteControlBlock(PP, Context, isysroot, OutputFile);
+ WriteControlBlock(PP, Context, isysroot, OutputFile);
// Write the remaining AST contents.
Stream.EnterSubblock(AST_BLOCK_ID, 5);
@@ -4573,10 +4619,10 @@
SmallString<2048> Buffer;
{
llvm::raw_svector_ostream Out(Buffer);
- for (ModuleFile *M : Chain->ModuleMgr) {
+ for (ModuleFile &M : Chain->ModuleMgr) {
using namespace llvm::support;
endian::Writer<little> LE(Out);
- StringRef FileName = M->FileName;
+ StringRef FileName = M.FileName;
LE.write<uint16_t>(FileName.size());
Out.write(FileName.data(), FileName.size());
@@ -4594,15 +4640,15 @@
// These values should be unique within a chain, since they will be read
// as keys into ContinuousRangeMaps.
- writeBaseIDOrNone(M->SLocEntryBaseOffset, M->LocalNumSLocEntries);
- writeBaseIDOrNone(M->BaseIdentifierID, M->LocalNumIdentifiers);
- writeBaseIDOrNone(M->BaseMacroID, M->LocalNumMacros);
- writeBaseIDOrNone(M->BasePreprocessedEntityID,
- M->NumPreprocessedEntities);
- writeBaseIDOrNone(M->BaseSubmoduleID, M->LocalNumSubmodules);
- writeBaseIDOrNone(M->BaseSelectorID, M->LocalNumSelectors);
- writeBaseIDOrNone(M->BaseDeclID, M->LocalNumDecls);
- writeBaseIDOrNone(M->BaseTypeIndex, M->LocalNumTypes);
+ writeBaseIDOrNone(M.SLocEntryBaseOffset, M.LocalNumSLocEntries);
+ writeBaseIDOrNone(M.BaseIdentifierID, M.LocalNumIdentifiers);
+ writeBaseIDOrNone(M.BaseMacroID, M.LocalNumMacros);
+ writeBaseIDOrNone(M.BasePreprocessedEntityID,
+ M.NumPreprocessedEntities);
+ writeBaseIDOrNone(M.BaseSubmoduleID, M.LocalNumSubmodules);
+ writeBaseIDOrNone(M.BaseSelectorID, M.LocalNumSelectors);
+ writeBaseIDOrNone(M.BaseDeclID, M.LocalNumDecls);
+ writeBaseIDOrNone(M.BaseTypeIndex, M.LocalNumTypes);
}
}
RecordData::value_type Record[] = {MODULE_OFFSET_MAP};
@@ -4650,7 +4696,6 @@
WriteOpenCLExtensionTypes(SemaRef);
WriteOpenCLExtensionDecls(SemaRef);
WriteCUDAPragmas(SemaRef);
- WritePragmaDiagnosticMappings(Context.getDiagnostics(), isModule);
// If we're emitting a module, write out the submodule information.
if (WritingModule)
@@ -4776,7 +4821,7 @@
for (const auto &ExtWriter : ModuleFileExtensionWriters)
WriteModuleFileExtension(SemaRef, *ExtWriter);
- return Signature;
+ return writeUnhashedControlBlock(PP, Context);
}
void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
diff --git a/lib/Serialization/GeneratePCH.cpp b/lib/Serialization/GeneratePCH.cpp
index 7f1b750..429ae8e 100644
--- a/lib/Serialization/GeneratePCH.cpp
+++ b/lib/Serialization/GeneratePCH.cpp
@@ -28,7 +28,8 @@
bool AllowASTWithErrors, bool IncludeTimestamps)
: PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()),
SemaPtr(nullptr), Buffer(Buffer), Stream(Buffer->Data),
- Writer(Stream, Extensions, IncludeTimestamps),
+ Writer(Stream, Buffer->Data, PP.getPCMCache(), Extensions,
+ IncludeTimestamps),
AllowASTWithErrors(AllowASTWithErrors) {
Buffer->IsComplete = false;
}
diff --git a/lib/Serialization/GlobalModuleIndex.cpp b/lib/Serialization/GlobalModuleIndex.cpp
index ae5796e..6978e7e 100644
--- a/lib/Serialization/GlobalModuleIndex.cpp
+++ b/lib/Serialization/GlobalModuleIndex.cpp
@@ -376,6 +376,15 @@
/// \brief The set of modules on which this module depends. Each entry is
/// a module ID.
SmallVector<unsigned, 4> Dependencies;
+ ASTFileSignature Signature;
+ };
+
+ struct ImportedModuleFileInfo {
+ off_t StoredSize;
+ time_t StoredModTime;
+ ASTFileSignature StoredSignature;
+ ImportedModuleFileInfo(off_t Size, time_t ModTime, ASTFileSignature Sig)
+ : StoredSize(Size), StoredModTime(ModTime), StoredSignature(Sig) {}
};
/// \brief Builder that generates the global module index file.
@@ -383,12 +392,20 @@
FileManager &FileMgr;
const PCHContainerReader &PCHContainerRdr;
- /// \brief Mapping from files to module file information.
+ /// Mapping from files to module file information.
typedef llvm::MapVector<const FileEntry *, ModuleFileInfo> ModuleFilesMap;
- /// \brief Information about each of the known module files.
+ /// Information about each of the known module files.
ModuleFilesMap ModuleFiles;
+ /// \brief Mapping from the imported module file to the imported
+ /// information.
+ typedef std::multimap<const FileEntry *, ImportedModuleFileInfo>
+ ImportedModuleFilesMap;
+
+ /// \brief Information about each importing of a module file.
+ ImportedModuleFilesMap ImportedModuleFiles;
+
/// \brief Mapping from identifiers to the list of module file IDs that
/// consider this identifier to be interesting.
typedef llvm::StringMap<SmallVector<unsigned, 2> > InterestingIdentifierMap;
@@ -424,7 +441,8 @@
bool loadModuleFile(const FileEntry *File);
/// \brief Write the index to the given bitstream.
- void writeIndex(llvm::BitstreamWriter &Stream);
+ /// \returns true if an error occurred, false otherwise.
+ bool writeIndex(llvm::BitstreamWriter &Stream);
};
}
@@ -515,7 +533,7 @@
unsigned ID = getModuleFileInfo(File).ID;
// Search for the blocks and records we care about.
- enum { Other, ControlBlock, ASTBlock } State = Other;
+ enum { Other, ControlBlock, ASTBlock, DiagnosticOptionsBlock } State = Other;
bool Done = false;
while (!Done) {
llvm::BitstreamEntry Entry = InStream.advance();
@@ -553,6 +571,15 @@
continue;
}
+ if (Entry.ID == UNHASHED_CONTROL_BLOCK_ID) {
+ if (InStream.EnterSubBlock(UNHASHED_CONTROL_BLOCK_ID))
+ return true;
+
+ // Found the Diagnostic Options block.
+ State = DiagnosticOptionsBlock;
+ continue;
+ }
+
if (InStream.SkipBlock())
return true;
@@ -587,7 +614,10 @@
// Skip the stored signature.
// FIXME: we could read the signature out of the import and validate it.
- Idx++;
+ ASTFileSignature StoredSignature = {
+ {{(uint32_t)Record[Idx++], (uint32_t)Record[Idx++],
+ (uint32_t)Record[Idx++], (uint32_t)Record[Idx++],
+ (uint32_t)Record[Idx++]}}};
// Retrieve the imported file name.
unsigned Length = Record[Idx++];
@@ -599,11 +629,16 @@
const FileEntry *DependsOnFile
= FileMgr.getFile(ImportedFile, /*openFile=*/false,
/*cacheFailure=*/false);
- if (!DependsOnFile ||
- (StoredSize != DependsOnFile->getSize()) ||
- (StoredModTime != DependsOnFile->getModificationTime()))
+
+ if (!DependsOnFile)
return true;
+ // Save the information in ImportedModuleFileInfo so we can verify after
+ // loading all pcms.
+ ImportedModuleFiles.insert(std::make_pair(
+ DependsOnFile, ImportedModuleFileInfo(StoredSize, StoredModTime,
+ StoredSignature)));
+
// Record the dependency.
unsigned DependsOnID = getModuleFileInfo(DependsOnFile).ID;
getModuleFileInfo(File).Dependencies.push_back(DependsOnID);
@@ -632,6 +667,12 @@
}
}
+ // Get Signature.
+ if (State == DiagnosticOptionsBlock && Code == SIGNATURE)
+ getModuleFileInfo(File).Signature = {
+ {{(uint32_t)Record[0], (uint32_t)Record[1], (uint32_t)Record[2],
+ (uint32_t)Record[3], (uint32_t)Record[4]}}};
+
// We don't care about this record.
}
@@ -680,7 +721,20 @@
}
-void GlobalModuleIndexBuilder::writeIndex(llvm::BitstreamWriter &Stream) {
+bool GlobalModuleIndexBuilder::writeIndex(llvm::BitstreamWriter &Stream) {
+ for (auto MapEntry : ImportedModuleFiles) {
+ auto *File = MapEntry.first;
+ ImportedModuleFileInfo &Info = MapEntry.second;
+ if (getModuleFileInfo(File).Signature) {
+ if (getModuleFileInfo(File).Signature != Info.StoredSignature)
+ // Verify Signature.
+ return true;
+ } else if (Info.StoredSize != File->getSize() ||
+ Info.StoredModTime != File->getModificationTime())
+ // Verify Size and ModTime.
+ return true;
+ }
+
using namespace llvm;
// Emit the file header.
@@ -756,6 +810,7 @@
}
Stream.ExitBlock();
+ return false;
}
GlobalModuleIndex::ErrorCode
@@ -816,7 +871,8 @@
SmallVector<char, 16> OutputBuffer;
{
llvm::BitstreamWriter OutputStream(OutputBuffer);
- Builder.writeIndex(OutputStream);
+ if (Builder.writeIndex(OutputStream))
+ return EC_IOError;
}
// Write the global index file to a temporary file.
diff --git a/lib/Serialization/Module.cpp b/lib/Serialization/Module.cpp
index 72b0861..5a44d26 100644
--- a/lib/Serialization/Module.cpp
+++ b/lib/Serialization/Module.cpp
@@ -19,28 +19,6 @@
using namespace serialization;
using namespace reader;
-ModuleFile::ModuleFile(ModuleKind Kind, unsigned Generation)
- : Kind(Kind), File(nullptr), Signature(0), DirectlyImported(false),
- Generation(Generation), SizeInBits(0),
- LocalNumSLocEntries(0), SLocEntryBaseID(0),
- SLocEntryBaseOffset(0), SLocEntryOffsets(nullptr),
- LocalNumIdentifiers(0),
- IdentifierOffsets(nullptr), BaseIdentifierID(0),
- IdentifierTableData(nullptr), IdentifierLookupTable(nullptr),
- LocalNumMacros(0), MacroOffsets(nullptr),
- BasePreprocessedEntityID(0),
- PreprocessedEntityOffsets(nullptr), NumPreprocessedEntities(0),
- LocalNumHeaderFileInfos(0),
- HeaderFileInfoTableData(nullptr), HeaderFileInfoTable(nullptr),
- LocalNumSubmodules(0), BaseSubmoduleID(0),
- LocalNumSelectors(0), SelectorOffsets(nullptr), BaseSelectorID(0),
- SelectorLookupTableData(nullptr), SelectorLookupTable(nullptr),
- LocalNumDecls(0), DeclOffsets(nullptr), BaseDeclID(0),
- FileSortedDecls(nullptr), NumFileSortedDecls(0),
- ObjCCategoriesMap(nullptr), LocalNumObjCCategoriesInMap(0),
- LocalNumTypes(0), TypeOffsets(nullptr), BaseTypeIndex(0)
-{}
-
ModuleFile::~ModuleFile() {
delete static_cast<ASTIdentifierLookupTable *>(IdentifierLookupTable);
delete static_cast<HeaderFileInfoLookupTable *>(HeaderFileInfoTable);
diff --git a/lib/Serialization/ModuleManager.cpp b/lib/Serialization/ModuleManager.cpp
index 722b547..e35a44d 100644
--- a/lib/Serialization/ModuleManager.cpp
+++ b/lib/Serialization/ModuleManager.cpp
@@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
#include "clang/Serialization/ModuleManager.h"
+#include "clang/Basic/MemoryBufferCache.h"
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/ModuleMap.h"
@@ -52,6 +53,30 @@
return std::move(InMemoryBuffers[Entry]);
}
+static bool checkSignature(ASTFileSignature Signature,
+ ASTFileSignature ExpectedSignature,
+ std::string &ErrorStr) {
+ if (!ExpectedSignature || Signature == ExpectedSignature)
+ return false;
+
+ ErrorStr =
+ Signature ? "signature mismatch" : "could not read module signature";
+ return true;
+}
+
+static void updateModuleImports(ModuleFile &MF, ModuleFile *ImportedBy,
+ SourceLocation ImportLoc) {
+ if (ImportedBy) {
+ MF.ImportedBy.insert(ImportedBy);
+ ImportedBy->Imports.insert(&MF);
+ } else {
+ if (!MF.DirectlyImported)
+ MF.ImportLoc = ImportLoc;
+
+ MF.DirectlyImported = true;
+ }
+}
+
ModuleManager::AddModuleResult
ModuleManager::addModule(StringRef FileName, ModuleKind Type,
SourceLocation ImportLoc, ModuleFile *ImportedBy,
@@ -84,141 +109,133 @@
}
// Check whether we already loaded this module, before
- ModuleFile *ModuleEntry = Modules[Entry];
- bool NewModule = false;
- if (!ModuleEntry) {
- // Allocate a new module.
- NewModule = true;
- ModuleEntry = new ModuleFile(Type, Generation);
- ModuleEntry->Index = Chain.size();
- ModuleEntry->FileName = FileName.str();
- ModuleEntry->File = Entry;
- ModuleEntry->ImportLoc = ImportLoc;
- ModuleEntry->InputFilesValidationTimestamp = 0;
-
- if (ModuleEntry->Kind == MK_ImplicitModule) {
- std::string TimestampFilename = ModuleEntry->getTimestampFilename();
- vfs::Status Status;
- // A cached stat value would be fine as well.
- if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status))
- ModuleEntry->InputFilesValidationTimestamp =
- llvm::sys::toTimeT(Status.getLastModificationTime());
- }
-
- // Load the contents of the module
- if (std::unique_ptr<llvm::MemoryBuffer> Buffer = lookupBuffer(FileName)) {
- // The buffer was already provided for us.
- ModuleEntry->Buffer = std::move(Buffer);
- } else {
- // Open the AST file.
- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buf(
- (std::error_code()));
- if (FileName == "-") {
- Buf = llvm::MemoryBuffer::getSTDIN();
- } else {
- // Leave the FileEntry open so if it gets read again by another
- // ModuleManager it must be the same underlying file.
- // FIXME: Because FileManager::getFile() doesn't guarantee that it will
- // give us an open file, this may not be 100% reliable.
- Buf = FileMgr.getBufferForFile(ModuleEntry->File,
- /*IsVolatile=*/false,
- /*ShouldClose=*/false);
- }
-
- if (!Buf) {
- ErrorStr = Buf.getError().message();
- delete ModuleEntry;
- return Missing;
- }
-
- ModuleEntry->Buffer = std::move(*Buf);
- }
-
- // Initialize the stream.
- ModuleEntry->Data = PCHContainerRdr.ExtractPCH(*ModuleEntry->Buffer);
- }
-
- if (ExpectedSignature) {
- // If we've not read the control block yet, read the signature eagerly now
- // so that we can check it.
- if (!ModuleEntry->Signature)
- ModuleEntry->Signature = ReadSignature(ModuleEntry->Data);
-
- if (ModuleEntry->Signature != ExpectedSignature) {
- ErrorStr = ModuleEntry->Signature ? "signature mismatch"
- : "could not read module signature";
-
- if (NewModule)
- delete ModuleEntry;
+ if (ModuleFile *ModuleEntry = Modules.lookup(Entry)) {
+ // Check the stored signature.
+ if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr))
return OutOfDate;
- }
- }
- if (ImportedBy) {
- ModuleEntry->ImportedBy.insert(ImportedBy);
- ImportedBy->Imports.insert(ModuleEntry);
- } else {
- if (!ModuleEntry->DirectlyImported)
- ModuleEntry->ImportLoc = ImportLoc;
-
- ModuleEntry->DirectlyImported = true;
- }
-
- Module = ModuleEntry;
-
- if (!NewModule)
+ Module = ModuleEntry;
+ updateModuleImports(*ModuleEntry, ImportedBy, ImportLoc);
return AlreadyLoaded;
+ }
- assert(!Modules[Entry] && "module loaded twice");
- Modules[Entry] = ModuleEntry;
+ // Allocate a new module.
+ auto NewModule = llvm::make_unique<ModuleFile>(Type, Generation);
+ NewModule->Index = Chain.size();
+ NewModule->FileName = FileName.str();
+ NewModule->File = Entry;
+ NewModule->ImportLoc = ImportLoc;
+ NewModule->InputFilesValidationTimestamp = 0;
- Chain.push_back(ModuleEntry);
- if (!ModuleEntry->isModule())
- PCHChain.push_back(ModuleEntry);
+ if (NewModule->Kind == MK_ImplicitModule) {
+ std::string TimestampFilename = NewModule->getTimestampFilename();
+ vfs::Status Status;
+ // A cached stat value would be fine as well.
+ if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status))
+ NewModule->InputFilesValidationTimestamp =
+ llvm::sys::toTimeT(Status.getLastModificationTime());
+ }
+
+ // Load the contents of the module
+ if (std::unique_ptr<llvm::MemoryBuffer> Buffer = lookupBuffer(FileName)) {
+ // The buffer was already provided for us.
+ NewModule->Buffer = &PCMCache->addBuffer(FileName, std::move(Buffer));
+ } else if (llvm::MemoryBuffer *Buffer = PCMCache->lookupBuffer(FileName)) {
+ NewModule->Buffer = Buffer;
+ } else {
+ // Open the AST file.
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buf((std::error_code()));
+ if (FileName == "-") {
+ Buf = llvm::MemoryBuffer::getSTDIN();
+ } else {
+ // Leave the FileEntry open so if it gets read again by another
+ // ModuleManager it must be the same underlying file.
+ // FIXME: Because FileManager::getFile() doesn't guarantee that it will
+ // give us an open file, this may not be 100% reliable.
+ Buf = FileMgr.getBufferForFile(NewModule->File,
+ /*IsVolatile=*/false,
+ /*ShouldClose=*/false);
+ }
+
+ if (!Buf) {
+ ErrorStr = Buf.getError().message();
+ return Missing;
+ }
+
+ NewModule->Buffer = &PCMCache->addBuffer(FileName, std::move(*Buf));
+ }
+
+ // Initialize the stream.
+ NewModule->Data = PCHContainerRdr.ExtractPCH(*NewModule->Buffer);
+
+ // Read the signature eagerly now so that we can check it. Avoid calling
+ // ReadSignature unless there's something to check though.
+ if (ExpectedSignature && checkSignature(ReadSignature(NewModule->Data),
+ ExpectedSignature, ErrorStr)) {
+ // Try to remove the buffer. If it can't be removed, then it was already
+ // validated by this process.
+ if (!PCMCache->tryToRemoveBuffer(NewModule->FileName))
+ FileMgr.invalidateCache(NewModule->File);
+ return OutOfDate;
+ }
+
+ // We're keeping this module. Store it everywhere.
+ Module = Modules[Entry] = NewModule.get();
+
+ updateModuleImports(*NewModule, ImportedBy, ImportLoc);
+
+ if (!NewModule->isModule())
+ PCHChain.push_back(NewModule.get());
if (!ImportedBy)
- Roots.push_back(ModuleEntry);
+ Roots.push_back(NewModule.get());
+ Chain.push_back(std::move(NewModule));
return NewlyLoaded;
}
void ModuleManager::removeModules(
- ModuleIterator first, ModuleIterator last,
+ ModuleIterator First,
llvm::SmallPtrSetImpl<ModuleFile *> &LoadedSuccessfully,
ModuleMap *modMap) {
- if (first == last)
+ auto Last = end();
+ if (First == Last)
return;
+
// Explicitly clear VisitOrder since we might not notice it is stale.
VisitOrder.clear();
// Collect the set of module file pointers that we'll be removing.
- llvm::SmallPtrSet<ModuleFile *, 4> victimSet(first, last);
+ llvm::SmallPtrSet<ModuleFile *, 4> victimSet(
+ (llvm::pointer_iterator<ModuleIterator>(First)),
+ (llvm::pointer_iterator<ModuleIterator>(Last)));
auto IsVictim = [&](ModuleFile *MF) {
return victimSet.count(MF);
};
// Remove any references to the now-destroyed modules.
- for (unsigned i = 0, n = Chain.size(); i != n; ++i) {
- Chain[i]->ImportedBy.remove_if(IsVictim);
+ for (auto I = begin(); I != First; ++I) {
+ I->Imports.remove_if(IsVictim);
+ I->ImportedBy.remove_if(IsVictim);
}
Roots.erase(std::remove_if(Roots.begin(), Roots.end(), IsVictim),
Roots.end());
// Remove the modules from the PCH chain.
- for (auto I = first; I != last; ++I) {
- if (!(*I)->isModule()) {
- PCHChain.erase(std::find(PCHChain.begin(), PCHChain.end(), *I),
+ for (auto I = First; I != Last; ++I) {
+ if (!I->isModule()) {
+ PCHChain.erase(std::find(PCHChain.begin(), PCHChain.end(), &*I),
PCHChain.end());
break;
}
}
// Delete the modules and erase them from the various structures.
- for (ModuleIterator victim = first; victim != last; ++victim) {
- Modules.erase((*victim)->File);
+ for (ModuleIterator victim = First; victim != Last; ++victim) {
+ Modules.erase(victim->File);
if (modMap) {
- StringRef ModuleName = (*victim)->ModuleName;
+ StringRef ModuleName = victim->ModuleName;
if (Module *mod = modMap->findModule(ModuleName)) {
mod->setASTFile(nullptr);
}
@@ -227,14 +244,17 @@
// Files that didn't make it through ReadASTCore successfully will be
// rebuilt (or there was an error). Invalidate them so that we can load the
// new files that will be renamed over the old ones.
- if (LoadedSuccessfully.count(*victim) == 0)
- FileMgr.invalidateCache((*victim)->File);
-
- delete *victim;
+ //
+ // The PCMCache tracks whether the module was succesfully loaded in another
+ // thread/context; in that case, it won't need to be rebuilt (and we can't
+ // safely invalidate it anyway).
+ if (LoadedSuccessfully.count(&*victim) == 0 &&
+ !PCMCache->tryToRemoveBuffer(victim->FileName))
+ FileMgr.invalidateCache(victim->File);
}
- // Remove the modules from the chain.
- Chain.erase(first, last);
+ // Delete the modules.
+ Chain.erase(Chain.begin() + (First - begin()), Chain.end());
}
void
@@ -274,11 +294,9 @@
// Notify the global module index about all of the modules we've already
// loaded.
- for (unsigned I = 0, N = Chain.size(); I != N; ++I) {
- if (!GlobalIndex->loadedModuleFile(Chain[I])) {
- ModulesInCommonWithGlobalIndex.push_back(Chain[I]);
- }
- }
+ for (ModuleFile &M : *this)
+ if (!GlobalIndex->loadedModuleFile(&M))
+ ModulesInCommonWithGlobalIndex.push_back(&M);
}
void ModuleManager::moduleFileAccepted(ModuleFile *MF) {
@@ -288,16 +306,12 @@
ModulesInCommonWithGlobalIndex.push_back(MF);
}
-ModuleManager::ModuleManager(FileManager &FileMgr,
+ModuleManager::ModuleManager(FileManager &FileMgr, MemoryBufferCache &PCMCache,
const PCHContainerReader &PCHContainerRdr)
- : FileMgr(FileMgr), PCHContainerRdr(PCHContainerRdr), GlobalIndex(),
- FirstVisitState(nullptr) {}
+ : FileMgr(FileMgr), PCMCache(&PCMCache), PCHContainerRdr(PCHContainerRdr),
+ GlobalIndex(), FirstVisitState(nullptr) {}
-ModuleManager::~ModuleManager() {
- for (unsigned i = 0, e = Chain.size(); i != e; ++i)
- delete Chain[e - i - 1];
- delete FirstVisitState;
-}
+ModuleManager::~ModuleManager() { delete FirstVisitState; }
void ModuleManager::visit(llvm::function_ref<bool(ModuleFile &M)> Visitor,
llvm::SmallPtrSetImpl<ModuleFile *> *ModuleFilesHit) {
@@ -314,11 +328,11 @@
Queue.reserve(N);
llvm::SmallVector<unsigned, 4> UnusedIncomingEdges;
UnusedIncomingEdges.resize(size());
- for (ModuleFile *M : llvm::reverse(*this)) {
- unsigned Size = M->ImportedBy.size();
- UnusedIncomingEdges[M->Index] = Size;
+ for (ModuleFile &M : llvm::reverse(*this)) {
+ unsigned Size = M.ImportedBy.size();
+ UnusedIncomingEdges[M.Index] = Size;
if (!Size)
- Queue.push_back(M);
+ Queue.push_back(&M);
}
// Traverse the graph, making sure to visit a module before visiting any
@@ -433,7 +447,7 @@
struct GraphTraits<ModuleManager> {
typedef ModuleFile *NodeRef;
typedef llvm::SetVector<ModuleFile *>::const_iterator ChildIteratorType;
- typedef ModuleManager::ModuleConstIterator nodes_iterator;
+ typedef pointer_iterator<ModuleManager::ModuleConstIterator> nodes_iterator;
static ChildIteratorType child_begin(NodeRef Node) {
return Node->Imports.begin();
@@ -444,11 +458,11 @@
}
static nodes_iterator nodes_begin(const ModuleManager &Manager) {
- return Manager.begin();
+ return nodes_iterator(Manager.begin());
}
static nodes_iterator nodes_end(const ModuleManager &Manager) {
- return Manager.end();
+ return nodes_iterator(Manager.end());
}
};
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 238032c8..32e3ce9 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -68,6 +68,7 @@
const InvalidatedSymbols *,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
const CallEvent *Call) const;
typedef void (CStringChecker::*FnCheck)(CheckerContext &,
@@ -1943,8 +1944,12 @@
// Overwrite the search string pointer. The new value is either an address
// further along in the same string, or NULL if there are no more tokens.
State = State->bindLoc(*SearchStrLoc,
- SVB.conjureSymbolVal(getTag(), CE, LCtx, CharPtrTy,
- C.blockCount()));
+ SVB.conjureSymbolVal(getTag(),
+ CE,
+ LCtx,
+ CharPtrTy,
+ C.blockCount()),
+ LCtx);
} else {
assert(SearchStrVal.isUnknown());
// Conjure a symbolic value. It's the best we can do.
@@ -2116,6 +2121,7 @@
const InvalidatedSymbols *,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
const CallEvent *Call) const {
CStringLengthTy Entries = state->get<CStringLength>();
if (Entries.isEmpty())
diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
index 3db1994..391b843 100644
--- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
@@ -36,25 +36,24 @@
AnalysisDeclContext* AC;
/// Check if two expressions refer to the same declaration.
- inline bool sameDecl(const Expr *A1, const Expr *A2) {
- if (const DeclRefExpr *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts()))
- if (const DeclRefExpr *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts()))
+ bool sameDecl(const Expr *A1, const Expr *A2) {
+ if (const auto *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts()))
+ if (const auto *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts()))
return D1->getDecl() == D2->getDecl();
return false;
}
/// Check if the expression E is a sizeof(WithArg).
- inline bool isSizeof(const Expr *E, const Expr *WithArg) {
- if (const UnaryExprOrTypeTraitExpr *UE =
- dyn_cast<UnaryExprOrTypeTraitExpr>(E))
- if (UE->getKind() == UETT_SizeOf)
+ bool isSizeof(const Expr *E, const Expr *WithArg) {
+ if (const auto *UE = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
+ if (UE->getKind() == UETT_SizeOf && !UE->isArgumentType())
return sameDecl(UE->getArgumentExpr(), WithArg);
return false;
}
/// Check if the expression E is a strlen(WithArg).
- inline bool isStrlen(const Expr *E, const Expr *WithArg) {
- if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ bool isStrlen(const Expr *E, const Expr *WithArg) {
+ if (const auto *CE = dyn_cast<CallExpr>(E)) {
const FunctionDecl *FD = CE->getDirectCallee();
if (!FD)
return false;
@@ -65,14 +64,14 @@
}
/// Check if the expression is an integer literal with value 1.
- inline bool isOne(const Expr *E) {
- if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(E))
+ bool isOne(const Expr *E) {
+ if (const auto *IL = dyn_cast<IntegerLiteral>(E))
return (IL->getValue().isIntN(1));
return false;
}
- inline StringRef getPrintableName(const Expr *E) {
- if (const DeclRefExpr *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
+ StringRef getPrintableName(const Expr *E) {
+ if (const auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
return D->getDecl()->getName();
return StringRef();
}
@@ -82,8 +81,8 @@
bool containsBadStrncatPattern(const CallExpr *CE);
public:
- WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac)
- : Checker(checker), BR(br), AC(ac) {}
+ WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC)
+ : Checker(Checker), BR(BR), AC(AC) {}
// Statement visitor methods.
void VisitChildren(Stmt *S);
@@ -108,8 +107,7 @@
const Expr *LenArg = CE->getArg(2);
// Identify wrong size expressions, which are commonly used instead.
- if (const BinaryOperator *BE =
- dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) {
+ if (const auto *BE = dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) {
// - sizeof(dst) - strlen(dst)
if (BE->getOpcode() == BO_Sub) {
const Expr *L = BE->getLHS();
diff --git a/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
index 7631322..668e772 100644
--- a/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
@@ -51,9 +51,9 @@
State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
auto Param = SVB.makeLoc(State->getRegion(MD->getParamDecl(0), LCtx));
auto ParamVal = State->getSVal(Param);
- ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal);
+ ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal, LCtx);
C.addTransition(SelfAssignState);
- ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal);
+ ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal, LCtx);
C.addTransition(NonSelfAssignState);
}
diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
index 86764c9..95b6c4d 100644
--- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
@@ -231,14 +231,6 @@
/// check::LiveSymbols
void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const {}
- /// \brief Called to determine if the checker currently needs to know if when
- /// contents of any regions change.
- ///
- /// Since it is not necessarily cheap to compute which regions are being
- /// changed, this allows the analyzer core to skip the more expensive
- /// #checkRegionChanges when no checkers are tracking any state.
- bool wantsRegionChangeUpdate(ProgramStateRef St) const { return true; }
-
/// \brief Called when the contents of one or more regions change.
///
/// This can occur in many different ways: an explicit bind, a blanket
@@ -255,18 +247,18 @@
/// by this change. For a simple bind, this list will be the same as
/// \p ExplicitRegions, since a bind does not affect the contents of
/// anything accessible through the base region.
+ /// \param LCtx LocationContext that is useful for getting various contextual
+ /// info, like callstack, CFG etc.
/// \param Call The opaque call triggering this invalidation. Will be 0 if the
/// change was not triggered by a call.
///
- /// Note that this callback will not be invoked unless
- /// #wantsRegionChangeUpdate returns \c true.
- ///
/// check::RegionChanges
ProgramStateRef
checkRegionChanges(ProgramStateRef State,
const InvalidatedSymbols *Invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
const CallEvent *Call) const {
return State;
}
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index 0891ea8..26fd83f 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -626,7 +626,7 @@
: public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> {
public:
IsObjCTypeParamDependentTypeVisitor() : Result(false) {}
- bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) {
+ bool VisitTypedefType(const TypedefType *Type) {
if (isa<ObjCTypeParamDecl>(Type->getDecl())) {
Result = true;
return false;
diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index 2d5cb60..fe48a94 100644
--- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -13,6 +13,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/ScopedPrinter.h"
using namespace clang;
using namespace ento;
@@ -269,7 +270,7 @@
unsigned NumTimesReached = Item.second.NumTimesReached;
ExplodedNode *N = Item.second.ExampleNode;
- reportBug(std::to_string(NumTimesReached), BR, N);
+ reportBug(llvm::to_string(NumTimesReached), BR, N);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index f1aa163..f8473db 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -28,7 +28,8 @@
namespace {
class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
check::PostStmt<CallExpr>,
- check::DeadSymbols> {
+ check::DeadSymbols,
+ eval::Assume> {
mutable std::unique_ptr<BugType> BT;
public:
@@ -57,6 +58,10 @@
void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+ ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
+ bool Assumption) const;
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const;
private:
typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
@@ -106,19 +111,6 @@
std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport(
const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const;
- /// Check if RetSym evaluates to an error value in the current state.
- bool definitelyReturnedError(SymbolRef RetSym,
- ProgramStateRef State,
- SValBuilder &Builder,
- bool noError = false) const;
-
- /// Check if RetSym evaluates to a NoErr value in the current state.
- bool definitelyDidnotReturnError(SymbolRef RetSym,
- ProgramStateRef State,
- SValBuilder &Builder) const {
- return definitelyReturnedError(RetSym, State, Builder, true);
- }
-
/// Mark an AllocationPair interesting for diagnostic reporting.
void markInteresting(BugReport *R, const AllocationPair &AP) const {
R->markInteresting(AP.first);
@@ -221,24 +213,6 @@
return nullptr;
}
-// When checking for error code, we need to consider the following cases:
-// 1) noErr / [0]
-// 2) someErr / [1, inf]
-// 3) unknown
-// If noError, returns true iff (1).
-// If !noError, returns true iff (2).
-bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym,
- ProgramStateRef State,
- SValBuilder &Builder,
- bool noError) const {
- DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr,
- Builder.getSymbolManager().getType(RetSym));
- DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal,
- nonloc::SymbolVal(RetSym));
- ProgramStateRef ErrState = State->assume(NoErr, noError);
- return ErrState == State;
-}
-
// Report deallocator mismatch. Remove the region from tracking - reporting a
// missing free error after this one is redundant.
void MacOSKeychainAPIChecker::
@@ -289,27 +263,25 @@
const Expr *ArgExpr = CE->getArg(paramIdx);
if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
if (const AllocationState *AS = State->get<AllocatedData>(V)) {
- if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
- // Remove the value from the state. The new symbol will be added for
- // tracking when the second allocator is processed in checkPostStmt().
- State = State->remove<AllocatedData>(V);
- ExplodedNode *N = C.generateNonFatalErrorNode(State);
- if (!N)
- return;
- initBugType();
- SmallString<128> sbuf;
- llvm::raw_svector_ostream os(sbuf);
- unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
- os << "Allocated data should be released before another call to "
- << "the allocator: missing a call to '"
- << FunctionsToTrack[DIdx].Name
- << "'.";
- auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
- Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
- Report->addRange(ArgExpr->getSourceRange());
- Report->markInteresting(AS->Region);
- C.emitReport(std::move(Report));
- }
+ // Remove the value from the state. The new symbol will be added for
+ // tracking when the second allocator is processed in checkPostStmt().
+ State = State->remove<AllocatedData>(V);
+ ExplodedNode *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ initBugType();
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
+ os << "Allocated data should be released before another call to "
+ << "the allocator: missing a call to '"
+ << FunctionsToTrack[DIdx].Name
+ << "'.";
+ auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
+ Report->addRange(ArgExpr->getSourceRange());
+ Report->markInteresting(AS->Region);
+ C.emitReport(std::move(Report));
}
return;
}
@@ -344,13 +316,12 @@
// Is the argument to the call being tracked?
const AllocationState *AS = State->get<AllocatedData>(ArgSM);
- if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) {
+ if (!AS)
return;
- }
- // If trying to free data which has not been allocated yet, report as a bug.
- // TODO: We might want a more precise diagnostic for double free
+
+ // TODO: We might want to report double free here.
// (that would involve tracking all the freed symbols in the checker state).
- if (!AS || RegionArgIsBad) {
+ if (RegionArgIsBad) {
// It is possible that this is a false positive - the argument might
// have entered as an enclosing function parameter.
if (isEnclosingFunctionParam(ArgExpr))
@@ -418,23 +389,6 @@
return;
}
- // If the buffer can be null and the return status can be an error,
- // report a bad call to free.
- if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) &&
- !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) {
- ExplodedNode *N = C.generateNonFatalErrorNode(State);
- if (!N)
- return;
- initBugType();
- auto Report = llvm::make_unique<BugReport>(
- *BT, "Only call free if a valid (non-NULL) buffer was returned.", N);
- Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(ArgSM));
- Report->addRange(ArgExpr->getSourceRange());
- Report->markInteresting(AS->Region);
- C.emitReport(std::move(Report));
- return;
- }
-
C.addTransition(State);
}
@@ -540,27 +494,63 @@
return Report;
}
+/// If the return symbol is assumed to be error, remove the allocated info
+/// from consideration.
+ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State,
+ SVal Cond,
+ bool Assumption) const {
+ AllocatedDataTy AMap = State->get<AllocatedData>();
+ if (AMap.isEmpty())
+ return State;
+
+ auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
+ if (!CondBSE)
+ return State;
+ BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
+ if (OpCode != BO_EQ && OpCode != BO_NE)
+ return State;
+
+ // Match for a restricted set of patterns for cmparison of error codes.
+ // Note, the comparisons of type '0 == st' are transformed into SymIntExpr.
+ SymbolRef ReturnSymbol = nullptr;
+ if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
+ const llvm::APInt &RHS = SIE->getRHS();
+ bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
+ (OpCode == BO_NE && RHS == NoErr);
+ if (!Assumption)
+ ErrorIsReturned = !ErrorIsReturned;
+ if (ErrorIsReturned)
+ ReturnSymbol = SIE->getLHS();
+ }
+
+ if (ReturnSymbol)
+ for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
+ if (ReturnSymbol == I->second.Region)
+ State = State->remove<AllocatedData>(I->first);
+ }
+
+ return State;
+}
+
void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- AllocatedDataTy ASet = State->get<AllocatedData>();
- if (ASet.isEmpty())
+ AllocatedDataTy AMap = State->get<AllocatedData>();
+ if (AMap.isEmpty())
return;
bool Changed = false;
AllocationPairVec Errors;
- for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) {
- if (SR.isLive(I->first))
+ for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
+ if (!SR.isDead(I->first))
continue;
Changed = true;
State = State->remove<AllocatedData>(I->first);
- // If the allocated symbol is null or if the allocation call might have
- // returned an error, do not report.
+ // If the allocated symbol is null do not report.
ConstraintManager &CMgr = State->getConstraintManager();
ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey());
- if (AllocFailed.isConstrainedTrue() ||
- definitelyReturnedError(I->second.Region, State, C.getSValBuilder()))
+ if (AllocFailed.isConstrainedTrue())
continue;
Errors.push_back(std::make_pair(I->first, &I->second));
}
@@ -612,6 +602,22 @@
"Data is allocated here.");
}
+void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
+ ProgramStateRef State,
+ const char *NL,
+ const char *Sep) const {
+
+ AllocatedDataTy AMap = State->get<AllocatedData>();
+
+ if (!AMap.isEmpty()) {
+ Out << Sep << "KeychainAPIChecker :" << NL;
+ for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
+ I.getKey()->dumpToStream(Out);
+ }
+ }
+}
+
+
void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
mgr.registerChecker<MacOSKeychainAPIChecker>();
}
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 8e839a1..504d470 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1154,7 +1154,7 @@
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
// Fill the region with the initialization value.
- State = State->bindDefault(RetVal, Init);
+ State = State->bindDefault(RetVal, Init, LCtx);
// Set the region's extent equal to the Size parameter.
const SymbolicRegion *R =
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index eb101e1..3f6ae62 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -2661,6 +2661,7 @@
const InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext* LCtx,
const CallEvent *Call) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
@@ -3647,7 +3648,7 @@
// same state.
SVal StoredVal = state->getSVal(regionLoc->getRegion());
if (StoredVal != val)
- escapes = (state == (state->bindLoc(*regionLoc, val)));
+ escapes = (state == (state->bindLoc(*regionLoc, val, C.getLocationContext())));
}
if (!escapes) {
// Case 4: We do not currently model what happens when a symbol is
@@ -3714,10 +3715,11 @@
ProgramStateRef
RetainCountChecker::checkRegionChanges(ProgramStateRef state,
- const InvalidatedSymbols *invalidated,
- ArrayRef<const MemRegion *> ExplicitRegions,
- ArrayRef<const MemRegion *> Regions,
- const CallEvent *Call) const {
+ const InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) const {
if (!invalidated)
return state;
diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index 15e8ea3..b47762b 100644
--- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -179,7 +179,8 @@
}
// Get the callee.
- const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee());
+ const CXXMethodDecl *MD =
+ dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
!MD->getParent()->hasAttr<FinalAttr>())
ReportVirtualCall(CE, MD->isPure());
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index 420e2a6..ef824b8 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -212,9 +212,12 @@
bool CallEvent::isCalled(const CallDescription &CD) const {
assert(getKind() != CE_ObjCMessage && "Obj-C methods are not supported");
- if (!CD.II)
+ if (!CD.IsLookupDone) {
+ CD.IsLookupDone = true;
CD.II = &getState()->getStateManager().getContext().Idents.get(CD.FuncName);
- if (getCalleeIdentifier() != CD.II)
+ }
+ const IdentifierInfo *II = getCalleeIdentifier();
+ if (!II || II != CD.II)
return false;
return (CD.RequiredArgs == CallDescription::NoArgRequirement ||
CD.RequiredArgs == getNumArgs());
@@ -896,6 +899,38 @@
llvm_unreachable("The while loop should always terminate.");
}
+static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) {
+ if (!MD)
+ return MD;
+
+ // Find the redeclaration that defines the method.
+ if (!MD->hasBody()) {
+ for (auto I : MD->redecls())
+ if (I->hasBody())
+ MD = cast<ObjCMethodDecl>(I);
+ }
+ return MD;
+}
+
+static bool isCallToSelfClass(const ObjCMessageExpr *ME) {
+ const Expr* InstRec = ME->getInstanceReceiver();
+ if (!InstRec)
+ return false;
+ const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts());
+
+ // Check that receiver is called 'self'.
+ if (!InstRecIg || !InstRecIg->getFoundDecl() ||
+ !InstRecIg->getFoundDecl()->getName().equals("self"))
+ return false;
+
+ // Check that the method name is 'class'.
+ if (ME->getSelector().getNumArgs() != 0 ||
+ !ME->getSelector().getNameForSlot(0).equals("class"))
+ return false;
+
+ return true;
+}
+
RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
const ObjCMessageExpr *E = getOriginExpr();
assert(E);
@@ -910,6 +945,7 @@
const MemRegion *Receiver = nullptr;
if (!SupersType.isNull()) {
+ // The receiver is guaranteed to be 'super' in this case.
// Super always means the type of immediate predecessor to the method
// where the call occurs.
ReceiverT = cast<ObjCObjectPointerType>(SupersType);
@@ -921,7 +957,7 @@
DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver);
QualType DynType = DTI.getType();
CanBeSubClassed = DTI.canBeASubClass();
- ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType);
+ ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
if (ReceiverT && CanBeSubClassed)
if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
@@ -929,7 +965,32 @@
CanBeSubClassed = false;
}
- // Lookup the method implementation.
+ // Handle special cases of '[self classMethod]' and
+ // '[[self class] classMethod]', which are treated by the compiler as
+ // instance (not class) messages. We will statically dispatch to those.
+ if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) {
+ // For [self classMethod], return the compiler visible declaration.
+ if (PT->getObjectType()->isObjCClass() &&
+ Receiver == getSelfSVal().getAsRegion())
+ return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+
+ // Similarly, handle [[self class] classMethod].
+ // TODO: We are currently doing a syntactic match for this pattern with is
+ // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m
+ // shows. A better way would be to associate the meta type with the symbol
+ // using the dynamic type info tracking and use it here. We can add a new
+ // SVal for ObjC 'Class' values that know what interface declaration they
+ // come from. Then 'self' in a class method would be filled in with
+ // something meaningful in ObjCMethodCall::getReceiverSVal() and we could
+ // do proper dynamic dispatch for class methods just like we do for
+ // instance methods now.
+ if (E->getInstanceReceiver())
+ if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver()))
+ if (isCallToSelfClass(M))
+ return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+ }
+
+ // Lookup the instance method implementation.
if (ReceiverT)
if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
// Repeatedly calling lookupPrivateMethod() is expensive, especially
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 79e204c..49f3ede 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -521,17 +521,19 @@
/// \brief Run checkers for region changes.
ProgramStateRef
CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
- const InvalidatedSymbols *invalidated,
- ArrayRef<const MemRegion *> ExplicitRegions,
- ArrayRef<const MemRegion *> Regions,
- const CallEvent *Call) {
+ const InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) {
for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) {
// If any checker declares the state infeasible (or if it starts that way),
// bail out.
if (!state)
return nullptr;
state = RegionChangesCheckers[i](state, invalidated,
- ExplicitRegions, Regions, Call);
+ ExplicitRegions, Regions,
+ LCtx, Call);
}
return state;
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index d563f8e..7d0c8b4 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -254,7 +254,7 @@
break;
case SubobjectAdjustment::MemberPointerAdjustment:
// FIXME: Unimplemented.
- State->bindDefault(Reg, UnknownVal());
+ State->bindDefault(Reg, UnknownVal(), LC);
return State;
}
}
@@ -265,7 +265,7 @@
currBldrCtx->blockCount());
// Bind the value of the expression to the sub-object region, and then bind
// the sub-object region to our expression.
- State = State->bindLoc(Reg, V);
+ State = State->bindLoc(Reg, V, LC);
State = State->BindExpr(Result, LC, Reg);
return State;
}
@@ -286,9 +286,11 @@
const InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> Explicits,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
const CallEvent *Call) {
return getCheckerManager().runCheckersForRegionChanges(state, invalidated,
- Explicits, Regions, Call);
+ Explicits, Regions,
+ LCtx, Call);
}
void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State,
@@ -2165,7 +2167,9 @@
// (3) We are binding to a MemRegion with stack storage that the store
// does not understand.
ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State,
- SVal Loc, SVal Val) {
+ SVal Loc,
+ SVal Val,
+ const LocationContext *LCtx) {
// Are we storing to something that causes the value to "escape"?
bool escapes = true;
@@ -2181,7 +2185,7 @@
// same state.
SVal StoredVal = State->getSVal(regionLoc->getRegion());
if (StoredVal != Val)
- escapes = (State == (State->bindLoc(*regionLoc, Val)));
+ escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx)));
}
}
@@ -2278,7 +2282,7 @@
const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/nullptr,
/*tag*/nullptr);
ProgramStateRef state = Pred->getState();
- state = processPointerEscapedOnBind(state, location, Val);
+ state = processPointerEscapedOnBind(state, location, Val, LC);
Bldr.generateNode(L, state, Pred);
return;
}
@@ -2288,13 +2292,13 @@
ExplodedNode *PredI = *I;
ProgramStateRef state = PredI->getState();
- state = processPointerEscapedOnBind(state, location, Val);
+ state = processPointerEscapedOnBind(state, location, Val, LC);
// When binding the value, pass on the hint that this is a initialization.
// For initializations, we do not need to inform clients of region
// changes.
state = state->bindLoc(location.castAs<Loc>(),
- Val, /* notifyChanges = */ !atDeclInit);
+ Val, LC, /* notifyChanges = */ !atDeclInit);
const MemRegion *LocReg = nullptr;
if (Optional<loc::MemRegionVal> LocRegVal =
@@ -2520,7 +2524,7 @@
assert (!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef.
if (Optional<Loc> LV = X.getAs<Loc>())
- state = state->bindLoc(*LV, UnknownVal());
+ state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext());
}
Bldr.generateNode(A, Pred, state);
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 89fab1d..ea4ff91 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -227,12 +227,13 @@
if (capturedR != originalR) {
SVal originalV;
+ const LocationContext *LCtx = Pred->getLocationContext();
if (copyExpr) {
- originalV = State->getSVal(copyExpr, Pred->getLocationContext());
+ originalV = State->getSVal(copyExpr, LCtx);
} else {
originalV = State->getSVal(loc::MemRegionVal(originalR));
}
- State = State->bindLoc(loc::MemRegionVal(capturedR), originalV);
+ State = State->bindLoc(loc::MemRegionVal(capturedR), originalV, LCtx);
}
}
}
@@ -534,7 +535,7 @@
} else {
assert(isa<InitListExpr>(Init));
Loc CLLoc = State->getLValue(CL, LCtx);
- State = State->bindLoc(CLLoc, V);
+ State = State->bindLoc(CLLoc, V, LCtx);
if (CL->isGLValue())
V = CLLoc;
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 7e9b203..ca2e242 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -317,7 +317,7 @@
// actually make things worse. Placement new makes this tricky as well,
// since it's then possible to be initializing one part of a multi-
// dimensional array.
- State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal);
+ State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal, LCtx);
Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
ProgramPoint::PreStmtKind);
}
@@ -572,7 +572,7 @@
SVal V = svalBuilder.conjureSymbolVal(CS, LCtx, VD->getType(),
currBldrCtx->blockCount());
ProgramStateRef state = Pred->getState();
- state = state->bindLoc(state->getLValue(VD, LCtx), V);
+ state = state->bindLoc(state->getLValue(VD, LCtx), V, LCtx);
StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
Bldr.generateNode(CS, Pred, state);
@@ -627,7 +627,7 @@
InitVal = State->getSVal(SizeExpr, LocCtxt);
}
- State = State->bindLoc(FieldLoc, InitVal);
+ State = State->bindLoc(FieldLoc, InitVal, LocCtxt);
}
// Decay the Loc into an RValue, because there might be a
diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index 92c5fe6..f5e64f4 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -115,11 +115,11 @@
SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T,
currBldrCtx->blockCount());
SVal V = svalBuilder.makeLoc(Sym);
- hasElems = hasElems->bindLoc(elementV, V);
+ hasElems = hasElems->bindLoc(elementV, V, LCtx);
// Bind the location to 'nil' on the false branch.
SVal nilV = svalBuilder.makeIntVal(0, T);
- noElems = noElems->bindLoc(elementV, nilV);
+ noElems = noElems->bindLoc(elementV, nilV, LCtx);
}
// Create the new nodes.
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index 03ace35..31556c7 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -111,24 +111,29 @@
return ConstraintMgr->removeDeadBindings(Result, SymReaper);
}
-ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, bool notifyChanges) const {
+ProgramStateRef ProgramState::bindLoc(Loc LV,
+ SVal V,
+ const LocationContext *LCtx,
+ bool notifyChanges) const {
ProgramStateManager &Mgr = getStateManager();
ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
LV, V));
const MemRegion *MR = LV.getAsRegion();
if (MR && Mgr.getOwningEngine() && notifyChanges)
- return Mgr.getOwningEngine()->processRegionChange(newState, MR);
+ return Mgr.getOwningEngine()->processRegionChange(newState, MR, LCtx);
return newState;
}
-ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const {
+ProgramStateRef ProgramState::bindDefault(SVal loc,
+ SVal V,
+ const LocationContext *LCtx) const {
ProgramStateManager &Mgr = getStateManager();
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V);
ProgramStateRef new_state = makeWithStore(newStore);
return Mgr.getOwningEngine() ?
- Mgr.getOwningEngine()->processRegionChange(new_state, R) :
+ Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx) :
new_state;
}
@@ -202,7 +207,7 @@
}
return Eng->processRegionChanges(newState, IS, TopLevelInvalidated,
- Invalidated, Call);
+ Invalidated, LCtx, Call);
}
const StoreRef &newStore =
diff --git a/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes b/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes
new file mode 100644
index 0000000..ccdc4e1
--- /dev/null
+++ b/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes
@@ -0,0 +1,8 @@
+Name: SomeOtherKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "methodB"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
diff --git a/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes b/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes
new file mode 100644
index 0000000..d547317
--- /dev/null
+++ b/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes
@@ -0,0 +1,4 @@
+Name: SomeBrokenLib
+Functions:
+ - Name: do_something_with_pointers
+ Nu llabilityOfRet: O
diff --git a/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h b/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h
new file mode 100644
index 0000000..b09c6f6
--- /dev/null
+++ b/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h
@@ -0,0 +1,6 @@
+#ifndef SOME_BROKEN_LIB_H
+#define SOME_BROKEN_LIB_H
+
+void do_something_with_pointers(int *ptr1, int *ptr2);
+
+#endif // SOME_BROKEN_LIB_H
diff --git a/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes b/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes
new file mode 100644
index 0000000..33eeaaa
--- /dev/null
+++ b/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes
@@ -0,0 +1,7 @@
+Name: SomeBrokenLib
+Functions:
+ - Name: do_something_with_pointers
+ NullabilityOfRet: O
+ - Name: do_something_with_pointers
+ NullabilityOfRet: O
+
diff --git a/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h b/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h
new file mode 100644
index 0000000..b09c6f6
--- /dev/null
+++ b/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h
@@ -0,0 +1,6 @@
+#ifndef SOME_BROKEN_LIB_H
+#define SOME_BROKEN_LIB_H
+
+void do_something_with_pointers(int *ptr1, int *ptr2);
+
+#endif // SOME_BROKEN_LIB_H
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
new file mode 100644
index 0000000..817af12
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
@@ -0,0 +1,74 @@
+Name: SomeKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "transform:"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
+ - Selector: "transform:integer:"
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ Properties:
+ - Name: intValue
+ PropertyKind: Instance
+ Availability: none
+ AvailabilityMsg: "wouldn't work anyway"
+ - Name: nonnullAInstance
+ PropertyKind: Instance
+ Nullability: N
+ - Name: nonnullAClass
+ PropertyKind: Class
+ Nullability: N
+ - Name: nonnullABoth
+ Nullability: N
+ - Name: B
+ Availability: none
+ AvailabilityMsg: "just don't"
+ - Name: C
+ Methods:
+ - Selector: "initWithA:"
+ MethodKind: Instance
+ DesignatedInit: true
+ - Name: OverriddenTypes
+ Methods:
+ - Selector: "methodToMangle:second:"
+ MethodKind: Instance
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'SOMEKIT_DOUBLE *'
+ - Position: 1
+ Type: 'float *'
+ Properties:
+ - Name: intPropertyToMangle
+ PropertyKind: Instance
+ Type: 'double *'
+Functions:
+ - Name: global_int_fun
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'double *'
+ - Position: 1
+ Type: 'float *'
+Globals:
+ - Name: global_int_ptr
+ Type: 'double *'
+SwiftVersions:
+ - Version: 3.0
+ Classes:
+ - Name: A
+ Methods:
+ - Selector: "transform:integer:"
+ MethodKind: Instance
+ NullabilityOfRet: O
+ Nullability: [ O, S ]
+ Properties:
+ - Name: explicitNonnullInstance
+ PropertyKind: Instance
+ Nullability: O
+ - Name: explicitNullableInstance
+ PropertyKind: Instance
+ Nullability: N
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes
new file mode 100644
index 0000000..28ede9d
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes
@@ -0,0 +1,15 @@
+Name: SomeKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "privateTransform:input:"
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ Properties:
+ - Name: internalProperty
+ Nullability: N
+Protocols:
+ - Name: InternalProtocol
+ Availability: none
+ AvailabilityMsg: "not for you"
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes
new file mode 100644
index 0000000..ff88fdb
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes
@@ -0,0 +1,98 @@
+Name: SomeKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "transform:"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
+ - Selector: "transform:integer:"
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ - Selector: "implicitGetOnlyInstance"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "getter gone"
+ - Selector: "implicitGetOnlyClass"
+ MethodKind: Class
+ Availability: none
+ AvailabilityMsg: "getter gone"
+ - Selector: "implicitGetSetInstance"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "getter gone"
+ - Selector: "implicitGetSetClass"
+ MethodKind: Class
+ Availability: none
+ AvailabilityMsg: "getter gone"
+ - Selector: "setImplicitGetSetInstance:"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "setter gone"
+ - Selector: "setImplicitGetSetClass:"
+ MethodKind: Class
+ Availability: none
+ AvailabilityMsg: "setter gone"
+ Properties:
+ - Name: intValue
+ PropertyKind: Instance
+ Availability: none
+ AvailabilityMsg: "wouldn't work anyway"
+ - Name: nonnullAInstance
+ PropertyKind: Instance
+ Nullability: N
+ - Name: nonnullAClass
+ PropertyKind: Class
+ Nullability: N
+ - Name: nonnullABoth
+ Nullability: N
+ - Name: B
+ Availability: none
+ AvailabilityMsg: "just don't"
+ - Name: C
+ Methods:
+ - Selector: "initWithA:"
+ MethodKind: Instance
+ DesignatedInit: true
+ - Name: OverriddenTypes
+ Methods:
+ - Selector: "methodToMangle:second:"
+ MethodKind: Instance
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'SOMEKIT_DOUBLE *'
+ - Position: 1
+ Type: 'float *'
+ Properties:
+ - Name: intPropertyToMangle
+ PropertyKind: Instance
+ Type: 'double *'
+Functions:
+ - Name: global_int_fun
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'double *'
+ - Position: 1
+ Type: 'float *'
+Globals:
+ - Name: global_int_ptr
+ Type: 'double (*)(int, int)'
+SwiftVersions:
+ - Version: 3.0
+ Classes:
+ - Name: A
+ Methods:
+ - Selector: "transform:integer:"
+ MethodKind: Instance
+ NullabilityOfRet: O
+ Nullability: [ O, S ]
+ Properties:
+ - Name: explicitNonnullInstance
+ PropertyKind: Instance
+ Nullability: O
+ - Name: explicitNullableInstance
+ PropertyKind: Instance
+ Nullability: N
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h
new file mode 100644
index 0000000..1a192f5
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h
@@ -0,0 +1,60 @@
+#ifndef SOMEKIT_H
+#define SOMEKIT_H
+
+__attribute__((objc_root_class))
+@interface A
+-(A*)transform:(A*)input;
+-(A*)transform:(A*)input integer:(int)integer;
+
+@property (nonatomic, readonly, retain) A* someA;
+@property (nonatomic, retain) A* someOtherA;
+
+@property (nonatomic) int intValue;
+@end
+
+@interface B : A
+@end
+
+@interface C : A
+- (instancetype)init;
+- (instancetype)initWithA:(A*)a;
+@end
+
+@interface ProcessInfo : A
++(instancetype)processInfo;
+@end
+
+@interface A(NonNullProperties)
+@property (nonatomic, readwrite, retain) A *nonnullAInstance;
+@property (class, nonatomic, readwrite, retain) A *nonnullAInstance;
+
+@property (nonatomic, readwrite, retain) A *nonnullAClass;
+@property (class, nonatomic, readwrite, retain) A *nonnullAClass;
+
+@property (nonatomic, readwrite, retain) A *nonnullABoth;
+@property (class, nonatomic, readwrite, retain) A *nonnullABoth;
+@end
+
+#import <SomeKit/SomeKitExplicitNullability.h>
+
+extern int *global_int_ptr;
+
+int *global_int_fun(int *ptr, int *ptr2);
+
+#define SOMEKIT_DOUBLE double
+
+__attribute__((objc_root_class))
+@interface OverriddenTypes
+-(int *)methodToMangle:(int *)ptr1 second:(int *)ptr2;
+@property int *intPropertyToMangle;
+@end
+
+@interface A(ImplicitGetterSetters)
+@property (nonatomic, readonly, retain) A *implicitGetOnlyInstance;
+@property (class, nonatomic, readonly, retain) A *implicitGetOnlyClass;
+
+@property (nonatomic, readwrite, retain) A *implicitGetSetInstance;
+@property (class, nonatomic, readwrite, retain) A *implicitGetSetClass;
+@end
+
+#endif
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h
new file mode 100644
index 0000000..40be241
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h
@@ -0,0 +1,5 @@
+@interface A(ExplicitNullabilityProperties)
+@property (nonatomic, readwrite, retain, nonnull) A *explicitNonnullInstance;
+@property (nonatomic, readwrite, retain, nullable) A *explicitNullableInstance;
+@end
+
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h
new file mode 100644
index 0000000..d1eeb61
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h
@@ -0,0 +1,55 @@
+#ifndef SOMEKIT_H
+#define SOMEKIT_H
+
+#define ROOT_CLASS __attribute__((objc_root_class))
+
+ROOT_CLASS
+@interface A
+-(A*)transform:(A*)input;
+-(A*)transform:(A*)input integer:(int)integer;
+
+@property (nonatomic, readonly, retain) A* someA;
+@property (nonatomic, retain) A* someOtherA;
+
+@property (nonatomic) int intValue;
+@end
+
+@interface B : A
+@end
+
+@interface C : A
+- (instancetype)init;
+- (instancetype)initWithA:(A*)a;
+@end
+
+
+@interface MyClass : A
+- Inst;
++ Clas;
+@end
+
+struct CGRect {
+ float origin;
+ float size;
+};
+typedef struct CGRect NSRect;
+
+@interface I
+- (void) Meth : (NSRect[4])exposedRects;
+- (void) Meth1 : (const I*)exposedRects;
+- (void) Meth2 : (const I*)exposedRects;
+- (void) Meth3 : (I*)exposedRects;
+- (const I*) Meth4;
+- (const I*) Meth5 : (int) Arg1 : (const I*)Arg2 : (double)Arg3 : (const I*) Arg4 :(const volatile id) Arg5;
+- (volatile const I*) Meth6 : (const char *)Arg1 : (const char *)Arg2 : (double)Arg3 : (const I*) Arg4 :(const volatile id) Arg5;
+@end
+
+@class NSURL, NSArray, NSError;
+@interface INTF_BLOCKS
+ + (void)getNonLocalVersionsOfItemAtURL:(NSURL *)url completionHandler:(void (^)(NSArray *nonLocalFileVersions, NSError *error))completionHandler;
+ + (void *)getNonLocalVersionsOfItemAtURL2:(NSURL *)url completionHandler:(void (^)(NSArray *nonLocalFileVersions, NSError *error))completionHandler;
+ + (NSError **)getNonLocalVersionsOfItemAtURL3:(int)url completionHandler:(void (^)(NSArray *nonLocalFileVersions, NSError *error))completionHandler;
+ + (id)getNonLocalVersionsOfItemAtURL4:(NSURL *)url completionHandler:(void (^)(int nonLocalFileVersions, NSError *error, NSURL*))completionHandler;
+@end
+
+#endif
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap
new file mode 100644
index 0000000..3abee2d
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module SomeKit {
+ umbrella header "SomeKit.h"
+ export *
+ module * { export * }
+}
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap
new file mode 100644
index 0000000..bbda9d0
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap
@@ -0,0 +1,8 @@
+module SomeKit.Private {
+ header "SomeKit_Private.h"
+ export *
+
+ explicit module NullAnnotation {
+ header "SomeKit_PrivateForNullAnnotation.h"
+ }
+}
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap
new file mode 100644
index 0000000..e310343
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap
@@ -0,0 +1,8 @@
+explicit framework module SomeKit.Private {
+ header "SomeKit_Private.h"
+ explicit NullAnnotation { header "SomeKit_PrivateForNullAnnotation.h" }
+ export *
+ module * { export * }
+syntax error
+
+}
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h
new file mode 100644
index 0000000..c761112
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h
@@ -0,0 +1,16 @@
+#ifndef SOMEKIT_PRIVATE_H
+#define SOMEKIT_PRIVATE_H
+
+#import <SomeKit/SomeKit.h>
+
+@interface A(Private)
+-(A*)privateTransform:(A*)input;
+
+@property (nonatomic) A* internalProperty;
+@end
+
+@protocol InternalProtocol
+@end
+
+#endif
+
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h
new file mode 100644
index 0000000..bae4456
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h
@@ -0,0 +1,17 @@
+#ifndef SOMEKIT_PRIVATE_H
+#define SOMEKIT_PRIVATE_H
+
+#import <SomeKit/SomeKitForNullAnnotation.h>
+
+@interface A(Private)
+-(A*)privateTransform:(A*)input;
+
+@property (nonatomic) A* internalProperty;
+@end
+
+@protocol InternalProtocol
+- (id) MomeMethod;
+@end
+
+#endif
+
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes
new file mode 100644
index 0000000..28ede9d
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes
@@ -0,0 +1,15 @@
+Name: SomeKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "privateTransform:input:"
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ Properties:
+ - Name: internalProperty
+ Nullability: N
+Protocols:
+ - Name: InternalProtocol
+ Availability: none
+ AvailabilityMsg: "not for you"
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes
new file mode 100644
index 0000000..2ad546b
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes
@@ -0,0 +1,8 @@
+Name: SomeOtherKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "methodA"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes
new file mode 100644
index 0000000..2ad546b
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes
@@ -0,0 +1,8 @@
+Name: SomeOtherKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "methodA"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
new file mode 100644
index 0000000..3911d76
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
@@ -0,0 +1,9 @@
+#ifndef SOME_OTHER_KIT_H
+
+__attribute__((objc_root_class))
+@interface A
+-(void)methodA;
+-(void)methodB;
+@end
+
+#endif
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap
new file mode 100644
index 0000000..0aaad92
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module SomeOtherKit {
+ umbrella header "SomeOtherKit.h"
+ export *
+ module * { export * }
+}
diff --git a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
new file mode 100644
index 0000000..c3b70b6
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
@@ -0,0 +1,53 @@
+Name: VersionedKit
+Classes:
+ - Name: TestProperties
+ Properties:
+ - Name: accessorsOnly
+ PropertyKind: Instance
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyForClass
+ PropertyKind: Class
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyExceptInVersion3
+ PropertyKind: Instance
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyForClassExceptInVersion3
+ PropertyKind: Class
+ SwiftImportAsAccessors: true
+SwiftVersions:
+ - Version: 3.0
+ Classes:
+ - Name: MyReferenceType
+ SwiftBridge: ''
+ - Name: TestProperties
+ Properties:
+ - Name: accessorsOnlyInVersion3
+ PropertyKind: Instance
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyForClassInVersion3
+ PropertyKind: Class
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyExceptInVersion3
+ PropertyKind: Instance
+ SwiftImportAsAccessors: false
+ - Name: accessorsOnlyForClassExceptInVersion3
+ PropertyKind: Class
+ SwiftImportAsAccessors: false
+ Functions:
+ - Name: moveToPoint
+ SwiftName: 'moveTo(a:b:)'
+ - Name: acceptClosure
+ Parameters:
+ - Position: 0
+ NoEscape: false
+ - Name: privateFunc
+ SwiftPrivate: false
+ Tags:
+ - Name: MyErrorCode
+ NSErrorDomain: ''
+ Typedefs:
+ - Name: MyDoubleWrapper
+ SwiftWrapper: none
+
+
+
\ No newline at end of file
diff --git a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
new file mode 100644
index 0000000..ffbc7df
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
@@ -0,0 +1,30 @@
+void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
+
+void acceptClosure(void (^ __attribute__((noescape)) block)(void));
+
+@class NSString;
+
+extern NSString *MyErrorDomain;
+
+enum __attribute__((ns_error_domain(MyErrorDomain))) MyErrorCode {
+ MyErrorCodeFailed = 1
+};
+
+__attribute__((swift_bridge("MyValueType")))
+@interface MyReferenceType
+@end
+
+void privateFunc(void) __attribute__((swift_private));
+
+typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct)));
+
+@interface TestProperties
+@property (nonatomic, readwrite, retain) id accessorsOnly;
+@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClass;
+
+@property (nonatomic, readwrite, retain) id accessorsOnlyInVersion3;
+@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClassInVersion3;
+
+@property (nonatomic, readwrite, retain) id accessorsOnlyExceptInVersion3;
+@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClassExceptInVersion3;
+@end
diff --git a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap
new file mode 100644
index 0000000..6d957fd
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module VersionedKit {
+ umbrella header "VersionedKit.h"
+ export *
+ module * { export * }
+}
diff --git a/test/APINotes/Inputs/Headers/APINotes.apinotes b/test/APINotes/Inputs/Headers/APINotes.apinotes
new file mode 100644
index 0000000..08210fc
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/APINotes.apinotes
@@ -0,0 +1,18 @@
+Name: HeaderLib
+SwiftInferImportAsMember: true
+Functions:
+ - Name: custom_realloc
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ - Name: unavailable_function
+ Availability: none
+ AvailabilityMsg: "I beg you not to use this"
+ - Name: do_something_with_pointers
+ NullabilityOfRet: O
+ Nullability: [ N, O ]
+
+Globals:
+ - Name: global_int
+ Nullability: N
+ - Name: unavailable_global_int
+ Availability: none
diff --git a/test/APINotes/Inputs/Headers/BrokenTypes.apinotes b/test/APINotes/Inputs/Headers/BrokenTypes.apinotes
new file mode 100644
index 0000000..00f7b50
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/BrokenTypes.apinotes
@@ -0,0 +1,10 @@
+Name: BrokenTypes
+Functions:
+ - Name: break_me_function
+ ResultType: 'int * with extra junk'
+ Parameters:
+ - Position: 0
+ Type: 'not_a_type'
+Globals:
+ - Name: break_me_variable
+ Type: 'double'
diff --git a/test/APINotes/Inputs/Headers/BrokenTypes.h b/test/APINotes/Inputs/Headers/BrokenTypes.h
new file mode 100644
index 0000000..fee054b
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/BrokenTypes.h
@@ -0,0 +1,8 @@
+#ifndef BROKEN_TYPES_H
+#define BROKEN_TYPES_H
+
+char break_me_function(void *ptr);
+
+extern char break_me_variable;
+
+#endif // BROKEN_TYPES_H
diff --git a/test/APINotes/Inputs/Headers/HeaderLib.apinotes b/test/APINotes/Inputs/Headers/HeaderLib.apinotes
new file mode 100644
index 0000000..c822964
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/HeaderLib.apinotes
@@ -0,0 +1,37 @@
+Name: HeaderLib
+SwiftInferImportAsMember: true
+Functions:
+ - Name: custom_realloc
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ - Name: unavailable_function
+ Availability: none
+ AvailabilityMsg: "I beg you not to use this"
+ - Name: do_something_with_pointers
+ NullabilityOfRet: O
+ Nullability: [ N, O ]
+ - Name: do_something_with_arrays
+ Parameters:
+ - Position: 0
+ Nullability: N
+ - Position: 1
+ Nullability: N
+ - Name: take_pointer_and_int
+ Parameters:
+ - Position: 0
+ Nullability: N
+ NoEscape: true
+ - Position: 1
+ NoEscape: true
+Globals:
+ - Name: global_int
+ Nullability: N
+ - Name: unavailable_global_int
+ Availability: none
+Tags:
+ - Name: unavailable_struct
+ Availability: none
+
+Typedefs:
+ - Name: unavailable_typedef
+ Availability: none
\ No newline at end of file
diff --git a/test/APINotes/Inputs/Headers/HeaderLib.h b/test/APINotes/Inputs/Headers/HeaderLib.h
new file mode 100644
index 0000000..8065249
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/HeaderLib.h
@@ -0,0 +1,19 @@
+#ifndef HEADER_LIB_H
+#define HEADER_LIB_H
+
+void *custom_realloc(void *member, unsigned size);
+
+int *global_int;
+
+int unavailable_function(void);
+int unavailable_global_int;
+
+void do_something_with_pointers(int *ptr1, int *ptr2);
+void do_something_with_arrays(int simple[], int nested[][2]);
+
+typedef int unavailable_typedef;
+struct unavailable_struct { int x, y, z; };
+
+void take_pointer_and_int(int *ptr1, int value);
+
+#endif
diff --git a/test/APINotes/Inputs/Headers/module.modulemap b/test/APINotes/Inputs/Headers/module.modulemap
new file mode 100644
index 0000000..54af0f5
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/module.modulemap
@@ -0,0 +1,7 @@
+module HeaderLib {
+ header "HeaderLib.h"
+}
+
+module BrokenTypes {
+ header "BrokenTypes.h"
+}
diff --git a/test/APINotes/Inputs/os-availability.apinotes b/test/APINotes/Inputs/os-availability.apinotes
new file mode 100644
index 0000000..d59e79c
--- /dev/null
+++ b/test/APINotes/Inputs/os-availability.apinotes
@@ -0,0 +1,53 @@
+Name: Foundation
+Classes:
+ - Name: NSCountedSet
+ Availability: iOS
+ Methods:
+ - Selector: 'initWithCapacity:'
+ MethodKind: Instance
+ DesignatedInit: true
+ - Name: NSArray
+ Methods:
+ - Selector: 'init'
+ MethodKind: Instance
+ DesignatedInit: true
+ - Selector: 'initWithObjects:'
+ MethodKind: Instance
+ DesignatedInit: true
+ Availability: iOS
+ - Selector: 'initWithObjects:count:'
+ MethodKind: Instance
+ DesignatedInit: true
+ Availability: iOS
+ Properties:
+ - Name: 'familyNameios'
+ Nullability: N
+ Availability: iOS
+ - Name: 'fontName'
+ Nullability: N
+Protocols:
+ - Name: UIApplicationDelegate
+ AuditedForNullability: true
+ Methods:
+ - Selector: 'application:willFinishLaunchingWithOptions:'
+ MethodKind: Instance
+ Nullability: [ N, U ]
+ - Name: UIApplicationDelegateIOS
+ Availability: iOS
+ AuditedForNullability: true
+ Methods:
+ - Selector: 'application:willFinishLaunchingWithOptions:'
+ MethodKind: Instance
+ Nullability: [ N, U ]
+Functions:
+ - Name: NSAvailableWindowDepthsiOS
+ NullabilityOfRet: N
+ Availability: iOS
+ - Name: NSAvailableWindowDepths
+ NullabilityOfRet: N
+Globals:
+ - Name: NSCalibratedWhiteColorSpace
+ Nullability: N
+ Availability: OSX
+ AvailabilityMsg: ''
+
diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes
new file mode 100644
index 0000000..05599a7
--- /dev/null
+++ b/test/APINotes/Inputs/roundtrip.apinotes
@@ -0,0 +1,181 @@
+---
+Name: AppKit
+Availability: available
+AvailabilityMsg: ''
+SwiftInferImportAsMember: true
+Classes:
+ - Name: NSCell
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ Methods:
+ - Selector: 'cellWithImage:'
+ MethodKind: Class
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ FactoryAsInit: C
+ ResultType: id
+ - Selector: init
+ MethodKind: Instance
+ NullabilityOfRet: U
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: true
+ SwiftName: ''
+ DesignatedInit: true
+ - Selector: 'initImageCell:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: U
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ DesignatedInit: true
+ - Selector: 'initTextCell:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: U
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ DesignatedInit: true
+ - Selector: 'initWithCoder:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: U
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ DesignatedInit: true
+ Required: true
+ - Name: NSView
+ AuditedForNullability: true
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ SwiftBridge: View
+ Methods:
+ - Selector: 'addSubview:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ - Selector: 'addSubview:positioned:relativeTo:'
+ MethodKind: Instance
+ Parameters:
+ - Position: 0
+ NoEscape: false
+ - Position: 1
+ - Position: 2
+ NoEscape: true
+ Type: id
+ Nullability: [ N, N, O ]
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: ''
+ - Selector: 'beginDraggingSessionWithItems:event:source:'
+ MethodKind: Instance
+ Nullability: [ U, U, N ]
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: 'beginDragginSession(_:event:source:)'
+ Properties:
+ - Name: enclosingScrollView
+ PropertyKind: Instance
+ Nullability: O
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: enclosing
+ Type: id
+ - Name: makeBackingLayer
+ PropertyKind: Class
+ Nullability: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ SwiftImportAsAccessors: false
+Functions:
+ - Name: NSAvailableWindowDepths
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: 'availableWindowDepths()'
+ ResultType: NSInteger
+Globals:
+ - Name: NSCalibratedWhiteColorSpace
+ Nullability: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: calibratedWhite
+ Type: id
+Enumerators:
+ - Name: NSColorRed
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: Red
+Tags:
+ - Name: NSSomeEnum
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: SomeEnum
+ NSErrorDomain: some_error_domain
+ - Name: NSSomeStruct
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: SomeStruct
+ NSErrorDomain: ''
+Typedefs:
+ - Name: NSTypedef
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: Typedef
+ SwiftBridge: ''
+ SwiftWrapper: struct
+SwiftVersions:
+ - Version: 3.0
+ Classes:
+ - Name: NSCell
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: NSBox
+ SwiftBridge: ''
+ Methods:
+ - Selector: init
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: true
+ SwiftName: ''
+ DesignatedInit: true
+ - Name: NSView
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: ''
+ Properties:
+ - Name: makeBackingLayer
+ PropertyKind: Class
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: ''
+ SwiftImportAsAccessors: true
diff --git a/test/APINotes/availability.m b/test/APINotes/availability.m
new file mode 100644
index 0000000..f9bee1a
--- /dev/null
+++ b/test/APINotes/availability.m
@@ -0,0 +1,48 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+#include "HeaderLib.h"
+#import <SomeKit/SomeKit.h>
+#import <SomeKit/SomeKit_Private.h>
+
+int main() {
+ int i;
+ i = unavailable_function(); // expected-error{{'unavailable_function' is unavailable: I beg you not to use this}}
+ // expected-note@HeaderLib.h:8{{'unavailable_function' has been explicitly marked unavailable here}}
+ i = unavailable_global_int; // expected-error{{'unavailable_global_int' is unavailable}}
+ // expected-note@HeaderLib.h:9{{'unavailable_global_int' has been explicitly marked unavailable here}}
+
+ unavailable_typedef t; // expected-error{{'unavailable_typedef' is unavailable}}
+ // expected-note@HeaderLib.h:14{{'unavailable_typedef' has been explicitly marked unavailable here}}
+
+ struct unavailable_struct s; // expected-error{{'unavailable_struct' is unavailable}}
+ // expected-note@HeaderLib.h:15{{'unavailable_struct' has been explicitly marked unavailable here}}
+
+ B *b = 0; // expected-error{{'B' is unavailable: just don't}}
+ // expected-note@SomeKit/SomeKit.h:15{{'B' has been explicitly marked unavailable here}}
+
+ id<InternalProtocol> proto = 0; // expected-error{{'InternalProtocol' is unavailable: not for you}}
+ // expected-note@SomeKit/SomeKit_Private.h:12{{'InternalProtocol' has been explicitly marked unavailable here}}
+
+ A *a = 0;
+ i = a.intValue; // expected-error{{intValue' is unavailable: wouldn't work anyway}}
+ // expected-note@SomeKit/SomeKit.h:12{{'intValue' has been explicitly marked unavailable here}}
+
+ [a transform:a]; // expected-error{{'transform:' is unavailable: anything but this}}
+ // expected-note@SomeKit/SomeKit.h:6{{'transform:' has been explicitly marked unavailable here}}
+
+ [a implicitGetOnlyInstance]; // expected-error{{'implicitGetOnlyInstance' is unavailable: getter gone}}
+ // expected-note@SomeKit/SomeKit.h:53{{'implicitGetOnlyInstance' has been explicitly marked unavailable here}}
+ [A implicitGetOnlyClass]; // expected-error{{'implicitGetOnlyClass' is unavailable: getter gone}}
+ // expected-note@SomeKit/SomeKit.h:54{{'implicitGetOnlyClass' has been explicitly marked unavailable here}}
+ [a implicitGetSetInstance]; // expected-error{{'implicitGetSetInstance' is unavailable: getter gone}}
+ // expected-note@SomeKit/SomeKit.h:56{{'implicitGetSetInstance' has been explicitly marked unavailable here}}
+ [a setImplicitGetSetInstance: a]; // expected-error{{'setImplicitGetSetInstance:' is unavailable: setter gone}}
+ // expected-note@SomeKit/SomeKit.h:56{{'setImplicitGetSetInstance:' has been explicitly marked unavailable here}}
+ [A implicitGetSetClass]; // expected-error{{'implicitGetSetClass' is unavailable: getter gone}}
+ // expected-note@SomeKit/SomeKit.h:57{{'implicitGetSetClass' has been explicitly marked unavailable here}}
+ [A setImplicitGetSetClass: a]; // expected-error{{'setImplicitGetSetClass:' is unavailable: setter gone}}
+ // expected-note@SomeKit/SomeKit.h:57{{'setImplicitGetSetClass:' has been explicitly marked unavailable here}}
+ return 0;
+}
+
diff --git a/test/APINotes/broken_types.m b/test/APINotes/broken_types.m
new file mode 100644
index 0000000..164ae79
--- /dev/null
+++ b/test/APINotes/broken_types.m
@@ -0,0 +1,19 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 2> %t.err
+// RUN: FileCheck %s < %t.err
+
+#include "BrokenTypes.h"
+
+// CHECK: <API Notes>:1:1: error: unknown type name 'not_a_type'
+// CHECK-NEXT: not_a_type
+// CHECK-NEXT: ^
+
+// CHECK: <API Notes>:1:7: error: unparsed tokens following type
+// CHECK-NEXT: int * with extra junk
+// CHECK-NEXT: ^
+
+// CHECK: BrokenTypes.h:4:6: error: API notes replacement type 'int *' has a different size from original type 'char'
+
+// CHECK: BrokenTypes.h:6:13: error: API notes replacement type 'double' has a different size from original type 'char'
+
+// CHECK: 5 errors generated.
diff --git a/test/APINotes/cache.m b/test/APINotes/cache.m
new file mode 100644
index 0000000..b87bdf1
--- /dev/null
+++ b/test/APINotes/cache.m
@@ -0,0 +1,32 @@
+// RUN: rm -rf %t/APINotesCache
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+// Check for the presence of the cached compiled form.
+// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
+
+// Run test again to ensure that caching doesn't cause problems.
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+// Check that the driver provides a default -fapinotes-cache-path=
+// RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-DEFAULT-PATH %s
+// CHECK-DEFAULT-PATH: -fapinotes-cache-path={{.*}}org.llvm.clang/APINotesCache
+
+// Check that the driver passes through a provided -fapinotes-cache-path=
+// RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -fapinotes-cache-path=/wobble -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-PATH %s
+// CHECK-PATH: -fapinotes-cache-path=/wobble
+
+#include "HeaderLib.h"
+#import <SomeKit/SomeKit.h>
+
+int main() {
+ int i;
+ i = unavailable_function(); // expected-error{{'unavailable_function' is unavailable: I beg you not to use this}}
+ // expected-note@HeaderLib.h:8{{'unavailable_function' has been explicitly marked unavailable here}}
+
+ A *a = 0;
+ [a transform:a]; // expected-error{{'transform:' is unavailable: anything but this}}
+ // expected-note@SomeKit/SomeKit.h:6{{'transform:' has been explicitly marked unavailable here}}
+
+ return 0;
+}
diff --git a/test/APINotes/cache_pruning.m b/test/APINotes/cache_pruning.m
new file mode 100644
index 0000000..1a36570
--- /dev/null
+++ b/test/APINotes/cache_pruning.m
@@ -0,0 +1,49 @@
+// We need 'touch' and 'find' for this test to work.
+// REQUIRES: shell
+
+// RUN: rm -rf %t/APINotesCache
+
+// Run Clang. This should generated the cached versions of both and a timestamp.
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB
+// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
+
+// Set the timestamp back a very long time. We should try to prune,
+// but nothing gets pruned because the API Notes files are new enough.
+// RUN: touch -m -a -t 201101010000 %t/APINotes.timestamp
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
+
+// Set the HeaderLib access time back a very long time.
+// This shouldn't prune anything, because the timestamp has been updated, so
+// the pruning mechanism won't fire.
+// RUN: find %t/APINotesCache -name APINotes-*.apinotesc | xargs touch -a -t 201101010000
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
+
+// Set the timestack back a very long time. This should prune the
+// HeaderLib file, because the pruning mechanism should fire and
+// HeaderLib is both old and not used.
+// RUN: touch -m -a -t 201101010000 %t/APINotesCache/APINotes.timestamp
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: ls %t/APINotesCache | not grep "APINotes-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
+
+// Run Clang. This should generated the cached versions of both and a timestamp.
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB
+// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
+// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
+
+#ifdef INCLUDE_HEADERLIB
+#include "HeaderLib.h"
+#endif
+#include <SomeKit/SomeKit.h>
+
+int main() { return 0; }
diff --git a/test/APINotes/module-cache.m b/test/APINotes/module-cache.m
new file mode 100644
index 0000000..2324697
--- /dev/null
+++ b/test/APINotes/module-cache.m
@@ -0,0 +1,90 @@
+// RUN: rm -rf %t
+
+// Set up a directory with API notes
+// RUN: mkdir -p %t/APINotes
+// RUN: cp %S/Inputs/APINotes/SomeOtherKit.apinotes %t/APINotes/SomeOtherKit.apinotes
+
+// First build: check that 'methodB' is unavailable but 'methodA' is available.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log
+
+// Do it again; now we're using caches.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log
+
+// Change the API notes file.
+// RUN: echo ' - Selector: "methodA"' >> %t/APINotes/SomeOtherKit.apinotes
+// RUN: echo ' MethodKind: Instance' >> %t/APINotes/SomeOtherKit.apinotes
+// RUN: echo ' Availability: none' >> %t/APINotes/SomeOtherKit.apinotes
+// RUN: echo ' AvailabilityMsg: "not here either"' >> %t/APINotes/SomeOtherKit.apinotes
+
+// Build again: check that both methods are now unavailable and that the module rebuilt.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log
+
+// Run the build again: check that both methods are now unavailable
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log
+
+// Set up a directory with pre-compiled API notes.
+// RUN: mkdir -p %t/CompiledAPINotes
+// RUN: rm -rf %t/ModulesCache
+// RUN: rm -rf %t/APINotesCache
+// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %S/Inputs/APINotes/SomeOtherKit.apinotes
+
+// First build: check that 'methodB' is unavailable but 'methodA' is available.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log
+
+// Do it again; now we're using caches.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log
+
+// Compile a new API notes file to replace the old one.
+// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %t/APINotes/SomeOtherKit.apinotes
+
+// Build again: check that both methods are now unavailable and that the module rebuilt.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log
+
+// Run the build again: check that both methods are now unavailable
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log
+
+@import SomeOtherKit;
+
+void test(A *a) {
+ // CHECK-METHODA: error: 'methodA' is unavailable: not here either
+ [a methodA];
+
+ // CHECK-METHODB: error: 'methodB' is unavailable: anything but this
+ [a methodB];
+}
+
+// CHECK-REBUILD: remark: building module{{.*}}SomeOtherKit
+
+// CHECK-WITHOUT-REBUILD-NOT: remark: building module{{.*}}SomeOtherKit
+
+// CHECK-ONE-ERROR: 1 error generated.
+// CHECK-TWO-ERRORS: 2 errors generated.
+
diff --git a/test/APINotes/nullability.c b/test/APINotes/nullability.c
new file mode 100644
index 0000000..1fcd0ee
--- /dev/null
+++ b/test/APINotes/nullability.c
@@ -0,0 +1,21 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+#include "HeaderLib.h"
+
+int main() {
+ custom_realloc(0, 0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+ int i = 0;
+ do_something_with_pointers(&i, 0);
+ do_something_with_pointers(0, &i); // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ extern void *p;
+ do_something_with_arrays(0, p); // expected-warning{{null passed to a callee that requires a non-null argument}}
+ do_something_with_arrays(p, 0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ take_pointer_and_int(0, 0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ float *fp = global_int; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * _Nonnull'}}
+ return 0;
+}
+
diff --git a/test/APINotes/nullability.m b/test/APINotes/nullability.m
new file mode 100644
index 0000000..f70c363
--- /dev/null
+++ b/test/APINotes/nullability.m
@@ -0,0 +1,44 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+// Test with Swift version 3.0. This should only affect the few APIs that have an entry in the 3.0 tables.
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 -fmodules-ignore-macro=SWIFT_VERSION_3_0
+
+#import <SomeKit/SomeKit.h>
+
+int main() {
+ A *a;
+
+#if SWIFT_VERSION_3_0
+ float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A * _Nullable'}}
+ [a transform: 0 integer: 0];
+#else
+ float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A *'}}
+ [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+#endif
+
+ [a setNonnullAInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+ [A setNonnullAInstance: 0]; // no warning
+
+ [a setNonnullAClass: 0]; // no warning
+ [A setNonnullAClass: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ [a setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+ [A setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ [a setInternalProperty: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+#if SWIFT_VERSION_3_0
+ // Version 3 information overrides header information.
+ [a setExplicitNonnullInstance: 0]; // okay
+ [a setExplicitNullableInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+#else
+ // Header information overrides unversioned information.
+ [a setExplicitNonnullInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+ [a setExplicitNullableInstance: 0]; // okay
+#endif
+
+ return 0;
+}
+
diff --git a/test/APINotes/objc_designated_inits.m b/test/APINotes/objc_designated_inits.m
new file mode 100644
index 0000000..1df8cf8
--- /dev/null
+++ b/test/APINotes/objc_designated_inits.m
@@ -0,0 +1,17 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+#include "HeaderLib.h"
+#import <SomeKit/SomeKit.h>
+
+@interface CSub : C
+-(instancetype)initWithA:(A*)a;
+@end
+
+@implementation CSub
+-(instancetype)initWithA:(A*)a { // expected-warning{{designated initializer missing a 'super' call to a designated initializer of the super class}}
+ // expected-note@SomeKit/SomeKit.h:20 2{{method marked as designated initializer of the class here}}
+ self = [super init]; // expected-warning{{designated initializer invoked a non-designated initializer}}
+ return self;
+}
+@end
diff --git a/test/APINotes/properties.m b/test/APINotes/properties.m
new file mode 100644
index 0000000..b1559b9
--- /dev/null
+++ b/test/APINotes/properties.m
@@ -0,0 +1,45 @@
+// RUN: rm -rf %t && mkdir -p %t
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' | FileCheck -check-prefix=CHECK -check-prefix=CHECK-4 %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' -fapinotes-swift-version=3 | FileCheck -check-prefix=CHECK -check-prefix=CHECK-3 %s
+
+// I know, FileChecking an AST dump is brittle. However, the attributes being
+// tested aren't used for anything by Clang, and don't even have a spelling.
+
+@import VersionedKit;
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnly 'id'
+// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClass 'id'
+// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyInVersion3 'id'
+// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0
+// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassInVersion3 'id'
+// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0
+// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyExceptInVersion3 'id'
+// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} 0{{$}}
+// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}}
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassExceptInVersion3 'id'
+// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} 0{{$}}
+// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}}
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: Decl
diff --git a/test/APINotes/search-order.m b/test/APINotes/search-order.m
new file mode 100644
index 0000000..2c667be
--- /dev/null
+++ b/test/APINotes/search-order.m
@@ -0,0 +1,25 @@
+// RUN: rm -rf %t && mkdir -p %t
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_SEARCH_PATH=1 -verify
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify
+
+@import SomeOtherKit;
+
+void test(A *a) {
+#if FROM_FRAMEWORK
+ [a methodA]; // expected-error{{unavailable}}
+ [a methodB];
+
+ // expected-note@SomeOtherKit/SomeOtherKit.h:5{{'methodA' has been explicitly marked unavailable here}}
+#elif FROM_SEARCH_PATH
+ [a methodA];
+ [a methodB]; // expected-error{{unavailable}}
+
+ // expected-note@SomeOtherKit/SomeOtherKit.h:6{{'methodB' has been explicitly marked unavailable here}}
+#else
+# error Not something we need to test
+#endif
+}
diff --git a/test/APINotes/types.m b/test/APINotes/types.m
new file mode 100644
index 0000000..fccff80
--- /dev/null
+++ b/test/APINotes/types.m
@@ -0,0 +1,23 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+#import <SomeKit/SomeKit.h>
+
+void test(OverriddenTypes *overridden) {
+ int *ip1 = global_int_ptr; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double (*)(int, int)'}}
+
+ int *ip2 = global_int_fun( // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'char *'}}
+ ip2, // expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'double *'}}
+ ip2); // expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'float *'}}
+
+ int *ip3 = [overridden // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'char *'}}
+ methodToMangle: ip3 // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'double *'}}
+ second: ip3]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'float *'}}
+
+ int *ip4 = overridden.intPropertyToMangle; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double *'}}
+}
+
+// expected-note@SomeKit/SomeKit.h:42{{passing argument to parameter 'ptr' here}}
+// expected-note@SomeKit/SomeKit.h:42{{passing argument to parameter 'ptr2' here}}
+// expected-note@SomeKit/SomeKit.h:48{{passing argument to parameter 'ptr1' here}}
+// expected-note@SomeKit/SomeKit.h:48{{passing argument to parameter 'ptr2' here}}
diff --git a/test/APINotes/versioned.m b/test/APINotes/versioned.m
new file mode 100644
index 0000000..38690fe
--- /dev/null
+++ b/test/APINotes/versioned.m
@@ -0,0 +1,50 @@
+// RUN: rm -rf %t && mkdir -p %t
+
+// Build and check the unversioned module file.
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s
+
+// Build and check the versioned module file.
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s
+
+#import <VersionedKit/VersionedKit.h>
+
+// CHECK-UNVERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
+// CHECK-VERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(a:b:)")));
+
+// CHECK-DUMP-LABEL: Dumping moveToPoint
+// CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 0
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)"
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)"
+// CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)"
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)"
+// CHECK-DUMP-NOT: Dumping
+
+// CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape)));
+// CHECK-VERSIONED: void acceptClosure(void (^block)(void));
+
+// CHECK-UNVERSIONED: enum MyErrorCode {
+// CHECK-UNVERSIONED-NEXT: MyErrorCodeFailed = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((ns_error_domain(MyErrorDomain)));
+
+// CHECK-UNVERSIONED: __attribute__((swift_bridge("MyValueType")))
+// CHECK-UNVERSIONED: @interface MyReferenceType
+
+// CHECK-UNVERSIONED: void privateFunc() __attribute__((swift_private));
+
+// CHECK-UNVERSIONED: typedef double MyDoubleWrapper __attribute__((swift_wrapper("struct")));
+
+// CHECK-VERSIONED: enum MyErrorCode {
+// CHECK-VERSIONED-NEXT: MyErrorCodeFailed = 1
+// CHECK-VERSIONED-NEXT: };
+
+// CHECK-VERSIONED-NOT: __attribute__((swift_bridge("MyValueType")))
+// CHECK-VERSIONED: @interface MyReferenceType
+
+// CHECK-VERSIONED: void privateFunc();
+
+// CHECK-VERSIONED: typedef double MyDoubleWrapper;
diff --git a/test/APINotes/yaml-convert-diags.c b/test/APINotes/yaml-convert-diags.c
new file mode 100644
index 0000000..e8767f2
--- /dev/null
+++ b/test/APINotes/yaml-convert-diags.c
@@ -0,0 +1,6 @@
+// RUN: rm -rf %t
+// RUN: not %clang_cc1 -fsyntax-only -fapinotes -fapinotes-cache-path=%t %s -I %S/Inputs/BrokenHeaders2 2>&1 | FileCheck %s
+
+#include "SomeBrokenLib.h"
+
+// CHECK: error: multiple definitions of global function 'do_something_with_pointers'
diff --git a/test/APINotes/yaml-os-availability.c b/test/APINotes/yaml-os-availability.c
new file mode 100644
index 0000000..62301af
--- /dev/null
+++ b/test/APINotes/yaml-os-availability.c
@@ -0,0 +1,31 @@
+# RUN: %clang -cc1apinotes -yaml-to-binary -target i386-apple-ios7 -o %t-ios.apinotesc %S/Inputs/os-availability.apinotes
+# RUN: %clang -cc1apinotes -binary-to-yaml %t-ios.apinotesc -o %t.os-availability-ios.apinotes
+# RUN: FileCheck %s -check-prefix=IOS < %t.os-availability-ios.apinotes
+
+# RUN: %clang -cc1apinotes -yaml-to-binary -target x86_64-apple-macosx10.9 -o %t-osx.apinotesc %S/Inputs/os-availability.apinotes
+# RUN: %clang -cc1apinotes -binary-to-yaml %t-osx.apinotesc -o %t.os-availability-osx.apinotes
+# RUN: FileCheck %s -check-prefix=OSX < %t.os-availability-osx.apinotes
+
+# IOS: Foundation
+# IOS: NSArray
+# IOS: initWithObjects
+# IOS: familyNameios
+# IOS: fontName
+# IOS: NSCountedSet
+# IOS: UIApplicationDelegate
+# IOS: UIApplicationDelegateIOS
+# IOS: NSAvailableWindowDepths
+# IOS: NSAvailableWindowDepthsiOS
+# IOS-NOT: NSCalibratedWhiteColorSpace
+
+# OSX: Foundation
+# qqOSX: NSArray
+# OSX-NOT: initWithObjects
+# OSX-NOT: familyNameios
+# OSX: fontName
+# OSX-NOT: NSCountedSet
+# OSX: UIApplicationDelegate
+# OSX-NOT: UIApplicationDelegateIOS
+# OSX: NSAvailableWindowDepths
+# OSX-NOT: NSAvailableWindowDepthsiOS
+# OSX: NSCalibratedWhiteColorSpace
diff --git a/test/APINotes/yaml-parse-diags.c b/test/APINotes/yaml-parse-diags.c
new file mode 100644
index 0000000..4505e29
--- /dev/null
+++ b/test/APINotes/yaml-parse-diags.c
@@ -0,0 +1,6 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fsyntax-only -fapinotes -fapinotes-cache-path=%t %s -I %S/Inputs/BrokenHeaders -verify
+
+#include "SomeBrokenLib.h"
+
+// expected-error@APINotes.apinotes:4{{unknown key 'Nu llabilityOfRet'}}
diff --git a/test/APINotes/yaml-reader-errors.c b/test/APINotes/yaml-reader-errors.c
new file mode 100644
index 0000000..3e01eaa
--- /dev/null
+++ b/test/APINotes/yaml-reader-errors.c
@@ -0,0 +1,65 @@
+# RUN: not %clang -cc1apinotes -yaml-to-binary -target i386-apple-ios7 -o %t.apinotesc %s > %t.err 2>&1
+# RUN: FileCheck %s < %t.err
+
+---
+Name: UIKit
+Availability: iOS
+AvailabilityMsg: iOSOnly
+Classes:
+ - Name: UIFont
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ Methods:
+ - Selector: 'fontWithName:size:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ DesignatedInit: true
+# CHECK: duplicate definition of method '-[UIFont fontWithName:size:]'
+ - Selector: 'fontWithName:size:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ DesignatedInit: true
+ Properties:
+ - Name: familyName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: fontName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+# CHECK: duplicate definition of instance property 'UIFont.familyName'
+ - Name: familyName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+# CHECK: multiple definitions of class 'UIFont'
+ - Name: UIFont
+Protocols:
+ - Name: MyProto
+ AuditedForNullability: true
+# CHECK: multiple definitions of protocol 'MyProto'
+ - Name: MyProto
+ AuditedForNullability: true
+Functions:
+ - Name: 'globalFoo'
+ Nullability: [ N, N, O, S ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: 'globalFoo2'
+ Nullability: [ N, N, O, S ]
+ NullabilityOfRet: O
+Globals:
+ - Name: globalVar
+ Nullability: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: globalVar2
+ Nullability: O
diff --git a/test/APINotes/yaml-reader-test.c b/test/APINotes/yaml-reader-test.c
new file mode 100644
index 0000000..01ad90a
--- /dev/null
+++ b/test/APINotes/yaml-reader-test.c
@@ -0,0 +1,102 @@
+# RUN: %clang -cc1apinotes -dump %s | FileCheck %s
+---
+Name: UIKit
+Availability: iOS
+AvailabilityMsg: iOSOnly
+Classes:
+ - Name: UIFont
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ Methods:
+ - Selector: 'fontWithName:size:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ DesignatedInit: true
+ Properties:
+ - Name: familyName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: fontName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+Protocols:
+ - Name: MyProto
+ AuditedForNullability: true
+ - Name: MyProto2
+ AuditedForNullability: true
+Functions:
+ - Name: 'globalFoo'
+ Nullability: [ N, N, O, S ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: 'globalFoo2'
+ Nullability: [ N, N, O, S ]
+ NullabilityOfRet: O
+Globals:
+ - Name: globalVar
+ Nullability: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: globalVar2
+ Nullability: O
+
+
+# CHECK: Name: UIKit
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: Classes:
+# CHECK: - Name: UIFont
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: Methods:
+# CHECK: - Selector: 'fontWithName:size:'
+# CHECK: MethodKind: Instance
+# CHECK: Nullability: [ N ]
+# CHECK: NullabilityOfRet: O
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: DesignatedInit: true
+# CHECK: Properties:
+# CHECK: - Name: familyName
+# CHECK: Nullability: N
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: - Name: fontName
+# CHECK: Nullability: N
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK:Protocols:
+# CHECK: - Name: MyProto
+# CHECK: AuditedForNullability: true
+# CHECK: Availability: available
+# CHECK: AvailabilityMsg: ''
+# CHECK: - Name: MyProto2
+# CHECK: AuditedForNullability: true
+# CHECK: Availability: available
+# CHECK: AvailabilityMsg: ''
+# CHECK:Functions:
+# CHECK: - Name: globalFoo
+# CHECK: Nullability: [ N, N, O, U ]
+# CHECK: NullabilityOfRet: O
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: - Name: globalFoo2
+# CHECK: Nullability: [ N, N, O, U ]
+# CHECK: NullabilityOfRet: O
+# CHECK: Availability: available
+# CHECK: AvailabilityMsg: ''
+# CHECK:Globals:
+# CHECK: - Name: globalVar
+# CHECK: Nullability: O
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: - Name: globalVar2
+# CHECK: Nullability: O
+# CHECK: Availability: available
+# CHECK: AvailabilityMsg:
diff --git a/test/APINotes/yaml-roundtrip.c b/test/APINotes/yaml-roundtrip.c
new file mode 100644
index 0000000..b721b6a
--- /dev/null
+++ b/test/APINotes/yaml-roundtrip.c
@@ -0,0 +1,10 @@
+# RUN: %clang -cc1apinotes -yaml-to-binary -o %t.apinotesc %S/Inputs/roundtrip.apinotes
+# RUN: %clang -cc1apinotes -binary-to-yaml -o %t.apinotes %t.apinotesc
+
+# Handle the infurating '...' the YAML writer adds but the parser
+# can't read.
+
+# RUN: cp %S/Inputs/roundtrip.apinotes %t-reference.apinotes
+# RUN: echo "..." >> %t-reference.apinotes
+# RUN: diff %t-reference.apinotes %t.apinotes
+
diff --git a/test/Analysis/cstring-syntax.c b/test/Analysis/cstring-syntax.c
index 4aa88ed..2cde013 100644
--- a/test/Analysis/cstring-syntax.c
+++ b/test/Analysis/cstring-syntax.c
@@ -10,4 +10,6 @@
strncat(dest, "AAAAAAAAAAAAAAAAAAAAAAAAAAA", sizeof(dest)); // expected-warning {{Potential buffer overflow. Replace with}}
strncat(dest, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", sizeof(dest) - strlen(dest)); // expected-warning {{Potential buffer overflow. Replace with}}
strncat(dest, src, sizeof(src)); // expected-warning {{Potential buffer overflow. Replace with}}
+ // Should not crash when sizeof has a type argument.
+ strncat(dest, "AAAAAAAAAAAAAAAAAAAAAAAAAAA", sizeof(char));
}
diff --git a/test/Analysis/debug-CallGraph.c b/test/Analysis/debug-CallGraph.c
index 64259e2..686abb0 100644
--- a/test/Analysis/debug-CallGraph.c
+++ b/test/Analysis/debug-CallGraph.c
@@ -1,5 +1,17 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCallGraph %s -fblocks 2>&1 | FileCheck %s
+int get5() {
+ return 5;
+}
+
+int add(int val1, int val2) {
+ return val1 + val2;
+}
+
+int test_add() {
+ return add(10, get5());
+}
+
static void mmm(int y) {
if (y != 0)
y++;
@@ -32,7 +44,7 @@
void fff() { eee(); }
// CHECK:--- Call graph Dump ---
-// CHECK-NEXT: {{Function: < root > calls: mmm foo aaa < > bbb ccc ddd eee fff $}}
+// CHECK-NEXT: {{Function: < root > calls: get5 add test_add mmm foo aaa < > bbb ccc ddd eee fff $}}
// CHECK-NEXT: {{Function: fff calls: eee $}}
// CHECK-NEXT: {{Function: eee calls: $}}
// CHECK-NEXT: {{Function: ddd calls: ccc $}}
@@ -42,3 +54,6 @@
// CHECK-NEXT: {{Function: aaa calls: foo $}}
// CHECK-NEXT: {{Function: foo calls: mmm $}}
// CHECK-NEXT: {{Function: mmm calls: $}}
+// CHECK-NEXT: {{Function: test_add calls: add get5 $}}
+// CHECK-NEXT: {{Function: add calls: $}}
+// CHECK-NEXT: {{Function: get5 calls: $}}
diff --git a/test/Analysis/inlining/InlineObjCClassMethod.m b/test/Analysis/inlining/InlineObjCClassMethod.m
index c9cc90b..2772646 100644
--- a/test/Analysis/inlining/InlineObjCClassMethod.m
+++ b/test/Analysis/inlining/InlineObjCClassMethod.m
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config ipa=dynamic-bifurcate -verify %s
void clang_analyzer_checkInlined(int);
+void clang_analyzer_eval(int);
// Test inlining of ObjC class methods.
@@ -194,7 +195,9 @@
@implementation SelfUsedInParent
+ (int)getNum {return 5;}
+ (int)foo {
- return [self getNum];
+ int r = [self getNum];
+ clang_analyzer_eval(r == 5); // expected-warning{{TRUE}}
+ return r;
}
@end
@interface SelfUsedInParentChild : SelfUsedInParent
@@ -229,8 +232,80 @@
+ (void)forwardDeclaredVariadicMethod:(int)x, ... {
clang_analyzer_checkInlined(0); // no-warning
}
-
@end
+@interface SelfClassTestParent : NSObject
+-(unsigned)returns10;
++(unsigned)returns20;
++(unsigned)returns30;
+@end
+@implementation SelfClassTestParent
+-(unsigned)returns10 { return 100; }
++(unsigned)returns20 { return 100; }
++(unsigned)returns30 { return 100; }
+@end
+@interface SelfClassTest : SelfClassTestParent
+-(unsigned)returns10;
++(unsigned)returns20;
++(unsigned)returns30;
+@end
+
+@implementation SelfClassTest
+-(unsigned)returns10 { return 10; }
++(unsigned)returns20 { return 20; }
++(unsigned)returns30 { return 30; }
++(void)classMethod {
+ unsigned result1 = [self returns20];
+ clang_analyzer_eval(result1 == 20); // expected-warning{{TRUE}}
+ unsigned result2 = [[self class] returns30];
+ clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}}
+ unsigned result3 = [[super class] returns30];
+ clang_analyzer_eval(result3 == 100); // expected-warning{{UNKNOWN}}
+}
+-(void)instanceMethod {
+ unsigned result0 = [self returns10];
+ clang_analyzer_eval(result0 == 10); // expected-warning{{TRUE}}
+ unsigned result2 = [[self class] returns30];
+ clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}}
+ unsigned result3 = [[super class] returns30];
+ clang_analyzer_eval(result3 == 100); // expected-warning{{UNKNOWN}}
+}
+@end
+
+@interface Parent : NSObject
++ (int)a;
++ (int)b;
+@end
+@interface Child : Parent
+@end
+@interface Other : NSObject
++(void)run;
+@end
+int main(int argc, const char * argv[]) {
+ @autoreleasepool {
+ [Other run];
+ }
+ return 0;
+}
+@implementation Other
++(void)run {
+ int result = [Child a];
+ // TODO: This should return 100.
+ clang_analyzer_eval(result == 12); // expected-warning{{TRUE}}
+}
+@end
+@implementation Parent
++ (int)a; {
+ return [self b];
+}
++ (int)b; {
+ return 12;
+}
+@end
+@implementation Child
++ (int)b; {
+ return 100;
+}
+@end
diff --git a/test/Analysis/keychainAPI.m b/test/Analysis/keychainAPI.m
index 4fc48c0..618fe53 100644
--- a/test/Analysis/keychainAPI.m
+++ b/test/Analysis/keychainAPI.m
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=osx.SecKeychainAPI %s -verify
+// RUN: %clang_cc1 -analyze -analyzer-checker=osx.SecKeychainAPI -fblocks %s -verify
+
+#include "Inputs/system-header-simulator-objc.h"
// Fake typedefs.
typedef unsigned int OSStatus;
@@ -6,8 +8,6 @@
typedef unsigned int SecKeychainItemRef;
typedef unsigned int SecItemClass;
typedef unsigned int UInt32;
-typedef unsigned int CFTypeRef;
-typedef unsigned int UInt16;
typedef unsigned int SecProtocolType;
typedef unsigned int SecAuthenticationType;
typedef unsigned int SecKeychainAttributeInfo;
@@ -77,7 +77,7 @@
void *outData;
st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData);
if (st == GenericError)
- SecKeychainItemFreeContent(ptr, outData); // expected-warning{{Only call free if a valid (non-NULL) buffer was returned}}
+ SecKeychainItemFreeContent(ptr, outData);
} // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'}}
// If null is passed in, the data is not allocated, so no need for the matching free.
@@ -101,14 +101,6 @@
SecKeychainItemFreeContent(ptr, outData);
}
-void fooOnlyFree() {
- unsigned int *ptr = 0;
- OSStatus st = 0;
- UInt32 length;
- void *outData = &length;
- SecKeychainItemFreeContent(ptr, outData);// expected-warning{{Trying to free data which has not been allocated}}
-}
-
// Do not warn if undefined value is passed to a function.
void fooOnlyFreeUndef() {
unsigned int *ptr = 0;
@@ -220,11 +212,27 @@
if (st == noErr)
SecKeychainItemFreeContent(ptr, outData[3]);
}
- if (length) { // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'}}
+ if (length) { // TODO: We do not report a warning here since the symbol is no longer live, but it's not marked as dead.
length++;
}
return 0;
-}// no-warning
+}
+
+int testErrorCodeAsLHS(CFTypeRef keychainOrArray, SecProtocolType protocol,
+ SecAuthenticationType authenticationType, SecKeychainItemRef *itemRef) {
+ unsigned int *ptr = 0;
+ OSStatus st = 0;
+ UInt32 length;
+ void *outData;
+ st = SecKeychainFindInternetPassword(keychainOrArray,
+ 16, "server", 16, "domain", 16, "account",
+ 16, "path", 222, protocol, authenticationType,
+ &length, &outData, itemRef);
+ if (noErr == st)
+ SecKeychainItemFreeContent(ptr, outData);
+
+ return 0;
+}
void free(void *ptr);
void deallocateWithFree() {
@@ -251,7 +259,6 @@
extern const CFAllocatorRef kCFAllocatorNull;
extern const CFAllocatorRef kCFAllocatorUseContext;
CFStringRef CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const uint8_t *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean externalFormat, CFAllocatorRef contentsDeallocator);
-extern void CFRelease(CFStringRef cf);
void DellocWithCFStringCreate1(CFAllocatorRef alloc) {
unsigned int *ptr = 0;
@@ -333,11 +340,11 @@
SecKeychainItemFreeContent(0, pwdBytes);
}
-void radar10508828_2() {
+void radar10508828_20092614() {
UInt32 pwdLen = 0;
void* pwdBytes = 0;
OSStatus rc = SecKeychainFindGenericPassword(0, 3, "foo", 3, "bar", &pwdLen, &pwdBytes, 0);
- SecKeychainItemFreeContent(0, pwdBytes); // expected-warning {{Only call free if a valid (non-NULL) buffer was returned}}
+ SecKeychainItemFreeContent(0, pwdBytes);
}
//Example from bug 10797.
@@ -426,3 +433,24 @@
SecKeychainItemFreeContent(attrList, outData);
}
+typedef struct AuthorizationValue {
+ int length;
+ void *data;
+} AuthorizationValue;
+typedef struct AuthorizationCallback {
+ OSStatus (*SetContextVal)(AuthorizationValue *inValue);
+} AuthorizationCallback;
+static AuthorizationCallback cb;
+int radar_19196494() {
+ @autoreleasepool {
+ AuthorizationValue login_password = {};
+ UInt32 passwordLength;
+ void *passwordData = 0;
+ OSStatus err = SecKeychainFindGenericPassword(0, 0, "", 0, "", (UInt32 *)&login_password.length, (void**)&login_password.data, 0);
+ cb.SetContextVal(&login_password);
+ if (err == noErr) {
+ SecKeychainItemFreeContent(0, login_password.data);
+ }
+ }
+ return 0;
+}
diff --git a/test/Analysis/ns_error_enum.m b/test/Analysis/ns_error_enum.m
new file mode 100644
index 0000000..bf61629
--- /dev/null
+++ b/test/Analysis/ns_error_enum.m
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -verify %s
+
+#define CF_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
+#define NS_ENUM(_type, _name) CF_ENUM(_type, _name)
+
+#define NS_ERROR_ENUM(_type, _name, _domain) \
+ enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type
+
+typedef NS_ENUM(unsigned, MyEnum) {
+ MyFirst,
+ MySecond,
+};
+
+typedef NS_ENUM(invalidType, MyInvalidEnum) {
+// expected-error@-1{{unknown type name 'invalidType'}}
+// expected-error@-2{{unknown type name 'invalidType'}}
+ MyFirstInvalid,
+ MySecondInvalid,
+};
+
+const char *MyErrorDomain;
+typedef NS_ERROR_ENUM(unsigned char, MyErrorEnum, MyErrorDomain) {
+ MyErrFirst,
+ MyErrSecond,
+};
+struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructErrorDomain {};
+
+typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) {
+ // expected-error@-1{{domain argument 'InvalidDomain' does not refer to global constant}}
+ MyErrFirstInvalid,
+ MyErrSecondInvalid,
+};
+
+typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, "domain-string");
+ // expected-error@-1{{domain argument must be an identifier}}
+
+int __attribute__((ns_error_domain(MyErrorDomain))) NotTagDecl;
+ // expected-error@-1{{ns_error_domain attribute only valid on enums, structs, and unions}}
+
+void foo() {}
+typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalidFunction, foo);
+ // expected-error@-1{{domain argument 'foo' does not refer to global constant}}
diff --git a/test/Analysis/virtualcall.cpp b/test/Analysis/virtualcall.cpp
index e42b898..311f0a1 100644
--- a/test/Analysis/virtualcall.cpp
+++ b/test/Analysis/virtualcall.cpp
@@ -115,12 +115,23 @@
int foo() override;
};
+// Regression test: don't crash when there's no direct callee.
+class F {
+public:
+ F() {
+ void (F::* ptr)() = &F::foo;
+ (this->*ptr)();
+ }
+ void foo();
+};
+
int main() {
A *a;
B *b;
C *c;
D *d;
E *e;
+ F *f;
}
#include "virtualcall.h"
diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3.cpp
index f381ed7..ade3274 100644
--- a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3.cpp
+++ b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3.cpp
@@ -150,35 +150,44 @@
void f(T);
f(T{0});
+
+ char c;
+ auto t3 = T{c};
}
#if __cplusplus <= 201402L
- // expected-error@-15 5{{cannot initialize}}
- // expected-error@-15 5{{cannot initialize}}
- // expected-error@-15 5{{cannot initialize}}
+ // expected-error@-18 5{{cannot initialize}}
+ // expected-error@-18 5{{cannot initialize}}
+ // expected-error@-18 5{{cannot initialize}}
//
//
- // expected-error@-15 5{{cannot initialize}}
+ // expected-error@-18 5{{cannot initialize}}
//
- // expected-error@-15 5{{cannot initialize}}
+ // expected-error@-18 5{{cannot initialize}}
//
- // expected-error@-15 5{{cannot initialize}}
+ // expected-error@-18 5{{cannot initialize}}
//
//
- // expected-error@-15 5{{cannot initialize}}
+ // expected-error@-18 5{{cannot initialize}}
+ //
+ //
+ // expected-error@-18 5{{cannot initialize}}
#else
- // expected-error@-29 {{cannot initialize}}
- // expected-error@-29 {{cannot initialize}}
- // expected-error@-29 {{cannot initialize}}
+ // expected-error@-35 {{cannot initialize}}
+ // expected-error@-35 {{cannot initialize}}
+ // expected-error@-35 {{cannot initialize}}
//
//
- // expected-error@-29 {{cannot initialize}}
+ // expected-error@-35 {{cannot initialize}}
//
- // expected-error@-29 {{cannot initialize}}
+ // expected-error@-35 {{cannot initialize}}
//
- // expected-error@-29 {{cannot initialize}}
+ // expected-error@-35 {{cannot initialize}}
//
//
- // expected-error@-29 {{cannot initialize}}
+ // expected-error@-35 {{cannot initialize}}
+ //
+ //
+ // expected-error@-35 {{cannot initialize}}
#endif
template<typename T> void bad() {
@@ -252,4 +261,12 @@
(void)B{0.0}; // expected-error {{type 'double' cannot be narrowed}}
#endif
}
+
+#if __cplusplus > 201402L
+ enum class F : unsigned {};
+ F f1(unsigned x) { return F{x}; }
+ F f2(const unsigned x) { return F{x}; }
+ F f3(bool x) { return F{x}; }
+ F f4(const bool x) { return F{x}; }
+#endif
}
diff --git a/test/CXX/special/class.dtor/p10-0x.cpp b/test/CXX/special/class.dtor/p10-0x.cpp
index 3b8a0ad..3be0a98 100644
--- a/test/CXX/special/class.dtor/p10-0x.cpp
+++ b/test/CXX/special/class.dtor/p10-0x.cpp
@@ -33,7 +33,7 @@
expected-error{{the type of object expression ('int') does not match the type being destroyed ('decltype(intp())' (aka 'int *')) in pseudo-destructor expression}}
i.~decltype(intp())(); // expected-error{{the type of object expression ('int') does not match the type being destroyed ('decltype(intp())' (aka 'int *')) in pseudo-destructor expression}}
pi->~decltype(int())();
- pi.~decltype(int())(); // expected-error{{the type of object expression ('int *') does not match the type being destroyed ('decltype(int())' (aka 'int')) in pseudo-destructor expression}}
+ pi.~decltype(int())(); // expected-error{{member reference type 'int *' is a pointer; did you mean to use '->'?}}
pi.~decltype(intp())();
pi->~decltype(intp())(); // expected-error{{the type of object expression ('int') does not match the type being destroyed ('decltype(intp())' (aka 'int *')) in pseudo-destructor expression}}
}
diff --git a/test/CodeCompletion/auto_type.c b/test/CodeCompletion/auto_type.c
new file mode 100644
index 0000000..3fcfff0
--- /dev/null
+++ b/test/CodeCompletion/auto_type.c
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -code-completion-at=%s:3:1 %s | FileCheck %s
+void func() {
+
+}
+// CHECK: COMPLETION: __auto_type
diff --git a/test/CodeCompletion/keywords.cpp b/test/CodeCompletion/keywords.cpp
new file mode 100644
index 0000000..6e5824c
--- /dev/null
+++ b/test/CodeCompletion/keywords.cpp
@@ -0,0 +1,79 @@
+int function(int x) {
+ return x + 1;
+}
+
+int variable = 0;
+
+class Class {
+public:
+ Class() { }
+
+ int method(int x) {
+ return x + 1;
+ }
+
+ virtual void virtualMethod() {
+ }
+
+ static void staticMethod() {
+ }
+
+ static int staticVar;
+};
+
+class SubClass : public Class {
+ void virtualMethod() override final {
+ }
+};
+
+struct Struct {
+};
+
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:1:1 %s | FileCheck --check-prefix=CHECK-TOP-LEVEL %s
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:5:1 %s | FileCheck --check-prefix=CHECK-TOP-LEVEL %s
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:11:1 %s | FileCheck --check-prefix=CHECK-TOP-LEVEL %s
+// CHECK-TOP-LEVEL: alignas(<#expression#>)
+// CHECK-TOP-LEVEL: constexpr
+// CHECK-TOP-LEVEL: static_assert(<#expression#>, <#message#>)
+// CHECK-TOP-LEVEL: thread_local
+// CHECK-TOP-LEVEL-NOT: final
+// CHECK-TOP-LEVEL-NOT: noexcept
+
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:1:14 %s | FileCheck --check-prefix=CHECK-PARAM %s
+// CHECK-PARAM-NOT: alignas
+// CHECK-PARAM-NOT: constexpr
+// CHECK-PARAM-NOT: final
+// CHECK-PARAM-NOT: thread_local
+
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:21:10 %s | FileCheck --check-prefix=CHECK-STATICVAR1 %s
+// CHECK-STATICVAR1: constexpr
+// CHECK-STATICVAR1: thread_local
+
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:7:13 %s | FileCheck --check-prefix=CHECK-CLASS-QUALIFIER %s
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:24:16 %s | FileCheck --check-prefix=CHECK-CLASS-QUALIFIER %s
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:29:15 %s | FileCheck --check-prefix=CHECK-CLASS-QUALIFIER %s
+// CHECK-CLASS-QUALIFIER: final
+
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:1:21 %s | FileCheck --check-prefix=CHECK-FUNCTION-QUALIFIER %s
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:9:11 %s | FileCheck --check-prefix=CHECK-FUNCTION-QUALIFIER %s
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:18:30 %s | FileCheck --check-prefix=CHECK-FUNCTION-QUALIFIER %s
+// CHECK-FUNCTION-QUALIFIER: noexcept
+// CHECK-FUNCTION-QUALIFIER-NOT: final
+// CHECK-FUNCTION-QUALIFIER-NOT: override
+
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:11:21 %s | FileCheck --check-prefix=CHECK-METHOD-QUALIFIER %s
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:15:32 %s | FileCheck --check-prefix=CHECK-METHOD-QUALIFIER %s
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:25:24 %s | FileCheck --check-prefix=CHECK-METHOD-QUALIFIER %s
+// CHECK-METHOD-QUALIFIER: final
+// CHECK-METHOD-QUALIFIER: noexcept
+// CHECK-METHOD-QUALIFIER: override
+
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:25:33 %s | FileCheck --check-prefix=CHECK-OVERRIDE-SPECIFIED %s
+// CHECK-OVERRIDE-SPECIFIED: final
+// CHECK-OVERRIDE-SPECIFIED: noexcept
+// CHECK-OVERRIDE-SPECIFIED-NOT: override
+
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:25:39 %s | FileCheck --check-prefix=CHECK-OVERRIDE-FINAL-SPECIFIED %s
+// CHECK-OVERRIDE-FINAL-SPECIFIED: noexcept
+// CHECK-OVERRIDE-FINAL-SPECIFIED-NOT: final
+// CHECK-OVERRIDE-FINAL-SPECIFIED-NOT: override
diff --git a/test/CodeCompletion/ordinary-name-cxx11.cpp b/test/CodeCompletion/ordinary-name-cxx11.cpp
index 8e6f383..34c3bf9 100644
--- a/test/CodeCompletion/ordinary-name-cxx11.cpp
+++ b/test/CodeCompletion/ordinary-name-cxx11.cpp
@@ -39,10 +39,12 @@
// CHECK-CC1-NEXT: COMPLETION: Pattern : [#size_t#]sizeof(<#expression-or-type#>)
// CHECK-CC1-NEXT: COMPLETION: Pattern : [#size_t#]sizeof...(<#parameter-pack#>)
// CHECK-CC1-NEXT: COMPLETION: static
+ // CHECK-CC1-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>)
// CHECK-CC1-NEXT: COMPLETION: Pattern : static_cast<<#type#>>(<#expression#>)
// CHECK-CC1-NEXT: COMPLETION: struct
// CHECK-CC1-NEXT: COMPLETION: Pattern : switch(<#condition#>){
// CHECK-CC1: COMPLETION: t : t
+ // CHECK-CC1-NEXT: COMPLETION: thread_local
// CHECK-CC1-NEXT: COMPLETION: Pattern : [#void#]throw <#expression#>
// CHECK-CC1-NEXT: COMPLETION: Pattern : [#bool#]true
// CHECK-CC1-NEXT: COMPLETION: Pattern : try{<#statements#>
@@ -72,6 +74,7 @@
// CHECK-CC2-NEXT: COMPLETION: char32
// CHECK-CC2-NEXT: COMPLETION: class
// CHECK-CC2-NEXT: COMPLETION: const
+ // CHECK-CC2-NEXT: COMPLETION: constexpr
// CHECK-CC2-NEXT: COMPLETION: Pattern : decltype(<#expression#>)
// CHECK-CC2-NEXT: COMPLETION: double
// CHECK-CC2-NEXT: COMPLETION: enum
@@ -86,10 +89,12 @@
// CHECK-CC2-NEXT: COMPLETION: short
// CHECK-CC2-NEXT: COMPLETION: signed
// CHECK-CC2-NEXT: COMPLETION: static
+ // CHECK-CC2-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>)
// CHECK-CC2-NEXT: COMPLETION: struct
// CHECK-CC2-NEXT: COMPLETION: t : t
// CHECK-CC2-NEXT: COMPLETION: Pattern : template <#declaration#>
// CHECK-CC2-NEXT: COMPLETION: Pattern : template<<#parameters#>>
+ // CHECK-CC2-NEXT: COMPLETION: thread_local
// CHECK-CC2-NEXT: COMPLETION: TYPEDEF : TYPEDEF
// CHECK-CC2-NEXT: COMPLETION: Pattern : typedef <#type#> <#name#>
// CHECK-CC2-NEXT: COMPLETION: Pattern : typename <#qualifier#>::<#name#>
@@ -111,6 +116,7 @@
// CHECK-CC3-NEXT: COMPLETION: char32_t
// CHECK-CC3-NEXT: COMPLETION: class
// CHECK-CC3-NEXT: COMPLETION: const
+ // CHECK-CC3-NEXT: COMPLETION: constexpr
// CHECK-CC3-NEXT: COMPLETION: Pattern : decltype(<#expression#>)
// CHECK-CC3-NEXT: COMPLETION: double
// CHECK-CC3-NEXT: COMPLETION: enum
@@ -129,8 +135,10 @@
// CHECK-CC3-NEXT: COMPLETION: short
// CHECK-CC3-NEXT: COMPLETION: signed
// CHECK-CC3-NEXT: COMPLETION: static
+ // CHECK-CC3-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>)
// CHECK-CC3-NEXT: COMPLETION: struct
// CHECK-CC3-NEXT: COMPLETION: Pattern : template<<#parameters#>>
+ // CHECK-CC3-NEXT: COMPLETION: thread_local
// CHECK-CC3-NEXT: COMPLETION: Pattern : typedef <#type#> <#name#>
// CHECK-CC3-NEXT: COMPLETION: Pattern : typename <#qualifier#>::<#name#>
// CHECK-CC3-NEXT: COMPLETION: Pattern : typeof <#expression#>
@@ -227,6 +235,7 @@
// CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : [#size_t#]sizeof(<#expression-or-type#>)
// CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : [#size_t#]sizeof...(<#parameter-pack#>)
// CHECK-NO-RTTI-NEXT: COMPLETION: static
+ // CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>)
// CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : static_cast<<#type#>>(<#expression#>)
// CHECK-NO-RTTI-NEXT: COMPLETION: struct
// CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : switch(<#condition#>){
diff --git a/test/CodeGen/lifetime2.c b/test/CodeGen/lifetime2.c
index 4374b3c..55c6d91 100644
--- a/test/CodeGen/lifetime2.c
+++ b/test/CodeGen/lifetime2.c
@@ -1,7 +1,7 @@
-// RUN: %clang -S -emit-llvm -o - -O2 %s | FileCheck %s -check-prefixes=CHECK,O2
-// RUN: %clang -S -emit-llvm -o - -O2 -Xclang -disable-lifetime-markers %s \
+// RUN: %clang_cc1 -S -emit-llvm -o - -O2 -disable-llvm-passes %s | FileCheck %s -check-prefixes=CHECK,O2
+// RUN: %clang_cc1 -S -emit-llvm -o - -O2 -disable-lifetime-markers %s \
// RUN: | FileCheck %s -check-prefixes=CHECK,O0
-// RUN: %clang -S -emit-llvm -o - -O0 %s | FileCheck %s -check-prefixes=CHECK,O0
+// RUN: %clang_cc1 -S -emit-llvm -o - -O0 %s | FileCheck %s -check-prefixes=CHECK,O0
extern int bar(char *A, int n);
@@ -25,13 +25,11 @@
char x;
l1:
bar(&x, 1);
- // O2: @llvm.lifetime.start(i64 5
- // O2: @llvm.lifetime.end(i64 5
char y[5];
bar(y, 5);
goto l1;
// Infinite loop
- // O2-NOT: @llvm.lifetime.end(i64 1
+ // O2-NOT: @llvm.lifetime.end(
}
// CHECK-LABEL: @goto_bypass
@@ -91,3 +89,27 @@
L:
bar(&x, 1);
}
+
+// O2-LABEL: @jump_backward_over_declaration(
+// O2: %[[p:.*]] = alloca i32*
+// O2: %[[v0:.*]] = bitcast i32** %[[p]] to i8*
+// O2: call void @llvm.lifetime.start(i64 {{.*}}, i8* %[[v0]])
+// O2-NOT: call void @llvm.lifetime.start(
+
+extern void foo2(int p);
+
+int jump_backward_over_declaration(int a) {
+ int *p = 0;
+label1:
+ if (p) {
+ foo2(*p);
+ return 0;
+ }
+
+ int i = 999;
+ if (a != 2) {
+ p = &i;
+ goto label1;
+ }
+ return -1;
+}
diff --git a/test/CodeGen/sanitize-thread-no-checking-at-run-time.m b/test/CodeGen/sanitize-thread-no-checking-at-run-time.m
index 098b7cf..3d862c0 100644
--- a/test/CodeGen/sanitize-thread-no-checking-at-run-time.m
+++ b/test/CodeGen/sanitize-thread-no-checking-at-run-time.m
@@ -1,5 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -x objective-c++ -emit-llvm -o - %s | FileCheck -check-prefix=WITHOUT %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -x objective-c++ -emit-llvm -o - %s -fsanitize=thread | FileCheck -check-prefix=TSAN %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -x objective-c++ -fblocks -emit-llvm -o - %s | FileCheck -check-prefix=WITHOUT %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -x objective-c++ -fblocks -emit-llvm -o - %s -fsanitize=thread | FileCheck -check-prefix=TSAN %s
+
+// WITHOUT-NOT: "sanitize_thread_no_checking_at_run_time"
__attribute__((objc_root_class))
@interface NSObject
@@ -26,9 +28,14 @@
}
@end
-// WITHOUT-NOT: "sanitize_thread_no_checking_at_run_time"
-
// TSAN: initialize{{.*}}) [[ATTR:#[0-9]+]]
// TSAN: dealloc{{.*}}) [[ATTR:#[0-9]+]]
// TSAN: cxx_destruct{{.*}}) [[ATTR:#[0-9]+]]
+
+void test2(id x) {
+ extern void test2_helper(id (^)(void));
+ test2_helper(^{ return x; });
+// TSAN: define internal void @__destroy_helper_block_(i8*) [[ATTR:#[0-9]+]]
+}
+
// TSAN: attributes [[ATTR]] = { noinline nounwind {{.*}} "sanitize_thread_no_checking_at_run_time" {{.*}} }
diff --git a/test/CodeGen/ubsan-null.c b/test/CodeGen/ubsan-null.c
new file mode 100644
index 0000000..6ddae8a
--- /dev/null
+++ b/test/CodeGen/ubsan-null.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -fsanitize=null -emit-llvm %s -o - | FileCheck %s
+
+struct A {
+ int a[2];
+ int b;
+};
+
+// CHECK-LABEL: @f1
+int *f1() {
+// CHECK-NOT: __ubsan_handle_type_mismatch
+// CHECK: ret
+// CHECK-SAME: getelementptr inbounds (%struct.A, %struct.A* null, i32 0, i32 1)
+ return &((struct A *)0)->b;
+}
+
+// CHECK-LABEL: @f2
+int f2() {
+// CHECK: __ubsan_handle_type_mismatch
+// CHECK: load
+// CHECK-SAME: getelementptr inbounds (%struct.A, %struct.A* null, i32 0, i32 1)
+// CHECK: ret
+ return ((struct A *)0)->b;
+}
diff --git a/test/CodeGen/ubsan-shift.c b/test/CodeGen/ubsan-shift.c
new file mode 100644
index 0000000..90c15d8
--- /dev/null
+++ b/test/CodeGen/ubsan-shift.c
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple=x86_64-apple-darwin -fsanitize=shift-exponent,shift-base -emit-llvm %s -o - | FileCheck %s
+
+// CHECK-LABEL: define i32 @f1
+int f1(int c, int shamt) {
+// CHECK: icmp ule i32 %{{.*}}, 31, !nosanitize
+// CHECK: icmp ule i32 %{{.*}}, 31, !nosanitize
+ return 1 << (c << shamt);
+}
+
+// CHECK-LABEL: define i32 @f2
+int f2(long c, int shamt) {
+// CHECK: icmp ule i32 %{{.*}}, 63, !nosanitize
+// CHECK: icmp ule i64 %{{.*}}, 31, !nosanitize
+ return 1 << (c << shamt);
+}
+
+// CHECK-LABEL: define i32 @f3
+unsigned f3(unsigned c, int shamt) {
+// CHECK: icmp ule i32 %{{.*}}, 31, !nosanitize
+// CHECK: icmp ule i32 %{{.*}}, 31, !nosanitize
+ return 1U << (c << shamt);
+}
+
+// CHECK-LABEL: define i32 @f4
+unsigned f4(unsigned long c, int shamt) {
+// CHECK: icmp ule i32 %{{.*}}, 63, !nosanitize
+// CHECK: icmp ule i64 %{{.*}}, 31, !nosanitize
+ return 1U << (c << shamt);
+}
+
+// CHECK-LABEL: define i32 @f5
+int f5(int c, long long shamt) {
+// CHECK: icmp ule i64 %{{[0-9]+}}, 31, !nosanitize
+//
+// CHECK: sub nuw nsw i32 31, %sh_prom, !nosanitize
+// CHECK: lshr i32 %{{.*}}, %shl.zeros, !nosanitize
+ return c << shamt;
+}
+
+// CHECK-LABEL: define i32 @f6
+int f6(int c, int shamt) {
+// CHECK: icmp ule i32 %[[WIDTH:.*]], 31, !nosanitize
+//
+// CHECK: sub nuw nsw i32 31, %[[WIDTH]], !nosanitize
+// CHECK: lshr i32 %{{.*}}, %shl.zeros, !nosanitize
+ return c << shamt;
+}
diff --git a/test/CodeGen/vector.c b/test/CodeGen/vector.c
index ebaea84..2e36dcc 100644
--- a/test/CodeGen/vector.c
+++ b/test/CodeGen/vector.c
@@ -60,23 +60,3 @@
extern __typeof(_mm_extract_epi16(_mm_setzero_si128(), 3)) check_result_int;
extern __typeof(_mm_extract_epi32(_mm_setzero_si128(), 3)) check_result_int;
}
-
-// Test some logic around our lax vector comparison rules with integers.
-
-typedef int vec_int1 __attribute__((vector_size(4)));
-vec_int1 lax_vector_compare1(int x, vec_int1 y) {
- y = x == y;
- return y;
-}
-
-// CHECK: define i32 @lax_vector_compare1(i32 {{.*}}, i32 {{.*}})
-// CHECK: icmp eq <1 x i32>
-
-typedef int vec_int2 __attribute__((vector_size(8)));
-vec_int2 lax_vector_compare2(long long x, vec_int2 y) {
- y = x == y;
- return y;
-}
-
-// CHECK: define void @lax_vector_compare2(<2 x i32>* {{.*sret.*}}, i64 {{.*}}, i64 {{.*}})
-// CHECK: icmp eq <2 x i32>
diff --git a/test/CodeGenCXX/explicit-instantiation.cpp b/test/CodeGenCXX/explicit-instantiation.cpp
index fb57118..85857fb 100644
--- a/test/CodeGenCXX/explicit-instantiation.cpp
+++ b/test/CodeGenCXX/explicit-instantiation.cpp
@@ -5,6 +5,9 @@
// This check logically is attached to 'template int S<int>::i;' below.
// CHECK: @_ZN1SIiE1iE = weak_odr global i32
+// This check is logically attached to 'template int ExportedStaticLocal::f<int>()' below.
+// CHECK-OPT: @_ZZN19ExportedStaticLocal1fIiEEvvE1i = linkonce_odr global
+
template<typename T, typename U, typename Result>
struct plus {
Result operator()(const T& t, const U& u) const;
@@ -153,3 +156,17 @@
template <typename T> void S<T>::g() {}
template <typename T> int S<T>::i;
template <typename T> void S<T>::S2::h() {}
+
+namespace ExportedStaticLocal {
+void sink(int&);
+template <typename T>
+inline void f() {
+ static int i;
+ sink(i);
+}
+// See the check line at the top of the file.
+extern template void f<int>();
+void use() {
+ f<int>();
+}
+}
diff --git a/test/CoverageMapping/unused_names.c b/test/CoverageMapping/unused_names.c
index a03d18b..49fa119 100644
--- a/test/CoverageMapping/unused_names.c
+++ b/test/CoverageMapping/unused_names.c
@@ -2,14 +2,15 @@
// RUN: FileCheck -input-file %t %s
// RUN: FileCheck -check-prefix=SYSHEADER -input-file %t %s
-// Since foo is never emitted, there should not be a profile name for it.
-
-// CHECK-DAG: @__profn_bar = {{.*}} [3 x i8] c"bar"
-// CHECK-DAG: @__profn_baz = {{.*}} [3 x i8] c"baz"
-// CHECK-DAG: @__profn_unused_names.c_qux = {{.*}} [18 x i8] c"unused_names.c:qux"
+// CHECK-DAG: @__profc_bar
// CHECK-DAG: @__llvm_prf_nm = private constant {{.*}}, section "{{.*}}__llvm_prf_names"
-// SYSHEADER-NOT: @__profn_foo =
+// These are never instantiated, so we shouldn't get counters for them.
+//
+// CHECK-NOT: @__profc_baz
+// CHECK-NOT: @__profc_unused_names.c_qux
+
+// SYSHEADER-NOT: @__profc_foo =
#ifdef IS_SYSHEADER
diff --git a/test/Driver/embed-bitcode.c b/test/Driver/embed-bitcode.c
index 36314e6..fa86c2c3 100644
--- a/test/Driver/embed-bitcode.c
+++ b/test/Driver/embed-bitcode.c
@@ -34,6 +34,13 @@
// CHECK-LTO-NOT: warning: argument unused during compilation: '-fembed-bitcode'
// CHECK-LTO-NOT: -cc1
// CHECK-LTO-NOT: -fembed-bitcode=all
+// RUN: touch %t.o
+// RUN: %clang -target armv7-apple-darwin -miphoneos-version-min=6.0 %t.o -fembed-bitcode -fembed-bitcode-marker -mlinker-version=277 2>&1 -### | FileCheck %s -check-prefix=CHECK-LTO-MARKER-277
+// RUN: %clang -target armv7-apple-darwin -miphoneos-version-min=6.0 %t.o -fembed-bitcode -fembed-bitcode-marker -mlinker-version=278 2>&1 -### | FileCheck %s -check-prefix=CHECK-LTO-MARKER-278
+// CHECK-LTO-MARKER-277-NOT: bitcode_process_mode
+// CHECK-LTO-MARKER-278: bitcode_process_mode
+
+
// RUN: %clang -c %s -fembed-bitcode-marker -fintegrated-as 2>&1 -### | FileCheck %s -check-prefix=CHECK-MARKER
// CHECK-MARKER: -cc1
diff --git a/test/Driver/frame-pointer-elim.c b/test/Driver/frame-pointer-elim.c
index e1d816e..e39499a 100644
--- a/test/Driver/frame-pointer-elim.c
+++ b/test/Driver/frame-pointer-elim.c
@@ -49,9 +49,9 @@
// RUN: %clang -### -target armv7s-apple-ios8.0 -momit-leaf-frame-pointer %s 2>&1 | \
// RUN: FileCheck --check-prefix=WARN-OMIT-LEAF-7S %s
-// WARN-OMIT-LEAF-7S: warning: optimization flag '-momit-leaf-frame-pointer' is not supported for target 'armv7s'
+// WARN-OMIT-LEAF-7S-NOT: warning: optimization flag '-momit-leaf-frame-pointer' is not supported for target 'armv7s'
// WARN-OMIT-LEAF-7S: "-mdisable-fp-elim"
-// WARN-OMIT-LEAF-7S-NOT: "-momit-leaf-frame-pointer"
+// WARN-OMIT-LEAF-7S: "-momit-leaf-frame-pointer"
// On the PS4, we default to omitting the frame pointer on leaf functions
// (OMIT_LEAF check line is above)
diff --git a/test/Driver/mglobal-merge.c b/test/Driver/mglobal-merge.c
index ad76736..271011e 100644
--- a/test/Driver/mglobal-merge.c
+++ b/test/Driver/mglobal-merge.c
@@ -11,7 +11,7 @@
// RUN: FileCheck --check-prefix=CHECK-NONE < %t %s
// CHECK-NGM-ARM: "-backend-option" "-arm-global-merge=false"
-// CHECK-NGM-AARCH64: "-backend-option" "-aarch64-global-merge=false"
+// CHECK-NGM-AARCH64: "-backend-option" "-aarch64-enable-global-merge=false"
// RUN: %clang -target armv7-unknown-unknown -### -fsyntax-only %s 2> %t \
// RUN: -mglobal-merge
@@ -26,7 +26,7 @@
// RUN: FileCheck --check-prefix=CHECK-NONE < %t %s
// CHECK-GM-ARM: "-backend-option" "-arm-global-merge=true"
-// CHECK-GM-AARCH64: "-backend-option" "-aarch64-global-merge=true"
+// CHECK-GM-AARCH64: "-backend-option" "-aarch64-enable-global-merge=true"
// RUN: %clang -target armv7-unknown-unknown -### -fsyntax-only %s 2> %t
// RUN: FileCheck --check-prefix=CHECK-NONE < %t %s
diff --git a/test/FixIt/fixit.cpp b/test/FixIt/fixit.cpp
index c43aac9..0b7fc62 100644
--- a/test/FixIt/fixit.cpp
+++ b/test/FixIt/fixit.cpp
@@ -409,3 +409,14 @@
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:26-[[@LINE-1]]:26}:"{}"
int use_czi = czi.a;
+namespace dotPointerDestructor {
+
+struct Bar {
+ ~Bar();
+};
+
+void bar(Bar *o) {
+ o.~Bar(); // expected-error {{member reference type 'dotPointerDestructor::Bar *' is a pointer; did you mean to use '->'}}
+} // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:4-[[@LINE-1]]:5}:"->"
+
+}
diff --git a/test/FixIt/no-fixit.cpp b/test/FixIt/no-fixit.cpp
index 7e8e1fb..2dad28d 100644
--- a/test/FixIt/no-fixit.cpp
+++ b/test/FixIt/no-fixit.cpp
@@ -11,3 +11,15 @@
(void)&i;
}
} x;
+
+namespace dotPointerDestructor {
+
+struct Bar {
+ ~Bar() = delete;
+};
+
+void bar(Bar *o) {
+ o.~Bar(); // no fixit
+}
+
+}
diff --git a/test/Frontend/objc-bool-is-bool.m b/test/Frontend/objc-bool-is-bool.m
index 464fe2e..ee4fb58 100644
--- a/test/Frontend/objc-bool-is-bool.m
+++ b/test/Frontend/objc-bool-is-bool.m
@@ -1,6 +1,8 @@
// RUN: %clang_cc1 -fsyntax-only -E -dM -triple=armv7k-apple-watchos %s | FileCheck --check-prefix=BOOL %s
// RUN: %clang_cc1 -fsyntax-only -E -dM -triple=x86_64-apple-darwin16 %s | FileCheck --check-prefix=CHAR %s
-// RUN: %clang_cc1 -x c -fsyntax-only -E -dM -triple=x86_64-apple-darwin16 %s | FileCheck --check-prefix=NONE %s
+// RUN: %clang_cc1 -x c -fsyntax-only -E -dM -triple=x86_64-apple-darwin16 %s | FileCheck --check-prefix=CHAR %s
+// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -E -dM -triple=x86_64-apple-darwin16 %s | FileCheck --check-prefix=CHAR %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -E -dM -triple=x86_64-apple-darwin16 %s | FileCheck --check-prefix=CHAR %s
// rdar://21170440
@@ -9,5 +11,3 @@
// CHAR: #define __OBJC_BOOL_IS_BOOL 0
// CHAR-NOT: #define __OBJC_BOOL_IS_BOOL 1
-
-// NONE-NOT: __OBJC_BOOL_IS_BOOL
diff --git a/test/Index/Core/index-pch.c b/test/Index/Core/index-pch.c
new file mode 100644
index 0000000..773cfc5
--- /dev/null
+++ b/test/Index/Core/index-pch.c
@@ -0,0 +1,13 @@
+// RUN: c-index-test core -print-source-symbols -- %s | FileCheck %s
+// RUN: %clang_cc1 -emit-pch %s -o %t.pch
+// RUN: c-index-test core -print-source-symbols -module-file %t.pch | FileCheck %s
+
+// CHECK: [[@LINE+1]]:6 | function/C | test1 | [[TEST1_USR:.*]] | [[TEST1_CG:.*]] | Decl | rel: 0
+void test1();
+
+// CHECK: [[@LINE+1]]:20 | function/C | test2 | [[TEST2_USR:.*]] | {{.*}} | Def | rel: 0
+static inline void test2() {
+ // CHECK: [[@LINE+2]]:3 | function/C | test1 | [[TEST1_USR]] | [[TEST1_CG]] | Ref,Call,RelCall,RelCont | rel: 1
+ // CHECK-NEXT: RelCall,RelCont | test2 | [[TEST2_USR]]
+ test1();
+}
diff --git a/test/Index/Core/index-with-module.m b/test/Index/Core/index-with-module.m
index e50b247..c83de63 100644
--- a/test/Index/Core/index-with-module.m
+++ b/test/Index/Core/index-with-module.m
@@ -1,5 +1,5 @@
// RUN: rm -rf %t.mcp
-// RUN: c-index-test core -print-source-symbols -- %s -I %S/Inputs/module -fmodules -fmodules-cache-path=%t.mcp | FileCheck %s
+// RUN: c-index-test core -print-source-symbols -dump-imported-module-files -- %s -I %S/Inputs/module -fmodules -fmodules-cache-path=%t.mcp | FileCheck %s
// CHECK: [[@LINE+1]]:9 | module/C | ModA | Decl |
@import ModA;
@@ -10,3 +10,9 @@
// CHECK: [[@LINE+1]]:3 | function/C | ModA_func | c:@F@ModA_func | {{.*}} | Ref,Call,RelCall,RelCont | rel: 1
ModA_func();
}
+
+// CHECK: ==== Module ModA ====
+// CHECK: 2:6 | function/C | ModA_func | c:@F@ModA_func | {{.*}} | Decl | rel: 0
+// CHECK: ---- Module Inputs ----
+// CHECK: user | {{.*}}ModA.h
+// CHECK: user | {{.*}}module.modulemap
diff --git a/test/Index/complete-cached-globals.cpp b/test/Index/complete-cached-globals.cpp
new file mode 100644
index 0000000..791faf2
--- /dev/null
+++ b/test/Index/complete-cached-globals.cpp
@@ -0,0 +1,25 @@
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test.
+
+namespace SomeNamespace {
+ class SomeClass {
+ };
+ void SomeFunction();
+}
+
+using SomeNamespace::SomeClass;
+using SomeNamespace::SomeFunction;
+
+static void foo() {
+ return;
+}
+
+// rdar://23454249
+
+// RUN: c-index-test -code-completion-at=%s:14:3 %s | FileCheck -check-prefix=CHECK-CC1 %s
+// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:14:3 %s | FileCheck -check-prefix=CHECK-CC1 %s
+
+// CHECK-CC1: ClassDecl:{TypedText SomeClass} (50)
+// CHECK-CC1: FunctionDecl:{ResultType void}{TypedText SomeFunction}{LeftParen (}{RightParen )} (50)
+// CHECK-CC1-NOT: {Text SomeNamespace::}{TypedText SomeClass}
+// CHECK-CC1-NOT: {Text SomeNamespace::}{TypedText SomeFunction}
diff --git a/test/Index/complete-objc-message.m b/test/Index/complete-objc-message.m
index e3fce6b..c2b0670 100644
--- a/test/Index/complete-objc-message.m
+++ b/test/Index/complete-objc-message.m
@@ -346,3 +346,54 @@
// RUN: c-index-test -code-completion-at=%s:197:6 %s | FileCheck -check-prefix=CHECK-NULLABLE %s
// CHECK-NULLABLE: ObjCInstanceMethodDecl:{ResultType A * _Nonnull}{TypedText method:}{Placeholder (nullable A *)}
+
+// rdar://28012953
+// Code completion results should include instance methods from RootProtocol and
+// RootClass when completing a method invocation for a RootClass object because
+// RootClasses metaclass subclasses from RootClass (i.e. RootClass is actually
+// an instance of RootClass).
+
+@protocol SubRootProtocol
+
+- (void)subProtocolInstanceMethod;
+
+@end
+
+@protocol RootProtocol <SubRootProtocol>
+
+- (void)protocolInstanceMethod;
++ (void)protocolClassMethod;
+
+@end
+
+@interface RootClass <RootProtocol>
+
+- (void)instanceMethod;
++ (void)classMethod;
+
+@end
+
+@protocol RootCategoryProtocol
+
+- (void)categoryProtocolInstanceMethod;
+
+@end
+
+@interface RootClass (Cat) <RootCategoryProtocol>
+
+- (void)categoryInstanceMethod;
+
+@end
+
+void completeAllTheRootThings() {
+ [RootClass classMethod];
+}
+
+// RUN: c-index-test -code-completion-at=%s:389:14 %s | FileCheck -check-prefix=CHECK-ROOT %s
+// CHECK-ROOT: ObjCInstanceMethodDecl:{ResultType void}{TypedText categoryInstanceMethod} (35)
+// CHECK-ROOT-NEXT: ObjCInstanceMethodDecl:{ResultType void}{TypedText categoryProtocolInstanceMethod} (37)
+// CHECK-ROOT-NEXT: ObjCClassMethodDecl:{ResultType void}{TypedText classMethod} (35)
+// CHECK-ROOT-NEXT: ObjCInstanceMethodDecl:{ResultType void}{TypedText instanceMethod} (35)
+// CHECK-ROOT-NEXT: ObjCClassMethodDecl:{ResultType void}{TypedText protocolClassMethod} (37)
+// CHECK-ROOT-NEXT: ObjCInstanceMethodDecl:{ResultType void}{TypedText protocolInstanceMethod} (37)
+// CHECK-ROOT-NEXT: ObjCInstanceMethodDecl:{ResultType void}{TypedText subProtocolInstanceMethod} (37)
diff --git a/test/Lexer/cxx-features.cpp b/test/Lexer/cxx-features.cpp
index 5fc1722..0af42dc 100644
--- a/test/Lexer/cxx-features.cpp
+++ b/test/Lexer/cxx-features.cpp
@@ -9,7 +9,6 @@
// expected-no-diagnostics
-// FIXME using `defined` in a macro has undefined behavior.
#if __cplusplus < 201103L
#define check(macro, cxx98, cxx11, cxx14, cxx1z) cxx98 == 0 ? defined(__cpp_##macro) : __cpp_##macro != cxx98
#elif __cplusplus < 201402L
diff --git a/test/Modules/Inputs/diag_pragma.h b/test/Modules/Inputs/diag_pragma.h
index a8f9589..59c73ea 100644
--- a/test/Modules/Inputs/diag_pragma.h
+++ b/test/Modules/Inputs/diag_pragma.h
@@ -1,3 +1,13 @@
#define DIAG_PRAGMA_MACRO 1
#pragma clang diagnostic ignored "-Wparentheses"
+
+#ifdef __cplusplus
+template<typename T> const char *f(T t) {
+ return "foo" + t;
+}
+#pragma clang diagnostic ignored "-Wstring-plus-int"
+template<typename T> const char *g(T t) {
+ return "foo" + t;
+}
+#endif
diff --git a/test/Modules/Inputs/import-textual/M/A/A.h b/test/Modules/Inputs/import-textual/M/A/A.h
deleted file mode 100644
index ebe4979..0000000
--- a/test/Modules/Inputs/import-textual/M/A/A.h
+++ /dev/null
@@ -1,4 +0,0 @@
-
-#import "someheader.h"
-
-typedef myint aint;
diff --git a/test/Modules/Inputs/import-textual/M/B/B.h b/test/Modules/Inputs/import-textual/M/B/B.h
deleted file mode 100644
index ba85071..0000000
--- a/test/Modules/Inputs/import-textual/M/B/B.h
+++ /dev/null
@@ -1,4 +0,0 @@
-
-#import "someheader.h"
-
-typedef myint bint;
diff --git a/test/Modules/Inputs/import-textual/M/module.modulemap b/test/Modules/Inputs/import-textual/M/module.modulemap
deleted file mode 100644
index f801948..0000000
--- a/test/Modules/Inputs/import-textual/M/module.modulemap
+++ /dev/null
@@ -1,17 +0,0 @@
-
-module M {
-
- module A {
- header "A/A.h"
- textual header "someheader.h"
- export *
- }
-
- module B {
- header "B/B.h"
- textual header "someheader.h"
- export *
- }
-
- export *
-}
diff --git a/test/Modules/Inputs/import-textual/M/someheader.h b/test/Modules/Inputs/import-textual/M/someheader.h
deleted file mode 100644
index 16fae40..0000000
--- a/test/Modules/Inputs/import-textual/M/someheader.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef C_GUARD
-#define C_GUARD
-
-typedef int myint;
-
-#endif
diff --git a/test/Modules/Inputs/import-textual/M2/A/A.h b/test/Modules/Inputs/import-textual/M2/A/A.h
deleted file mode 100644
index ebe4979..0000000
--- a/test/Modules/Inputs/import-textual/M2/A/A.h
+++ /dev/null
@@ -1,4 +0,0 @@
-
-#import "someheader.h"
-
-typedef myint aint;
diff --git a/test/Modules/Inputs/import-textual/M2/B/B.h b/test/Modules/Inputs/import-textual/M2/B/B.h
deleted file mode 100644
index ba85071..0000000
--- a/test/Modules/Inputs/import-textual/M2/B/B.h
+++ /dev/null
@@ -1,4 +0,0 @@
-
-#import "someheader.h"
-
-typedef myint bint;
diff --git a/test/Modules/Inputs/import-textual/M2/module.modulemap b/test/Modules/Inputs/import-textual/M2/module.modulemap
deleted file mode 100644
index f801948..0000000
--- a/test/Modules/Inputs/import-textual/M2/module.modulemap
+++ /dev/null
@@ -1,17 +0,0 @@
-
-module M {
-
- module A {
- header "A/A.h"
- textual header "someheader.h"
- export *
- }
-
- module B {
- header "B/B.h"
- textual header "someheader.h"
- export *
- }
-
- export *
-}
diff --git a/test/Modules/Inputs/import-textual/M2/someheader.h b/test/Modules/Inputs/import-textual/M2/someheader.h
deleted file mode 100644
index df2009a..0000000
--- a/test/Modules/Inputs/import-textual/M2/someheader.h
+++ /dev/null
@@ -1 +0,0 @@
-typedef int myint;
diff --git a/test/Modules/Inputs/incomplete-type/A.h b/test/Modules/Inputs/incomplete-type/A.h
new file mode 100644
index 0000000..6943aaf
--- /dev/null
+++ b/test/Modules/Inputs/incomplete-type/A.h
@@ -0,0 +1 @@
+#define __P(protos) ()
diff --git a/test/Modules/Inputs/incomplete-type/B.h b/test/Modules/Inputs/incomplete-type/B.h
new file mode 100644
index 0000000..51df71e
--- /dev/null
+++ b/test/Modules/Inputs/incomplete-type/B.h
@@ -0,0 +1,2 @@
+#import "A.h"
+#import "B2.h"
diff --git a/test/Modules/Inputs/incomplete-type/B2.h b/test/Modules/Inputs/incomplete-type/B2.h
new file mode 100644
index 0000000..def109f
--- /dev/null
+++ b/test/Modules/Inputs/incomplete-type/B2.h
@@ -0,0 +1,2 @@
+void test(int __P) {
+}
diff --git a/test/Modules/Inputs/incomplete-type/C.h b/test/Modules/Inputs/incomplete-type/C.h
new file mode 100644
index 0000000..ea6a2ee
--- /dev/null
+++ b/test/Modules/Inputs/incomplete-type/C.h
@@ -0,0 +1,2 @@
+#import "A.h"
+#import "B.h"
diff --git a/test/Modules/Inputs/incomplete-type/module.modulemap b/test/Modules/Inputs/incomplete-type/module.modulemap
new file mode 100644
index 0000000..17c8d7d
--- /dev/null
+++ b/test/Modules/Inputs/incomplete-type/module.modulemap
@@ -0,0 +1,15 @@
+module X {
+ header "A.h"
+ export *
+}
+
+// Y imports X
+module Y {
+ header "B.h"
+ export *
+}
+
+// Z imports X and Y
+module Z {
+ header "C.h"
+}
diff --git a/test/Modules/Inputs/invalid-module-id/NC-Prefix.pch b/test/Modules/Inputs/invalid-module-id/NC-Prefix.pch
new file mode 100644
index 0000000..73a9816
--- /dev/null
+++ b/test/Modules/Inputs/invalid-module-id/NC-Prefix.pch
@@ -0,0 +1,3 @@
+#ifdef __OBJC__
+ #import <NC/NULog.h>
+#endif
diff --git a/test/Modules/Inputs/invalid-module-id/NC.framework/Headers/NC.h b/test/Modules/Inputs/invalid-module-id/NC.framework/Headers/NC.h
new file mode 100644
index 0000000..3866c88
--- /dev/null
+++ b/test/Modules/Inputs/invalid-module-id/NC.framework/Headers/NC.h
@@ -0,0 +1 @@
+#import <NC/NUGeometry.h>
diff --git a/test/Modules/Inputs/invalid-module-id/NC.framework/Headers/NU-Visibility.h b/test/Modules/Inputs/invalid-module-id/NC.framework/Headers/NU-Visibility.h
new file mode 100644
index 0000000..1e7614c
--- /dev/null
+++ b/test/Modules/Inputs/invalid-module-id/NC.framework/Headers/NU-Visibility.h
@@ -0,0 +1 @@
+// NU-Visibility.h
diff --git a/test/Modules/Inputs/invalid-module-id/NC.framework/Headers/NUGeometry.h b/test/Modules/Inputs/invalid-module-id/NC.framework/Headers/NUGeometry.h
new file mode 100644
index 0000000..8923e04
--- /dev/null
+++ b/test/Modules/Inputs/invalid-module-id/NC.framework/Headers/NUGeometry.h
@@ -0,0 +1 @@
+#import <NC/NU-Visibility.h>
diff --git a/test/Modules/Inputs/invalid-module-id/NC.framework/Modules/module.modulemap b/test/Modules/Inputs/invalid-module-id/NC.framework/Modules/module.modulemap
new file mode 100644
index 0000000..475b414
--- /dev/null
+++ b/test/Modules/Inputs/invalid-module-id/NC.framework/Modules/module.modulemap
@@ -0,0 +1,6 @@
+framework module NC {
+ umbrella header "NC.h"
+
+ export *
+ module * { export * }
+}
diff --git a/test/Modules/Inputs/invalid-module-id/NC.framework/Modules/module.private.modulemap b/test/Modules/Inputs/invalid-module-id/NC.framework/Modules/module.private.modulemap
new file mode 100644
index 0000000..80488bd
--- /dev/null
+++ b/test/Modules/Inputs/invalid-module-id/NC.framework/Modules/module.private.modulemap
@@ -0,0 +1,5 @@
+explicit module NC.Private
+{
+ header "NULog.h"
+ header "NUAssert.h"
+}
diff --git a/test/Modules/Inputs/invalid-module-id/NC.framework/PrivateHeaders/NUAssert.h b/test/Modules/Inputs/invalid-module-id/NC.framework/PrivateHeaders/NUAssert.h
new file mode 100644
index 0000000..7bf8def
--- /dev/null
+++ b/test/Modules/Inputs/invalid-module-id/NC.framework/PrivateHeaders/NUAssert.h
@@ -0,0 +1 @@
+#import <NC/NULog.h>
diff --git a/test/Modules/Inputs/invalid-module-id/NC.framework/PrivateHeaders/NULog.h b/test/Modules/Inputs/invalid-module-id/NC.framework/PrivateHeaders/NULog.h
new file mode 100644
index 0000000..8923e04
--- /dev/null
+++ b/test/Modules/Inputs/invalid-module-id/NC.framework/PrivateHeaders/NULog.h
@@ -0,0 +1 @@
+#import <NC/NU-Visibility.h>
diff --git a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/cstddef b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/cstddef
deleted file mode 100644
index 4898c05..0000000
--- a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/cstddef
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _LIBCPP_CSTDDEF
-#define _LIBCPP_CSTDDEF
-
-#include <stddef.h>
-#include <type_traits>
-
-typedef ptrdiff_t my_ptrdiff_t;
-
-#endif
diff --git a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/math.h b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/math.h
index 9e2b693..f761b91 100644
--- a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/math.h
+++ b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/math.h
@@ -4,6 +4,4 @@
#include_next <math.h>
template<typename T> T abs(T t) { return (t < 0) ? -t : t; }
-#include <type_traits>
-
#endif
diff --git a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/module.modulemap b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/module.modulemap
index f57c11c..b06142a 100644
--- a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/module.modulemap
+++ b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/module.modulemap
@@ -6,7 +6,5 @@
// FIXME: remove "textual" from stdint module below once the issue
// between umbrella headers and builtins is resolved.
module stdint { textual header "stdint.h" export * }
- module type_traits { header "type_traits" export * }
- module cstddef { header "cstddef" export * }
module __config { header "__config" export * }
}
diff --git a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/stddef.h b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/stddef.h
index 14167cf..bd42008 100644
--- a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/stddef.h
+++ b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/stddef.h
@@ -2,6 +2,5 @@
#define LIBCXX_STDDEF_H
#include <__config>
-#include_next <stddef.h>
#endif
diff --git a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/type_traits b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/type_traits
deleted file mode 100644
index a91056e..0000000
--- a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/c++/v1/type_traits
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _LIBCPP_TYPE_TRAITS
-#define _LIBCPP_TYPE_TRAITS
-
-#include <cstddef>
-
-#endif
diff --git a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/module.modulemap b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/module.modulemap
index 25b9468..7244cb0 100644
--- a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/module.modulemap
+++ b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/module.modulemap
@@ -5,12 +5,4 @@
module stdint { header "stdint.h" export * }
module stdio { header "stdio.h" export * }
module util { header "util.h" export * }
- module POSIX {
- module sys {
- module types {
- umbrella header "sys/_types/_types.h"
- export *
- }
- }
- }
}
diff --git a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/stddef.h b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/stddef.h
index b98249f..eca7241 100644
--- a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/stddef.h
+++ b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/stddef.h
@@ -1,6 +1 @@
-#ifndef __STDDEF_H__
-#define __STDDEF_H__
-
-#include "sys/_types/_ptrdiff_t.h"
-
-#endif
+// stddef.h
diff --git a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/sys/_types/_ptrdiff_t.h b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/sys/_types/_ptrdiff_t.h
deleted file mode 100644
index d14110e..0000000
--- a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/sys/_types/_ptrdiff_t.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef _PTRDIFF_T
-#define _PTRDIFF_T
-typedef int * ptrdiff_t;
-#endif /* _PTRDIFF_T */
diff --git a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/sys/_types/_types.h b/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/sys/_types/_types.h
deleted file mode 100644
index 33d5e51..0000000
--- a/test/Modules/Inputs/libc-libcxx/sysroot/usr/include/sys/_types/_types.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _SYS_TYPES_UMBRELLA
-#define _SYS_TYPES_UMBRELLA
-
-#include "_ptrdiff_t.h"
-
-#endif
diff --git a/test/Modules/Inputs/outofdate-rebuild/AppKit.h b/test/Modules/Inputs/outofdate-rebuild/AppKit.h
new file mode 100644
index 0000000..e357918
--- /dev/null
+++ b/test/Modules/Inputs/outofdate-rebuild/AppKit.h
@@ -0,0 +1,3 @@
+// AppKit
+#import "CoreVideo.h" // CoreVideo
+struct B { int i; };
diff --git a/test/Modules/Inputs/outofdate-rebuild/Cocoa.h b/test/Modules/Inputs/outofdate-rebuild/Cocoa.h
new file mode 100644
index 0000000..d631140
--- /dev/null
+++ b/test/Modules/Inputs/outofdate-rebuild/Cocoa.h
@@ -0,0 +1,5 @@
+// Cocoa
+#import "Foundation.h"
+#import "AppKit.h"
+
+struct A { int i; };
diff --git a/test/Modules/Inputs/outofdate-rebuild/CoreText.h b/test/Modules/Inputs/outofdate-rebuild/CoreText.h
new file mode 100644
index 0000000..7ff0e23
--- /dev/null
+++ b/test/Modules/Inputs/outofdate-rebuild/CoreText.h
@@ -0,0 +1 @@
+struct C { int i; };
diff --git a/test/Modules/Inputs/outofdate-rebuild/CoreVideo.h b/test/Modules/Inputs/outofdate-rebuild/CoreVideo.h
new file mode 100644
index 0000000..bd249dc
--- /dev/null
+++ b/test/Modules/Inputs/outofdate-rebuild/CoreVideo.h
@@ -0,0 +1,3 @@
+// CoreVideo
+#import "Foundation.h" // Foundation
+struct E { int i; };
diff --git a/test/Modules/Inputs/outofdate-rebuild/Foundation.h b/test/Modules/Inputs/outofdate-rebuild/Foundation.h
new file mode 100644
index 0000000..b2d053a
--- /dev/null
+++ b/test/Modules/Inputs/outofdate-rebuild/Foundation.h
@@ -0,0 +1,3 @@
+// Foundation
+#import "CoreText.h"
+struct D { int i; };
diff --git a/test/Modules/Inputs/outofdate-rebuild/module.modulemap b/test/Modules/Inputs/outofdate-rebuild/module.modulemap
new file mode 100644
index 0000000..71c99e8
--- /dev/null
+++ b/test/Modules/Inputs/outofdate-rebuild/module.modulemap
@@ -0,0 +1,19 @@
+module Cocoa {
+ header "Cocoa.h"
+}
+
+module AppKit {
+ header "AppKit.h"
+}
+
+module CoreText {
+ header "CoreText.h"
+}
+
+module Foundation {
+ header "Foundation.h"
+}
+
+module CoreVideo {
+ header "CoreVideo.h"
+}
diff --git a/test/Modules/Inputs/shadow/A1/A.h b/test/Modules/Inputs/shadow/A1/A.h
new file mode 100644
index 0000000..f07c681
--- /dev/null
+++ b/test/Modules/Inputs/shadow/A1/A.h
@@ -0,0 +1 @@
+#define A1_A_h
diff --git a/test/Modules/Inputs/shadow/A1/module.modulemap b/test/Modules/Inputs/shadow/A1/module.modulemap
new file mode 100644
index 0000000..9439a43
--- /dev/null
+++ b/test/Modules/Inputs/shadow/A1/module.modulemap
@@ -0,0 +1,5 @@
+module A {
+ header "A.h"
+}
+
+module A1 {}
diff --git a/test/Modules/Inputs/shadow/A2/A.h b/test/Modules/Inputs/shadow/A2/A.h
new file mode 100644
index 0000000..9880ed0
--- /dev/null
+++ b/test/Modules/Inputs/shadow/A2/A.h
@@ -0,0 +1 @@
+#define A2_A_h
diff --git a/test/Modules/Inputs/shadow/A2/module.modulemap b/test/Modules/Inputs/shadow/A2/module.modulemap
new file mode 100644
index 0000000..935d89b
--- /dev/null
+++ b/test/Modules/Inputs/shadow/A2/module.modulemap
@@ -0,0 +1,5 @@
+module A {
+ header "A.h"
+}
+
+module A2 {}
diff --git a/test/Modules/Inputs/swift_name/module.modulemap b/test/Modules/Inputs/swift_name/module.modulemap
new file mode 100644
index 0000000..b7ec6b1
--- /dev/null
+++ b/test/Modules/Inputs/swift_name/module.modulemap
@@ -0,0 +1,2 @@
+module SwiftNameInferred [swift_infer_import_as_member] {
+}
\ No newline at end of file
diff --git a/test/Modules/Inputs/system-out-of-date/X.h b/test/Modules/Inputs/system-out-of-date/X.h
new file mode 100644
index 0000000..edcfa18
--- /dev/null
+++ b/test/Modules/Inputs/system-out-of-date/X.h
@@ -0,0 +1 @@
+#import <Y.h>
diff --git a/test/Modules/Inputs/system-out-of-date/Y.h b/test/Modules/Inputs/system-out-of-date/Y.h
new file mode 100644
index 0000000..90fe1bc
--- /dev/null
+++ b/test/Modules/Inputs/system-out-of-date/Y.h
@@ -0,0 +1 @@
+//empty
diff --git a/test/Modules/Inputs/system-out-of-date/Z.h b/test/Modules/Inputs/system-out-of-date/Z.h
new file mode 100644
index 0000000..edcfa18
--- /dev/null
+++ b/test/Modules/Inputs/system-out-of-date/Z.h
@@ -0,0 +1 @@
+#import <Y.h>
diff --git a/test/Modules/Inputs/system-out-of-date/module.map b/test/Modules/Inputs/system-out-of-date/module.map
new file mode 100644
index 0000000..0c0f42a
--- /dev/null
+++ b/test/Modules/Inputs/system-out-of-date/module.map
@@ -0,0 +1,12 @@
+module X [system] {
+ header "X.h" // imports Y
+ export *
+}
+module Y {
+ header "Y.h"
+ export *
+}
+module Z {
+ header "Z.h" // imports Y
+ export *
+}
diff --git a/test/Modules/Inputs/warning-mismatch/Mismatch.h b/test/Modules/Inputs/warning-mismatch/Mismatch.h
new file mode 100644
index 0000000..a07b0ee
--- /dev/null
+++ b/test/Modules/Inputs/warning-mismatch/Mismatch.h
@@ -0,0 +1 @@
+struct Mismatch { int i; };
diff --git a/test/Modules/Inputs/warning-mismatch/System.h b/test/Modules/Inputs/warning-mismatch/System.h
new file mode 100644
index 0000000..8e69e70
--- /dev/null
+++ b/test/Modules/Inputs/warning-mismatch/System.h
@@ -0,0 +1,2 @@
+#import "Mismatch.h"
+struct System { int i; };
diff --git a/test/Modules/Inputs/warning-mismatch/module.modulemap b/test/Modules/Inputs/warning-mismatch/module.modulemap
new file mode 100644
index 0000000..c22cde4
--- /dev/null
+++ b/test/Modules/Inputs/warning-mismatch/module.modulemap
@@ -0,0 +1,7 @@
+module System [system] {
+ header "System.h"
+}
+
+module Mismatch {
+ header "Mismatch.h"
+}
diff --git a/test/Modules/builtin-import.mm b/test/Modules/builtin-import.mm
deleted file mode 100644
index 2536ac5..0000000
--- a/test/Modules/builtin-import.mm
+++ /dev/null
@@ -1,12 +0,0 @@
-// REQUIRES: system-darwin
-
-// RUN: rm -rf %t
-// RUN: %clang -cc1 -fsyntax-only -nostdinc++ -isysroot %S/Inputs/libc-libcxx/sysroot -isystem %S/Inputs/libc-libcxx/sysroot/usr/include/c++/v1 -std=c++11 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x objective-c++ -fmodules-local-submodule-visibility %s
-
-#include <stdio.h>
-#include <stddef.h>
-#include <cstddef>
-
-typedef ptrdiff_t try1_ptrdiff_t;
-typedef my_ptrdiff_t try2_ptrdiff_t;
-
diff --git a/test/Modules/diag-pragma.cpp b/test/Modules/diag-pragma.cpp
new file mode 100644
index 0000000..347401f
--- /dev/null
+++ b/test/Modules/diag-pragma.cpp
@@ -0,0 +1,47 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -emit-module -fmodules-cache-path=%t -fmodule-name=diag_pragma -x c++ %S/Inputs/module.map -fmodules-ts
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -emit-module -fmodule-name=diag_pragma -x c++ %S/Inputs/module.map -fmodules-ts -o %t/explicit.pcm -Werror=string-plus-int
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DEXPLICIT_FLAG -fmodule-file=%t/explicit.pcm
+
+import diag_pragma;
+
+int foo(int x) {
+ // Diagnostics from templates in the module follow the diagnostic state from
+ // when the module was built.
+#ifdef EXPLICIT_FLAG
+ // expected-error@diag_pragma.h:7 {{adding 'int' to a string}}
+#else
+ // expected-warning@diag_pragma.h:7 {{adding 'int' to a string}}
+#endif
+ // expected-note@diag_pragma.h:7 {{use array indexing}}
+ f(0); // expected-note {{instantiation of}}
+
+ g(0); // ok, warning was ignored when building module
+
+ // Diagnostics from this source file ignore the diagnostic state from the
+ // module.
+ void("foo" + x); // expected-warning {{adding 'int' to a string}}
+ // expected-note@-1 {{use array indexing}}
+
+#pragma clang diagnostic ignored "-Wstring-plus-int"
+
+ // Diagnostics from the module ignore diagnostic state changes from this
+ // source file.
+#ifdef EXPLICIT_FLAG
+ // expected-error@diag_pragma.h:7 {{adding 'long' to a string}}
+#else
+ // expected-warning@diag_pragma.h:7 {{adding 'long' to a string}}
+#endif
+ // expected-note@diag_pragma.h:7 {{use array indexing}}
+ f(0L); // expected-note {{instantiation of}}
+
+ g(0L);
+
+ void("bar" + x);
+
+ if (x = DIAG_PRAGMA_MACRO) // expected-warning {{using the result of an assignment as a condition without parentheses}} \
+ // expected-note {{place parentheses}} expected-note {{use '=='}}
+ return 0;
+ return 1;
+}
diff --git a/test/Modules/diagnostic-options-out-of-date.m b/test/Modules/diagnostic-options-out-of-date.m
index ed9e8e1..fd98a8f 100644
--- a/test/Modules/diagnostic-options-out-of-date.m
+++ b/test/Modules/diagnostic-options-out-of-date.m
@@ -7,6 +7,16 @@
// RUN: %clang_cc1 -Werror -Wconversion -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs %s -fmodules-disable-diagnostic-validation
// Make sure we don't error out when using the pch
// RUN: %clang_cc1 -Werror -Wno-conversion -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -fsyntax-only -I %S/Inputs -include-pch %t.pch %s -verify -fmodules-disable-diagnostic-validation
+
+// Build A.pcm
+// RUN: %clang_cc1 -Werror -Wno-conversion -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs %s
+// Build pch that imports A.pcm
+// RUN: %clang_cc1 -Werror -Wno-conversion -emit-pch -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -o %t.pch -I %S/Inputs -x objective-c-header %S/Inputs/pch-import-module-out-of-date.pch
+// We will rebuild A.pcm and overwrite the original A.pcm that the pch imports, but the two versions have the same hash.
+// RUN: %clang_cc1 -Werror -Wconversion -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs %s
+// Make sure we don't error out when using the pch
+// RUN: %clang_cc1 -Werror -Wno-conversion -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -fsyntax-only -I %S/Inputs -include-pch %t.pch %s -verify
+
// expected-no-diagnostics
@import DiagOutOfDate;
diff --git a/test/Modules/import-textual-noguard.mm b/test/Modules/import-textual-noguard.mm
deleted file mode 100644
index dd124b6..0000000
--- a/test/Modules/import-textual-noguard.mm
+++ /dev/null
@@ -1,8 +0,0 @@
-// RUN: rm -rf %t
-// RUN: %clang_cc1 -fsyntax-only -std=c++11 -fmodules -fimplicit-module-maps -I%S/Inputs/import-textual/M2 -fmodules-cache-path=%t -x objective-c++ -fmodules-local-submodule-visibility %s -verify
-
-#include "A/A.h" // expected-error {{could not build module 'M'}}
-#include "B/B.h"
-
-typedef aint xxx;
-typedef bint yyy;
diff --git a/test/Modules/import-textual.mm b/test/Modules/import-textual.mm
deleted file mode 100644
index 6593239..0000000
--- a/test/Modules/import-textual.mm
+++ /dev/null
@@ -1,10 +0,0 @@
-// RUN: rm -rf %t
-// RUN: %clang_cc1 -fsyntax-only -std=c++11 -fmodules -fimplicit-module-maps -I%S/Inputs/import-textual/M -fmodules-cache-path=%t -x objective-c++ -fmodules-local-submodule-visibility %s -verify
-
-// expected-no-diagnostics
-
-#include "A/A.h"
-#include "B/B.h"
-
-typedef aint xxx;
-typedef bint yyy;
diff --git a/test/Modules/incomplete-type-void.c b/test/Modules/incomplete-type-void.c
new file mode 100644
index 0000000..2ce00f7
--- /dev/null
+++ b/test/Modules/incomplete-type-void.c
@@ -0,0 +1,9 @@
+// RUN: rm -rf %t.cache
+// RUN: %clang_cc1 -fmodules %s -fmodules-cache-path=%t.cache \
+// RUN: -fsyntax-only -I %S/Inputs/incomplete-type -verify
+
+// expected-no-diagnostics
+
+#import "C.h"
+#import "A.h"
+void foo __P(());
diff --git a/test/Modules/infer_swift_name.m b/test/Modules/infer_swift_name.m
new file mode 100644
index 0000000..d4b4a5d
--- /dev/null
+++ b/test/Modules/infer_swift_name.m
@@ -0,0 +1,6 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/swift_name %s -verify
+// REQUIRES: shell
+
+@import SwiftNameInferred; // ok
+@import SwiftName; // expected-error{{module 'SwiftName' not found}}
diff --git a/test/Modules/invalid-pch-module-id.m b/test/Modules/invalid-pch-module-id.m
new file mode 100644
index 0000000..34d9995
--- /dev/null
+++ b/test/Modules/invalid-pch-module-id.m
@@ -0,0 +1,13 @@
+// RUN: rm -rf %t.cache
+//
+// RUN: %clang_cc1 -x objective-c-header -fmodules -F%S/Inputs/invalid-module-id \
+// RUN: -fmodule-implementation-of NC -fmodules-cache-path=%t.cache \
+// RUN: -fimplicit-module-maps \
+// RUN: -emit-pch %S/Inputs/invalid-module-id/NC-Prefix.pch -o %t.pch
+//
+// RUN: %clang_cc1 -x objective-c -fmodules -F%S/Inputs/invalid-module-id \
+// RUN: -fmodule-implementation-of NC -fmodules-cache-path=%t.cache \
+// RUN: -fimplicit-module-maps -include-pch %t.pch %s -fsyntax-only
+
+#import <NC/NULog.h>
+#import <NC/NUGeometry.h>
diff --git a/test/Modules/minimal-identifier-tables.cpp b/test/Modules/minimal-identifier-tables.cpp
deleted file mode 100644
index 0674746..0000000
--- a/test/Modules/minimal-identifier-tables.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: echo 'extern int some_long_variable_name;' > %t/x.h
-// RUN: echo 'extern int some_long_variable_name;' > %t/y.h
-// RUN: echo 'module X { header "x.h" } module Y { header "y.h" }' > %t/map
-// RUN: %clang_cc1 -fmodules -x c++ -fmodule-name=X %t/map -emit-module -o %t/x.pcm
-// RUN: %clang_cc1 -fmodules -x c++ -fmodule-name=Y %t/map -fmodule-file=%t/x.pcm -emit-module -o %t/y.pcm
-// RUN: cat %t/y.pcm | FileCheck %s
-//
-// CHECK-NOT: some_long_variable_name
diff --git a/test/Modules/module_file_info.m b/test/Modules/module_file_info.m
index fa841b7..c5f47d9 100644
--- a/test/Modules/module_file_info.m
+++ b/test/Modules/module_file_info.m
@@ -29,11 +29,6 @@
// CHECK: CPU:
// CHECK: ABI:
-// CHECK: Diagnostic options:
-// CHECK: IgnoreWarnings: Yes
-// CHECK: Diagnostic flags:
-// CHECK: -Wunused
-
// CHECK: Header search options:
// CHECK: System root [-isysroot=]: '/'
// CHECK: Use builtin include directories [-nobuiltininc]: Yes
@@ -47,3 +42,8 @@
// CHECK: Predefined macros:
// CHECK: -DBLARG
// CHECK: -DWIBBLE=WOBBLE
+
+// CHECK: Diagnostic options:
+// CHECK: IgnoreWarnings: Yes
+// CHECK: Diagnostic flags:
+// CHECK: -Wunused
diff --git a/test/Modules/modules-cache-path-canonicalization.m b/test/Modules/modules-cache-path-canonicalization.m
new file mode 100644
index 0000000..7cc7460
--- /dev/null
+++ b/test/Modules/modules-cache-path-canonicalization.m
@@ -0,0 +1,30 @@
+// RUN: rm -rf %t/cache %T/rel
+
+// This testcase reproduces a use-after-free after looking up a PCM in
+// a non-canonical modules-cache-path.
+//
+// Prime the module cache (note the '.' in the path).
+// RUN: %clang_cc1 -fdisable-module-hash -fmodules-cache-path=%t/./cache \
+// RUN: -fmodules -fimplicit-module-maps -I %S/Inputs/outofdate-rebuild \
+// RUN: %s -fsyntax-only
+//
+// Force a module to be rebuilt by creating a conflict.
+// RUN: echo "@import CoreText;" > %t.m
+// RUN: %clang_cc1 -DMISMATCH -Werror -fdisable-module-hash \
+// RUN: -fmodules-cache-path=%t/./cache -fmodules -fimplicit-module-maps \
+// RUN: -I %S/Inputs/outofdate-rebuild %t.m -fsyntax-only
+//
+// Rebuild.
+// RUN: %clang_cc1 -fdisable-module-hash -fmodules-cache-path=%t/./cache \
+// RUN: -fmodules -fimplicit-module-maps -I %S/Inputs/outofdate-rebuild \
+// RUN: %s -fsyntax-only
+
+
+// Unrelated to the above: Check that a relative path is resolved correctly.
+//
+// RUN: %clang_cc1 -working-directory %T/rel -fmodules-cache-path=./cache \
+// RUN: -fmodules -fimplicit-module-maps -I %S/Inputs/outofdate-rebuild \
+// RUN: -fdisable-module-hash %t.m -fsyntax-only -Rmodule-build 2>&1 \
+// RUN: | FileCheck %s
+// CHECK: /rel/cache/CoreText.pcm
+@import Cocoa;
diff --git a/test/Modules/outofdate-rebuild.m b/test/Modules/outofdate-rebuild.m
new file mode 100644
index 0000000..e933dce
--- /dev/null
+++ b/test/Modules/outofdate-rebuild.m
@@ -0,0 +1,12 @@
+// RUN: rm -rf %t.cache
+// RUN: echo "@import CoreText;" > %t.m
+// RUN: %clang_cc1 -fdisable-module-hash -fmodules-cache-path=%t.cache -fmodules -fimplicit-module-maps -I %S/Inputs/outofdate-rebuild %s -fsyntax-only -Rmodule-build
+// RUN: echo -----------------------------------------------
+// RUN: %clang_cc1 -DMISMATCH -Werror -fdisable-module-hash -fmodules-cache-path=%t.cache -fmodules -fimplicit-module-maps -I %S/Inputs/outofdate-rebuild %t.m -fsyntax-only -Rmodule-build
+// RUN: echo -----------------------------------------------
+// RUN: %clang_cc1 -fdisable-module-hash -fmodules-cache-path=%t.cache -fmodules -fimplicit-module-maps -I %S/Inputs/outofdate-rebuild %s -fsyntax-only -Rmodule-build
+
+// This testcase reproduces a use-after-free in
+// https://reviews.llvm.org/D28299 when ModuleManager removes an entry
+// from the PCMcache without notifying its parent ASTReader.
+@import Cocoa;
diff --git a/test/Modules/rebuild.m b/test/Modules/rebuild.m
index 40f2d47..150c2ce 100644
--- a/test/Modules/rebuild.m
+++ b/test/Modules/rebuild.m
@@ -19,11 +19,10 @@
// RUN: diff %t/Module.size %t/Module.size.saved
// RUN: cp %t/Module.pcm %t/Module.pcm.saved.2
-// But the signature at least is expected to change, so we rebuild DependsOnModule.
-// NOTE: if we change how the signature is created, this test may need updating.
+// The signature is the hash of the PCM content, we will not rebuild rebuild DependsOnModule.
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fdisable-module-hash -fsyntax-only -F %S/Inputs %s
// RUN: diff %t/Module.pcm %t/Module.pcm.saved.2
-// RUN: not diff %t/DependsOnModule.pcm %t/DependsOnModule.pcm.saved
+// RUN: diff %t/DependsOnModule.pcm %t/DependsOnModule.pcm.saved
// Rebuild Module, reset its timestamp, and verify its size hasn't changed
// RUN: rm %t/Module.pcm
@@ -34,10 +33,9 @@
// RUN: cp %t/Module.pcm %t/Module.pcm.saved.2
// Verify again with Module pre-imported.
-// NOTE: if we change how the signature is created, this test may need updating.
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fdisable-module-hash -fsyntax-only -F %S/Inputs %s
// RUN: diff %t/Module.pcm %t/Module.pcm.saved.2
-// RUN: not diff %t/DependsOnModule.pcm %t/DependsOnModule.pcm.saved
+// RUN: diff %t/DependsOnModule.pcm %t/DependsOnModule.pcm.saved
#ifdef PREIMPORT
@import Module;
diff --git a/test/Modules/shadow.m b/test/Modules/shadow.m
new file mode 100644
index 0000000..44320af
--- /dev/null
+++ b/test/Modules/shadow.m
@@ -0,0 +1,21 @@
+// RUN: rm -rf %t
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/shadow/A1 -I %S/Inputs/shadow/A2 %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -fmodule-map-file=%S/Inputs/shadow/A2/module.modulemap %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION
+// REDEFINITION: error: redefinition of module 'A'
+// REDEFINITION: note: previously defined
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -I %S/Inputs/shadow %s -verify
+
+@import A1;
+@import A2;
+@import A;
+
+#import "A2/A.h" // expected-note {{implicitly imported}}
+// expected-error@A2/module.modulemap:1 {{import of shadowed module 'A'}}
+// expected-note@A1/module.modulemap:1 {{previous definition}}
+
+#if defined(A2_A_h)
+#error got the wrong definition of module A
+#elif !defined(A1_A_h)
+#error missing definition from A1
+#endif
diff --git a/test/Modules/system-out-of-date-test.m b/test/Modules/system-out-of-date-test.m
new file mode 100644
index 0000000..9ce14cc
--- /dev/null
+++ b/test/Modules/system-out-of-date-test.m
@@ -0,0 +1,13 @@
+// RUN: rm -rf %t
+// RUN: echo '@import X;' | \
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps \
+// RUN: -fmodules-cache-path=%t -I %S/Inputs/system-out-of-date \
+// RUN: -fsyntax-only -x objective-c -
+
+// We have an version built with different diagnostic options.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/system-out-of-date -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module %s -fsyntax-only 2>&1 | FileCheck %s
+ @import X;
+
+ #import <Z.h>
+// CHECK: While building module 'Z' imported from
+// CHECK: {{.*}}Y-{{.*}}pcm' was validated as a system module and is now being imported as a non-system module
diff --git a/test/Modules/warning-mismatch.m b/test/Modules/warning-mismatch.m
new file mode 100644
index 0000000..7c83664
--- /dev/null
+++ b/test/Modules/warning-mismatch.m
@@ -0,0 +1,9 @@
+// RUN: rm -rf %t.cache
+// RUN: echo "@import Mismatch;" >%t.m
+// RUN: %clang_cc1 -Wno-system-headers -fdisable-module-hash -fmodules-cache-path=%t.cache -fmodules -fimplicit-module-maps -I %S/Inputs/warning-mismatch %t.m -fsyntax-only -Rmodule-build
+// RUN: echo -----------------------------------------------
+// RUN: %clang_cc1 -Wsystem-headers -fdisable-module-hash -fmodules-cache-path=%t.cache -fmodules -fimplicit-module-maps -I %S/Inputs/warning-mismatch %s -fsyntax-only -Rmodule-build
+
+// This testcase triggers a warning flag mismatch in an already validated header.
+@import Mismatch;
+@import System;
diff --git a/test/PCH/empty-def-fwd-struct.h b/test/PCH/empty-def-fwd-struct.h
new file mode 100644
index 0000000..6a9372c
--- /dev/null
+++ b/test/PCH/empty-def-fwd-struct.h
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -emit-pch -x c++-header %s -std=c++14 -o %t.pch
+// RUN: %clang_cc1 -emit-llvm -x c++ /dev/null -std=c++14 -include-pch %t.pch -o %t.o
+struct FVector;
+struct FVector {};
+struct FBox {
+ FVector Min;
+ FBox(int);
+};
+namespace {
+FBox InvalidBoundingBox(0);
+}
+
diff --git a/test/Preprocessor/expr_define_expansion.c b/test/Preprocessor/expr_define_expansion.c
index 23cb435..3e5a2c4 100644
--- a/test/Preprocessor/expr_define_expansion.c
+++ b/test/Preprocessor/expr_define_expansion.c
@@ -1,28 +1,6 @@
-// RUN: %clang_cc1 %s -E -CC -verify
-// RUN: %clang_cc1 %s -E -CC -DPEDANTIC -pedantic -verify
+// RUN: %clang_cc1 %s -E -CC -pedantic -verify
+// expected-no-diagnostics
#define FOO && 1
#if defined FOO FOO
#endif
-
-#define A
-#define B defined(A)
-#if B // expected-warning{{macro expansion producing 'defined' has undefined behavior}}
-#endif
-
-#define m_foo
-#define TEST(a) (defined(m_##a) && a)
-
-#if defined(PEDANTIC)
-// expected-warning@+4{{macro expansion producing 'defined' has undefined behavior}}
-#endif
-
-// This shouldn't warn by default, only with pedantic:
-#if TEST(foo)
-#endif
-
-
-// Only one diagnostic for this case:
-#define INVALID defined(
-#if INVALID // expected-error{{macro name missing}}
-#endif
diff --git a/test/Preprocessor/init.c b/test/Preprocessor/init.c
index 8b89019..1d3e9fb 100644
--- a/test/Preprocessor/init.c
+++ b/test/Preprocessor/init.c
@@ -8705,6 +8705,7 @@
// WEBASSEMBLY32-NEXT:#define __LONG_MAX__ 2147483647L
// WEBASSEMBLY32-NOT:#define __LP64__
// WEBASSEMBLY32-NEXT:#define __NO_INLINE__ 1
+// WEBASSEMBLY32-NEXT:#define __OBJC_BOOL_IS_BOOL 0
// WEBASSEMBLY32-NEXT:#define __ORDER_BIG_ENDIAN__ 4321
// WEBASSEMBLY32-NEXT:#define __ORDER_LITTLE_ENDIAN__ 1234
// WEBASSEMBLY32-NEXT:#define __ORDER_PDP_ENDIAN__ 3412
@@ -9020,6 +9021,7 @@
// WEBASSEMBLY64-NEXT:#define __LONG_MAX__ 9223372036854775807L
// WEBASSEMBLY64-NEXT:#define __LP64__ 1
// WEBASSEMBLY64-NEXT:#define __NO_INLINE__ 1
+// WEBASSEMBLY64-NEXT:#define __OBJC_BOOL_IS_BOOL 0
// WEBASSEMBLY64-NEXT:#define __ORDER_BIG_ENDIAN__ 4321
// WEBASSEMBLY64-NEXT:#define __ORDER_LITTLE_ENDIAN__ 1234
// WEBASSEMBLY64-NEXT:#define __ORDER_PDP_ENDIAN__ 3412
diff --git a/test/Profile/cxx-virtual-destructor-calls.cpp b/test/Profile/cxx-virtual-destructor-calls.cpp
index cc3df68..c60fc92 100644
--- a/test/Profile/cxx-virtual-destructor-calls.cpp
+++ b/test/Profile/cxx-virtual-destructor-calls.cpp
@@ -13,15 +13,6 @@
virtual ~B();
};
-// Base dtor
-// CHECK: @__profn__ZN1BD2Ev = private constant [9 x i8] c"_ZN1BD2Ev"
-
-// Complete dtor must not be instrumented
-// CHECK-NOT: @__profn__ZN1BD1Ev = private constant [9 x i8] c"_ZN1BD1Ev"
-
-// Deleting dtor must not be instrumented
-// CHECK-NOT: @__profn__ZN1BD0Ev = private constant [9 x i8] c"_ZN1BD0Ev"
-
// Base dtor counters and profile data
// CHECK: @__profc__ZN1BD2Ev = private global [1 x i64] zeroinitializer
// CHECK: @__profd__ZN1BD2Ev =
diff --git a/test/Sema/attr-availability-swift.c b/test/Sema/attr-availability-swift.c
new file mode 100644
index 0000000..42e7524
--- /dev/null
+++ b/test/Sema/attr-availability-swift.c
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fsyntax-only -fblocks -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fsyntax-only -ast-dump %s | FileCheck %s
+//
+
+#if !__has_feature(attribute_availability_with_message)
+# error "Missing __has_feature"
+#endif
+
+#if __has_feature(attribute_availability_swift)
+# warning "okay"
+// expected-warning@-1{{okay}}
+#else
+# error "Missing __has_feature"
+#endif
+
+extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable)));
+// CHECK: AvailabilityAttr {{.*}}swift 0 0 0 Unavailable "" ""
+extern int noSwiftGlobal1 __attribute__((availability(macosx, introduced=10.1))); // okay
+// CHECK: AvailabilityAttr {{.*}}macos 10.1 0 0 "" ""
+// CHECK: AvailabilityAttr {{.*}}Inherited swift 0 0 0 Unavailable "" ""
+extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable, message="and this one has a message"))); // okay
+// CHECK: AvailabilityAttr {{.*}}swift 0 0 0 Unavailable "and this one has a message" ""
+// CHECK: AvailabilityAttr {{.*}}Inherited macos 10.1 0 0 "" ""
+extern int noSwiftGlobal2 __attribute__((availability(swift, introduced=5))); // expected-warning{{only 'unavailable' and 'deprecated' are supported for Swift availability}}
+// CHECK: VarDecl
+// CHECK-NOT: AvailabilityAttr
+extern int noSwiftGlobal3 __attribute__((availability(swift, deprecated, message="t")));
+// CHECK: VarDecl
+// CHECK: AvailabilityAttr {{.*}}swift 0 1 0 "t" ""
diff --git a/test/Sema/attr-availability.c b/test/Sema/attr-availability.c
index a4b40ff..e7a800f 100644
--- a/test/Sema/attr-availability.c
+++ b/test/Sema/attr-availability.c
@@ -80,7 +80,6 @@
extern int x2 __attribute__((availability(macosx,introduced=10.2))); // expected-note {{previous attribute is here}}
extern int x2 __attribute__((availability(macosx,introduced=10.5))); // expected-warning {{availability does not match previous declaration}}
-
enum Original {
OriginalDeprecated __attribute__((availability(macosx, deprecated=10.2))), // expected-note + {{'OriginalDeprecated' has been explicitly marked deprecated here}}
OriginalUnavailable __attribute__((availability(macosx, unavailable))) // expected-note + {{'OriginalUnavailable' has been explicitly marked unavailable here}}
diff --git a/test/Sema/attr-noescape.c b/test/Sema/attr-noescape.c
new file mode 100644
index 0000000..ec367b6
--- /dev/null
+++ b/test/Sema/attr-noescape.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 %s -fblocks -verify -fsyntax-only
+
+#if !__has_attribute(noescape)
+# error "missing noescape attribute"
+#endif
+
+int *global_var __attribute((noescape)); // expected-warning{{'noescape' attribute only applies to parameters}}
+
+void foo(__attribute__((noescape)) int *int_ptr,
+ __attribute__((noescape)) int (^block)(int),
+ __attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute ignored on parameter of non-pointer type 'int'}}
+}
diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c
index 5465122..ca3132e 100644
--- a/test/Sema/format-strings.c
+++ b/test/Sema/format-strings.c
@@ -653,6 +653,33 @@
}
#pragma GCC diagnostic warning "-Wformat-nonliteral"
+void test_os_log_format(char c, const char *pc, int i, int *pi, void *p, void *buf) {
+ __builtin_os_log_format(buf, "");
+ __builtin_os_log_format(buf, "%d"); // expected-warning {{more '%' conversions than data arguments}}
+ __builtin_os_log_format(buf, "%d", i);
+ __builtin_os_log_format(buf, "%P", p); // expected-warning {{using '%P' format specifier without precision}}
+ __builtin_os_log_format(buf, "%.10P", p);
+ __builtin_os_log_format(buf, "%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}}
+ __builtin_os_log_format(buf, "%.*P", i, p);
+ __builtin_os_log_format(buf, "%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}}
+ __builtin_os_log_format(buf, pc); // expected-error {{os_log() format argument is not a string constant}}
+
+ printf("%{private}s", pc); // expected-warning {{using 'private' format specifier annotation outside of os_log()/os_trace()}}
+ __builtin_os_log_format(buf, "%{private}s", pc);
+
+ // <rdar://problem/23835805>
+ __builtin_os_log_format_buffer_size("no-args");
+ __builtin_os_log_format(buf, "%s", "hi");
+
+ // <rdar://problem/24828090>
+ wchar_t wc = 'a';
+ __builtin_os_log_format(buf, "%C", wc);
+ printf("%C", wc);
+ wchar_t wcs[] = {'a', 0};
+ __builtin_os_log_format(buf, "%S", wcs);
+ printf("%S", wcs);
+}
+
void test_char_pointer_arithmetic(int b) {
const char s1[] = "string";
const char s2[] = "%s string";
diff --git a/test/Sema/vector-cast.c b/test/Sema/vector-cast.c
index ea4acfa..03db540 100644
--- a/test/Sema/vector-cast.c
+++ b/test/Sema/vector-cast.c
@@ -45,24 +45,12 @@
}
typedef float float2 __attribute__ ((vector_size (8)));
-typedef __attribute__((vector_size(8))) double float64x1_t;
-typedef __attribute__((vector_size(16))) double float64x2_t;
-float64x1_t vget_low_f64(float64x2_t __p0);
void f4() {
float2 f2;
- double d, a, b, c;
- float64x2_t v = {0.0, 1.0};
- // FIXME: These diagnostics are inaccurate: should complain that 'double' to vector 'float2' involves truncation
- f2 += d; // expected-error {{cannot convert between vector values of different size ('float2' (vector of 2 'float' values) and 'double')}}
- d += f2; // expected-error {{cannot convert between vector values of different size}}
- a = 3.0 + vget_low_f64(v);
- b = vget_low_f64(v) + 3.0;
- c = vget_low_f64(v);
- c -= vget_low_f64(v);
- // LAX conversions between scalar and vector types require same size and one element sized vectors.
- d = f2; // expected-error {{assigning to 'double' from incompatible type 'float2'}}
- d = d + f2; // expected-error {{assigning to 'double' from incompatible type 'float2'}}
+ double d;
+ f2 += d;
+ d += f2;
}
// rdar://15931426
diff --git a/test/Sema/vector-compound-assign-lax.cpp b/test/Sema/vector-compound-assign-lax.cpp
new file mode 100644
index 0000000..296cfb7
--- /dev/null
+++ b/test/Sema/vector-compound-assign-lax.cpp
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -ffreestanding -triple armv7 -target-feature +neon -fsyntax-only %s -verify -Wvector-conversion -DNEON
+// RUN: %clang_cc1 -ffreestanding -triple=x86_64-apple-darwin -target-feature +sse2 -fsyntax-only %s -verify -Wvector-conversion -DSSE2
+
+// Test that there are no diagnostics for vector compound assignments where we
+// support lax conversions from the RHS type to the LHS type. For context, see:
+//
+// <rdar://problem/30112602> cannot convert between vector values ...
+// <rdar://problem/28639522> Cannot create binary operator with two ...
+// <rdar://problem/30110333> Invalid conversion from int64x2 to uint32x4 ...
+
+// expected-no-diagnostics
+
+#include <stdint.h>
+
+typedef int v_int32x4_t __attribute__((__vector_size__(16)));
+typedef unsigned v_uint32x4_t __attribute__((__vector_size__(16)));
+
+#ifdef NEON
+typedef __attribute__((neon_vector_type(8))) int16_t n_int16x8_t;
+typedef __attribute__((neon_vector_type(8))) uint16_t n_uint16x8_t;
+
+void fn1() {
+ n_int16x8_t i;
+ n_uint16x8_t u;
+ i += u;
+}
+#endif
+
+void fn2() {
+ v_int32x4_t i;
+ v_uint32x4_t u;
+ i += u;
+}
+
+#ifdef SSE2
+#include <x86intrin.h>
+
+typedef __attribute__((__ext_vector_type__(4))) unsigned int ev_uint32x4_t;
+
+void fn3() {
+ __m128i i;
+ ev_uint32x4_t u;
+ i += u; // FIXME: Converting uint32x4 to int64x2 should *not* be allowed.
+}
+#endif
diff --git a/test/SemaCXX/designated-initializers-base-class.cpp b/test/SemaCXX/designated-initializers-base-class.cpp
new file mode 100644
index 0000000..9c2e61e
--- /dev/null
+++ b/test/SemaCXX/designated-initializers-base-class.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify -Winitializer-overrides
+// expected-no-diagnostics
+
+struct B {
+ int x;
+};
+
+struct D : B {
+ int y;
+};
+
+void test() { D d = {1, .y = 2}; }
diff --git a/test/SemaCXX/pseudo-destructors.cpp b/test/SemaCXX/pseudo-destructors.cpp
index 45f1eaf..bc0d7b2 100644
--- a/test/SemaCXX/pseudo-destructors.cpp
+++ b/test/SemaCXX/pseudo-destructors.cpp
@@ -89,3 +89,26 @@
void AliasTemplate(int *p) {
p->~Id<int>();
}
+
+namespace dotPointerAccess {
+struct Base {
+ virtual ~Base() {}
+};
+
+struct Derived : Base {
+ ~Derived() {}
+};
+
+void test() {
+ Derived d;
+ static_cast<Base *>(&d).~Base(); // expected-error {{member reference type 'dotPointerAccess::Base *' is a pointer; did you mean to use '->'}}
+ d->~Derived(); // expected-error {{member reference type 'dotPointerAccess::Derived' is not a pointer; did you mean to use '.'}}
+}
+
+typedef Derived *Foo;
+
+void test2(Foo d) {
+ d.~Foo(); // This is ok
+ d.~Derived(); // expected-error {{member reference type 'Foo' (aka 'dotPointerAccess::Derived *') is a pointer; did you mean to use '->'}}
+}
+}
diff --git a/test/SemaObjC/arc-repeated-weak.mm b/test/SemaObjC/arc-repeated-weak.mm
index 11161a0..8e2828f 100644
--- a/test/SemaObjC/arc-repeated-weak.mm
+++ b/test/SemaObjC/arc-repeated-weak.mm
@@ -462,3 +462,16 @@
use(NSBundle.foo2.weakProp); // expected-warning{{weak property 'weakProp' may be accessed multiple times}}
use(NSBundle2.foo2.weakProp); // expected-note{{also accessed here}}
}
+
+// This used to crash in the constructor of WeakObjectProfileTy when a
+// DeclRefExpr was passed that didn't reference a VarDecl.
+
+typedef INTF * INTFPtrTy;
+
+enum E {
+ e1
+};
+
+void foo1() {
+ INTFPtrTy tmp = (INTFPtrTy)e1; // expected-error{{cast of 'E' to 'INTFPtrTy' (aka 'INTF *') is disallowed with ARC}}
+}
diff --git a/test/SemaObjC/arc.m b/test/SemaObjC/arc.m
index f463bb0..72c07a9 100644
--- a/test/SemaObjC/arc.m
+++ b/test/SemaObjC/arc.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -fblocks -verify -Wno-objc-root-class -Wblock-capture-autoreleasing %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -fblocks -verify -Wno-objc-root-class %s
typedef unsigned long NSUInteger;
typedef const void * CFTypeRef;
@@ -809,9 +809,30 @@
TKAssertEqual(object, (id)nil);
}
-void block_capture_autoreleasing(A * __autoreleasing *a, A **b) { // expected-note {{declare the parameter __autoreleasing explicitly to suppress this warning}} expected-note {{declare the parameter __strong or capture a __block __strong variable to keep values alive across autorelease pools}}
+void block_capture_autoreleasing(A * __autoreleasing *a,
+ A **b, // expected-note {{declare the parameter __autoreleasing explicitly to suppress this warning}} expected-note {{declare the parameter __strong or capture a __block __strong variable to keep values alive across autorelease pools}}
+ A * _Nullable *c, // expected-note {{declare the parameter __autoreleasing explicitly to suppress this warning}} expected-note {{declare the parameter __strong or capture a __block __strong variable to keep values alive across autorelease pools}}
+ A * _Nullable __autoreleasing *d,
+ A ** _Nullable e, // expected-note {{declare the parameter __autoreleasing explicitly to suppress this warning}} expected-note {{declare the parameter __strong or capture a __block __strong variable to keep values alive across autorelease pools}}
+ A * __autoreleasing * _Nullable f,
+ id __autoreleasing *g,
+ id *h, // expected-note {{declare the parameter __autoreleasing explicitly to suppress this warning}} expected-note {{declare the parameter __strong or capture a __block __strong variable to keep values alive across autorelease pools}}
+ id _Nullable *i, // expected-note {{declare the parameter __autoreleasing explicitly to suppress this warning}} expected-note {{declare the parameter __strong or capture a __block __strong variable to keep values alive across autorelease pools}}
+ id _Nullable __autoreleasing *j,
+ id * _Nullable k, // expected-note {{declare the parameter __autoreleasing explicitly to suppress this warning}} expected-note {{declare the parameter __strong or capture a __block __strong variable to keep values alive across autorelease pools}}
+ id __autoreleasing * _Nullable l) {
^{
(void)*a;
(void)*b; // expected-warning {{block captures an autoreleasing out-parameter, which may result in use-after-free bugs}}
+ (void)*c; // expected-warning {{block captures an autoreleasing out-parameter, which may result in use-after-free bugs}}
+ (void)*d;
+ (void)*e; // expected-warning {{block captures an autoreleasing out-parameter, which may result in use-after-free bugs}}
+ (void)*f;
+ (void)*g;
+ (void)*h; // expected-warning {{block captures an autoreleasing out-parameter, which may result in use-after-free bugs}}
+ (void)*i; // expected-warning {{block captures an autoreleasing out-parameter, which may result in use-after-free bugs}}
+ (void)*j;
+ (void)*k; // expected-warning {{block captures an autoreleasing out-parameter, which may result in use-after-free bugs}}
+ (void)*l;
}();
}
diff --git a/test/SemaObjC/attr-swift.m b/test/SemaObjC/attr-swift.m
new file mode 100644
index 0000000..bdcbbd0
--- /dev/null
+++ b/test/SemaObjC/attr-swift.m
@@ -0,0 +1,217 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s
+
+// --- swift_private ---
+
+__attribute__((swift_private))
+@protocol FooProto
+@end
+
+__attribute__((swift_private))
+@interface Foo
+@end
+
+@interface Bar
+@property id prop __attribute__((swift_private));
+- (void)instMethod __attribute__((swift_private));
++ (instancetype)bar __attribute__((swift_private));
+@end
+
+void function(id) __attribute__((swift_private));
+
+struct __attribute__((swift_private)) Point {
+ int x;
+ int y;
+};
+
+enum __attribute__((swift_private)) Colors {
+ Red, Green, Blue
+};
+
+typedef struct {
+ float x, y, z;
+} Point3D __attribute__((swift_private));
+
+
+// --- swift_name ---
+
+__attribute__((swift_name("SNFooType")))
+@protocol SNFoo
+@end
+
+__attribute__((swift_name("SNFooClass")))
+@interface SNFoo <SNFoo>
+- (instancetype)init __attribute__((swift_name("init()")));
+- (instancetype)initWithValue:(int)value __attribute__((swift_name("fooWithValue(_:)")));
+
++ (void)refresh __attribute__((swift_name("refresh()")));
+
++ (instancetype)foo __attribute__((swift_name("foo()")));
++ (SNFoo *)fooWithValue:(int)value __attribute__((swift_name("foo(value:)")));
++ (SNFoo *)fooWithValue:(int)value value:(int)value2 __attribute__((swift_name("foo(value:extra:)")));
++ (SNFoo *)fooWithConvertingValue:(int)value value:(int)value2 __attribute__((swift_name("init(_:extra:)")));
+
++ (SNFoo *)fooWithOtherValue:(int)value __attribute__((swift_name("init"))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}}
++ (SNFoo *)fooWithAnotherValue:(int)value __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
++ (SNFoo *)fooWithYetAnotherValue:(int)value __attribute__((swift_name("foo(value:extra:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 1; got 2)}}
+
++ (SNFoo *)fooAndReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo()"))); // no-warning
++ (SNFoo *)fooWithValue:(int)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(value:)"))); // no-warning
++ (SNFoo *)fooFromErrorCode:(const int *)errorCode __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
++ (SNFoo *)fooWithValue:(int)value fromErrorCode:(const int *)errorCode __attribute__((swift_name("foo(value:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}}
++ (SNFoo *)fooWithPointerA:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo()"))); // no-warning
++ (SNFoo *)fooWithPointerB:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(pointer:)"))); // no-warning
++ (SNFoo *)fooWithPointerC:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(pointer:errorCode:)"))); // no-warning
++ (SNFoo *)fooWithOtherFoo:(SNFoo *)other __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
+
++ (instancetype)specialFoo __attribute__((swift_name("init(options:)")));
++ (instancetype)specialBar __attribute__((swift_name("init(options:extra:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 2)}}
++ (instancetype)specialBaz __attribute__((swift_name("init(_:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}}
++ (instancetype)specialGarply __attribute__((swift_name("foo(options:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}}
+
++ (instancetype)trailingParen __attribute__((swift_name("foo("))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}}
++ (instancetype)trailingColon:(int)value __attribute__((swift_name("foo(value)"))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}}
++ (instancetype)initialIgnore:(int)value __attribute__((swift_name("_(value:)"))); // expected-warning {{'swift_name' attribute has invalid identifier for base name}}
++ (instancetype)middleOmitted:(int)value __attribute__((swift_name("foo(:)"))); // expected-warning {{'swift_name' attribute has invalid identifier for parameter name}}
+
+@property(strong) id someProp __attribute__((swift_name("prop")));
+@end
+
+enum __attribute__((swift_name("MoreColors"))) MoreColors {
+ Cyan,
+ Magenta,
+ Yellow __attribute__((swift_name("RoseGold"))),
+ Black __attribute__((swift_name("SpaceGrey()"))) // expected-warning {{'swift_name' attribute has invalid identifier for base name}}
+};
+
+struct __attribute__((swift_name("FooStruct"))) BarStruct {
+ int x, y, z __attribute__((swift_name("zed")));
+};
+
+int global_int __attribute__((swift_name("GlobalInt")));
+
+void foo1(int i) __attribute__((swift_name("foo"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+void foo2(int i) __attribute__((swift_name("foo()"))); // expected-warning{{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
+void foo2(int i) __attribute__((swift_name("foo(a:b:)"))); // expected-warning{{too many parameters in 'swift_name' attribute (expected 1; got 2)}}
+void foo3(int i, int j) __attribute__((swift_name("fooWithX(_:y:)"))); // okay
+void foo4(int i, int *error) __attribute__((swift_name("fooWithA(_:)"))); // okay
+
+typedef int some_int_type __attribute__((swift_name("SomeInt")));
+
+struct Point3D createPoint3D(float x, float y, float z) __attribute__((swift_name("Point3D.init(x:y:z:)")));
+struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radians:)")));
+struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radians:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}}
+
+extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity")));
+
+// Getters and setters.
+float Point3DGetMagnitude(Point3D point) __attribute__((swift_name("getter:Point3D.magnitude(self:)")));
+
+float Point3DGetMagnitudeAndSomethingElse(Point3D point, float wat) __attribute__((swift_name("getter:Point3D.magnitude(self:wat:)"))); // expected-warning {{'swift_name' attribute for getter must not take any parameters besides 'self:'}}
+
+float Point3DGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.radius(self:)")));
+void Point3DSetRadius(Point3D point, float radius) __attribute__((swift_name("setter:Point3D.radius(self:newValue:)")));
+
+float Point3DPreGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.preRadius(self:)")));
+void Point3DPreSetRadius(float radius, Point3D point) __attribute__((swift_name("setter:Point3D.preRadius(newValue:self:)")));
+
+void Point3DSetRadiusAndSomethingElse(Point3D point, float radius, float wat) __attribute__((swift_name("setter:Point3D.radius(self:newValue:wat:)"))); // expected-warning {{'swift_name' attribute for setter must take one parameter for new value}}
+
+float Point3DGetComponent(Point3D point, unsigned index) __attribute__((swift_name("getter:Point3D.subscript(self:_:)")));
+float Point3DSetComponent(Point3D point, unsigned index, float value) __attribute__((swift_name("setter:Point3D.subscript(self:_:newValue:)")));
+
+float Point3DGetMatrixComponent(Point3D point, unsigned x, unsigned y) __attribute__((swift_name("getter:Point3D.subscript(self:x:y:)")));
+void Point3DSetMatrixComponent(Point3D point, unsigned x, float value, unsigned y) __attribute__((swift_name("setter:Point3D.subscript(self:x:newValue:y:)")));
+
+float Point3DSetWithoutNewValue(Point3D point, unsigned x, unsigned y) __attribute__((swift_name("setter:Point3D.subscript(self:x:y:)"))); // expected-warning {{'swift_name' attribute for 'subscript' setter must take a 'newValue:' parameter}}
+
+float Point3DSubscriptButNotGetterSetter(Point3D point, unsigned x) __attribute__((swift_name("Point3D.subscript(self:_:)"))); // expected-warning {{'swift_name' attribute for 'subscript' must be a getter or setter}}
+
+void Point3DSubscriptSetterTwoNewValues(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("setter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-warning {{'swift_name' attribute for 'subscript' setter cannot take multiple 'newValue:' parameters}}
+float Point3DSubscriptGetterNewValue(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("getter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-warning {{'swift_name' attribute for 'subscript' getter cannot take a 'newValue:' parameter}}
+
+void Point3DMethodWithNewValue(Point3D point, float newValue) __attribute__((swift_name("Point3D.method(self:newValue:)")));
+void Point3DMethodWithNewValues(Point3D point, float newValue, float newValueB) __attribute__((swift_name("Point3D.method(self:newValue:newValue:)")));
+
+float Point3DStaticSubscript(unsigned x) __attribute__((swift_name("getter:Point3D.subscript(_:)"))); // expected-warning {{'swift_name' attribute for 'subscript' must take a 'self:' parameter}}
+float Point3DStaticSubscriptNoArgs(void) __attribute__((swift_name("getter:Point3D.subscript()"))); // expected-warning {{'swift_name' attribute for 'subscript' must take at least one parameter}}
+
+float Point3DPreGetComponent(Point3D point, unsigned index) __attribute__((swift_name("getter:Point3D.subscript(self:_:)")));
+
+Point3D getCurrentPoint3D(void) __attribute__((swift_name("getter:currentPoint3D()")));
+
+void setCurrentPoint3D(Point3D point) __attribute__((swift_name("setter:currentPoint3D(newValue:)")));
+
+Point3D getLastPoint3D(void) __attribute__((swift_name("getter:lastPoint3D()")));
+
+void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(newValue:)")));
+
+Point3D getZeroPoint(void) __attribute__((swift_name("getter:Point3D.zero()")));
+Point3D getZeroPointNoPrototype() __attribute__((swift_name("getter:Point3D.zeroNoPrototype()"))); // expected-warning{{'swift_name' attribute can only be applied to function declarations with prototypes}}
+void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D.zero(newValue:)")));
+
+Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+void badSetter1(void) __attribute__((swift_name("getter:bad1())"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+
+Point3D badGetter2(Point3D point) __attribute__((swift_name("getter:bad2(_:))"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+
+void badSetter2(Point3D point) __attribute__((swift_name("setter:bad2(self:))"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+
+// --- swift_error ---
+
+@class NSError;
+
+typedef struct __attribute__((objc_bridge(NSError))) __CFError *CFErrorRef;
+
+@interface Erroneous
+- (_Bool) tom0: (NSError**) err __attribute__((swift_error(none)));
+- (_Bool) tom1: (NSError**) err __attribute__((swift_error(nonnull_error)));
+- (_Bool) tom2: (NSError**) err __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute with 'null_result' convention can only be applied to a method returning a pointer}}
+- (_Bool) tom3: (NSError**) err __attribute__((swift_error(nonzero_result)));
+- (_Bool) tom4: (NSError**) err __attribute__((swift_error(zero_result)));
+
+- (Undeclared) richard0: (NSError**) err __attribute__((swift_error(none))); // expected-error {{expected a type}}
+- (Undeclared) richard1: (NSError**) err __attribute__((swift_error(nonnull_error))); // expected-error {{expected a type}}
+- (Undeclared) richard2: (NSError**) err __attribute__((swift_error(null_result))); // expected-error {{expected a type}}
+// FIXME: the follow-on warnings should really be suppressed, but apparently having an ill-formed return type doesn't mark anything as invalid
+- (Undeclared) richard3: (NSError**) err __attribute__((swift_error(nonzero_result))); // expected-error {{expected a type}} expected-error {{can only be applied}}
+- (Undeclared) richard4: (NSError**) err __attribute__((swift_error(zero_result))); // expected-error {{expected a type}} expected-error {{can only be applied}}
+
+- (instancetype) harry0: (NSError**) err __attribute__((swift_error(none)));
+- (instancetype) harry1: (NSError**) err __attribute__((swift_error(nonnull_error)));
+- (instancetype) harry2: (NSError**) err __attribute__((swift_error(null_result)));
+- (instancetype) harry3: (NSError**) err __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute with 'nonzero_result' convention can only be applied to a method returning an integral type}}
+- (instancetype) harry4: (NSError**) err __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute with 'zero_result' convention can only be applied to a method returning an integral type}}
+
+- (instancetype) harry0 __attribute__((swift_error(none)));
+- (instancetype) harry1 __attribute__((swift_error(nonnull_error))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}}
+- (instancetype) harry2 __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}}
+- (instancetype) harry3 __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}}
+- (instancetype) harry4 __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}}
+@end
+
+extern _Bool tom0(CFErrorRef *) __attribute__((swift_error(none)));
+extern _Bool tom1(CFErrorRef *) __attribute__((swift_error(nonnull_error)));
+extern _Bool tom2(CFErrorRef *) __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute with 'null_result' convention can only be applied to a function returning a pointer}}
+extern _Bool tom3(CFErrorRef *) __attribute__((swift_error(nonzero_result)));
+extern _Bool tom4(CFErrorRef *) __attribute__((swift_error(zero_result)));
+
+extern Undeclared richard0(CFErrorRef *) __attribute__((swift_error(none))); // expected-error {{unknown type name 'Undeclared'}}
+extern Undeclared richard1(CFErrorRef *) __attribute__((swift_error(nonnull_error))); // expected-error {{unknown type name 'Undeclared'}}
+extern Undeclared richard2(CFErrorRef *) __attribute__((swift_error(null_result))); // expected-error {{unknown type name 'Undeclared'}}
+extern Undeclared richard3(CFErrorRef *) __attribute__((swift_error(nonzero_result))); // expected-error {{unknown type name 'Undeclared'}}
+extern Undeclared richard4(CFErrorRef *) __attribute__((swift_error(zero_result))); // expected-error {{unknown type name 'Undeclared'}}
+
+extern void *harry0(CFErrorRef *) __attribute__((swift_error(none)));
+extern void *harry1(CFErrorRef *) __attribute__((swift_error(nonnull_error)));
+extern void *harry2(CFErrorRef *) __attribute__((swift_error(null_result)));
+extern void *harry3(CFErrorRef *) __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute with 'nonzero_result' convention can only be applied to a function returning an integral type}}
+extern void *harry4(CFErrorRef *) __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute with 'zero_result' convention can only be applied to a function returning an integral type}}
+
+extern void *wilma0(void) __attribute__((swift_error(none)));
+extern void *wilma1(void) __attribute__((swift_error(nonnull_error))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}}
+extern void *wilma2(void) __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}}
+extern void *wilma3(void) __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}}
+extern void *wilma4(void) __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}}
+
+
+extern _Bool suzanne __attribute__((swift_error(none))); // expected-error {{'swift_error' attribute only applies to functions and methods}}
diff --git a/test/SemaObjC/attr-swift_newtype.c b/test/SemaObjC/attr-swift_newtype.c
new file mode 100644
index 0000000..61e4d89
--- /dev/null
+++ b/test/SemaObjC/attr-swift_newtype.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -verify -fsyntax-only %s
+
+typedef int T1 __attribute__((swift_newtype(struct)));
+typedef int T2 __attribute__((swift_newtype(enum)));
+
+typedef int T3 __attribute__((swift_wrapper(struct)));
+typedef int T4 __attribute__((swift_wrapper(enum)));
+
+
+typedef int Bad1 __attribute__((swift_newtype(bad))); // expected-warning{{'swift_newtype' attribute argument not supported: 'bad'}}
+typedef int Bad2 __attribute__((swift_newtype())); // expected-error{{argument required after attribute}}
+typedef int Bad3 __attribute__((swift_newtype(bad, badder)));
+ // expected-error@-1{{expected ')'}}
+ // expected-note@-2{{to match this '('}}
+ // expected-warning@-3{{'swift_newtype' attribute argument not supported: 'bad'}}
+
+
+// TODO: better error message below
+// FIXME: why is this a parse error, rather than Sema error triggering?
+struct Bad4 __attribute__((swift_newtype(struct))) { }; // expected-error{{expected identifier or '('}}
diff --git a/test/SemaObjC/format-strings-objc.m b/test/SemaObjC/format-strings-objc.m
index 767d5ac..d5e3220 100644
--- a/test/SemaObjC/format-strings-objc.m
+++ b/test/SemaObjC/format-strings-objc.m
@@ -265,40 +265,17 @@
NSLog(@"%2$[tt]@ %1$[tt]s", @"Foo", @"Bar"); // expected-warning {{object format flags cannot be used with 's' conversion specifier}}
}
-// rdar://23622446
-@interface RD23622446_Tester: NSObject
+// Test os_log_format primitive with ObjC string literal format argument.
+void test_os_log_format(char c, const char *pc, int i, int *pi, void *p, void *buf, NSString *nss) {
+ __builtin_os_log_format(buf, @"");
+ __builtin_os_log_format(buf, @"%d"); // expected-warning {{more '%' conversions than data arguments}}
+ __builtin_os_log_format(buf, @"%d", i);
+ __builtin_os_log_format(buf, @"%P", p); // expected-warning {{using '%P' format specifier without precision}}
+ __builtin_os_log_format(buf, @"%.10P", p);
+ __builtin_os_log_format(buf, @"%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}}
+ __builtin_os_log_format(buf, @"%.*P", i, p);
+ __builtin_os_log_format(buf, @"%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}}
-+ (void)stringWithFormat:(const char *)format, ... __attribute__((format(__printf__, 1, 2)));
-
-@end
-
-@implementation RD23622446_Tester
-
-__attribute__ ((format_arg(1)))
-const char *rd23622446(const char *format) {
- return format;
+ __builtin_os_log_format(buf, @"%{private}s", pc);
+ __builtin_os_log_format(buf, @"%@", nss);
}
-
-+ (void)stringWithFormat:(const char *)format, ... {
- return;
-}
-
-- (const char *)test:(const char *)format __attribute__ ((format_arg(1))) {
- return format;
-}
-
-- (NSString *)str:(NSString *)format __attribute__ ((format_arg(1))) {
- return format;
-}
-
-- (void)foo {
- [RD23622446_Tester stringWithFormat:rd23622446("%u"), 1, 2]; // expected-warning {{data argument not used by format string}}
- [RD23622446_Tester stringWithFormat:[self test: "%u"], 1, 2]; // expected-warning {{data argument not used by format string}}
- [RD23622446_Tester stringWithFormat:[self test: "%s %s"], "name"]; // expected-warning {{more '%' conversions than data arguments}}
- NSLog([self str: @"%@ %@"], @"name"); // expected-warning {{more '%' conversions than data arguments}}
- [RD23622446_Tester stringWithFormat:rd23622446("%d"), 1]; // ok
- [RD23622446_Tester stringWithFormat:[self test: "%d %d"], 1, 2]; // ok
- NSLog([self str: @"%@"], @"string"); // ok
-}
-
-@end
diff --git a/test/SemaObjC/kindof.m b/test/SemaObjC/kindof.m
index 9d758d3..63ba18f 100644
--- a/test/SemaObjC/kindof.m
+++ b/test/SemaObjC/kindof.m
@@ -385,7 +385,7 @@
@end
@interface NSGeneric<ObjectType> : NSObject
-- (void)test:(__kindof ObjectType)T; // expected-note{{passing argument to parameter 'T' here}}
+- (void)test:(__kindof ObjectType)T;
- (void)mapUsingBlock:(id (^)(__kindof ObjectType))block;
@end
@implementation NSGeneric
@@ -395,14 +395,6 @@
}
@end
-void testGeneric(NSGeneric<NSString*> *generic) {
- NSObject *NSObject_obj;
- // Assign from NSObject_obj to __kindof NSString*.
- [generic test:NSObject_obj]; // expected-warning{{incompatible pointer types sending 'NSObject *' to parameter of type '__kindof NSString *'}}
- NSString *NSString_str;
- [generic test:NSString_str];
-}
-
// Check that clang doesn't crash when a type parameter is illegal.
@interface Array1<T> : NSObject
@end
diff --git a/test/SemaObjC/parameterized_classes_subst.m b/test/SemaObjC/parameterized_classes_subst.m
index da2d56f..f90ee90 100644
--- a/test/SemaObjC/parameterized_classes_subst.m
+++ b/test/SemaObjC/parameterized_classes_subst.m
@@ -426,36 +426,3 @@
// warning about likely protocol/class name typos.
// --------------------------------------------------------------------------
typedef NSArray<NSObject> ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}}
-
-// rdar://25060179
-@interface MyMutableDictionary<KeyType, ObjectType> : NSObject
-- (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key; // expected-note{{passing argument to parameter 'obj' here}} \
- // expected-note{{passing argument to parameter 'key' here}}
-@end
-
-void bar(MyMutableDictionary<NSString *, NSString *> *stringsByString,
- NSNumber *n1, NSNumber *n2) {
- // We warn here when the key types do not match.
- stringsByString[n1] = n2; // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} \
- // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString<NSCopying> *'}}
-}
-
-@interface MyTest<K, V> : NSObject <NSCopying>
-- (V)test:(K)key;
-- (V)test2:(K)key; // expected-note{{previous definition is here}}
-- (void)mapUsingBlock:(id (^)(V))block;
-- (void)mapUsingBlock2:(id (^)(V))block; // expected-note{{previous definition is here}}
-@end
-
-@implementation MyTest
-- (id)test:(id)key {
- return key;
-}
-- (int)test2:(id)key{ // expected-warning{{conflicting return type in implementation}}
- return 0;
-}
-- (void)mapUsingBlock:(id (^)(id))block {
-}
-- (void)mapUsingBlock2:(id)block { // expected-warning{{conflicting parameter types in implementation}}
-}
-@end
diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt
index e0df503..7244e2c 100644
--- a/tools/c-index-test/CMakeLists.txt
+++ b/tools/c-index-test/CMakeLists.txt
@@ -24,8 +24,10 @@
libclang
clangAST
clangBasic
+ clangCodeGen
clangFrontend
clangIndex
+ clangSerialization
)
endif()
diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp
index 0ab24fb..f371870 100644
--- a/tools/c-index-test/core_main.cpp
+++ b/tools/c-index-test/core_main.cpp
@@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
@@ -15,6 +16,7 @@
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Index/CodegenNameGenerator.h"
+#include "clang/Serialization/ASTReader.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
@@ -49,6 +51,17 @@
"invocation\n"
);
+static cl::opt<bool>
+DumpModuleImports("dump-imported-module-files",
+ cl::desc("Print symbols and input files from imported modules"));
+
+static cl::opt<std::string>
+ModuleFilePath("module-file",
+ cl::desc("Path to module file to print symbols from"));
+static cl::opt<std::string>
+ ModuleFormat("fmodule-format", cl::init("raw"),
+ cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'"));
+
}
} // anonymous namespace
@@ -134,7 +147,19 @@
// Print Source Symbols
//===----------------------------------------------------------------------===//
-static bool printSourceSymbols(ArrayRef<const char *> Args) {
+static void dumpModuleFileInputs(serialization::ModuleFile &Mod,
+ ASTReader &Reader,
+ raw_ostream &OS) {
+ OS << "---- Module Inputs ----\n";
+ Reader.visitInputFiles(Mod, /*IncludeSystem=*/true, /*Complain=*/false,
+ [&](const serialization::InputFile &IF, bool isSystem) {
+ OS << (isSystem ? "system" : "user") << " | ";
+ OS << IF.getFile()->getName() << '\n';
+ });
+}
+
+static bool printSourceSymbols(ArrayRef<const char *> Args,
+ bool dumpModuleImports) {
SmallVector<const char *, 4> ArgsWithProgName;
ArgsWithProgName.push_back("clang");
ArgsWithProgName.append(Args.begin(), Args.end());
@@ -144,7 +169,8 @@
if (!CInvok)
return true;
- auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(outs());
+ raw_ostream &OS = outs();
+ auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS);
IndexingOptions IndexOpts;
std::unique_ptr<FrontendAction> IndexAction;
IndexAction = createIndexingAction(DataConsumer, IndexOpts,
@@ -157,6 +183,50 @@
if (!Unit)
return true;
+ if (dumpModuleImports) {
+ if (auto Reader = Unit->getASTReader()) {
+ Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool {
+ OS << "==== Module " << Mod.ModuleName << " ====\n";
+ indexModuleFile(Mod, *Reader, DataConsumer, IndexOpts);
+ dumpModuleFileInputs(Mod, *Reader, OS);
+ return true; // skip module dependencies.
+ });
+ }
+ }
+
+ return false;
+}
+
+static bool printSourceSymbolsFromModule(StringRef modulePath,
+ StringRef format) {
+ FileSystemOptions FileSystemOpts;
+ auto pchContOps = std::make_shared<PCHContainerOperations>();
+ // Register the support for object-file-wrapped Clang modules.
+ pchContOps->registerReader(llvm::make_unique<ObjectFilePCHContainerReader>());
+ auto pchRdr = pchContOps->getReaderOrNull(format);
+ if (!pchRdr) {
+ errs() << "unknown module format: " << format << '\n';
+ return true;
+ }
+
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ CompilerInstance::createDiagnostics(new DiagnosticOptions());
+ std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
+ modulePath, *pchRdr, Diags,
+ FileSystemOpts, /*UseDebugInfo=*/false,
+ /*OnlyLocalDecls=*/true, None,
+ /*CaptureDiagnostics=*/false,
+ /*AllowPCHWithCompilerErrors=*/true,
+ /*UserFilesAreVolatile=*/false);
+ if (!AU) {
+ errs() << "failed to create TU for: " << modulePath << '\n';
+ return true;
+ }
+
+ auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(outs());
+ IndexingOptions IndexOpts;
+ indexASTUnit(*AU, DataConsumer, IndexOpts);
+
return false;
}
@@ -219,11 +289,15 @@
}
if (options::Action == ActionType::PrintSourceSymbols) {
+ if (!options::ModuleFilePath.empty()) {
+ return printSourceSymbolsFromModule(options::ModuleFilePath,
+ options::ModuleFormat);
+ }
if (CompArgs.empty()) {
errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n";
return 1;
}
- return printSourceSymbols(CompArgs);
+ return printSourceSymbols(CompArgs, options::DumpModuleImports);
}
return 0;
diff --git a/tools/clang-check/CMakeLists.txt b/tools/clang-check/CMakeLists.txt
index 04151a8..233f981 100644
--- a/tools/clang-check/CMakeLists.txt
+++ b/tools/clang-check/CMakeLists.txt
@@ -9,6 +9,7 @@
)
target_link_libraries(clang-check
+ clangAPINotes
clangAST
clangBasic
clangDriver
diff --git a/tools/clang-format/ClangFormat.cpp b/tools/clang-format/ClangFormat.cpp
index 6c50daf..c097239 100644
--- a/tools/clang-format/ClangFormat.cpp
+++ b/tools/clang-format/ClangFormat.cpp
@@ -249,8 +249,7 @@
if (fillRanges(Code.get(), Ranges))
return true;
StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
- FormatStyle FormatStyle =
- getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer());
+ FormatStyle FormatStyle = getStyle(Style, AssumedFileName, FallbackStyle);
if (SortIncludes.getNumOccurrences() != 0)
FormatStyle.SortIncludes = SortIncludes;
unsigned CursorPosition = Cursor;
diff --git a/tools/clang-offload-bundler/ClangOffloadBundler.cpp b/tools/clang-offload-bundler/ClangOffloadBundler.cpp
index 20988b4..f37868f 100644
--- a/tools/clang-offload-bundler/ClangOffloadBundler.cpp
+++ b/tools/clang-offload-bundler/ClangOffloadBundler.cpp
@@ -514,7 +514,10 @@
// Dump the contents of the temporary file if that was requested.
if (DumpTemporaryFiles) {
errs() << ";\n; Object file bundler IR file.\n;\n";
- AuxModule.get()->dump();
+ AuxModule.get()->print(errs(), nullptr,
+ /*ShouldPreserveUseListOrder=*/false,
+ /*IsForDebug=*/true);
+ errs() << '\n';
}
// Find clang in order to create the bundle binary.
diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt
index f6e26fa..9dfe8cf 100644
--- a/tools/driver/CMakeLists.txt
+++ b/tools/driver/CMakeLists.txt
@@ -32,6 +32,7 @@
driver.cpp
cc1_main.cpp
cc1as_main.cpp
+ apinotes_main.cpp
DEPENDS
${tablegen_deps}
@@ -39,6 +40,7 @@
target_link_libraries(clang
clangBasic
+ clangAPINotes
clangCodeGen
clangDriver
clangFrontend
diff --git a/tools/driver/apinotes_main.cpp b/tools/driver/apinotes_main.cpp
new file mode 100644
index 0000000..930dc39
--- /dev/null
+++ b/tools/driver/apinotes_main.cpp
@@ -0,0 +1,154 @@
+//===-- api_notes.cpp - API Notes Driver ----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file provides conversion between the YAML (source) and binary forms
+/// of API notes.
+///
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/APINotesYAMLCompiler.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/Triple.h"
+
+using namespace llvm;
+namespace api_notes = clang::api_notes;
+
+int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0,
+ void *MainAddr) {
+
+ // Mark all our options with this category, everything else (except for
+ // -version and -help) will be hidden.
+ static cl::OptionCategory APINotesCategory("API Notes options");
+
+ static cl::opt<api_notes::ActionType>
+ Action(cl::desc("Mode:"), cl::init(api_notes::ActionType::None),
+ cl::values(
+ clEnumValN(api_notes::ActionType::YAMLToBinary,
+ "yaml-to-binary",
+ "Convert YAML to binary format"),
+ clEnumValN(api_notes::ActionType::BinaryToYAML,
+ "binary-to-yaml",
+ "Convert binary format to YAML"),
+ clEnumValN(api_notes::ActionType::Dump,
+ "dump",
+ "Parse and dump the output")),
+ cl::cat(APINotesCategory));
+
+ static cl::opt<std::string>
+ InputFilename(cl::Positional, cl::desc("<input file>"),
+ cl::Required, cl::cat(APINotesCategory));
+
+ static cl::opt<std::string>
+ Target("target", cl::desc("Generate binary format for the given target"),
+ cl::cat(APINotesCategory));
+
+ static cl::opt<std::string>
+ OutputFilename("o", cl::desc("Output file name"), cl::cat(APINotesCategory));
+
+ cl::HideUnrelatedOptions(APINotesCategory);
+
+ SmallVector<const char *, 4> Args;
+ Args.push_back(Argv0);
+ Args.append(Argv.begin(), Argv.end());
+ cl::ParseCommandLineOptions(Args.size(),
+ Args.data(),
+ "Clang API Notes Tool\n");
+
+ if (Action == clang::api_notes::ActionType::None) {
+ errs() << "action required\n";
+ cl::PrintHelpMessage();
+ return 1;
+ }
+
+ auto fileBufOrErr = MemoryBuffer::getFile(InputFilename);
+ if (std::error_code EC = fileBufOrErr.getError()) {
+ llvm::errs() << "\n Could not open input file: " + EC.message() << '\n';
+ return true;
+ }
+ StringRef input = fileBufOrErr.get()->getBuffer();
+
+ switch (Action) {
+ case api_notes::ActionType::None:
+ llvm_unreachable("handled above");
+
+ case api_notes::ActionType::YAMLToBinary: {
+ if (OutputFilename.empty()) {
+ errs() << "output file is required\n";
+ cl::PrintHelpMessage();
+ return 1;
+ }
+
+ api_notes::OSType targetOS = api_notes::OSType::Absent;
+ // TODO: Check that we've specified the target.
+ if (!Target.empty()) {
+ llvm::Triple target(llvm::Triple::normalize(Target));
+ switch (target.getOS()) {
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX:
+ targetOS = api_notes::OSType::OSX;
+ break;
+ case llvm::Triple::IOS:
+ targetOS = api_notes::OSType::IOS;
+ break;
+ case llvm::Triple::WatchOS:
+ targetOS = api_notes::OSType::WatchOS;
+ break;
+ case llvm::Triple::TvOS:
+ targetOS = api_notes::OSType::TvOS;
+ break;
+ default:
+ errs() << "target is not supported\n";
+ return 1;
+ }
+ }
+ std::error_code EC;
+ llvm::raw_fd_ostream os(OutputFilename, EC,
+ llvm::sys::fs::OpenFlags::F_None);
+
+ if (api_notes::compileAPINotes(input, /*sourceFile=*/nullptr, os, targetOS))
+ return 1;
+
+ os.flush();
+
+ return os.has_error();
+ }
+
+ case api_notes::ActionType::BinaryToYAML: {
+ if (OutputFilename.empty()) {
+ errs() << "output file required\n";
+ cl::PrintHelpMessage();
+ return 1;
+ }
+
+ std::error_code EC;
+ llvm::raw_fd_ostream os(OutputFilename, EC,
+ llvm::sys::fs::OpenFlags::F_None);
+
+ if (api_notes::decompileAPINotes(std::move(fileBufOrErr.get()), os))
+ return 1;
+
+ os.flush();
+
+ return os.has_error();
+ }
+
+ case api_notes::ActionType::Dump:
+ return api_notes::parseAndDumpAPINotes(input);
+ }
+
+ return 1;
+}
+
diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp
index 6161302..45b1ad3 100644
--- a/tools/driver/driver.cpp
+++ b/tools/driver/driver.cpp
@@ -198,6 +198,8 @@
void *MainAddr);
extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
void *MainAddr);
+extern int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0,
+ void *MainAddr);
static void insertTargetAndModeArgs(StringRef Target, StringRef Mode,
SmallVectorImpl<const char *> &ArgVector,
@@ -299,6 +301,8 @@
return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP);
if (Tool == "as")
return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP);
+ if (Tool == "apinotes")
+ return cc1apinotes_main(argv.slice(2), argv[0], GetExecutablePathVP);
// Reject unknown tools.
llvm::errs() << "error: unknown integrated tool '" << Tool << "'\n";
diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt
index 2dd6703..5cad9dc 100644
--- a/tools/libclang/CMakeLists.txt
+++ b/tools/libclang/CMakeLists.txt
@@ -35,6 +35,7 @@
set(LIBS
clangAST
+ clangAPINotes
clangBasic
clangFrontend
clangIndex
diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp
index cb8aebf..6231742 100644
--- a/tools/libclang/CXIndexDataConsumer.cpp
+++ b/tools/libclang/CXIndexDataConsumer.cpp
@@ -1314,6 +1314,7 @@
case SymbolLanguage::C: return CXIdxEntityLang_C;
case SymbolLanguage::ObjC: return CXIdxEntityLang_ObjC;
case SymbolLanguage::CXX: return CXIdxEntityLang_CXX;
+ case SymbolLanguage::Swift: llvm_unreachable("unexpected Swift symbol language");;
}
llvm_unreachable("invalid symbol language");
}
diff --git a/unittests/AST/DeclTest.cpp b/unittests/AST/DeclTest.cpp
index 87aeef4..67b80ac 100644
--- a/unittests/AST/DeclTest.cpp
+++ b/unittests/AST/DeclTest.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "MatchVerifier.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
@@ -57,3 +58,53 @@
"constexpr _Complex __uint128_t c = 0xffffffffffffffff;",
Args));
}
+
+TEST(Decl, Availability) {
+ const char *CodeStr = "int x __attribute__((availability(macosx, "
+ "introduced=10.2, deprecated=10.8, obsoleted=10.10)));";
+ auto Matcher = varDecl(hasName("x"));
+ std::vector<std::string> Args = {"-target", "x86_64-apple-macosx10.9"};
+
+ class AvailabilityVerifier : public MatchVerifier<clang::VarDecl> {
+ public:
+ void verify(const MatchFinder::MatchResult &Result,
+ const clang::VarDecl &Node) override {
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 1)) !=
+ clang::AR_NotYetIntroduced) {
+ setFailure("failed introduced");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 2)) !=
+ clang::AR_Available) {
+ setFailure("failed available (exact)");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 3)) !=
+ clang::AR_Available) {
+ setFailure("failed available");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 8)) !=
+ clang::AR_Deprecated) {
+ setFailure("failed deprecated (exact)");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 9)) !=
+ clang::AR_Deprecated) {
+ setFailure("failed deprecated");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 10)) !=
+ clang::AR_Unavailable) {
+ setFailure("failed obsoleted (exact)");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 11)) !=
+ clang::AR_Unavailable) {
+ setFailure("failed obsoleted");
+ }
+
+ if (Node.getAvailability() != clang::AR_Deprecated)
+ setFailure("did not default to target OS version");
+
+ setSuccess();
+ }
+ };
+
+ AvailabilityVerifier Verifier;
+ EXPECT_TRUE(Verifier.match(CodeStr, Matcher, Args, Lang_C));
+}
diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt
index 3cb3cb8..3a9f34f 100644
--- a/unittests/Basic/CMakeLists.txt
+++ b/unittests/Basic/CMakeLists.txt
@@ -6,6 +6,7 @@
CharInfoTest.cpp
DiagnosticTest.cpp
FileManagerTest.cpp
+ MemoryBufferCacheTest.cpp
SourceManagerTest.cpp
VirtualFileSystemTest.cpp
)
diff --git a/unittests/Basic/MemoryBufferCacheTest.cpp b/unittests/Basic/MemoryBufferCacheTest.cpp
new file mode 100644
index 0000000..be243c2
--- /dev/null
+++ b/unittests/Basic/MemoryBufferCacheTest.cpp
@@ -0,0 +1,94 @@
+//===- MemoryBufferCacheTest.cpp - MemoryBufferCache tests ----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/MemoryBufferCache.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+std::unique_ptr<MemoryBuffer> getBuffer(int I) {
+ SmallVector<char, 8> Bytes;
+ raw_svector_ostream(Bytes) << "data:" << I;
+ return MemoryBuffer::getMemBuffer(StringRef(Bytes.data(), Bytes.size()));
+}
+
+TEST(MemoryBufferCacheTest, addBuffer) {
+ auto B1 = getBuffer(1);
+ auto B2 = getBuffer(2);
+ auto B3 = getBuffer(3);
+ auto *RawB1 = B1.get();
+ auto *RawB2 = B2.get();
+ auto *RawB3 = B3.get();
+
+ // Add a few buffers.
+ MemoryBufferCache Cache;
+ EXPECT_EQ(RawB1, &Cache.addBuffer("1", std::move(B1)));
+ EXPECT_EQ(RawB2, &Cache.addBuffer("2", std::move(B2)));
+ EXPECT_EQ(RawB3, &Cache.addBuffer("3", std::move(B3)));
+ EXPECT_EQ(RawB1, Cache.lookupBuffer("1"));
+ EXPECT_EQ(RawB2, Cache.lookupBuffer("2"));
+ EXPECT_EQ(RawB3, Cache.lookupBuffer("3"));
+ EXPECT_FALSE(Cache.isBufferFinal("1"));
+ EXPECT_FALSE(Cache.isBufferFinal("2"));
+ EXPECT_FALSE(Cache.isBufferFinal("3"));
+
+ // Remove the middle buffer.
+ EXPECT_FALSE(Cache.tryToRemoveBuffer("2"));
+ EXPECT_EQ(nullptr, Cache.lookupBuffer("2"));
+ EXPECT_FALSE(Cache.isBufferFinal("2"));
+
+ // Replace the middle buffer.
+ B2 = getBuffer(2);
+ ASSERT_NE(RawB2, B2.get());
+ RawB2 = B2.get();
+ EXPECT_EQ(RawB2, &Cache.addBuffer("2", std::move(B2)));
+
+ // Check that nothing is final.
+ EXPECT_FALSE(Cache.isBufferFinal("1"));
+ EXPECT_FALSE(Cache.isBufferFinal("2"));
+ EXPECT_FALSE(Cache.isBufferFinal("3"));
+}
+
+TEST(MemoryBufferCacheTest, finalizeCurrentBuffers) {
+ // Add a buffer.
+ MemoryBufferCache Cache;
+ auto B1 = getBuffer(1);
+ auto *RawB1 = B1.get();
+ Cache.addBuffer("1", std::move(B1));
+ ASSERT_FALSE(Cache.isBufferFinal("1"));
+
+ // Finalize it.
+ Cache.finalizeCurrentBuffers();
+ EXPECT_TRUE(Cache.isBufferFinal("1"));
+ EXPECT_TRUE(Cache.tryToRemoveBuffer("1"));
+ EXPECT_EQ(RawB1, Cache.lookupBuffer("1"));
+ EXPECT_TRUE(Cache.isBufferFinal("1"));
+
+ // Repeat.
+ auto B2 = getBuffer(2);
+ auto *RawB2 = B2.get();
+ Cache.addBuffer("2", std::move(B2));
+ EXPECT_FALSE(Cache.isBufferFinal("2"));
+
+ Cache.finalizeCurrentBuffers();
+ EXPECT_TRUE(Cache.isBufferFinal("1"));
+ EXPECT_TRUE(Cache.isBufferFinal("2"));
+ EXPECT_TRUE(Cache.tryToRemoveBuffer("1"));
+ EXPECT_TRUE(Cache.tryToRemoveBuffer("2"));
+ EXPECT_EQ(RawB1, Cache.lookupBuffer("1"));
+ EXPECT_EQ(RawB2, Cache.lookupBuffer("2"));
+ EXPECT_TRUE(Cache.isBufferFinal("1"));
+ EXPECT_TRUE(Cache.isBufferFinal("2"));
+}
+
+} // namespace
diff --git a/unittests/Basic/SourceManagerTest.cpp b/unittests/Basic/SourceManagerTest.cpp
index a967b0e..dddc3f9 100644
--- a/unittests/Basic/SourceManagerTest.cpp
+++ b/unittests/Basic/SourceManagerTest.cpp
@@ -12,6 +12,7 @@
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/MemoryBufferCache.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Lex/HeaderSearch.h"
@@ -78,10 +79,11 @@
SourceMgr.setMainFileID(mainFileID);
VoidModuleLoader ModLoader;
+ MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, &*Target);
Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
- SourceMgr, HeaderInfo, ModLoader,
+ SourceMgr, PCMCache, HeaderInfo, ModLoader,
/*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
@@ -198,10 +200,11 @@
SourceMgr.overrideFileContents(headerFile, std::move(HeaderBuf));
VoidModuleLoader ModLoader;
+ MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, &*Target);
Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
- SourceMgr, HeaderInfo, ModLoader,
+ SourceMgr, PCMCache, HeaderInfo, ModLoader,
/*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
@@ -298,10 +301,11 @@
SourceMgr.overrideFileContents(headerFile, std::move(HeaderBuf));
VoidModuleLoader ModLoader;
+ MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, &*Target);
Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
- SourceMgr, HeaderInfo, ModLoader,
+ SourceMgr, PCMCache, HeaderInfo, ModLoader,
/*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
diff --git a/unittests/Format/CMakeLists.txt b/unittests/Format/CMakeLists.txt
index eb7756a..240be6e 100644
--- a/unittests/Format/CMakeLists.txt
+++ b/unittests/Format/CMakeLists.txt
@@ -7,7 +7,6 @@
FormatTest.cpp
FormatTestJava.cpp
FormatTestJS.cpp
- FormatTestObjC.cpp
FormatTestProto.cpp
FormatTestSelective.cpp
SortImportsTestJS.cpp
diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp
index b402b5c..47e226e 100644
--- a/unittests/Format/FormatTest.cpp
+++ b/unittests/Format/FormatTest.cpp
@@ -2493,6 +2493,42 @@
Style);
}
+TEST_F(FormatTest, FormatObjCTryCatch) {
+ verifyFormat("@try {\n"
+ " f();\n"
+ "} @catch (NSException e) {\n"
+ " @throw;\n"
+ "} @finally {\n"
+ " exit(42);\n"
+ "}");
+ verifyFormat("DEBUG({\n"
+ " @try {\n"
+ " } @finally {\n"
+ " }\n"
+ "});\n");
+}
+
+TEST_F(FormatTest, FormatObjCAutoreleasepool) {
+ FormatStyle Style = getLLVMStyle();
+ verifyFormat("@autoreleasepool {\n"
+ " f();\n"
+ "}\n"
+ "@autoreleasepool {\n"
+ " f();\n"
+ "}\n",
+ Style);
+ Style.BreakBeforeBraces = FormatStyle::BS_Allman;
+ verifyFormat("@autoreleasepool\n"
+ "{\n"
+ " f();\n"
+ "}\n"
+ "@autoreleasepool\n"
+ "{\n"
+ " f();\n"
+ "}\n",
+ Style);
+}
+
TEST_F(FormatTest, StaticInitializers) {
verifyFormat("static SomeClass SC = {1, 'a'};");
@@ -7355,6 +7391,704 @@
verifyGoogleFormat("- foo:(int)foo;");
}
+TEST_F(FormatTest, FormatObjCInterface) {
+ verifyFormat("@interface Foo : NSObject <NSSomeDelegate> {\n"
+ "@public\n"
+ " int field1;\n"
+ "@protected\n"
+ " int field2;\n"
+ "@private\n"
+ " int field3;\n"
+ "@package\n"
+ " int field4;\n"
+ "}\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyGoogleFormat("@interface Foo : NSObject<NSSomeDelegate> {\n"
+ " @public\n"
+ " int field1;\n"
+ " @protected\n"
+ " int field2;\n"
+ " @private\n"
+ " int field3;\n"
+ " @package\n"
+ " int field4;\n"
+ "}\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface /* wait for it */ Foo\n"
+ "+ (id)init;\n"
+ "// Look, a comment!\n"
+ "- (int)answerWith:(int)i;\n"
+ "@end");
+
+ verifyFormat("@interface Foo\n"
+ "@end\n"
+ "@interface Bar\n"
+ "@end");
+
+ verifyFormat("@interface Foo : Bar\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo : /**/ Bar /**/ <Baz, /**/ Quux>\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyGoogleFormat("@interface Foo : Bar<Baz, Quux>\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo (HackStuff)\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo ()\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo (HackStuff) <MyProtocol>\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyGoogleFormat("@interface Foo (HackStuff)<MyProtocol>\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo {\n"
+ " int _i;\n"
+ "}\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo : Bar {\n"
+ " int _i;\n"
+ "}\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo : Bar <Baz, Quux> {\n"
+ " int _i;\n"
+ "}\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo (HackStuff) {\n"
+ " int _i;\n"
+ "}\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo () {\n"
+ " int _i;\n"
+ "}\n"
+ "+ (id)init;\n"
+ "@end");
+
+ verifyFormat("@interface Foo (HackStuff) <MyProtocol> {\n"
+ " int _i;\n"
+ "}\n"
+ "+ (id)init;\n"
+ "@end");
+
+ FormatStyle OnePerLine = getGoogleStyle();
+ OnePerLine.BinPackParameters = false;
+ verifyFormat("@interface aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ()<\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa> {\n"
+ "}",
+ OnePerLine);
+}
+
+TEST_F(FormatTest, FormatObjCImplementation) {
+ verifyFormat("@implementation Foo : NSObject {\n"
+ "@public\n"
+ " int field1;\n"
+ "@protected\n"
+ " int field2;\n"
+ "@private\n"
+ " int field3;\n"
+ "@package\n"
+ " int field4;\n"
+ "}\n"
+ "+ (id)init {\n}\n"
+ "@end");
+
+ verifyGoogleFormat("@implementation Foo : NSObject {\n"
+ " @public\n"
+ " int field1;\n"
+ " @protected\n"
+ " int field2;\n"
+ " @private\n"
+ " int field3;\n"
+ " @package\n"
+ " int field4;\n"
+ "}\n"
+ "+ (id)init {\n}\n"
+ "@end");
+
+ verifyFormat("@implementation Foo\n"
+ "+ (id)init {\n"
+ " if (true)\n"
+ " return nil;\n"
+ "}\n"
+ "// Look, a comment!\n"
+ "- (int)answerWith:(int)i {\n"
+ " return i;\n"
+ "}\n"
+ "+ (int)answerWith:(int)i {\n"
+ " return i;\n"
+ "}\n"
+ "@end");
+
+ verifyFormat("@implementation Foo\n"
+ "@end\n"
+ "@implementation Bar\n"
+ "@end");
+
+ EXPECT_EQ("@implementation Foo : Bar\n"
+ "+ (id)init {\n}\n"
+ "- (void)foo {\n}\n"
+ "@end",
+ format("@implementation Foo : Bar\n"
+ "+(id)init{}\n"
+ "-(void)foo{}\n"
+ "@end"));
+
+ verifyFormat("@implementation Foo {\n"
+ " int _i;\n"
+ "}\n"
+ "+ (id)init {\n}\n"
+ "@end");
+
+ verifyFormat("@implementation Foo : Bar {\n"
+ " int _i;\n"
+ "}\n"
+ "+ (id)init {\n}\n"
+ "@end");
+
+ verifyFormat("@implementation Foo (HackStuff)\n"
+ "+ (id)init {\n}\n"
+ "@end");
+ verifyFormat("@implementation ObjcClass\n"
+ "- (void)method;\n"
+ "{}\n"
+ "@end");
+}
+
+TEST_F(FormatTest, FormatObjCProtocol) {
+ verifyFormat("@protocol Foo\n"
+ "@property(weak) id delegate;\n"
+ "- (NSUInteger)numberOfThings;\n"
+ "@end");
+
+ verifyFormat("@protocol MyProtocol <NSObject>\n"
+ "- (NSUInteger)numberOfThings;\n"
+ "@end");
+
+ verifyGoogleFormat("@protocol MyProtocol<NSObject>\n"
+ "- (NSUInteger)numberOfThings;\n"
+ "@end");
+
+ verifyFormat("@protocol Foo;\n"
+ "@protocol Bar;\n");
+
+ verifyFormat("@protocol Foo\n"
+ "@end\n"
+ "@protocol Bar\n"
+ "@end");
+
+ verifyFormat("@protocol myProtocol\n"
+ "- (void)mandatoryWithInt:(int)i;\n"
+ "@optional\n"
+ "- (void)optional;\n"
+ "@required\n"
+ "- (void)required;\n"
+ "@optional\n"
+ "@property(assign) int madProp;\n"
+ "@end\n");
+
+ verifyFormat("@property(nonatomic, assign, readonly)\n"
+ " int *looooooooooooooooooooooooooooongNumber;\n"
+ "@property(nonatomic, assign, readonly)\n"
+ " NSString *looooooooooooooooooooooooooooongName;");
+
+ verifyFormat("@implementation PR18406\n"
+ "}\n"
+ "@end");
+}
+
+TEST_F(FormatTest, FormatObjCMethodDeclarations) {
+ verifyFormat("- (void)doSomethingWith:(GTMFoo *)theFoo\n"
+ " rect:(NSRect)theRect\n"
+ " interval:(float)theInterval {\n"
+ "}");
+ verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n"
+ " longKeyword:(NSRect)theRect\n"
+ " longerKeyword:(float)theInterval\n"
+ " error:(NSError **)theError {\n"
+ "}");
+ verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n"
+ " longKeyword:(NSRect)theRect\n"
+ " evenLongerKeyword:(float)theInterval\n"
+ " error:(NSError **)theError {\n"
+ "}");
+ verifyFormat("- (instancetype)initXxxxxx:(id<x>)x\n"
+ " y:(id<yyyyyyyyyyyyyyyyyyyy>)y\n"
+ " NS_DESIGNATED_INITIALIZER;",
+ getLLVMStyleWithColumns(60));
+
+ // Continuation indent width should win over aligning colons if the function
+ // name is long.
+ FormatStyle continuationStyle = getGoogleStyle();
+ continuationStyle.ColumnLimit = 40;
+ continuationStyle.IndentWrappedFunctionNames = true;
+ verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n"
+ " dontAlignNamef:(NSRect)theRect {\n"
+ "}",
+ continuationStyle);
+
+ // Make sure we don't break aligning for short parameter names.
+ verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n"
+ " aShortf:(NSRect)theRect {\n"
+ "}",
+ continuationStyle);
+
+ // Format pairs correctly.
+ verifyFormat("- (void)drawRectOn:(id)surface\n"
+ " ofSize:(aaaaaaaa)height\n"
+ " :(size_t)width\n"
+ " atOrigin:(size_t)x\n"
+ " :(size_t)y\n"
+ " aaaaa:(a)yyy\n"
+ " bbb:(d)cccc;");
+ verifyFormat("- (void)drawRectOn:(id)surface ofSize:(aaa)height:(bbb)width;");
+ verifyFormat("- (void)drawRectOn:(id)surface\n"
+ " ofSize:(size_t)height\n"
+ " :(size_t)width;",
+ getLLVMStyleWithColumns(60));
+}
+
+TEST_F(FormatTest, FormatObjCMethodExpr) {
+ verifyFormat("[foo bar:baz];");
+ verifyFormat("return [foo bar:baz];");
+ verifyFormat("return (a)[foo bar:baz];");
+ verifyFormat("f([foo bar:baz]);");
+ verifyFormat("f(2, [foo bar:baz]);");
+ verifyFormat("f(2, a ? b : c);");
+ verifyFormat("[[self initWithInt:4] bar:[baz quux:arrrr]];");
+
+ // Unary operators.
+ verifyFormat("int a = +[foo bar:baz];");
+ verifyFormat("int a = -[foo bar:baz];");
+ verifyFormat("int a = ![foo bar:baz];");
+ verifyFormat("int a = ~[foo bar:baz];");
+ verifyFormat("int a = ++[foo bar:baz];");
+ verifyFormat("int a = --[foo bar:baz];");
+ verifyFormat("int a = sizeof [foo bar:baz];");
+ verifyFormat("int a = alignof [foo bar:baz];", getGoogleStyle());
+ verifyFormat("int a = &[foo bar:baz];");
+ verifyFormat("int a = *[foo bar:baz];");
+ // FIXME: Make casts work, without breaking f()[4].
+ // verifyFormat("int a = (int)[foo bar:baz];");
+ // verifyFormat("return (int)[foo bar:baz];");
+ // verifyFormat("(void)[foo bar:baz];");
+ verifyFormat("return (MyType *)[self.tableView cellForRowAtIndexPath:cell];");
+
+ // Binary operators.
+ verifyFormat("[foo bar:baz], [foo bar:baz];");
+ verifyFormat("[foo bar:baz] = [foo bar:baz];");
+ verifyFormat("[foo bar:baz] *= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] /= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] %= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] += [foo bar:baz];");
+ verifyFormat("[foo bar:baz] -= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] <<= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] >>= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] &= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] ^= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] |= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] ? [foo bar:baz] : [foo bar:baz];");
+ verifyFormat("[foo bar:baz] || [foo bar:baz];");
+ verifyFormat("[foo bar:baz] && [foo bar:baz];");
+ verifyFormat("[foo bar:baz] | [foo bar:baz];");
+ verifyFormat("[foo bar:baz] ^ [foo bar:baz];");
+ verifyFormat("[foo bar:baz] & [foo bar:baz];");
+ verifyFormat("[foo bar:baz] == [foo bar:baz];");
+ verifyFormat("[foo bar:baz] != [foo bar:baz];");
+ verifyFormat("[foo bar:baz] >= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] <= [foo bar:baz];");
+ verifyFormat("[foo bar:baz] > [foo bar:baz];");
+ verifyFormat("[foo bar:baz] < [foo bar:baz];");
+ verifyFormat("[foo bar:baz] >> [foo bar:baz];");
+ verifyFormat("[foo bar:baz] << [foo bar:baz];");
+ verifyFormat("[foo bar:baz] - [foo bar:baz];");
+ verifyFormat("[foo bar:baz] + [foo bar:baz];");
+ verifyFormat("[foo bar:baz] * [foo bar:baz];");
+ verifyFormat("[foo bar:baz] / [foo bar:baz];");
+ verifyFormat("[foo bar:baz] % [foo bar:baz];");
+ // Whew!
+
+ verifyFormat("return in[42];");
+ verifyFormat("for (auto v : in[1]) {\n}");
+ verifyFormat("for (int i = 0; i < in[a]; ++i) {\n}");
+ verifyFormat("for (int i = 0; in[a] < i; ++i) {\n}");
+ verifyFormat("for (int i = 0; i < n; ++i, ++in[a]) {\n}");
+ verifyFormat("for (int i = 0; i < n; ++i, in[a]++) {\n}");
+ verifyFormat("for (int i = 0; i < f(in[a]); ++i, in[a]++) {\n}");
+ verifyFormat("for (id foo in [self getStuffFor:bla]) {\n"
+ "}");
+ verifyFormat("[self aaaaa:MACRO(a, b:, c:)];");
+ verifyFormat("[self aaaaa:(1 + 2) bbbbb:3];");
+ verifyFormat("[self aaaaa:(Type)a bbbbb:3];");
+
+ verifyFormat("[self stuffWithInt:(4 + 2) float:4.5];");
+ verifyFormat("[self stuffWithInt:a ? b : c float:4.5];");
+ verifyFormat("[self stuffWithInt:a ? [self foo:bar] : c];");
+ verifyFormat("[self stuffWithInt:a ? (e ? f : g) : c];");
+ verifyFormat("[cond ? obj1 : obj2 methodWithParam:param]");
+ verifyFormat("[button setAction:@selector(zoomOut:)];");
+ verifyFormat("[color getRed:&r green:&g blue:&b alpha:&a];");
+
+ verifyFormat("arr[[self indexForFoo:a]];");
+ verifyFormat("throw [self errorFor:a];");
+ verifyFormat("@throw [self errorFor:a];");
+
+ verifyFormat("[(id)foo bar:(id)baz quux:(id)snorf];");
+ verifyFormat("[(id)foo bar:(id) ? baz : quux];");
+ verifyFormat("4 > 4 ? (id)a : (id)baz;");
+
+ // This tests that the formatter doesn't break after "backing" but before ":",
+ // which would be at 80 columns.
+ verifyFormat(
+ "void f() {\n"
+ " if ((self = [super initWithContentRect:contentRect\n"
+ " styleMask:styleMask ?: otherMask\n"
+ " backing:NSBackingStoreBuffered\n"
+ " defer:YES]))");
+
+ verifyFormat(
+ "[foo checkThatBreakingAfterColonWorksOk:\n"
+ " [bar ifItDoes:reduceOverallLineLengthLikeInThisCase]];");
+
+ verifyFormat("[myObj short:arg1 // Force line break\n"
+ " longKeyword:arg2 != nil ? arg2 : @\"longKeyword\"\n"
+ " evenLongerKeyword:arg3 ?: @\"evenLongerKeyword\"\n"
+ " error:arg4];");
+ verifyFormat(
+ "void f() {\n"
+ " popup_window_.reset([[RenderWidgetPopupWindow alloc]\n"
+ " initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,\n"
+ " pos.width(), pos.height())\n"
+ " styleMask:NSBorderlessWindowMask\n"
+ " backing:NSBackingStoreBuffered\n"
+ " defer:NO]);\n"
+ "}");
+ verifyFormat(
+ "void f() {\n"
+ " popup_wdow_.reset([[RenderWidgetPopupWindow alloc]\n"
+ " iniithContentRect:NSMakRet(origin_global.x, origin_global.y,\n"
+ " pos.width(), pos.height())\n"
+ " syeMask:NSBorderlessWindowMask\n"
+ " bking:NSBackingStoreBuffered\n"
+ " der:NO]);\n"
+ "}",
+ getLLVMStyleWithColumns(70));
+ verifyFormat(
+ "void f() {\n"
+ " popup_window_.reset([[RenderWidgetPopupWindow alloc]\n"
+ " initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,\n"
+ " pos.width(), pos.height())\n"
+ " styleMask:NSBorderlessWindowMask\n"
+ " backing:NSBackingStoreBuffered\n"
+ " defer:NO]);\n"
+ "}",
+ getChromiumStyle(FormatStyle::LK_Cpp));
+ verifyFormat("[contentsContainer replaceSubview:[subviews objectAtIndex:0]\n"
+ " with:contentsNativeView];");
+
+ verifyFormat(
+ "[pboard addTypes:[NSArray arrayWithObject:kBookmarkButtonDragType]\n"
+ " owner:nillllll];");
+
+ verifyFormat(
+ "[pboard setData:[NSData dataWithBytes:&button length:sizeof(button)]\n"
+ " forType:kBookmarkButtonDragType];");
+
+ verifyFormat("[defaultCenter addObserver:self\n"
+ " selector:@selector(willEnterFullscreen)\n"
+ " name:kWillEnterFullscreenNotification\n"
+ " object:nil];");
+ verifyFormat("[image_rep drawInRect:drawRect\n"
+ " fromRect:NSZeroRect\n"
+ " operation:NSCompositeCopy\n"
+ " fraction:1.0\n"
+ " respectFlipped:NO\n"
+ " hints:nil];");
+ verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];");
+ verifyFormat("[aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaa)\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];");
+ verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaa[aaaaaaaaaaaaaaaaaaaaa]\n"
+ " aaaaaaaaaaaaaaaaaaaaaa];");
+ verifyFormat("[call aaaaaaaa.aaaaaa.aaaaaaaa.aaaaaaaa.aaaaaaaa.aaaaaaaa\n"
+ " .aaaaaaaa];", // FIXME: Indentation seems off.
+ getLLVMStyleWithColumns(60));
+
+ verifyFormat(
+ "scoped_nsobject<NSTextField> message(\n"
+ " // The frame will be fixed up when |-setMessageText:| is called.\n"
+ " [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)]);");
+ verifyFormat("[self aaaaaa:bbbbbbbbbbbbb\n"
+ " aaaaaaaaaa:bbbbbbbbbbbbbbbbb\n"
+ " aaaaa:bbbbbbbbbbb + bbbbbbbbbbbb\n"
+ " aaaa:bbb];");
+ verifyFormat("[self param:function( //\n"
+ " parameter)]");
+ verifyFormat(
+ "[self aaaaaaaaaa:aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa |\n"
+ " aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa |\n"
+ " aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa];");
+
+ // FIXME: This violates the column limit.
+ verifyFormat(
+ "[aaaaaaaaaaaaaaaaaaaaaaaaa\n"
+ " aaaaaaaaaaaaaaaaa:aaaaaaaa\n"
+ " aaa:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];",
+ getLLVMStyleWithColumns(60));
+
+ // Variadic parameters.
+ verifyFormat(
+ "NSArray *myStrings = [NSArray stringarray:@\"a\", @\"b\", nil];");
+ verifyFormat(
+ "[self aaaaaaaaaaaaa:aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa,\n"
+ " aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa,\n"
+ " aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa];");
+ verifyFormat("[self // break\n"
+ " a:a\n"
+ " aaa:aaa];");
+ verifyFormat("bool a = ([aaaaaaaa aaaaa] == aaaaaaaaaaaaaaaaa ||\n"
+ " [aaaaaaaa aaaaa] == aaaaaaaaaaaaaaaaaaaa);");
+
+ // Formats pair-parameters.
+ verifyFormat("[I drawRectOn:surface ofSize:aa:bbb atOrigin:cc:dd];");
+ verifyFormat("[I drawRectOn:surface //\n"
+ " ofSize:aa:bbb\n"
+ " atOrigin:cc:dd];");
+}
+
+TEST_F(FormatTest, ObjCAt) {
+ verifyFormat("@autoreleasepool");
+ verifyFormat("@catch");
+ verifyFormat("@class");
+ verifyFormat("@compatibility_alias");
+ verifyFormat("@defs");
+ verifyFormat("@dynamic");
+ verifyFormat("@encode");
+ verifyFormat("@end");
+ verifyFormat("@finally");
+ verifyFormat("@implementation");
+ verifyFormat("@import");
+ verifyFormat("@interface");
+ verifyFormat("@optional");
+ verifyFormat("@package");
+ verifyFormat("@private");
+ verifyFormat("@property");
+ verifyFormat("@protected");
+ verifyFormat("@protocol");
+ verifyFormat("@public");
+ verifyFormat("@required");
+ verifyFormat("@selector");
+ verifyFormat("@synchronized");
+ verifyFormat("@synthesize");
+ verifyFormat("@throw");
+ verifyFormat("@try");
+
+ EXPECT_EQ("@interface", format("@ interface"));
+
+ // The precise formatting of this doesn't matter, nobody writes code like
+ // this.
+ verifyFormat("@ /*foo*/ interface");
+}
+
+TEST_F(FormatTest, ObjCSnippets) {
+ verifyFormat("@autoreleasepool {\n"
+ " foo();\n"
+ "}");
+ verifyFormat("@class Foo, Bar;");
+ verifyFormat("@compatibility_alias AliasName ExistingClass;");
+ verifyFormat("@dynamic textColor;");
+ verifyFormat("char *buf1 = @encode(int *);");
+ verifyFormat("char *buf1 = @encode(typeof(4 * 5));");
+ verifyFormat("char *buf1 = @encode(int **);");
+ verifyFormat("Protocol *proto = @protocol(p1);");
+ verifyFormat("SEL s = @selector(foo:);");
+ verifyFormat("@synchronized(self) {\n"
+ " f();\n"
+ "}");
+
+ verifyFormat("@synthesize dropArrowPosition = dropArrowPosition_;");
+ verifyGoogleFormat("@synthesize dropArrowPosition = dropArrowPosition_;");
+
+ verifyFormat("@property(assign, nonatomic) CGFloat hoverAlpha;");
+ verifyFormat("@property(assign, getter=isEditable) BOOL editable;");
+ verifyGoogleFormat("@property(assign, getter=isEditable) BOOL editable;");
+ verifyFormat("@property (assign, getter=isEditable) BOOL editable;",
+ getMozillaStyle());
+ verifyFormat("@property BOOL editable;", getMozillaStyle());
+ verifyFormat("@property (assign, getter=isEditable) BOOL editable;",
+ getWebKitStyle());
+ verifyFormat("@property BOOL editable;", getWebKitStyle());
+
+ verifyFormat("@import foo.bar;\n"
+ "@import baz;");
+}
+
+TEST_F(FormatTest, ObjCForIn) {
+ verifyFormat("- (void)test {\n"
+ " for (NSString *n in arrayOfStrings) {\n"
+ " foo(n);\n"
+ " }\n"
+ "}");
+ verifyFormat("- (void)test {\n"
+ " for (NSString *n in (__bridge NSArray *)arrayOfStrings) {\n"
+ " foo(n);\n"
+ " }\n"
+ "}");
+}
+
+TEST_F(FormatTest, ObjCLiterals) {
+ verifyFormat("@\"String\"");
+ verifyFormat("@1");
+ verifyFormat("@+4.8");
+ verifyFormat("@-4");
+ verifyFormat("@1LL");
+ verifyFormat("@.5");
+ verifyFormat("@'c'");
+ verifyFormat("@true");
+
+ verifyFormat("NSNumber *smallestInt = @(-INT_MAX - 1);");
+ verifyFormat("NSNumber *piOverTwo = @(M_PI / 2);");
+ verifyFormat("NSNumber *favoriteColor = @(Green);");
+ verifyFormat("NSString *path = @(getenv(\"PATH\"));");
+
+ verifyFormat("[dictionary setObject:@(1) forKey:@\"number\"];");
+}
+
+TEST_F(FormatTest, ObjCDictLiterals) {
+ verifyFormat("@{");
+ verifyFormat("@{}");
+ verifyFormat("@{@\"one\" : @1}");
+ verifyFormat("return @{@\"one\" : @1;");
+ verifyFormat("@{@\"one\" : @1}");
+
+ verifyFormat("@{@\"one\" : @{@2 : @1}}");
+ verifyFormat("@{\n"
+ " @\"one\" : @{@2 : @1},\n"
+ "}");
+
+ verifyFormat("@{1 > 2 ? @\"one\" : @\"two\" : 1 > 2 ? @1 : @2}");
+ verifyIncompleteFormat("[self setDict:@{}");
+ verifyIncompleteFormat("[self setDict:@{@1 : @2}");
+ verifyFormat("NSLog(@\"%@\", @{@1 : @2, @2 : @3}[@1]);");
+ verifyFormat(
+ "NSDictionary *masses = @{@\"H\" : @1.0078, @\"He\" : @4.0026};");
+ verifyFormat(
+ "NSDictionary *settings = @{AVEncoderKey : @(AVAudioQualityMax)};");
+
+ verifyFormat("NSDictionary *d = @{\n"
+ " @\"nam\" : NSUserNam(),\n"
+ " @\"dte\" : [NSDate date],\n"
+ " @\"processInfo\" : [NSProcessInfo processInfo]\n"
+ "};");
+ verifyFormat(
+ "@{\n"
+ " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee : "
+ "regularFont,\n"
+ "};");
+ verifyGoogleFormat(
+ "@{\n"
+ " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee : "
+ "regularFont,\n"
+ "};");
+ verifyFormat(
+ "@{\n"
+ " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee :\n"
+ " reeeeeeeeeeeeeeeeeeeeeeeegularFont,\n"
+ "};");
+
+ // We should try to be robust in case someone forgets the "@".
+ verifyFormat("NSDictionary *d = {\n"
+ " @\"nam\" : NSUserNam(),\n"
+ " @\"dte\" : [NSDate date],\n"
+ " @\"processInfo\" : [NSProcessInfo processInfo]\n"
+ "};");
+ verifyFormat("NSMutableDictionary *dictionary =\n"
+ " [NSMutableDictionary dictionaryWithDictionary:@{\n"
+ " aaaaaaaaaaaaaaaaaaaaa : aaaaaaaaaaaaa,\n"
+ " bbbbbbbbbbbbbbbbbb : bbbbb,\n"
+ " cccccccccccccccc : ccccccccccccccc\n"
+ " }];");
+
+ // Ensure that casts before the key are kept on the same line as the key.
+ verifyFormat(
+ "NSDictionary *d = @{\n"
+ " (aaaaaaaa id)aaaaaaaaa : (aaaaaaaa id)aaaaaaaaaaaaaaaaaaaaaaaa,\n"
+ " (aaaaaaaa id)aaaaaaaaaaaaaa : (aaaaaaaa id)aaaaaaaaaaaaaa,\n"
+ "};");
+}
+
+TEST_F(FormatTest, ObjCArrayLiterals) {
+ verifyIncompleteFormat("@[");
+ verifyFormat("@[]");
+ verifyFormat(
+ "NSArray *array = @[ @\" Hey \", NSApp, [NSNumber numberWithInt:42] ];");
+ verifyFormat("return @[ @3, @[], @[ @4, @5 ] ];");
+ verifyFormat("NSArray *array = @[ [foo description] ];");
+
+ verifyFormat(
+ "NSArray *some_variable = @[\n"
+ " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ "];");
+ verifyFormat(
+ "NSArray *some_variable = @[\n"
+ " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\"\n"
+ "];");
+ verifyFormat("NSArray *some_variable = @[\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ "];");
+ verifyFormat("NSArray *array = @[\n"
+ " @\"a\",\n"
+ " @\"a\",\n" // Trailing comma -> one per line.
+ "];");
+
+ // We should try to be robust in case someone forgets the "@".
+ verifyFormat("NSArray *some_variable = [\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ " @\"aaaaaaaaaaaaaaaaa\",\n"
+ "];");
+ verifyFormat(
+ "- (NSAttributedString *)attributedStringForSegment:(NSUInteger)segment\n"
+ " index:(NSUInteger)index\n"
+ " nonDigitAttributes:\n"
+ " (NSDictionary *)noDigitAttributes;");
+ verifyFormat("[someFunction someLooooooooooooongParameter:@[\n"
+ " NSBundle.mainBundle.infoDictionary[@\"a\"]\n"
+ "]];");
+}
TEST_F(FormatTest, BreaksStringLiterals) {
EXPECT_EQ("\"some text \"\n"
@@ -10960,13 +11694,13 @@
llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: LLVM")));
ASSERT_TRUE(
FS.addFile("/a/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;")));
- auto Style1 = getStyle("file", "/a/.clang-format", "Google", "", &FS);
+ auto Style1 = getStyle("file", "/a/.clang-format", "Google", &FS);
ASSERT_EQ(Style1, getLLVMStyle());
// Test 2: fallback to default.
ASSERT_TRUE(
FS.addFile("/b/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;")));
- auto Style2 = getStyle("file", "/b/test.cpp", "Mozilla", "", &FS);
+ auto Style2 = getStyle("file", "/b/test.cpp", "Mozilla", &FS);
ASSERT_EQ(Style2, getMozillaStyle());
// Test 3: format file in parent directory.
@@ -10975,7 +11709,7 @@
llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google")));
ASSERT_TRUE(FS.addFile("/c/sub/sub/sub/test.cpp", 0,
llvm::MemoryBuffer::getMemBuffer("int i;")));
- auto Style3 = getStyle("file", "/c/sub/sub/sub/test.cpp", "LLVM", "", &FS);
+ auto Style3 = getStyle("file", "/c/sub/sub/sub/test.cpp", "LLVM", &FS);
ASSERT_EQ(Style3, getGoogleStyle());
}
diff --git a/unittests/Format/FormatTestObjC.cpp b/unittests/Format/FormatTestObjC.cpp
deleted file mode 100644
index 6a530f9..0000000
--- a/unittests/Format/FormatTestObjC.cpp
+++ /dev/null
@@ -1,822 +0,0 @@
-//===- unittest/Format/FormatTestObjC.cpp - Formatting unit tests----------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Format/Format.h"
-
-#include "../Tooling/ReplacementTest.h"
-#include "FormatTestUtils.h"
-
-#include "clang/Frontend/TextDiagnosticPrinter.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "gtest/gtest.h"
-
-#define DEBUG_TYPE "format-test"
-
-using clang::tooling::ReplacementTest;
-using clang::tooling::toReplacements;
-
-namespace clang {
-namespace format {
-namespace {
-
-class FormatTestObjC : public ::testing::Test {
-protected:
- FormatTestObjC() {
- Style = getLLVMStyle();
- Style.Language = FormatStyle::LK_ObjC;
- }
-
- enum IncompleteCheck {
- IC_ExpectComplete,
- IC_ExpectIncomplete,
- IC_DoNotCheck
- };
-
- std::string format(llvm::StringRef Code,
- IncompleteCheck CheckIncomplete = IC_ExpectComplete) {
- DEBUG(llvm::errs() << "---\n");
- DEBUG(llvm::errs() << Code << "\n\n");
- std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
- bool IncompleteFormat = false;
- tooling::Replacements Replaces =
- reformat(Style, Code, Ranges, "<stdin>", &IncompleteFormat);
- if (CheckIncomplete != IC_DoNotCheck) {
- bool ExpectedIncompleteFormat = CheckIncomplete == IC_ExpectIncomplete;
- EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n";
- }
- auto Result = applyAllReplacements(Code, Replaces);
- EXPECT_TRUE(static_cast<bool>(Result));
- DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
- return *Result;
- }
-
- void verifyFormat(StringRef Code) {
- EXPECT_EQ(Code.str(), format(test::messUp(Code)));
- }
-
- void verifyIncompleteFormat(StringRef Code) {
- EXPECT_EQ(Code.str(), format(test::messUp(Code), IC_ExpectIncomplete));
- }
-
- FormatStyle Style;
-};
-
-TEST_F(FormatTestObjC, DetectsObjCInHeaders) {
- Style = getStyle("LLVM", "a.h", "none", "@interface\n"
- "- (id)init;");
- EXPECT_EQ(FormatStyle::LK_ObjC, Style.Language);
- Style = getStyle("LLVM", "a.h", "none", "@interface\n"
- "+ (id)init;");
- EXPECT_EQ(FormatStyle::LK_ObjC, Style.Language);
-
- // No recognizable ObjC.
- Style = getStyle("LLVM", "a.h", "none", "void f() {}");
- EXPECT_EQ(FormatStyle::LK_Cpp, Style.Language);
-}
-
-TEST_F(FormatTestObjC, FormatObjCTryCatch) {
- verifyFormat("@try {\n"
- " f();\n"
- "} @catch (NSException e) {\n"
- " @throw;\n"
- "} @finally {\n"
- " exit(42);\n"
- "}");
- verifyFormat("DEBUG({\n"
- " @try {\n"
- " } @finally {\n"
- " }\n"
- "});\n");
-}
-
-TEST_F(FormatTestObjC, FormatObjCAutoreleasepool) {
- verifyFormat("@autoreleasepool {\n"
- " f();\n"
- "}\n"
- "@autoreleasepool {\n"
- " f();\n"
- "}\n");
- Style.BreakBeforeBraces = FormatStyle::BS_Allman;
- verifyFormat("@autoreleasepool\n"
- "{\n"
- " f();\n"
- "}\n"
- "@autoreleasepool\n"
- "{\n"
- " f();\n"
- "}\n");
-}
-
-TEST_F(FormatTestObjC, FormatObjCInterface) {
- verifyFormat("@interface Foo : NSObject <NSSomeDelegate> {\n"
- "@public\n"
- " int field1;\n"
- "@protected\n"
- " int field2;\n"
- "@private\n"
- " int field3;\n"
- "@package\n"
- " int field4;\n"
- "}\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface /* wait for it */ Foo\n"
- "+ (id)init;\n"
- "// Look, a comment!\n"
- "- (int)answerWith:(int)i;\n"
- "@end");
-
- verifyFormat("@interface Foo\n"
- "@end\n"
- "@interface Bar\n"
- "@end");
-
- verifyFormat("@interface Foo : Bar\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo : /**/ Bar /**/ <Baz, /**/ Quux>\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo (HackStuff)\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo ()\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo (HackStuff) <MyProtocol>\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo {\n"
- " int _i;\n"
- "}\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo : Bar {\n"
- " int _i;\n"
- "}\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo : Bar <Baz, Quux> {\n"
- " int _i;\n"
- "}\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo (HackStuff) {\n"
- " int _i;\n"
- "}\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo () {\n"
- " int _i;\n"
- "}\n"
- "+ (id)init;\n"
- "@end");
-
- verifyFormat("@interface Foo (HackStuff) <MyProtocol> {\n"
- " int _i;\n"
- "}\n"
- "+ (id)init;\n"
- "@end");
-
- Style = getGoogleStyle(FormatStyle::LK_ObjC);
- verifyFormat("@interface Foo : NSObject<NSSomeDelegate> {\n"
- " @public\n"
- " int field1;\n"
- " @protected\n"
- " int field2;\n"
- " @private\n"
- " int field3;\n"
- " @package\n"
- " int field4;\n"
- "}\n"
- "+ (id)init;\n"
- "@end");
- verifyFormat("@interface Foo : Bar<Baz, Quux>\n"
- "+ (id)init;\n"
- "@end");
- verifyFormat("@interface Foo (HackStuff)<MyProtocol>\n"
- "+ (id)init;\n"
- "@end");
- Style.BinPackParameters = false;
- Style.ColumnLimit = 80;
- verifyFormat("@interface aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ()<\n"
- " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
- " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
- " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
- " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa> {\n"
- "}");
-}
-
-TEST_F(FormatTestObjC, FormatObjCImplementation) {
- verifyFormat("@implementation Foo : NSObject {\n"
- "@public\n"
- " int field1;\n"
- "@protected\n"
- " int field2;\n"
- "@private\n"
- " int field3;\n"
- "@package\n"
- " int field4;\n"
- "}\n"
- "+ (id)init {\n}\n"
- "@end");
-
- verifyFormat("@implementation Foo\n"
- "+ (id)init {\n"
- " if (true)\n"
- " return nil;\n"
- "}\n"
- "// Look, a comment!\n"
- "- (int)answerWith:(int)i {\n"
- " return i;\n"
- "}\n"
- "+ (int)answerWith:(int)i {\n"
- " return i;\n"
- "}\n"
- "@end");
-
- verifyFormat("@implementation Foo\n"
- "@end\n"
- "@implementation Bar\n"
- "@end");
-
- EXPECT_EQ("@implementation Foo : Bar\n"
- "+ (id)init {\n}\n"
- "- (void)foo {\n}\n"
- "@end",
- format("@implementation Foo : Bar\n"
- "+(id)init{}\n"
- "-(void)foo{}\n"
- "@end"));
-
- verifyFormat("@implementation Foo {\n"
- " int _i;\n"
- "}\n"
- "+ (id)init {\n}\n"
- "@end");
-
- verifyFormat("@implementation Foo : Bar {\n"
- " int _i;\n"
- "}\n"
- "+ (id)init {\n}\n"
- "@end");
-
- verifyFormat("@implementation Foo (HackStuff)\n"
- "+ (id)init {\n}\n"
- "@end");
- verifyFormat("@implementation ObjcClass\n"
- "- (void)method;\n"
- "{}\n"
- "@end");
-
- Style = getGoogleStyle(FormatStyle::LK_ObjC);
- verifyFormat("@implementation Foo : NSObject {\n"
- " @public\n"
- " int field1;\n"
- " @protected\n"
- " int field2;\n"
- " @private\n"
- " int field3;\n"
- " @package\n"
- " int field4;\n"
- "}\n"
- "+ (id)init {\n}\n"
- "@end");
-}
-
-TEST_F(FormatTestObjC, FormatObjCProtocol) {
- verifyFormat("@protocol Foo\n"
- "@property(weak) id delegate;\n"
- "- (NSUInteger)numberOfThings;\n"
- "@end");
-
- verifyFormat("@protocol MyProtocol <NSObject>\n"
- "- (NSUInteger)numberOfThings;\n"
- "@end");
-
- verifyFormat("@protocol Foo;\n"
- "@protocol Bar;\n");
-
- verifyFormat("@protocol Foo\n"
- "@end\n"
- "@protocol Bar\n"
- "@end");
-
- verifyFormat("@protocol myProtocol\n"
- "- (void)mandatoryWithInt:(int)i;\n"
- "@optional\n"
- "- (void)optional;\n"
- "@required\n"
- "- (void)required;\n"
- "@optional\n"
- "@property(assign) int madProp;\n"
- "@end\n");
-
- verifyFormat("@property(nonatomic, assign, readonly)\n"
- " int *looooooooooooooooooooooooooooongNumber;\n"
- "@property(nonatomic, assign, readonly)\n"
- " NSString *looooooooooooooooooooooooooooongName;");
-
- verifyFormat("@implementation PR18406\n"
- "}\n"
- "@end");
-
- Style = getGoogleStyle(FormatStyle::LK_ObjC);
- verifyFormat("@protocol MyProtocol<NSObject>\n"
- "- (NSUInteger)numberOfThings;\n"
- "@end");
-}
-
-TEST_F(FormatTestObjC, FormatObjCMethodDeclarations) {
- verifyFormat("- (void)doSomethingWith:(GTMFoo *)theFoo\n"
- " rect:(NSRect)theRect\n"
- " interval:(float)theInterval {\n"
- "}");
- verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n"
- " longKeyword:(NSRect)theRect\n"
- " longerKeyword:(float)theInterval\n"
- " error:(NSError **)theError {\n"
- "}");
- verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n"
- " longKeyword:(NSRect)theRect\n"
- " evenLongerKeyword:(float)theInterval\n"
- " error:(NSError **)theError {\n"
- "}");
- Style.ColumnLimit = 60;
- verifyFormat("- (instancetype)initXxxxxx:(id<x>)x\n"
- " y:(id<yyyyyyyyyyyyyyyyyyyy>)y\n"
- " NS_DESIGNATED_INITIALIZER;");
- verifyFormat("- (void)drawRectOn:(id)surface\n"
- " ofSize:(size_t)height\n"
- " :(size_t)width;");
-
- // Continuation indent width should win over aligning colons if the function
- // name is long.
- Style = getGoogleStyle(FormatStyle::LK_ObjC);
- Style.ColumnLimit = 40;
- Style.IndentWrappedFunctionNames = true;
- verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n"
- " dontAlignNamef:(NSRect)theRect {\n"
- "}");
-
- // Make sure we don't break aligning for short parameter names.
- verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n"
- " aShortf:(NSRect)theRect {\n"
- "}");
-
- // Format pairs correctly.
- Style.ColumnLimit = 80;
- verifyFormat("- (void)drawRectOn:(id)surface\n"
- " ofSize:(aaaaaaaa)height\n"
- " :(size_t)width\n"
- " atOrigin:(size_t)x\n"
- " :(size_t)y\n"
- " aaaaa:(a)yyy\n"
- " bbb:(d)cccc;");
- verifyFormat("- (void)drawRectOn:(id)surface ofSize:(aaa)height:(bbb)width;");
-}
-
-TEST_F(FormatTestObjC, FormatObjCMethodExpr) {
- verifyFormat("[foo bar:baz];");
- verifyFormat("return [foo bar:baz];");
- verifyFormat("return (a)[foo bar:baz];");
- verifyFormat("f([foo bar:baz]);");
- verifyFormat("f(2, [foo bar:baz]);");
- verifyFormat("f(2, a ? b : c);");
- verifyFormat("[[self initWithInt:4] bar:[baz quux:arrrr]];");
-
- // Unary operators.
- verifyFormat("int a = +[foo bar:baz];");
- verifyFormat("int a = -[foo bar:baz];");
- verifyFormat("int a = ![foo bar:baz];");
- verifyFormat("int a = ~[foo bar:baz];");
- verifyFormat("int a = ++[foo bar:baz];");
- verifyFormat("int a = --[foo bar:baz];");
- verifyFormat("int a = sizeof [foo bar:baz];");
- verifyFormat("int a = alignof [foo bar:baz];");
- verifyFormat("int a = &[foo bar:baz];");
- verifyFormat("int a = *[foo bar:baz];");
- // FIXME: Make casts work, without breaking f()[4].
- // verifyFormat("int a = (int)[foo bar:baz];");
- // verifyFormat("return (int)[foo bar:baz];");
- // verifyFormat("(void)[foo bar:baz];");
- verifyFormat("return (MyType *)[self.tableView cellForRowAtIndexPath:cell];");
-
- // Binary operators.
- verifyFormat("[foo bar:baz], [foo bar:baz];");
- verifyFormat("[foo bar:baz] = [foo bar:baz];");
- verifyFormat("[foo bar:baz] *= [foo bar:baz];");
- verifyFormat("[foo bar:baz] /= [foo bar:baz];");
- verifyFormat("[foo bar:baz] %= [foo bar:baz];");
- verifyFormat("[foo bar:baz] += [foo bar:baz];");
- verifyFormat("[foo bar:baz] -= [foo bar:baz];");
- verifyFormat("[foo bar:baz] <<= [foo bar:baz];");
- verifyFormat("[foo bar:baz] >>= [foo bar:baz];");
- verifyFormat("[foo bar:baz] &= [foo bar:baz];");
- verifyFormat("[foo bar:baz] ^= [foo bar:baz];");
- verifyFormat("[foo bar:baz] |= [foo bar:baz];");
- verifyFormat("[foo bar:baz] ? [foo bar:baz] : [foo bar:baz];");
- verifyFormat("[foo bar:baz] || [foo bar:baz];");
- verifyFormat("[foo bar:baz] && [foo bar:baz];");
- verifyFormat("[foo bar:baz] | [foo bar:baz];");
- verifyFormat("[foo bar:baz] ^ [foo bar:baz];");
- verifyFormat("[foo bar:baz] & [foo bar:baz];");
- verifyFormat("[foo bar:baz] == [foo bar:baz];");
- verifyFormat("[foo bar:baz] != [foo bar:baz];");
- verifyFormat("[foo bar:baz] >= [foo bar:baz];");
- verifyFormat("[foo bar:baz] <= [foo bar:baz];");
- verifyFormat("[foo bar:baz] > [foo bar:baz];");
- verifyFormat("[foo bar:baz] < [foo bar:baz];");
- verifyFormat("[foo bar:baz] >> [foo bar:baz];");
- verifyFormat("[foo bar:baz] << [foo bar:baz];");
- verifyFormat("[foo bar:baz] - [foo bar:baz];");
- verifyFormat("[foo bar:baz] + [foo bar:baz];");
- verifyFormat("[foo bar:baz] * [foo bar:baz];");
- verifyFormat("[foo bar:baz] / [foo bar:baz];");
- verifyFormat("[foo bar:baz] % [foo bar:baz];");
- // Whew!
-
- verifyFormat("return in[42];");
- verifyFormat("for (auto v : in[1]) {\n}");
- verifyFormat("for (int i = 0; i < in[a]; ++i) {\n}");
- verifyFormat("for (int i = 0; in[a] < i; ++i) {\n}");
- verifyFormat("for (int i = 0; i < n; ++i, ++in[a]) {\n}");
- verifyFormat("for (int i = 0; i < n; ++i, in[a]++) {\n}");
- verifyFormat("for (int i = 0; i < f(in[a]); ++i, in[a]++) {\n}");
- verifyFormat("for (id foo in [self getStuffFor:bla]) {\n"
- "}");
- verifyFormat("[self aaaaa:MACRO(a, b:, c:)];");
- verifyFormat("[self aaaaa:(1 + 2) bbbbb:3];");
- verifyFormat("[self aaaaa:(Type)a bbbbb:3];");
-
- verifyFormat("[self stuffWithInt:(4 + 2) float:4.5];");
- verifyFormat("[self stuffWithInt:a ? b : c float:4.5];");
- verifyFormat("[self stuffWithInt:a ? [self foo:bar] : c];");
- verifyFormat("[self stuffWithInt:a ? (e ? f : g) : c];");
- verifyFormat("[cond ? obj1 : obj2 methodWithParam:param]");
- verifyFormat("[button setAction:@selector(zoomOut:)];");
- verifyFormat("[color getRed:&r green:&g blue:&b alpha:&a];");
-
- verifyFormat("arr[[self indexForFoo:a]];");
- verifyFormat("throw [self errorFor:a];");
- verifyFormat("@throw [self errorFor:a];");
-
- verifyFormat("[(id)foo bar:(id)baz quux:(id)snorf];");
- verifyFormat("[(id)foo bar:(id) ? baz : quux];");
- verifyFormat("4 > 4 ? (id)a : (id)baz;");
-
- // This tests that the formatter doesn't break after "backing" but before ":",
- // which would be at 80 columns.
- verifyFormat(
- "void f() {\n"
- " if ((self = [super initWithContentRect:contentRect\n"
- " styleMask:styleMask ?: otherMask\n"
- " backing:NSBackingStoreBuffered\n"
- " defer:YES]))");
-
- verifyFormat(
- "[foo checkThatBreakingAfterColonWorksOk:\n"
- " [bar ifItDoes:reduceOverallLineLengthLikeInThisCase]];");
-
- verifyFormat("[myObj short:arg1 // Force line break\n"
- " longKeyword:arg2 != nil ? arg2 : @\"longKeyword\"\n"
- " evenLongerKeyword:arg3 ?: @\"evenLongerKeyword\"\n"
- " error:arg4];");
- verifyFormat(
- "void f() {\n"
- " popup_window_.reset([[RenderWidgetPopupWindow alloc]\n"
- " initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,\n"
- " pos.width(), pos.height())\n"
- " styleMask:NSBorderlessWindowMask\n"
- " backing:NSBackingStoreBuffered\n"
- " defer:NO]);\n"
- "}");
- verifyFormat("[contentsContainer replaceSubview:[subviews objectAtIndex:0]\n"
- " with:contentsNativeView];");
-
- verifyFormat(
- "[pboard addTypes:[NSArray arrayWithObject:kBookmarkButtonDragType]\n"
- " owner:nillllll];");
-
- verifyFormat(
- "[pboard setData:[NSData dataWithBytes:&button length:sizeof(button)]\n"
- " forType:kBookmarkButtonDragType];");
-
- verifyFormat("[defaultCenter addObserver:self\n"
- " selector:@selector(willEnterFullscreen)\n"
- " name:kWillEnterFullscreenNotification\n"
- " object:nil];");
- verifyFormat("[image_rep drawInRect:drawRect\n"
- " fromRect:NSZeroRect\n"
- " operation:NSCompositeCopy\n"
- " fraction:1.0\n"
- " respectFlipped:NO\n"
- " hints:nil];");
- verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
- " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];");
- verifyFormat("[aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaa)\n"
- " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];");
- verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaa[aaaaaaaaaaaaaaaaaaaaa]\n"
- " aaaaaaaaaaaaaaaaaaaaaa];");
-
- verifyFormat(
- "scoped_nsobject<NSTextField> message(\n"
- " // The frame will be fixed up when |-setMessageText:| is called.\n"
- " [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)]);");
- verifyFormat("[self aaaaaa:bbbbbbbbbbbbb\n"
- " aaaaaaaaaa:bbbbbbbbbbbbbbbbb\n"
- " aaaaa:bbbbbbbbbbb + bbbbbbbbbbbb\n"
- " aaaa:bbb];");
- verifyFormat("[self param:function( //\n"
- " parameter)]");
- verifyFormat(
- "[self aaaaaaaaaa:aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa |\n"
- " aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa |\n"
- " aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa];");
-
- // Variadic parameters.
- verifyFormat(
- "NSArray *myStrings = [NSArray stringarray:@\"a\", @\"b\", nil];");
- verifyFormat(
- "[self aaaaaaaaaaaaa:aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa,\n"
- " aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa,\n"
- " aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa];");
- verifyFormat("[self // break\n"
- " a:a\n"
- " aaa:aaa];");
- verifyFormat("bool a = ([aaaaaaaa aaaaa] == aaaaaaaaaaaaaaaaa ||\n"
- " [aaaaaaaa aaaaa] == aaaaaaaaaaaaaaaaaaaa);");
-
- // Formats pair-parameters.
- verifyFormat("[I drawRectOn:surface ofSize:aa:bbb atOrigin:cc:dd];");
- verifyFormat("[I drawRectOn:surface //\n"
- " ofSize:aa:bbb\n"
- " atOrigin:cc:dd];");
-
- Style.ColumnLimit = 70;
- verifyFormat(
- "void f() {\n"
- " popup_wdow_.reset([[RenderWidgetPopupWindow alloc]\n"
- " iniithContentRect:NSMakRet(origin_global.x, origin_global.y,\n"
- " pos.width(), pos.height())\n"
- " syeMask:NSBorderlessWindowMask\n"
- " bking:NSBackingStoreBuffered\n"
- " der:NO]);\n"
- "}");
-
- Style.ColumnLimit = 60;
- verifyFormat("[call aaaaaaaa.aaaaaa.aaaaaaaa.aaaaaaaa.aaaaaaaa.aaaaaaaa\n"
- " .aaaaaaaa];"); // FIXME: Indentation seems off.
- // FIXME: This violates the column limit.
- verifyFormat(
- "[aaaaaaaaaaaaaaaaaaaaaaaaa\n"
- " aaaaaaaaaaaaaaaaa:aaaaaaaa\n"
- " aaa:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];");
-
- Style = getChromiumStyle(FormatStyle::LK_ObjC);
- Style.ColumnLimit = 80;
- verifyFormat(
- "void f() {\n"
- " popup_window_.reset([[RenderWidgetPopupWindow alloc]\n"
- " initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,\n"
- " pos.width(), pos.height())\n"
- " styleMask:NSBorderlessWindowMask\n"
- " backing:NSBackingStoreBuffered\n"
- " defer:NO]);\n"
- "}");
-}
-
-TEST_F(FormatTestObjC, ObjCAt) {
- verifyFormat("@autoreleasepool");
- verifyFormat("@catch");
- verifyFormat("@class");
- verifyFormat("@compatibility_alias");
- verifyFormat("@defs");
- verifyFormat("@dynamic");
- verifyFormat("@encode");
- verifyFormat("@end");
- verifyFormat("@finally");
- verifyFormat("@implementation");
- verifyFormat("@import");
- verifyFormat("@interface");
- verifyFormat("@optional");
- verifyFormat("@package");
- verifyFormat("@private");
- verifyFormat("@property");
- verifyFormat("@protected");
- verifyFormat("@protocol");
- verifyFormat("@public");
- verifyFormat("@required");
- verifyFormat("@selector");
- verifyFormat("@synchronized");
- verifyFormat("@synthesize");
- verifyFormat("@throw");
- verifyFormat("@try");
-
- EXPECT_EQ("@interface", format("@ interface"));
-
- // The precise formatting of this doesn't matter, nobody writes code like
- // this.
- verifyFormat("@ /*foo*/ interface");
-}
-
-TEST_F(FormatTestObjC, ObjCSnippets) {
- verifyFormat("@autoreleasepool {\n"
- " foo();\n"
- "}");
- verifyFormat("@class Foo, Bar;");
- verifyFormat("@compatibility_alias AliasName ExistingClass;");
- verifyFormat("@dynamic textColor;");
- verifyFormat("char *buf1 = @encode(int *);");
- verifyFormat("char *buf1 = @encode(typeof(4 * 5));");
- verifyFormat("char *buf1 = @encode(int **);");
- verifyFormat("Protocol *proto = @protocol(p1);");
- verifyFormat("SEL s = @selector(foo:);");
- verifyFormat("@synchronized(self) {\n"
- " f();\n"
- "}");
-
- verifyFormat("@import foo.bar;\n"
- "@import baz;");
-
- verifyFormat("@synthesize dropArrowPosition = dropArrowPosition_;");
-
- verifyFormat("@property(assign, nonatomic) CGFloat hoverAlpha;");
- verifyFormat("@property(assign, getter=isEditable) BOOL editable;");
-
- Style = getMozillaStyle();
- verifyFormat("@property (assign, getter=isEditable) BOOL editable;");
- verifyFormat("@property BOOL editable;");
-
- Style = getWebKitStyle();
- verifyFormat("@property (assign, getter=isEditable) BOOL editable;");
- verifyFormat("@property BOOL editable;");
-
- Style = getGoogleStyle(FormatStyle::LK_ObjC);
- verifyFormat("@synthesize dropArrowPosition = dropArrowPosition_;");
- verifyFormat("@property(assign, getter=isEditable) BOOL editable;");
-}
-
-TEST_F(FormatTestObjC, ObjCForIn) {
- verifyFormat("- (void)test {\n"
- " for (NSString *n in arrayOfStrings) {\n"
- " foo(n);\n"
- " }\n"
- "}");
- verifyFormat("- (void)test {\n"
- " for (NSString *n in (__bridge NSArray *)arrayOfStrings) {\n"
- " foo(n);\n"
- " }\n"
- "}");
-}
-
-TEST_F(FormatTestObjC, ObjCLiterals) {
- verifyFormat("@\"String\"");
- verifyFormat("@1");
- verifyFormat("@+4.8");
- verifyFormat("@-4");
- verifyFormat("@1LL");
- verifyFormat("@.5");
- verifyFormat("@'c'");
- verifyFormat("@true");
-
- verifyFormat("NSNumber *smallestInt = @(-INT_MAX - 1);");
- verifyFormat("NSNumber *piOverTwo = @(M_PI / 2);");
- verifyFormat("NSNumber *favoriteColor = @(Green);");
- verifyFormat("NSString *path = @(getenv(\"PATH\"));");
-
- verifyFormat("[dictionary setObject:@(1) forKey:@\"number\"];");
-}
-
-TEST_F(FormatTestObjC, ObjCDictLiterals) {
- verifyFormat("@{");
- verifyFormat("@{}");
- verifyFormat("@{@\"one\" : @1}");
- verifyFormat("return @{@\"one\" : @1;");
- verifyFormat("@{@\"one\" : @1}");
-
- verifyFormat("@{@\"one\" : @{@2 : @1}}");
- verifyFormat("@{\n"
- " @\"one\" : @{@2 : @1},\n"
- "}");
-
- verifyFormat("@{1 > 2 ? @\"one\" : @\"two\" : 1 > 2 ? @1 : @2}");
- verifyIncompleteFormat("[self setDict:@{}");
- verifyIncompleteFormat("[self setDict:@{@1 : @2}");
- verifyFormat("NSLog(@\"%@\", @{@1 : @2, @2 : @3}[@1]);");
- verifyFormat(
- "NSDictionary *masses = @{@\"H\" : @1.0078, @\"He\" : @4.0026};");
- verifyFormat(
- "NSDictionary *settings = @{AVEncoderKey : @(AVAudioQualityMax)};");
-
- verifyFormat("NSDictionary *d = @{\n"
- " @\"nam\" : NSUserNam(),\n"
- " @\"dte\" : [NSDate date],\n"
- " @\"processInfo\" : [NSProcessInfo processInfo]\n"
- "};");
- verifyFormat(
- "@{\n"
- " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee : "
- "regularFont,\n"
- "};");
- verifyFormat(
- "@{\n"
- " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee :\n"
- " reeeeeeeeeeeeeeeeeeeeeeeegularFont,\n"
- "};");
-
- // We should try to be robust in case someone forgets the "@".
- verifyFormat("NSDictionary *d = {\n"
- " @\"nam\" : NSUserNam(),\n"
- " @\"dte\" : [NSDate date],\n"
- " @\"processInfo\" : [NSProcessInfo processInfo]\n"
- "};");
- verifyFormat("NSMutableDictionary *dictionary =\n"
- " [NSMutableDictionary dictionaryWithDictionary:@{\n"
- " aaaaaaaaaaaaaaaaaaaaa : aaaaaaaaaaaaa,\n"
- " bbbbbbbbbbbbbbbbbb : bbbbb,\n"
- " cccccccccccccccc : ccccccccccccccc\n"
- " }];");
-
- // Ensure that casts before the key are kept on the same line as the key.
- verifyFormat(
- "NSDictionary *d = @{\n"
- " (aaaaaaaa id)aaaaaaaaa : (aaaaaaaa id)aaaaaaaaaaaaaaaaaaaaaaaa,\n"
- " (aaaaaaaa id)aaaaaaaaaaaaaa : (aaaaaaaa id)aaaaaaaaaaaaaa,\n"
- "};");
-
- Style = getGoogleStyle(FormatStyle::LK_ObjC);
- verifyFormat(
- "@{\n"
- " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee : "
- "regularFont,\n"
- "};");
-}
-
-TEST_F(FormatTestObjC, ObjCArrayLiterals) {
- verifyIncompleteFormat("@[");
- verifyFormat("@[]");
- verifyFormat(
- "NSArray *array = @[ @\" Hey \", NSApp, [NSNumber numberWithInt:42] ];");
- verifyFormat("return @[ @3, @[], @[ @4, @5 ] ];");
- verifyFormat("NSArray *array = @[ [foo description] ];");
-
- verifyFormat(
- "NSArray *some_variable = @[\n"
- " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- "];");
- verifyFormat(
- "NSArray *some_variable = @[\n"
- " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\"\n"
- "];");
- verifyFormat("NSArray *some_variable = @[\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- "];");
- verifyFormat("NSArray *array = @[\n"
- " @\"a\",\n"
- " @\"a\",\n" // Trailing comma -> one per line.
- "];");
-
- // We should try to be robust in case someone forgets the "@".
- verifyFormat("NSArray *some_variable = [\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- " @\"aaaaaaaaaaaaaaaaa\",\n"
- "];");
- verifyFormat(
- "- (NSAttributedString *)attributedStringForSegment:(NSUInteger)segment\n"
- " index:(NSUInteger)index\n"
- " nonDigitAttributes:\n"
- " (NSDictionary *)noDigitAttributes;");
- verifyFormat("[someFunction someLooooooooooooongParameter:@[\n"
- " NSBundle.mainBundle.infoDictionary[@\"a\"]\n"
- "]];");
-}
-} // end namespace
-} // end namespace format
-} // end namespace clang
diff --git a/unittests/Lex/CMakeLists.txt b/unittests/Lex/CMakeLists.txt
index ef0f06c..2002fbf 100644
--- a/unittests/Lex/CMakeLists.txt
+++ b/unittests/Lex/CMakeLists.txt
@@ -13,6 +13,7 @@
clangAST
clangBasic
clangLex
+ clangAPINotes
clangParse
clangSema
)
diff --git a/unittests/Lex/LexerTest.cpp b/unittests/Lex/LexerTest.cpp
index 918167b..b5a6fd9 100644
--- a/unittests/Lex/LexerTest.cpp
+++ b/unittests/Lex/LexerTest.cpp
@@ -12,6 +12,7 @@
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/MemoryBufferCache.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
@@ -64,10 +65,12 @@
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
VoidModuleLoader ModLoader;
+ MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, Target.get());
Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
- SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr,
+ SourceMgr, PCMCache, HeaderInfo, ModLoader,
+ /*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
PP.EnterMainSourceFile();
diff --git a/unittests/Lex/PPCallbacksTest.cpp b/unittests/Lex/PPCallbacksTest.cpp
index 064abaf..0841791 100644
--- a/unittests/Lex/PPCallbacksTest.cpp
+++ b/unittests/Lex/PPCallbacksTest.cpp
@@ -14,6 +14,7 @@
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/MemoryBufferCache.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
@@ -161,13 +162,14 @@
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
VoidModuleLoader ModLoader;
+ MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, Target.get());
AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
- SourceMgr, HeaderInfo, ModLoader,
+ SourceMgr, PCMCache, HeaderInfo, ModLoader,
/*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
@@ -198,11 +200,12 @@
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
VoidModuleLoader ModLoader;
+ MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, OpenCLLangOpts, Target.get());
Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags,
- OpenCLLangOpts, SourceMgr, HeaderInfo, ModLoader,
+ OpenCLLangOpts, SourceMgr, PCMCache, HeaderInfo, ModLoader,
/*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
diff --git a/unittests/Lex/PPConditionalDirectiveRecordTest.cpp b/unittests/Lex/PPConditionalDirectiveRecordTest.cpp
index dccfffd..b635604 100644
--- a/unittests/Lex/PPConditionalDirectiveRecordTest.cpp
+++ b/unittests/Lex/PPConditionalDirectiveRecordTest.cpp
@@ -12,6 +12,7 @@
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/MemoryBufferCache.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
@@ -93,10 +94,11 @@
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
VoidModuleLoader ModLoader;
+ MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, Target.get());
Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
- SourceMgr, HeaderInfo, ModLoader,
+ SourceMgr, PCMCache, HeaderInfo, ModLoader,
/*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
diff --git a/unittests/Tooling/ReplacementTest.h b/unittests/Tooling/ReplacementTest.h
index b6fe5c7..91530f0 100644
--- a/unittests/Tooling/ReplacementTest.h
+++ b/unittests/Tooling/ReplacementTest.h
@@ -24,7 +24,7 @@
/// \brief Converts a set of replacements to Replacements class.
/// \return A Replacements class containing \p Replaces on success; otherwise,
/// an empty Replacements is returned.
-inline tooling::Replacements
+static tooling::Replacements
toReplacements(const std::set<tooling::Replacement> &Replaces) {
tooling::Replacements Result;
for (const auto &R : Replaces) {
diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp
index 27ab34c..8ba2ebc 100644
--- a/utils/TableGen/ClangAttrEmitter.cpp
+++ b/utils/TableGen/ClangAttrEmitter.cpp
@@ -1160,6 +1160,32 @@
}
};
+ class AttrArgument : public SimpleArgument {
+ public:
+ AttrArgument(const Record &Arg, StringRef Attr)
+ : SimpleArgument(Arg, Attr, "Attr *")
+ {}
+
+ void writePCHReadDecls(raw_ostream &OS) const override {
+ OS << " AttrVec vec;\n"
+ " ReadAttributes(F, vec, Record, Idx);\n"
+ " assert(vec.size() == 1);\n"
+ " Attr *" << getLowerName() << " = vec.front();";
+ }
+
+ void writePCHWrite(raw_ostream &OS) const override {
+ OS << " AddAttributes(SA->get" << getUpperName() << "());";
+ }
+
+ void writeDump(raw_ostream &OS) const override {}
+
+ void writeDumpChildren(raw_ostream &OS) const override {
+ OS << " dumpAttr(SA->get" << getUpperName() << "());\n";
+ }
+
+ void writeHasChildren(raw_ostream &OS) const override { OS << "true"; }
+ };
+
} // end anonymous namespace
static std::unique_ptr<Argument>
@@ -1207,6 +1233,8 @@
Ptr = llvm::make_unique<VariadicExprArgument>(Arg, Attr);
else if (ArgName == "VersionArgument")
Ptr = llvm::make_unique<VersionArgument>(Arg, Attr);
+ else if (ArgName == "AttrArgument")
+ Ptr = llvm::make_unique<AttrArgument>(Arg, Attr);
if (!Ptr) {
// Search in reverse order so that the most-derived type is handled first.
diff --git a/utils/perf-training/lit.cfg b/utils/perf-training/lit.cfg
index edae551..16c9367 100644
--- a/utils/perf-training/lit.cfg
+++ b/utils/perf-training/lit.cfg
@@ -7,7 +7,7 @@
def getSysrootFlagsOnDarwin(config, lit_config):
# On Darwin, support relocatable SDKs by providing Clang with a
# default system root path.
- if 'darwin' in config.target_triple:
+ if lit.util.isMacOSTriple(config.target_triple):
try:
out = lit.util.capture(['xcrun', '--show-sdk-path']).strip()
res = 0
diff --git a/utils/perf-training/order-files.lit.cfg b/utils/perf-training/order-files.lit.cfg
index a4fd812..3ea3c9e 100644
--- a/utils/perf-training/order-files.lit.cfg
+++ b/utils/perf-training/order-files.lit.cfg
@@ -8,7 +8,7 @@
def getSysrootFlagsOnDarwin(config, lit_config):
# On Darwin, support relocatable SDKs by providing Clang with a
# default system root path.
- if 'darwin' in config.target_triple:
+ if lit.util.isMacOSTriple(config.target_triple):
try:
out = lit.util.capture(['xcrun', '--show-sdk-path']).strip()
res = 0