123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- // 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 "exceptions.h"
- #include "ClangUtils.h"
- #include "ClangHelpers.h"
- #include <boost/shared_ptr.hpp>
- #include <boost/type_traits/remove_pointer.hpp>
- using boost::unique_lock;
- using boost::mutex;
- using boost::try_to_lock_t;
- using boost::shared_ptr;
- using boost::remove_pointer;
- namespace YouCompleteMe {
- typedef shared_ptr <
- remove_pointer< CXCodeCompleteResults >::type > CodeCompleteResultsWrap;
- TranslationUnit::TranslationUnit()
- : filename_( "" ),
- clang_translation_unit_( NULL ) {
- }
- TranslationUnit::TranslationUnit(
- const std::string &filename,
- const std::vector< UnsavedFile > &unsaved_files,
- const std::vector< std::string > &flags,
- CXIndex clang_index )
- : filename_( filename ),
- clang_translation_unit_( NULL ) {
- 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 );
- const CXUnsavedFile *unsaved = cxunsaved_files.size() > 0
- ? &cxunsaved_files[ 0 ] : NULL;
- clang_translation_unit_ = clang_parseTranslationUnit(
- clang_index,
- filename.c_str(),
- &pointer_flags[ 0 ],
- pointer_flags.size(),
- const_cast<CXUnsavedFile *>( unsaved ),
- cxunsaved_files.size(),
- clang_defaultEditingTranslationUnitOptions() );
- if ( !clang_translation_unit_ )
- boost_throw( ClangParseError() );
- // Only with a reparse is the preable precompiled. I do not know why...
- // TODO: report this bug on the clang tracker
- Reparse( cxunsaved_files );
- }
- TranslationUnit::~TranslationUnit() {
- Destroy();
- }
- void TranslationUnit::Destroy() {
- unique_lock< mutex > lock( clang_access_mutex_ );
- if ( clang_translation_unit_ ) {
- clang_disposeTranslationUnit( clang_translation_unit_ );
- clang_translation_unit_ = NULL;
- }
- }
- std::vector< Diagnostic > TranslationUnit::LatestDiagnostics() {
- if ( !clang_translation_unit_ )
- return std::vector< Diagnostic >();
- unique_lock< mutex > lock( diagnostics_mutex_ );
- return latest_diagnostics_;
- }
- bool TranslationUnit::IsCurrentlyUpdating() const {
- // We return true when the TU is invalid; an invalid TU also acts a sentinel,
- // preventing other threads from trying to use it.
- if ( !clang_translation_unit_ )
- return true;
- unique_lock< mutex > lock( clang_access_mutex_, try_to_lock_t() );
- return !lock.owns_lock();
- }
- std::vector< Diagnostic > TranslationUnit::Reparse(
- const std::vector< UnsavedFile > &unsaved_files ) {
- std::vector< CXUnsavedFile > cxunsaved_files =
- ToCXUnsavedFiles( unsaved_files );
- Reparse( cxunsaved_files );
- unique_lock< mutex > lock( diagnostics_mutex_ );
- return latest_diagnostics_;
- }
- void TranslationUnit::ReparseForIndexing(
- const std::vector< UnsavedFile > &unsaved_files ) {
- std::vector< CXUnsavedFile > cxunsaved_files =
- ToCXUnsavedFiles( unsaved_files );
- Reparse( cxunsaved_files,
- CXTranslationUnit_PrecompiledPreamble |
- CXTranslationUnit_SkipFunctionBodies );
- }
- std::vector< CompletionData > TranslationUnit::CandidatesForLocation(
- int line,
- int column,
- const std::vector< UnsavedFile > &unsaved_files ) {
- unique_lock< mutex > lock( clang_access_mutex_ );
- if ( !clang_translation_unit_ )
- return std::vector< CompletionData >();
- std::vector< CXUnsavedFile > cxunsaved_files =
- ToCXUnsavedFiles( unsaved_files );
- const CXUnsavedFile *unsaved = cxunsaved_files.size() > 0
- ? &cxunsaved_files[ 0 ] : NULL;
- // 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.
- CodeCompleteResultsWrap results(
- clang_codeCompleteAt( clang_translation_unit_,
- filename_.c_str(),
- line,
- column,
- const_cast<CXUnsavedFile *>( unsaved ),
- cxunsaved_files.size(),
- clang_defaultCodeCompleteOptions() ),
- clang_disposeCodeCompleteResults );
- std::vector< CompletionData > candidates = ToCompletionDataVector(
- results.get() );
- return candidates;
- }
- Location TranslationUnit::GetDeclarationLocation(
- int line,
- int column,
- const std::vector< UnsavedFile > &unsaved_files ) {
- ReparseForIndexing( unsaved_files );
- unique_lock< mutex > lock( clang_access_mutex_ );
- if ( !clang_translation_unit_ )
- return Location();
- CXCursor cursor = GetCursor( line, column );
- if ( !CursorIsValid( cursor ) )
- return Location();
- CXCursor referenced_cursor = clang_getCursorReferenced( cursor );
- if ( !CursorIsValid( referenced_cursor ) )
- return Location();
- return Location( clang_getCursorLocation( referenced_cursor ) );
- }
- Location TranslationUnit::GetDefinitionLocation(
- int line,
- int column,
- const std::vector< UnsavedFile > &unsaved_files ) {
- ReparseForIndexing( unsaved_files );
- unique_lock< mutex > lock( clang_access_mutex_ );
- if ( !clang_translation_unit_ )
- return Location();
- CXCursor cursor = GetCursor( line, column );
- if ( !CursorIsValid( cursor ) )
- return Location();
- CXCursor definition_cursor = clang_getCursorDefinition( cursor );
- if ( !CursorIsValid( definition_cursor ) )
- return Location();
- return Location( clang_getCursorLocation( definition_cursor ) );
- }
- // Argument taken as non-const ref because we need to be able to pass a
- // non-const pointer to clang. This function (and clang too) will not modify the
- // param though.
- void TranslationUnit::Reparse(
- std::vector< CXUnsavedFile > &unsaved_files ) {
- Reparse( unsaved_files, clang_defaultEditingTranslationUnitOptions() );
- }
- // Argument taken as non-const ref because we need to be able to pass a
- // non-const pointer to clang. This function (and clang too) will not modify the
- // param though.
- void TranslationUnit::Reparse( std::vector< CXUnsavedFile > &unsaved_files,
- uint parse_options ) {
- int failure = 0;
- {
- unique_lock< mutex > lock( clang_access_mutex_ );
- if ( !clang_translation_unit_ )
- return;
- CXUnsavedFile *unsaved = unsaved_files.size() > 0
- ? &unsaved_files[ 0 ] : NULL;
- failure = clang_reparseTranslationUnit( clang_translation_unit_,
- unsaved_files.size(),
- unsaved,
- parse_options );
- }
- if ( failure ) {
- Destroy();
- boost_throw( ClangParseError() );
- }
- UpdateLatestDiagnostics();
- }
- void TranslationUnit::UpdateLatestDiagnostics() {
- unique_lock< mutex > lock1( clang_access_mutex_ );
- unique_lock< mutex > lock2( 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 =
- BuildDiagnostic(
- DiagnosticWrap( clang_getDiagnostic( clang_translation_unit_, i ),
- clang_disposeDiagnostic ),
- clang_translation_unit_ );
- if ( diagnostic.kind_ != 'I' )
- latest_diagnostics_.push_back( diagnostic );
- }
- }
- CXCursor TranslationUnit::GetCursor( int line, int column ) {
- // ASSUMES A LOCK IS ALREADY HELD ON clang_access_mutex_!
- if ( !clang_translation_unit_ )
- return clang_getNullCursor();
- CXFile file = clang_getFile( clang_translation_unit_, filename_.c_str() );
- CXSourceLocation source_location = clang_getLocation(
- clang_translation_unit_,
- file,
- line,
- column );
- return clang_getCursor( clang_translation_unit_, source_location );
- }
- } // namespace YouCompleteMe
|