Fixed null ref exception in GetSignatureHelp (#175)

- Fixed a few places where ScriptParseInfo was being used before it was verified to be non-null
- Added 1 basic test for GetSignatureHelp scenario
This commit is contained in:
Connor Quagliana
2016-12-09 15:18:08 -08:00
committed by Kevin Cunnane
parent 431dfa4156
commit 40c1434745
3 changed files with 62 additions and 7 deletions

View File

@@ -847,6 +847,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri); ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri);
if (scriptParseInfo == null)
{
// Cache not set up yet - skip and wait until later
return null;
}
ConnectionInfo connInfo; ConnectionInfo connInfo;
LanguageService.ConnectionServiceInstance.TryFindConnection( LanguageService.ConnectionServiceInstance.TryFindConnection(
scriptFile.ClientFilePath, scriptFile.ClientFilePath,
@@ -858,7 +864,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
ParseAndBind(scriptFile, connInfo); ParseAndBind(scriptFile, connInfo);
} }
if (scriptParseInfo != null && scriptParseInfo.ParseResult != null) if (scriptParseInfo.ParseResult != null)
{ {
if (Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock)) if (Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock))
{ {
@@ -928,13 +934,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// get the current script parse info object // get the current script parse info object
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri); ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri);
ScriptDocumentInfo scriptDocumentInfo = new ScriptDocumentInfo(textDocumentPosition, scriptFile, scriptParseInfo);
if (scriptParseInfo == null) if (scriptParseInfo == null)
{ {
return AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, useLowerCaseSuggestions); return AutoCompleteHelper.GetDefaultCompletionItems(ScriptDocumentInfo.CreateDefaultDocumentInfo(textDocumentPosition, scriptFile), useLowerCaseSuggestions);
} }
ScriptDocumentInfo scriptDocumentInfo = new ScriptDocumentInfo(textDocumentPosition, scriptFile, scriptParseInfo);
// reparse and bind the SQL statement if needed // reparse and bind the SQL statement if needed
if (RequiresReparse(scriptParseInfo, scriptFile)) if (RequiresReparse(scriptParseInfo, scriptFile))
{ {

View File

@@ -18,6 +18,16 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion
/// Create new instance /// Create new instance
/// </summary> /// </summary>
public ScriptDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile, ScriptParseInfo scriptParseInfo) public ScriptDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile, ScriptParseInfo scriptParseInfo)
: this(textDocumentPosition, scriptFile)
{
Validate.IsNotNull(nameof(scriptParseInfo), scriptParseInfo);
ScriptParseInfo = scriptParseInfo;
// need to adjust line & column for base-1 parser indices
Token = GetToken(scriptParseInfo, ParserLine, ParserColumn);
}
private ScriptDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile)
{ {
StartLine = textDocumentPosition.Position.Line; StartLine = textDocumentPosition.Position.Line;
ParserLine = textDocumentPosition.Position.Line + 1; ParserLine = textDocumentPosition.Position.Line + 1;
@@ -30,11 +40,18 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion
textDocumentPosition.Position.Line, textDocumentPosition.Position.Line,
textDocumentPosition.Position.Character); textDocumentPosition.Position.Character);
ParserColumn = textDocumentPosition.Position.Character + 1; ParserColumn = textDocumentPosition.Position.Character + 1;
ScriptParseInfo = scriptParseInfo;
Contents = scriptFile.Contents; Contents = scriptFile.Contents;
}
// need to adjust line & column for base-1 parser indices /// <summary>
Token = GetToken(scriptParseInfo, ParserLine, ParserColumn); /// Creates a new <see cref="ScriptDocumentInfo"/> with no backing <see cref="ScriptParseInfo"/> defined
/// </summary>
/// <param name="textDocumentPosition">A <see cref="TextDocumentPosition"/></param>
/// <param name="scriptFile">A <see cref="ScriptFile"/> to process</param>
/// <returns></returns>
public static ScriptDocumentInfo CreateDefaultDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile)
{
return new ScriptDocumentInfo(textDocumentPosition, scriptFile);
} }
/// <summary> /// <summary>

View File

@@ -5,6 +5,7 @@
using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.LanguageServices; using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts; using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using Microsoft.SqlTools.Test.Utility; using Microsoft.SqlTools.Test.Utility;
using Xunit; using Xunit;
@@ -126,6 +127,36 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServer
Assert.Equal(3, fileMarkers[1].ScriptRegion.EndLineNumber); Assert.Equal(3, fileMarkers[1].ScriptRegion.EndLineNumber);
} }
[Fact]
public void GetSignatureHelpReturnsNullIfParseInfoNotInitialized()
{
// Given service doesn't have parseinfo intialized for a document
const string docContent = "SELECT * FROM sys.objects";
LanguageService service = TestObjects.GetTestLanguageService();
var scriptFile = new ScriptFile();
scriptFile.SetFileContents(docContent);
// When requesting SignatureHelp
SignatureHelp signatureHelp = service.GetSignatureHelp(CreateDummyDocPosition(), scriptFile);
// Then null is returned as no parse info can be used to find the signature
Assert.Null(signatureHelp);
}
private TextDocumentPosition CreateDummyDocPosition()
{
return new TextDocumentPosition
{
TextDocument = new TextDocumentIdentifier { Uri = TestObjects.ScriptUri },
Position = new Position
{
Line = 0,
Character = 0
}
};
}
#endregion #endregion
#region "General Language Service tests" #region "General Language Service tests"