-
Notifications
You must be signed in to change notification settings - Fork 75
Implement package SideEffects6 with rule 28.3.1, predicate should not have side effects #1051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f381feb
8dba813
1b83422
dd8135c
372fd8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
|---|---|---|
| @@ -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())) | ||
| } | ||
| } |
| 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()`. | ||
| */ | ||
| 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 } | ||
| } | ||
| 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. | ||||||
|
||||||
| * 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
AI
Feb 23, 2026
There was a problem hiding this comment.
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").
| * 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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| #define _GHLIBCPP_VECTOR | ||
| #include <iterator> | ||
| #include <string> | ||
| #include "memory.h" | ||
|
|
||
| namespace std { | ||
|
|
||
|
|
||
There was a problem hiding this comment.
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.