コード例 #1
0
    def get_completions(self, request, response):
        src = request['src']
        contents = request['contents']
        line = request['line']
        column = request['column']
        prefix = request['prefix']
        token_start_column = request['tokenStartColumn']
        flags = request['flags']

        # NOTE: there is no need to update the translation unit here.
        # libclang's completions API seamlessly takes care of unsaved content
        # without any special handling.
        translation_unit = self._get_translation_unit(src, flags)
        if translation_unit:
            if self.completion_cache is None:
                self.completion_cache = CompletionCache(
                    self.src, translation_unit)
            completions = self.completion_cache.get_completions(
                line + 1,
                token_start_column + 1,
                prefix,
                contents,
                limit=COMPLETIONS_LIMIT)
        else:
            completions = []
        response['file'] = src
        response['completions'] = completions
        response['line'] = line
        response['column'] = column
        response['prefix'] = prefix
コード例 #2
0
ファイル: clang_server.py プロジェクト: BruceZu/nuclide
    def get_completions(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        prefix = request['prefix']
        token_start_column = request['tokenStartColumn']
        flags = request['flags']

        # NOTE: there is no need to update the translation unit here.
        # libclang's completions API seamlessly takes care of unsaved content
        # without any special handling.
        translation_unit = self._get_translation_unit(None, flags)
        if translation_unit:
            if self.completion_cache is None:
                self.completion_cache = CompletionCache(self.src, translation_unit)
            completions = self.completion_cache.get_completions(
                line + 1,
                token_start_column + 1,
                prefix,
                contents,
                limit=COMPLETIONS_LIMIT)
        else:
            completions = []
        response['completions'] = completions
        response['line'] = line
        response['column'] = column
        response['prefix'] = prefix
コード例 #3
0
    def get_completions(self, request):
        contents = request['contents']
        line = request['line']
        column = request['column']
        prefix = request['prefix']
        token_start_column = request['tokenStartColumn']

        # NOTE: there is no need to update the translation unit here.
        # libclang's completions API seamlessly takes care of unsaved content
        # without any special handling.
        translation_unit = self._get_translation_unit(None)
        if translation_unit:
            if self.completion_cache is None:
                self.completion_cache = CompletionCache(
                    self.src, translation_unit, self.custom_clang_lib)
            return self.completion_cache.get_completions(
                line + 1,
                token_start_column + 1,
                prefix,
                contents,
                limit=COMPLETIONS_LIMIT)
        return []
コード例 #4
0
ファイル: clang_server.py プロジェクト: DannyiCracked/nuclide
    def get_completions(self, request):
        contents = request['contents']
        line = request['line']
        column = request['column']
        prefix = request['prefix']
        token_start_column = request['tokenStartColumn']

        # NOTE: there is no need to update the translation unit here.
        # libclang's completions API seamlessly takes care of unsaved content
        # without any special handling.
        translation_unit = self._get_translation_unit(None)
        if translation_unit:
            if self.completion_cache is None:
                self.completion_cache = CompletionCache(
                    self.src, translation_unit, self.custom_clang_lib)
            return self.completion_cache.get_completions(
                line + 1,
                token_start_column + 1,
                prefix,
                contents,
                limit=COMPLETIONS_LIMIT)
        return []
コード例 #5
0
class Server:
    # Extra functions from the libclang API.
    # TOOD(hansonw): Remove this when these bindings are upstreamed.
    CUSTOM_CLANG_FUNCTIONS = [
        ("clang_getChildDiagnostics", [Diagnostic], POINTER(c_void_p)),
        ("clang_getNumDiagnosticsInSet", [POINTER(c_void_p)], c_uint),
        ("clang_getDiagnosticInSet", [POINTER(c_void_p),
                                      c_uint], POINTER(c_void_p)),
        ("clang_getClangVersion", [], POINTER(c_void_p)),
        ("clang_getCString", [c_void_p], c_char_p),
        ("clang_sortCodeCompletionResults", [c_void_p, c_uint], None),

        # Much faster than actually fetching/checking the file string.
        ("clang_Location_isFromMainFile", [SourceLocation], c_int),
    ]

    # New in Clang 3.8: not in the Python bindings yet.
    # Should also be removed once upstreamed.
    PARSE_CREATE_PREAMBLE_ON_FIRST_PARSE = 0x100

    # Prefix of the string returned by clang_getClangVersion.
    CLANG_VERSION_PREFIX = 'clang version'

    def __init__(self, src, flags, input_stream, output_stream):
        self.src = src
        self.flags = flags
        self.input_stream = input_stream
        self.output_stream = output_stream
        self.index = Index.create()
        self.translation_unit = None
        self.completion_cache = None
        self.cached_contents = None
        conf = Config()
        self.custom_clang_lib = conf.lib
        self._register_custom_clang_functions()

        # Cache the libclang version.
        cxstr = self.custom_clang_lib.clang_getClangVersion()
        version = self.custom_clang_lib.clang_getCString(cxstr)

        if version.startswith(Server.CLANG_VERSION_PREFIX):
            version = version[len(Server.CLANG_VERSION_PREFIX):]
        else:
            version = '3.7.0'
        self.clang_version = LooseVersion(version)

    def run(self):
        input_stream = self.input_stream
        output_stream = self.output_stream
        while True:
            line = input_stream.readline()
            response = self.process_request(line)
            json.dump(response, output_stream)
            # Use \n to signal the end of the response.
            output_stream.write('\n')
            output_stream.flush()

    def process_request(self, line):
        '''
        request should be a dict containing id and args.
        The response will be a dict containing id and exactly one of result/error.
        '''
        request = json.loads(line)

        reqid = request['id']
        method = request['method']
        args = request['args']

        start_time = time.time()
        result = None
        error = None
        try:
            if method == 'compile':
                result = self.compile(args)
            elif method == 'get_completions':
                result = self.get_completions(args)
            elif method == 'get_declaration':
                result = self.get_declaration(args)
            elif method == 'get_declaration_info':
                result = self.get_declaration_info(args)
            elif method == 'get_outline':
                result = self.get_outline(args)
            else:
                error = 'Unknown method to clang_server.py: %s.' % method
        except:
            error = traceback.format_exc()

        root_logger.info('Finished %s request in %.2lf seconds.', method,
                         time.time() - start_time)

        response = {
            'id': reqid,
        }
        if error is not None:
            response['type'] = 'error-response'
            response['error'] = error
        else:
            response['type'] = 'response'
            response['result'] = result
        return response

    def compile(self, request):
        contents = request['contents']

        # Update the translation unit with the latest contents.
        # Force a re-parse, in case the user e.g. changed a header file.
        self.cached_contents = None
        translation_unit = self._update_translation_unit(contents)
        if not translation_unit:
            return {'diagnostics': []}

        # Return the diagnostics.
        diagnostics = []
        for diag in translation_unit.diagnostics:
            if diag.spelling == PRAGMA_ONCE_IN_MAIN_FILE and is_header_file(
                    self.src):
                continue
            diagnostics.append(self.diagnostic_dict(diag))
        return {'diagnostics': diagnostics}

    def diagnostic_dict(self, diag):
        ranges = map(range_dict, diag.ranges)
        if len(ranges) == 0:
            ranges = None
        fixits = []
        for fixit in diag.fixits:
            fixits.append({
                'range': range_dict(fixit.range),
                'value': fixit.value,
            })
        children = []
        for child in child_diagnostics(self.custom_clang_lib, diag):
            children.append({
                'spelling': child.spelling,
                'location': location_dict(child.location),
                'ranges': map(range_dict, child.ranges),
            })
            # Some fixits may be nested; add them to the root diagnostic.
            for fixit in child.fixits:
                fixits.append({
                    'range': range_dict(fixit.range),
                    'value': fixit.value,
                })
        return {
            'spelling': diag.spelling,
            'severity': diag.severity,
            'location': location_dict(diag.location),
            'ranges': ranges,
            'fixits': fixits,
            'children': children,
        }

    def get_completions(self, request):
        contents = request['contents']
        line = request['line']
        column = request['column']
        prefix = request['prefix']
        token_start_column = request['tokenStartColumn']

        # NOTE: there is no need to update the translation unit here.
        # libclang's completions API seamlessly takes care of unsaved content
        # without any special handling.
        translation_unit = self._get_translation_unit(None)
        if translation_unit:
            if self.completion_cache is None:
                self.completion_cache = CompletionCache(
                    self.src, translation_unit, self.custom_clang_lib)
            return self.completion_cache.get_completions(
                line + 1,
                token_start_column + 1,
                prefix,
                contents,
                limit=COMPLETIONS_LIMIT)
        return []

    def get_declaration(self, request):
        contents = request['contents']
        line = request['line']
        column = request['column']

        # Update the translation unit with the latest contents.
        translation_unit = self._update_translation_unit(contents)
        if not translation_unit:
            return None

        return get_declaration_location_and_spelling(translation_unit,
                                                     contents, self.flags,
                                                     self.src, line + 1,
                                                     column + 1)

    def get_declaration_info(self, request):
        contents = request['contents']
        line = request['line']
        column = request['column']

        # Update the translation unit with the latest contents.
        translation_unit = self._update_translation_unit(contents)
        if not translation_unit:
            return None

        location = translation_unit.get_location(self.src,
                                                 (line + 1, column + 1))
        cursor = Cursor.from_location(translation_unit, location)
        cursor = cursor.referenced
        if cursor is None:
            return None

        return self.get_declaration_info_for_cursor(cursor)

    def get_declaration_info_for_cursor(self, cursor):
        '''Returns string id in clang-callgraph-service format for entity under the
        cursor. Currently works only for definitions of class methods, instance
        methods and functions. Returns None for everything else.
        '''
        result = []
        while cursor is not None and not cursor.kind.is_translation_unit():
            file = cursor.location.file
            result.append({
                'name': self.get_name_for_cursor(cursor),
                'type': cursor.kind.name,
                'cursor_usr': cursor.get_usr(),
                'file': resolve_file(file),
            })
            cursor = cursor.semantic_parent

        return result

    def get_name_for_cursor(self, cursor):
        name = cursor.displayname
        # clang doesn't include the interface name for categories; add it
        # manually
        if (cursor.kind == CursorKind.OBJC_CATEGORY_DECL
                or cursor.kind == CursorKind.OBJC_CATEGORY_IMPL_DECL):
            # Find reference to base class.
            base_name = ''
            for child in cursor.get_children():
                if child.kind == CursorKind.OBJC_CLASS_REF:
                    base_name = child.displayname
                    break
            return base_name + ' (' + name + ')'
        return name

    def get_outline(self, request):
        contents = request['contents']
        translation_unit = self._update_translation_unit(contents)
        if not translation_unit:
            return None
        return outline.get_outline(self.custom_clang_lib, translation_unit,
                                   contents)

    def _get_translation_unit(self, unsaved_contents):
        '''
        Get the current translation unit, or create it if it does not exist.
        Flags can be optional if the translation unit already exists.
        '''
        if self.translation_unit is not None:
            return self.translation_unit

        # Configure the options.
        # See also clang_defaultEditingTranslationUnitOptions in Index.h.
        options = (
            TranslationUnit.PARSE_PRECOMPILED_PREAMBLE
            | TranslationUnit.PARSE_CACHE_COMPLETION_RESULTS
            | TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION
            | TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
            | TranslationUnit.PARSE_INCOMPLETE)

        # Clang 3.8 comes with CXTranslationUnit_CreatePreambleOnFirstParse,
        # which allows us to skip the forced reparse.
        # Otherwise, we have have to force an immediate reparse to generate
        # precompiled headers (necessary for fast autocompletion).
        if self.clang_version >= LooseVersion('3.8'):
            options |= Server.PARSE_CREATE_PREAMBLE_ON_FIRST_PARSE
            self.cached_contents = unsaved_contents

        args = self._get_args_for_flags()
        self.translation_unit = self.index.parse(
            self.src, args, self._make_files(unsaved_contents), options)
        return self.translation_unit

    # Clang's API expects a list of (src, contents) pairs.
    def _make_files(self, unsaved_contents):
        if unsaved_contents is None:
            return []
        return [(self.src, unsaved_contents.encode('utf-8'))]

    def _get_args_for_flags(self):
        # Enable typo-detection (and the corresponding fixits)
        # For some reason this is not enabled by default in libclang.
        args = ['-fspell-checking']
        for arg in self.flags:
            if arg == self.src:
                # Including the input file as an argument causes index.parse() to fail.
                # Surprisingly, including the path to the clang binary
                # as the first argument does not cause any issues.
                pass
            elif arg == '-c':
                # No need to generate a .o file.
                args.append('-fsyntax-only')
            elif arg == '-Werror':
                # We disable this so that the severity can be better reflected in the UI.
                # For example, this allows unused code to appear as a warning
                # instead of an error.
                pass
            elif arg == '-MMD' or arg == '-MD':
                # Do not write out dependency files.
                pass
            else:
                args.append(arg)
        return args

    def _update_translation_unit(self, unsaved_contents=None):
        translation_unit = self._get_translation_unit(unsaved_contents)
        if translation_unit is None:
            return None
        # Reparsing isn't cheap, so skip it if nothing changed.
        if (unsaved_contents is not None
                and unsaved_contents == self.cached_contents):
            return translation_unit
        options = 0  # There are no reparse options available in libclang yet.
        translation_unit.reparse(self._make_files(unsaved_contents), options)
        self.cached_contents = unsaved_contents
        if self.completion_cache is not None:
            self.completion_cache.invalidate()
        return translation_unit

    def _register_custom_clang_functions(self):
        # Extend the Clang C bindings with the additional required functions.
        for item in Server.CUSTOM_CLANG_FUNCTIONS:
            func = getattr(self.custom_clang_lib, item[0])
            func.argtypes = item[1]
            func.restype = item[2]
コード例 #6
0
class Server:
    def __init__(self, src, input_stream, output_stream):
        self.src = src
        self.input_stream = input_stream
        self.output_stream = output_stream
        self.index = Index.create()
        self.src_to_translation_unit = {}
        self.completion_cache = None

    def run(self):
        input_stream = self.input_stream
        output_stream = self.output_stream
        while True:
            line = input_stream.readline()
            response = self.process_request(line)
            json.dump(response, output_stream)
            # Use \n to signal the end of the response.
            output_stream.write('\n')
            output_stream.flush()

    def process_request(self, line):
        '''Note that line will likely including a trailing newline.

        Returns a dict or list that can be serialized by json.dump().
        '''
        request = json.loads(line)

        # Every request should have an id that must also be present in the
        # response.
        reqid = request['reqid']
        response = {'reqid': reqid}

        start_time = time.time()
        try:
            method = request['method']
            if method == 'compile':
                self.compile(request, response)
            elif method == 'get_completions':
                self.get_completions(request, response)
            elif method == 'get_declaration':
                self.get_declaration(request, response)
            elif method == 'get_declaration_info':
                self.get_declaration_info(request, response)
            else:
                response[
                    'error'] = 'Unknown method to clang_server.py: %s.' % method
        except:
            response['error'] = traceback.format_exc()

        root_logger.info('Finished %s request in %.2lf seconds.', method,
                         time.time() - start_time)

        # response must have a key named "error" if there was a failure of any
        # kind.
        return response

    def _get_translation_unit(self, src, flags=None):
        '''flags can be optional if the translation unit is in the cache.'''
        translation_unit = self.src_to_translation_unit.get(src)
        if translation_unit is None:
            translation_unit = self._create_translation_unit(src, flags)
            self.src_to_translation_unit[src] = translation_unit
        return translation_unit

    def _create_translation_unit(self, src, flags):
        if flags is None:
            return None

        unsaved_files = None
        # Configure the options.
        # See also clang_defaultEditingTranslationUnitOptions in Index.h.
        options = (
            TranslationUnit.PARSE_PRECOMPILED_PREAMBLE
            | TranslationUnit.PARSE_CACHE_COMPLETION_RESULTS
            | TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION
            | TranslationUnit.PARSE_INCOMPLETE)

        args = self._get_args_for_flags(src, flags)
        translation_unit = self.index.parse(src, args, unsaved_files, options)
        return translation_unit

    def _get_args_for_flags(self, src, flags):
        args = []
        for arg in flags:
            if arg == src:
                # Including the input file as an argument causes index.parse() to fail.
                # Surprisingly, including '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang'
                # as the first argument does not cause any issues.
                pass
            elif arg == '-c':
                # No need to generate a .o file.
                args.append('-fsyntax-only')
            elif arg == '-Werror':
                # We disable this so that the severity can be better reflected in the UI.
                # For example, this allows unused code to appear as a warning
                # instead of an error.
                pass
            elif arg == '-MMD' or arg == '-MD':
                # Do not write out dependency files.
                pass
            else:
                args.append(arg)
        return args

    def compile(self, request, response):
        src = request['src']
        contents = request['contents']
        flags = request['flags']

        # Update the translation unit with the latest contents.
        translation_unit = self._tryUpdateTranslationUnit(src, contents, flags)
        if not translation_unit:
            sys.stderr.write(
                'Suspicious: requesting compilation of %s without flags' % src)
            response['diagnostics'] = []
            return

        # Return the diagnostics.
        diagnostics = []
        for diag in translation_unit.diagnostics:
            ranges = []
            # Clang indexes for line and column are 1-based.
            for source_range in diag.ranges:
                ranges.append({
                    'start': {
                        'line': source_range.start.line - 1,
                        'column': source_range.start.column - 1,
                    },
                    'end': {
                        'line': source_range.end.line - 1,
                        'column': source_range.end.column - 1,
                    }
                })
            if len(ranges) == 0:
                ranges = None
            diagnostics.append({
                'spelling': diag.spelling,
                'severity': diag.severity,
                'location': {
                    'file': str(diag.location.file),
                    'line': diag.location.line - 1,
                    'column': diag.location.column - 1,
                },
                'ranges': ranges,
            })
        response['diagnostics'] = diagnostics

    def get_completions(self, request, response):
        src = request['src']
        contents = request['contents']
        line = request['line']
        column = request['column']
        prefix = request['prefix']
        token_start_column = request['tokenStartColumn']
        flags = request['flags']

        # NOTE: there is no need to update the translation unit here.
        # libclang's completions API seamlessly takes care of unsaved content
        # without any special handling.
        translation_unit = self._get_translation_unit(src, flags)
        if translation_unit:
            if self.completion_cache is None:
                self.completion_cache = CompletionCache(
                    self.src, translation_unit)
            completions = self.completion_cache.get_completions(
                line + 1,
                token_start_column + 1,
                prefix,
                contents,
                limit=COMPLETIONS_LIMIT)
        else:
            completions = []
        response['file'] = src
        response['completions'] = completions
        response['line'] = line
        response['column'] = column
        response['prefix'] = prefix

    def get_declaration(self, request, response):
        src = request['src']
        contents = request['contents']
        line = request['line']
        column = request['column']
        flags = request['flags']

        response['src'] = src
        response['line'] = line
        response['column'] = column

        # Update the translation unit with the latest contents.
        translation_unit = self._tryUpdateTranslationUnit(src, contents, flags)
        if not translation_unit:
            return

        location_and_spelling = get_declaration_location_and_spelling(
            translation_unit, src, line + 1, column + 1)
        # Clang returns 1-indexed values, but we want to return 0-indexed.
        if location_and_spelling:
            location_and_spelling['line'] -= 1
            location_and_spelling['column'] -= 1
            location_and_spelling['extent']['start']['line'] -= 1
            location_and_spelling['extent']['start']['column'] -= 1
            location_and_spelling['extent']['end']['line'] -= 1
            location_and_spelling['extent']['end']['column'] -= 1
        response['locationAndSpelling'] = location_and_spelling

    def get_declaration_info(self, request, response):
        src = request['src']
        contents = request['contents']
        line = request['line']
        column = request['column']
        flags = request['flags']

        response['src'] = src
        response['line'] = line
        response['column'] = column

        # Update the translation unit with the latest contents.
        translation_unit = self._tryUpdateTranslationUnit(src, contents, flags)
        if not translation_unit:
            return

        location = translation_unit.get_location(src, (line + 1, column + 1))
        cursor = Cursor.from_location(translation_unit, location)
        cursor = cursor.referenced
        if cursor is None:
            return

        response['info'] = self.get_declaration_info_for_cursor(cursor)

    def get_declaration_info_for_cursor(self, cursor):
        '''Returns string id in clang-callgraph-service format for entity under the
        cursor. Currently works only for definitions of class methods, instance
        methods and functions. Returns None for everything else.
        '''
        result = []
        while cursor is not None and not cursor.kind.is_translation_unit():
            file = cursor.location.file
            result.append({
                'name': self.get_name_for_cursor(cursor),
                'type': cursor.kind.name,
                'cursor_usr': cursor.get_usr(),
                'file': file.name if file is not None else None,
            })
            cursor = cursor.semantic_parent

        return result

    def get_name_for_cursor(self, cursor):
        name = cursor.displayname
        # clang doesn't include the interface name for categories; add it
        # manually
        if (cursor.kind == CursorKind.OBJC_CATEGORY_DECL
                or cursor.kind == CursorKind.OBJC_CATEGORY_IMPL_DECL):
            # Find reference to base class.
            base_name = ''
            for child in cursor.get_children():
                if child.kind == CursorKind.OBJC_CLASS_REF:
                    base_name = child.displayname
                    break
            return base_name + ' (' + name + ')'
        return name

    def _update(self, translation_unit, src, unsaved_contents=None):
        # If unsaved_contents is unspecified, then assume that the file can be read
        # directly from disk as-is.
        if unsaved_contents:
            contents_as_str = unsaved_contents.encode('utf8')
            unsaved_files = [(src, contents_as_str)]
        else:
            unsaved_files = []
        options = 0  # There are no reparse options available in libclang yet.
        translation_unit.reparse(unsaved_files, options)
        if self.completion_cache is not None:
            self.completion_cache.invalidate()

    def _tryUpdateTranslationUnit(self,
                                  src,
                                  unsaved_contents=None,
                                  flags=None):
        '''Returns None if the flags for the src cannot be found.'''
        translation_unit = self._get_translation_unit(src, flags)
        if translation_unit is None:
            return None
        self._update(translation_unit, src, unsaved_contents)
        return translation_unit
コード例 #7
0
ファイル: clang_server.py プロジェクト: BruceZu/nuclide
class Server:
    # Extra functions from the libclang API.
    # TOOD(hansonw): Remove this when these bindings are upstreamed.
    CUSTOM_CLANG_FUNCTIONS = [
        ("clang_getChildDiagnostics",
         [Diagnostic],
            POINTER(c_void_p)),

        ("clang_getNumDiagnosticsInSet",
            [POINTER(c_void_p)],
            c_uint),

        ("clang_getDiagnosticInSet",
            [POINTER(c_void_p), c_uint],
            POINTER(c_void_p)),

        ("clang_getClangVersion",
            [],
            POINTER(c_void_p)),

        ("clang_getCString",
            [c_void_p],
            c_char_p),
    ]

    # New in Clang 3.8: not in the Python bindings yet.
    # Should also be removed once upstreamed.
    PARSE_CREATE_PREAMBLE_ON_FIRST_PARSE = 0x100

    # Prefix of the string returned by clang_getClangVersion.
    CLANG_VERSION_PREFIX = 'clang version'

    def __init__(self, src, input_stream, output_stream):
        self.src = src
        self.input_stream = input_stream
        self.output_stream = output_stream
        self.index = Index.create()
        self.translation_unit = None
        self.completion_cache = None
        self.cached_contents = None
        conf = Config()
        self.custom_clang_lib = conf.lib
        self._register_custom_clang_functions()

        # Cache the libclang version.
        cxstr = self.custom_clang_lib.clang_getClangVersion()
        version = self.custom_clang_lib.clang_getCString(cxstr)

        if version.startswith(Server.CLANG_VERSION_PREFIX):
            version = version[len(Server.CLANG_VERSION_PREFIX):]
        else:
            version = '3.7.0'
        self.clang_version = LooseVersion(version)

    def run(self):
        input_stream = self.input_stream
        output_stream = self.output_stream
        while True:
            line = input_stream.readline()
            response = self.process_request(line)
            json.dump(response, output_stream)
            # Use \n to signal the end of the response.
            output_stream.write('\n')
            output_stream.flush()

    def process_request(self, line):
        '''Note that line will likely including a trailing newline.

        Returns a dict or list that can be serialized by json.dump().
        '''
        request = json.loads(line)

        # Every request should have an id that must also be present in the
        # response.
        reqid = request['reqid']
        response = {'reqid': reqid}

        start_time = time.time()
        try:
            method = request['method']
            if method == 'compile':
                self.compile(request, response)
            elif method == 'get_completions':
                self.get_completions(request, response)
            elif method == 'get_declaration':
                self.get_declaration(request, response)
            elif method == 'get_declaration_info':
                self.get_declaration_info(request, response)
            else:
                response[
                    'error'] = 'Unknown method to clang_server.py: %s.' % method
        except:
            response['error'] = traceback.format_exc()

        root_logger.info('Finished %s request in %.2lf seconds.',
                         method, time.time() - start_time)

        # response must have a key named "error" if there was a failure of any
        # kind.
        return response

    def compile(self, request, response):
        contents = request['contents']
        flags = request['flags']

        # Update the translation unit with the latest contents.
        # Force a re-parse, in case the user e.g. changed a header file.
        self.cached_contents = None
        translation_unit = self._update_translation_unit(contents, flags)
        if not translation_unit:
            sys.stderr.write(
                'Suspicious: requesting compilation of %s without flags' % self.src)
            response['diagnostics'] = []
            return

        # Return the diagnostics.
        diagnostics = []
        for diag in translation_unit.diagnostics:
            if diag.spelling == PRAGMA_ONCE_IN_MAIN_FILE and is_header_file(self.src):
                continue
            diagnostics.append(self.diagnostic_dict(diag))
        response['diagnostics'] = diagnostics

    def diagnostic_dict(self, diag):
        ranges = map(range_dict, diag.ranges)
        if len(ranges) == 0:
            ranges = None
        fixits = []
        for fixit in diag.fixits:
            fixits.append({
                'range': range_dict(fixit.range),
                'value': fixit.value,
            })
        children = []
        for child in child_diagnostics(self.custom_clang_lib, diag):
            children.append({
                'spelling': child.spelling,
                'location': location_dict(child.location),
                'ranges': map(range_dict, child.ranges),
            })
            # Some fixits may be nested; add them to the root diagnostic.
            for fixit in child.fixits:
                fixits.append({
                    'range': range_dict(fixit.range),
                    'value': fixit.value,
                })
        return {
            'spelling': diag.spelling,
            'severity': diag.severity,
            'location': location_dict(diag.location),
            'ranges': ranges,
            'fixits': fixits,
            'children': children,
        }

    def get_completions(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        prefix = request['prefix']
        token_start_column = request['tokenStartColumn']
        flags = request['flags']

        # NOTE: there is no need to update the translation unit here.
        # libclang's completions API seamlessly takes care of unsaved content
        # without any special handling.
        translation_unit = self._get_translation_unit(None, flags)
        if translation_unit:
            if self.completion_cache is None:
                self.completion_cache = CompletionCache(self.src, translation_unit)
            completions = self.completion_cache.get_completions(
                line + 1,
                token_start_column + 1,
                prefix,
                contents,
                limit=COMPLETIONS_LIMIT)
        else:
            completions = []
        response['completions'] = completions
        response['line'] = line
        response['column'] = column
        response['prefix'] = prefix

    def get_declaration(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        flags = request['flags']

        response['line'] = line
        response['column'] = column

        # Update the translation unit with the latest contents.
        translation_unit = self._update_translation_unit(contents, flags)
        if not translation_unit:
            return

        response['locationAndSpelling'] = get_declaration_location_and_spelling(
            translation_unit, self.src, line + 1, column + 1)

    def get_declaration_info(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        flags = request['flags']

        response['line'] = line
        response['column'] = column

        # Update the translation unit with the latest contents.
        translation_unit = self._update_translation_unit(contents, flags)
        if not translation_unit:
            return

        location = translation_unit.get_location(src, (line + 1, column + 1))
        cursor = Cursor.from_location(translation_unit, location)
        cursor = cursor.referenced
        if cursor is None:
            return

        response['info'] = self.get_declaration_info_for_cursor(cursor)

    def get_declaration_info_for_cursor(self, cursor):
        '''Returns string id in clang-callgraph-service format for entity under the
        cursor. Currently works only for definitions of class methods, instance
        methods and functions. Returns None for everything else.
        '''
        result = []
        while cursor is not None and not cursor.kind.is_translation_unit():
            file = cursor.location.file
            result.append({
                'name': self.get_name_for_cursor(cursor),
                'type': cursor.kind.name,
                'cursor_usr': cursor.get_usr(),
                'file': resolve_file(file),
            })
            cursor = cursor.semantic_parent

        return result

    def get_name_for_cursor(self, cursor):
        name = cursor.displayname
        # clang doesn't include the interface name for categories; add it
        # manually
        if (cursor.kind == CursorKind.OBJC_CATEGORY_DECL or
                cursor.kind == CursorKind.OBJC_CATEGORY_IMPL_DECL):
            # Find reference to base class.
            base_name = ''
            for child in cursor.get_children():
                if child.kind == CursorKind.OBJC_CLASS_REF:
                    base_name = child.displayname
                    break
            return base_name + ' (' + name + ')'
        return name

    def _get_translation_unit(self, unsaved_contents, flags=None):
        '''
        Get the current translation unit, or create it if it does not exist.
        Flags can be optional if the translation unit already exists.
        '''
        if self.translation_unit is not None:
            return self.translation_unit

        if flags is None:
            return None

        # Configure the options.
        # See also clang_defaultEditingTranslationUnitOptions in Index.h.
        options = (
            TranslationUnit.PARSE_PRECOMPILED_PREAMBLE |
            TranslationUnit.PARSE_CACHE_COMPLETION_RESULTS |
            TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION |
            TranslationUnit.PARSE_INCOMPLETE)

        # Clang 3.8 comes with CXTranslationUnit_CreatePreambleOnFirstParse,
        # which allows us to skip the forced reparse.
        # Otherwise, we have have to force an immediate reparse to generate
        # precompiled headers (necessary for fast autocompletion).
        if self.clang_version >= LooseVersion('3.8'):
            options |= Server.PARSE_CREATE_PREAMBLE_ON_FIRST_PARSE
            self.cached_contents = unsaved_contents

        args = self._get_args_for_flags(flags)
        self.translation_unit = self.index.parse(
            self.src, args, self._make_files(unsaved_contents), options)
        return self.translation_unit

    # Clang's API expects a list of (src, contents) pairs.
    def _make_files(self, unsaved_contents):
        if unsaved_contents is None:
            return []
        return [(self.src, unsaved_contents.encode('utf-8'))]

    def _get_args_for_flags(self, flags):
        # Enable typo-detection (and the corresponding fixits)
        # For some reason this is not enabled by default in libclang.
        args = ['-fspell-checking']
        for arg in flags:
            if arg == self.src:
                # Including the input file as an argument causes index.parse() to fail.
                # Surprisingly, including '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang'
                # as the first argument does not cause any issues.
                pass
            elif arg == '-c':
                # No need to generate a .o file.
                args.append('-fsyntax-only')
            elif arg == '-Werror':
                # We disable this so that the severity can be better reflected in the UI.
                # For example, this allows unused code to appear as a warning
                # instead of an error.
                pass
            elif arg == '-MMD' or arg == '-MD':
                # Do not write out dependency files.
                pass
            else:
                args.append(arg)
        return args

    def _update_translation_unit(self, unsaved_contents=None, flags=None):
        translation_unit = self._get_translation_unit(unsaved_contents, flags)
        if translation_unit is None:
            return None
        # Reparsing isn't cheap, so skip it if nothing changed.
        if (unsaved_contents is not None and
                unsaved_contents == self.cached_contents):
            return translation_unit
        options = 0  # There are no reparse options available in libclang yet.
        translation_unit.reparse(self._make_files(unsaved_contents), options)
        self.cached_contents = unsaved_contents
        if self.completion_cache is not None:
            self.completion_cache.invalidate()
        return translation_unit

    def _register_custom_clang_functions(self):
        # Extend the Clang C bindings with the additional required functions.
        for item in Server.CUSTOM_CLANG_FUNCTIONS:
            func = getattr(self.custom_clang_lib, item[0])
            func.argtypes = item[1]
            func.restype = item[2]
コード例 #8
0
ファイル: clang_server.py プロジェクト: kevinrodbe/nuclide
class Server:

    def __init__(self, src, input_stream, output_stream):
        self.src = src
        self.input_stream = input_stream
        self.output_stream = output_stream
        self.index = Index.create()
        self.translation_unit = None
        self.completion_cache = None
        self.cached_contents = None

    def run(self):
        input_stream = self.input_stream
        output_stream = self.output_stream
        while True:
            line = input_stream.readline()
            response = self.process_request(line)
            json.dump(response, output_stream)
            # Use \n to signal the end of the response.
            output_stream.write('\n')
            output_stream.flush()

    def process_request(self, line):
        '''Note that line will likely including a trailing newline.

        Returns a dict or list that can be serialized by json.dump().
        '''
        request = json.loads(line)

        # Every request should have an id that must also be present in the
        # response.
        reqid = request['reqid']
        response = {'reqid': reqid}

        start_time = time.time()
        try:
            method = request['method']
            if method == 'compile':
                self.compile(request, response)
            elif method == 'get_completions':
                self.get_completions(request, response)
            elif method == 'get_declaration':
                self.get_declaration(request, response)
            elif method == 'get_declaration_info':
                self.get_declaration_info(request, response)
            else:
                response[
                    'error'] = 'Unknown method to clang_server.py: %s.' % method
        except:
            response['error'] = traceback.format_exc()

        root_logger.info('Finished %s request in %.2lf seconds.',
                         method, time.time() - start_time)

        # response must have a key named "error" if there was a failure of any
        # kind.
        return response

    def compile(self, request, response):
        contents = request['contents']
        flags = request['flags']

        # Update the translation unit with the latest contents.
        translation_unit = self._update_translation_unit(contents, flags)
        if not translation_unit:
            sys.stderr.write(
                'Suspicious: requesting compilation of %s without flags' % self.src)
            response['diagnostics'] = []
            return

        # Return the diagnostics.
        diagnostics = []
        for diag in translation_unit.diagnostics:
            if diag.spelling == PRAGMA_ONCE_IN_MAIN_FILE and is_header_file(self.src):
                continue
            ranges = []
            # Clang indexes for line and column are 1-based.
            for source_range in diag.ranges:
                ranges.append({
                    'start': {
                        'line': source_range.start.line - 1,
                        'column': source_range.start.column - 1,
                    },
                    'end': {
                        'line': source_range.end.line - 1,
                        'column': source_range.end.column - 1,
                    }
                })
            if len(ranges) == 0:
                ranges = None
            diagnostics.append({
                'spelling': diag.spelling,
                'severity': diag.severity,
                'location': {
                    'file': str(diag.location.file),
                    'line': diag.location.line - 1,
                    'column': diag.location.column - 1,
                },
                'ranges': ranges,
            })
        response['diagnostics'] = diagnostics

    def get_completions(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        prefix = request['prefix']
        token_start_column = request['tokenStartColumn']
        flags = request['flags']

        # NOTE: there is no need to update the translation unit here.
        # libclang's completions API seamlessly takes care of unsaved content
        # without any special handling.
        translation_unit = self._get_translation_unit(None, flags)
        if translation_unit:
            if self.completion_cache is None:
                self.completion_cache = CompletionCache(self.src, translation_unit)
            completions = self.completion_cache.get_completions(
                line + 1,
                token_start_column + 1,
                prefix,
                contents,
                limit=COMPLETIONS_LIMIT)
        else:
            completions = []
        response['completions'] = completions
        response['line'] = line
        response['column'] = column
        response['prefix'] = prefix

    def get_declaration(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        flags = request['flags']

        response['line'] = line
        response['column'] = column

        # Update the translation unit with the latest contents.
        translation_unit = self._update_translation_unit(contents, flags)
        if not translation_unit:
            return

        location_and_spelling = get_declaration_location_and_spelling(
            translation_unit, self.src, line + 1, column + 1)
        # Clang returns 1-indexed values, but we want to return 0-indexed.
        if location_and_spelling:
            location_and_spelling['line'] -= 1
            location_and_spelling['column'] -= 1
            location_and_spelling['extent']['start']['line'] -= 1
            location_and_spelling['extent']['start']['column'] -= 1
            location_and_spelling['extent']['end']['line'] -= 1
            location_and_spelling['extent']['end']['column'] -= 1
        response['locationAndSpelling'] = location_and_spelling

    def get_declaration_info(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        flags = request['flags']

        response['line'] = line
        response['column'] = column

        # Update the translation unit with the latest contents.
        translation_unit = self._update_translation_unit(contents, flags)
        if not translation_unit:
            return

        location = translation_unit.get_location(src, (line + 1, column + 1))
        cursor = Cursor.from_location(translation_unit, location)
        cursor = cursor.referenced
        if cursor is None:
            return

        response['info'] = self.get_declaration_info_for_cursor(cursor)

    def get_declaration_info_for_cursor(self, cursor):
        '''Returns string id in clang-callgraph-service format for entity under the
        cursor. Currently works only for definitions of class methods, instance
        methods and functions. Returns None for everything else.
        '''
        result = []
        while cursor is not None and not cursor.kind.is_translation_unit():
            file = cursor.location.file
            result.append({
                'name': self.get_name_for_cursor(cursor),
                'type': cursor.kind.name,
                'cursor_usr': cursor.get_usr(),
                'file': file.name if file is not None else None,
            })
            cursor = cursor.semantic_parent

        return result

    def get_name_for_cursor(self, cursor):
        name = cursor.displayname
        # clang doesn't include the interface name for categories; add it
        # manually
        if (cursor.kind == CursorKind.OBJC_CATEGORY_DECL or
                cursor.kind == CursorKind.OBJC_CATEGORY_IMPL_DECL):
            # Find reference to base class.
            base_name = ''
            for child in cursor.get_children():
                if child.kind == CursorKind.OBJC_CLASS_REF:
                    base_name = child.displayname
                    break
            return base_name + ' (' + name + ')'
        return name

    def _get_translation_unit(self, unsaved_contents, flags=None):
        '''
        Get the current translation unit, or create it if it does not exist.
        Flags can be optional if the translation unit already exists.
        '''
        if self.translation_unit is not None:
            return self.translation_unit

        if flags is None:
            return None

        # Configure the options.
        # See also clang_defaultEditingTranslationUnitOptions in Index.h.
        options = (
            TranslationUnit.PARSE_PRECOMPILED_PREAMBLE |
            TranslationUnit.PARSE_CACHE_COMPLETION_RESULTS |
            TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION |
            TranslationUnit.PARSE_INCOMPLETE)

        args = self._get_args_for_flags(flags)
        self.translation_unit = self.index.parse(
            self.src, args, self._make_files(unsaved_contents), options)
        self.cached_contents = unsaved_contents
        return self.translation_unit

    # Clang's API expects a list of (src, contents) pairs.
    def _make_files(self, unsaved_contents):
        if unsaved_contents is None:
            return []
        return [(self.src, unsaved_contents.encode('utf-8'))]

    def _get_args_for_flags(self, flags):
        args = []
        for arg in flags:
            if arg == self.src:
                # Including the input file as an argument causes index.parse() to fail.
                # Surprisingly, including '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang'
                # as the first argument does not cause any issues.
                pass
            elif arg == '-c':
                # No need to generate a .o file.
                args.append('-fsyntax-only')
            elif arg == '-Werror':
                # We disable this so that the severity can be better reflected in the UI.
                # For example, this allows unused code to appear as a warning
                # instead of an error.
                pass
            elif arg == '-MMD' or arg == '-MD':
                # Do not write out dependency files.
                pass
            else:
                args.append(arg)
        return args

    def _update_translation_unit(self, unsaved_contents=None, flags=None):
        translation_unit = self._get_translation_unit(unsaved_contents, flags)
        if translation_unit is None:
            return None
        # Reparsing isn't cheap, so skip it if nothing changed.
        if (unsaved_contents is not None and
            unsaved_contents == self.cached_contents):
            return translation_unit
        options = 0  # There are no reparse options available in libclang yet.
        translation_unit.reparse(self._make_files(unsaved_contents), options)
        self.cached_contents = unsaved_contents
        if self.completion_cache is not None:
            self.completion_cache.invalidate()
        return translation_unit
コード例 #9
0
ファイル: clang_server.py プロジェクト: sshyran/nuclide
class Server:
    # Extra functions from the libclang API.
    # TOOD(hansonw): Remove this when these bindings are upstreamed.
    CUSTOM_CLANG_FUNCTIONS = [
        ("clang_getChildDiagnostics", [Diagnostic], POINTER(c_void_p)),
        ("clang_getNumDiagnosticsInSet", [POINTER(c_void_p)], c_uint),
        ("clang_getDiagnosticInSet", [POINTER(c_void_p),
                                      c_uint], POINTER(c_void_p)),
    ]

    def __init__(self, src, input_stream, output_stream):
        self.src = src
        self.input_stream = input_stream
        self.output_stream = output_stream
        self.index = Index.create()
        self.translation_unit = None
        self.completion_cache = None
        self.cached_contents = None
        conf = Config()
        self.custom_clang_lib = conf.lib
        self._register_custom_clang_functions()

    def run(self):
        input_stream = self.input_stream
        output_stream = self.output_stream
        while True:
            line = input_stream.readline()
            response = self.process_request(line)
            json.dump(response, output_stream)
            # Use \n to signal the end of the response.
            output_stream.write('\n')
            output_stream.flush()

    def process_request(self, line):
        '''Note that line will likely including a trailing newline.

        Returns a dict or list that can be serialized by json.dump().
        '''
        request = json.loads(line)

        # Every request should have an id that must also be present in the
        # response.
        reqid = request['reqid']
        response = {'reqid': reqid}

        start_time = time.time()
        try:
            method = request['method']
            if method == 'compile':
                self.compile(request, response)
            elif method == 'get_completions':
                self.get_completions(request, response)
            elif method == 'get_declaration':
                self.get_declaration(request, response)
            elif method == 'get_declaration_info':
                self.get_declaration_info(request, response)
            else:
                response[
                    'error'] = 'Unknown method to clang_server.py: %s.' % method
        except:
            response['error'] = traceback.format_exc()

        root_logger.info('Finished %s request in %.2lf seconds.', method,
                         time.time() - start_time)

        # response must have a key named "error" if there was a failure of any
        # kind.
        return response

    def compile(self, request, response):
        contents = request['contents']
        flags = request['flags']

        # Update the translation unit with the latest contents.
        # Force a re-parse, in case the user e.g. changed a header file.
        self.cached_contents = None
        translation_unit = self._update_translation_unit(contents, flags)
        if not translation_unit:
            sys.stderr.write(
                'Suspicious: requesting compilation of %s without flags' %
                self.src)
            response['diagnostics'] = []
            return

        # Return the diagnostics.
        diagnostics = []
        for diag in translation_unit.diagnostics:
            if diag.spelling == PRAGMA_ONCE_IN_MAIN_FILE and is_header_file(
                    self.src):
                continue
            diagnostics.append(self.diagnostic_dict(diag))
        response['diagnostics'] = diagnostics

    def diagnostic_dict(self, diag):
        ranges = map(range_dict, diag.ranges)
        if len(ranges) == 0:
            ranges = None
        fixits = []
        for fixit in diag.fixits:
            fixits.append({
                'range': range_dict(fixit.range),
                'value': fixit.value,
            })
        children = []
        for child in child_diagnostics(self.custom_clang_lib, diag):
            children.append({
                'spelling': child.spelling,
                'location': location_dict(child.location),
                'ranges': map(range_dict, child.ranges),
            })
            # Some fixits may be nested; add them to the root diagnostic.
            for fixit in child.fixits:
                fixits.append({
                    'range': range_dict(fixit.range),
                    'value': fixit.value,
                })
        return {
            'spelling': diag.spelling,
            'severity': diag.severity,
            'location': location_dict(diag.location),
            'ranges': ranges,
            'fixits': fixits,
            'children': children,
        }

    def get_completions(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        prefix = request['prefix']
        token_start_column = request['tokenStartColumn']
        flags = request['flags']

        # NOTE: there is no need to update the translation unit here.
        # libclang's completions API seamlessly takes care of unsaved content
        # without any special handling.
        translation_unit = self._get_translation_unit(None, flags)
        if translation_unit:
            if self.completion_cache is None:
                self.completion_cache = CompletionCache(
                    self.src, translation_unit)
            completions = self.completion_cache.get_completions(
                line + 1,
                token_start_column + 1,
                prefix,
                contents,
                limit=COMPLETIONS_LIMIT)
        else:
            completions = []
        response['completions'] = completions
        response['line'] = line
        response['column'] = column
        response['prefix'] = prefix

    def get_declaration(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        flags = request['flags']

        response['line'] = line
        response['column'] = column

        # Update the translation unit with the latest contents.
        translation_unit = self._update_translation_unit(contents, flags)
        if not translation_unit:
            return

        response[
            'locationAndSpelling'] = get_declaration_location_and_spelling(
                translation_unit, self.src, line + 1, column + 1)

    def get_declaration_info(self, request, response):
        contents = request['contents']
        line = request['line']
        column = request['column']
        flags = request['flags']

        response['line'] = line
        response['column'] = column

        # Update the translation unit with the latest contents.
        translation_unit = self._update_translation_unit(contents, flags)
        if not translation_unit:
            return

        location = translation_unit.get_location(src, (line + 1, column + 1))
        cursor = Cursor.from_location(translation_unit, location)
        cursor = cursor.referenced
        if cursor is None:
            return

        response['info'] = self.get_declaration_info_for_cursor(cursor)

    def get_declaration_info_for_cursor(self, cursor):
        '''Returns string id in clang-callgraph-service format for entity under the
        cursor. Currently works only for definitions of class methods, instance
        methods and functions. Returns None for everything else.
        '''
        result = []
        while cursor is not None and not cursor.kind.is_translation_unit():
            file = cursor.location.file
            result.append({
                'name': self.get_name_for_cursor(cursor),
                'type': cursor.kind.name,
                'cursor_usr': cursor.get_usr(),
                'file': resolve_file(file),
            })
            cursor = cursor.semantic_parent

        return result

    def get_name_for_cursor(self, cursor):
        name = cursor.displayname
        # clang doesn't include the interface name for categories; add it
        # manually
        if (cursor.kind == CursorKind.OBJC_CATEGORY_DECL
                or cursor.kind == CursorKind.OBJC_CATEGORY_IMPL_DECL):
            # Find reference to base class.
            base_name = ''
            for child in cursor.get_children():
                if child.kind == CursorKind.OBJC_CLASS_REF:
                    base_name = child.displayname
                    break
            return base_name + ' (' + name + ')'
        return name

    def _get_translation_unit(self, unsaved_contents, flags=None):
        '''
        Get the current translation unit, or create it if it does not exist.
        Flags can be optional if the translation unit already exists.
        '''
        if self.translation_unit is not None:
            return self.translation_unit

        if flags is None:
            return None

        # Configure the options.
        # See also clang_defaultEditingTranslationUnitOptions in Index.h.
        options = (
            TranslationUnit.PARSE_PRECOMPILED_PREAMBLE
            | TranslationUnit.PARSE_CACHE_COMPLETION_RESULTS
            | TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION
            | TranslationUnit.PARSE_INCOMPLETE)

        args = self._get_args_for_flags(flags)
        self.translation_unit = self.index.parse(
            self.src, args, self._make_files(unsaved_contents), options)
        # Do not cache the contents after the initial compile!
        # We need to trigger an immediate reparse for Clang to generate precompiled headers.
        # TODO(#9832847): Use CXTranslationUnit_CreatePreambleOnFirstParse after Clang 3.8.
        # self.cached_contents = unsaved_contents
        return self.translation_unit

    # Clang's API expects a list of (src, contents) pairs.
    def _make_files(self, unsaved_contents):
        if unsaved_contents is None:
            return []
        return [(self.src, unsaved_contents.encode('utf-8'))]

    def _get_args_for_flags(self, flags):
        # Enable typo-detection (and the corresponding fixits)
        # For some reason this is not enabled by default in libclang.
        args = ['-fspell-checking']
        for arg in flags:
            if arg == self.src:
                # Including the input file as an argument causes index.parse() to fail.
                # Surprisingly, including '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang'
                # as the first argument does not cause any issues.
                pass
            elif arg == '-c':
                # No need to generate a .o file.
                args.append('-fsyntax-only')
            elif arg == '-Werror':
                # We disable this so that the severity can be better reflected in the UI.
                # For example, this allows unused code to appear as a warning
                # instead of an error.
                pass
            elif arg == '-MMD' or arg == '-MD':
                # Do not write out dependency files.
                pass
            else:
                args.append(arg)
        return args

    def _update_translation_unit(self, unsaved_contents=None, flags=None):
        translation_unit = self._get_translation_unit(unsaved_contents, flags)
        if translation_unit is None:
            return None
        # Reparsing isn't cheap, so skip it if nothing changed.
        if (unsaved_contents is not None
                and unsaved_contents == self.cached_contents):
            return translation_unit
        options = 0  # There are no reparse options available in libclang yet.
        translation_unit.reparse(self._make_files(unsaved_contents), options)
        self.cached_contents = unsaved_contents
        if self.completion_cache is not None:
            self.completion_cache.invalidate()
        return translation_unit

    def _register_custom_clang_functions(self):
        # Extend the Clang C bindings with the additional required functions.
        for item in Server.CUSTOM_CLANG_FUNCTIONS:
            func = getattr(self.custom_clang_lib, item[0])
            func.argtypes = item[1]
            func.restype = item[2]