Skip to content

Commit a2ad08b

Browse files
committed
Rustup rust src heuristics
1 parent 99f469e commit a2ad08b

File tree

3 files changed

+77
-6
lines changed

3 files changed

+77
-6
lines changed

ycmd/completers/rust/rust_completer.py

+29-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
# Not installing aliases from python-future; it's unreliable and slow.
2323
from builtins import * # noqa
2424

25-
from ycmd.utils import ToBytes, SetEnviron, ProcessIsRunning, urljoin
25+
from ycmd.utils import ( FindExecutable, ToUnicode, ToBytes, SetEnviron,
26+
ProcessIsRunning, urljoin )
2627
from ycmd.completers.completer import Completer
2728
from ycmd import responses, utils, hmac_utils
2829

@@ -35,6 +36,7 @@
3536
import binascii
3637
import threading
3738
import os
39+
import subprocess
3840

3941
from os import path as p
4042

@@ -66,6 +68,16 @@
6668
LOGFILE_FORMAT = 'racerd_{port}_{std}_'
6769

6870

71+
def _GetRustSysroot( rustc_exec ):
72+
return ToUnicode( utils.SafePopen( [ rustc_exec,
73+
'--print',
74+
'sysroot' ],
75+
stdin_windows = subprocess.PIPE,
76+
stdout = subprocess.PIPE,
77+
stderr = subprocess.PIPE )
78+
.communicate()[ 0 ].rstrip() )
79+
80+
6981
def FindRacerdBinary( user_options ):
7082
"""
7183
Find path to racerd binary
@@ -127,13 +139,28 @@ def _GetRustSrcPath( self ):
127139
"""
128140
Attempt to read user option for rust_src_path. Fallback to environment
129141
variable if it's not provided.
142+
Finally try to be smart and figure out the path assuming the user set up
143+
rust by the means of rustup.
130144
"""
131145
rust_src_path = ( self.user_options[ 'rust_src_path' ] or
132146
os.environ.get( 'RUST_SRC_PATH' ) )
133147

134148
if rust_src_path:
135149
return os.path.expanduser( os.path.expandvars( rust_src_path ) )
136-
return None
150+
151+
# Try to figure out the src path using rustup
152+
rustc_executable = FindExecutable( 'rustc' )
153+
if not rustc_executable:
154+
return None
155+
156+
rust_sysroot = _GetRustSysroot( rustc_executable )
157+
rust_src_path = p.join( rust_sysroot,
158+
'lib',
159+
'rustlib',
160+
'src',
161+
'rust',
162+
'src' )
163+
return rust_src_path if p.isdir( rust_src_path ) else None
137164

138165

139166
def SupportedFiletypes( self ):

ycmd/tests/rust/get_completions_test.py

+48-4
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,12 @@ def GetCompletions_Basic_test( app ):
5454
CompletionEntryMatcher( 'build_shuttle' ) ) )
5555

5656

57-
@SharedYcmd
57+
@IsolatedYcmd()
58+
@patch( 'ycmd.completers.rust.rust_completer._GetRustSysroot',
59+
return_value = '/non/existing/rust/src/path' )
5860
def GetCompletions_WhenStandardLibraryCompletionFails_MentionRustSrcPath_test(
59-
app ):
61+
app, *args ):
62+
WaitUntilCompleterServerReady( app, 'rust' )
6063
filepath = PathToTestFile( 'std_completions.rs' )
6164
contents = ReadFile( filepath )
6265

@@ -74,8 +77,11 @@ def GetCompletions_WhenStandardLibraryCompletionFails_MentionRustSrcPath_test(
7477
ErrorMatcher( RuntimeError, ERROR_FROM_RACERD_MESSAGE ) )
7578

7679

77-
@SharedYcmd
78-
def GetCompletions_WhenNoCompletionsFound_MentionRustSrcPath_test( app ):
80+
@IsolatedYcmd()
81+
@patch( 'ycmd.completers.rust.rust_completer._GetRustSysroot',
82+
return_value = '/non/existing/rust/src/path' )
83+
def GetCompletions_WhenNoCompletionsFound_MentionRustSrcPath_test( app, *args ):
84+
WaitUntilCompleterServerReady( app, 'rust' )
7985
filepath = PathToTestFile( 'test.rs' )
8086
contents = ReadFile( filepath )
8187

@@ -132,3 +138,41 @@ def GetCompletions_NonExistingRustSrcPathFromEnvironmentVariable_test( app ):
132138
assert_that( response,
133139
ErrorMatcher( RuntimeError,
134140
NON_EXISTING_RUST_SOURCES_PATH_MESSAGE ) )
141+
142+
143+
@IsolatedYcmd()
144+
@patch( 'ycmd.completers.rust.rust_completer.FindExecutable',
145+
return_value = None )
146+
def GetCompletions_WhenRustcNotFound_MentionRustSrcPath_test( app, *args ):
147+
WaitUntilCompleterServerReady( app, 'rust' )
148+
filepath = PathToTestFile( 'test.rs' )
149+
contents = ReadFile( filepath )
150+
151+
completion_data = BuildRequest( filepath = filepath,
152+
filetype = 'rust',
153+
contents = contents,
154+
force_semantic = True,
155+
line_num = 1,
156+
column_num = 1 )
157+
158+
response = app.post_json( '/completions',
159+
completion_data,
160+
expect_errors = True ).json
161+
assert_that( response,
162+
ErrorMatcher( RuntimeError, ERROR_FROM_RACERD_MESSAGE ) )
163+
164+
165+
@IsolatedYcmd()
166+
@patch( 'ycmd.completers.rust.rust_completer._GetRustSysroot',
167+
return_value = PathToTestFile( 'rustup-toolchain' ) )
168+
def GetCompletions_RustupPathHeuristics_test( app, *args ):
169+
request_data = BuildRequest( filetype = 'rust' )
170+
171+
assert_that( app.post_json( '/debug_info', request_data ).json,
172+
has_entry( 'completer', has_entry( 'items', has_items(
173+
has_entry( 'value', PathToTestFile( 'rustup-toolchain',
174+
'lib',
175+
'rustlib',
176+
'src',
177+
'rust',
178+
'src' ) ) ) ) ) )

ycmd/tests/rust/testdata/rustup-toolchain/lib/rustlib/src/rust/src/libstd/path.rs

Whitespace-only changes.

0 commit comments

Comments
 (0)