Roslyn SyntaxTree - Changing Field value - c#

using the Roslyn SyntaxTree API, I'd like to replace the literal value "UV254" with a new value.
Example:
public class Analog
{
public const string Value = "UV254";
}
After update
public class Analog
{
public const string Value = "UV220";
}
I came up with the below solution but I suspect this could be simplified:
string sourceCode = "public class Analog { public const string Value = \"UV254\"; public const string Description = \"A Description\";}";
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
CompilationUnitSyntax syntaxRoot = syntaxTree.GetCompilationUnitRoot();
LiteralExpressionSyntax afterLiteralExpressionSyntax = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("UV220"));
LiteralExpressionSyntax beforeLiteralExpressionSyntax = null;
foreach (VariableDeclarationSyntax variableDeclarationSyntax in syntaxRoot.DescendantNodes().OfType<VariableDeclarationSyntax>())
{
foreach(VariableDeclaratorSyntax variableDeclaratorSyntax in variableDeclarationSyntax.Variables)
{
if(variableDeclaratorSyntax.Identifier.ValueText == "Value")
{
beforeLiteralExpressionSyntax = variableDeclaratorSyntax.DescendantNodes().OfType<LiteralExpressionSyntax>().Single();
break;
}
}
if(beforeLiteralExpressionSyntax != null)
{
break;
}
}
var newRoot = syntaxRoot.ReplaceNode(beforeLiteralExpressionSyntax, afterLiteralExpressionSyntax);
var fixedTree = newRoot.SyntaxTree.WithRootAndOptions(newRoot, syntaxTree.Options);
Can this be simplified?
Thanks for the help.

I think you can use some LINQ to shorten the determination of beforeLiteralExpressionSyntax. You could write the following instead:
LiteralExpressionSyntax beforeLiteralExpressionSyntax =
syntaxRoot.DescendantNodes().OfType<VariableDeclarationSyntax>()
.SelectMany(decl => decl.Variables)
.FirstOrDefault(declarator => declarator.Identifier.ValueText == "Value")
?.DescendantNodes().OfType<LiteralExpressionSyntax>()
.Single();
This will assign a null value to beforeLiteralExpressionSyntax if the field wasn't found. If you're sure the field will always be there, you could replace FirstOrDefault with First and replace the ?. with ..
Other than that, I don't think there is much you can do to simplify the code. My experience of working with Roslyn is that ultimately it is quite complicated to navigate through the syntax tree, pick out relevant bits of it and make changes to it, but I guess some of that is inevitable because it reflects the complexity of the C# language.

Related

generate getter and setter lambda with Roslyn

I am new to using Roslyn and the truth is that I am finding it quite interesting, but when using lambda expressions I am blocked. I would like to generate a property with getters and setters with lambda, so it looks like this:
private string uiDescription;
private string uiDescription;
public override string UiDescription {
get => uiDescription ?? Name;
set => uiDescription = value;
}
Would it be possible? Thanks in advance
I already found it, so I'll leave it here in case anyone is interested. Surely it can be done better:
var variableDeclarationDescription = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("string"))
.AddVariables(SyntaxFactory.VariableDeclarator("uiDescription"));
var fieldDeclarationDescription = SyntaxFactory.FieldDeclaration(variableDeclarationDescription)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword));
var descriptionBody = variableDeclarationDescription.Variables.ToString()+ " ?? Name;"
var resourceGet = SyntaxFactory.IdentifierName(descriptionBody);
descriptionBody = variableDeclarationDescription.Variables.ToString() + " = value ;";
var resourceSet = SyntaxFactory.IdentifierName(descriptionBody);
var propertyGetLambda = SyntaxFactory.ArrowExpressionClause(resourceGet);
var propertySetLambda = SyntaxFactory.ArrowExpressionClause(resourceSet);
var propertyDeclarationDescription = SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName("string"), "UiDescription")
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.OverrideKeyword))
.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).
WithExpressionBody(propertyGetLambda),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).
WithExpressionBody(propertySetLambda)
);
I hope it can serve someone in the future. Thanks

