mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1469 lines
54 KiB
1469 lines
54 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.Collections.ObjectModel; |
|
using System.IO; |
|
using System.Linq; |
|
|
|
using ICSharpCode.NRefactory; |
|
using ICSharpCode.NRefactory.Ast; |
|
using ICSharpCode.NRefactory.Visitors; |
|
using ICSharpCode.SharpDevelop.Dom.CSharp; |
|
using ICSharpCode.SharpDevelop.Dom.VBNet; |
|
using NR = ICSharpCode.NRefactory; |
|
|
|
namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver |
|
{ |
|
/// <summary> |
|
/// NRefactoryResolver implements the IResolver interface for the NRefactory languages (C# and VB). |
|
/// </summary> |
|
/// <remarks> |
|
/// About implementing code-completion for other languages: |
|
/// |
|
/// It possible to convert from your AST to NRefactory (to C# or VB) (or even let your parser create |
|
/// NRefactory AST objects directly), but then code-completion might be incorrect when the rules of your language |
|
/// differ from the C#/VB language rules. |
|
/// If you want to correctly implement code-completion for your own language, you should implement your own resolver. |
|
/// </remarks> |
|
public class NRefactoryResolver : IResolver |
|
{ |
|
ICompilationUnit cu; |
|
IClass callingClass; |
|
IMember callingMember; |
|
ICSharpCode.NRefactory.Visitors.LookupTableVisitor lookupTableVisitor; |
|
IProjectContent projectContent; |
|
|
|
readonly NR.SupportedLanguage language; |
|
|
|
int caretLine; |
|
int caretColumn; |
|
|
|
bool inferAllowed; |
|
internal bool allowMethodGroupResolveResult; |
|
|
|
public NR.SupportedLanguage Language { |
|
get { |
|
return language; |
|
} |
|
} |
|
|
|
public IProjectContent ProjectContent { |
|
get { |
|
return projectContent; |
|
} |
|
set { |
|
if (value == null) |
|
throw new ArgumentNullException("value"); |
|
projectContent = value; |
|
} |
|
} |
|
|
|
public ICompilationUnit CompilationUnit { |
|
get { |
|
return cu; |
|
} |
|
} |
|
|
|
public IClass CallingClass { |
|
get { |
|
return callingClass; |
|
} |
|
} |
|
|
|
public IMember CallingMember { |
|
get { |
|
return callingMember; |
|
} |
|
} |
|
|
|
public int CaretLine { |
|
get { |
|
return caretLine; |
|
} |
|
} |
|
|
|
public int CaretColumn { |
|
get { |
|
return caretColumn; |
|
} |
|
} |
|
|
|
readonly LanguageProperties languageProperties; |
|
|
|
public LanguageProperties LanguageProperties { |
|
get { |
|
return languageProperties; |
|
} |
|
} |
|
|
|
public NRefactoryResolver(LanguageProperties languageProperties) |
|
{ |
|
if (languageProperties == null) |
|
throw new ArgumentNullException("languageProperties"); |
|
this.languageProperties = languageProperties; |
|
if (languageProperties is LanguageProperties.CSharpProperties) { |
|
language = NR.SupportedLanguage.CSharp; |
|
inferAllowed = true; |
|
allowMethodGroupResolveResult = true; |
|
} else if (languageProperties is LanguageProperties.VBNetProperties) { |
|
language = NR.SupportedLanguage.VBNet; |
|
inferAllowed = false; |
|
allowMethodGroupResolveResult = false; |
|
} else { |
|
throw new NotSupportedException("The language " + languageProperties.ToString() + " is not supported in the NRefactoryResolver"); |
|
} |
|
} |
|
|
|
Expression ParseExpression(string expression, int caretColumnOffset) |
|
{ |
|
Expression expr = SpecialConstructs(expression); |
|
if (expr == null) { |
|
// SEMICOLON HACK: Parsing expressions without trailing semicolon does not work correctly |
|
if (language == NR.SupportedLanguage.CSharp && !expression.EndsWith(";")) |
|
expression += ";"; |
|
using (NR.IParser p = NR.ParserFactory.CreateParser(language, new System.IO.StringReader(expression))) { |
|
p.Lexer.SetInitialLocation(new NR.Location(caretColumn + caretColumnOffset, caretLine)); |
|
expr = p.ParseExpression(); |
|
} |
|
} |
|
return expr; |
|
} |
|
|
|
|
|
string GetFixedExpression(ExpressionResult expressionResult) |
|
{ |
|
string expression = expressionResult.Expression; |
|
if (expression == null) { |
|
expression = ""; |
|
} |
|
expression = expression.TrimStart(); |
|
|
|
return expression; |
|
} |
|
|
|
public bool Initialize(ParseInformation parseInfo, int caretLine, int caretColumn) |
|
{ |
|
this.caretLine = caretLine; |
|
this.caretColumn = caretColumn; |
|
callingClass = null; |
|
callingMember = null; |
|
|
|
if (parseInfo == null) { |
|
return false; |
|
} |
|
|
|
cu = parseInfo.CompilationUnit; |
|
if (cu == null || cu.ProjectContent == null) { |
|
return false; |
|
} |
|
this.ProjectContent = cu.ProjectContent; |
|
|
|
if (language == SupportedLanguage.VBNet) { |
|
IVBNetOptionProvider provider = (IVBNetOptionProvider)cu; |
|
|
|
inferAllowed = provider.OptionInfer ?? false; |
|
} |
|
|
|
callingClass = cu.GetInnermostClass(caretLine, caretColumn); |
|
callingMember = GetCallingMember(); |
|
return true; |
|
} |
|
|
|
public ResolveResult Resolve(ExpressionResult expressionResult, |
|
ParseInformation parseInfo, |
|
string fileContent) |
|
{ |
|
string expression = GetFixedExpression(expressionResult); |
|
|
|
if (!Initialize(parseInfo, expressionResult.Region.BeginLine, expressionResult.Region.BeginColumn)) |
|
return null; |
|
|
|
Expression expr = null; |
|
if (language == NR.SupportedLanguage.VBNet) { |
|
if (expression.Length == 0 || expression[0] == '.' && (expression.Length > 1 && !char.IsDigit(expression[1]))) { |
|
return WithResolve(expression, fileContent); |
|
} else if ("global".Equals(expression, StringComparison.InvariantCultureIgnoreCase)) { |
|
return new NamespaceResolveResult(null, null, ""); |
|
} |
|
// array |
|
} |
|
if (expressionResult.Context.IsTypeContext && !expressionResult.Context.IsObjectCreation) { |
|
expr = ParseTypeReference(expression, language); |
|
} |
|
if (expr == null) { |
|
expr = ParseExpression(expression, 0); |
|
if (expr == null) { |
|
return null; |
|
} |
|
|
|
// "new" is missing |
|
if (expressionResult.Context.IsObjectCreation && !(expr is ObjectCreateExpression)) { |
|
Expression tmp = expr; |
|
while (tmp != null) { |
|
if (tmp is IdentifierExpression) |
|
return ResolveInternal(expr, ExpressionContext.Type); |
|
if (tmp is MemberReferenceExpression) |
|
tmp = (tmp as MemberReferenceExpression).TargetObject; |
|
else |
|
break; |
|
} |
|
expr = ParseExpression("new " + expression, -4); |
|
if (expr == null) { |
|
return null; |
|
} |
|
} |
|
} |
|
|
|
ResolveResult rr; |
|
if (expressionResult.Context == ExpressionContext.Attribute) { |
|
return ResolveAttribute(expr, new NR.Location(caretColumn, caretLine)); |
|
} else if (expressionResult.Context == CSharpExpressionContext.ObjectInitializer && expr is IdentifierExpression) { |
|
bool isCollectionInitializer; |
|
rr = ResolveObjectInitializer((expr as IdentifierExpression).Identifier, fileContent, out isCollectionInitializer); |
|
if (!isCollectionInitializer || rr != null) { |
|
return rr; |
|
} |
|
} |
|
|
|
RunLookupTableVisitor(fileContent); |
|
|
|
rr = CtrlSpaceResolveHelper.GetResultFromDeclarationLine(callingClass, callingMember as IMethodOrProperty, caretLine, caretColumn, expressionResult); |
|
if (rr != null) return rr; |
|
|
|
return ResolveInternal(expr, expressionResult.Context); |
|
} |
|
|
|
TypeReferenceExpression ParseTypeReference(string typeReference, SupportedLanguage language) |
|
{ |
|
string typeOfFunc = "typeof"; |
|
|
|
if (language == SupportedLanguage.VBNet) |
|
typeOfFunc = "GetType"; |
|
|
|
TypeOfExpression toe = ParseExpression(typeOfFunc + "(" + typeReference + ")", -(typeOfFunc.Length + 1)) as TypeOfExpression; |
|
|
|
if (toe != null) |
|
return new TypeReferenceExpression(toe.TypeReference); |
|
|
|
return null; |
|
} |
|
|
|
ResolveResult WithResolve(string expression, string fileContent) |
|
{ |
|
RunLookupTableVisitor(fileContent); |
|
|
|
WithStatement innermost = null; |
|
if (lookupTableVisitor.WithStatements != null) { |
|
foreach (WithStatement with in lookupTableVisitor.WithStatements) { |
|
if (IsInside(new NR.Location(caretColumn, caretLine), with.StartLocation, with.EndLocation)) { |
|
innermost = with; |
|
} |
|
} |
|
} |
|
if (innermost != null) { |
|
if (expression.Length > 1) { |
|
Expression expr = ParseExpression(DummyFindVisitor.dummyName + expression, -DummyFindVisitor.dummyName.Length); |
|
if (expr == null) return null; |
|
DummyFindVisitor v = new DummyFindVisitor(); |
|
expr.AcceptVisitor(v, null); |
|
if (v.result == null) return null; |
|
v.result.TargetObject = innermost.Expression; |
|
return ResolveInternal(expr, ExpressionContext.Default); |
|
} else { |
|
return ResolveInternal(innermost.Expression, ExpressionContext.Default); |
|
} |
|
} else { |
|
return null; |
|
} |
|
} |
|
private class DummyFindVisitor : AbstractAstVisitor { |
|
internal const string dummyName = "___withStatementExpressionDummy"; |
|
internal MemberReferenceExpression result; |
|
public override object VisitMemberReferenceExpression(MemberReferenceExpression fieldReferenceExpression, object data) |
|
{ |
|
IdentifierExpression ie = fieldReferenceExpression.TargetObject as IdentifierExpression; |
|
if (ie != null && ie.Identifier == dummyName) |
|
result = fieldReferenceExpression; |
|
return base.VisitMemberReferenceExpression(fieldReferenceExpression, data); |
|
} |
|
} |
|
|
|
public INode ParseCurrentMember(string fileContent) |
|
{ |
|
CompilationUnit cu = ParseCurrentMemberAsCompilationUnit(fileContent); |
|
if (cu != null && cu.Children.Count > 0) { |
|
TypeDeclaration td = cu.Children[0] as TypeDeclaration; |
|
if (td != null && td.Children.Count > 0) { |
|
return td.Children[0]; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
public CompilationUnit ParseCurrentMemberAsCompilationUnit(string fileContent) |
|
{ |
|
System.IO.TextReader content = ExtractCurrentMethod(fileContent); |
|
if (content != null) { |
|
NR.IParser p = NR.ParserFactory.CreateParser(language, content); |
|
p.Parse(); |
|
return p.CompilationUnit; |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
void RunLookupTableVisitor(string fileContent) |
|
{ |
|
lookupTableVisitor = new LookupTableVisitor(language); |
|
|
|
if (callingMember != null) { |
|
CompilationUnit cu = ParseCurrentMemberAsCompilationUnit(fileContent); |
|
if (cu != null) { |
|
lookupTableVisitor.VisitCompilationUnit(cu, null); |
|
} |
|
} |
|
} |
|
|
|
public void RunLookupTableVisitor(INode currentMemberNode) |
|
{ |
|
if (currentMemberNode == null) |
|
throw new ArgumentNullException("currentMemberNode"); |
|
lookupTableVisitor = new LookupTableVisitor(language); |
|
currentMemberNode.AcceptVisitor(lookupTableVisitor, null); |
|
} |
|
|
|
string GetAttributeName(Expression expr) |
|
{ |
|
if (expr is IdentifierExpression) { |
|
return (expr as IdentifierExpression).Identifier; |
|
} else if (expr is MemberReferenceExpression) { |
|
ResolveVisitor typeVisitor = new ResolveVisitor(this); |
|
MemberReferenceExpression fieldReferenceExpression = (MemberReferenceExpression)expr; |
|
ResolveResult rr = typeVisitor.Resolve(fieldReferenceExpression.TargetObject); |
|
if (rr is NamespaceResolveResult) { |
|
return ((NamespaceResolveResult)rr).Name + "." + fieldReferenceExpression.MemberName; |
|
} |
|
} else if (expr is TypeReferenceExpression) { |
|
return (expr as TypeReferenceExpression).TypeReference.Type; |
|
} |
|
return null; |
|
} |
|
|
|
IClass GetAttribute(string name, NR.Location position) |
|
{ |
|
if (name == null) |
|
return null; |
|
IClass c = SearchClass(name, 0, position); |
|
if (c != null) { |
|
if (c.IsTypeInInheritanceTree(c.ProjectContent.SystemTypes.Attribute.GetUnderlyingClass())) |
|
return c; |
|
} |
|
return SearchClass(name + "Attribute", 0, position); |
|
} |
|
|
|
ResolveResult ResolveAttribute(Expression expr, NR.Location position) |
|
{ |
|
string attributeName = GetAttributeName(expr); |
|
if (attributeName != null) { |
|
IClass c = GetAttribute(attributeName, position); |
|
if (c != null) { |
|
return new TypeResolveResult(callingClass, callingMember, c); |
|
} else { |
|
string ns = SearchNamespace(attributeName, position); |
|
if (ns != null) { |
|
return new NamespaceResolveResult(callingClass, callingMember, ns); |
|
} |
|
} |
|
return new UnknownIdentifierResolveResult(callingClass, callingMember, attributeName); |
|
} else if (expr is InvocationExpression) { |
|
InvocationExpression ie = (InvocationExpression)expr; |
|
attributeName = GetAttributeName(ie.TargetObject); |
|
IClass c = GetAttribute(attributeName, position); |
|
if (c != null) { |
|
ResolveVisitor resolveVisitor = new ResolveVisitor(this); |
|
return resolveVisitor.ResolveConstructorOverload(c, ie.Arguments); |
|
} |
|
return CreateUnknownMethodResolveResult(expr as InvocationExpression); |
|
} |
|
return null; |
|
} |
|
|
|
internal UnknownMethodResolveResult CreateUnknownMethodResolveResult(InvocationExpression invocationExpression) |
|
{ |
|
List<IReturnType> arguments = new List<IReturnType>(); |
|
ResolveVisitor resolveVisitor = new ResolveVisitor(this); |
|
|
|
foreach (Expression ex in invocationExpression.Arguments) { |
|
ResolveResult rr = resolveVisitor.Resolve(ex); |
|
if (rr != null) |
|
arguments.Add(rr.ResolvedType ?? UnknownReturnType.Instance); |
|
else |
|
arguments.Add(UnknownReturnType.Instance); |
|
} |
|
|
|
IReturnType target = null; |
|
|
|
if (callingClass != null) |
|
target = callingClass.DefaultReturnType; |
|
|
|
string methodName = ""; |
|
bool isStatic = false; |
|
|
|
if (invocationExpression.TargetObject is IdentifierExpression) { |
|
IdentifierExpression ie = invocationExpression.TargetObject as IdentifierExpression; |
|
methodName = ie.Identifier; |
|
isStatic = callingMember != null && callingMember.Modifiers.HasFlag(ModifierEnum.Static); |
|
} |
|
|
|
if (invocationExpression.TargetObject is MemberReferenceExpression) { |
|
MemberReferenceExpression mre = invocationExpression.TargetObject as MemberReferenceExpression; |
|
var rr = resolveVisitor.Resolve(mre.TargetObject); |
|
isStatic = rr is TypeResolveResult; |
|
if (rr != null) |
|
target = rr.ResolvedType; |
|
methodName = mre.MemberName; |
|
} |
|
|
|
return new UnknownMethodResolveResult(callingClass, callingMember, target, methodName, isStatic, arguments); |
|
} |
|
|
|
public ResolveResult ResolveInternal(Expression expr, ExpressionContext context) |
|
{ |
|
if (projectContent == null) |
|
throw new InvalidOperationException("Cannot use uninitialized resolver"); |
|
|
|
// we need to special-case this to pass the context to ResolveIdentifier |
|
if (expr is IdentifierExpression) |
|
return ResolveIdentifier(expr as IdentifierExpression, context); |
|
|
|
ResolveVisitor resolveVisitor = new ResolveVisitor(this); |
|
return resolveVisitor.Resolve(expr); |
|
} |
|
|
|
/// <summary> |
|
/// Used for the fix for SD2-511. |
|
/// </summary> |
|
public int LimitMethodExtractionUntilLine; |
|
|
|
public TextReader ExtractCurrentMethod(string fileContent) |
|
{ |
|
if (callingMember == null) |
|
return null; |
|
return ExtractMethod(fileContent, callingMember, language, LimitMethodExtractionUntilLine); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a new class containing only the specified member. |
|
/// This is useful because we only want to parse current method for local variables, |
|
/// as all fields etc. are already prepared in the AST. |
|
/// </summary> |
|
public static TextReader ExtractMethod(string fileContent, IMember member, |
|
NR.SupportedLanguage language, int extractUntilLine) |
|
{ |
|
// As the parse information is always some seconds old, the end line could be wrong |
|
// if the user just inserted a line in the method. |
|
// We can ignore that case because it is sufficient for the parser when the first part of the |
|
// method body is ok. |
|
// Since we are operating directly on the edited buffer, the parser might not be |
|
// able to resolve invalid declarations. |
|
// We can ignore even that because the 'invalid line' is the line the user is currently |
|
// editing, and the declarations he is using are always above that line. |
|
|
|
|
|
// The ExtractMethod-approach has the advantage that the method contents do not have |
|
// do be parsed and stored in memory before they are needed. |
|
// Previous SharpDevelop versions always stored the SharpRefactory[VB] parse tree as 'Tag' |
|
// to the AST CompilationUnit. |
|
// This approach doesn't need that, so one could even go and implement a special parser |
|
// mode that does not parse the method bodies for the normal run (in the ParserUpdateThread or |
|
// SolutionLoadThread). That could improve the parser's speed dramatically. |
|
|
|
if (member.Region.IsEmpty) return null; |
|
int startLine = member.Region.BeginLine; |
|
if (startLine < 1) return null; |
|
DomRegion bodyRegion = member.BodyRegion; |
|
if (bodyRegion.IsEmpty) bodyRegion = member.Region; |
|
int endLine = bodyRegion.EndLine; |
|
|
|
// Fix for SD2-511 (Code completion in inserted line) |
|
if (extractUntilLine > startLine && extractUntilLine < endLine) |
|
endLine = extractUntilLine; |
|
|
|
int offset = 0; |
|
for (int i = 0; i < startLine - 1; ++i) { // -1 because the startLine must be included |
|
offset = fileContent.IndexOf('\n', offset) + 1; |
|
if (offset <= 0) return null; |
|
} |
|
int startOffset = offset; |
|
for (int i = startLine - 1; i < endLine; ++i) { |
|
int newOffset = fileContent.IndexOf('\n', offset) + 1; |
|
if (newOffset <= 0) break; |
|
offset = newOffset; |
|
} |
|
int length = offset - startOffset; |
|
string classDecl, endClassDecl; |
|
if (language == NR.SupportedLanguage.VBNet) { |
|
classDecl = "Class A"; |
|
endClassDecl = "End Class\n"; |
|
} else { |
|
classDecl = "class A {"; |
|
endClassDecl = "}\n"; |
|
} |
|
System.Text.StringBuilder b = new System.Text.StringBuilder(classDecl, length + classDecl.Length + endClassDecl.Length + startLine - 1); |
|
b.Append('\n', startLine - 1); |
|
b.Append(fileContent, startOffset, length); |
|
b.Append(endClassDecl); |
|
return new System.IO.StringReader(b.ToString()); |
|
} |
|
|
|
#region Resolve Identifier |
|
internal IReturnType ConstructType(IReturnType baseType, List<TypeReference> typeArguments) |
|
{ |
|
if (typeArguments == null || typeArguments.Count == 0) |
|
return baseType; |
|
return new ConstructedReturnType(baseType, |
|
typeArguments.ConvertAll(r => TypeVisitor.CreateReturnType(r, this))); |
|
} |
|
|
|
public ResolveResult ResolveIdentifier(IdentifierExpression expr, ExpressionContext context) |
|
{ |
|
ResolveResult result = ResolveIdentifierInternal(expr); |
|
if (result is TypeResolveResult) |
|
return result; |
|
|
|
NR.Location position = expr.StartLocation; |
|
string identifier = expr.Identifier; |
|
ResolveResult result2 = null; |
|
|
|
if (callingClass != null) { |
|
if (callingMember is IMethod) { |
|
foreach (ITypeParameter typeParameter in (callingMember as IMethod).TypeParameters) { |
|
if (IsSameName(identifier, typeParameter.Name)) { |
|
return new TypeResolveResult(callingClass, callingMember, new GenericReturnType(typeParameter)); |
|
} |
|
} |
|
} |
|
foreach (ITypeParameter typeParameter in callingClass.TypeParameters) { |
|
if (IsSameName(identifier, typeParameter.Name)) { |
|
return new TypeResolveResult(callingClass, callingMember, new GenericReturnType(typeParameter)); |
|
} |
|
} |
|
} |
|
|
|
SearchTypeResult typeResult = SearchType(identifier, expr.TypeArguments.Count, position); |
|
IReturnType t = typeResult.Result; |
|
if (t != null) { |
|
result2 = new TypeResolveResult(callingClass, callingMember, ConstructType(t, expr.TypeArguments)); |
|
} else if (result == null && typeResult.NamespaceResult != null) { |
|
return new NamespaceResolveResult(callingClass, callingMember, typeResult.NamespaceResult); |
|
} |
|
|
|
if (result == null && result2 == null) |
|
return new UnknownIdentifierResolveResult(CallingClass, CallingMember, expr.Identifier); |
|
|
|
if (result == null) return result2; |
|
if (result2 == null) return result; |
|
if (context == ExpressionContext.Type) |
|
return result2; |
|
return new MixedResolveResult(result, result2); |
|
} |
|
|
|
public ResolveResult ResolveIdentifier(string identifier, NR.Location position, ExpressionContext context) |
|
{ |
|
return ResolveIdentifier(new IdentifierExpression(identifier) { StartLocation = position }, context); |
|
} |
|
|
|
IField CreateLocalVariableField(LocalLookupVariable variable) |
|
{ |
|
IReturnType type = GetVariableType(variable); |
|
var f = new DefaultField.LocalVariableField(type, variable.Name, DomRegion.FromLocation(variable.StartPos, variable.EndPos), callingClass); |
|
if (variable.IsConst) { |
|
f.Modifiers |= ModifierEnum.Const; |
|
} |
|
return f; |
|
} |
|
|
|
ResolveResult ResolveIdentifierInternal(IdentifierExpression identifierExpression) |
|
{ |
|
NR.Location position = identifierExpression.StartLocation; |
|
string identifier = identifierExpression.Identifier; |
|
if (callingMember != null) { // LocalResolveResult requires callingMember to be set |
|
LocalLookupVariable var = SearchVariable(identifier, position); |
|
if (var != null) { |
|
return new LocalResolveResult(callingMember, CreateLocalVariableField(var)); |
|
} |
|
IParameter para = SearchMethodParameter(identifier); |
|
if (para != null) { |
|
IField field = new DefaultField.ParameterField(para.ReturnType, para.Name, para.Region, callingClass); |
|
return new LocalResolveResult(callingMember, field); |
|
} |
|
if (IsSameName(identifier, "value")) { |
|
IProperty property = callingMember as IProperty; |
|
if (property != null && property.SetterRegion.IsInside(position.Line, position.Column) |
|
|| callingMember is IEvent) |
|
{ |
|
IField field = new DefaultField.ParameterField(callingMember.ReturnType, "value", callingMember.Region, callingClass); |
|
return new LocalResolveResult(callingMember, field); |
|
} |
|
} |
|
} |
|
if (callingClass != null) { |
|
IClass tmp = callingClass; |
|
do { |
|
ResolveResult rr = ResolveMember(tmp.DefaultReturnType, identifier, |
|
identifierExpression.TypeArguments, |
|
IsInvoked(identifierExpression), |
|
false, true); |
|
if (rr != null && rr.IsValid) |
|
return rr; |
|
// also try to resolve the member in outer classes |
|
tmp = tmp.DeclaringType; |
|
} while (tmp != null); |
|
} |
|
|
|
if (languageProperties.CanImportClasses) { |
|
IUsingScope scope = callingClass != null ? callingClass.UsingScope : cu.UsingScope; |
|
for (; scope != null; scope = scope.Parent) { |
|
foreach (IUsing @using in scope.Usings) { |
|
foreach (string import in @using.Usings) { |
|
IClass c = GetClass(import, 0); |
|
if (c != null) { |
|
ResolveResult rr = ResolveMember(c.DefaultReturnType, identifier, |
|
identifierExpression.TypeArguments, |
|
IsInvoked(identifierExpression), |
|
false, null); |
|
if (rr != null && rr.IsValid) |
|
return rr; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (languageProperties.ImportModules) { |
|
List<ICompletionEntry> list = new List<ICompletionEntry>(); |
|
CtrlSpaceResolveHelper.AddImportedNamespaceContents(list, cu, callingClass); |
|
List<IMember> resultMembers = new List<IMember>(); |
|
foreach (ICompletionEntry o in list) { |
|
IClass c = o as IClass; |
|
if (c != null && IsSameName(identifier, c.Name)) { |
|
return new TypeResolveResult(callingClass, callingMember, c); |
|
} |
|
IMember member = o as IMember; |
|
if (member != null && IsSameName(identifier, member.Name)) { |
|
resultMembers.Add(member); |
|
} |
|
} |
|
return CreateMemberOrMethodGroupResolveResult(null, identifier, new IList<IMember>[] { resultMembers }, false, null); |
|
} |
|
|
|
return null; |
|
} |
|
#endregion |
|
|
|
private ResolveResult CreateMemberResolveResult(IMember member) |
|
{ |
|
if (member == null) return null; |
|
return new MemberResolveResult(callingClass, callingMember, member); |
|
} |
|
|
|
#region ResolveMember |
|
internal ResolveResult ResolveMember(IReturnType declaringType, string memberName, |
|
List<TypeReference> typeArguments, bool isInvocation, |
|
bool allowExtensionMethods, bool? isAccessThoughReferenceOfCurrentClass) |
|
{ |
|
// in VB everything can be used with invocation syntax (because the same syntax also accesses indexers), |
|
// don't use 'isInvocation'. |
|
if (language == NR.SupportedLanguage.VBNet) |
|
isInvocation = false; |
|
|
|
List<IList<IMember>> members = MemberLookupHelper.LookupMember(declaringType, memberName, callingClass, languageProperties, isInvocation, isAccessThoughReferenceOfCurrentClass); |
|
List<IReturnType> typeArgs = null; |
|
if (members != null && typeArguments != null && typeArguments.Count != 0) { |
|
typeArgs = typeArguments.ConvertAll(r => TypeVisitor.CreateReturnType(r, this)); |
|
|
|
// For all member-groups: |
|
// Remove all non-methods and methods with incorrect type argument count, then |
|
// apply the type arguments to the remaining methods. |
|
members = members.Select(memberGroup => ApplyTypeArgumentsToMethods(memberGroup, typeArgs)) |
|
.Where(memberGroup => memberGroup.Count > 0) // keep only non-empty groups |
|
.ToList(); |
|
} |
|
if (language == NR.SupportedLanguage.VBNet && members != null && members.Count > 0) { |
|
// use the correct casing of the member name |
|
memberName = members[0][0].Name; |
|
} |
|
return CreateMemberOrMethodGroupResolveResult(declaringType, memberName, members, allowExtensionMethods, typeArgs); |
|
} |
|
|
|
static IList<IMember> ApplyTypeArgumentsToMethods(IList<IMember> memberGroup, IList<IReturnType> typeArgs) |
|
{ |
|
if (typeArgs == null || typeArgs.Count == 0) |
|
return memberGroup; |
|
else |
|
return ApplyTypeArgumentsToMethods(memberGroup.OfType<IMethod>(), typeArgs).Select(m=>(IMember)m).ToList(); |
|
} |
|
|
|
static IEnumerable<IMethod> ApplyTypeArgumentsToMethods(IEnumerable<IMethod> methodGroup, IList<IReturnType> typeArgs) |
|
{ |
|
if (typeArgs == null || typeArgs.Count == 0) |
|
return methodGroup; |
|
// we have type arguments, so: |
|
// - remove all non-methods |
|
// - remove all methods with incorrect type parameter count |
|
// - apply type arguments to remaining methods |
|
return methodGroup |
|
.Where((IMethod m) => m.TypeParameters.Count == typeArgs.Count) |
|
.Select((IMethod originalMethod) => { |
|
IMethod m = (IMethod)originalMethod.CreateSpecializedMember(); |
|
m.ReturnType = ConstructedReturnType.TranslateType(m.ReturnType, typeArgs, true); |
|
for (int j = 0; j < m.Parameters.Count; ++j) { |
|
m.Parameters[j].ReturnType = ConstructedReturnType.TranslateType(m.Parameters[j].ReturnType, typeArgs, true); |
|
} |
|
return m; |
|
}); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a MemberResolveResult or MethodGroupResolveResult. |
|
/// </summary> |
|
/// <param name="declaringType">The type on which to search the member.</param> |
|
/// <param name="memberName">The name of the member.</param> |
|
/// <param name="members">The list of members to put into the MethodGroupResolveResult</param> |
|
/// <param name="allowExtensionMethods">Whether to add extension methods to the resolve result</param> |
|
/// <param name="typeArgs">Type arguments to apply to the extension methods</param> |
|
internal ResolveResult CreateMemberOrMethodGroupResolveResult( |
|
IReturnType declaringType, string memberName, IList<IList<IMember>> members, |
|
bool allowExtensionMethods, IList<IReturnType> typeArgs) |
|
{ |
|
List<MethodGroup> methods = new List<MethodGroup>(); |
|
if (members != null) { |
|
foreach (IList<IMember> memberGroup in members) { |
|
if (memberGroup != null && memberGroup.Count > 0) { |
|
MethodGroup methodGroup = new MethodGroup(); |
|
foreach (IMember m in memberGroup) { |
|
if (m is IMethod) |
|
methodGroup.Add(m as IMethod); |
|
else |
|
return new MemberResolveResult(callingClass, callingMember, m); |
|
} |
|
methods.Add(methodGroup); |
|
} |
|
} |
|
} |
|
if (allowExtensionMethods == false || declaringType == null) { |
|
if (methods.Count == 0) |
|
return null; |
|
else |
|
return new MethodGroupResolveResult(callingClass, callingMember, |
|
declaringType ?? methods[0][0].DeclaringTypeReference, |
|
memberName, methods, language == SupportedLanguage.VBNet, allowMethodGroupResolveResult) { IsVBNetAddressOf = allowMethodGroupResolveResult && language == SupportedLanguage.VBNet }; |
|
} else { |
|
methods.Add( |
|
new MethodGroup( |
|
new LazyList<IMethod>(() => ApplyTypeArgumentsToMethods(SearchExtensionMethods(memberName), typeArgs).ToList()) |
|
) |
|
{ IsExtensionMethodGroup = true }); |
|
return new MethodGroupResolveResult(callingClass, callingMember, |
|
declaringType, |
|
memberName, methods, language == SupportedLanguage.VBNet, allowMethodGroupResolveResult) { IsVBNetAddressOf = allowMethodGroupResolveResult && language == SupportedLanguage.VBNet }; |
|
} |
|
} |
|
#endregion |
|
|
|
#region Resolve In Object Initializer |
|
ResolveResult ResolveObjectInitializer(string identifier, string fileContent, out bool isCollectionInitializer) |
|
{ |
|
foreach (IMember m in ObjectInitializerCtrlSpace(fileContent, out isCollectionInitializer)) { |
|
if (IsSameName(m.Name, identifier)) |
|
return CreateMemberResolveResult(m); |
|
} |
|
return null; |
|
} |
|
|
|
List<IMember> ObjectInitializerCtrlSpace(string fileContent, out bool isCollectionInitializer) |
|
{ |
|
isCollectionInitializer = true; |
|
if (callingMember == null) { |
|
return new List<IMember>(); |
|
} |
|
CompilationUnit parsedCu = ParseCurrentMemberAsCompilationUnit(fileContent); |
|
if (parsedCu == null) { |
|
return new List<IMember>(); |
|
} |
|
return ObjectInitializerCtrlSpace(parsedCu, new NR.Location(caretColumn, caretLine), out isCollectionInitializer); |
|
} |
|
|
|
List<IMember> ObjectInitializerCtrlSpace(CompilationUnit parsedCu, NR.Location location, out bool isCollectionInitializer) |
|
{ |
|
FindObjectInitializerExpressionContainingCaretVisitor v = new FindObjectInitializerExpressionContainingCaretVisitor(location); |
|
parsedCu.AcceptVisitor(v, null); |
|
return ObjectInitializerCtrlSpace(v.result, out isCollectionInitializer); |
|
} |
|
|
|
List<IMember> ObjectInitializerCtrlSpace(CollectionInitializerExpression collectionInitializer, out bool isCollectionInitializer) |
|
{ |
|
isCollectionInitializer = true; |
|
List<IMember> results = new List<IMember>(); |
|
if (collectionInitializer != null) { |
|
ObjectCreateExpression oce = collectionInitializer.Parent as ObjectCreateExpression; |
|
MemberInitializerExpression mie = collectionInitializer.Parent as MemberInitializerExpression; |
|
if (oce != null && !oce.IsAnonymousType) { |
|
IReturnType resolvedType = TypeVisitor.CreateReturnType(oce.CreateType, this); |
|
ObjectInitializerCtrlSpaceInternal(results, resolvedType, out isCollectionInitializer); |
|
} |
|
else if (mie != null) { |
|
IMember member = ResolveMemberInitializerExpressionInObjectInitializer(mie); |
|
if (member != null) { |
|
ObjectInitializerCtrlSpaceInternal(results, member.ReturnType, out isCollectionInitializer); |
|
} |
|
} |
|
} |
|
return results; |
|
} |
|
|
|
IMember ResolveMemberInitializerExpressionInObjectInitializer(MemberInitializerExpression mie) |
|
{ |
|
CollectionInitializerExpression parentCI = mie.Parent as CollectionInitializerExpression; |
|
bool tmp; |
|
return ObjectInitializerCtrlSpace(parentCI, out tmp).Find(m => IsSameName(m.Name, mie.Name)); |
|
} |
|
|
|
void ObjectInitializerCtrlSpaceInternal(List<IMember> results, IReturnType resolvedType, out bool isCollectionInitializer) |
|
{ |
|
isCollectionInitializer = MemberLookupHelper.ConversionExists(resolvedType, new GetClassReturnType(projectContent, "System.Collections.IEnumerable", 0)); |
|
if (resolvedType != null) { |
|
bool isClassInInheritanceTree = false; |
|
if (callingClass != null) |
|
isClassInInheritanceTree = callingClass.IsTypeInInheritanceTree(resolvedType.GetUnderlyingClass()); |
|
foreach (IField f in resolvedType.GetFields()) { |
|
if (languageProperties.ShowMember(f, false) |
|
&& f.IsAccessible(callingClass, isClassInInheritanceTree) |
|
&& !(f.IsReadonly && IsValueType(f.ReturnType))) |
|
{ |
|
results.Add(f); |
|
} |
|
} |
|
foreach (IProperty p in resolvedType.GetProperties()) { |
|
if (languageProperties.ShowMember(p, false) |
|
&& p.IsAccessible(callingClass, isClassInInheritanceTree) |
|
&& !(p.CanSet == false && IsValueType(p.ReturnType))) |
|
{ |
|
results.Add(p); |
|
} |
|
} |
|
} |
|
} |
|
|
|
static bool IsValueType(IReturnType rt) |
|
{ |
|
if (rt == null) |
|
return false; |
|
IClass c = rt.GetUnderlyingClass(); |
|
return c != null && (c.ClassType == ClassType.Struct || c.ClassType == ClassType.Enum); |
|
} |
|
|
|
// Finds the inner most CollectionInitializerExpression containing the specified caret position |
|
sealed class FindObjectInitializerExpressionContainingCaretVisitor : AbstractAstVisitor |
|
{ |
|
NR.Location caretPosition; |
|
internal CollectionInitializerExpression result; |
|
|
|
public FindObjectInitializerExpressionContainingCaretVisitor(ICSharpCode.NRefactory.Location caretPosition) |
|
{ |
|
this.caretPosition = caretPosition; |
|
} |
|
|
|
public override object VisitCollectionInitializerExpression(CollectionInitializerExpression collectionInitializerExpression, object data) |
|
{ |
|
base.VisitCollectionInitializerExpression(collectionInitializerExpression, data); |
|
if (result == null |
|
&& collectionInitializerExpression.StartLocation <= caretPosition |
|
&& collectionInitializerExpression.EndLocation >= caretPosition) |
|
{ |
|
result = collectionInitializerExpression; |
|
} |
|
return null; |
|
} |
|
} |
|
#endregion |
|
|
|
Expression SpecialConstructs(string expression) |
|
{ |
|
if (language == NR.SupportedLanguage.VBNet) { |
|
// MyBase and MyClass are no expressions, only MyBase.Identifier and MyClass.Identifier |
|
if ("mybase".Equals(expression, StringComparison.InvariantCultureIgnoreCase)) { |
|
return new BaseReferenceExpression(); |
|
} else if ("myclass".Equals(expression, StringComparison.InvariantCultureIgnoreCase)) { |
|
return new ClassReferenceExpression(); |
|
} // Global is handled in Resolve() because we don't need an expression for that |
|
} |
|
return null; |
|
} |
|
|
|
public bool IsSameName(string name1, string name2) |
|
{ |
|
return languageProperties.NameComparer.Equals(name1, name2); |
|
} |
|
|
|
bool IsInside(NR.Location between, NR.Location start, NR.Location end) |
|
{ |
|
if (between.Y < start.Y || between.Y > end.Y) { |
|
return false; |
|
} |
|
if (between.Y > start.Y) { |
|
if (between.Y < end.Y) { |
|
return true; |
|
} |
|
// between.Y == end.Y |
|
return between.X <= end.X; |
|
} |
|
// between.Y == start.Y |
|
if (between.X < start.X) { |
|
return false; |
|
} |
|
// start is OK and between.Y <= end.Y |
|
return between.Y < end.Y || between.X <= end.X; |
|
} |
|
|
|
IMember GetCallingMember() |
|
{ |
|
if (callingClass == null) |
|
return null; |
|
foreach (IMethod method in callingClass.Methods) { |
|
if (method.Region.IsInside(caretLine, caretColumn) || method.BodyRegion.IsInside(caretLine, caretColumn)) { |
|
return method; |
|
} |
|
} |
|
foreach (IProperty property in callingClass.Properties) { |
|
if (property.Region.IsInside(caretLine, caretColumn) || property.BodyRegion.IsInside(caretLine, caretColumn)) { |
|
return property; |
|
} |
|
} |
|
foreach (IEvent ev in callingClass.Events) { |
|
if (ev.Region.IsInside(caretLine, caretColumn) || ev.BodyRegion.IsInside(caretLine, caretColumn)) { |
|
return ev; |
|
} |
|
} |
|
foreach (IField f in callingClass.Fields) { |
|
if (f.Region.IsInside(caretLine, caretColumn) || f.BodyRegion.IsInside(caretLine, caretColumn)) { |
|
return f; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
/// <remarks> |
|
/// use the usings to find the correct name of a namespace |
|
/// </remarks> |
|
public string SearchNamespace(string name, NR.Location position) |
|
{ |
|
return SearchType(name, 0, position).NamespaceResult; |
|
} |
|
|
|
public IClass GetClass(string fullName, int typeArgumentCount) |
|
{ |
|
return projectContent.GetClass(fullName, typeArgumentCount); |
|
} |
|
|
|
/// <remarks> |
|
/// use the usings and the name of the namespace to find a class |
|
/// </remarks> |
|
public IClass SearchClass(string name, int typeArgumentCount, NR.Location position) |
|
{ |
|
IReturnType t = SearchType(name, typeArgumentCount, position).Result; |
|
return (t != null) ? t.GetUnderlyingClass() : null; |
|
} |
|
|
|
public SearchTypeResult SearchType(string name, int typeArgumentCount, NR.Location position) |
|
{ |
|
if (position.IsEmpty) |
|
return projectContent.SearchType(new SearchTypeRequest(name, typeArgumentCount, callingClass, cu, caretLine, caretColumn)); |
|
else |
|
return projectContent.SearchType(new SearchTypeRequest(name, typeArgumentCount, callingClass, cu, position.Line, position.Column)); |
|
} |
|
|
|
public IList<IMethod> SearchExtensionMethods(string name) |
|
{ |
|
List<IMethod> results = new List<IMethod>(); |
|
foreach (IMethodOrProperty m in SearchAllExtensionMethods()) { |
|
if (IsSameName(name, m.Name)) { |
|
results.Add((IMethod)m); |
|
} |
|
} |
|
return results; |
|
} |
|
|
|
ReadOnlyCollection<IMethodOrProperty> cachedExtensionMethods; |
|
IClass cachedExtensionMethods_LastClass; // invalidate cache when callingClass != LastClass |
|
|
|
public ReadOnlyCollection<IMethodOrProperty> SearchAllExtensionMethods(bool searchInAllNamespaces = false) |
|
{ |
|
if (callingClass == null) |
|
return EmptyList<IMethodOrProperty>.Instance; |
|
if (callingClass != cachedExtensionMethods_LastClass) { |
|
cachedExtensionMethods_LastClass = callingClass; |
|
cachedExtensionMethods = new ReadOnlyCollection<IMethodOrProperty>(CtrlSpaceResolveHelper.FindAllExtensions(languageProperties, callingClass, searchInAllNamespaces)); |
|
} |
|
return cachedExtensionMethods; |
|
} |
|
|
|
#region DynamicLookup |
|
IParameter SearchMethodParameter(string parameter) |
|
{ |
|
IMethodOrProperty method = callingMember as IMethodOrProperty; |
|
if (method == null) { |
|
return null; |
|
} |
|
foreach (IParameter p in method.Parameters) { |
|
if (IsSameName(p.Name, parameter)) { |
|
return p; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
Dictionary<LocalLookupVariable, IReturnType> variableReturnTypeCache = new Dictionary<LocalLookupVariable, IReturnType>(); |
|
|
|
IReturnType GetVariableType(LocalLookupVariable v) |
|
{ |
|
if (v == null) { |
|
return null; |
|
} |
|
|
|
if (v.ParentLambdaExpression != null && (v.TypeRef == null || v.TypeRef.IsNull)) { |
|
IReturnType[] lambdaParameterTypes = GetImplicitLambdaParameterTypes(v.ParentLambdaExpression); |
|
if (lambdaParameterTypes != null) { |
|
int parameterIndex = v.ParentLambdaExpression.Parameters.FindIndex(p => p.ParameterName == v.Name); |
|
if (parameterIndex >= 0 && parameterIndex < lambdaParameterTypes.Length) { |
|
return lambdaParameterTypes[parameterIndex]; |
|
} |
|
} |
|
} |
|
|
|
// Don't create multiple IReturnType's for the same local variable. |
|
// This is required to ensure that the protection against infinite recursion |
|
// for type inference cycles in InferredReturnType works correctly. |
|
// It also helps performance if we infer every local variable only once. |
|
IReturnType rt; |
|
if (variableReturnTypeCache.TryGetValue(v, out rt)) |
|
return rt; |
|
|
|
if (v.TypeRef == null || v.TypeRef.IsNull || v.TypeRef.Type == "var") { |
|
if (inferAllowed) { |
|
if (v.ParentLambdaExpression != null) { |
|
rt = new LambdaParameterReturnType(v.ParentLambdaExpression, v.Name, this); |
|
} else { |
|
rt = new InferredReturnType(v.Initializer, this); |
|
if (v.IsLoopVariable) { |
|
rt = new ElementReturnType(this.projectContent, rt); |
|
} |
|
} |
|
} else { |
|
rt = this.projectContent.SystemTypes.Object; |
|
} |
|
} else { |
|
rt = TypeVisitor.CreateReturnType(v.TypeRef, this); |
|
} |
|
variableReturnTypeCache[v] = rt; |
|
return rt; |
|
} |
|
|
|
LocalLookupVariable SearchVariable(string name, NR.Location position) |
|
{ |
|
if (lookupTableVisitor == null || !lookupTableVisitor.Variables.ContainsKey(name)) |
|
return null; |
|
List<LocalLookupVariable> variables = lookupTableVisitor.Variables[name]; |
|
if (variables.Count <= 0) { |
|
return null; |
|
} |
|
|
|
foreach (LocalLookupVariable v in variables) { |
|
if (IsInside(position, v.StartPos, v.EndPos)) { |
|
return v; |
|
} |
|
} |
|
return null; |
|
} |
|
#endregion |
|
|
|
IClass GetPrimitiveClass(string systemType, string newName) |
|
{ |
|
IClass c = projectContent.GetClass(systemType, 0); |
|
if (c == null) |
|
return null; |
|
DefaultClass c2 = new DefaultClass(c.CompilationUnit, newName); |
|
c2.ClassType = c.ClassType; |
|
c2.Modifiers = c.Modifiers; |
|
c2.Documentation = c.Documentation; |
|
c2.BaseTypes.AddRange(c.BaseTypes); |
|
c2.Methods.AddRange(c.Methods); |
|
c2.Fields.AddRange(c.Fields); |
|
c2.Properties.AddRange(c.Properties); |
|
c2.Events.AddRange(c.Events); |
|
return c2; |
|
} |
|
|
|
static void AddCSharpKeywords(List<ICompletionEntry> ar, BitArray keywords) |
|
{ |
|
for (int i = 0; i < keywords.Length; i++) { |
|
if (keywords[i]) { |
|
ar.Add(new KeywordEntry(NR.Parser.CSharp.Tokens.GetTokenString(i))); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Returns code completion entries for given context. |
|
/// </summary> |
|
/// <param name="caretLine"></param> |
|
/// <param name="caretColumn"></param> |
|
/// <param name="parseInfo"></param> |
|
/// <param name="fileContent"></param> |
|
/// <param name="context"></param> |
|
/// <param name="showEntriesFromAllNamespaces">If true, returns entries from all namespaces, regardless of current imports.</param> |
|
/// <returns></returns> |
|
public List<ICompletionEntry> CtrlSpace(int caretLine, int caretColumn, ParseInformation parseInfo, string fileContent, ExpressionContext context, bool showEntriesFromAllNamespaces = false) |
|
{ |
|
if (!Initialize(parseInfo, caretLine, caretColumn)) |
|
return null; |
|
|
|
List<ICompletionEntry> result = new List<ICompletionEntry>(); |
|
if (language == NR.SupportedLanguage.VBNet) { |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} else { |
|
if (context == ExpressionContext.TypeDeclaration) { |
|
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.TypeLevel); |
|
AddCSharpPrimitiveTypes(result); |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} else if (context == CSharpExpressionContext.InterfaceDeclaration) { |
|
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.InterfaceLevel); |
|
AddCSharpPrimitiveTypes(result); |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} else if (context == ExpressionContext.MethodBody) { |
|
result.Add(new KeywordEntry("var")); |
|
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.StatementStart); |
|
AddCSharpPrimitiveTypes(result); |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} else if (context == ExpressionContext.Global) { |
|
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.GlobalLevel); |
|
} else if (context == CSharpExpressionContext.InterfacePropertyDeclaration) { |
|
result.Add(new KeywordEntry("get")); |
|
result.Add(new KeywordEntry("set")); |
|
} else if (context == CSharpExpressionContext.BaseConstructorCall) { |
|
result.Add(new KeywordEntry("this")); |
|
result.Add(new KeywordEntry("base")); |
|
} else if (context == CSharpExpressionContext.ConstraintsStart) { |
|
result.Add(new KeywordEntry("where")); |
|
} else if (context == CSharpExpressionContext.Constraints) { |
|
result.Add(new KeywordEntry("where")); |
|
result.Add(new KeywordEntry("new")); |
|
result.Add(new KeywordEntry("struct")); |
|
result.Add(new KeywordEntry("class")); |
|
AddCSharpPrimitiveTypes(result); |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} else if (context == ExpressionContext.InheritableType) { |
|
result.Add(new KeywordEntry("where")); // the inheritance list can be followed by constraints |
|
AddCSharpPrimitiveTypes(result); |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} else if (context == CSharpExpressionContext.PropertyDeclaration) { |
|
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.InPropertyDeclaration); |
|
} else if (context == CSharpExpressionContext.EventDeclaration) { |
|
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.InEventDeclaration); |
|
} else if (context == CSharpExpressionContext.FullyQualifiedType) { |
|
cu.ProjectContent.AddNamespaceContents(result, "", languageProperties, true); |
|
} else if (context == CSharpExpressionContext.ParameterType || context == CSharpExpressionContext.FirstParameterType) { |
|
result.Add(new KeywordEntry("ref")); |
|
result.Add(new KeywordEntry("out")); |
|
result.Add(new KeywordEntry("params")); |
|
if (context == CSharpExpressionContext.FirstParameterType && languageProperties.SupportsExtensionMethods) { |
|
if (callingMember != null && callingMember.IsStatic) { |
|
result.Add(new KeywordEntry("this")); |
|
} |
|
} |
|
AddCSharpPrimitiveTypes(result); |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} else if (context == CSharpExpressionContext.ObjectInitializer) { |
|
bool isCollectionInitializer; |
|
result.AddRange(ObjectInitializerCtrlSpace(fileContent, out isCollectionInitializer)); |
|
if (isCollectionInitializer) { |
|
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.ExpressionStart); |
|
AddCSharpPrimitiveTypes(result); |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} |
|
} else if (context == ExpressionContext.Attribute) { |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
result.Add(new KeywordEntry("assembly")); |
|
result.Add(new KeywordEntry("module")); |
|
result.Add(new KeywordEntry("field")); |
|
result.Add(new KeywordEntry("event")); |
|
result.Add(new KeywordEntry("method")); |
|
result.Add(new KeywordEntry("param")); |
|
result.Add(new KeywordEntry("property")); |
|
result.Add(new KeywordEntry("return")); |
|
result.Add(new KeywordEntry("type")); |
|
} else if (context == ExpressionContext.Default) { |
|
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.ExpressionStart); |
|
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.ExpressionContent); |
|
AddCSharpPrimitiveTypes(result); |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} else { |
|
// e.g. some ExpressionContext.TypeDerivingFrom() |
|
AddCSharpPrimitiveTypes(result); |
|
CtrlSpaceInternal(result, fileContent, showEntriesFromAllNamespaces); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
void AddCSharpPrimitiveTypes(List<ICompletionEntry> result) |
|
{ |
|
foreach (KeyValuePair<string, string> pair in TypeReference.PrimitiveTypesCSharp) { |
|
IClass c = GetPrimitiveClass(pair.Value, pair.Key); |
|
if (c != null) result.Add(c); |
|
} |
|
} |
|
|
|
void CtrlSpaceInternal(List<ICompletionEntry> result, string fileContent, bool showEntriesFromAllNamespaces) |
|
{ |
|
lookupTableVisitor = new LookupTableVisitor(language); |
|
|
|
if (callingMember != null) { |
|
CompilationUnit parsedCu = ParseCurrentMemberAsCompilationUnit(fileContent); |
|
if (parsedCu != null) { |
|
lookupTableVisitor.VisitCompilationUnit(parsedCu, null); |
|
} |
|
} |
|
|
|
CtrlSpaceResolveHelper.AddContentsFromCalling(result, callingClass, callingMember); |
|
|
|
foreach (KeyValuePair<string, List<LocalLookupVariable>> pair in lookupTableVisitor.Variables) { |
|
if (pair.Value != null && pair.Value.Count > 0) { |
|
foreach (LocalLookupVariable v in pair.Value) { |
|
if (IsInside(new NR.Location(caretColumn, caretLine), v.StartPos, v.EndPos)) { |
|
// convert to a field for display |
|
result.Add(CreateLocalVariableField(v)); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
if (callingMember is IProperty) { |
|
IProperty property = (IProperty)callingMember; |
|
if (property.SetterRegion.IsInside(caretLine, caretColumn)) { |
|
result.Add(new DefaultField.ParameterField(property.ReturnType, "value", property.Region, callingClass)); |
|
} |
|
} |
|
if (callingMember is IEvent) { |
|
result.Add(new DefaultField.ParameterField(callingMember.ReturnType, "value", callingMember.Region, callingClass)); |
|
} |
|
|
|
if (showEntriesFromAllNamespaces) { |
|
// CC contains contents of all referenced assemblies |
|
CtrlSpaceResolveHelper.AddReferencedProjectsContents(result, cu, callingClass); |
|
} else { |
|
// CC contains contents of all imported namespaces |
|
CtrlSpaceResolveHelper.AddImportedNamespaceContents(result, cu, callingClass); |
|
} |
|
} |
|
|
|
sealed class CompareLambdaByLocation : IEqualityComparer<LambdaExpression> |
|
{ |
|
public bool Equals(LambdaExpression x, LambdaExpression y) |
|
{ |
|
return x.StartLocation == y.StartLocation && x.EndLocation == y.EndLocation; |
|
} |
|
|
|
public int GetHashCode(LambdaExpression obj) |
|
{ |
|
return unchecked (8123351 * obj.StartLocation.GetHashCode() + obj.EndLocation.GetHashCode()); |
|
} |
|
} |
|
|
|
Dictionary<LambdaExpression, IReturnType[]> lambdaParameterTypes = new Dictionary<LambdaExpression, IReturnType[]>(new CompareLambdaByLocation()); |
|
|
|
internal void SetImplicitLambdaParameterTypes(LambdaExpression lambda, IReturnType[] types) |
|
{ |
|
lambdaParameterTypes[lambda] = types; |
|
} |
|
|
|
internal void UnsetImplicitLambdaParameterTypes(LambdaExpression lambda) |
|
{ |
|
lambdaParameterTypes.Remove(lambda); |
|
} |
|
|
|
IReturnType[] GetImplicitLambdaParameterTypes(LambdaExpression lambda) |
|
{ |
|
IReturnType[] types; |
|
if (lambdaParameterTypes.TryGetValue(lambda, out types)) |
|
return types; |
|
else |
|
return null; |
|
} |
|
|
|
internal static bool IsInvoked(Expression expr) |
|
{ |
|
InvocationExpression ie = expr.Parent as InvocationExpression; |
|
if (ie != null) { |
|
return ie.TargetObject == expr; |
|
} |
|
return false; |
|
} |
|
|
|
public IReturnType GetExpectedTypeFromContext(Expression expr) |
|
{ |
|
if (expr == null) |
|
return null; |
|
|
|
InvocationExpression ie = expr.Parent as InvocationExpression; |
|
if (ie != null) { |
|
int index = ie.Arguments.IndexOf(expr); |
|
if (index < 0) |
|
return null; |
|
|
|
ResolveResult rr = ResolveInternal(ie, ExpressionContext.Default); |
|
IMethod m; |
|
if (rr is MemberResolveResult) { |
|
m = (rr as MemberResolveResult).ResolvedMember as IMethod; |
|
if ((rr as MemberResolveResult).IsExtensionMethodCall) |
|
index++; |
|
} else if (rr is DelegateCallResolveResult) { |
|
m = (rr as DelegateCallResolveResult).DelegateInvokeMethod; |
|
} else { |
|
m = null; |
|
} |
|
if (m != null && index < m.Parameters.Count) |
|
return m.Parameters[index].ReturnType; |
|
} |
|
|
|
ObjectCreateExpression oce = expr.Parent as ObjectCreateExpression; |
|
if (oce != null) { |
|
int index = oce.Parameters.IndexOf(expr); |
|
if (index < 0) |
|
return null; |
|
|
|
MemberResolveResult mrr = ResolveInternal(oce, ExpressionContext.Default) as MemberResolveResult; |
|
if (mrr != null) { |
|
IMethod m = mrr.ResolvedMember as IMethod; |
|
if (m != null && index < m.Parameters.Count) |
|
return m.Parameters[index].ReturnType; |
|
} |
|
} |
|
|
|
if (expr.Parent is CastExpression) { |
|
ResolveResult rr = ResolveInternal((Expression)expr.Parent, ExpressionContext.Default); |
|
if (rr != null) |
|
return rr.ResolvedType; |
|
} else if (expr.Parent is LambdaExpression) { |
|
IReturnType delegateType = GetExpectedTypeFromContext(expr.Parent as Expression); |
|
IMethod sig = CSharp.TypeInference.GetDelegateOrExpressionTreeSignature(delegateType, true); |
|
if (sig != null) |
|
return sig.ReturnType; |
|
} else if (expr.Parent is ReturnStatement) { |
|
Expression lambda = GetParentAnonymousMethodOrLambda(expr.Parent); |
|
if (lambda != null) { |
|
IReturnType delegateType = GetExpectedTypeFromContext(lambda); |
|
IMethod sig = CSharp.TypeInference.GetDelegateOrExpressionTreeSignature(delegateType, true); |
|
if (sig != null) |
|
return sig.ReturnType; |
|
} else { |
|
if (callingMember != null) |
|
return callingMember.ReturnType; |
|
} |
|
} else if (expr.Parent is ParenthesizedExpression) { |
|
return GetExpectedTypeFromContext(expr.Parent as Expression); |
|
} else if (expr.Parent is VariableDeclaration) { |
|
return GetTypeFromVariableDeclaration((VariableDeclaration)expr.Parent); |
|
} else if (expr.Parent is AssignmentExpression) { |
|
ResolveResult rr = ResolveInternal((expr.Parent as AssignmentExpression).Left, ExpressionContext.Default); |
|
if (rr != null) |
|
return rr.ResolvedType; |
|
} else if (expr.Parent is MemberInitializerExpression) { |
|
IMember m = ResolveMemberInitializerExpressionInObjectInitializer((MemberInitializerExpression)expr.Parent); |
|
if (m != null) |
|
return m.ReturnType; |
|
} else if (expr.Parent is CollectionInitializerExpression) { |
|
IReturnType collectionType; |
|
if (expr.Parent.Parent is ObjectCreateExpression) |
|
collectionType = TypeVisitor.CreateReturnType(((ObjectCreateExpression)expr.Parent.Parent).CreateType, this); |
|
else if (expr.Parent.Parent is ArrayCreateExpression) |
|
collectionType = TypeVisitor.CreateReturnType(((ArrayCreateExpression)expr.Parent.Parent).CreateType, this); |
|
else if (expr.Parent.Parent is VariableDeclaration) |
|
collectionType = GetTypeFromVariableDeclaration((VariableDeclaration)expr.Parent.Parent); |
|
else |
|
collectionType = null; |
|
if (collectionType != null) |
|
return new ElementReturnType(projectContent, collectionType); |
|
} else if (expr.Parent is IndexerExpression) { |
|
IndexerExpression indexerExpr = expr.Parent as IndexerExpression; |
|
MemberResolveResult indexer = ResolveInternal(indexerExpr, ExpressionContext.Default) |
|
as MemberResolveResult; |
|
|
|
if (indexer != null && indexer.ResolvedMember is IProperty) { |
|
IProperty p = indexer.ResolvedMember as IProperty; |
|
int position = indexerExpr.Indexes.IndexOf(expr); |
|
if (position < 0 || position >= p.Parameters.Count) |
|
return null; |
|
return p.Parameters[position].ReturnType; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
Expression GetParentAnonymousMethodOrLambda(INode node) |
|
{ |
|
while (node != null) { |
|
if (node is AnonymousMethodExpression || node is LambdaExpression) |
|
return (Expression)node; |
|
node = node.Parent; |
|
} |
|
return null; |
|
} |
|
|
|
IReturnType GetTypeFromVariableDeclaration(VariableDeclaration varDecl) |
|
{ |
|
TypeReference typeRef = varDecl.TypeReference; |
|
if (typeRef == null || typeRef.IsNull) { |
|
LocalVariableDeclaration lvd = varDecl.Parent as LocalVariableDeclaration; |
|
if (lvd != null) typeRef = lvd.TypeReference; |
|
FieldDeclaration fd = varDecl.Parent as FieldDeclaration; |
|
if (fd != null) typeRef = fd.TypeReference; |
|
} |
|
return TypeVisitor.CreateReturnType(typeRef, this); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Used in CtrlSpace-results to represent a keyword. |
|
/// </summary> |
|
public class KeywordEntry : ICompletionEntry |
|
{ |
|
public string Name { get; private set; } |
|
|
|
public KeywordEntry(string name) |
|
{ |
|
if (name == null) |
|
throw new ArgumentNullException("name"); |
|
this.Name = name; |
|
} |
|
|
|
public override int GetHashCode() |
|
{ |
|
return Name.GetHashCode(); |
|
} |
|
|
|
public override bool Equals(object obj) |
|
{ |
|
KeywordEntry e = obj as KeywordEntry; |
|
return e != null && e.Name == this.Name; |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
return Name; |
|
} |
|
} |
|
}
|
|
|