Find used types and members from a specific assembly - ndepend

I am trying to create a CQLinq query which will give me all types and/or members from a specific assembly that is being used by the rest of the solution.
I need this information so I can move/refactor all the referenced types/members and in the end remove the specific assembly (and all references to it) from the solution.

For optimal result presentation, the query can look like:
let targets = Assemblies.WithName("SpecificAssemblyName").Single().ChildTypesAndMembers.ToHashSet()
let methodsUser = Application.Methods.UsingAny(targets)
let typesUser = Application.Types.UsingAny(targets)
from x in ((IEnumerable<IMember>)typesUser).Concat(methodsUser)
where !targets.Contains(x)
select new {
x,
called = x.IsMethod ? x.AsMethod.MethodsCalled.Intersect(targets).Concat(x.AsMethod.FieldsUsed.Intersect(targets)) :
x.AsType.TypesUsed.Intersect(targets)}

Related

How to enumerate over columns with tokio-postgres when the field types are unknown at compile-time?

I would like a generic function that converts the result of a SQL query to JSON. I would like to build a JSON string manually (or use an external library). For that to happen, I need to be able to enumerate the columns in a row dynamically.
let rows = client
.query("select * from ExampleTable;")
.await?;
// This is how you read a string if you know the first column is a string type.
let thisValue: &str = rows[0].get(0);
Dynamic types are possible with Rust, but not with the tokio-postgres library API.
The row.get function of tokio-postgres is designed to require generic inference according to the source code
Without the right API, how can I enumerate rows and columns?
You need to enumerate the rows and columns, doing so you can get the column reference while enumerating, and from that get the postgresql-type. With the type information it's possible to have conditional logic to choose different sub-functions to both: i) get the strongly typed variable; and, ii) convert to a JSON value.
for (rowIndex, row) in rows.iter().enumerate() {
for (colIndex, column) in row.columns().iter().enumerate() {
let colType: string = col.type_().to_string();
if colType == "int4" { //i32
let value: i32 = row.get(colIndex);
return value.to_string();
}
else if colType == "text" {
let value: &str = row.get(colIndex);
return value; //TODO: escape characters
}
//TODO: more type support
else {
//TODO: raise error
}
}
}
Bonus tips for tokio-postgres code maintainers
Ideally, tokio-postgres would include a direct API that returns a dyn any type. The internals of row.rs already use the database column type information to confirm that the supplied generic type is valid. Ideally a new API uses would use the internal column information quite directly with improved FromSQL API, but a simpler middle-ground exists:-
It would be possible for an extra function layer in row.rs that uses the same column type conditional logic used in this answer to then leverage the existing get function. If a user such as myself needs to handle this kind of conditional logic, I also need to maintain this code when new types are handled by tokio-postgresql, therefore, this kind of logic should be included inside the library where such functionality can be better maintained.

Getting list of types that are effected by an extension method in cqlinq

How to get the list of types that are extended by a extension method in ndepend cqlinq? Using reflection to code this seems a bit of donkey work where ndepend is already there.
NDepend code model doesn't have a straight way to resolve the method parameter type. So we can come up with a satisfying answer with code query relying on string formatting extended type name, extracted from the method name. But this query is overly complex and there are edge cases where it won't work properly (explained below).
Here is the code query, it runs fast even on large code base thanks to the use of a dictionary:
//
// First let build dicoExtensionMethods
let dicoExtensionMethods =
(from m in Application.Methods
where m.IsExtensionMethod
// extract extended type simple name (with generic parameters like "IEnumerable<String>")
let beginIndex = m.Name.IndexOf("(") + 1
let endIndex = m.Name.IndexOf(',', beginIndex) > 0 ? m.Name.IndexOf(',', beginIndex) : m.Name.IndexOf(")", beginIndex)
let extendedTypeSimpleName1 = m.Name.Substring(beginIndex, endIndex - beginIndex)
// Take care of generic type first char, like "IEnumerable<"
let extendedTypeSimpleName2 = extendedTypeSimpleName1.IndexOf('<') == -1 ? extendedTypeSimpleName1 :
extendedTypeSimpleName1.Substring(0, extendedTypeSimpleName1.IndexOf('<') + 1 )
select new { m, extendedTypeSimpleName2 })
.ToLookup(pair => pair.extendedTypeSimpleName2)
.ToDictionary(g => g.Key, g=> g.Select(p =>p.m))
//
// Second for each type get extension methods from dicoExtensionMethods
from t in Types
// Format type name like "IEnumerable<"
let typeName = !t.IsGeneric ? t.SimpleName : t.Name.Substring(0, t.Name.IndexOf('<') + 1 )
where dicoExtensionMethods.ContainsKey(typeName)
let methods = dicoExtensionMethods[typeName]
select new { t, methods }
As written it is a complex query because of type name formatting, and it works fine most of the time.
However when it comes to extending generic types, it says for example that IEnumerable<T> is extended by both methods that extend IEnumerable<String> and IEnumerable<Int32>. This is acceptable, but it is not 100% correct.
Also if you extend several types with same name but various generic arity (like Func<T1,T2> and Func<T1,T2,T3>), then this code query won't work properly.
The same if you extend several types with same name, declared in different assemblies or namespace (which is a code smell anyway).
Hope this helps!

