Get symbol info from new node - c#

I have a CSharpSyntaxRewriter that overrides VisitMemberAccessExpression, inside that method, I am calling MemberAccessExpressionSyntax.WithName(), but the node it returns has a different SyntaxTree compared to the original node, this is a problem since it means an error is thrown when calling SemanticModel.GetSymbolInfo(node).
Is there a way to change the Name of a MemberAccessExpressionSyntax but still have a SyntaxTree that works with SemanticModel.GetSymbolInfo(node)?
My code is:
public sealed override SyntaxNode VisitNode(SyntaxNode node)
{
var nodeSyntax = (MemberAccessExpressionSyntax) node;
if (nodeSyntax.Name.ToString() == OldModifier && !HasSymbol(nodeSyntax, out _))
{
if (ModifierType == ModifierType.Damage)
nodeSyntax = nodeSyntax.WithName(IdentifierName($"GetDamage({NewModifier})"));
else
nodeSyntax = nodeSyntax.WithName(IdentifierName($"GetCritChance({NewModifier})"));
nodeSyntax = nodeSyntax.WithLeadingTrivia(node.GetLeadingTrivia()).WithTrailingTrivia(node.GetTrailingTrivia());
}
return nodeSyntax;
}
(VisitNode is called from VisitMemberAccessExpression)
And here are images showing the difference in the SyntaxTree:
original:
After calling WithName:

There's a few ways to approach something like this:
Change Your Visit Method
Can you restructure your code to call VisitNode before you've done any changes to VisitMemberAccessExpression? For example can you go from:
var memberAccess = memberAccess.With...();
memberAccess = VisitNode(memberAccess);
return memberAccess;
to
var memberAccess = VisitNode(memberAccess);
memberAccess = memberAccess.With...();
return memberAccess;
Then it might mean that you are then visiting with the original node before you've done rewriting. Note this can still be tricky though if you are doing recursive stuff.
Use ReplaceNodes
There's a helper method you can use instead of a rewriter. This will call the function you pass to do rewriting at each step, but that function is handed both the original node in the original tree and the node after child nodes have been rewritten; you can use the original one to ask binding questions (since that's from the original tree) and consume the one that's been rewritten for recursive rewriting.
Break Your Problem into Two Steps
Do two walks: first get a list of all the places will need to update, add those SyntaxNodes to a HashSet or like, and then do the rewrite second where you are updating those places.

Related

Creating BlockSyntax from string (Roslyn)

