Query the operations with the XPath queries of MA0240 - #1540
Merged
Merged
Conversation
The XPath queries of the banned syntax files could only be evaluated on
the syntax tree, whereas boxing, an implicit conversion, a virtual call
or a constant are properties of the operations, which a syntactic query
can only approximate.
The operations of a file are now exposed as a second document, in the
'operation' namespace:
//operation:Conversion[@IsImplicit='true' and @TypeMetadataName='System.Object']; No boxing
//operation:Invocation[@TargetMethodName='WriteLine']; Use the logger
Each operation is an element named after its OperationKind, and the
properties of the operation are the attributes of its element, found by
reflecting over the interfaces it implements, as every implementation is
an internal type. The names of the elements are the current name of the
kind and not the one ToString returns, which is the obsolete alias for
'Binary' and 'Unary' in Roslyn 4.8.
An entry is evaluated on a single document, and the 'syntax' function
returns the nodes of the operations it is given, so a query on the
operations can continue on the syntax tree to reach what only the syntax
has, such as a verbatim string:
syntax(//operation:Invocation)//StringLiteralExpression/@token[starts-with(., '@')]
//operation:Invocation[syntax(.)//InterpolatedStringExpression]
The queries are now compiled with an XsltContext, as an expression that
is not compiled with one cannot use a function of its own, whatever the
context it is evaluated with. This context resolves the prefixes when the
query is evaluated, so an undefined prefix is now reported by the scanner
instead of throwing when the query is compiled.
The attributes that expose a symbol also have a 'Name' attribute, which
is the name of the symbol alone: 'semantic:TypeName', 'semantic:SymbolName'
and '@TargetMethodName' among others.
The rule does nothing more for the projects that do not use it: the
operations are only built for the files analyzed by an entry that uses
the 'operation' prefix.
This was referenced Sep 21, 2026
Closed
Closed
Bump Meziantou.Analyzer from 3.0.139 to 3.0.270
Analogy-LogViewer/Analogy.LogViewer.NLog.Targets#581
Closed
Closed
Bump Meziantou.Analyzer from 3.0.139 to 3.0.270
Analogy-LogViewer/Analogy.AspNetCore.LogProvider#562
Closed
Closed
Closed
This was referenced Sep 28, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The XPath queries of the banned syntax files could only be evaluated on the syntax tree. Boxing, an implicit conversion, a virtual call or a constant folded by the compiler are properties of the operations, and a syntactic query can only approximate them. This exposes the operations as a second document, queryable with the same file format, the same severities and the same MA0241 validation.
What changed
The operation document. Each operation is an element of the
operationnamespace named after itsOperationKind, and the children of an element are the child operations. The operations of a file form a forest (method bodies, field/property/parameter initializers, attributes), so the document has one element per root.The attributes are unprefixed and named after the properties of the operation interfaces (
@IsImplicit,@OperatorKind,@TargetMethod). They are discovered by reflecting over the interfaces the operation implements, because every implementation inMicrosoft.CodeAnalysisis an internal type. A property returning a type expands to the seven suffixes thesemantic:attributes already use, another symbol to five, andConstantValueto@HasConstantValue/@ConstantValue.semantic:is deliberately not exposed on the operations: that vocabulary was built onGetTypeInfo/GetSymbolInfoof a syntax node and half of it has no meaning there. A conversion is a node, soConvertedTypehas no equivalent, andTypewould get a second name. An entry is evaluated on one document, and using both prefixes is reported by MA0241.The
syntaxfunction returns the syntax nodes of the operations it is given, so a query on the operations can continue on the syntax tree and reach what only the syntax has:A function call cannot be a step of a path, so the function takes the operations as its argument and the path continues after the call.
syntax(.)in a predicate is the node of the operation being tested.A
Nameattribute was added everywhere a symbol is exposed, on both documents:semantic:TypeName,semantic:ConvertedTypeName,semantic:ReturnTypeName,semantic:ContainingTypeName,semantic:SymbolName,semantic:ContainingSymbolName, and@TargetMethodName,@TypeNameand friends on the operations.Notes for the reviewer
Two findings are worth a look, as they are behaviour and not refactoring.
OperationKindhas members that share a value, and the nameToStringreturns for such a value is not the same in every version of Roslyn:OperationKind.Binary.ToString()is"BinaryOperator"on Roslyn 4.8 and"Binary"on 5.9. The elements are therefore named after the current name of the kind, derived from the members of the enumeration, so//operation:Binarymeans the same thing on every version. The obsolete alias is reported by MA0241 instead of silently selecting nothing. The multi-version CI is what caught this.An expression that is not compiled with an
XsltContextcannot use a function of its own, whatever the context it is evaluated with, so the queries are now compiled with one. A consequence is that this context resolves the prefixes when the query is evaluated rather than when it is compiled, so an undefined prefix no longer throws at compile time: it is now reported by the scanner, which already walks every prefix, with a better message than the engine produced.Cost. The rule still does nothing for the projects without a banned syntax file, and the operations are only built for the files analysed by an entry that uses the
operationprefix. The navigator of the syntax tree used by thesyntaxfunction is only built when an entry calls it. The attributes of an operation are computed once per file, and the reflection is done once per operation type per process.Refactoring.
SyntaxNodeXPathNavigatorlost the pieces the two navigators share (the attribute record, the type names, the formatters, the namespaces), which moved to their own files. Its behaviour is unchanged, and the existing tests of MA0240 are the regression signal.Validation
dotnet teston the five Roslyn versions: 24 335 tests, all passing.DoNotUseBannedSyntaxAnalyzerTests, including a smoke test that computes the attributes of every kind of operation over a file exercising every node kind, and a test for each query printed in the documentation.dotnet run --project src/DocumentationGeneratorreports no pending change.