Detecting direct instantiation with nDepend

With the nDepend API, would something like the following be possible?
I want to keep a watch out for instances where our object factory has been bypassed and a concrete class is being instantiated directly.
Obviously I'd need to be able to filter out things like:
StringBuilder stringBuilder = new StringBuilder();
perhaps by adding to the Where clause type names to exclude, or namespaces in which to check, but I want to make sure we see:
IMyCustomType item = ObjectFactory.Get<IMyCustomType>();
and not this:
MyCustomType item = new MyCustomType();
Thanks.
Maybe such code rule below could help you, hopefully it is understandable enough to not have to comment it:
warnif count > 0
let ctors = Application.Namespaces.WithNameLike("Namespaces1*").ChildMethods().Where(m => m.IsConstructor)
let codeThatMustNotCallCtors = Application.Namespaces.WithNameLike("Namespaces2*").ChildMethods()
from m in codeThatMustNotCallCtors.UsingAny(ctors)
select new { m, ctorsCalled = m.MethodsCalled.Intersect(ctors ) }

How could I search references to a field on a AST or a CompilationUnit in eclipse?

Hi,
I'm developing an Eclipse plugin. I
need to find all the references in the
source using AST's or jdt.core.dom
or something like that. I need this
references like ASTNodes in order to
get the parent node and check several
things in the expression where
references are involved.
Thanks beforehand.
Edited:
I want to concrete a little more, My problem is that I try to catch some references to a constant but... I have not idea how I can do to catch in the matches this references. I need check the expressions which the references to a determined constant are involved. I only get the source of the method where they are used.
I think the problem is the scope or the pattern:
pattern = SearchPattern.createPattern(field, IJavaSearchConstants.REFERENCES);
scope = SearchEngine.createJavaSearchScope(declaringType.getMethods());
Thanks beforehand!
I used something like:
Search for the declaration of an
method, returns an IMethod
Search for references to the
IMethod, record those IMethods
For each IMethod returned, create an
AST from its compilation unit
Searching for declarations or references looks like the following code.
SearchRequestor findMethod = ...; // do something with the search results
SearchEngine engine = new SearchEngine();
IJavaSearchScope workspaceScope = SearchEngine.createWorkspaceScope();
SearchPattern pattern = SearchPattern.createPattern(searchString,
IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS,
SearchPattern.R_EXACT_MATCH);
SearchParticipant[] participant = new SearchParticipant[] { SearchEngine
.getDefaultSearchParticipant() };
engine.search(pattern, participant, workspaceScope, findMethod,
monitor);
Once you have your IMethod references, you can get to the AST using:
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
if (methodToSearch.isBinary()) {
parser.setSource(methodToSearch.getClassFile());
} else {
parser.setSource(methodToSearch.getCompilationUnit());
}
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
See http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.jdt.doc.isv/guide/jdt_int_core.htm for more details on java search, the java model, and the AST.

ADO.NET Mapping From SQLDataReader to Domain Object?

