Polyspace Query Language Syntax for Creating User-Defined Defects
Polyspace Query Language (PQL) allows you to create new defects to check for issues specific to your code. A user-defined defect is a query that you run on your code. The query filters the semantic and syntactic information available in your code and flags constructs where the defect occurs.
In PQL, declare a defect using this syntax:
defect name = when query except exception // optional raise message on variable
nameis the identifier of the user-defined defect in the a package. Use this identifier to refer to this defect when developing a user-defined coding standardqueryis the condition for reporting an occurrence of this defect in your code. You can think of the query as a Boolean function. When the query is true, Polyspace® reports a defect. This query can be a logical combination predefined PQL predicates. PQL does not support negation of predicates that has an output parameter.exceptionis the condition where the defect is not reported. This is a logical combination of predicates. If the condition inexceopionis met, no defects are reported even if the condition inqueryis satisfied. Theexceptclause is optional and can be omitted.messageis the contextual message displayed by Polyspace when it reports the defect in various user interfaces. The message can be a string variable or a constant string. PQL classes that has thePrintableattribute set toYescan be printed as strings in the message.variableis a variable that Polyspace reports the defect on. If you specify a variable on which a violation cannot be reported, Polyspace issues an error.
These components are described in the subsequent sections.
Syntax for Creating Queries
A query consists of one predicate or a logical combination of multiple predicates:
predicate1 op1 predicate2 op2 predicateN...
,predicate1, ...predicate2are predicates. Predicates can be:predicateNA supported predefined PQL predicate. For more information, see the classes in the category Create Your Own Coding Rules and Coding Standard.
A call to a user-defined predicate function.
A comparison operation that evaluate to a Boolean value. Supported comparison operators are:
==,!=,<, and>.
op1,op2... are logical operations. Supported operations include:and— Returns true if both operands are true.or— Returns true if at least one operand is true.not— Returns the negated value of the operand. This operator is not supported if the operand is a predicate with an output parameter. For example, thisnotoperation is supported:On the other hand, this negation operation is not supported:Cpp.Node.is (&node) and not Cpp.PreprocDef.isa(node) // from all nodes, finds the nodes that are not preprocessor definitions
In most cases, negating a predicate with an output parameter is meaningless. PQL classes supports boolean predicates that are explicitly designed for use with theCpp.PreprocDef.is(&pd) and not pd.value(&val) //meaningless
notoperator. Use the boolean predicates instead of predicates with output parameters.
Syntax for Using Predefined Predicates
PQL classes support predefined predicates that query specific properties of the class. Predicates have the syntax:
predicateName(var1, var2, ..., varN)
var1, var2, ...,
varN can be input parameters or output parameters. Output
parameters are designated by prefixing the parameter with the character
&. The type of each parameter is inferred based on its first use. For a list of supported types, see General Polyspace Query Language Syntax. For a list of supported classes, see Create Your Own Coding Rules and Coding Standard.
The parameters of predicates act as variables in subsequent code. For example, this code
queries your code and checks for syntactic nodes. The variable node is
defined in this statement and remains a valid variable in the next predicate, which also
declares the variable
statement:
Cpp.Node.is(&node) and node.getADescendant(&child)
node and child are output
parameters, they hold the result of the predicates. Each predicate is allowed to have only
one output parameter and the output parameter is typically the last parameter.When you combine multiple predicates using logical operators, it is possible that
subsequent predicates are run on the result of the previous predicates. In the preceding
example, the output variable node holds the results of the predicate
cpp.isaNode all the syntactical nodes of your code The next predicate
limits itself to the content of node by using dot notation. That is,
node.isIfStatement(&statement) runs the query
isIfStatement on the content of node and stores the
result in the variable statement. This technique is useful for running
multistep queries on your code.
Once the type of a variable is inferred, attempts to change the type results in an
error. For example, in this code, node is first inferred to be a variable
of Node type. Using this variable as an IfStatement
type in the next predicate results in a compile
fail:
Cpp.Node.is(&node) and Cpp.IfStatement.is(&node)
& as a placeholder
instead of the variable name. For example, consider the predicate
Cpp.Macro.sourceLocation:sourceLocation(Macro self, Cpp.SourceLoc.Path &file, Lang.Int &line, Lang.Int &column)
Cpp.Macro.is(¯o) and macro.sourceLocation(&file, &, &)
Syntax for Defining New User-Defined Predicate Functions
In addition to the predefined predicates, you can define your own predicates that uses one or more existing predicates. The syntax for defining a predicate function is:
#[Attribute]
predicate predicateName (type1 varName1, type2 varName2, ..., typeN varNameN){
return
predicateExpression
}predicateNameis the identifier of the predicate function.type1,type2, ... ,typeNare the type identifiers of the parametersvarName1,varName2, ... ,varNameN. A parameter is an output parameter when it is preceded by the character&.predicateExpressionis a valid PQL predicate, which can be a single predicate or a logical combination of multiple predicates.predicateExpressioncannot be a recursive call topredicateName. PQL does not allow recursive predicates.Attributesare keywords that associate specific properties to the predicate. For example:Deprecated— Specify that the predicate is no longer in use. If any code uses a component specified by this attribute, Polyspace issuesvalueas a warning.NoWarning— Specify that warnings arising from the predicate is suppressed. This attribute does not accept anyvalue.NoInline— Specify that when creating the user-defined coding standard, the predicate is not inlined. Such predicates cannot accept an input parameter. This attribute is used for debugging.
Syntax for Creating Context-Sensitive Messages
A user-defined defect has this syntax:
defect name = when query except exception raise message on variable
message following the raise keyword is
reported in the Results Details pane when you review results for
user-defined defects. Polyspace expects message to be a string. It can be a string
literal, a string object, or the return value of a predicate that returns a string. For
easier result review, add contextual information in the string.To insert contextual information in the message, use this
syntax:
raise "messageCompoment \"{stringVar}\" messageCompoment2"messageCompoment and
messageCompoment2 are string literals or string variables.
stringVar is contextual information such as token name or
component name. This is PQL identifier converted to string. For
example:defect Exampledefect =
when
Cpp.Field.is(&field)
and field.isUnused()
and field.isPublic()
and field.extension(&ext)
and ext == ".h"
and field.filename(&fnstr)
raise "Unused public field in header: \"{fnstr}\""
on fieldtoString() predicates that retrieves the type as a string, which can
be reported in the message.Syntax for Reporting Defects
A user-defined defect can be reported on Raisable type variable like
this:
defect name = when query raise message on variable
variable is an object of Raisable type.
Types that inherit from ObjectWithPositions type or the
AstNodeProperties are raisable types. If you do not have a raisable variable, report the defect on the tuple (file,
line,column):
defect name = when query raise message at (file, line, column)
file must be the name of a file extracted as a string variable,
and line and column must be the
desired line and column numbers extracted as string. This syntax is useful when dealing with
types that do not have a fixed location. For example, this defect reports a result on a
macro:defect Defect1 =
when
Cpp.Macro.is(¯o)
and macro.sourceLocation(&file, &line, &column)
raise "This is a macro"
at { file, line, column }