我正在尝试使用Roslyn编写一些代码来验证一些架构分层约束以帮助进行某些程序集整合。
即: *内部类型,必须位于.Internal后缀命名空间中。 *只允许从X.Y.Z命名空间使用'X.Y.Z.Internal'类型。
解决此问题的最简单方法是根据FAQ(9)通过枚举查找所有内部类型,然后使用SymbolFinder查找所有引用并检查引用站点的包含类型。正如Get Symbol for ReferenceLocation中提到的,GetEnclosingSymbol()并没有真正为此目的返回最有用的数据。
看起来可能有一种使用SymbolFinder.FindSymbolAtPosition的有用方法,但遗憾的是,似乎ISyntaxFactsService和ISemanticModelFactsService是内部的。
这是一种耻辱,因为这似乎是尝试SemanticModelExtensions.GetSymbols()内部扩展方法使用的方法的限制因素。
我错过了一些直截了当的话吗?我当然希望如此。
答案 0 :(得分:0)
SymbolFinder
需要Workspace
,在MsBuild中运行时不需要SymbolFinder
。所以基本上如果你想使用NamedTypes
,你必须在Visual Studio中,而不是让我们说CI工作。
内部类型,必须位于.Internal后缀命名空间中。
通过在INamedSymbol
上注册符号操作可以轻松解决这个问题。在你的回调中,你将获得一个internal
,你必须检查它是否是ContainingType
,以及它是否在一个正确命名的命名空间内。 (注意,您可能需要考虑使用嵌套类型执行的操作。)
只允许从X.Y.Z命名空间使用'X.Y.Z.Internal'类型。这更难处理。如果只在VS内部工作是一个选项,那么您可以使用FindReferencesAsync
找到上述每个类型符号的所有引用。您所指的链接有很好的建议。您可以将封闭符号视为已完成here。然后你需要找到方法/属性/字段是否在适当命名的命名空间中。因此,您必须在ContainingNamespace
和SyntaxNode
上向上走符号声明树。
我猜你想把这个分析作为CI工作的一部分来运行,所以这个解决方案不适合你的需要。在这种情况下,您可以为所有标识符注册ISymbol
操作,获取基础class my_class(models.Model):
_name = "my.model"
name = fields.Char('Name', required=True)
issue_type_id = fields.Many2one('my.model.type',"My Model Type", domain='_search_my_model_types', required=True)
@api.model
def _search_my_model_types(self):
my_model_type_ids = []
return [('id', 'in', my_model_type_ids)]
class my_class_type(models.Model):
_name = "my.model.type"
name = fields.Char("Name")
和封闭符号。然后你和以前一样。所以你可以查看名字。 (此解决方案可能很慢,您必须检查性能是否适合您的需求。)
答案 1 :(得分:0)
TL; DR:我想我找到了一种有效的方法,虽然我不确定表现是否合适。
以下解决方案包括:
从上面链接的GetReferenceSymbolLocationsAsync
模式中调用AddSymbolsAsync
方法内的ReferenceLocatoinExtensions.cs
。
使用FindNode
将ReferenceLocation
转换为SyntaxNode
根据检索到的SyntaxNode
相对于其父节点的位置,进行一些调整,直到找到将从SemanticModel.GetDeclaredSymbol返回非空值的正确SyntaxNode
( )
为了能够在更高级别的代码中自定义规则,请执行一些额外的簿记。 (额外的簿记类型在最后。)
private static async Task<List<ReferencedSymbolLocation>> GetReferenceSymbolLocationsAsync(
Solution solution,
SemanticModel semanticModel,
ReferenceLocation reference,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var syntaxTree = semanticModel.SyntaxTree;
var position = reference.Location.SourceSpan.Start;
var result = new List<ReferencedSymbolLocation>();
if (position >= syntaxTree.Length)
{
return result;
}
var root = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
var foundNode = root.FindNode(reference.Location.SourceSpan, false, getInnermostNodeForTie: false);
// Try and keep track of what kind of syntactical context
// we found the referenced symbol:
ReferenceType discoveredType = ReferenceType.Other;
for (var current = foundNode; current != null; current = current.Parent)
{
cancellationToken.ThrowIfCancellationRequested();
if (current as BaseListSyntax != null)
{
discoveredType = ReferenceType.BaseClass;
continue;
}
else if (current as ClassDeclarationSyntax != null)
{
if (discoveredType == ReferenceType.Other)
{
discoveredType = ReferenceType.Class;
}
result.Add(CreateReferenceSymbolLocation(reference, semanticModel, current, discoveredType, cancellationToken));
break;
}
else if (current as PropertyDeclarationSyntax != null)
{
result.Add(CreateReferenceSymbolLocation(reference, semanticModel, current, ReferenceType.Property, cancellationToken));
break;
}
else if (current as ParameterSyntax != null)
{
// This covers method parameters and lambda parameters:
result.Add(CreateReferenceSymbolLocation(reference, semanticModel, current, ReferenceType.Parameter, cancellationToken));
break;
}
else if (current?.Parent as VariableDeclarationSyntax != null)
{
var grandparent = current?.Parent?.Parent;
var parent = current.Parent as VariableDeclarationSyntax;
if (grandparent as LocalDeclarationStatementSyntax != null)
{
discoveredType = ReferenceType.LocalVariable;
}
// Ditto for field based things:
else if (grandparent as BaseFieldDeclarationSyntax != null)
{
if (grandparent as FieldDeclarationSyntax != null)
{
discoveredType = ReferenceType.Field;
}
else if (grandparent as EventFieldDeclarationSyntax != null)
{
discoveredType = ReferenceType.Event;
}
}
else if (grandparent as ForStatementSyntax != null)
{
discoveredType = ReferenceType.ForVariable;
}
else if (grandparent as FixedStatementSyntax != null)
{
discoveredType = ReferenceType.FixedVariable;
}
else if (grandparent as UsingStatementSyntax != null)
{
discoveredType = ReferenceType.UsingVariable;
}
foreach (var variable in parent.Variables)
{
result.Add(CreateReferenceSymbolLocation(reference, semanticModel, variable, discoveredType, cancellationToken));
}
break;
}
else if (current as InvocationExpressionSyntax != null)
{
discoveredType = ReferenceType.MethodInvocation;
continue;
}
else if (current as ObjectCreationExpressionSyntax != null)
{
// This covers constructing a class directly 'new XYZ()'
// and 'new Action<XYZ>()':
discoveredType = ReferenceType.ObjectCreation;
continue;
}
else if (current as MethodDeclarationSyntax != null)
{
result.Add(CreateReferenceSymbolLocation(reference, semanticModel, current, discoveredType, cancellationToken));
break;
}
}
return result;
}
private static ReferencedSymbolLocation CreateReferenceSymbolLocation(
ReferenceLocation reference,
SemanticModel semanticModel,
SyntaxNode node,
ReferenceType referenceType,
CancellationToken cancellationToken)
{
return new ReferencedSymbolLocation(reference, semanticModel.GetDeclaredSymbol(node, cancellationToken), referenceType);
}
public enum ReferenceType
{
None = 0,
/// <summary>
/// Used for ReferenceSymbolLocations where the context of the reference
/// isn't yet in this enumeration. ReferenceSymbolLocation.ReferencedSymbol will point at the
/// declaration that contains the ReferenceLocation.
/// </summary>
Other,
Class,
BaseClass,
Field,
Property,
Parameter,
Event,
LocalVariable,
ForVariable,
FixedVariable,
UsingVariable,
// The following are related to references found inside of statements:
MethodInvocation,
ObjectCreation,
}
public class ReferencedSymbolLocation
{
public ReferenceLocation ReferenceLocation { get; private set; }
public ISymbol ReferencedSymbol { get; private set; }
public ReferenceType ReferenceType { get; private set; }
internal ReferencedSymbolLocation(ReferenceLocation location, ISymbol referencedSymbol, ReferenceType referenceType)
{
ReferenceLocation = location;
ReferencedSymbol = referencedSymbol;
ReferenceType = referenceType;
}
}