Resharper find pattern and replace - how to insert strings - c#

We have a sanity check method
void IsNotNull<T>(T obj){...}
invocation
IsNotNull(obj);
I want to replace this to invoke the other overload that takes a second param of type string (message)
void IsNotNull<T>(T obj, string message){...}
So I want to change the invocation as
IsNotNull(obj, "obj is null");
I'm trying to achieve this using resharper's find pattern and replace.
So my find pattern is : IsNotNull($args$) - This works fine and it finds the method calls
Replace pattern: IsNotNull($args$, "$args$ is null") - This doesn't do anything
I also tried this IsNotNull($args$, """" + $args$ + """")
--Edited--
The suggestion box showing the correct wording(for both argument and identifier), but once applied it's different. I'm using Resharper 6
After applying the suggestion I get this
When I click Edit Pattern

What is your $args$ parameter defined as in the Search and Replace? If you make it to be Identifier, then you replace should work:
Find: IsNotNull($args$) - where $args$ is an Identifier
Replace: IsNotNull($args$, "$args$ is null")
You should have the result you want, i.e. IsNotNull(obj, "obj is null").

You can consider trying to use this pattern:
IsNotNull($args$, string.Format("{0} is null", $args$))
It works perfectly for me with ReSharper 7.1.
It seems, that R# doesn't want to evaluate argument expression inside of string literals normally. Given your pattern
IsNotNull($args$, "$args$ is null")
It replaced IsNotNull(5); by IsNotNull(5, 5); which is odd

The easiest method would be to rewrite the original method like so:
void IsNotNull<T>(T obj){
IsNotNull(obj, "obj is null");
}
Then click on the method signature, and choose Refactor->Inline Method (Ctrl+R, Ctrl+I). If you need to keep the original method signature around, you can, or you can check the box for "Remove inlined method declaration."
EDIT: Looks like hmemcpy's solution works in 7.1, so I'd suggest an upgrade. If that's not possible, however, try the following regular expression find-and-replace in Visual Studio.
Find: IsNotNull\(([^\),]+)\);
Replace: IsNotNull($1, "$1 is null");
Make sure "Use Regular Expressions" is checked, and "Look in:" should be "Entire Solution".

Related

How does Visual Studio syntax-highlight strings in the Regex constructor?

