Contenuto principale

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
In this syntax:

  • name is 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 standard

  • query is 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.

  • exception is the condition where the defect is not reported. This is a logical combination of predicates. If the condition in exceopion is met, no defects are reported even if the condition in query is satisfied. The except clause is optional and can be omitted.

  • message is 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 the Printable attribute set to Yes can be printed as strings in the message.

  • variable is 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...
Where

  • predicate1, predicate2, ... predicateN are predicates. Predicates can be:

    • A 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, this not operation is supported:

      Cpp.Node.is (&node)
      and not Cpp.PreprocDef.isa(node) // from all nodes, finds the nodes that are not preprocessor definitions
      On the other hand, this negation operation is not supported:
      Cpp.PreprocDef.is(&pd)
      and not pd.value(&val) //meaningless
      In most cases, negating a predicate with an output parameter is meaningless. PQL classes supports boolean predicates that are explicitly designed for use with the not operator. 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)
The parameters 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)
You do not have to explicitly declare these variables. Because both 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)
When using a predefined predicate, you can choose to ignore an output variable by using & 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)
You can ignore one or more of the output variables when calling this predicate. For example, this code find the file in which a macro is defined, and ignores the line and column:
Cpp.Macro.is(&macro)
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
}
Where:

  • predicateName is the identifier of the predicate function.

  • type1, type2, ... , typeN are the type identifiers of the parameters varName1, varName2, ... , varNameN. A parameter is an output parameter when it is preceded by the character &.

  • predicateExpression is a valid PQL predicate, which can be a single predicate or a logical combination of multiple predicates. predicateExpression cannot be a recursive call to predicateName. PQL does not allow recursive predicates.

  • Attributes are 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 issues value as a warning.

    • NoWarning — Specify that warnings arising from the predicate is suppressed. This attribute does not accept any value.

    • 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
The 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"
Here, 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 field
In this defect, the unused public field name is extracted as a string. Then the name is inserted in the message to provide contextual information. Many PQL types has an associated predicate that retrieves their names as string. The types can also have toString() 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
Here, 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)
Here, 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(&macro)
        and macro.sourceLocation(&file, &line, &column)
    raise "This is a macro"
    at { file, line, column }   

See Also

Topics