How do I tell if a variable is in scope at some syntax node with Roslyn?

I'm new to Roslyn. I'm wondering if there's a way to tell if a variable is in scope at some position in a semantic model. To give a bit of background on what I'm doing, I'm trying to transform foreach blocks that iterate through the results of a Select, e.g. of the form
foreach (string str in new int[0].Select(i => i.ToString()))
{
}
to
foreach (int item in new int[0])
{
string str = item.ToString();
}
Here is the relevant portion of my code fix provider. Currently, I am hard-coding the iteration variable to be item:
var ident = SyntaxFactory.Identifier("item");
Then, I am retrieving the Body of the selector's SimpleLambdaExpressionSyntax, and (in the above case) substituting the parameter i with item to get item.ToString():
var paramTokens = from token in selectorBody.DescendantTokens()
where token.Text == selectorParam.Identifier.Text
select token;
selectorBody = selectorBody.ReplaceTokens(paramTokens, (_, __) => ident);
I want to know if there is a way to tell whether a variable named item is already in scope at the location of the foreach block, so my code fix provider does not generate a conflicting variable declaration. Would this be possible to somehow achieve using the SemanticModel/Symbol/etc. APIs?
Thanks.
I could think of two ways to do it.
Using this test code so I could test the different declarations (field, property, variable, class names)
const string code = #"
public class AClass{
private int MyFld = 5;
protected double MyProp{get;set;}
public void AMethod(){
string myVar = null;
for (int myIterator=0; myIterator<10;myIterator++)
foreach (string str in new int[0].Select(i => i.ToString())){ }
}
public void AnotherMethod()
{
string anotherVar = null;
}
}";
-
void Main()
{
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetRoot();
var startNode = root
.DescendantNodes()
.OfType<SimpleLambdaExpressionSyntax>() // start at the Select() lambda
.FirstOrDefault();
FindSymbolDeclarationsInAncestors(startNode, "myVar").Dump(); // True
FindSymbolDeclarationsInAncestors(startNode, "anotherVar").Dump(); // False
CompilationLookUpSymbols(tree, startNode, "myVar").Dump(); // True
CompilationLookUpSymbols(tree, startNode, "anotherVar").Dump(); // False
}
// You could manually traverse the ancestor nodes, and find the different DeclarationSyntax-es.
// I may have missed some, like CatchDeclarationSyntax..
// Error-prone but more fun.
public bool FindSymbolDeclarationsInAncestors(CSharpSyntaxNode currentNode, string symbolToFind)
{
return currentNode
.Ancestors().SelectMany(a => a.ChildNodes()) // get direct siblings
.SelectMany(node => // find different declarations
(node as VariableDeclarationSyntax)?.Variables.Select(v => v.Identifier.ValueText)
?? (node as FieldDeclarationSyntax)?.Declaration?.Variables.Select(v => v.Identifier.ValueText)
?? (node as LocalDeclarationStatementSyntax)?.Declaration?.Variables.Select(v => v.Identifier.ValueText)
?? new[] {
(node as PropertyDeclarationSyntax)?.Identifier.ValueText,
(node as MethodDeclarationSyntax)?.Identifier.ValueText,
(node as ClassDeclarationSyntax)?.Identifier.ValueText,
})
.Any(member => string.Equals(member, symbolToFind));
}
// Or use the SemanticModel from the CSharpCompilation.
// Possibly slower? Also, not as much fun as manually traversing trees.
public bool CompilationLookUpSymbols(SyntaxTree tree, CSharpSyntaxNode currentNode, string symbolToFind)
{
var compilation = CSharpCompilation.Create("dummy", new[] { tree });
var model = compilation.GetSemanticModel(tree);
return model.LookupSymbols(currentNode.SpanStart, name: symbolToFind).Any();
}

Parsing SQL Statement With Irony

