📜 ⬆️ ⬇️

ReSharper: search code by pattern

There are two types of searches that you often use: “Find Text” and “Find Usages”. But none of them allows you to search for complex language constructs, for example, all the places in your code that use the expression " s == null || s == String.Empty ". You can use regular expressions and try to make Find Text, but such regular expressions will look monstrous and, for sure, contain many errors (for example, they will not take into account the possibility of comments at almost every point of the program). Obviously, to solve this problem, you need some other type of search, which would know about the syntax of the language, the type system and would not force the developer to learn some new syntax of the query language.


Smart search


ReSharper 5.0 has a new search type “Search With Pattern”, which allows you to search for pieces of code by pattern, and you can impose restrictions on parts of this pattern. For example, for expressions, you can specify the type, and for the arguments of their estimated number.

Let's look at a specific example. We will search in your code for all the expressions " enumerable.Count() > 0 . enumerable.Count() > 0 ", where enumerable is any expression of type IEnumerable .
If you solved this problem through Find Usages, then you would make the calls of the Count() method, and accordingly you would have to look through the tents or hundreds of calls - this is tedious and fraught with errors. Find Text looks a little better, but if you think that the searched expression can be written with line feeds, comments, and the Count() method in your project implements objects of different types, it becomes clear that it is not suitable either.

Open the “Search With Pattern” window (Resharper -> Find -> Search With Pattern) and enter the following pattern in the text field:
')
$enumerable$.Count() > 0

The $enumerable$ will be highlighted in red. The fact is that the names of placeholders are enclosed in "$" signs; during a search on the place of such a placeholder, any text that meets the specified constraints will be expected: the type of placeholder and its parameters. In our case, any IEnumerable expression can be in place of $enumerable$ . But first we need to define this placeholder. To do this, click "Add Placeholder", select "Expression", in the "Name" field, enter " enumerable " (placeholder name without dollar signs). In "Expression Type", enter " IEnumerable " (start typing the name of the type, and the resolver will prompt you the options). Do not forget to tick "Or derived type".

In just a few seconds, without knowledge of regular expressions, we created a pattern for the search. But there is one more pleasant and very powerful thing: notice that under the template editing field there is a tick “Match similar constructs”. If this checkbox is checked, then there is a search for not only exact matches with the sample, but also semantically identical structures. For example, the constructs " a > 0 " and " 0 < a " are semantically identical. In our case, this checkbox is reasonably left set, because you don't care how the result of the Count() method is compared with zero.

All is ready! Now you can click the “Find” button and look at the results.

Smart Replacement


Such a powerful search without the replacement function would not be complete, because it is interesting not only to find all the bad places in the program, but also to replace them with the correct code. To do this, in the “Search With Pattern” window, click the “Replace” button. A field for entering a replacement pattern appears. In this field, you can write any text that is correct in terms of language, you can also use placeholders. Enter in this field:

$enumerable$.Any()

Now press the Replace button!

Make the search pattern backlight and QuickFix


You now have a search pattern and a replacement pattern. It is logical to make this backlight and QuickFix. To do this, in the pattern editing window, simply click the “Save” button. Your pattern will be saved in the “Patterns Catalog”. This directory can be simply used to store frequently used templates, and you can make it a powerful tool for creating your own analyzes.

If you open the catalog (ReSharper -> Tools -> Patterns Catalog), you can customize your pattern to customize the text of the tooltip, the text that will be displayed in QuickFix and the type of backlight. Set all your parameters for your pattern.

Everything! The backlight works! Now all the code that matches your template will be highlighted on the fly! And the corresponding QuickFix will appear on the backlight!

Examples of search patterns


A search pattern that simplifies expressions:



In this example, we used placeholders for type, expression, and identifier. It did not set any restrictions on them, but used them in the replacement pattern. The only constrained placeholder is $seq$ , it is limited to the IEnumrable type.

But a pattern that implements highlighting and QuickFix “Replace 'if' with '?:'”:



If you have ideas for useful patterns, then tell us about them in the comments. This will be useful for the community, and perhaps the most interesting patterns will be included in the next delivery of ReSharper.

We write extensions to ReSharper easily and quickly


But that is not all. In fact, the mechanism that performs pattern matching is much more powerful than the end user can see. The current restrictions are related to the fact that the ReSharper team is not clear how to properly express certain aspects in the UI. For example, in the current implementation it is impossible to find a construct in which something is missing (for example, a method call without any verification, or a method of a certain type, but without an attribute). But this can be done through the API.

Moreover, if you write Highlighting or QuickFix using this API, then you save a lot of your time, i.e. You do not have to seriously understand the source code model and other intricacies. You describe the sample in terms of the C # syntax, set the parameters and get the result, for example, for “Replace 'if' with '?:'” It is enough to write such code:

var myMatcher = searcherFactory
.CreatePattern( "if ($condition$) { $x$ = $expr1$; } else { $x$ = $expr2$; }" )
.AddExpressionPlaceholder( "condition" )
.AddIdentifierPlaceholder( "x" )
.AddExpressionPlaceholder( "expr1" )
.AddExpressionPlaceholder( "expr2" )
.CreateStatementMatcher();


* This source code was highlighted with Source Code Highlighter .


Very simple, and most importantly, it is clear without explanation, and does not require knowledge of the internals of ReSharper. If you started writing this code without using the Search With Pattern API, then you would need knowledge of how the syntax tree is organized, IIfStatement , how IExpression differs from IReferenceExpression , how to compare expressions for equivalence (you need to compare two occurrences of the expression $x$ ), and many other complex things.

You are looking for a code on the model, do additional checks (for example, ineffable through the Search With Pattern API API) and can hang the lights!

Search With Pattern API is available through the StructuralSearchEngine interface. If there are enough interested people, then in the next article I can give a small example of how you can easily create your own backlights and QuickFix with it.

Source: https://habr.com/ru/post/88408/


All Articles