Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions cpp/common/src/codingstandards/cpp/ast/Templates.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import cpp

/**
* A predicate to simplify getting a namespace for a template parameter, since
* `TemplateParameterType`'s `getNamespace()` has no result, and `enclosingElement()` has no result,
* and there are multiple cases to navigate to work around this.
*/
Namespace getTemplateParameterNamespace(TypeTemplateParameter param) {
exists(Declaration decl |
param = decl.(TemplateClass).getATemplateArgument() or
param = decl.(TemplateVariable).getATemplateArgument() or
param = decl.(TemplateFunction).getATemplateArgument()
|
result = decl.getNamespace()
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import Representation
import Scope
import SideEffects1
import SideEffects2
import SideEffects6
import SmartPointers1
import SmartPointers2
import Statements
Expand Down Expand Up @@ -134,6 +135,7 @@ newtype TCPPQuery =
TScopePackageQuery(ScopeQuery q) or
TSideEffects1PackageQuery(SideEffects1Query q) or
TSideEffects2PackageQuery(SideEffects2Query q) or
TSideEffects6PackageQuery(SideEffects6Query q) or
TSmartPointers1PackageQuery(SmartPointers1Query q) or
TSmartPointers2PackageQuery(SmartPointers2Query q) or
TStatementsPackageQuery(StatementsQuery q) or
Expand Down Expand Up @@ -207,6 +209,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
isScopeQueryMetadata(query, queryId, ruleId, category) or
isSideEffects1QueryMetadata(query, queryId, ruleId, category) or
isSideEffects2QueryMetadata(query, queryId, ruleId, category) or
isSideEffects6QueryMetadata(query, queryId, ruleId, category) or
isSmartPointers1QueryMetadata(query, queryId, ruleId, category) or
isSmartPointers2QueryMetadata(query, queryId, ruleId, category) or
isStatementsQueryMetadata(query, queryId, ruleId, category) or
Expand Down
44 changes: 44 additions & 0 deletions cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects6.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
import cpp
import RuleMetadata
import codingstandards.cpp.exclusions.RuleMetadata

newtype SideEffects6Query =
TPredicateWithPersistentSideEffectsQuery() or
TNonConstPredicateFunctionObjectQuery()

predicate isSideEffects6QueryMetadata(Query query, string queryId, string ruleId, string category) {
query =
// `Query` instance for the `predicateWithPersistentSideEffects` query
SideEffects6Package::predicateWithPersistentSideEffectsQuery() and
queryId =
// `@id` for the `predicateWithPersistentSideEffects` query
"cpp/misra/predicate-with-persistent-side-effects" and
ruleId = "RULE-28-3-1" and
category = "required"
or
query =
// `Query` instance for the `nonConstPredicateFunctionObject` query
SideEffects6Package::nonConstPredicateFunctionObjectQuery() and
queryId =
// `@id` for the `nonConstPredicateFunctionObject` query
"cpp/misra/non-const-predicate-function-object" and
ruleId = "RULE-28-3-1" and
category = "required"
}

module SideEffects6Package {
Query predicateWithPersistentSideEffectsQuery() {
//autogenerate `Query` type
result =
// `Query` type for `predicateWithPersistentSideEffects` query
TQueryCPP(TSideEffects6PackageQuery(TPredicateWithPersistentSideEffectsQuery()))
}

Query nonConstPredicateFunctionObjectQuery() {
//autogenerate `Query` type
result =
// `Query` type for `nonConstPredicateFunctionObject` query
TQueryCPP(TSideEffects6PackageQuery(TNonConstPredicateFunctionObjectQuery()))
}
}
118 changes: 118 additions & 0 deletions cpp/common/src/codingstandards/cpp/types/Predicate.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* A library for handling "predicate" types, which are function parameters in the standard library.
*
* For example, `std::sort` takes a predicate as its third argument, and `std::set` takes a
* predicate as its second template parameter. Predicates are always template parameters, and we
* can identify them by their name -- this is what MISRA expects us to do.
*/

import cpp
private import codingstandards.cpp.StdNamespace
private import codingstandards.cpp.ast.Templates
private import codingstandards.cpp.types.Templates
private import semmle.code.cpp.dataflow.new.DataFlow

/**
* A "predicate" type parameter as defined by MISRA, which is a standard library function named
* "Compare" or "Predicate" or "BinaryPredicate" in the standard library.
*
* To be more widely useful, we match more flexibly on the name, as `_Compare` is also common, etc.
*
* To get a particular `Type` that is _used_ as a predicate type, see `getASubstitutedType()`,
* rather than the type parameter itself, see `getASubstitutedType()`.
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment repeats the same guidance twice ("see getASubstitutedType()" in consecutive lines). This should be clarified to avoid confusion about whether a different API was intended for the second reference.

Suggested change
* rather than the type parameter itself, see `getASubstitutedType()`.
* rather than inspecting the type parameter itself.

Copilot uses AI. Check for mistakes.
*/
class PredicateType extends TypeTemplateParameter {
PredicateType() {
this.getName().matches(["%Compare%", "%Predicate%"]) and
getTemplateParameterNamespace(this) instanceof StdNS
}

/**
* Get a type that is used (anywhere, via some substitution) as this predicate type parameter.
*
* For example, `std::sort(..., ..., [](int a, int b) { return a < b; })` creates a `Closure`
* type, and substitutes that closure type for the predicate type parameter of `std::sort`.
*/
Type getASubstitutedType(Substitution sub) { result = sub.getSubstitutedTypeForParameter(this) }
}

/**
* A class type that has been substituted for a predicate type parameter, and has an `operator()`
* member function.
*
* For example, the closure type in `std::sort(..., ..., [](int a, int b) { return a < b; })` is a
* `PredicateFunctionObject`, and so is any `std::less<int>` that is used as a predicate type
* parameter, etc.
*
* This does not cover function pointer types, as these are not class types.
*/
class PredicateFunctionObject extends Class {
PredicateType pred;
Function operator;
Substitution sub;

PredicateFunctionObject() {
this = pred.getASubstitutedType(sub) and
operator.getDeclaringType() = this and
operator.getName() = "operator()"
}

/**
* Get the predicate type parameter that this function object is being substituted for.
*/
PredicateType getPredicateType() { result = pred }

/**
* Get the `operator()` function that this function object defines. This is the function that will
* be invoked and essentially defines the predicate behavior.
*/
Function getCallOperator() { result = operator }

/**
* Get the `Substitution` object that makes this type a `PredicateFunctionObject`.
*
* This is a particular instantiation of some template that contains a predicate type parameter,
* which is substituted by this type in that instantiation. The `Substitution` object may refer
* to a `TemplateClass`, `TemplateVariable`, or `TemplateFunction`.
*/
Substitution getSubstitution() { result = sub }
}

/**
* Gets a function access where the purpose of that access is to pass the accessed function as a
* predicate argument to a standard library template.
*
* For example, in `std::sort(..., ..., myCompare)`, where `myCompare` is a function, then
* `myCompare` will be converted into a function pointer and that function pointer will be used as
* a predicate in that `std::sort` call.
*
* This is more complex to identify than `PredicateFunctionObject` because the addressee of the
* function pointer is not necessarily statically known.
*/
class PredicateFunctionPointerUse extends FunctionAccess {
Expr functionPointerArgument;
FunctionCall templateFunctionCall;
FunctionTemplateInstantiation instantiation;
Substitution sub;
PredicateType pred;
Parameter parameter;
int index;

PredicateFunctionPointerUse() {
functionPointerArgument = templateFunctionCall.getArgument(index) and
templateFunctionCall.getTarget() = instantiation and
parameter = instantiation.getParameter(index) and
sub.asFunctionSubstitution() = instantiation and
parameter.getType() = sub.getSubstitutedTypeForParameter(pred) and
exists(DataFlow::Node func, DataFlow::Node arg |
func.asExpr() = this and
arg.asExpr() = functionPointerArgument and
DataFlow::localFlow(func, arg)
)
}

/**
* Get the predicate type parameter that this function pointer is being substituted for.
*/
PredicateType getPredicateType() { result = pred }
}
80 changes: 80 additions & 0 deletions cpp/common/src/codingstandards/cpp/types/Templates.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import cpp

private newtype TSubstitution =
TClassSubstitution(ClassTemplateInstantiation cti) or
TFunctionSubstitution(FunctionTemplateInstantiation fti) or
TVariableSubstitution(VariableTemplateInstantiation vti)

/**
* A class to facilitate working with template substitutions, especially since templates may be a
* `TemplateClass`, `TemplateFunction`, or `TemplateVariable`, which complicates their usage.
*
* A `Substitution` in particular refers to an instantiation of that template of some kind, and
* allows analysis of which parameters were substituted with which types in that instatiation.
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in doc comment: "instatiation" should be "instantiation".

Suggested change
* allows analysis of which parameters were substituted with which types in that instatiation.
* allows analysis of which parameters were substituted with which types in that instantiation.

Copilot uses AI. Check for mistakes.
*/
class Substitution extends TSubstitution {
ClassTemplateInstantiation asClassSubstitution() { this = TClassSubstitution(result) }

FunctionTemplateInstantiation asFunctionSubstitution() { this = TFunctionSubstitution(result) }

VariableTemplateInstantiation asVariableSubstitution() { this = TVariableSubstitution(result) }

/**
* Get the nth template parameter type of the template that is being substituted.
*
* For example, in `std::vector<int>`, the 0th template parameter is `typename T`.
*/
TypeTemplateParameter getTemplateParameter(int index) {
result = this.asClassSubstitution().getTemplate().getTemplateArgument(index) or
result = this.asFunctionSubstitution().getTemplate().getTemplateArgument(index) or
result = this.asVariableSubstitution().getTemplate().getTemplateArgument(index)
}

/**
* Get the type that is substituting the nth template parameter in this substitution.
*
* For example, in `std::vector<int>`, the 0th substituted type is `int`.
*/
Type getSubstitutedType(int index) {
result = this.asClassSubstitution().getTemplateArgument(index) or
result = this.asFunctionSubstitution().getTemplateArgument(index) or
result = this.asVariableSubstitution().getTemplateArgument(index)
}

/**
* Get the type that is substituting the given template parameter in this substitution.
*
* For example, in `std::vector<int>`, this predicate matches the given type `int` with the type
* parameter `typename T`.
*/
Type getSubstitutedTypeForParameter(TypeTemplateParameter param) {
exists(int idx |
this.getTemplateParameter(idx) = param and
result = this.getSubstitutedType(idx)
)
}

string toString() {
result = this.asClassSubstitution().toString() or
result = this.asFunctionSubstitution().toString() or
result = this.asVariableSubstitution().toString()
}

/**
* Get a `Locatable` that represents a where this substitution was declared in the source code.
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar in doc comment is off: "represents a where" should be reworded (e.g., "represents where" or "represents a location where").

Suggested change
* Get a `Locatable` that represents a where this substitution was declared in the source code.
* Get a `Locatable` that represents where this substitution was declared in the source code.

Copilot uses AI. Check for mistakes.
*
* The result may be a `TypeMention`, `Call`, etc. depending on the kind of template and how it is
* being used, but it handles the various template cases for you.
*
* Note that this instantiation may have been declared in multiple places.
*/
Locatable getASubstitutionSite() {
result.(TypeMention).getMentionedType() = this.asClassSubstitution()
or
result.(Call).getTarget() = this.asFunctionSubstitution()
or
result.(FunctionAccess).getTarget() = this.asFunctionSubstitution()
or
result.(VariableAccess).getTarget() = this.asVariableSubstitution()
}
}
1 change: 1 addition & 0 deletions cpp/common/test/includes/standard-library/deque.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define _GHLIBCPP_DEQUE
#include <iterator>
#include <string>
#include "memory.h"

namespace std {
template <class T, class Allocator = std::allocator<T>> class deque {
Expand Down
16 changes: 16 additions & 0 deletions cpp/common/test/includes/standard-library/functional.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,21 @@ template <class R, class... Args> class function<R(Args...)> {
template <class F> function(F &&);
template <class F> function &operator=(F &&);
};

template <typename T>
struct less {
bool operator()(const T &x, const T &y) const;
typedef T first_argument_type;
typedef T second_argument_type;
typedef bool result_type;
};

template <typename T>
struct greater {
bool operator()(const T &x, const T &y) const;
typedef T first_argument_type;
typedef T second_argument_type;
typedef bool result_type;
};
} // namespace std
#endif
1 change: 1 addition & 0 deletions cpp/common/test/includes/standard-library/iosfwd.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#ifndef _GHLIBCPP_IOSFWD
#define _GHLIBCPP_IOSFWD
#include "memory.h"
namespace std {
template <class charT> class char_traits;
template <> class char_traits<char>;
Expand Down
3 changes: 2 additions & 1 deletion cpp/common/test/includes/standard-library/map
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "iterator.h"
#include <bits/stl_function.h>
#include "memory.h"
#include "functional.h"

namespace std {

Expand Down
6 changes: 6 additions & 0 deletions cpp/common/test/includes/standard-library/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ class bad_alloc : public exception {
bad_alloc &operator=(const bad_alloc &) noexcept;
virtual const char *what() const noexcept;
};

template <class T> class allocator {
public:
allocator() throw();
typedef size_t size_type;
};
} // namespace std

#endif // _GHLIBCPP_MEMORY
3 changes: 2 additions & 1 deletion cpp/common/test/includes/standard-library/set
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "iterator.h"
#include <bits/stl_function.h>
#include "memory.h"
#include "functional.h"

namespace std {
template <typename _Key, typename _Compare = std::less<_Key>,
Expand Down
7 changes: 1 addition & 6 deletions cpp/common/test/includes/standard-library/string
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef _GHLIBCPP_STRING
#define _GHLIBCPP_STRING
#include "cwchar"
#include "memory.h"
#include "initializer_list"
#include "ios.h"
#include "iosfwd.h"
Expand Down Expand Up @@ -88,12 +89,6 @@ template <> struct char_traits<wchar_t> {
static int_type eof();
};

template <class T> class allocator {
public:
allocator() throw();
typedef size_t size_type;
};

template <class charT, class traits = char_traits<charT>,
class Allocator = allocator<charT>>
class basic_string {
Expand Down
1 change: 1 addition & 0 deletions cpp/common/test/includes/standard-library/vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define _GHLIBCPP_VECTOR
#include <iterator>
#include <string>
#include "memory.h"

namespace std {

Expand Down
Loading
Loading