/// <summary>
        /// Same as <see cref="ParseTranslationUnit"/> but requires a full command line for
        /// <paramref name="commandLineArgs"/> including args[0]. This is useful if the standard
        /// library paths are relative to the binary.
        /// </summary>
        /// <param name="sourceFileName">
        /// The name of the source file to load, or null if the source file is included in
        /// <paramref name="commandLineArgs"/>.
        /// </param>
        /// <param name="commandLineArgs">
        /// The command-line arguments that would be passed to the clang executable if it were
        /// being invoked out-of-process. These command-line options will be parsed and will affect
        /// how the translation unit is parsed. Note that the following options are ignored: '-c',
        /// '-emit-ast', '-fsyntax-only' (which is the default), and '-o &lt;output file&gt;'.
        /// </param>
        /// <param name="unsavedFiles">
        /// The files that have not yet been saved to disk but may be required for parsing,
        /// including the contents of those files.
        /// </param>
        /// <param name="options">
        /// A bitmask of options that affects how the translation unit is managed but not its
        /// compilation. This should be a bitwise OR of the
        /// <see cref="TranslationUnitCreationOptions"/> flags.
        /// </param>
        /// <returns>
        /// The created <see cref="TranslationUnit"/>, describing the parsed code and containing
        /// any diagnostics produced by the compiler.
        /// </returns>
        /// <exception cref="ErrorCodeException">
        /// The error code returned by libclang is not <see cref="ErrorCode.Success"/>.
        /// </exception>
        public TranslationUnit ParseTranslationUnitFullArgv(
            string sourceFileName,
            IEnumerable <string> commandLineArgs,
            IEnumerable <UnsavedFile> unsavedFiles = null,
            TranslationUnitCreationOptions options = TranslationUnitCreationOptions.None)
        {
            ThrowIfDisposed();

            using (var cString = new CString(sourceFileName))
                using (var args = new CStrings(commandLineArgs))
                    using (var files = new CXUnsavedFiles(unsavedFiles))
                    {
                        var argsPtr = stackalloc sbyte *[args.Count];
                        args.Apply((arg, i) => argsPtr[i] = arg.Ptr);
                        var filesPtr = stackalloc CXUnsavedFile[files.Count];
                        files.Apply((file, i) => filesPtr[i] = files[i]);

                        CXTranslationUnitImpl *ptr;
                        NativeMethods.clang_parseTranslationUnit2FullArgv(
                            Ptr,
                            cString.Ptr,
                            argsPtr,
                            args.Count,
                            filesPtr,
                            (uint)files.Count,
                            (uint)options,
                            &ptr).Check();
                        return(new TranslationUnit(ptr, this));
                    }
        }
        /// <summary>
        /// Gets a source file within the translation unit.
        /// </summary>
        /// <param name="fileName">The name of the file.</param>
        /// <returns>
        /// The <see cref="SourceFile"/> object for the named file in the translation unit, or null
        /// if the file was not a part of this translation unit.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="fileName"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="fileName"/> is empty.
        /// </exception>
        public SourceFile GetFile(string fileName)
        {
            Requires.NotNullOrEmpty(fileName, nameof(fileName));
            ThrowIfDisposed();

            using (var cString = new CString(fileName))
            {
                var ptr = NativeMethods.clang_getFile(Ptr, cString.Ptr);
                return(ptr == null ? null : new SourceFile(ptr, this));
            }
        }
        /// <summary>
        /// Gets the offset of a field named <paramref name="fieldName"/> in a record of type T in
        /// bits as it would be returned by __offsetof__ as per C++11 [18.2p4].
        /// </summary>
        /// <param name="fieldName">The name of the field.</param>
        /// <returns>
        /// error:
        /// <para>
        /// If the cursor is not a record field declaration, <see cref="TypeLayoutError.Invalid"/>
        /// is returned.
        /// </para>
        /// <para>
        /// If the field's type declaration is an incomplete type,
        /// <see cref="TypeLayoutError.Incomplete"/> is returned.
        /// </para>
        /// <para>
        /// If the field's type declaration is a dependent type,
        /// <see cref="TypeLayoutError.Dependent"/> is returned.
        /// </para>
        /// <para>
        /// If the field's name <paramref name="fieldName"/> is not found,
        /// <see cref="TypeLayoutError.InvalidFieldName"/> is returned.
        /// </para>
        /// <para>offset: The offset of the field named <paramref name="fieldName"/>.</para>
        /// </returns>
        public (TypeLayoutError?error, long offset) TryGetOffsetOf(string fieldName)
        {
            Requires.NotNullOrEmpty(fieldName, nameof(fieldName));
            ThrowIfDisposed();

            using (var cString = new CString(fieldName))
            {
                long offset = NativeMethods.clang_Type_getOffsetOf(Struct, cString.Ptr);
                var  error  = offset < 0 ? (TypeLayoutError?)offset : null;
                return(error : error, offset : offset);
            }
        }
        /// <summary>
        /// Saves a translation unit into a serialized representation of that translation unit on
        /// disk.
        /// </summary>
        /// <param name="fileName">The file to which the translation unit will be saved.</param>
        /// <returns>
        /// <see cref="TranslationUnitSaveError.None"/> if the translation unit was saved
        /// successfully, otherwise a problem occurred.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="fileName"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="fileName"/> is empty.
        /// </exception>
        public TranslationUnitSaveError TrySave(string fileName)
        {
            Requires.NotNullOrEmpty(fileName, nameof(fileName));
            ThrowIfDisposed();

            using (var cString = new CString(fileName))
            {
                return((TranslationUnitSaveError)NativeMethods.clang_saveTranslationUnit(
                           Ptr,
                           cString.Ptr,
                           (uint)CXSaveTranslationUnit_Flags.CXSaveTranslationUnit_None));
            }
        }
        /// <summary>
        /// Create a translation unit from an AST file (-emit-ast).
        /// </summary>
        /// <param name="astFileName">The path to the AST file.</param>
        /// <returns>
        /// The translation unit from the AST file.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="astFileName"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="astFileName"/> is empty.
        /// </exception>
        /// <exception cref="ErrorCodeException">
        /// The error code returned by libclang is not <see cref="ErrorCode.Success"/>.
        /// </exception>
        public TranslationUnit CreateTranslationUnit(string astFileName)
        {
            Requires.NotNullOrEmpty(astFileName, nameof(astFileName));
            ThrowIfDisposed();

            using (var cString = new CString(astFileName))
            {
                CXTranslationUnitImpl *ptr;
                NativeMethods.clang_createTranslationUnit2(
                    Ptr,
                    cString.Ptr,
                    &ptr).Check();
                return(new TranslationUnit(ptr, this));
            }
        }