diff options
Diffstat (limited to 'elpa/irony-20220110.849/server')
34 files changed, 3475 insertions, 0 deletions
| diff --git a/elpa/irony-20220110.849/server/.clang-format b/elpa/irony-20220110.849/server/.clang-format new file mode 100644 index 0000000..68e7062 --- /dev/null +++ b/elpa/irony-20220110.849/server/.clang-format @@ -0,0 +1,8 @@ +BasedOnStyle: LLVM +AllowShortFunctionsOnASingleLine: false +AlwaysBreakTemplateDeclarations: true +BinPackParameters: false +BreakConstructorInitializersBeforeComma: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +IndentFunctionDeclarationAfterType: false diff --git a/elpa/irony-20220110.849/server/.clang-tidy b/elpa/irony-20220110.849/server/.clang-tidy new file mode 100644 index 0000000..57e038a --- /dev/null +++ b/elpa/irony-20220110.849/server/.clang-tidy @@ -0,0 +1,24 @@ +Checks: '-*,readability-identifier-naming,misc-assert-side-effect,readability-container-size-empty' +CheckOptions: +  - key: readability-identifier-naming.TypedefCase +    value: CamelCase +  - key: readability-identifier-naming.StructCase +    value: CamelCase +  - key: readability-identifier-naming.ClassCase +    value: CamelCase +  - key: readability-identifier-naming.VariableCase +    value: camelBack +  - key: readability-identifier-naming.ParameterCase +    value: camelBack +  - key: readability-identifier-naming.FunctionCase +    value: camelBack +  - key: readability-identifier-naming.NamespaceCase +    value: lower_case +  - key: readability-identifier-naming.GlobalConstantCase +    value: UPPER_CASE +  - key: readability-identifier-naming.PrivateMemberSuffix +    value: _ +  - key: misc-assert-side-effect.CheckFunctionCalls +    value: 1 +  - key: misc-assert-side-effect.AssertMacros +    value: 'assert' diff --git a/elpa/irony-20220110.849/server/CMakeLists.txt b/elpa/irony-20220110.849/server/CMakeLists.txt new file mode 100644 index 0000000..21f9c9f --- /dev/null +++ b/elpa/irony-20220110.849/server/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.0.2) + +project(IronyMode) + +set(CMAKE_MODULE_PATH +  ${PROJECT_SOURCE_DIR}/cmake +  ${PROJECT_SOURCE_DIR}/cmake/modules +  ${CMAKE_MODULE_PATH}) + +include(CTest) +include(GNUInstallDirs) + +# Starting from CMake >= 3.1, if a specific standard is required, +# it can be set from the command line with: +#     cmake -DCMAKE_CXX_STANDARD=[11|14|17] +function(irony_target_set_cxx_standard target) +  set(cxx_standard 11) +  if (CMAKE_VERSION VERSION_LESS "3.1") +    if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") +      target_compile_options(${target} PRIVATE -std=c++${cxx_standard}) +    endif() +  elseif (CMAKE_VERSION VERSION_LESS "3.8") +    set_property(TARGET ${target} PROPERTY CXX_STANDARD ${cxx_standard}) +  else() +    target_compile_features(${target} PUBLIC cxx_std_${cxx_standard}) +  endif() +endfunction() + +add_subdirectory(src) + +if (BUILD_TESTING) +  add_subdirectory(test) +endif() diff --git a/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/LICENSE.TXT b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/LICENSE.TXT new file mode 100644 index 0000000..a7f579f --- /dev/null +++ b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/LICENSE.TXT @@ -0,0 +1,43 @@ +============================================================================== +LLVM Release License +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + +    LLVM Team + +    University of Illinois at Urbana-Champaign + +    http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +    * Redistributions of source code must retain the above copyright notice, +      this list of conditions and the following disclaimers. + +    * Redistributions in binary form must reproduce the above copyright notice, +      this list of conditions and the following disclaimers in the +      documentation and/or other materials provided with the distribution. + +    * Neither the names of the LLVM Team, University of Illinois at +      Urbana-Champaign, nor the names of its contributors may be used to +      endorse or promote products derived from this Software without specific +      prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/README b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/README new file mode 100644 index 0000000..ed43509 --- /dev/null +++ b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/README @@ -0,0 +1,12 @@ +Origin: +- http://llvm.org/svn/llvm-project/clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py?p=294607 +- https://github.com/llvm-mirror/clang-tools-extra/blob/c2e903ec98385b82e35bdb303e411854a2e8c032/clang-tidy/tool/run-clang-tidy.py + +Modifications: +- the python version has been frozen to python2, +  as the script is not python3-compatible +- added -warnings-as-errors option +- the run-clang-tidy.py script has been modified +  to return a sensible exit code when running on Travis CI, +  i.e. it honors exit code of the underlying processes +  causing build failures on pull requests diff --git a/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/run-clang-tidy.py b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/run-clang-tidy.py new file mode 100755 index 0000000..17db708 --- /dev/null +++ b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/run-clang-tidy.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python2 +# +#===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- python -*--===# +# +#                     The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# FIXME: Integrate with clang-tidy-diff.py + +""" +Parallel clang-tidy runner +========================== + +Runs clang-tidy over all files in a compilation database. Requires clang-tidy +and clang-apply-replacements in $PATH. + +Example invocations. +- Run clang-tidy on all files in the current working directory with a default +  set of checks and show warnings in the cpp files and all project headers. +    run-clang-tidy.py $PWD + +- Fix all header guards. +    run-clang-tidy.py -fix -checks=-*,llvm-header-guard + +- Fix all header guards included from clang-tidy and header guards +  for clang-tidy headers. +    run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \ +                      -header-filter=extra/clang-tidy + +Compilation database setup: +http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html +""" + +import argparse +import json +import multiprocessing +import os +import Queue +import re +import shutil +import subprocess +import sys +import tempfile +import threading + + +def find_compilation_database(path): +  """Adjusts the directory until a compilation database is found.""" +  result = './' +  while not os.path.isfile(os.path.join(result, path)): +    if os.path.realpath(result) == '/': +      print 'Error: could not find compilation database.' +      sys.exit(1) +    result += '../' +  return os.path.realpath(result) + + +def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path, +                        header_filter, extra_arg, extra_arg_before, quiet, +                        warnings_as_errors): +  """Gets a command line for clang-tidy.""" +  start = [clang_tidy_binary] +  if header_filter is not None: +    start.append('-header-filter=' + header_filter) +  else: +    # Show warnings in all in-project headers by default. +    start.append('-header-filter=^' + build_path + '/.*') +  if checks: +    start.append('-checks=' + checks) +  if tmpdir is not None: +    start.append('-export-fixes') +    # Get a temporary file. We immediately close the handle so clang-tidy can +    # overwrite it. +    (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir) +    os.close(handle) +    start.append(name) +  for arg in extra_arg: +      start.append('-extra-arg=%s' % arg) +  for arg in extra_arg_before: +      start.append('-extra-arg-before=%s' % arg) +  start.append('-p=' + build_path) +  if quiet: +      start.append('-quiet') +  if warnings_as_errors is not None: +      start.append('-warnings-as-errors=%s' % warnings_as_errors) +  start.append(f) +  return start + + +def apply_fixes(args, tmpdir): +  """Calls clang-apply-fixes on a given directory. Deletes the dir when done.""" +  invocation = [args.clang_apply_replacements_binary] +  if args.format: +    invocation.append('-format') +  invocation.append(tmpdir) +  ret = subprocess.call(invocation) +  shutil.rmtree(tmpdir) +  return ret + + +def run_tidy(args, tmpdir, build_path, queue, results, i): +  """Takes filenames out of queue and runs clang-tidy on them.""" +  results[i] = 0 +  while True: +    name = queue.get() +    invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks, +                                     tmpdir, build_path, args.header_filter, +                                     args.extra_arg, args.extra_arg_before, +                                     args.quiet, args.warnings_as_errors) +    sys.stdout.write(' '.join(invocation) + '\n') +    if subprocess.call(invocation) != 0: +      results[i] = 1 +    queue.task_done() + + +def main(): +  parser = argparse.ArgumentParser(description='Runs clang-tidy over all files ' +                                   'in a compilation database. Requires ' +                                   'clang-tidy and clang-apply-replacements in ' +                                   '$PATH.') +  parser.add_argument('-clang-tidy-binary', metavar='PATH', +                      default='clang-tidy', +                      help='path to clang-tidy binary') +  parser.add_argument('-clang-apply-replacements-binary', metavar='PATH', +                      default='clang-apply-replacements', +                      help='path to clang-apply-replacements binary') +  parser.add_argument('-checks', default=None, +                      help='checks filter, when not specified, use clang-tidy ' +                      'default') +  parser.add_argument('-header-filter', default=None, +                      help='regular expression matching the names of the ' +                      'headers to output diagnostics from. Diagnostics from ' +                      'the main file of each translation unit are always ' +                      'displayed.') +  parser.add_argument('-warnings-as-errors', default=None, metavar='STRING', +                      help='Upgrades warnings to errors. ' +                      'Same format as "-checks".' +                      "This option's value is appended to the value of " +                      "the 'WarningsAsErrors' option in .clang-tidy " +                      "file, if any.'") +  parser.add_argument('-j', type=int, default=0, +                      help='number of tidy instances to be run in parallel.') +  parser.add_argument('files', nargs='*', default=['.*'], +                      help='files to be processed (regex on path)') +  parser.add_argument('-fix', action='store_true', help='apply fix-its') +  parser.add_argument('-format', action='store_true', help='Reformat code ' +                      'after applying fixes') +  parser.add_argument('-p', dest='build_path', +                      help='Path used to read a compile command database.') +  parser.add_argument('-extra-arg', dest='extra_arg', +                      action='append', default=[], +                      help='Additional argument to append to the compiler ' +                      'command line.') +  parser.add_argument('-extra-arg-before', dest='extra_arg_before', +                      action='append', default=[], +                      help='Additional argument to prepend to the compiler ' +                      'command line.') +  parser.add_argument('-quiet', action='store_true', +                      help='Run clang-tidy in quiet mode') +  args = parser.parse_args() + +  db_path = 'compile_commands.json' + +  if args.build_path is not None: +    build_path = args.build_path +  else: +    # Find our database +    build_path = find_compilation_database(db_path) + +  try: +    invocation = [args.clang_tidy_binary, '-list-checks'] +    invocation.append('-p=' + build_path) +    if args.checks: +      invocation.append('-checks=' + args.checks) +    invocation.append('-') +    print subprocess.check_output(invocation) +  except: +    print >>sys.stderr, "Unable to run clang-tidy." +    sys.exit(1) + +  # Load the database and extract all files. +  database = json.load(open(os.path.join(build_path, db_path))) +  files = [entry['file'] for entry in database] + +  max_task = args.j +  if max_task == 0: +    max_task = multiprocessing.cpu_count() + +  tmpdir = None +  if args.fix: +    tmpdir = tempfile.mkdtemp() + +  # Build up a big regexy filter from all command line arguments. +  file_name_re = re.compile('(' + ')|('.join(args.files) + ')') + +  try: +    # Spin up a bunch of tidy-launching threads. +    results = [None] * max_task +    queue = Queue.Queue(max_task) +    for i in range(max_task): +      t = threading.Thread(target=run_tidy, +                           args=(args, tmpdir, build_path, queue, results, i)) +      t.daemon = True +      t.start() + +    # Fill the queue with files. +    for name in files: +      if file_name_re.search(name): +        queue.put(name) + +    # Wait for all threads to be done. +    queue.join() + +  except KeyboardInterrupt: +    # This is a sad hack. Unfortunately subprocess goes +    # bonkers with ctrl-c and we start forking merrily. +    print '\nCtrl-C detected, goodbye.' +    if args.fix: +      shutil.rmtree(tmpdir) +    os.kill(0, 9) + +  for ret in results: +    if ret != 0: +      sys.exit(ret) + +  if args.fix: +    print 'Applying fixes ...' +    ret = apply_fixes(args, tmpdir) +    if ret != 0: +      sys.exit(ret) + +if __name__ == '__main__': +  main() diff --git a/elpa/irony-20220110.849/server/cmake/CheckClangResourceDir.cmake b/elpa/irony-20220110.849/server/cmake/CheckClangResourceDir.cmake new file mode 100644 index 0000000..20bcda9 --- /dev/null +++ b/elpa/irony-20220110.849/server/cmake/CheckClangResourceDir.cmake @@ -0,0 +1,90 @@ +# +# Get the Clang resource directory. +# +# If found the following variable will be set: +# - CLANG_RESOURCE_DIR +# +set(CHECK_CLANG_RESOURCE_DIR_CHECKER_CODE_IN +  ${CMAKE_CURRENT_LIST_DIR}/LibClangDiagnosticsChecker.cpp) + +function(check_clang_resource_dir) +  if (CLANG_RESOURCE_DIR) +    return()                    # already in cache +  endif() + +  message(STATUS "Detecting Clang resource directory") +  find_package (LibClang REQUIRED) +   +  set(checker_code +    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/LibClangDiagnosticsChecker.cpp) +  set(checked_file +    "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check-libclang-stddef.cpp") + +  configure_file(${CHECK_CLANG_RESOURCE_DIR_CHECKER_CODE_IN} +    ${checker_code} COPYONLY) +  file(WRITE "${checked_file}" "#include <stddef.h>\n") + +  # Paths stolen from Rip-Rip/clang_complete#getBuiltinHeaderPath() +  find_path(CHECK_CLANG_RESOURCE_DIR include/stddef.h +    NO_DEFAULT_PATH +    # the default path, favor this one over the other, in case a specific +    # libclang has been chosen. +    HINTS "${LIBCLANG_LIBRARY_DIR}/../lib/clang" +    # other, distribution specific, paths +    PATHS +    "${LIBCLANG_LIBRARY_DIR}/../clang" # Gentoo +    "${LIBCLANG_LIBRARY_DIR}/clang"    # openSUSE, Windows +    "${LIBCLANG_LIBRARY_DIR}/"         # Google +    "/usr/lib64/clang"                 # x86_64 (openSUSE, Fedora) +    "/usr/lib/clang" +    PATH_SUFFIXES ${LIBCLANG_KNOWN_LLVM_VERSIONS}) + +  if (CHECK_CLANG_RESOURCE_DIR) +    # On Windows the paths weren't escaped correctly, similar to: +    # http://public.kitware.com/pipermail/cmake/2006-February/008473.html +    list(APPEND run_args -resource-dir \"${CHECK_CLANG_RESOURCE_DIR}\") +  endif() + +  list(APPEND run_args ${checked_file}) + +  try_run( +    CHECK_CLANG_RESOURCE_DIR_NUM_DIAGNOSTICS +    CHECK_CLANG_RESOURCE_DIR_COMPILE_RESULT +    ${CMAKE_BINARY_DIR} +    ${checker_code} +    CMAKE_FLAGS +      "-DINCLUDE_DIRECTORIES:STRING=${LIBCLANG_INCLUDE_DIRS}" +      "-DLINK_LIBRARIES:STRING=${LIBCLANG_LIBRARIES}" +    COMPILE_OUTPUT_VARIABLE compile_output +    RUN_OUTPUT_VARIABLE run_output +    ARGS ${run_args} +    ) + +  if (NOT CHECK_CLANG_RESOURCE_DIR_COMPILE_RESULT) +    set(CHECK_CLANG_RESOURCE_DIR_NUM_DIAGNOSTICS 1) +  endif() + +  if (CHECK_CLANG_RESOURCE_DIR_NUM_DIAGNOSTICS EQUAL 0) +    message(STATUS "Detecting libclang builtin headers directory -- success") +    if (CHECK_CLANG_RESOURCE_DIR) +      set(CLANG_RESOURCE_DIR "${CHECK_CLANG_RESOURCE_DIR}" +        CACHE INTERNAL "Clang resource directory.") +    endif() +  else() +    message(STATUS "Detecting Clang resource directory -- fail") + +    if (NOT CHECK_CLANG_RESOURCE_DIR_COMPILE_RESULT) +      message(WARNING "CheckClangResourceDir: failed to compile checker, please report. +  Compile output: +    ${compile_output} +") +    else() +      message(WARNING "CheckClangResourceDir: unsupported configuration, please report. + +  Check with args: ${run_args} +  Check output: +    ${run_output} +") +    endif() +  endif() +endfunction() diff --git a/elpa/irony-20220110.849/server/cmake/LibClangDiagnosticsChecker.cpp b/elpa/irony-20220110.849/server/cmake/LibClangDiagnosticsChecker.cpp new file mode 100644 index 0000000..64ea7aa --- /dev/null +++ b/elpa/irony-20220110.849/server/cmake/LibClangDiagnosticsChecker.cpp @@ -0,0 +1,47 @@ +/* +  This program takes some forward its command line arguments to libclang and +  returns the number of diagnostics that occured during the parsing. + +  It is used during CMake generation to adjust the default parameters to +  libclang. +*/ + +#include <clang-c/Index.h> + +#include <stdio.h> + +int main(int argc, const char *argv[]) { +  for (int i = 1; i < argc; ++i) { +    fprintf(stdout, "argv[%d]: %s\n", i, argv[i]); +  } + +  CXIndex Idx = clang_createIndex(0, 0); +  CXTranslationUnit TU = clang_parseTranslationUnit( +      Idx, NULL, &argv[1], argc - 1, 0, 0, CXTranslationUnit_None); +  int NumDiagnostics; + +  if (TU == NULL) { +    NumDiagnostics = 1; +    fprintf(stderr, "failed to create translation unit!\n"); +  } else { +    int i; + +    NumDiagnostics = clang_getNumDiagnostics(TU); + +    for (i = 0; i < NumDiagnostics; ++i) { +      CXDiagnostic Diag = clang_getDiagnostic(TU, i); +      CXString DiagStr = +          clang_formatDiagnostic(Diag, clang_defaultDiagnosticDisplayOptions()); + +      fprintf(stderr, "%s\n", clang_getCString(DiagStr)); + +      clang_disposeString(DiagStr); +      clang_disposeDiagnostic(Diag); +    } + +    clang_disposeTranslationUnit(TU); +  } + +  clang_disposeIndex(Idx); +  return NumDiagnostics; +} diff --git a/elpa/irony-20220110.849/server/cmake/modules/FindLibClang.cmake b/elpa/irony-20220110.849/server/cmake/modules/FindLibClang.cmake new file mode 100644 index 0000000..edc786d --- /dev/null +++ b/elpa/irony-20220110.849/server/cmake/modules/FindLibClang.cmake @@ -0,0 +1,106 @@ +# Find libclang. +# +# This module defines the following variables: +# LIBCLANG_FOUND - true if libclang has been found and can be used +# LIBCLANG_KNOWN_LLVM_VERSIONS - known LLVM release numbers +# LIBCLANG_INCLUDE_DIRS - the libclang include directories +# LIBCLANG_LIBRARIES - the libraries needed to use libclang +# LIBCLANG_LIBRARY_DIR - the path to the directory containing libclang +# +# This module defines the following IMPORTED target: +# - irony_libclang + +# most recent versions come first +# http://llvm.org/apt/ +set(LIBCLANG_KNOWN_LLVM_VERSIONS 9.0.0 9.0 9 +  8.0.0 8.0 8 +  7.0.1 7.0.0 7.0 7 +  6.0.1 6.0.0 6.0 6 +  5.0.2 5.0.1 5.0.0 5.0 5 +  4.0.1 4.0.0 4.0 4 +  3.9.1 3.9.0 3.9 +  3.8.1 3.8.0 3.8 +  3.7.1 3.7.0 3.7 +  3.6.2 3.6.1 3.6.0 3.6 +  3.5.2 3.5.1 3.5.0 3.5 +  3.4.2 3.4.1 3.4 +  3.3 +  3.2 +  3.1) + +set(libclang_llvm_header_search_paths) +set(libclang_llvm_lib_search_paths +  # LLVM Fedora +  /usr/lib/llvm +  ) + +foreach (version ${LIBCLANG_KNOWN_LLVM_VERSIONS}) +  string(REPLACE "." "" undotted_version "${version}") +  list(APPEND libclang_llvm_header_search_paths +    # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/ +    "/usr/lib/llvm-${version}/include/" +    # LLVM MacPorts +    "/opt/local/libexec/llvm-${version}/include" +    # LLVM Homebrew +    "/usr/local/Cellar/llvm/${version}/include" +    # LLVM Homebrew/versions +    "/usr/local/lib/llvm-${version}/include" +    # FreeBSD ports versions +    "/usr/local/llvm${undotted_version}/include" +    # Gentoo clang-4 +    "/usr/lib/llvm/${version}/include" +    ) + +  list(APPEND libclang_llvm_lib_search_paths +    # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/ +    "/usr/lib/llvm-${version}/lib/" +    # LLVM MacPorts +    "/opt/local/libexec/llvm-${version}/lib" +    # LLVM Homebrew +    "/usr/local/Cellar/llvm/${version}/lib" +    # LLVM Homebrew/versions +    "/usr/local/lib/llvm-${version}/lib" +    # FreeBSD ports versions +    "/usr/local/llvm${undotted_version}/lib" +    # Gentoo clang-4 +    "/usr/lib/llvm/${version}/lib" +    ) +endforeach() + +find_path(LIBCLANG_INCLUDE_DIR clang-c/Index.h +  PATHS ${libclang_llvm_header_search_paths} +  PATH_SUFFIXES LLVM/include #Windows package from http://llvm.org/releases/ +  DOC "The path to the directory that contains clang-c/Index.h") + +find_library(LIBCLANG_LIBRARY +  NAMES +    # On Windows with MSVC, the import library uses the ".imp" file extension +    # instead of the comon ".lib" +    libclang.imp +    libclang +    clang +  PATHS ${libclang_llvm_lib_search_paths} +  PATH_SUFFIXES LLVM/lib #Windows package from http://llvm.org/releases/ +  DOC "The file that corresponds to the libclang library.") + +get_filename_component(LIBCLANG_LIBRARY_DIR ${LIBCLANG_LIBRARY} PATH) + +set(LIBCLANG_LIBRARIES ${LIBCLANG_LIBRARY}) +set(LIBCLANG_INCLUDE_DIRS ${LIBCLANG_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LIBCLANG_FOUND to TRUE if +# all listed variables are TRUE +find_package_handle_standard_args(LibClang DEFAULT_MSG +  LIBCLANG_LIBRARY LIBCLANG_INCLUDE_DIR) + +mark_as_advanced(LIBCLANG_INCLUDE_DIR LIBCLANG_LIBRARY) + +if (LIBCLANG_FOUND AND NOT TARGET irony_libclang) +  add_library(irony_libclang UNKNOWN IMPORTED) +  set_target_properties(irony_libclang PROPERTIES +    IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" +    IMPORTED_LOCATION "${LIBCLANG_LIBRARY}" +    INTERFACE_INCLUDE_DIRECTORIES "${LIBCLANG_INCLUDE_DIR}" +  ) +endif() diff --git a/elpa/irony-20220110.849/server/src/CMakeLists.txt b/elpa/irony-20220110.849/server/src/CMakeLists.txt new file mode 100644 index 0000000..c516e70 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/CMakeLists.txt @@ -0,0 +1,111 @@ +# XXX: if there are issues +# due to https://reviews.llvm.org/D30911 / SVN Revision 298424 +# then use the fallback when LLVM_VERSION VERSION_LESS 5.0.1 +find_package(Clang) + +if (Clang_FOUND) +  # XXX: at least since Clang 8.0.0 +  # it's looks like the 'libclang' IMPORTED target +  # does not specify the following target property: +  #     INTERFACE_INCLUDE_DIRECTORIES ${CLANG_INCLUDE_DIRS} +  # which is confirmed by https://github.com/Sarcasm/irony-mode/issues/530 +  # so we work around this, +  # but ideally Clang upstream should export fully usable IMPORTED targets +  add_library(irony_libclang INTERFACE) +  target_link_libraries(irony_libclang INTERFACE libclang) +  target_include_directories(irony_libclang INTERFACE ${CLANG_INCLUDE_DIRS}) + +  get_property(IRONY_CLANG_EXECUTABLE TARGET clang PROPERTY LOCATION) +  execute_process( +    COMMAND "${IRONY_CLANG_EXECUTABLE}" -print-resource-dir +    OUTPUT_VARIABLE CLANG_RESOURCE_DIR +    OUTPUT_STRIP_TRAILING_WHITESPACE +  ) +else() +  # fallback to historically FindLibClang.cmake +  # and -resource-dir guess mecanism +  include(CheckClangResourceDir) +  find_package(LibClang REQUIRED) +  check_clang_resource_dir() +endif() + +# not to be taken as a module-definition file to link on Windows +set_source_files_properties(Commands.def PROPERTIES HEADER_FILE_ONLY TRUE) + +if(MSVC) +  # irony-server uses some code that breaks when iterator debugging is enabled +  # +  # The culprit is CommandLineArgumentParser who initialize its member +  # 'Position', of type 'std::string::const_iterator', to 'Input.begin() - 1'. +  # With checked iterator the begin() - 1 breaks in debug build. +  add_definitions(/D_ITERATOR_DEBUG_LEVEL=0) +endif() + +add_executable(irony-server +  support/CommandLineParser.cpp +  support/CommandLineParser.h +  support/iomanip_quoted.h +  support/NonCopyable.h +  support/CIndex.h +  support/TemporaryFile.cpp +  support/TemporaryFile.h + +  Command.cpp +  Commands.def +  Command.h +  CompDBCache.h +  CompDBCache.cpp +  Irony.cpp +  Irony.h +  TUManager.cpp +  TUManager.h + +  main.cpp) + +irony_target_set_cxx_standard(irony-server) + +target_include_directories(irony-server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +# retrieve the package version from irony.el +function(irony_find_package_version OUTPUT_VAR) +  # this is a hack that force CMake to reconfigure, it is necessary to see if +  # the version in irony.el has changed, this is not possible to add the +  # definitions at build time +  configure_file(${PROJECT_SOURCE_DIR}/../irony.el +    ${CMAKE_CURRENT_BINARY_DIR}/irony.el +    COPYONLY) + +  set(version_header "\;\; Version: ") +  file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/irony.el version +    LIMIT_COUNT 1 +    REGEX "^${version_header}*") + +  if (NOT version) +    message (FATAL_ERROR "couldn't find irony.el's version header!") +  endif() + +  string(LENGTH ${version_header} version_header_length) +  string(SUBSTRING ${version} ${version_header_length} -1 package_version) +  set(${OUTPUT_VAR} ${package_version} PARENT_SCOPE) +endfunction() + +irony_find_package_version(IRONY_PACKAGE_VERSION) +message(STATUS "Irony package version is '${IRONY_PACKAGE_VERSION}'") + +set_source_files_properties(main.cpp +  PROPERTIES +  COMPILE_DEFINITIONS IRONY_PACKAGE_VERSION=\"${IRONY_PACKAGE_VERSION}\") + +if (CLANG_RESOURCE_DIR) +  # look for CLANG_RESOURCE_DIR_DIR usage in the code for an explanation +  set_source_files_properties(TUManager.cpp +    PROPERTIES +    COMPILE_DEFINITIONS CLANG_RESOURCE_DIR=\"${CLANG_RESOURCE_DIR}\") +endif() + +target_link_libraries(irony-server irony_libclang) + +set_target_properties(irony-server +  PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) + +install(TARGETS irony-server DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/elpa/irony-20220110.849/server/src/Command.cpp b/elpa/irony-20220110.849/server/src/Command.cpp new file mode 100644 index 0000000..363b6cb --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Command.cpp @@ -0,0 +1,278 @@ +/** + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * \brief Command parser definitions. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "Command.h" + +#include "support/CommandLineParser.h" + +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <limits> +#include <map> + + +namespace { + +struct StringConverter { +  StringConverter(std::string *dest) : dest_(dest) { +  } + +  bool operator()(const std::string &str) { +    *dest_ = str; +    return true; +  } + +private: +  std::string *dest_; +}; + +struct UnsignedIntConverter { +  UnsignedIntConverter(unsigned *dest) : dest_(dest) { +  } + +  bool operator()(const std::string &str) { +    char *end; +    long num = std::strtol(str.c_str(), &end, 10); + +    if (end != (str.c_str() + str.size())) +      return false; + +    if (errno == ERANGE) +      return false; + +    if (num < 0) +      return false; + +    unsigned long unum = static_cast<unsigned long>(num); +    if (unum > std::numeric_limits<unsigned>::max()) +      return false; + +    *dest_ = unum; +    return true; +  } + +private: +  unsigned *dest_; +}; + +/// Convert "on" and "off" to a boolean +struct OptionConverter { +  OptionConverter(bool *dest) : dest_(dest) { +  } + +  bool operator()(const std::string &str) { +    if (str == "on") { +      *dest_ = true; +    } else if (str == "off") { +      *dest_ = false; +    } else { +      return false; +    } +    return true; +  } + +private: +  bool *dest_; +}; + +const std::map<std::string, PrefixMatchStyle> PREFIX_MATCH_STYLE_MAP = { +  { "exact", PrefixMatchStyle::Exact }, +  { "case-insensitive", PrefixMatchStyle::CaseInsensitive }, +  { "smart-case", PrefixMatchStyle::SmartCase}, +}; + +/// Convert style to a PrefixMatchStyle +struct PrefixMatchStyleConverter { +  PrefixMatchStyleConverter(PrefixMatchStyle *dest) : dest_(dest) { +  } + +  bool operator()(const std::string &str) { +    auto res = PREFIX_MATCH_STYLE_MAP.find(str); + +    if (res == PREFIX_MATCH_STYLE_MAP.cend()) { +      return false; +    } +    *dest_ = res->second; +    return true; +  } + +private: +  PrefixMatchStyle *dest_; +}; + +std::ostream &operator<<(std::ostream &os, PrefixMatchStyle style) { +  for (auto it : PREFIX_MATCH_STYLE_MAP) { +    if (it.second == style) { +      os << it.first; +      return os; +    } +  } +  os << "UnknownStyle"; +  return os; +} + + +} // unnamed namespace + +std::ostream &operator<<(std::ostream &os, const Command::Action &action) { +  os << "Command::"; + +  switch (action) { +#define X(sym, str, help)                                                      \ +  case Command::sym:                                                           \ +    os << #sym;                                                                \ +    break; +#include "Commands.def" +  } +  return os; +} + +std::ostream &operator<<(std::ostream &os, const Command &command) { +  os << "Command{action=" << command.action << ", " +     << "file='" << command.file << "', " +     << "unsavedFile='" << command.unsavedFile << "', " +     << "dir='" << command.dir << "', " +     << "line=" << command.line << ", " +     << "column=" << command.column << ", " +     << "prefix='" << command.prefix << "', " +     << "caseStyle='" << command.style << "', " +     << "flags=["; +  bool first = true; +  for (const std::string &flag : command.flags) { +    if (!first) +      os << ", "; +    os << "'" << flag << "'"; +    first = false; +  } +  os << "], " +     << "opt=" << (command.opt ? "on" : "off"); + +  return os << "}"; +} + +static Command::Action actionFromString(const std::string &actionStr) { +#define X(sym, str, help)                                                      \ +  if (actionStr == str)                                                        \ +    return Command::sym; + +#include "Commands.def" + +  return Command::Unknown; +} + +CommandParser::CommandParser() : tempFile_("irony-server") { +} + +Command *CommandParser::parse(const std::vector<std::string> &argv) { +  command_.clear(); + +  if (argv.begin() == argv.end()) { +    std::clog << "error: no command specified.\n" +                 "See 'irony-server help' to list available commands\n"; +    return 0; +  } + +  const std::string &actionStr = argv[0]; + +  command_.action = actionFromString(actionStr); + +  bool readCompileOptions = false; +  std::vector<std::function<bool(const std::string &)>> positionalArgs; + +  switch (command_.action) { +  case Command::SetDebug: +    positionalArgs.push_back(OptionConverter(&command_.opt)); +    break; + +  case Command::Parse: +    positionalArgs.push_back(StringConverter(&command_.file)); +    readCompileOptions = true; +    break; + +  case Command::Complete: +    positionalArgs.push_back(StringConverter(&command_.file)); +    positionalArgs.push_back(UnsignedIntConverter(&command_.line)); +    positionalArgs.push_back(UnsignedIntConverter(&command_.column)); +    readCompileOptions = true; +    break; + +  case Command::GetType: +    positionalArgs.push_back(UnsignedIntConverter(&command_.line)); +    positionalArgs.push_back(UnsignedIntConverter(&command_.column)); +    break; + +  case Command::SetUnsaved: +    positionalArgs.push_back(StringConverter(&command_.file)); +    positionalArgs.push_back(StringConverter(&command_.unsavedFile)); +    break; + +  case Command::ResetUnsaved: +    positionalArgs.push_back(StringConverter(&command_.file)); +    break; + +  case Command::Candidates: +    positionalArgs.push_back(StringConverter(&command_.prefix)); +    positionalArgs.push_back(PrefixMatchStyleConverter(&command_.style)); +    break; +  case Command::CompletionDiagnostics: +  case Command::Diagnostics: +  case Command::Help: +  case Command::Exit: +    // no-arguments commands +    break; + +  case Command::GetCompileOptions: +    positionalArgs.push_back(StringConverter(&command_.dir)); +    positionalArgs.push_back(StringConverter(&command_.file)); +    break; + +  case Command::Unknown: +    std::clog << "error: invalid command specified: " << actionStr << "\n"; +    return 0; +  } + +  auto argsBegin = argv.begin() + 1; +  const auto argsEnd = std::find(argsBegin, argv.end(), "--"); +  const int argCount = std::distance(argsBegin, argsEnd); + +  // compile options are provided after '--' +  if (readCompileOptions && argsEnd != argv.end()) { +    command_.flags.assign(std::next(argsEnd), argv.end()); +  } + +  if (argCount != static_cast<int>(positionalArgs.size())) { +    std::clog << "error: invalid number of arguments for '" << actionStr +              << "' (requires " << positionalArgs.size() << " got " << argCount +              << ")\n"; +    return 0; +  } + +  for (auto fn : positionalArgs) { +    if (!fn(*argsBegin)) { +      std::clog << "error: parsing command '" << actionStr +                << "': invalid argument '" << *argsBegin << "'\n"; +      return 0; +    } +    ++argsBegin; +  } + +  // '-' is used as a special file to inform that the buffer hasn't been saved +  // on disk and only the buffer content is available. libclang needs a file, so +  // this is treated as a special value for irony-server to create a temporary +  // file for this. note that libclang will gladly accept '-' as a filename but +  // we don't want to let this happen since irony already reads stdin. +  if (command_.file == "-") { +    command_.file = tempFile_.getPath(); +  } + +  return &command_; +} diff --git a/elpa/irony-20220110.849/server/src/Command.h b/elpa/irony-20220110.849/server/src/Command.h new file mode 100644 index 0000000..9f36aa4 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Command.h @@ -0,0 +1,73 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * \brief Command parser declarations. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_COMMAND_H_ +#define IRONY_MODE_SERVER_COMMAND_H_ + +#include "support/CIndex.h" +#include "support/TemporaryFile.h" +#include "Style.h" + +#include <iosfwd> +#include <string> +#include <vector> + +class TemporaryFile; + +// TODO: a tagged union? +struct Command { +  Command() { +    clear(); +  } + +  void clear() { +    action = Unknown; +    flags.clear(); +    file.clear(); +    unsavedFile.clear(); +    dir.clear(); +    prefix.clear(); +    style = PrefixMatchStyle::Exact; +    line = 0; +    column = 0; +    opt = false; +  } + +#define X(sym, str, desc) sym, +  enum Action { +#include "Commands.def" +  } action; + +  std::vector<std::string> flags; +  std::string file; +  std::string unsavedFile; +  std::string dir; +  std::string prefix; +  PrefixMatchStyle style; +  unsigned line; +  unsigned column; +  bool opt; +}; + +std::ostream &operator<<(std::ostream &os, const Command::Action &action); +std::ostream &operator<<(std::ostream &os, const Command &command); + +class CommandParser { +public: +  CommandParser(); + +  Command *parse(const std::vector<std::string> &argv); + +private: +  Command command_; +  TemporaryFile tempFile_; +}; + +#endif // IRONY_MODE_SERVER_COMMAND_H_ diff --git a/elpa/irony-20220110.849/server/src/Commands.def b/elpa/irony-20220110.849/server/src/Commands.def new file mode 100644 index 0000000..f99d8af --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Commands.def @@ -0,0 +1,39 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * \brief Command list. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef X +#error Please define the 'X(id, command, description)' macro before inclusion! +#endif + +X(Candidates, "candidates", +  "PREFIX STYLE - print completion candidates (require previous complete). " +  "STYLE is \"exact\", \"case-insensitive\" or \"smart-case\"") +X(Complete, "complete", +  "FILE LINE COL [-- [COMPILE_OPTIONS...]] - perform code completion at a given location") +X(CompletionDiagnostics, "completion-diagnostics", +  "print the diagnostics generated during complete") +X(Diagnostics, "diagnostics", "print the diagnostics of the last parse") +X(Exit, "exit", "exit interactive mode, print nothing") +X(GetCompileOptions, "get-compile-options", "BUILD_DIR FILE - " +  "get compile options for FILE from JSON database in BUILD_DIR") +X(GetType, "get-type", "LINE COL - get type of symbol at a given location") +X(Help, "help", "show this message") +X(Parse, "parse", "FILE [-- [COMPILE_OPTIONS...]] - parse the given file") +X(ResetUnsaved, "reset-unsaved", "FILE - reset FILE, its content is up to date") +X(SetDebug, "set-debug", "ON|OFF - enable or disable verbose logging") +X(SetUnsaved, +  "set-unsaved", +  "FILE UNSAVED-CONTENT-FILE - tell irony-server that " +  "UNSAVED-CONTENT-FILE contains the effective content of FILE") + +// sentinel value, should be the last one +X(Unknown, "<unkown>",  "<unspecified>") + +#undef X diff --git a/elpa/irony-20220110.849/server/src/CompDBCache.cpp b/elpa/irony-20220110.849/server/src/CompDBCache.cpp new file mode 100644 index 0000000..f79ec8c --- /dev/null +++ b/elpa/irony-20220110.849/server/src/CompDBCache.cpp @@ -0,0 +1,71 @@ +#include "CompDBCache.h" + +#include <sys/stat.h> + +#include <cassert> + +CompDBCache::CompDBCache() +  : db_(nullptr), mtime_(0) { +} + +CompDBCache::~CompDBCache() { +  clear(); +} + +CXCompilationDatabase CompDBCache::fromDirectory(const std::string &buildDir, +                                                 CXCompilationDatabase_Error *error) { +  assert(error != nullptr); + +  const std::string jsonFilename = constructJsonDbFilename(buildDir); +  const time_t mtime = modificationTime(jsonFilename); + +  if (jsonFilename == filename_ && mtime != 0 && mtime == mtime_) { +    // Using the cached compilation database. +    // Just set the provided error code to indicate success. +    *error = CXCompilationDatabase_NoError; +  } else { +    clear(); + +    db_ = clang_CompilationDatabase_fromDirectory(buildDir.c_str(), error); + +    if (mtime != 0 && *error == CXCompilationDatabase_NoError) { +      // Successfully loaded a JSON compilation database. +      // Cache the result. +      filename_ = jsonFilename; +      mtime_ = mtime; +    } +  } + +  return db_; +} + +void CompDBCache::clear() { +  if (db_) { +    clang_CompilationDatabase_dispose(db_); +    db_ = nullptr; +    filename_.clear(); +    mtime_ = 0; +  } +} + +std::string CompDBCache::constructJsonDbFilename(const std::string &buildDir) const { +  std::string ret = buildDir; +  if (!buildDir.empty() && buildDir.back() != '/') +    ret += '/'; +  ret += "compile_commands.json"; +  return ret; +} + +time_t CompDBCache::modificationTime(const std::string &filename) const { +  time_t mtime = 0; +#ifdef _WIN32 +  struct _stat st; +  const int statRes = _stat(filename.c_str(), &st); +#else +  struct stat st; +  const int statRes = stat(filename.c_str(), &st); +#endif +  if (statRes == 0) +    mtime = st.st_mtime; +  return mtime; +} diff --git a/elpa/irony-20220110.849/server/src/CompDBCache.h b/elpa/irony-20220110.849/server/src/CompDBCache.h new file mode 100644 index 0000000..568b790 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/CompDBCache.h @@ -0,0 +1,86 @@ +/**-*-C++-*- + * \file + * \author Idar Tollefsen <idart@hotmail.com> + * + * \brief Creates a compilation database and caches it. + * + * Keeps a cache of the loaded compilation database, only creating a new + * one when needed. + * + * This file is distributed under the GNU General Public License. + * See COPYING for details. + */ + +#ifndef IRONY_MODE_COMPDBCACHE_H_ +#define IRONY_MODE_COMPDBCACHE_H_ + +#include "support/CIndex.h" +#include "support/NonCopyable.h" + +#include <ctime> +#include <string> + +class CompDBCache : public util::NonCopyable { +public: +  CompDBCache(); +  ~CompDBCache(); + +  /** +   * \brief Get a compilation database from a database found in a directory. +   * +   * This in essence a wrapper around +   * \c clang_CompilationDatabase_fromDirectory() with added caching. +   * It will either create a new compilation database or return the +   * already loaded one (if any). +   * +   * This class owns the resulting compilation database. +   * Therefore, unlike \c clang_CompilationDatabase_fromDirectory(), +   * callers must NOT call \c clang_CompilationDatabase_dispose() on the +   * returned compilation database. This class will handle that internally. +   * +   * \param buildDir Directory containing the database (such as +   *                 "compile_commands.json") to create a compilation +   *                 database from. +   * \param error Error code from attempting to create a compilation +   *              database (\c CXCompilationDatabase_NoError on success). +   * +   * \return The compilation database or nullptr. +   */ +  CXCompilationDatabase fromDirectory(const std::string &buildDir, +                                      CXCompilationDatabase_Error *error); + +private: +  /** +   * \brief Clear the cache. +   * +   * This will dispose the currently loaded compilation database (if any) by +   * calling \c clang_CompilationDatabase_dispose() on it. And it will reset +   * other internal housekeeping variables related to the caching of the +   * compilation database. +   */ +  void clear(); + +  /** +   * \brief Construct JSON compilation database filename. +   * +   * \param buildDir Directory that might contain "compile_commands.json". +   * +   * \return Path to "compilation_commands.json" in \c buildDir. +   */ +  std::string constructJsonDbFilename(const std::string &buildDir) const; + +  /** +   * \brief Get modification time of a file. +   * +   * \param filename The file to get last modification time of. +   * +   * \return The modification time of \c filename or 0. +   */ +  time_t modificationTime(const std::string &filename) const; + +  CXCompilationDatabase db_; +  std::string filename_; +  time_t mtime_; +}; + +#endif diff --git a/elpa/irony-20220110.849/server/src/Irony.cpp b/elpa/irony-20220110.849/server/src/Irony.cpp new file mode 100644 index 0000000..2157b32 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Irony.cpp @@ -0,0 +1,638 @@ +/** + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * \brief irony-server "API" definitions. + * + * \sa Irony.h for more information. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "Irony.h" + +#include "support/iomanip_quoted.h" + +#include <algorithm> +#include <cassert> +#include <iostream> +#include <fstream> +#include <cctype> + +namespace { + +std::string cxStringToStd(CXString cxString) { +  std::string stdStr; + +  if (const char *cstr = clang_getCString(cxString)) { +    stdStr = cstr; +  } + +  clang_disposeString(cxString); +  return stdStr; +} + +const char *diagnosticSeverity(const CXDiagnostic &diagnostic) { +  switch (clang_getDiagnosticSeverity(diagnostic)) { +  case CXDiagnostic_Ignored: +    return "ignored"; +  case CXDiagnostic_Note: +    return "note"; +  case CXDiagnostic_Warning: +    return "warning"; +  case CXDiagnostic_Error: +    return "error"; +  case CXDiagnostic_Fatal: +    return "fatal"; +  } + +  return "unknown"; +} + +void dumpDiagnostic(const CXDiagnostic &diagnostic) { +  std::string file; +  unsigned line = 0, column = 0, offset = 0; +  CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); +  if (!clang_equalLocations(location, clang_getNullLocation())) { +    CXFile cxFile; + +// clang_getInstantiationLocation() has been marked deprecated and +// is aimed to be replaced by clang_getExpansionLocation(). +#if CINDEX_VERSION >= 6 +    clang_getExpansionLocation(location, &cxFile, &line, &column, &offset); +#else +    clang_getInstantiationLocation(location, &cxFile, &line, &column, &offset); +#endif + +    file = cxStringToStd(clang_getFileName(cxFile)); +  } + +  const char *severity = diagnosticSeverity(diagnostic); + +  std::string message = cxStringToStd(clang_getDiagnosticSpelling(diagnostic)); + +  std::cout << '(' << support::quoted(file)    // +            << ' ' << line                     // +            << ' ' << column                   // +            << ' ' << offset                   // +            << ' ' << severity                 // +            << ' ' << support::quoted(message) // +            << ")\n"; +} + +bool readFileContent(const std::string &filename, +                     Irony::UnsavedBuffer &outBuf) { +  std::ifstream ifs(filename.c_str(), +                    std::ios::in | std::ios::binary | std::ios::ate); + +  if (!ifs.is_open()) { +    return false; +  } + +  // FIXME: it's possible that this method of reading the file is 100% reliable, +  // I can't confirm that tellg() is guaranteed to return a byte count. +  // std::streamoff does not mention 'byte'. +  // In practice it seems to work but this may be just luck. +  // See also this discussion: +  // - http://stackoverflow.com/questions/22984956/tellg-function-give-wrong-size-of-file/22986486#22986486 +  auto nbytes = ifs.tellg(); + +  if (nbytes == std::ifstream::pos_type(-1)) { +    return false; +  } + +  outBuf.resize(nbytes); +  ifs.seekg(0, std::ios::beg); + +  ifs.read(&outBuf[0], outBuf.size()); + +  if (!ifs){ +    outBuf.clear(); +    return false; +  } + +  return true; +} + +} // unnamed namespace + +Irony::Irony() +  : activeTu_(nullptr), activeCompletionResults_(nullptr), debug_(false) { +} + +void Irony::parse(const std::string &file, +                  const std::vector<std::string> &flags) { +  resetCache(); +  activeTu_ = tuManager_.parse(file, flags, cxUnsavedFiles_); +  file_ = file; + +  if (activeTu_ == nullptr) { +    std::cout << "(error . (" +              << "parse-error" +              << " \"failed to parse file\"" +              << " " << support::quoted(file) << "))\n"; +    return; +  } + +  std::cout << "(success . t)\n"; +} + +void Irony::diagnostics() const { +  unsigned diagnosticCount; + +  if (activeTu_ == nullptr) { +    diagnosticCount = 0; +  } else { +    diagnosticCount = clang_getNumDiagnostics(activeTu_); +  } + +  std::cout << "(\n"; + +  for (unsigned i = 0; i < diagnosticCount; ++i) { +    CXDiagnostic diagnostic = clang_getDiagnostic(activeTu_, i); + +    dumpDiagnostic(diagnostic); + +    clang_disposeDiagnostic(diagnostic); +  } + +  std::cout << ")\n"; +} + +void Irony::resetCache() { +  activeTu_ = nullptr; + +  if (activeCompletionResults_ != nullptr) { +    clang_disposeCodeCompleteResults(activeCompletionResults_); +    activeCompletionResults_ = nullptr; +  } +} + +void Irony::getType(unsigned line, unsigned col) const { +  if (activeTu_ == nullptr) { +    std::clog << "W: get-type - parse wasn't called\n"; + +    std::cout << "nil\n"; +    return; +  } + +  CXFile cxFile = clang_getFile(activeTu_, file_.c_str()); +  CXSourceLocation sourceLoc = clang_getLocation(activeTu_, cxFile, line, col); +  CXCursor cursor = clang_getCursor(activeTu_, sourceLoc); + +  if (clang_Cursor_isNull(cursor)) { +    // TODO: "error: no type at point"? +    std::cout << "nil"; +    return; +  } + +  CXType cxTypes[2]; +  cxTypes[0] = clang_getCursorType(cursor); +  cxTypes[1] = clang_getCanonicalType(cxTypes[0]); + +  std::cout << "("; + +  for (const CXType &cxType : cxTypes) { +    CXString typeDescr = clang_getTypeSpelling(cxType); +    std::string typeStr = clang_getCString(typeDescr); +    clang_disposeString(typeDescr); + +    if (typeStr.empty()) +      break; + +    std::cout << support::quoted(typeStr) << " "; +  } + +  std::cout << ")\n"; +} + +namespace { + +class CompletionChunk { +public: +  explicit CompletionChunk(CXCompletionString completionString) +    : completionString_(completionString) +    , numChunks_(clang_getNumCompletionChunks(completionString_)) +    , chunkIdx_(0) { +  } + +  bool hasNext() const { +    return chunkIdx_ < numChunks_; +  } + +  void next() { +    if (!hasNext()) { +      assert(0 && "out of range completion chunk"); +      abort(); +    } + +    ++chunkIdx_; +  } + +  CXCompletionChunkKind kind() const { +    return clang_getCompletionChunkKind(completionString_, chunkIdx_); +  } + +  // TODO: operator>> so that one can re-use string allocated buffer +  std::string text() const { +    return cxStringToStd( +        clang_getCompletionChunkText(completionString_, chunkIdx_)); +  } + +private: +  CXCompletionString completionString_; +  unsigned int numChunks_; +  unsigned chunkIdx_; +}; + +} // unnamed namespace + +void Irony::complete(const std::string &file, +                     unsigned line, +                     unsigned col, +                     const std::vector<std::string> &flags) { +  resetCache(); + +  if (CXTranslationUnit tu = +          tuManager_.getOrCreateTU(file, flags, cxUnsavedFiles_)) { +    activeCompletionResults_ = +        clang_codeCompleteAt(tu, +                             file.c_str(), +                             line, +                             col, +                             const_cast<CXUnsavedFile *>(cxUnsavedFiles_.data()), +                             cxUnsavedFiles_.size(), +                             (clang_defaultCodeCompleteOptions() & +                              ~CXCodeComplete_IncludeCodePatterns) +#if HAS_BRIEF_COMMENTS_IN_COMPLETION +                                 | +                                 CXCodeComplete_IncludeBriefComments +#endif +                             ); +  } + +  if (activeCompletionResults_ == nullptr) { +    std::cout << "(error . (" +              << "complete-error" +              << " \"failed to perform code completion\"" +              << " " << support::quoted(file) << " " << line << " " << col +              << "))\n"; +    return; +  } + +  clang_sortCodeCompletionResults(activeCompletionResults_->Results, +                                  activeCompletionResults_->NumResults); + +  std::cout << "(success . t)\n"; +} + +namespace { + +bool hasUppercase(const std::string &prefix) +{ +  for (char c : prefix) { +    if (std::isupper(c)) { +      return true; +    } +  } +  return false; +} + +bool isEqual(const bool insensitive, const char a, const char b) +{ +  if (insensitive) { +    return std::tolower(a) == std::tolower(b); +  } else { +    return a == b; +  } +} + +bool startsWith(const std::string& str, const std::string &prefix, bool caseInsensitive) +{ +  if (str.length() < prefix.length()) { +    return false; +  } + +  const auto charCmp = [&](const char a, const char b) { +    return isEqual(caseInsensitive, a, b); +  }; + +  auto res = std::mismatch(prefix.begin(), prefix.end(), str.begin(), charCmp); +  return res.first == prefix.end(); +} + +bool isStyleCaseInsensitive(const std::string &prefix, PrefixMatchStyle style) +{ +  if (style == PrefixMatchStyle::SmartCase) { +    // For SmartCase style, do case insensitive matching only there isn't upper +    // case letter. +    if (!hasUppercase(prefix)) { +      style = PrefixMatchStyle::CaseInsensitive; +    } +  } +  return style == PrefixMatchStyle::CaseInsensitive; +} + +} // unnamed namespace + +void Irony::completionDiagnostics() const { +  unsigned diagnosticCount; + +  if (activeCompletionResults_ == nullptr) { +    diagnosticCount = 0; +  } else { +    diagnosticCount = +        clang_codeCompleteGetNumDiagnostics(activeCompletionResults_); +  } + +  std::cout << "(\n"; + +  for (unsigned i = 0; i < diagnosticCount; ++i) { +    CXDiagnostic diagnostic = +        clang_codeCompleteGetDiagnostic(activeCompletionResults_, i); + +    dumpDiagnostic(diagnostic); +    clang_disposeDiagnostic(diagnostic); +  } + +  std::cout << ")\n"; +} + +void Irony::candidates(const std::string &prefix, PrefixMatchStyle style) const { +  if (activeCompletionResults_ == nullptr) { +    std::cout << "nil\n"; +    return; +  } + +  bool caseInsensitive = isStyleCaseInsensitive(prefix, style); + +  CXCodeCompleteResults *completions = activeCompletionResults_; + +  std::cout << "(\n"; + +  // re-use the same buffers to avoid unnecessary allocations +  std::string typedtext, brief, resultType, prototype, postCompCar; + +  std::vector<unsigned> postCompCdr; + +  for (unsigned i = 0; i < completions->NumResults; ++i) { +    CXCompletionResult candidate = completions->Results[i]; +    CXAvailabilityKind availability = +      clang_getCompletionAvailability(candidate.CompletionString); + +    unsigned priority = +      clang_getCompletionPriority(candidate.CompletionString); +    unsigned annotationStart = 0; +    bool typedTextSet = false; +    bool hasPrefix = true; + + +    if (availability == CXAvailability_NotAccessible || +        availability == CXAvailability_NotAvailable) { +      continue; +    } +     +    typedtext.clear(); +    brief.clear(); +    resultType.clear(); +    prototype.clear(); +    postCompCar.clear(); +    postCompCdr.clear(); + +    for (CompletionChunk chunk(candidate.CompletionString); chunk.hasNext(); +         chunk.next()) { +      char ch = 0; + +      auto chunkKind = chunk.kind(); + +      switch (chunkKind) { +      case CXCompletionChunk_ResultType: +        resultType = chunk.text(); +        break; + +      case CXCompletionChunk_TypedText: +      case CXCompletionChunk_Text: +      case CXCompletionChunk_Placeholder: +      case CXCompletionChunk_Informative: +      case CXCompletionChunk_CurrentParameter: +        prototype += chunk.text(); +        break; + +      case CXCompletionChunk_LeftParen:       ch = '(';  break; +      case CXCompletionChunk_RightParen:      ch = ')';  break; +      case CXCompletionChunk_LeftBracket:     ch = '[';  break; +      case CXCompletionChunk_RightBracket:    ch = ']';  break; +      case CXCompletionChunk_LeftBrace:       ch = '{';  break; +      case CXCompletionChunk_RightBrace:      ch = '}';  break; +      case CXCompletionChunk_LeftAngle:       ch = '<';  break; +      case CXCompletionChunk_RightAngle:      ch = '>';  break; +      case CXCompletionChunk_Comma:           ch = ',';  break; +      case CXCompletionChunk_Colon:           ch = ':';  break; +      case CXCompletionChunk_SemiColon:       ch = ';';  break; +      case CXCompletionChunk_Equal:           ch = '=';  break; +      case CXCompletionChunk_HorizontalSpace: ch = ' ';  break; +      case CXCompletionChunk_VerticalSpace:   ch = '\n'; break; + +      case CXCompletionChunk_Optional: +        // ignored for now +        break; +      } + +      if (ch != 0) { +        prototype += ch; +        // commas look better followed by a space +        if (ch == ',') { +          prototype += ' '; +        } +      } + +      if (typedTextSet) { +        if (ch != 0) { +          postCompCar += ch; +          if (ch == ',') { +            postCompCar += ' '; +          } +        } else if (chunkKind == CXCompletionChunk_Text || +                   chunkKind == CXCompletionChunk_TypedText) { +          postCompCar += chunk.text(); +        } else if (chunkKind == CXCompletionChunk_Placeholder || +                   chunkKind == CXCompletionChunk_CurrentParameter) { +          postCompCdr.push_back(postCompCar.size()); +          postCompCar += chunk.text(); +          postCompCdr.push_back(postCompCar.size()); +        } +      } + +      // Consider only the first typed text. The CXCompletionChunk_TypedText +      // doc suggests that exactly one typed text will be given but at least +      // in Objective-C it seems that more than one can appear, see: +      // https://github.com/Sarcasm/irony-mode/pull/78#issuecomment-37115538 +      if (chunkKind == CXCompletionChunk_TypedText && !typedTextSet) { +        typedtext = chunk.text(); +        if (!startsWith(typedtext, prefix, caseInsensitive)) { +          hasPrefix = false; +          break; +        } +        // annotation is what comes after the typedtext +        annotationStart = prototype.size(); +        typedTextSet = true; +      } +    } + +    if (!hasPrefix) { +      continue; +    } +    if (!typedTextSet) { +      // clang may generate candidates without any typedText, and we may +      // generate some output like: +      //    ("" 1 "bool" "" "hasUppercase(const std::string &prefix)" +      //     0 ("") available) +      // That will cause infinite completion in irony.el +      continue; +    } + +#if HAS_BRIEF_COMMENTS_IN_COMPLETION +    brief = cxStringToStd( +        clang_getCompletionBriefComment(candidate.CompletionString)); +#endif + +    // see irony-completion.el#irony-completion-candidates +    std::cout << '(' << support::quoted(typedtext) +              << ' ' << priority +              << ' ' << support::quoted(resultType) +              << ' ' << support::quoted(brief) +              << ' ' << support::quoted(prototype) +              << ' ' << annotationStart +              << " (" << support::quoted(postCompCar); +    for (unsigned index : postCompCdr) +      std::cout << ' ' << index; +    std::cout << ")" +              << ")\n"; +  } + +  std::cout << ")\n"; +} + +void Irony::computeCxUnsaved() { +  cxUnsavedFiles_.clear(); + +  for (const auto &p : filenameToContent_) { +    CXUnsavedFile cxUnsavedFile; + +    cxUnsavedFile.Filename = p.first.c_str(); +    cxUnsavedFile.Contents = p.second.data(); +    cxUnsavedFile.Length = p.second.size(); +    cxUnsavedFiles_.push_back(cxUnsavedFile); +  } +} + +void Irony::setUnsaved(const std::string &file, +                       const std::string &unsavedContentFile) { +  resetCache(); + +  UnsavedBuffer content; +  if (!readFileContent(unsavedContentFile, content)) { +    filenameToContent_.erase(file); +    std::cout << "(error . (" +              << "file-read-error" +              << " \"failed to read unsaved buffer\"" +              << " " << support::quoted(file) << " " +              << support::quoted(unsavedContentFile) << ")\n"; +  } else { +    filenameToContent_[file] = content; +    std::cout << "(success . t)\n"; +  } + +  computeCxUnsaved(); +} + +void Irony::resetUnsaved(const std::string &file) { +  resetCache(); + +  const auto erasedCount = filenameToContent_.erase(file); + +  if (erasedCount == 0) { +    std::cout << "(error . (" +              << "no-such-entry" +              << " \"failed reset unsaved buffer\"" +              << " " << support::quoted(file) << ")\n"; +  } else { +    std::cout << "(success . t)\n"; +  } + +  computeCxUnsaved(); +} + +void Irony::getCompileOptions(const std::string &buildDir, +                              const std::string &file) const { +#if !(HAS_COMPILATION_DATABASE) + +  (void)buildDir; +  (void)file; + +  CXString cxVersionString = clang_getClangVersion(); + +  std::cout << "(error . (" +            << "unsupported" +            << " \"compilation database requires Clang >= 3.2\"" +            << " " << support::quoted(clang_getCString(cxVersionString)) +            << "))\n"; + +  clang_disposeString(cxVersionString); + +  return; + +#else +  CXCompilationDatabase_Error error; +  CXCompilationDatabase db = +      compDBCache_.fromDirectory(buildDir.c_str(), &error); + +  switch (error) { +  case CXCompilationDatabase_CanNotLoadDatabase: +    std::cout << "(error . (" +              << "cannot-load-database" +              << " \"failed to load compilation database from directory\"" +              << " " << support::quoted(buildDir) << "))\n"; +    return; + +  case CXCompilationDatabase_NoError: +    break; +  } + +  CXCompileCommands compileCommands = +      clang_CompilationDatabase_getCompileCommands(db, file.c_str()); + +  std::cout << "(success . (\n"; + +  for (unsigned i = 0, numCompileCommands = +                           clang_CompileCommands_getSize(compileCommands); +       i < numCompileCommands; ++i) { +    CXCompileCommand compileCommand = +        clang_CompileCommands_getCommand(compileCommands, i); + +    std::cout << "(" +              << "("; +    for (unsigned j = 0, +                  numArgs = clang_CompileCommand_getNumArgs(compileCommand); +         j < numArgs; ++j) { +      CXString arg = clang_CompileCommand_getArg(compileCommand, j); +      std::cout << support::quoted(clang_getCString(arg)) << " "; +      clang_disposeString(arg); +    } + +    std::cout << ")" +              << " . "; + +    CXString directory = clang_CompileCommand_getDirectory(compileCommand); +    std::cout << support::quoted(clang_getCString(directory)); +    clang_disposeString(directory); + +    std::cout << ")\n"; +  } + +  std::cout << "))\n"; + +  clang_CompileCommands_dispose(compileCommands); +#endif +} diff --git a/elpa/irony-20220110.849/server/src/Irony.h b/elpa/irony-20220110.849/server/src/Irony.h new file mode 100644 index 0000000..66968f5 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Irony.h @@ -0,0 +1,147 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * \brief irony-server "API" declarations. + * + * Contains the commands that the Emacs package relies on. These commands are + * mostly wrappers around a subset of the features provided by libclang. Command + * results are printed to \c std::cout as s-expr, in order to make it easy for + * Emacs to consume. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_IRONY_H_ +#define IRONY_MODE_SERVER_IRONY_H_ + +#include "TUManager.h" + +#include "CompDBCache.h" +#include "Style.h" + +#include <string> +#include <vector> + +class Irony { +public: +  // use std::string over std::vector<char> because I have some doubts +  // that libclang expect unsaved buffers to be a null terminated C strings +  typedef std::string UnsavedBuffer; + +public: +  Irony(); + +  bool isDebugEnabled() const { +    return debug_; +  } + +  /// \name Command +  /// \{ + +  /// \brief Set or unset debugging of commands. +  void setDebug(bool enable) { +    debug_ = enable; +  } + +  /// Parse or reparse the given file and compile options. +  /// +  /// If the compile options have changed, the translation unit is re-created to +  /// take this into account. +  /// +  /// Output \c nil or \c t, whether or not parsing the translation unit +  /// succeeded. +  /// +  /// \sa diagnostics(), getType() +  void parse(const std::string &file, const std::vector<std::string> &flags); + +  /// Parse the given file for code completion. +  /// +  /// Shares the same semantics and output as \c parse(). +  /// +  /// \sa candidates(), completionDiagnostics() +  void complete(const std::string &file, +                unsigned line, +                unsigned col, +                const std::vector<std::string> &flags); + +  void setUnsaved(const std::string &file, +                  const std::string &unsavedContentFile); + +  void resetUnsaved(const std::string &file); + +  /// \} + +  /// \name Queries +  /// \{ + +  /// \brief Retrieve the last parse diagnostics for the given file. +  /// +  /// \pre parse() was called. +  void diagnostics() const; + +  /// \brief Get types of symbol at a given location. +  /// +  /// Example: +  /// +  /// \code +  ///   typedef int MyType; +  ///   MyType a; +  /// \endcode +  /// +  /// Type of cursor location for 'a' is: +  /// +  /// \code{.el} +  ///    ("MyType" "int") +  /// \endcode +  /// +  /// TODO: test with CXString(), seems to be twice the same string +  /// +  void getType(unsigned line, unsigned col) const; + +  /// Get all the completion candidates. +  /// +  /// \pre complete() was called. +  void candidates(const std::string &prefix, PrefixMatchStyle style) const; + +  /// Get the diagnostics produced by the last \c complete(). +  /// +  /// \pre complete() was called. +  void completionDiagnostics() const; + +  /// \brief Get compile options from JSON database. +  /// +  /// \param buildDir Directory containing compile_commands.json +  /// \param file File to obtain compile commands for. +  /// +  /// Example output: +  /// +  /// \code{.el} +  ///    ( +  ///     (("-Wfoo" "-DBAR" "-Iqux") . "/path/to/working/directory") +  ///     (("-Wfoo-alt" "-DBAR_ALT" "-Iqux/alt") . "/alt/working/directory") +  ///    ) +  /// \endcode +  /// +  void getCompileOptions(const std::string &buildDir, +                         const std::string &file) const; + +  /// \} + +private: +  void resetCache(); +  void computeCxUnsaved(); + +private: +  mutable CompDBCache compDBCache_; +  TUManager tuManager_; +  std::map<std::string, UnsavedBuffer> filenameToContent_; +  CXTranslationUnit activeTu_; +  std::string file_; +  std::vector<CXUnsavedFile> cxUnsavedFiles_; +  CXCodeCompleteResults *activeCompletionResults_; +  bool debug_; +}; + +#endif // IRONY_MODE_SERVER_IRONY_H_ diff --git a/elpa/irony-20220110.849/server/src/Style.h b/elpa/irony-20220110.849/server/src/Style.h new file mode 100644 index 0000000..52adb5e --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Style.h @@ -0,0 +1,17 @@ +/**-*-C++-*- + * \file + * + * \brief Style definition. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ +#ifndef IRONY_MODE_SERVER_STYLE_H_ +#define IRONY_MODE_SERVER_STYLE_H_ + +enum class PrefixMatchStyle { +  Exact, CaseInsensitive, SmartCase, +}; + + +#endif //IRONY_MODE_SERVER_STYLE_H_ diff --git a/elpa/irony-20220110.849/server/src/TUManager.cpp b/elpa/irony-20220110.849/server/src/TUManager.cpp new file mode 100644 index 0000000..afbdc82 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/TUManager.cpp @@ -0,0 +1,182 @@ +/** + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * \brief See TUManager.hh + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + * + */ + +#include "TUManager.h" + +#include <iostream> + +TUManager::TUManager() +  : index_(clang_createIndex(0, 0)) +  , translationUnits_() +  , parseTUOptions_(clang_defaultEditingTranslationUnitOptions()) { + +  // this seems necessary to trigger "correct" reparse (/codeCompleteAt) +  // clang_reparseTranslationUnit documentation states: +  // +  // >  * \param TU The translation unit whose contents will be re-parsed. The +  // >  * translation unit must originally have been built with +  // >  * \c clang_createTranslationUnitFromSourceFile(). +  // +  // clang_createTranslationUnitFromSourceFile() is just a call to +  // clang_parseTranslationUnit() with +  // CXTranslationUnit_DetailedPreprocessingRecord enabled but because we want +  // some other flags to be set we can't just call +  // clang_createTranslationUnitFromSourceFile() +  parseTUOptions_ |= CXTranslationUnit_DetailedPreprocessingRecord; + +#if HAS_BRIEF_COMMENTS_IN_COMPLETION +  parseTUOptions_ |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; +#endif + +  // XXX: A bug in old version of Clang (at least '3.1-8') caused the completion +  // to fail on the standard library types when +  // CXTranslationUnit_PrecompiledPreamble is used. We disable this option for +  // old versions of libclang. As a result the completion will work but +  // significantly slower. +  // +  // -- https://github.com/Sarcasm/irony-mode/issues/4 +  if (CINDEX_VERSION < 6) { +    parseTUOptions_ &= ~CXTranslationUnit_PrecompiledPreamble; +  } + +#if CINDEX_VERSION >= 34 +  // Keep going even after fatal errors, or syntax checking will stop after the +  // first error. For instance unused variables will not be reported until the +  // error has been fixed. +  parseTUOptions_ |= CXTranslationUnit_KeepGoing; +#endif +} + +TUManager::~TUManager() { +  clang_disposeIndex(index_); +} + +CXTranslationUnit &TUManager::tuRef(const std::string &filename, +                                    const std::vector<std::string> &flags) { +  CXTranslationUnit &tu = translationUnits_[filename]; + +  // if the flags changed since the last time, invalidate the translation unit +  auto &flagsCache = flagsPerFileCache_[filename]; +  if (flagsCache.size() != flags.size() || +      !std::equal(flagsCache.begin(), flagsCache.end(), flags.begin())) { +    if (tu) { +      clang_disposeTranslationUnit(tu); +      tu = nullptr; +    } +    // remember the flags for the next parse +    flagsCache = flags; +  } +  return tu; +} + +CXTranslationUnit +TUManager::parse(const std::string &filename, +                 const std::vector<std::string> &flags, +                 const std::vector<CXUnsavedFile> &unsavedFiles) { +  CXTranslationUnit &tu = tuRef(filename, flags); + +  if (tu == nullptr) { +    std::vector<const char *> argv; + +#ifdef CLANG_RESOURCE_DIR +    // Make sure libclang find its builtin headers, this is a known issue with +    // libclang, see: +    // - http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-July/022893.html +    // +    // > Make sure that Clang is using its own . It will be in a directory +    // > ending in clang/3.2/include/ where 3.2 is the version of clang that you +    // > are using. You may need to explicitly add it to your header search. +    // > Usually clang finds this directory relative to the executable with +    // > CompilerInvocation::GetResourcesPath(Argv0, MainAddr), but using just +    // > the libraries, it can't automatically find it. +    argv.push_back("-resource-dir"); +    argv.push_back(CLANG_RESOURCE_DIR); +#endif + +    for (auto &flag : flags) { +      argv.push_back(flag.c_str()); +    } + +    tu = clang_parseTranslationUnit( +        index_, +        filename.c_str(), +        argv.data(), +        static_cast<int>(argv.size()), +        const_cast<CXUnsavedFile *>(unsavedFiles.data()), +        unsavedFiles.size(), +        parseTUOptions_); +  } + +  if (tu == nullptr) { +    std::clog << "error: libclang couldn't parse '" << filename << "'\n"; +    return nullptr; +  } + +  // Reparsing is necessary to enable optimizations. +  // +  // From the clang mailing list (cfe-dev): +  // From: Douglas Gregor +  // Subject: Re: Clang indexing library performance +  // ... +  // You want to use the "default editing options" when parsing the translation +  // unit +  //    clang_defaultEditingTranslationUnitOptions() +  // and then reparse at least once. That will enable the various +  // code-completion optimizations that should bring this time down +  // significantly. +  if (clang_reparseTranslationUnit( +          tu, +          unsavedFiles.size(), +          const_cast<CXUnsavedFile *>(unsavedFiles.data()), +          clang_defaultReparseOptions(tu))) { +    // a 'fatal' error occured (even a diagnostic is impossible) +    clang_disposeTranslationUnit(tu); +    std::clog << "error: libclang couldn't reparse '" << filename << "'\n"; +    tu = 0; +    return nullptr; +  } + +  return tu; +} + +CXTranslationUnit +TUManager::getOrCreateTU(const std::string &filename, +                         const std::vector<std::string> &flags, +                         const std::vector<CXUnsavedFile> &unsavedFiles) { +  if (auto tu = tuRef(filename, flags)) +    return tu; + +  return parse(filename, flags, unsavedFiles); +} + +void TUManager::invalidateCachedTU(const std::string &filename) { +  TranslationUnitsMap::iterator it = translationUnits_.find(filename); + +  if (it != translationUnits_.end()) { +    if (CXTranslationUnit &tu = it->second) +      clang_disposeTranslationUnit(tu); + +    translationUnits_.erase(it); +  } +} + +void TUManager::invalidateAllCachedTUs() { +  TranslationUnitsMap::iterator it = translationUnits_.begin(); + +  while (it != translationUnits_.end()) { +    if (CXTranslationUnit &tu = it->second) { +      clang_disposeTranslationUnit(tu); +      translationUnits_.erase(it++); // post-increment keeps the iterator valid +    } else { +      ++it; +    } +  } +} diff --git a/elpa/irony-20220110.849/server/src/TUManager.h b/elpa/irony-20220110.849/server/src/TUManager.h new file mode 100644 index 0000000..bd7730d --- /dev/null +++ b/elpa/irony-20220110.849/server/src/TUManager.h @@ -0,0 +1,109 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * \brief Translation Unit manager. + * + * Keeps a cache of translation units, reparsing or recreating them as + * necessary. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_TUMANAGER_H_ +#define IRONY_MODE_SERVER_TUMANAGER_H_ + +#include "support/CIndex.h" +#include "support/NonCopyable.h" + +#include <map> +#include <string> +#include <vector> + +class TUManager : public util::NonCopyable { +public: +  TUManager(); +  ~TUManager(); + +  /** +   * \brief Parse \p filename with flag \p flags. +   * +   * The first time call \c clang_parseTranslationUnit() and save the TU in the +   * member \c translationUnits_, The next call with the same \p filename will +   * call \c clang_reparseTranslationUnit(). +   * +   * usage: +   * \code +   * std::vector<std::string> flags; +   * flags.push_back("-I../utils"); +   * CXTranslationUnit tu = tuManager.parse("file.cpp", flags); +   * +   * if (! tu) +   *   std::cerr << "parsing translation unit failed\n"; +   * \endcode +   * +   * \return The translation unit, if the parsing failed the translation unit +   *         will be \c NULL. +   */ +  CXTranslationUnit parse(const std::string &filename, +                          const std::vector<std::string> &flags, +                          const std::vector<CXUnsavedFile> &unsavedFiles); + +  /** +   * \brief Retrieve, creating it if necessary the TU associated to filename. +   * +   * \return The translation unit. Will be NULL if the translation unit couldn't +   *         be created. +   */ +  CXTranslationUnit getOrCreateTU(const std::string &filename, +                          const std::vector<std::string> &flags, +                          const std::vector<CXUnsavedFile> &unsavedFiles); + +  /** +   * \brief Invalidate a given cached TU, the next use of a TU will require +   *        reparsing. +   * +   * This can be useful for example: when the flags used to compile a file have +   * changed. +   * +   * \param filename    The filename for which the associated +   *                    translation unit flags need to be invalidated. +   * +   * \sa invalidateAllCachedTUs() +   */ +  void invalidateCachedTU(const std::string &filename); + +  /** +   * \brief Invalidate all cached TU, the next use of a TU will require +   *        reparsing. +   * +   * \sa invalidateCachedTU() +   */ +  void invalidateAllCachedTUs(); + +private: +  /** +   * \brief Get a reference to the translation unit that matches \p filename +   *        with the given set of flags. +   * +   * The TU will be null if it has never been parsed or if the flags have +   * changed. +   * +   * \todo Find a proper name. +   */ +  CXTranslationUnit &tuRef(const std::string &filename, +                           const std::vector<std::string> &flags); + +private: +  typedef std::map<const std::string, CXTranslationUnit> TranslationUnitsMap; +  typedef std::map<const std::string, std::vector<std::string>> FilenameFlagsMap; + +private: +  CXIndex index_; +  TranslationUnitsMap translationUnits_; // cache variable +  FilenameFlagsMap flagsPerFileCache_; +  unsigned parseTUOptions_; +}; + +#endif /* !IRONY_MODE_SERVER_TUMANAGER_H_ */ diff --git a/elpa/irony-20220110.849/server/src/main.cpp b/elpa/irony-20220110.849/server/src/main.cpp new file mode 100644 index 0000000..7797528 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/main.cpp @@ -0,0 +1,235 @@ +/** + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "Irony.h" +#include "Command.h" + +#include "support/CIndex.h" +#include "support/CommandLineParser.h" + +#include <cassert> +#include <cstdlib> +#include <fstream> +#include <iomanip> +#include <iostream> +#include <iterator> +#include <memory> +#include <vector> + +static void printHelp() { +  std::cout << "usage: irony-server [OPTIONS...] [COMMAND] [ARGS...]\n" +    "\n" +    "Options:\n" +    "  -v, --version\n" +    "  -h, --help\n" +    "  -i, --interactive\n" +    "  -d, --debug\n" +    "  --log-file PATH\n" +    "\n" +    "Commands:\n"; + +#define X(sym, str, desc)                                                      \ +  if (Command::sym != Command::Unknown)                                        \ +    std::cout << std::left << std::setw(25) << "  " str << desc << "\n"; +#include "Commands.def" +} + +static void printVersion() { +  // do not change the format for the first line, external programs should be +  // able to rely on it +  std::cout << "irony-server version " IRONY_PACKAGE_VERSION "\n"; + +  CXString cxVersionString = clang_getClangVersion(); +  std::cout << clang_getCString(cxVersionString) << "\n"; +  clang_disposeString(cxVersionString); +} + +struct CommandProviderInterface { +  virtual ~CommandProviderInterface() { } + +  virtual std::vector<std::string> nextCommand() = 0; +}; + +struct CommandLineCommandProvider : CommandProviderInterface { +  CommandLineCommandProvider(const std::vector<std::string> &argv) +    : argv_(argv), firstCall_(true) { +  } + +  std::vector<std::string> nextCommand() { +    if (firstCall_) { +      firstCall_ = false; +      return argv_; +    } + +    return std::vector<std::string>(1, "exit"); +  } + +private: +  std::vector<std::string> argv_; +  bool firstCall_; +}; + +struct InteractiveCommandProvider : CommandProviderInterface { +  std::vector<std::string> nextCommand() { +    std::string line; + +    if (std::getline(std::cin, line)) { +      return unescapeCommandLine(line); +    } + +    return std::vector<std::string>(1, "exit"); +  } +}; + +struct RestoreClogOnExit { +  RestoreClogOnExit() : rdbuf_(std::clog.rdbuf()) { +  } + +  ~RestoreClogOnExit() { +    std::clog.rdbuf(rdbuf_); +  } + +private: +  RestoreClogOnExit(const RestoreClogOnExit &); +  RestoreClogOnExit &operator=(const RestoreClogOnExit &); + +private: +  std::streambuf *rdbuf_; +}; + +int main(int ac, const char *av[]) { +  std::vector<std::string> argv(&av[1], &av[ac]); + +  // stick to STL streams, no mix of C and C++ for IO operations +  std::ios_base::sync_with_stdio(false); + +  bool interactiveMode = false; + +  Irony irony; + +  if (ac == 1) { +    printHelp(); +    return 1; +  } + +  std::ofstream logFile; + +  // When logging to a specific file, std::clog.rdbuf() is replaced by the log +  // file's one. When we return from the main, this buffer is deleted (at the +  // same time as logFile) but std::clog is still active, and will try to +  // release the rdbuf() which has already been released in logFile's +  // destructor. To avoid this we restore std::clog()'s original rdbuf on exit. +  RestoreClogOnExit clogBufferRestorer; + +  unsigned optCount = 0; +  while (optCount < argv.size()) { +    const std::string &opt = argv[optCount]; + +    if (opt.c_str()[0] != '-') +      break; + +    if (opt == "--help" || opt == "-h") { +      printHelp(); +      return 0; +    } + +    if (opt == "--version" || opt == "-v") { +      printVersion(); +      return 0; +    } + +    if (opt == "--interactive" || opt == "-i") { +      interactiveMode = true; +    } else if (opt == "--debug" || opt == "-d") { +      irony.setDebug(true); +    } else if (opt == "--log-file" && (optCount + 1) < argv.size()) { +      ++optCount; +      logFile.open(argv[optCount]); +      std::clog.rdbuf(logFile.rdbuf()); +    } else { +      std::cerr << "error: invalid option '" << opt << "'\n"; +      return 1; +    } + +    ++optCount; +  } + +  argv.erase(argv.begin(), argv.begin() + optCount); + +  CommandParser commandParser; +  std::unique_ptr<CommandProviderInterface> commandProvider; + +  if (interactiveMode) { +    commandProvider.reset(new InteractiveCommandProvider()); +  } else { +    commandProvider.reset(new CommandLineCommandProvider(argv)); +  } + +  while (Command *c = commandParser.parse(commandProvider->nextCommand())) { +    if (c->action != Command::Exit) { +      std::clog << "execute: " << *c << std::endl; +    } + +    switch (c->action) { +    case Command::Help: +      printHelp(); +      break; + +    case Command::Candidates: +      irony.candidates(c->prefix, c->style); +      break; + +    case Command::CompletionDiagnostics: +      irony.completionDiagnostics(); +      break; + +    case Command::Complete: +      irony.complete(c->file, c->line, c->column, c->flags); +      break; + +    case Command::Diagnostics: +      irony.diagnostics(); +      break; + +    case Command::Exit: +      return 0; + +    case Command::GetCompileOptions: +      irony.getCompileOptions(c->dir, c->file); +      break; + +    case Command::GetType: +      irony.getType(c->line, c->column); +      break; + +    case Command::Parse: +      irony.parse(c->file, c->flags); +      break; + +    case Command::SetDebug: +      irony.setDebug(c->opt); +      break; + +    case Command::SetUnsaved: +      irony.setUnsaved(c->file, c->unsavedFile); +      break; + +    case Command::ResetUnsaved: +      irony.resetUnsaved(c->file); +      break; + +    case Command::Unknown: +      assert(0 && "unreacheable code...reached!"); +      break; +    } + +    std::cout << "\n;;EOT\n" << std::flush; +  } + +  return 1; +} diff --git a/elpa/irony-20220110.849/server/src/support/CIndex.h b/elpa/irony-20220110.849/server/src/support/CIndex.h new file mode 100644 index 0000000..89d3f62 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/CIndex.h @@ -0,0 +1,33 @@ +/** + * \file + * \brief Wrapper around Clang Indexing Public C Interface header. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ +#define IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ + +#include <clang-c/Index.h> + +/// Use <tt>\#if CINDEX_VERSION_VERSION > 10047</tt> to test for +/// CINDEX_VERSION_MAJOR = 1 and CINDEX_VERSION_MINOR = 47. +#ifndef CINDEX_VERSION +#define CINDEX_VERSION 0 // pre-clang 3.2 support +#endif + +#if CINDEX_VERSION >= 6 +#define HAS_BRIEF_COMMENTS_IN_COMPLETION 1 +#else +#define HAS_BRIEF_COMMENTS_IN_COMPLETION 0 +#endif + +#if CINDEX_VERSION >= 6 +#define HAS_COMPILATION_DATABASE 1 +#include <clang-c/CXCompilationDatabase.h> +#else +#define HAS_COMPILATION_DATABASE 0 +#endif + +#endif /* !IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ */ diff --git a/elpa/irony-20220110.849/server/src/support/CommandLineParser.cpp b/elpa/irony-20220110.849/server/src/support/CommandLineParser.cpp new file mode 100644 index 0000000..a838092 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/CommandLineParser.cpp @@ -0,0 +1,119 @@ +/** + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "CommandLineParser.h" + +namespace { + +/// \brief A parser for escaped strings of command line arguments. +/// +/// Assumes \-escaping for quoted arguments (see the documentation of +/// unescapeCommandLine(...)). +class CommandLineArgumentParser { +public: +  CommandLineArgumentParser(const std::string &commandLine) +    : input_(commandLine), position_(input_.begin() - 1) { +  } + +  std::vector<std::string> parse() { +    bool hasMoreInput = true; +    while (hasMoreInput && nextNonWhitespace()) { +      std::string argument; +      hasMoreInput = parseStringInto(argument); +      commandLine_.push_back(argument); +    } +    return commandLine_; +  } + +private: +  // All private methods return true if there is more input available. + +  bool parseStringInto(std::string &string) { +    do { +      if (*position_ == '"') { +        if (!parseDoubleQuotedStringInto(string)) +          return false; +      } else if (*position_ == '\'') { +        if (!parseSingleQuotedStringInto(string)) +          return false; +      } else { +        if (!parseFreeStringInto(string)) +          return false; +      } +    } while (*position_ != ' '); +    return true; +  } + +  bool parseDoubleQuotedStringInto(std::string &string) { +    if (!next()) +      return false; +    while (*position_ != '"') { +      if (!skipEscapeCharacter()) +        return false; +      string.push_back(*position_); +      if (!next()) +        return false; +    } +    return next(); +  } + +  bool parseSingleQuotedStringInto(std::string &string) { +    if (!next()) +      return false; +    while (*position_ != '\'') { +      string.push_back(*position_); +      if (!next()) +        return false; +    } +    return next(); +  } + +  bool parseFreeStringInto(std::string &string) { +    do { +      if (!skipEscapeCharacter()) +        return false; +      string.push_back(*position_); +      if (!next()) +        return false; +    } while (*position_ != ' ' && *position_ != '"' && *position_ != '\''); +    return true; +  } + +  bool skipEscapeCharacter() { +    if (*position_ == '\\') { +      return next(); +    } +    return true; +  } + +  bool nextNonWhitespace() { +    do { +      if (!next()) +        return false; +    } while (*position_ == ' '); +    return true; +  } + +  bool next() { +    ++position_; +    return position_ != input_.end(); +  } + +private: +  const std::string input_; +  std::string::const_iterator position_; +  std::vector<std::string> commandLine_; +}; + +} // unnamed namespace + +std::vector<std::string> +unescapeCommandLine(const std::string &escapedCommandLine) { +  CommandLineArgumentParser parser(escapedCommandLine); +  return parser.parse(); +} diff --git a/elpa/irony-20220110.849/server/src/support/CommandLineParser.h b/elpa/irony-20220110.849/server/src/support/CommandLineParser.h new file mode 100644 index 0000000..b764797 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/CommandLineParser.h @@ -0,0 +1,21 @@ +/** + * \file + * \brief Facility to parse a command line into a string array. + * + * \note Please note that the code borrowed from the Clang, + * lib/Tooling/JSONCompilationDatabase.cpp. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_ +#define IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_ + +#include <string> +#include <vector> + +std::vector<std::string> +unescapeCommandLine(const std::string &escapedCommandLine); + +#endif // IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_ diff --git a/elpa/irony-20220110.849/server/src/support/NonCopyable.h b/elpa/irony-20220110.849/server/src/support/NonCopyable.h new file mode 100644 index 0000000..d30a5b2 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/NonCopyable.h @@ -0,0 +1,34 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * \brief NonCopyable class like in Boost. + * + * \see http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-copyable_Mixin + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ +#define IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ + +namespace util { + +class NonCopyable { +protected: +  NonCopyable() { +  } + +  // Protected non-virtual destructor +  ~NonCopyable() { +  } + +private: +  NonCopyable(const NonCopyable &); +  NonCopyable &operator=(const NonCopyable &); +}; + +} // ! namespace util + +#endif /* !IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ */ diff --git a/elpa/irony-20220110.849/server/src/support/TemporaryFile.cpp b/elpa/irony-20220110.849/server/src/support/TemporaryFile.cpp new file mode 100644 index 0000000..e7393e1 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/TemporaryFile.cpp @@ -0,0 +1,74 @@ +/** + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "TemporaryFile.h" + +#include <algorithm> +#include <cstdio> +#include <cstdlib> +#include <fstream> +#include <iostream> +#include <random> + +static std::string getTemporaryFileDirectory() { +  const char *temporaryDirEnvVars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; + +  for (const char *envVar : temporaryDirEnvVars) { +    if (const char *dir = std::getenv(envVar)) +      return dir; +  } + +  return "/tmp"; +} + +TemporaryFile::TemporaryFile(const std::string &prefix, +                             const std::string &suffix) +  : pathOrPattern_(prefix + "-%%%%%%" + suffix) { +} + +TemporaryFile::~TemporaryFile() { +  if (openedFile_) { +    openedFile_.reset(); +    std::remove(pathOrPattern_.c_str()); +  } +} + +const std::string &TemporaryFile::getPath() { +  if (!openedFile_) { +    openedFile_.reset(new std::fstream); + +    std::random_device rd; +    std::default_random_engine e(rd()); +    std::uniform_int_distribution<int> dist(0, 15); +    std::string pattern = pathOrPattern_; +    std::string tmpDir = getTemporaryFileDirectory() + "/"; +    int i = 0; + +    do { +      // exiting is better than infinite loop +      if (++i > TemporaryFile::MAX_ATTEMPS) { +        std::cerr << "error: couldn't create temporary file, please check your " +                     "temporary file directory (" << tmpDir << ")\n"; +        exit(EXIT_FAILURE); +      } + +      // make the filename based on the pattern +      std::transform(pattern.begin(), +                     pattern.end(), +                     pathOrPattern_.begin(), +                     [&e, &dist](char ch) { +        return ch == '%' ? "0123456789abcdef"[dist(e)] : ch; +      }); +      // create the file +      openedFile_->open(tmpDir + pathOrPattern_, std::ios_base::out); +    } while (!openedFile_->is_open()); +    pathOrPattern_ = tmpDir + pathOrPattern_; +  } + +  return pathOrPattern_; +} diff --git a/elpa/irony-20220110.849/server/src/support/TemporaryFile.h b/elpa/irony-20220110.849/server/src/support/TemporaryFile.h new file mode 100644 index 0000000..5bf77f4 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/TemporaryFile.h @@ -0,0 +1,36 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin <guillaume.papin@epitech.eu> + * + * \brief Not the best piece of code out there. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_ +#define IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_ + +#include <iosfwd> +#include <memory> +#include <string> + +class TemporaryFile { +  enum { +    // if we can't create the temp file, exits. +    MAX_ATTEMPS = 25 +  }; + +public: +  TemporaryFile(const std::string &prefix, const std::string &suffix = ""); +  ~TemporaryFile(); + +  /// Returns the path of this temporary filename +  const std::string &getPath(); + +private: +  std::string pathOrPattern_; +  std::unique_ptr<std::fstream> openedFile_; +}; + +#endif // IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_ diff --git a/elpa/irony-20220110.849/server/src/support/iomanip_quoted.h b/elpa/irony-20220110.849/server/src/support/iomanip_quoted.h new file mode 100644 index 0000000..a8ca38b --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/iomanip_quoted.h @@ -0,0 +1,52 @@ +/**-*-C++-*- + * \file + * \brief Dumb implementation of something that might look like C++14 + * std::quoted. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_ +#define IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_ + +#include <ostream> +#include <string> + +namespace support { +namespace detail { + +struct QuotedStringProxy { +  QuotedStringProxy(const std::string &s) : s(s) { +  } + +  std::string s; +}; + +std::ostream &operator<<(std::ostream &os, const QuotedStringProxy &q) { +  const std::string &s = q.s; + +  os << '"'; +  if (s.find_first_of("\"\\") == std::string::npos) { +    os << s; +  } else { +    for (auto ch : s) { +      if (ch == '\\' || ch == '"') +        os << '\\'; + +      os << ch; +    } +  } +  os << '"'; +  return os; +} + +} // namespace detail + +detail::QuotedStringProxy quoted(const std::string &s) { +  return detail::QuotedStringProxy(s); +} + +} // namespace support + +#endif // IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_ diff --git a/elpa/irony-20220110.849/server/test/CMakeLists.txt b/elpa/irony-20220110.849/server/test/CMakeLists.txt new file mode 100644 index 0000000..078b69a --- /dev/null +++ b/elpa/irony-20220110.849/server/test/CMakeLists.txt @@ -0,0 +1,3 @@ +add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) + +add_subdirectory (elisp) diff --git a/elpa/irony-20220110.849/server/test/elisp/CMakeLists.txt b/elpa/irony-20220110.849/server/test/elisp/CMakeLists.txt new file mode 100644 index 0000000..8877a89 --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/CMakeLists.txt @@ -0,0 +1,47 @@ +# On MS-Windows, emacs_dir is a special environment variable, which +# indicates the full path of the directory in which Emacs is +# installed. +# +# There is no standard location that I know of for Emacs on Windows so +# using this special environment variable will at least help people +# who build the server from inside Emacs. +if(DEFINED ENV{emacs_dir}) +  list(APPEND EMACS_EXECUTABLE_HINTS $ENV{emacs_dir}/bin) +endif() + +find_program(EMACS_EXECUTABLE emacs +  HINTS ${EMACS_EXECUTABLE_HINTS}) + +if (EMACS_EXECUTABLE) +  message(STATUS "Found emacs: ${EMACS_EXECUTABLE}") +else() +  message(WARNING "emacs not found: elisp tests will be skipped!") +  return() +endif() + +# +# add_ert_test(<FileName>) +# +# Create a test which run the given Elisp script using the Emacs ERT testing +# framework. +# +# The target is deduced from the ``FileName`` argument, e.g: the file foo.el +# will be the target 'check-foo-el'. +# +# FIXME: assumes MELPA is configured... +function(add_ert_test test_file) +  get_filename_component(name ${test_file} NAME_WE) +  add_test(check-${name}-el +    ${EMACS_EXECUTABLE} -Q --batch +    -l package +    --eval "(package-initialize)" +    --eval "(unless (require 'cl-lib nil t) (package-refresh-contents) (package-install 'cl-lib))" +    -l ${CMAKE_CURRENT_SOURCE_DIR}/${test_file} +    -f ert-run-tests-batch-and-exit) + +  set_tests_properties(check-${name}-el PROPERTIES TIMEOUT 15) +endfunction() + +add_ert_test(irony.el) +add_ert_test(irony-iotask.el) +add_ert_test(irony-cdb-json.el) diff --git a/elpa/irony-20220110.849/server/test/elisp/irony-cdb-json.el b/elpa/irony-20220110.849/server/test/elisp/irony-cdb-json.el new file mode 100644 index 0000000..a810dae --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/irony-cdb-json.el @@ -0,0 +1,87 @@ +;; -*-no-byte-compile: t; -*- +(load + (concat (file-name-directory (or load-file-name buffer-file-name)) +         "test-config")) + +(require 'irony-cdb-json) +(require 'cl-lib) + +(defconst irony-cdb/compile-command +  '((file      . "../src/file.cc") +    (directory . "/home/user/project/build") +    (command   . "/usr/bin/clang++ -DSOMEDEF=1 -c -o file.o /home/user/project/src/file.cc"))) + +(ert-deftest cdb/parse/simple/path-is-absolute () +  (should +   (equal "/home/user/project/src/file.cc" +          (nth 0 (irony-cdb-json--transform-compile-command +                  irony-cdb/compile-command))))) + +(ert-deftest cdb/parse/simple/compile-options () +  (should +   (equal '("-DSOMEDEF=1") +          (nth 1 (irony-cdb-json--transform-compile-command +                  irony-cdb/compile-command))))) + +(ert-deftest cdb/parse/simple/invocation-directory () +  (should +   (equal "/home/user/project/build" +          (nth 2 (irony-cdb-json--transform-compile-command +                  irony-cdb/compile-command))))) + +(ert-deftest cdb/choose-closest-path/chooses-closest () +  (should +   (equal "/tmp/a/cdb" +          (irony-cdb--choose-closest-path "/tmp/a/1" +                                          '("/tmp/a/cdb" "/tmp/cdb"))))) + +(ert-deftest cdb/choose-closest-path/chooses-closest2 () +  (should +   (equal "/tmp/a/cdb" +          (irony-cdb--choose-closest-path "/tmp/a/1" +                                          '("/tmp/cdb" "/tmp/a/cdb"))))) + +(ert-deftest cdb/choose-closest-path/prefers-deeper () +  (should +   (equal "/tmp/a/build/cdb" +          (irony-cdb--choose-closest-path "/tmp/a/1" +                                          '("/tmp/a/build/cdb" "/tmp/cdb"))))) + +(ert-deftest cdb/choose-closest-path/prefers-deeper2 () +  (should +   (equal "/tmp/a/build/cdb" +          (irony-cdb--choose-closest-path "/tmp/a/1" +                                          '("/tmp/cdb" "/tmp/a/build/cdb"))))) + +(ert-deftest cdb/choose-closest-path/will-survive-garbage () +  (should +   (equal nil +          (irony-cdb--choose-closest-path "/tmp/a/1" +                                          'ordures)))) + +; http://endlessparentheses.com/understanding-letf-and-how-it-replaces-flet.html +(ert-deftest cdb/locate-db/choose-among-candidates () +  (should +   (equal "/foo/build/cdb" +          (cl-letf (((symbol-function 'locate-dominating-file) +                     (lambda  (file name) +                       (cond +                         ((string= name "./cdb") "/") ; found /cdb +                         ((string= name "build/cdb") "/foo/") ; found /foo/build/cdb +                         )))) +            (irony-cdb--locate-dominating-file-with-dirs "/foo/bar/qux.cpp" +                                                         "cdb" +                                                         '("." "build" "out/x86_64")))))) + +(ert-deftest cdb/locate-dominating-file-with-dirs/children-first () +  (should +   (equal "/tmp/foo/bar/out/x86_64/cdb" +          (cl-letf (((symbol-function 'locate-dominating-file) +                     (lambda  (file name) +                       (cond +                         ((string= name "./cdb") "/tmp/foo/") ; found /tmp/foo/cdb +                         ((string= name "out/x86_64/cdb") "/tmp/foo/bar/") ;found /tmp/foo/bar/out/x86_64/cdb +                         )))) +                    (irony-cdb--locate-dominating-file-with-dirs "/tmp/foo/bar/qux.cpp" +                                                                 "cdb" +                                                                 '("." "out/x86_64" )))))) diff --git a/elpa/irony-20220110.849/server/test/elisp/irony-iotask.el b/elpa/irony-20220110.849/server/test/elisp/irony-iotask.el new file mode 100644 index 0000000..7ef213b --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/irony-iotask.el @@ -0,0 +1,249 @@ +;; -*-no-byte-compile: t; -*- +(load (concat (file-name-directory (or load-file-name +                                       buffer-file-name)) +              "test-config")) + +;; load irony-iotask +;; +;; XXX: No idea why this is necessary, test-config already adds the directory to +;; the load-path so irony is found... +(unless (require 'irony-iotask nil t) +  (let ((irony-iotask-dir (expand-file-name "../../.." test-dir))) +    (add-to-list 'load-path irony-iotask-dir) +    (require 'irony-iotask))) + +(defun irony-iotask-echo-process-exit-filter (process output) +  (when (buffer-live-p (process-buffer process)) +    (with-current-buffer (process-buffer process) +      (goto-char (process-mark process)) +      (insert output) +      (set-marker (process-mark process) (point)) +      (when (>= (buffer-size) (length "exit\n")) +        (should (string= (buffer-string) "exit\n")) +        (erase-buffer))))) + +;; Note: these tests use process communication with the standard I/O streams. +;; The subprocess used for this communication is Emacs. +;; +;; The following article provides useful information for using Elisp as a +;; scripting language, Emacs as an interpreter, it details how the standard I/O +;; streams works in Elisp scripts: +;; - http://www.lunaryorn.com/2014/08/12/emacs-script-pitfalls.html + +(defmacro irony-iotask/with-echo-process (&rest body) +  "Start an Emacs process that runs the given PROCESS-SCRIPT. + +The process is setup with `irony-iotask-setup-process'. + +It's possible to schedule some iotasks in the BODY for testing. + +There is an exposed variable named `process' available for use in +BODY. + +Elisp is used as a scripting language because it should be +available on all OSes irony-iotask support." +  (declare (indent 1)) +  `(let ((process-connection-type nil) +         (process-adaptive-read-buffering nil) +         process) +     (setq process +           (start-process "emacs-irony-test" +                          "*emacs-irony-test*" +                          (expand-file-name invocation-name +                                            invocation-directory) +                          "-Q" +                          "--batch" +                          "--eval" +                          (prin1-to-string +                           '(let ((msg)) +                              (while (not (equal msg "exit")) +                                (setq msg (read-from-minibuffer "")) +                                (message msg)))))) +     (unwind-protect +         (progn +           (irony-iotask-setup-process process) +           ,@body) +       ;; the iotask process filter does not clean the process buffer +       ;; at the end of a request, but at the begining of a new one +       (with-current-buffer (process-buffer process) +         (erase-buffer)) +       (set-process-filter process #'irony-iotask-echo-process-exit-filter) +       (process-send-string process "exit\n") +       ;; wait for the process to finish normally, or kill it if it doesn't +       (with-timeout (1 (kill-process process)) +         (while (process-live-p process) +           (sit-for 0.05))) +       ;; start with a clean buffer, +       ;; Emacs 24.3 seems to keep some +       (kill-buffer (process-buffer process)) +       (delete-process process)))) + +;; irony-iotask-result + +(ert-deftest irony-iotask-result/ready-p-value () +  (let ((result (irony-iotask-result-create))) +    (should-not (irony-iotask-result-valid-p result)) +    (irony-iotask-result-set-value result 1) +    (should (irony-iotask-result-valid-p result)))) + +(ert-deftest irony-iotask-result/ready-p-error () +  (let ((result (irony-iotask-result-create))) +    (should-not (irony-iotask-result-valid-p result)) +    (irony-iotask-result-set-error result 'irony-iotask-error (list "blah")) +    (should (irony-iotask-result-valid-p result)))) + +(ert-deftest irony-iotask-result/set-value () +  (let ((result (irony-iotask-result-create))) +    (irony-iotask-result-set-value result 'blah) +    (should (eq (irony-iotask-result-get result) 'blah)))) + +(irony--define-error 'irony-iotask-result/test-error +                     "Irony I/O task sample error") + +(ert-deftest irony-iotask-result/set-error () +  (let ((result (irony-iotask-result-create))) +    (irony-iotask-result-set-error result 'irony-iotask-result/test-error) +    (should-error (irony-iotask-result-get result) +                  :type 'irony-iotask-result/test-error))) + +(ert-deftest irony-iotask-result/set-error-data () +  (let ((result (irony-iotask-result-create))) +    (irony-iotask-result-set-error result +                                   'irony-iotask-result/test-error +                                   'foo 'bar 'baz 'qux) +    (condition-case err +        (irony-iotask-result-get result) +      (irony-iotask-result/test-error +       (should (equal (cdr err) '(foo bar baz qux))))))) + +(ert-deftest irony-iotask-result/get-empty () +  (let ((result (irony-iotask-result-create))) +    (should-error (irony-iotask-result-get result) +                  :type 'irony-iotask-result-get-error))) + +;; task + +(irony-iotask-define-task irony-iotask/task-start-t +  "doc" +  :start (lambda (&optional value) +           (irony-iotask-set-result (or value 42)))) + +(ert-deftest irony-iotask/task-start/simple () +  (let ((task (irony-iotask-package-task irony-iotask/task-start-t))) +    (irony-iotask/with-echo-process +     (should (equal 42 (irony-iotask-run process task)))))) + +(ert-deftest irony-iotask/task-start/with-arguments () +  (let ((task (irony-iotask-package-task irony-iotask/task-start-t 43))) +    (irony-iotask/with-echo-process +     (should (equal 43 (irony-iotask-run process task)))))) + +(irony-iotask-define-task irony-iotask/task-update-t +  "doc" +  :start (lambda (&optional hello) +           (irony-iotask-send-string (format "%s\n" (or hello "hello")))) +  :update (lambda (&optional hello) +            (setq hello (or hello "hello")) +            (when (string= (buffer-string) (format "%s\n" hello)) +              (irony-iotask-set-result (format "%s ok" hello))))) + +(ert-deftest irony-iotask-schedule/task-update/simple () +  (let ((task (irony-iotask-package-task irony-iotask/task-update-t))) +    (irony-iotask/with-echo-process +     (should (string= "hello ok" (irony-iotask-run process task)))))) + +(ert-deftest irony-iotask-schedule/task-update/with-arguments () +  (let ((task (irony-iotask-package-task irony-iotask/task-update-t "bonjour"))) +    (irony-iotask/with-echo-process +     (should (string= "bonjour ok" (irony-iotask-run process task)))))) + +(irony-iotask-define-task irony-iotask/task-invalid-msg-t +  "doc" +  :start (lambda () +           (irony-iotask-send-string "ping\n")) +  :update (lambda () +            (when (string= (buffer-string) "ping\n") +              (throw 'invalid-msg t)))) + +(ert-deftest irony-iotask-schedule/task-update/invalid-msg () +  (let ((task (irony-iotask-package-task irony-iotask/task-invalid-msg-t))) +    (irony-iotask/with-echo-process +     (should-error (irony-iotask-run process task) +                   :type 'irony-iotask-bad-data)))) + +(ert-deftest irony-iotask-chain/simple () +  (let ((task (irony-iotask-chain +               (irony-iotask-package-task irony-iotask/task-update-t "hi") +               (irony-iotask-package-task irony-iotask/task-update-t "hej")))) +    (irony-iotask/with-echo-process +     (should (equal "hej ok" (irony-iotask-run process task)))))) + +(defvar irony-iotask/task-finish-var nil) +(defvar irony-iotask/task-on-var nil) +(irony-iotask-define-task irony-iotask/task-finish-t +  "doc" +  :start (lambda () +           (irony-iotask-put :text "how") +           (irony-iotask-send-string "hello\n")) +  :update (lambda () +            (cond +             ((string= (buffer-string) "hello\n") +              (irony-iotask-put :text (concat (irony-iotask-get :text) " are")) +              (irony-iotask-set-result t)) +             ((>= (buffer-size) (1+ (length "hello\n"))) +              (throw 'invalid-msg t)))) +  :on-success (lambda () +                (setq irony-iotask/task-on-var "success")) +  :finish (lambda () +            (setq irony-iotask/task-finish-var (concat (irony-iotask-get :text) +                                                       " you?")))) + +(ert-deftest irony-iotask-schedule/task-finish/simple () +  (let ((task (irony-iotask-package-task irony-iotask/task-finish-t))) +    (irony-iotask/with-echo-process +     (setq irony-iotask/task-finish-var nil) +     (irony-iotask-run process task) +     (should (equal "how are you?" irony-iotask/task-finish-var))))) + +(ert-deftest irony-iotask-schedule/task-on-success/simple () +  (let ((task (irony-iotask-package-task irony-iotask/task-finish-t))) +    (irony-iotask/with-echo-process +     (setq irony-iotask/task-on-var nil) +     (irony-iotask-run process task) +     (should (equal "success" irony-iotask/task-on-var))))) + +(irony-iotask-define-task irony-iotask/task-on-error-t +  "doc" +  :start (lambda () +           (irony-iotask-set-error 'irony-iotask-error)) +  :on-error (lambda () +              (setq irony-iotask/task-on-var "error"))) + +(ert-deftest irony-iotask-schedule/task-on-error/simple () +  (let ((task (irony-iotask-package-task irony-iotask/task-on-error-t))) +    (irony-iotask/with-echo-process +     (setq irony-iotask/task-on-var nil) +     (ignore-errors +       (irony-iotask-run process task)) +     (should (equal "error" irony-iotask/task-on-var))))) + +(ert-deftest irony-iotask-schedule/callback/recalls-schedule () +  (let ((task (irony-iotask-package-task irony-iotask/task-update-t "a"))) +    (irony-iotask/with-echo-process +     (lexical-let ((run-process process) +                   results) +       (irony-iotask-schedule process task +                              (lambda (result) +                                (setq results (list result)) +                                (irony-iotask-schedule +                                 run-process +                                 (irony-iotask-package-task +                                  irony-iotask/task-update-t "b") +                                 (lambda (result) +                                   (setq results (append results (list result))))))) +       (should (with-local-quit +                 (while (< (length results) 2) +                   (accept-process-output process 0.05)) +                 t)) +       (should (string= "a ok" (irony-iotask-result-get (nth 0 results)))) +       (should (string= "b ok" (irony-iotask-result-get (nth 1 results)))))))) diff --git a/elpa/irony-20220110.849/server/test/elisp/irony.el b/elpa/irony-20220110.849/server/test/elisp/irony.el new file mode 100644 index 0000000..fe2378b --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/irony.el @@ -0,0 +1,121 @@ +;; -*-no-byte-compile: t; -*- +(load (concat (file-name-directory (or load-file-name +                                       buffer-file-name)) +              "test-config")) + +(ert-deftest irony/buffer-size-in-bytes () +  (with-temp-buffer +    ;; this smiley takes 3 bytes apparently +    (insert "☺") +    (should (equal 3 (irony--buffer-size-in-bytes))) +    (erase-buffer) +    (insert "☺\n") +    (should (equal 4 (irony--buffer-size-in-bytes))) +    (erase-buffer) +    (insert "\t") +    (should (equal 1 (irony--buffer-size-in-bytes))))) + +(ert-deftest irony/find-server-executable/does-not-exists () +  (let ((irony-server-install-prefix "/does/not/exists") +        (exec-path nil)) +    (should-error (irony--find-server-executable) +                  :type 'irony-server-error))) + +(ert-deftest irony/split-command-line/just-spaces () +  (let ((cmd-line "clang -Wall -Wextra")) +    (should (equal +             '("clang" "-Wall" "-Wextra") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/start-with-space () +  (let ((cmd-line " clang -Wall -Wextra")) +    (should (equal +             '("clang" "-Wall" "-Wextra") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/end-with-space () +  (let ((cmd-line "clang -Wall -Wextra ")) +    (should (equal +             '("clang" "-Wall" "-Wextra") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/space-everywhere () +  (let ((cmd-line "    \t  clang   \t  -Wall \t  -Wextra\t")) +    (should (equal +             '("clang" "-Wall" "-Wextra") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/with-quotes () +  (let ((cmd-line "clang -Wall -Wextra \"-I/tmp/dir with spaces\"")) +    (should (equal +             '("clang" "-Wall" "-Wextra" "-I/tmp/dir with spaces") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/with-quotes () +  "Test if files are removed from the arguments list. + +https://github.com/Sarcasm/irony-mode/issues/101" +  (let ((cmd-line "g++ -DFOO=\\\"\\\"")) +    (should (equal +             '("g++" "-DFOO=\"\"") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/start-with-quotes () +  (let ((cmd-line "\"cl ang\" -Wall -Wextra \"-I/tmp/dir with spaces\"")) +    (should (equal +             '("cl ang" "-Wall" "-Wextra" "-I/tmp/dir with spaces") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/quotes-in-word () +  (let ((cmd-line "clang -Wall -Wextra -I\"/tmp/dir with spaces\"")) +    (should (equal +             '("clang" "-Wall" "-Wextra" "-I/tmp/dir with spaces") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/ill-end-quote () +  (let ((cmd-line "clang -Wall -Wextra\"")) +    (should-error (irony--split-command-line cmd-line) +                  :type 'irony-parse-error))) + +(ert-deftest irony/split-command-line/backslash-1 () +  (let ((cmd-line "clang\\ -Wall")) +    (should (equal +             '("clang -Wall") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/backslash-2 () +  (let ((cmd-line "\\\\\\ clang\\ -Wall\\")) +    (should (equal +             '("\\ clang -Wall\\") +             (irony--split-command-line cmd-line))))) + +(ert-deftest irony/extract-working-directory-option/not-specified () +  (let ((compile-flags '("-Wall"))) +    (should +     (not (irony--extract-working-directory-option compile-flags))))) + +(ert-deftest irony/extract-working-directory-option/specified-1 () +  (let ((compile-flags '("-working-directory" "/tmp/lol"))) +    (should (equal "/tmp/lol" +                   (irony--extract-working-directory-option compile-flags))))) + +(ert-deftest irony/extract-working-directory-option/specified-2 () +  (let ((compile-flags '("-Wall" "-working-directory=/tmp/lol" "-Wshadow"))) +    (should (equal "/tmp/lol" +                   (irony--extract-working-directory-option compile-flags))))) + +;; TODO: restore functionality +;; (ert-deftest irony/include-directories-1 () +;;   (let ((irony-compile-flags '("-Iinclude" "-I/tmp/foo")) +;;         (irony-compile-flags-work-dir "/tmp/blah/")) +;;     (should (equal +;;              '("/tmp/blah/include" "/tmp/foo") +;;              (irony-user-search-paths))))) + +;; (ert-deftest irony/include-directories-2 () +;;   (let ((irony-compile-flags '("-Wextra" "-Iinclude" "-I" "foo" "-Wall")) +;;         (irony-compile-flags-work-dir "/tmp/blah/")) +;;     (should (equal +;;              '("/tmp/blah/include" +;;                "/tmp/blah/foo") +;;              (irony-user-search-paths))))) diff --git a/elpa/irony-20220110.849/server/test/elisp/test-config.el b/elpa/irony-20220110.849/server/test/elisp/test-config.el new file mode 100644 index 0000000..224cdd0 --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/test-config.el @@ -0,0 +1,14 @@ +;; -*-no-byte-compile: t; -*- +(defvar test-dir (if load-file-name +                     (file-name-as-directory +                      (expand-file-name (concat (file-name-directory +                                                 load-file-name))))) +  "Elisp test directory path.") + +;; load irony +(unless (require 'irony nil t) +  (let ((irony-dir (expand-file-name "../../.." test-dir))) +    (add-to-list 'load-path irony-dir) +    (require 'irony))) + +(require 'ert) | 