I have some string with source code like
var newSource = #"
int a = 5;
int b = 10;
Console.WriteLine(a + b);";
I try to create BlockSyntax object with parsed code
var newTokens = SyntaxFactory.ParseTokens(newSource);
var newBody = SyntaxFactory.Block();
newBody = newBody.InsertTokensAfter(
newBody.OpenBraceToken, // or newBody.ChildTokens().First()
newTokens
);
But method InsertTokenAfter throws System.InvalidOperationException 'The item specified is not the element of a list.'
As I understand it, the method can not find a token in ChildTokens(), but why it happens?
.NET Core 1.0.4
The InsertTokensAfter method will not work for adding statements. It only allows you to insert tokens in an existing list of tokens (which is a specific construct that occurs in the C# syntax tree only in the modifier list of a declaration).
Statements are nodes. You can insert one or more nodes in a list of statements using InsertNodesAfter, but you would have to have an existing statement node in the list already to do this, and in your example you have an empty block that does not yet have any statements.
You could use the block.WithStatements() method, passing it a list of statements directly, if you had a list of statements. Unfortunately, there is no SyntaxFactory.ParseStatementList method.
There is, however, a SyntaxFactory.ParseStatement method, but that only parses one statement. Fortunately, a block is a statement. So you can just add a pair of braces around the source for your statements, and parse that as a block.
var block = (BlockSyntax)SyntaxFactory.ParseStatement("{" + newSource + "}");
During debug you can find a class
public abstract partial class CodeFixVerifier : DiagnosticVerifier
in the TestHelper namespace. Your code fails in the ApplyFix(Document document, CodeAction codeAction) method. I suppose the clue is in the document parameter: changes should be applied to the document, but your newBody is not attached yet.
If you are interested in the fix of your code - you can apply a code like
StatementSyntax newTokens = SyntaxFactory.ParseStatement(newSource);
BlockSyntax block = SyntaxFactory.Block(newTokens);

Roslyn Find Same Node in Changed Document

As we all know Roslyn Syntax Trees are Immutable, so after making changes you need to get a new node.
I'm trying to update a document using the document editor, but I keep getting an error that the node is not found in the syntax tree.
public static T FindEquivalentNode<T>(this Document newDocument, T node)
where T : CSharpSyntaxNode
{
var root = newDocument.GetSyntaxRootAsync().Result;
return root.DescendantNodes().OfType<T>()
.FirstOrDefault(newNode => SyntaxFactory.AreEquivalent(newNode, node));
}
When I try to Call this again the document editor:
var newFieldDeclaration = documentEditor.GetChangedDocument().FindEquivalentNode(syntaxNode);
documentEditor.ReplaceNode(newFieldDeclaration, propertyDeclaration);
I get an error:
The node is not part of the tree
The newField Declaration is not null it find an equivalent field yet I still get this error, How Can I Replace this node?
You get the node because in your FindEquivalentNode method, you are doing that:
SyntaxFactory.AreEquivalent(newNode, node)
SyntaxFactory.AreEquivalent is not return true for "real" same nodes, but for nodes\tokens that are seems equal in their structure (with consideration of topLevel parameter).
Back to your question, if you want to call ReplaceNode you must have the "real" old node, so you wouldn't get an
The node is not part of the tree
To achive that, you have a few options, one of them is what #Tamas wrote in comments, use SyntaxAnnotations.
Example:
//Add annotation to node
var annotation = new SyntaxAnnotation("your_annotation", "some_data");
node = node .WithAdditionalAnnotations(annotation);
// Now you can change the tree as much you want
// And when you want to get the node from the changed tree
var annotatedNode = someNode.DescendantNodesAndSelf().
FirstOrDefault(n => n.GetAnnotations("your_annotation").Any())

How to get Invoked method name in Roslyn?

I have a code like this;
class abc{
void A()
{
//Some to do statements
}
void B()
{
var obj=A();
}
void C()
{
var call=B();
}
}
I want to find the Invoked method name using roslyn.
like here o/p will be:
for method B :Invoked method A
for method C:Invoked method A
i want something like this:
root.DescendantNodes().OfType<InvocationExpressionSyntax>().Where(md => md.Identifier.ValueText.Equals(methodName)).FirstOrDefault();
but InvocationExpression doesn't contain Identifier to access.
How to get identifier name
I agree with SLaks and would add to his answer, if what you want is:
"I want the method invocation syntax nodes where the receiver syntax -- let me emphasize SYNTAX -- is a single identifier equal to some particular string"
then the query you want is something like
var root = tree.GetRoot();
var nodes = from node in root.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
let id = node.Expression as IdentifierNameSyntax
where id != null
where id.Identifier.ValueText == methodName
select node;
But I think that is maybe not what you want. If what you want is "I want to know exactly which method is invoked regardless of whether it is written A(...) or Foo.Bar.A(...) then you don't want to be considering the syntax at all. You'll need to use the semantic analyzer to get the symbol associated with the invocation, and then check whether that symbol identifies the method you're interested in.
Are you primarily interested in a syntactic or a semantic analysis here? Roslyn does either, but you're only looking at syntax in the code you've presented.
If what you're looking for is a semantic analysis then you want to do something like this:
var tree = CSharpSyntaxTree.ParseText(code);
var c = CSharpCompilation.Create("asm.dll");
c = c.AddSyntaxTrees(tree);
var model = c.GetSemanticModel(tree);
string methodName = "A";
var root = tree.GetRoot();
var symbols = from node in root.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
let symbol = model.GetSymbolInfo(node.Expression).Symbol as IMethodSymbol
where symbol != null
where symbol.Name = methodName
select symbol;
The reason InvocationExpressionSyntax does not have Identifier because it may have with this case var obj = this.A() so it's reason why InvocationExpressionSyntax will contain property Expression with type ExpressionSyntax (which may be IdentifierNameSyntax type),
1.So simple answer for your case is you can access property Expression like this.
foreach (var item in root.DescendantNodes()
.OfType<InvocationExpressionSyntax>())
{
var expr = item.Expression;
if (expr is IdentifierNameSyntax)
{
IdentifierNameSyntax identifierName = r as IdentifierNameSyntax; // identifierName is your method name
}
if (expr is MemberAccessExpressionSyntax)
{
MemberAccessExpressionSyntax memberAccessExpressionSyntax = r as MemberAccessExpressionSyntax;
//memberAccessExpressionSyntax.Name is your method name
}
}
2.but for exact lookup the invocation method is the same with method you declared you have to use Semantic information as in this example in Roslyn project
https://github.com/dotnet/roslyn/wiki/FAQ#how-do-i-find-all-calls-in-a-compilation-into-a-particular-namespace
You're misunderstanding the role of these nodes in the syntax tree.
You should use the Roslyn Syntax Visualizer in VS to see what the nodes for a method call actually look like.
In short: An InvocationExpression just means someExpression(some arguments). That expression can be a number of things, such as a constructor call, a member access, an array access, or any other expression that resolves to a method or delegate.
You need to check the Kind of the InvocationExpressionSyntax and figure out how you want to handle every possible expression.

Extension method for Null handling not working on linq for xml

I have an nullexception issue when trying to get the value of an xml tag, which is under a subtree that may not be there.
The extension handler works great when it can't find a tag on an existing subtree, but seems to not be able to handle when looking for a tag in a subtree that doesn't exist.
In this case, the subtree is summaryData, which may or not be there, and trying to get addressLine1 is where it doesn't handle the null, and I get the error
System.NullReferenceException occurred, Message=Object reference not
set to an instance of an object.
Here is the xml, cut down for clarity, but structure is correct:
<record>
<accounts>
<account >
</account >
</accounts>
<summaryData>
<Date>2013-02-04</Date>
<address >
<city>Little Rock</city>
<postalCode>00000</postalCode>
<state>AR</state>
<addressLine1>Frank St</addressLine1>
</serviceAddress>
</summaryData>
</record>
My C# code is:
xmlDoc.Descendants("account")
//select (string)c.Element("account") ;
select new
{
//this works fine
Stuffinxml = c.Element("stuffinxml").Value,
//this field may not be there, but the handler handlers the exception correctly here when it as the correct root (account)
otherstuff = CustXmlHelp.GetElementValue(mR.Element("otherstuff")),
//this is the problem, where the summaryData root does not exist (or moved somewhere else)
street_address = GetElementValue(c.Element("summaryData").Element("serviceAddress").Element("addressLine1"))
};
My extension method to handle a null is:
public static string GetElementValue(this XElement element)
{
if (element != null)
{
return element.Value;
}
else
{
return string.Empty;
}
}
Any help would be appreciated, as I can't see why it fails when the subtree does not exist.
The summary data may or may not be there
That's why. As you're nesting calls, you'll have to null check them all.
Any one of these could be null:
c.Element("summaryData").Element("serviceAddress").Element("addressLine1")
Without a complex conditional, there's not a nice way around it:
street_address = c.Element("summaryData") != null
? c.Element("summaryData").Element("serviceAddress") != null
? GetElementValue(c.Element("summaryData").Element("serviceAddress").Element("addressLine1"))
: string.Empty
: string.Empty;
If the summaryDate element does not exist then
c.Element("summaryData").Element("serviceAddress").Element("addressLine1")
will throw a NullReferenceException because you're trying to call Element() on a null reference (c.Element("summaryData"))
As had been stated, your exception is due to the fact that you are passing multiple nested queries
c.Element("summaryData").Element("serviceAddress").Element("addressLine1")
is the equivalant of writing:
var x1 = c.Element("summaryData");
var x2 = x1.Element("serviceAddress")
var x3 = x2.Element("addressLine1")
So if any of c, x1, or x2 are null, you are going to get a NullReferenceException.
One possible alternative to a pure LINQ solution using multiple null checks, is to use XPath to build the expression.
With XPath, rather than doing a null check at every level, you can instead write your expression:
street_address = GetElementValue(c.XPathSelectElement("/summaryData/serviceAddress/addressLine1"))
This will evaluate the entire expression and if it does not exist in its entirety, it will return null, but will not thrown an exception like your pure LINQ query.

How to evaluate and process a simple string syntax-tree in C#?

I have a corpus of token-index based documents which offers a query method. The user manually(!) enters a query string which needs to be parsed and evaluated. The corpus should then return a list of all documents matching the given query string. The query language features the simple boolean operators AND, NOT and OR which can also be prioritized by parenthesis.
After some research I already used ANTLR to parse a given query string into a syntax tree.
For example: The query
"Bill OR (John AND Jim) OR (NOT Simon AND Mike)"
is translated in the following syntax tree:
EDIT: Please see the correct graph in Bart Kiers post (copied here):
All nodes in the tree are simple strings and each node knows its parent and children but not its siblings.
As you can see, the ANTLR grammar already dictated the order in which the operations need to be executed: the ones at the bottom of the tree come first.
So what I probably need to do is recusively(?) evaluate all operands in the tree.
In general, I can do a simple search on my corpus using a method Get(string term) for each leaf in the tree (like "Bill" or "John"). Get() returns a list of documents containing the term in the leaf. I can also evaluate the parent of each leaf to recognize a possible NOT operator which would then lead to a result list of documents NOT containing the term in the leaf (using the method Not() instead of Get()).
The AND and OR operator should be transformed into method calls which need two parameters:
AND should call a method Intersect(list1, list2) which returns a list of documents that are in list1 AND in list2.
OR should call a method Union(list1, list2) which returns a list of documents that are either in list1 OR in list2.
The parameters list1 and list2 contain the documents I received before using Get() or Not().
My question is: How can I - semantically and syntactically in C# - evaluate all necessary search terms and use them to call the right operator methods in the correct order? Intuitively it sounds like recursion but somehow I can't picture it - especially since not all methods that need to be called have the same amount of parameters. Or are there maybe entirely other ways to accomplish this?
In Pseudo Code
Set Eval (Tree t) {
switch (t.Operator) {
case OR:
Set result = emptySet;
foreach(child in T.Children) {
result = Union(result, Eval(child));
}
return result;
case AND:
Set result = UniversalSet;
foreach(child in T.Children) {
result = Intersection(result, Eval(child));
}
return result;
case blah: // Whatever.
}
// Unreachable.
}
Does that help?
Or were you looking to optimize the order of evaluations, which probably has books written on it...
I would have expected the following tree to be generated:
(note that in your AST, the OR node has 3 children)
Either way, if you have created an ANTLR grammar that is able to create an AST (whether in the form of your original image, or mine posted above), it means that you have defined the proper operator precedence in your grammar. In that case, you shouldn't be confused to execute the order of your operators since your tree already mandates that (John <- AND -> Jim) and (NOT -> Simon) are to be evaluated first.
Could you perhaps post the ANTLR grammar you've been working on?
Also, you're talking about sets, but in your example, only single values are shown, so I get the impression your language is a bit more complex than you've shown so far. Perhaps you could explain your actual language, instead of a dumbed-down version of it?
PS. The source that created the image can be found here: http://graph.gafol.net/elDKbwzbA (ANTLR grammar also incuded)
I'm not familiar with the object model which ANTLR generates but assuming its something like this:
class BinaryNode : Node
{
public Node LeftChild;
public Node RightChild;
public readonly string Operator;
}
class UnaryNode : Node
{
public Node Child;
public readonly string Operator;
}
class TerminalNode : Node
{
public readonly string LeafItem;
}
class Node { }
public class Executor
{
public IEnumerable<object> Get(string value)
{
return null;
}
public IEnumerable<object> GetAll()
{
return null;
}
public IEnumerable<object> GetItems(Node node)
{
if (node is TerminalNode)
{
var x = node as TerminalNode;
return Get(x.LeafItem);
}
else if (node is BinaryNode)
{
var x = node as BinaryNode;
if (x.Operator == "AND")
{
return GetItems(x.LeftChild).Intersect(GetItems(x.RightChild));
}
else if (x.Operator == "OR")
{
return GetItems(x.LeftChild).Concat(GetItems(x.RightChild));
}
}
else if (node is UnaryNode)
{
var x = node as UnaryNode;
if (x.Operator == "NOT")
{
return GetAll().Except(GetItems(x.Child));
}
}
throw new NotSupportedException();
}
}
Note however this evaluates the query eagerly, which is not optimal. But it should give you an idea of how recursion would work.
I'm not exactly sure what you're trying to do, but I think I would turn the AST into a Func<Person, bool>. Each leaf node can be evaulated to a Func<Person, bool> for example p => p.Name == "Bill" AND, OR, and NOT can be implemented as higher order functions for example:
public static Func<T, bool> And<T>(Func<T, bool> a, Func<T, bool> b)
{
return t => a(t) && b(T);
}
Once you've done all this and collapsed your AST into a single Func<Person, bool>, you can pass that as the parameter to the Where() extension method on any type that implements IEnumerable<Person>.
In other words, I would first "compile" the AST into a Func<Person, boo>, and then use LINQ to Objects to actually filter my collection. Compiling should be easy because your AST is an implementation of the Composite design pattern. Each node should be able to expose the method Func<Person, bool> Compile().

Categories

Resources