Hi fellow programmers and nerds!
When creating regular expressions Visual Studio, the IDE will highlight the string if it's preceded by a verbatim identifier (for example, #"Some string). This looks something like this:
(Notice the way the string is highlighted). Most of you will have seen this by now, I'm sure.
My problem: I am using a package acquired from NuGet which deals with regular expressions, and they have a function which takes in a regular expression string, however their function doesn't have the syntax highlighting.
As you can see, this just makes reading the Regex string just a pain. I mean, it's not all-too-important, but it would make a difference if we can just have that visually-helpful highlighting to reduce the time and effort one's brain uses trying to decipher the expression, especially in a case like mine where there will be quite a quantity of these expressions.
The question
So what I'm wanting to know is, is there a way to make a function highlight the string this way*, or is it just something that's hardwired into the IDE for the specific case of the Regex c-tor? Is there some sort of annotation which can be tacked onto the function to achieve this with minimal effort, or would it be necessary to use some sort of extension?
*I have wrapped the call to AddStyle() into one of my own functions anyway, and the string will be passed as a parameter, so if any modifications need to be made to achieve the syntax-highlight, they can be made to my function. Therefore the fact that the AddStyle() function is from an external library should be irrelevant.
If it's a lot of work then it's not worth my time, somebody else is welcome to develop an extension to solve this, but if there is a way...
Important distinction
Please bear in mind I am talking about Visual Studio, NOT Visual Studio Code.
Also, if there is a way to pull the original expression string from the Regex, I might do it that way, since performance isn't a huge concern here as this is a once-on-startup thing, however I would prefer not to do it that way. I don't actually need the Regex object.
According to https://devblogs.microsoft.com/dotnet/visual-studio-2019-net-productivity/#regex-language-support and https://www.meziantou.net/visual-studio-tips-and-tricks-regex-editing.htm you can mark the string with a special comment to get syntax highlighting:
// language=regex
var str = #"[A-Z]\d+;
or
MyMethod(/* language=regex */ #"[A-Z]\d+);
(the comment may contain more than just this language=regex part)
The first linked blog talks about a preview, but this feature is also present in the final product.
.NET 7 introduces the new [StringSyntax(...)] attribute, which is used in .NET 7 on more than 350 string, string[], and ReadOnlySpan<char> parameters, properties, and fields to highlight to an interested tool what kind of syntax is expected to be passed or set.
https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/?WT_mc_id=dotnet-35129-website&hmsr=joyk.com&utm_source=joyk.com&utm_medium=referral
So for a method argument you should just use:
void MyMethod([StringSyntax(StringSyntaxAttribute.Regex)] string regex);
Here is a video demonstrating the feature: https://youtu.be/Y2YOaqSAJAQ

Matching and replacing function expressions

I need to do some very light parsing of C# (actually transpiled Razor code) to replace a list of function calls with textual replacements.
If given a set containing {"Foo.myFunc" : "\"def\"" } it should replace this code:
var res = "abc" + Foo.myFunc(foo, Bar.otherFunc( Baz.funk()));
with this:
var res = "abc" + "def"
I don't care about the nested expressions.
This seems fairly trivial and I think I should be able to avoid building an entire C# parser using something like this for every member of the mapping set:
find expression start (e.g. Foo.myFunc)
Push()/Pop() parentheses on a Stack until Count == 0.
Mark this as expression stop
replace everything from expression start until expression stop
But maybe I don't need to ... Is there a (possibly built-in) .NET library that can do this for me? Counting is not possible in the family of languages that RE is in, but maybe the extended regex syntax in C# can handle this somehow using back references?
edit:
As the comments to this answer demonstrates simply counting brackets will not be sufficient generally, as something like trollMe("(") will throw off those algorithms. Only true parsing would then suffice, I guess (?).
The trick for a normal string will be:
(?>"(\\"|[^"])*")
A verbatim string:
(?>#"(""|[^"])*")
Maybe this can help, but I'm not sure that this will work in all cases:
<func>(?=\()((?>/\*.*?\*/)|(?>#"(""|[^"])*")|(?>"(\\"|[^"])*")|\r?\n|[^()"]|(?<open>\()|(?<-open>\)))+?(?(open)(?!))
Replace <func> with your function name.
Useless to say that trollMe("\"(", "((", #"abc""de((f") works as expected.
DEMO

Resharper structural find and replace with objects

I use a library which provides a custom fluent implementation for string.Format. The idea is to replace the common format construct with one that reads in a more natural way like this:
// From this
string.Format("some format string with {0} parameter", 1);
string.Format(SomeConstant, "abc");
// To this
"some format string with {0} parameter".Format(1);
SomeConstant.Format("abc");
I was trying to setup a simple structural find and replace pattern in Resharper to help me deal with those inside my code base. I noticed that if I set the format string parameter as an Argument Placeholder matching exactly one argument and the list of arguments uses entity objects (like a Person object), the previous example would turn into:
// Note that this is the System.String class and not the original format string
String.Format(someEntity);
If the entity is first converted to a string, then it matches properly and keeps the format string as I would expect.
SomeConstant.Format(someEntity.ToString());
I had to use an Expression Placeholder with an expression type of System.String to properly match the first parameter.
Is this a bug or is there something that I am not understanding in this behavior? Why would the type of the second argument influence the match of the first argument? I have a feeling that I might be misunderstanding how Argument Placeholders are working...
NB: Using Resharper 9.0 Update 1
EDIT: As requested, here is the search pattern details:
Search Pattern: String.Format($format$, $args$)
Replace Pattern: $format$.Format($args)
format: Argument Placeholder, matching exactly one argument
args: Argument Placeholder, matching at least one argument

.Net regex not replacing correctly

Background
I am trying to do some regex matching and replacing, but for some reason the replacement isn't correct in .NET.
Regex pattern - "^.*?/rebate/?$"
Input string - "/my-tax/rebate"
Replacement string - "/new-path/rebate"
Basically, if the word 'rebate' is seen in a string, the input string needs to be replaced entirely by the replacement string.
Problem
If I create a regex with the pattern and execute
patternMatch.Pattern.Replace("/my-tax/rebate", "/new-path/rebate")
I get /my-tax/new-path/rebate, which isn't correct.
But, if I execute -
new Regex(#"^.*?/rebate/?$").Replace("/my-tax/rebate", "/new-path/rebate"),
the result is correct - /new-path/rebate
Why is that?
patternMatch is an object with two properties - one Pattern (which is the Regex Pattern) and another one is TargetPath (which is the replacement string). In this example, I am only using the pattern property.
patternMatch.Pattern on debugging is
Here are the results during run time-
You are simply wrongly using the function. I'm not sure how you are getting /my-tax/new-path/rebate since it is giving me an error on ideone.com (Maybe you have a regex named Pattern?).
Anyway, you shouldn't have any issues with using the function like this:
patternMatch.Replace("/my-tax/rebate", "/new-path/rebate");
ideone demo
A number of points in your question are incorrect. The regex is replacing correctly.
Per #XiaoguangQiao's comment, what is patternMatch.Pattern.Replace? Your example...
var patternMatch = new Regex("^.*?/rebate/?$");
patternMatch.Pattern.Replace("/my-tax/rebate", "/new-path/rebate");
...errors with the message...
'System.Text.RegularExpressions.Regex' does not contain a definition for 'Pattern' and no extension method 'Pattern' accepting a first argument of type 'System.Text.RegularExpressions.Regex' could be found
...when I throw it into a quick LINQPad 4 query (set to C# Statement(s)).
pattern is a private string field of System.Text.RegularExpressions.Regex; and patternMatch.Replace("/my-tax/rebate", "/new-path/rebate") - which I expect is what you meant - yields the correct result ("/new-path/rebate") rather than the incorrect result you said you get ("/my-tax/new-path/rebate").
Otherwise your pattern(s) (i.e. with and without the extra / that #rene pointed out) is fine for the input ("/my-tax/rebate") and replacement ("/new-path/rebate") you initially outline - insofar as they match and yield the result you want. You can check this outside your code in quick fiddles with the extra / and without the extra /.
Use String.Replace Method.
str.replace("rebate","new-path/rebate")
http://msdn.microsoft.com/en-us/library/fk49wtc1%28v=vs.110%29.aspx

Error when using .Contains to check if a string contains a character

I am trying to find out if a string contains a character. I tried the following
where ViewBag.Options is a string:
#ViewBag.Options.Contains('q')
but it gives me an error saying:
The best overloaded method match for 'string.Contains(string)' has some invalid arguments.
And it's write: string.Contains doesn't have an overload taking just a single character.
Options:
Use #ViewBag.Options.Contains("q")
Use #ViewBag.Options.IndexOf('q') != -1
Use some more complicated LINQy approach (e.g. Any) - feasible, but there's no need here. (I'm a fan of LINQ where appropriate but I don't think that's the right approach here; I wouldn't start introducing lambda expressions into my code just for the sake of it)
Use some more complicated regular expression approach - again, there's no point.
Using single quotes in c# indicates a character.
Try with double quotes:
#ViewBag.Options.Contains("q");
Use this:
#ViewBag.Options.Contains("q")
Use any of them
#ViewBag.Options.Contains("q");
#ViewBag.Options.Any(x => x == 'q');
If you insist
#ViewBag.Options.Contains('q'.ToString());
The error is self explanatory. Paramerters of .Contains takes a string and no overload of this method takes a character.

Categories

Resources