I have a very simple mapping function called "BuildEntity" that does the usual boring "left/right" coding required to dump my reader data into my domain object. (shown below) My question is this - If I don't bring back every column in this mapping as is, I get the "System.IndexOutOfRangeException" exception and wanted to know if ado.net had anything to correct this so I don't need to bring back every column with each call into SQL ...
What I'm really looking for is something like "IsValidColumn" so I can keep this 1 mapping function throughout my DataAccess class with all the left/right mappings defined - and have it work even when a sproc doesn't return every column listed ...
Using reader As SqlDataReader = cmd.ExecuteReader()
Dim product As Product
While reader.Read()
product = New Product()
product.ID = Convert.ToInt32(reader("ProductID"))
product.SupplierID = Convert.ToInt32(reader("SupplierID"))
product.CategoryID = Convert.ToInt32(reader("CategoryID"))
product.ProductName = Convert.ToString(reader("ProductName"))
product.QuantityPerUnit = Convert.ToString(reader("QuantityPerUnit"))
product.UnitPrice = Convert.ToDouble(reader("UnitPrice"))
product.UnitsInStock = Convert.ToInt32(reader("UnitsInStock"))
product.UnitsOnOrder = Convert.ToInt32(reader("UnitsOnOrder"))
product.ReorderLevel = Convert.ToInt32(reader("ReorderLevel"))
productList.Add(product)
End While
Also check out this extension method I wrote for use on data commands:
public static void Fill<T>(this IDbCommand cmd,
IList<T> list, Func<IDataReader, T> rowConverter)
{
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
list.Add(rowConverter(rdr));
}
}
}
You can use it like this:
cmd.Fill(products, r => r.GetProduct());
Where "products" is the IList<Product> you want to populate, and "GetProduct" contains the logic to create a Product instance from a data reader. It won't help with this specific problem of not having all the fields present, but if you're doing a lot of old-fashioned ADO.NET like this it can be quite handy.
Although connection.GetSchema("Tables") does return meta data about the tables in your database, it won't return everything in your sproc if you define any custom columns.
For example, if you throw in some random ad-hoc column like *SELECT ProductName,'Testing' As ProductTestName FROM dbo.Products" you won't see 'ProductTestName' as a column because it's not in the Schema of the Products table. To solve this, and ask for every column available in the returned data, leverage a method on the SqlDataReader object "GetSchemaTable()"
If I add this to the existing code sample you listed in your original question, you will notice just after the reader is declared I add a data table to capture the meta data from the reader itself. Next I loop through this meta data and add each column to another table that I use in the left-right code to check if each column exists.
Updated Source Code
Using reader As SqlDataReader = cmd.ExecuteReader()
Dim table As DataTable = reader.GetSchemaTable()
Dim colNames As New DataTable()
For Each row As DataRow In table.Rows
colNames.Columns.Add(row.ItemArray(0))
Next
Dim product As Product While reader.Read()
product = New Product()
If Not colNames.Columns("ProductID") Is Nothing Then
product.ID = Convert.ToInt32(reader("ProductID"))
End If
product.SupplierID = Convert.ToInt32(reader("SupplierID"))
product.CategoryID = Convert.ToInt32(reader("CategoryID"))
product.ProductName = Convert.ToString(reader("ProductName"))
product.QuantityPerUnit = Convert.ToString(reader("QuantityPerUnit"))
product.UnitPrice = Convert.ToDouble(reader("UnitPrice"))
product.UnitsInStock = Convert.ToInt32(reader("UnitsInStock"))
product.UnitsOnOrder = Convert.ToInt32(reader("UnitsOnOrder"))
product.ReorderLevel = Convert.ToInt32(reader("ReorderLevel"))
productList.Add(product)
End While
This is a hack to be honest, as you should return every column to hydrate your object correctly. But I thought to include this reader method as it would actually grab all the columns, even if they are not defined in your table schema.
This approach to mapping your relational data into your domain model might cause some issues when you get into a lazy loading scenario.
Why not just have each sproc return complete column set, using null, -1, or acceptable values where you don't have the data. Avoids having to catch IndexOutOfRangeException or re-writing everything in LinqToSql.
Use the GetSchemaTable() method to retrieve the metadata of the DataReader. The DataTable that is returned can be used to check if a specific column is present or not.
Why don't you use LinqToSql - everything you need is done automatically. For the sake of being general you can use any other ORM tool for .NET
If you don't want to use an ORM you can also use reflection for things like this (though in this case because ProductID is not named the same on both sides, you couldn't do it in the simplistic fashion demonstrated here):
List Provider in C#
I would call reader.GetOrdinal for each field name before starting the while loop. Unfortunately GetOrdinal throws an IndexOutOfRangeException if the field doesn't exist, so it won't be very performant.
You could probably store the results in a Dictionary<string, int> and use its ContainsKey method to determine if the field was supplied.
I ended up writing my own, but this mapper is pretty good (and simple): https://code.google.com/p/dapper-dot-net/