public override object TrackedVisitAssignmentExpression(AssignmentExpression assignmentExpression, object data) { // If we are in a property set region, // this may be the statement where the field value is assigned. if (this.associatedMember == null && // skip if already found to improve performance this.currentContext == VisitorContext.PropertySetRegion && assignmentExpression.Op == AssignmentOperatorType.Assign && data != null) { // Resolve the expression. if (!FileUtility.IsEqualFileName(this.FileName, this.memberToFind.DeclaringType.CompilationUnit.FileName)) { throw new InvalidOperationException("The PropertyFieldAssociationVisitor does currently not support the case that the field is declared in a different file than the property."); } MemberResolveResult mrr = this.Resolve(assignmentExpression.Left) as MemberResolveResult; if (mrr != null && mrr.ResolvedMember is IField && !((IField)mrr.ResolvedMember).IsLocalVariable) { PropertyDeclaration pd; #if DEBUG LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertySetRegion, resolved field: " + mrr.ResolvedMember.ToString()); #endif if (data as bool? ?? false) { // We are looking for this property. #if DEBUG LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertySetRegion, this property seems to reference field " + mrr.ResolvedMember.ToString()); #endif this.associatedMember = mrr.ResolvedMember; } else if ((pd = (data as PropertyDeclaration)) != null) { // We are looking for the field in this.memberToFind. if (this.memberToFind.CompareTo(mrr.ResolvedMember) == 0) { // Resolve the property. MemberResolveResult prr = NRefactoryAstCacheService.ResolveLowLevel(this.FileName, this.FileContent, pd.StartLocation.Y, pd.StartLocation.X + 1, null, pd.Name, ExpressionContext.Default) as MemberResolveResult; if (prr != null) { #if DEBUG LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertySetRegion, resolved property: " + prr.ResolvedMember.ToString()); #endif if (prr.ResolvedMember is IProperty) { #if DEBUG LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertySetRegion, property " + prr.ResolvedMember.ToString() + " seems to reference field " + mrr.ResolvedMember.ToString()); #endif this.associatedMember = prr.ResolvedMember; } } } } } } return(base.TrackedVisitAssignmentExpression(assignmentExpression, data)); }
/// <summary> /// Resolves an expression in the current node's context. /// </summary> /// <param name="expression">The expression to be resolved.</param> /// <param name="context">The ExpressionContext.</param> public ResolveResult Resolve(Expression expression, ExpressionContext context) { if (!this.PositionAvailable) { LoggingService.Info("ResourceToolkit: PositionTrackingAstVisitor: Resolve failed due to position information being unavailable. Expression: " + expression.ToString()); return(null); } #if DEBUG LoggingService.Debug("ResourceToolkit: PositionTrackingAstVisitor: Using this parent node for resolve: " + this.parentNodes.Peek().ToString()); #endif return(NRefactoryAstCacheService.ResolveLowLevel(this.fileName, this.fileContent, this.CurrentNodeStartLocation.Y, this.CurrentNodeStartLocation.X + 1, this.compilationUnit, null, expression, context)); }
/// <summary> /// Tries to determine the resource set which is referenced by the /// resource manager which is assigned to the specified member. /// </summary> /// <param name="member">The referenced member to examine.</param> /// <returns> /// The ResourceSetReference, if successful, or a null reference, if the /// specified member is not a resource manager or if the /// resource file cannot be determined. /// </returns> static ResourceSetReference ResolveResourceSet(IMember member) { if (member != null && member.ReturnType != null && member.DeclaringType != null && member.DeclaringType.CompilationUnit != null) { ResourceSetReference rsr; if (!NRefactoryAstCacheService.CacheEnabled || !cachedResourceSetReferenceMappings.TryGetValue(member, out rsr)) { string declaringFileName = member.DeclaringType.CompilationUnit.FileName; if (declaringFileName != null) { if (IsResourceManager(member.ReturnType, declaringFileName)) { SupportedLanguage?language = NRefactoryResourceResolver.GetFileLanguage(declaringFileName); if (language == null) { return(null); } CompilationUnit cu = NRefactoryAstCacheService.GetFullAst(language.Value, declaringFileName, ResourceResolverService.GetParsableFileContent(declaringFileName)); if (cu != null) { ResourceManagerInitializationFindVisitor visitor = new ResourceManagerInitializationFindVisitor(member); cu.AcceptVisitor(visitor, null); if (visitor.FoundResourceSet != null) { rsr = visitor.FoundResourceSet; if (NRefactoryAstCacheService.CacheEnabled) { cachedResourceSetReferenceMappings.Add(member, rsr); } return(rsr); } } } } return(null); } return(rsr); } return(null); }
// ******************************************************************************************************************************** /// <summary> /// Tries to resolve the resource reference using all available /// NRefactory resource resolvers. /// </summary> static ResourceResolveResult TryResolve(ExpressionResult result, Expression expr, int caretLine, int caretColumn, string fileName, string fileContent, IExpressionFinder expressionFinder, char?charTyped) { ResolveResult rr = NRefactoryAstCacheService.ResolveLowLevel(fileName, fileContent, caretLine + 1, caretColumn + 1, null, result.Expression, expr, result.Context); if (rr != null) { ResourceResolveResult rrr; foreach (INRefactoryResourceResolver resolver in Resolvers) { if ((rrr = resolver.Resolve(result, expr, rr, caretLine, caretColumn, fileName, fileContent, expressionFinder, charTyped)) != null) { return(rrr); } } } return(null); }
public override object TrackedVisitReturnStatement(ReturnStatement returnStatement, object data) { // If we are in a property get region, // this may be the statement where the field value is returned. if (this.associatedMember == null && // skip if already found to improve performance this.currentContext == VisitorContext.PropertyGetRegion && data != null) { // Fix some type casting and parenthesized expressions Expression expr = returnStatement.Expression; while (true) { CastExpression ce = expr as CastExpression; if (ce != null) { expr = ce.Expression; continue; } ParenthesizedExpression pe = expr as ParenthesizedExpression; if (pe != null) { expr = pe.Expression; continue; } break; } // Resolve the expression. if (!FileUtility.IsEqualFileName(this.FileName, this.memberToFind.DeclaringType.CompilationUnit.FileName)) { throw new InvalidOperationException("The PropertyFieldAssociationVisitor does currently not support the case that the field is declared in a different file than the property."); } MemberResolveResult mrr = this.Resolve(expr) as MemberResolveResult; if (mrr != null && mrr.ResolvedMember is IField) { PropertyDeclaration pd; #if DEBUG LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertyGetRegion, resolved field: " + mrr.ResolvedMember.ToString()); #endif if (data as bool? ?? false) { // We are looking for this property. #if DEBUG LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertyGetRegion, this property seems to reference field " + mrr.ResolvedMember.ToString()); #endif this.associatedMember = mrr.ResolvedMember; } else if ((pd = (data as PropertyDeclaration)) != null) { // We are looking for the field in this.memberToFind. if (this.memberToFind.CompareTo(mrr.ResolvedMember) == 0) { // Resolve the property. MemberResolveResult prr = NRefactoryAstCacheService.ResolveLowLevel(this.FileName, this.FileContent, pd.StartLocation.Y, pd.StartLocation.X + 1, null, pd.Name, ExpressionContext.Default) as MemberResolveResult; if (prr != null) { #if DEBUG LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertyGetRegion, resolved property: " + prr.ResolvedMember.ToString()); #endif if (prr.ResolvedMember is IProperty) { #if DEBUG LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertyGetRegion, property " + prr.ResolvedMember.ToString() + " seems to reference field " + mrr.ResolvedMember.ToString()); #endif this.associatedMember = prr.ResolvedMember; } } } } } } return(base.TrackedVisitReturnStatement(returnStatement, data)); }
// ******************************************************************************************************************************** /// <summary> /// Attempts to resolve a reference to a resource. /// </summary> /// <param name="fileName">The name of the file that contains the expression to be resolved.</param> /// <param name="document">The document that contains the expression to be resolved.</param> /// <param name="caretLine">The 0-based line in the file that contains the expression to be resolved.</param> /// <param name="caretColumn">The 0-based column position of the expression to be resolved.</param> /// <param name="caretOffset">The offset of the position of the expression to be resolved.</param> /// <param name="charTyped">The character that has been typed at the caret position but is not yet in the buffer (this is used when invoked from code completion), or <c>null</c>.</param> /// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the specified position in the specified file, or <c>null</c> if that expression does not reference a (known) resource.</returns> protected override ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn, int caretOffset, char?charTyped) { IExpressionFinder ef = ResourceResolverService.GetExpressionFinder(fileName); if (ef == null) { return(null); } bool foundStringLiteral = false; while (true) { ExpressionResult result = ef.FindFullExpression(document.Text, caretOffset); if (result.Expression == null) { // Try to find an expression to the left, but only // in the same line. if (foundStringLiteral || --caretOffset < 0) { return(null); } var line = document.GetLineForOffset(caretOffset); if (line.LineNumber - 1 != caretLine) { return(null); } continue; } if (!result.Region.IsEmpty) { caretLine = result.Region.BeginLine - 1; caretColumn = result.Region.BeginColumn - 1; } PrimitiveExpression pe; Expression expr = NRefactoryAstCacheService.ParseExpression(fileName, result.Expression, caretLine + 1, caretColumn + 1); if (expr == null) { return(null); } else if ((pe = expr as PrimitiveExpression) != null) { if (pe.Value is string) { if (foundStringLiteral) { return(null); } // We are inside a string literal and need to find // the next outer expression to decide // whether it is a resource key. if (!result.Region.IsEmpty) { // Go back to the start of the string literal - 2. caretOffset = document.PositionToOffset(result.Region.BeginLine, result.Region.BeginColumn) - 2; if (caretOffset < 0) { return(null); } } else { LoggingService.Debug("ResourceToolkit: NRefactoryResourceResolver: Found string literal, but result region is empty. Trying to infer position from text."); int newCaretOffset = document.GetText(0, Math.Min(document.TextLength, caretOffset + result.Expression.Length)).LastIndexOf(result.Expression); if (newCaretOffset == -1) { LoggingService.Warn("ResourceToolkit: NRefactoryResourceResolver: Could not find resolved expression in text."); --caretOffset; continue; } else { caretOffset = newCaretOffset; } } foundStringLiteral = true; continue; } else { return(null); } } return(TryResolve(result, expr, caretLine, caretColumn, fileName, document.Text, ef, charTyped)); } }
/// <summary> /// Tries to find a resource reference in the specified expression. /// </summary> /// <param name="expressionResult">The ExpressionResult for the expression.</param> /// <param name="expr">The AST representation of the full expression.</param> /// <param name="resolveResult">SharpDevelop's ResolveResult for the expression.</param> /// <param name="caretLine">The 0-based line where the expression is located.</param> /// <param name="caretColumn">The 0-based column where the expression is located.</param> /// <param name="fileName">The name of the source file where the expression is located.</param> /// <param name="fileContent">The content of the source file where the expression is located.</param> /// <param name="expressionFinder">The ExpressionFinder for the file.</param> /// <param name="charTyped">The character that has been typed at the caret position but is not yet in the buffer (this is used when invoked from code completion), or <c>null</c>.</param> /// <returns>A ResourceResolveResult describing the referenced resource, or <c>null</c>, if this expression does not reference a resource using the standard .NET framework classes.</returns> public ResourceResolveResult Resolve(ExpressionResult expressionResult, Expression expr, ResolveResult resolveResult, int caretLine, int caretColumn, string fileName, string fileContent, IExpressionFinder expressionFinder, char?charTyped) { /* * We need to catch the following cases here: * * Something.GetString( * Something.GetString("...") * Something.ApplyResources(obj, "...") * Something[ * Something["..."] * */ if (charTyped == '(') { // Something.GetString // This is a MethodResolveResult and we need the reference to "Something", // which is the next outer expression. // This is only valid when invoked from code completion // and the method invocation character ('(' in C# and VB) // has been typed. // This code is also reused when reducing a complete InvocationExpression // (MemberResolveResult) to the method reference by passing '(' as // charTyped explicitly. MethodGroupResolveResult methrr = resolveResult as MethodGroupResolveResult; if (methrr != null) { if ((methrr.Name == "GetString" || methrr.Name == "GetObject" || methrr.Name == "GetStream" || methrr.Name == "ApplyResources") && (resolveResult = NRefactoryAstCacheService.ResolveNextOuterExpression(ref expressionResult, caretLine, caretColumn, fileName, fileContent, expressionFinder)) != null) { return(ResolveResource(resolveResult, expr)); } else { return(null); } } } // Do not use "else if" here. // '(' is also the IndexerExpressionStartToken for VB, // so the "else" block further down might still apply. if (charTyped == null) { // A MemberResolveResult with a complete expression // must only be considered a valid resource reference // when Resolve is not invoked from code completion // (i.e. charTyped == null) because this indicates // that the resource reference is already before the typed character // and we are not interested in the following expression. // This may happen when typing something like: // Something.GetString("...")[ MemberResolveResult mrr = resolveResult as MemberResolveResult; if (mrr != null) { if (mrr.ResolvedMember is IMethod && (mrr.ResolvedMember.Name == "GetString" || mrr.ResolvedMember.Name == "GetObject" || mrr.ResolvedMember.Name == "GetStream" || mrr.ResolvedMember.Name == "ApplyResources")) { // Something.GetString("...") // This is a MemberResolveResult and we need the reference to "Something". // The expression finder may only remove the string literal, so // we have to call Resolve again in this case to resolve // the method reference. if ((resolveResult = NRefactoryAstCacheService.ResolveNextOuterExpression(ref expressionResult, caretLine, caretColumn, fileName, fileContent, expressionFinder)) != null) { if (resolveResult is MethodGroupResolveResult) { return(this.Resolve(expressionResult, expr, resolveResult, caretLine, caretColumn, fileName, fileContent, expressionFinder, '(')); } else { return(ResolveResource(resolveResult, expr)); } } else { return(null); } } else if (expr is IndexerExpression && IsResourceManager(mrr.ResolvedMember.DeclaringType.DefaultReturnType, fileName)) { // Something["..."] is an IndexerExpression. // We need the reference to Something and this is // the next outer expression. if ((resolveResult = NRefactoryAstCacheService.ResolveNextOuterExpression(ref expressionResult, caretLine, caretColumn, fileName, fileContent, expressionFinder)) != null) { return(ResolveResource(resolveResult, expr)); } else { return(null); } } } } else { // This request is triggered from code completion. // The only case that has not been caught above is: // Something[ // The reference to "Something" is already in this expression. // So we have to test the trigger character against the // indexer expression start token of the file's language. LanguageProperties lp = NRefactoryResourceResolver.GetLanguagePropertiesForFile(fileName); if (lp != null && !String.IsNullOrEmpty(lp.IndexerExpressionStartToken) && lp.IndexerExpressionStartToken[0] == charTyped) { #if DEBUG LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver: Indexer expression start typed, ResolveResult: " + resolveResult.ToString()); LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver: -> Expression: " + expr.ToString()); #endif return(ResolveResource(resolveResult, expr)); } } return(null); }