1
0

TranslationUnit.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright (C) 2011, 2012 Strahinja Val Markovic <val@markovic.io>
  2. //
  3. // This file is part of YouCompleteMe.
  4. //
  5. // YouCompleteMe is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // YouCompleteMe is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
  17. #include "TranslationUnit.h"
  18. #include "CompletionData.h"
  19. #include "standard.h"
  20. #include "exceptions.h"
  21. #include "ClangUtils.h"
  22. #include "ClangHelpers.h"
  23. #include <boost/shared_ptr.hpp>
  24. #include <boost/type_traits/remove_pointer.hpp>
  25. using boost::unique_lock;
  26. using boost::mutex;
  27. using boost::try_to_lock_t;
  28. using boost::shared_ptr;
  29. using boost::remove_pointer;
  30. namespace YouCompleteMe {
  31. typedef shared_ptr <
  32. remove_pointer< CXCodeCompleteResults >::type > CodeCompleteResultsWrap;
  33. TranslationUnit::TranslationUnit()
  34. : filename_( "" ),
  35. clang_translation_unit_( NULL ) {
  36. }
  37. TranslationUnit::TranslationUnit(
  38. const std::string &filename,
  39. const std::vector< UnsavedFile > &unsaved_files,
  40. const std::vector< std::string > &flags,
  41. CXIndex clang_index )
  42. : filename_( filename ),
  43. clang_translation_unit_( NULL ) {
  44. std::vector< const char * > pointer_flags;
  45. pointer_flags.reserve( flags.size() );
  46. foreach ( const std::string & flag, flags ) {
  47. pointer_flags.push_back( flag.c_str() );
  48. }
  49. std::vector< CXUnsavedFile > cxunsaved_files =
  50. ToCXUnsavedFiles( unsaved_files );
  51. const CXUnsavedFile *unsaved = cxunsaved_files.size() > 0
  52. ? &cxunsaved_files[ 0 ] : NULL;
  53. clang_translation_unit_ = clang_parseTranslationUnit(
  54. clang_index,
  55. filename.c_str(),
  56. &pointer_flags[ 0 ],
  57. pointer_flags.size(),
  58. const_cast<CXUnsavedFile *>( unsaved ),
  59. cxunsaved_files.size(),
  60. clang_defaultEditingTranslationUnitOptions() );
  61. if ( !clang_translation_unit_ )
  62. boost_throw( ClangParseError() );
  63. // Only with a reparse is the preable precompiled. I do not know why...
  64. // TODO: report this bug on the clang tracker
  65. Reparse( cxunsaved_files );
  66. }
  67. TranslationUnit::~TranslationUnit() {
  68. Destroy();
  69. }
  70. void TranslationUnit::Destroy() {
  71. unique_lock< mutex > lock( clang_access_mutex_ );
  72. if ( clang_translation_unit_ ) {
  73. clang_disposeTranslationUnit( clang_translation_unit_ );
  74. clang_translation_unit_ = NULL;
  75. }
  76. }
  77. std::vector< Diagnostic > TranslationUnit::LatestDiagnostics() {
  78. if ( !clang_translation_unit_ )
  79. return std::vector< Diagnostic >();
  80. unique_lock< mutex > lock( diagnostics_mutex_ );
  81. return latest_diagnostics_;
  82. }
  83. bool TranslationUnit::IsCurrentlyUpdating() const {
  84. // We return true when the TU is invalid; an invalid TU also acts a sentinel,
  85. // preventing other threads from trying to use it.
  86. if ( !clang_translation_unit_ )
  87. return true;
  88. unique_lock< mutex > lock( clang_access_mutex_, try_to_lock_t() );
  89. return !lock.owns_lock();
  90. }
  91. std::vector< Diagnostic > TranslationUnit::Reparse(
  92. const std::vector< UnsavedFile > &unsaved_files ) {
  93. std::vector< CXUnsavedFile > cxunsaved_files =
  94. ToCXUnsavedFiles( unsaved_files );
  95. Reparse( cxunsaved_files );
  96. unique_lock< mutex > lock( diagnostics_mutex_ );
  97. return latest_diagnostics_;
  98. }
  99. void TranslationUnit::ReparseForIndexing(
  100. const std::vector< UnsavedFile > &unsaved_files ) {
  101. std::vector< CXUnsavedFile > cxunsaved_files =
  102. ToCXUnsavedFiles( unsaved_files );
  103. Reparse( cxunsaved_files,
  104. CXTranslationUnit_PrecompiledPreamble |
  105. CXTranslationUnit_SkipFunctionBodies );
  106. }
  107. std::vector< CompletionData > TranslationUnit::CandidatesForLocation(
  108. int line,
  109. int column,
  110. const std::vector< UnsavedFile > &unsaved_files ) {
  111. unique_lock< mutex > lock( clang_access_mutex_ );
  112. if ( !clang_translation_unit_ )
  113. return std::vector< CompletionData >();
  114. std::vector< CXUnsavedFile > cxunsaved_files =
  115. ToCXUnsavedFiles( unsaved_files );
  116. const CXUnsavedFile *unsaved = cxunsaved_files.size() > 0
  117. ? &cxunsaved_files[ 0 ] : NULL;
  118. // codeCompleteAt reparses the TU if the underlying source file has changed on
  119. // disk since the last time the TU was updated and there are no unsaved files.
  120. // If there are unsaved files, then codeCompleteAt will parse the in-memory
  121. // file contents we are giving it. In short, it is NEVER a good idea to call
  122. // clang_reparseTranslationUnit right before a call to clang_codeCompleteAt.
  123. // This only makes clang reparse the whole file TWICE, which has a huge impact
  124. // on latency. At the time of writing, it seems that most users of libclang
  125. // in the open-source world don't realize this (I checked). Some don't even
  126. // call reparse*, but parse* which is even less efficient.
  127. CodeCompleteResultsWrap results(
  128. clang_codeCompleteAt( clang_translation_unit_,
  129. filename_.c_str(),
  130. line,
  131. column,
  132. const_cast<CXUnsavedFile *>( unsaved ),
  133. cxunsaved_files.size(),
  134. clang_defaultCodeCompleteOptions() ),
  135. clang_disposeCodeCompleteResults );
  136. std::vector< CompletionData > candidates = ToCompletionDataVector(
  137. results.get() );
  138. return candidates;
  139. }
  140. Location TranslationUnit::GetDeclarationLocation(
  141. int line,
  142. int column,
  143. const std::vector< UnsavedFile > &unsaved_files ) {
  144. ReparseForIndexing( unsaved_files );
  145. unique_lock< mutex > lock( clang_access_mutex_ );
  146. if ( !clang_translation_unit_ )
  147. return Location();
  148. CXCursor cursor = GetCursor( line, column );
  149. if ( !CursorIsValid( cursor ) )
  150. return Location();
  151. CXCursor referenced_cursor = clang_getCursorReferenced( cursor );
  152. if ( !CursorIsValid( referenced_cursor ) )
  153. return Location();
  154. return Location( clang_getCursorLocation( referenced_cursor ) );
  155. }
  156. Location TranslationUnit::GetDefinitionLocation(
  157. int line,
  158. int column,
  159. const std::vector< UnsavedFile > &unsaved_files ) {
  160. ReparseForIndexing( unsaved_files );
  161. unique_lock< mutex > lock( clang_access_mutex_ );
  162. if ( !clang_translation_unit_ )
  163. return Location();
  164. CXCursor cursor = GetCursor( line, column );
  165. if ( !CursorIsValid( cursor ) )
  166. return Location();
  167. CXCursor definition_cursor = clang_getCursorDefinition( cursor );
  168. if ( !CursorIsValid( definition_cursor ) )
  169. return Location();
  170. return Location( clang_getCursorLocation( definition_cursor ) );
  171. }
  172. // Argument taken as non-const ref because we need to be able to pass a
  173. // non-const pointer to clang. This function (and clang too) will not modify the
  174. // param though.
  175. void TranslationUnit::Reparse(
  176. std::vector< CXUnsavedFile > &unsaved_files ) {
  177. Reparse( unsaved_files, clang_defaultEditingTranslationUnitOptions() );
  178. }
  179. // Argument taken as non-const ref because we need to be able to pass a
  180. // non-const pointer to clang. This function (and clang too) will not modify the
  181. // param though.
  182. void TranslationUnit::Reparse( std::vector< CXUnsavedFile > &unsaved_files,
  183. uint parse_options ) {
  184. int failure = 0;
  185. {
  186. unique_lock< mutex > lock( clang_access_mutex_ );
  187. if ( !clang_translation_unit_ )
  188. return;
  189. CXUnsavedFile *unsaved = unsaved_files.size() > 0
  190. ? &unsaved_files[ 0 ] : NULL;
  191. failure = clang_reparseTranslationUnit( clang_translation_unit_,
  192. unsaved_files.size(),
  193. unsaved,
  194. parse_options );
  195. }
  196. if ( failure ) {
  197. Destroy();
  198. boost_throw( ClangParseError() );
  199. }
  200. UpdateLatestDiagnostics();
  201. }
  202. void TranslationUnit::UpdateLatestDiagnostics() {
  203. unique_lock< mutex > lock1( clang_access_mutex_ );
  204. unique_lock< mutex > lock2( diagnostics_mutex_ );
  205. latest_diagnostics_.clear();
  206. uint num_diagnostics = clang_getNumDiagnostics( clang_translation_unit_ );
  207. latest_diagnostics_.reserve( num_diagnostics );
  208. for ( uint i = 0; i < num_diagnostics; ++i ) {
  209. Diagnostic diagnostic =
  210. BuildDiagnostic(
  211. DiagnosticWrap( clang_getDiagnostic( clang_translation_unit_, i ),
  212. clang_disposeDiagnostic ),
  213. clang_translation_unit_ );
  214. if ( diagnostic.kind_ != 'I' )
  215. latest_diagnostics_.push_back( diagnostic );
  216. }
  217. }
  218. CXCursor TranslationUnit::GetCursor( int line, int column ) {
  219. // ASSUMES A LOCK IS ALREADY HELD ON clang_access_mutex_!
  220. if ( !clang_translation_unit_ )
  221. return clang_getNullCursor();
  222. CXFile file = clang_getFile( clang_translation_unit_, filename_.c_str() );
  223. CXSourceLocation source_location = clang_getLocation(
  224. clang_translation_unit_,
  225. file,
  226. line,
  227. column );
  228. return clang_getCursor( clang_translation_unit_, source_location );
  229. }
  230. } // namespace YouCompleteMe