I am trying to create a method that converts a regular sql statement to c# objects, So i decided to use Irony to parse the sql statement then i return the statement as an Action that contains the type of the statement and the values of it depending on the type
Here is my non completed code [ Because i got frustrated as i don't know what to do then ]
private List<Action> ParseStatement(string statement)
{
var parser = new Parser(new SqlGrammar());
var parsed = parser.Parse(statement);
var status = parsed.Status;
while (parsed.Status == ParseTreeStatus.Parsing)
{
Task.Yield();
}
if (status == ParseTreeStatus.Error)
throw new ArgumentException("The statement cannot be parsed.");
ParseTreeNode parsedStmt = parsed.Root.ChildNodes[0];
switch (parsedStmt.Term.Name)
{
case "insertStmt":
var table = parsedStmt.ChildNodes.Find(x => x.Term.Name == "Id").ChildNodes[0].Token.ValueString;
var valuesCount =
parsedStmt.ChildNodes.Find(x => x.Term.Name == "insertData").ChildNodes.Find(
x => x.Term.Name == "exprList").ChildNodes.Count;
var values = parsedStmt.ChildNodes.Find(x => x.Term.Name == "insertData").ChildNodes.Find(
x => x.Term.Name == "exprList").ChildNodes;
foreach (var value in values)
{
string type = value.Token.Terminal.Name;
}
break;
}
return null;
}
private Type ParseType(string type)
{
switch (type)
{
case "number":
return typeof (int);
case "string":
return typeof (string);
}
return null;
}
So the Question Here is : How could i make use of Irony to convert a string SQL Statement to a c# objects ?
Here is an example of what i want to achieve :
INSERT INTO Persons VALUES (4,'Nilsen', 'Johan', 'Bakken 2',
'Stavanger')
And get it converted to
return new Action<string type, string table, int val1, string val2, string val3, string val4, string val5>;
Dynamically depending on what the method have read from the statement.
I hope i have well explained my idea so you can help me guys, And if there is something unclear please tell me and i will try to explain it.
I was trying to parse SQL with Irony as well. I gave up because the sample SQL parser in Irony don't handle: CTEs, Order by column number, half the special statements like
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
While I had a great time learning about Irony, I don't have the coding chops to implement all the aforementioned parts correctly.
I ended up using the Microsoft-provided SQL parsing library. Sample code for LINQPad 5 below:
// Add a reference to
// C:\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\
// Microsoft.SqlServer.TransactSql.ScriptDom.dll
//
// https://blogs.msdn.microsoft.com/gertd/2008/08/21/getting-to-the-crown-jewels/
public void Main()
{
var sqlFilePath = #"C:\Users\Colin\Documents\Vonigo\database-scripts\Client\Estimate\spClient_EstimateAddNew.sql";
bool fQuotedIdenfifiers = false;
var parser = new TSql100Parser(fQuotedIdenfifiers);
string inputScript = File.ReadAllText(sqlFilePath);
IList<ParseError> errors;
using (StringReader sr = new StringReader(inputScript))
{
var fragment = parser.Parse(sr, out errors);
fragment.Dump();
}
}
If you are not doing this as a fun exercise I would recommend using Linq to SQL to generate your stub classes or Entity Framework as Drunken Code Monkey mentioned in the comments.
Here's a good article to get you started: Generating EF code from existing DB

Using strings instead of enums?

Is it common place to use a string for comparison as opposed to an enum?
I am aware about your context, but as a first step you can just refactor this way:
Step 1
if (typeOfObject == "UAV")
{
DoSomeWork(_stkObjectRootToIsolateForUavs);
}
else if (typeOfObject == "Entity")
{
DoSomeWork(_stkObjectRootToIsolateForEntities);
}
private void DoSomeWork(IAgStkObject agStkObject)
{
IAgStkObject stkObject = agStkObject.CurrentScenario.Children[stkObjectName];
IAgDataProviderGroup group = (IAgDataProviderGroup)stkUavObject.DataProviders["Heading"];
IAgDataProvider provider = (IAgDataProvider)group.Group["Fixed"];
IAgDrResult result = ((IAgDataPrvTimeVar)provider).ExecSingle(_stkObjectRootToIsolateForUavs.CurrentTime);
stkObjectHeadingAndVelocity[0] = (double)result.DataSets[1].GetValues().GetValue(0);
stkObjectHeadingAndVelocity[1] = (double)result.DataSets[4].GetValues().GetValue(0);
}
Then consider replasing if's with switch:
Step 2
switch (typeOfObject)
{
case "UAV":
DoSomeWork(_stkObjectRootToIsolateForUavs);
break;
case "Entity":
DoSomeWork(_stkObjectRootToIsolateForEntities);
break;
default:
throw new NotImplementedException():
}
This can be even better when using enums.
At the very least, the strings should be declared as constants (or perhaps readonly fields) somewhere, instead of spread out through the code. However, this looks like the schoolbook example for when to use an enum.
public enum ObjectType
{
UAV,
Entity,
// and so on
}
To add to #Restuta's answer, I'd use a
IDictionary<MyEnumifiedString, Action<IAgStkObject>>
to get rid of that if.
I'd agree with #Frederik that this seems a perfect case for using enums, but it could be that the only thing you can get out of the application is a string. In which case your example is perfectly OK.
Oh yes - and make sure you have the string constants defined in one place, preferably a config file so that if they change the other application you don't have to recompile yours.
Regarding your first question I will always use a defined type to store the strings simply to have one location for change if needed.
So for your example i would have the following
public sealed class RootTypes
{
public const string Entity = "entity";
public const string UAV = "uav";
}
Your code then updates to this
typeOfObject = typeOfObject.ToLower();
if (typeOfObject == RootTypes.UAV)
{
stkUavObject = _stkObjectRootToIsolateForUavs.CurrentScenario.Children[stkObjectName];
var group = (IAgDataProviderGroup) stkUavObject.DataProviders["Heading"];
var provider = (IAgDataProvider) group.Group["Fixed"];
IAgDrResult result = ((IAgDataPrvTimeVar) provider).ExecSingle(_stkObjectRootToIsolateForUavs.CurrentTime);
stkObjectHeadingAndVelocity[0] = (double) result.DataSets[1].GetValues().GetValue(0);
stkObjectHeadingAndVelocity[1] = (double) result.DataSets[4].GetValues().GetValue(0);
}
else if (typeOfObject == RootTypes.Entity)
{
IAgStkObject stkEntityObject = _stkObjectRootToIsolateForEntities.CurrentScenario.Children[stkObjectName];
var group = (IAgDataProviderGroup) stkEntityObject.DataProviders["Heading"];
var provider = (IAgDataProvider) group.Group["Fixed"];
IAgDrResult result = ((IAgDataPrvTimeVar) provider).ExecSingle(_stkObjectRootToIsolateForEntities.CurrentTime);
stkObjectHeadingAndVelocity[0] = (double) result.DataSets[1].GetValues().GetValue(0);
stkObjectHeadingAndVelocity[1] = (double) result.DataSets[4].GetValues().GetValue(0);
}
The issue of code redundancy has been anserwed by Restuta
Use enums with bit flags:
[Flags]
public enum MyFlags
{
SomeFlag = 0x1, // 001
OtherFlag = 0x2,// 010
ThirdFlag = 0x4 // 100
}
var firstObject = MyFlags.SomeFlag;
var secondObject = MyFlags.SomeFlag | MyFlags.OtherFlag;
if(((int)secondObject & MyFlags.SomeFlag) != 0)
{
// true
}
if(((int)secondObject & MyFlags.OtherFlag) != 0)
{
// true
}
if(((int)firstObject & MyFlags.SomeFlag) != 0)
{
// true
}
if(((int)firstObject & MyFlags.OtherFlag) != 0)
{
// false
}
This article would be helpful.

C# Array Conversion

Any help here as I'm a C# noob. The following code works fine and returns 1 string ViewState2. I'd like it to return an array of ViewState2 and EventValidation2 so I can manipulate it later on. How would I convert the code below to return an array?
public string get_status(string local_fname)
{
var dts_doc = new HtmlAgilityPack.HtmlDocument();
dts_doc.Load(local_fname);
//Pull the values
var ViewState = dts_doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[1]/input[4]/#value[1]");
var EventValidation = dts_doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[2]/input[1]/#value[1]");
string ViewState2 = ViewState.Attributes[3].Value;
string EventValidation2 = EventValidation.Attributes[3].Value;
//Display the values
//System.Console.WriteLine(ViewState.Attributes[3].Value);
//System.Console.WriteLine(EventValidation.Attributes[3].Value);
//System.Console.ReadKey();
return ViewState2;
}
Don't use an array, but a class. Doing this, you don't have to remember what each element means.
public class Status
{
public string ViewState {get; set;}
public string EventValidation {get; set;}
}
using System;
using HtmlAgilityPack;
[...]
public Status GetStatus(string localFileName)
{
var dtsDoc = new HtmlDocument();
dtsDoc.Load(localFileName);
//Pull the values
var viewStateNode = dtsDoc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[1]/input[4]/#value[1]");
var eventValidationNode = dtsDoc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[2]/input[1]/#value[1]");
string viewState = viewStateNode.Attributes[3].Value;
string eventValidation = eventValidationNode.Attributes[3].Value;
//Display the values
//Console.WriteLine(viewState);
//Console.WriteLine(eventValidation);
//Console.ReadKey();
return new Status
{
ViewState = viewState,
EventValidation = eventValidation
}
}
Also, you should read up on coding guidelines and naming conventions in the C# language, also the using statement might be interesting. I have corrected some "mistakes", but probably didn't catch all. Also, I have renamed a couple of variables, to make their content clearer. You also might want to look into using the var keyword only in a loop, while using LINQ (or anomynous types in general) or with really long class names. Written out type names can increase readability quite a lot.
If you really want an array with ViewState2 and EventValidation2 in it, you can make the following changes:
// Notice: return value of string[] instead of string
public string[] get_status(string local_frame);
And:
// Notice: returning an array
return new string[] { ViewState2, EventValidation2 };
That said, this is really the "quick and dirty" approach, and is not really appropriate if you're going to want this code to be maintainable (when's the last time you read documentation on a function that "returns an array of length 2, with a string representing X as the first element and another string representing Y as the second"?).
Femaref's right; the correct thing to do would be to encapsulate the information you want returned in its own type.
Assuming you answer yes to this question (although I'd recommend a different approach, see below) this will do what you're asking:
public String[] get_status(string local_fname)
{
var dts_doc = new HtmlAgilityPack.HtmlDocument();
dts_doc.Load(local_fname);
//Pull the values
var ViewState = dts_doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[1]/input[4]/#value[1]");
var EventValidation = dts_doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[2]/input[1]/#value[1]");
string ViewState2 = ViewState.Attributes[3].Value;
string EventValidation2 = EventValidation.Attributes[3].Value;
String[] retValues = new String[2];
retValues[0] = ViewState2;
retValues[1] = EventValidation2;
return retValues;
//Display the values
//System.Console.WriteLine(ViewState.Attributes[3].Value);
//System.Console.WriteLine(EventValidation.Attributes[3].Value);
//System.Console.ReadKey();
return ViewState2;
}
That said, I would follow the approach afte the line.
I'd write a class that has the data members you want:
public class DataClass
{
public string ViewState { get; set; }
public string EventValidation { get; set; }
}
Then I'd modify the method to return an instance of your data class.
public DataClass get_status(string local_fname)
{
var dts_doc = new HtmlAgilityPack.HtmlDocument();
dts_doc.Load(local_fname);
//Pull the values
var ViewState = dts_doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[1]/input[4]/#value[1]");
var EventValidation = dts_doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[2]/input[1]/#value[1]");
var dc = new DataClass();
dc.ViewState = ViewState.Attributes[3].Value;
dc.EventValidation = EventValidation.Attributes[3].Value;
return dc;
}
string[] array = new string[2];
array[0] = ViewState2;
array[1] = EventValidation2;
return array;
But it seems to trivial as answer. Please Does it solve your problem? If no, can you specify better the question please?

Categories