f88c9feb4f
This change should fix the random hangs and segfaults when using the clang completer. Also, assertion errors printed to the console on vim exit should go away too, same thing with segfaults on vim exit. These "on exit" errors were caused by not cleanly shutting down the background threads; both the identifier completer and the clang one now join the threads on destruction. This results in a clean shutdown. The new clang completer architecture now uses only one clang thread (again) instead of a completion and parsing thread. Since the parsing task needs to wait on the completion task if it was started first (and vice-versa) there's no point to using two threads. The desired "simplicity" of using two threads for these two tasks actually created needless complexity (and bugs). Sigh. Such is life. A TranslationUnit abstraction was also created and this in turn also reduces the complexity of the clang completer. The clang completer now also has some (very) basic tests.
176 lines
5.7 KiB
C++
176 lines
5.7 KiB
C++
// Copyright (C) 2011, 2012 Strahinja Val Markovic <val@markovic.io>
|
|
//
|
|
// This file is part of YouCompleteMe.
|
|
//
|
|
// YouCompleteMe is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// YouCompleteMe is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "TranslationUnit.h"
|
|
#include "CompletionData.h"
|
|
#include "standard.h"
|
|
#include "ClangUtils.h"
|
|
|
|
#include <clang-c/Index.h>
|
|
#include <boost/make_shared.hpp>
|
|
|
|
using boost::unique_lock;
|
|
using boost::mutex;
|
|
using boost::try_to_lock_t;
|
|
|
|
namespace YouCompleteMe
|
|
{
|
|
|
|
TranslationUnit::TranslationUnit(
|
|
const std::string &filename,
|
|
const std::vector< UnsavedFile > &unsaved_files,
|
|
const std::vector< std::string > &flags,
|
|
CXIndex clang_index )
|
|
: filename_( filename )
|
|
{
|
|
std::vector< const char* > pointer_flags;
|
|
pointer_flags.reserve( flags.size() );
|
|
|
|
foreach ( const std::string &flag, flags )
|
|
{
|
|
pointer_flags.push_back( flag.c_str() );
|
|
}
|
|
|
|
std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles(
|
|
unsaved_files );
|
|
|
|
clang_translation_unit_ = clang_parseTranslationUnit(
|
|
clang_index,
|
|
filename.c_str(),
|
|
&pointer_flags[ 0 ],
|
|
pointer_flags.size(),
|
|
&cxunsaved_files[ 0 ],
|
|
cxunsaved_files.size(),
|
|
clang_defaultEditingTranslationUnitOptions() );
|
|
|
|
// Only with a reparse is the preable precompiled. I do not know why...
|
|
// TODO: report this bug on the clang tracker
|
|
clang_reparseTranslationUnit(
|
|
clang_translation_unit_,
|
|
cxunsaved_files.size(),
|
|
&cxunsaved_files[ 0 ],
|
|
clang_defaultEditingTranslationUnitOptions() );
|
|
}
|
|
|
|
|
|
TranslationUnit::~TranslationUnit()
|
|
{
|
|
clang_disposeTranslationUnit( clang_translation_unit_ );
|
|
}
|
|
|
|
|
|
std::vector< Diagnostic > TranslationUnit::LatestDiagnostics()
|
|
{
|
|
std::vector< Diagnostic > diagnostics;
|
|
unique_lock< mutex > lock( diagnostics_mutex_ );
|
|
|
|
// We don't need the latest diags after we return them once so we swap the
|
|
// internal data with a new, empty diag vector. This vector is then returned
|
|
// and on C++11 compilers a move ctor is invoked, thus no copy is created.
|
|
// Theoretically, just returning the value of a
|
|
// [boost::|std::]move(latest_diagnostics_) call _should_ leave the
|
|
// latest_diagnostics_ vector in an emtpy, valid state but I'm not going to
|
|
// rely on that. I just had to look this up in the standard to be sure, and
|
|
// future readers of this code (myself included) should not be forced to do
|
|
// that to understand what the hell is going on.
|
|
|
|
std::swap( latest_diagnostics_, diagnostics );
|
|
return diagnostics;
|
|
}
|
|
|
|
|
|
bool TranslationUnit::IsCurrentlyUpdating() const
|
|
{
|
|
unique_lock< mutex > lock( clang_access_mutex_, try_to_lock_t() );
|
|
return !lock.owns_lock();
|
|
}
|
|
|
|
|
|
void TranslationUnit::Reparse( const std::vector< UnsavedFile > &unsaved_files )
|
|
{
|
|
unique_lock< mutex > lock( clang_access_mutex_ );
|
|
|
|
std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles(
|
|
unsaved_files );
|
|
|
|
clang_reparseTranslationUnit(
|
|
clang_translation_unit_,
|
|
cxunsaved_files.size(),
|
|
&cxunsaved_files[ 0 ],
|
|
clang_defaultEditingTranslationUnitOptions() );
|
|
|
|
UpdateLatestDiagnostics();
|
|
}
|
|
|
|
|
|
std::vector< CompletionData > TranslationUnit::CandidatesForLocation(
|
|
int line,
|
|
int column,
|
|
const std::vector< UnsavedFile > &unsaved_files )
|
|
{
|
|
unique_lock< mutex > lock( clang_access_mutex_ );
|
|
|
|
std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles(
|
|
unsaved_files );
|
|
|
|
// codeCompleteAt reparses the TU if the underlying source file has changed on
|
|
// disk since the last time the TU was updated and there are no unsaved files.
|
|
// If there are unsaved files, then codeCompleteAt will parse the in-memory
|
|
// file contents we are giving it. In short, it is NEVER a good idea to call
|
|
// clang_reparseTranslationUnit right before a call to clang_codeCompleteAt.
|
|
// This only makes clang reparse the whole file TWICE, which has a huge impact
|
|
// on latency. At the time of writing, it seems that most users of libclang
|
|
// in the open-source world don't realize this (I checked). Some don't even
|
|
// call reparse*, but parse* which is even less efficient.
|
|
|
|
CXCodeCompleteResults *results =
|
|
clang_codeCompleteAt( clang_translation_unit_,
|
|
filename_.c_str(),
|
|
line,
|
|
column,
|
|
&cxunsaved_files[ 0 ],
|
|
cxunsaved_files.size(),
|
|
clang_defaultCodeCompleteOptions() );
|
|
|
|
std::vector< CompletionData > candidates = ToCompletionDataVector( results );
|
|
clang_disposeCodeCompleteResults( results );
|
|
return candidates;
|
|
}
|
|
|
|
|
|
// Should only be called while holding the clang_access_mutex_
|
|
// TODO: assert that
|
|
void TranslationUnit::UpdateLatestDiagnostics()
|
|
{
|
|
unique_lock< mutex > lock( diagnostics_mutex_ );
|
|
|
|
latest_diagnostics_.clear();
|
|
uint num_diagnostics = clang_getNumDiagnostics( clang_translation_unit_ );
|
|
latest_diagnostics_.reserve( num_diagnostics );
|
|
|
|
for ( uint i = 0; i < num_diagnostics; ++i )
|
|
{
|
|
Diagnostic diagnostic = CXDiagnosticToDiagnostic(
|
|
clang_getDiagnostic( clang_translation_unit_, i ) );
|
|
|
|
if ( diagnostic.kind_ != 'I' )
|
|
latest_diagnostics_.push_back( diagnostic );
|
|
}
|
|
}
|
|
|
|
} // namespace YouCompleteMe
|