diff --git a/c/misra/src/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.ql b/c/misra/src/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.ql index da73214859..e593e62812 100644 --- a/c/misra/src/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.ql +++ b/c/misra/src/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.ql @@ -16,6 +16,7 @@ import cpp import codingstandards.c.misra import codingstandards.c.Objects +import codingstandards.cpp.Expr /** * Holds if the value of an expression is used or stored. @@ -37,32 +38,14 @@ predicate isUsedOrStored(Expr e) { e = any(ClassAggregateLiteral l).getAFieldExpr(_) } -/** - * Find expressions that defer their value directly to an inner expression - * value. - * - * When an array is on the rhs of a comma expr, or in the then/else branch of a - * ternary expr, and the result us used as a pointer, then the ArrayToPointer - * conversion is marked inside comma expr/ternary expr, on the operands. These - * conversions are only non-compliant if they flow into an operation or store. - * - * Full flow analysis with localFlowStep should not be necessary, and may cast a - * wider net than needed for some queries, potentially resulting in false - * positives. - */ -Expr temporaryObjectFlowStep(Expr e) { - e = result.(CommaExpr).getRightOperand() - or - e = result.(ConditionalExpr).getThen() - or - e = result.(ConditionalExpr).getElse() -} - from FieldAccess fa, TemporaryObjectIdentity temporary, ArrayToPointerConversion conversion where not isExcluded(conversion, InvalidMemory3Package::arrayToPointerConversionOfTemporaryObjectQuery()) and fa = temporary.getASubobjectAccess() and conversion.getExpr() = fa and - isUsedOrStored(temporaryObjectFlowStep*(conversion.getExpr())) + exists(Expr useOrStore | + temporaryObjectFlowStep*(conversion.getExpr(), useOrStore) and + isUsedOrStored(useOrStore) + ) select conversion, "Array to pointer conversion of array $@ from temporary object $@.", fa.getTarget(), fa.getTarget().getName(), temporary, temporary.toString() diff --git a/change_notes/2025-05-20-update-integer-literal-lexing.md b/change_notes/2025-05-20-update-integer-literal-lexing.md new file mode 100644 index 0000000000..0fc7c0155c --- /dev/null +++ b/change_notes/2025-05-20-update-integer-literal-lexing.md @@ -0,0 +1,2 @@ + - All queries related to integer suffixes: + - No visible changes expected: the regex for parsing integer suffixes, and how they are treated after lexing, has been refactored. \ No newline at end of file diff --git a/change_notes/2025-06-03-refactor-temporary-object-flow-step.md b/change_notes/2025-06-03-refactor-temporary-object-flow-step.md new file mode 100644 index 0000000000..3521ec4928 --- /dev/null +++ b/change_notes/2025-06-03-refactor-temporary-object-flow-step.md @@ -0,0 +1,2 @@ + - `RULE-18-9` - `ArraytoPointerConversionOfTemporaryObject.ql` + - The behavior for finding flow steps of temporary objects (for example, from ternary branches to the ternary expr result) has been extracted for reuse in other rules, no visible changes expected. \ No newline at end of file diff --git a/cpp/cert/test/rules/OOP54-CPP/GracefullyHandleSelfCopyAssignment.expected b/cpp/cert/test/rules/OOP54-CPP/GracefullyHandleSelfCopyAssignment.expected index 442f211c78..bd4e47723f 100644 --- a/cpp/cert/test/rules/OOP54-CPP/GracefullyHandleSelfCopyAssignment.expected +++ b/cpp/cert/test/rules/OOP54-CPP/GracefullyHandleSelfCopyAssignment.expected @@ -1,2 +1,2 @@ -| test.cpp:57:6:57:14 | operator= | User defined copy or user defined move does not handle self-assignment correctly. | -| test.cpp:66:6:66:14 | operator= | User defined copy or user defined move does not handle self-assignment correctly. | +| test.cpp:56:6:56:14 | operator= | User defined copy or user defined move does not handle self-assignment correctly. | +| test.cpp:65:6:65:14 | operator= | User defined copy or user defined move does not handle self-assignment correctly. | diff --git a/cpp/cert/test/rules/OOP54-CPP/test.cpp b/cpp/cert/test/rules/OOP54-CPP/test.cpp index 9e1bdcd4f4..8e63ab4537 100644 --- a/cpp/cert/test/rules/OOP54-CPP/test.cpp +++ b/cpp/cert/test/rules/OOP54-CPP/test.cpp @@ -1,6 +1,5 @@ #include #include -#include class A { public: diff --git a/cpp/common/src/codingstandards/cpp/Cpp14Literal.qll b/cpp/common/src/codingstandards/cpp/Cpp14Literal.qll index ca3a7fb251..8e0c089f4d 100644 --- a/cpp/common/src/codingstandards/cpp/Cpp14Literal.qll +++ b/cpp/common/src/codingstandards/cpp/Cpp14Literal.qll @@ -12,11 +12,33 @@ module Cpp14Literal { /** Convenience for implementing class `UnrecognizedNumericLiteral` */ abstract private class RecognizedNumericLiteral extends StandardLibrary::Literal { } + /** An integer literal suffix (e.g. 'u', 'll', etc, or even an empty string). */ + class IntegerLiteralSuffix extends string { + string uPart; + string lPart; + + IntegerLiteralSuffix() { + uPart = ["", "u", "U"] and + lPart = ["", "l", "L"] + ["", "l", "L"] and + this = uPart + lPart + } + + predicate isSigned() { uPart = "" } + + predicate isUnsigned() { uPart != "" } + + int getLCount() { result = lPart.length() } + } + /** An integer literal. */ abstract class IntegerLiteral extends NumericLiteral { predicate isSigned() { not isUnsigned() } - predicate isUnsigned() { getValueText().regexpMatch(".*[uU][lL]?$") } + predicate isUnsigned() { getSuffix().isUnsigned() } + + IntegerLiteralSuffix getSuffix() { + result = getValueText().regexpCapture("[^uUlL]*([uU]?[lL]*)\\s*$", 1) + } } /** diff --git a/cpp/common/src/codingstandards/cpp/Expr.qll b/cpp/common/src/codingstandards/cpp/Expr.qll index 0b650ae41b..3fc25b9996 100644 --- a/cpp/common/src/codingstandards/cpp/Expr.qll +++ b/cpp/common/src/codingstandards/cpp/Expr.qll @@ -242,6 +242,28 @@ predicate isCompileTimeEvaluatedExpression(Expr expression) { ) } +/** + * Find expressions that defer their value directly to an inner expression + * value -- how temporary object can flow without being copied or having their + * address taken. + * + * Full flow analysis with localFlowStep is often not necessary for certain + * rules related to temporary objects, and may cast a wider net than needed for + * those queries. + * + * When an array is on the rhs of a comma expr, or in the then/else branch of a + * ternary expr, and the result us used as a pointer, then the ArrayToPointer + * conversion is marked inside comma expr/ternary expr, on the operands. These + * conversions are only non-compliant if they flow into an operation or store. + */ +predicate temporaryObjectFlowStep(Expr source, Expr sink) { + source = sink.(CommaExpr).getRightOperand() + or + source = sink.(ConditionalExpr).getThen() + or + source = sink.(ConditionalExpr).getElse() +} + predicate isDirectCompileTimeEvaluatedExpression(Expr expression) { expression instanceof Literal or diff --git a/cpp/common/src/codingstandards/cpp/Lambda.qll b/cpp/common/src/codingstandards/cpp/Lambda.qll new file mode 100644 index 0000000000..d2e02b18f7 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Lambda.qll @@ -0,0 +1,238 @@ +import codingstandards.cpp.alertreporting.CustomPathStateProblem +import codingstandards.cpp.Expr +import codingstandards.cpp.Scope + +signature class LambdaSig extends LambdaExpression; + +class ImplicitThisCapturingLambdaExpr extends LambdaExpression { + ImplicitThisCapturingLambdaExpr() { + exists(LambdaCapture capture | + capture = getACapture() and + capture.getField().getName() = "(captured this)" and + capture.isImplicit() + ) + } +} + +class ImplicitCaptureLambdaExpr extends LambdaExpression { + LambdaCapture capture; + + ImplicitCaptureLambdaExpr() { capture = getCapture(_) and capture.isImplicit() } + + LambdaCapture getImplicitCapture() { result = capture } +} + +/** + * A module to find lambda expressions that are eventually copied or moved. + * + * Unfortunately, CodeQL does not capture all lambda flow or all lambda copies/moves. However, + * since lambdas can only be used in an extremely limited number of ways, we can easily roll our + * own dataflow-like analysis as a custom path problem, to match lambdas to stores. + */ +module TransientLambda { + final class FinalLocatable = Locatable; + + /** + * Create a custom path problem, which inherently performs a graph search to find paths from start + * nodes (in this case, lambda expressions) to end nodes (in this case, copies and moves of + * lambdas). + */ + private module TransientLambdaConfig implements CustomPathStateProblemConfigSig { + class Node extends FinalLocatable { + Node() { + this instanceof Source + or + this instanceof Variable + or + this.(VariableAccess).getTarget() instanceof Parameter + or + this instanceof Expr + or + this instanceof Function + or + this instanceof NewExpr + } + } + + class State = TranslationUnit; + + // Do not search past the first copy or move of a lambda. + predicate searchPastEnd() { none() } + + predicate start(Node n, TranslationUnit state) { n instanceof Source and state = n.getFile() } + + bindingset[state] + pragma[inline_late] + predicate end(Node n, TranslationUnit state) { + n instanceof Variable and not n instanceof Parameter + or + n instanceof Function and + not functionDefinedInTranslationUnit(n, state) + or + exists(NewExpr alloc | + alloc = n and + alloc.getAllocatedType().stripTopLevelSpecifiers() instanceof Closure + ) + } + + predicate edge(Node a, TranslationUnit s1, Node b, TranslationUnit s2) { + s2 = s1 and + ( + a = b.(Variable).getInitializer().getExpr() + or + exists(Call fc, int i | + a = fc.getArgument(i) and + ( + b = fc.getTarget().getParameter(i) + or + b = fc.getTarget() + ) + ) + or + exists(Call fc, Function f, ReturnStmt ret | + f = fc.getTarget() and + ret.getEnclosingFunction() = f and + a = ret.getExpr() and + b = fc + ) + or + b = a.(Parameter).getAnAccess() + or + temporaryObjectFlowStep(a, b) + or + a = b.(NewExpr).getInitializer() + ) + } + } + + import CustomPathStateProblem as TransientFlow + + module PathProblem { + import TransientFlow + } + + predicate isStored(Source lambda, Element store, string reason) { + TransientFlow::problem(lambda, store) and + ( + if store instanceof Function and not functionDefinedInTranslationUnit(store, lambda.getFile()) + then reason = "passed to a different translation unit" + else reason = "copied or moved" + ) + } +} + +/** + * An alterate module for detecting transient lambdas which uses the standard CodeQL dataflow + * library. + * + * Ideally, this module eventually replaces `TransientLambda`, however, current CodeQL support for + * flow of lambdas is unreliable and incomplete/inconsistent. This implementation does not detect + * all cases correctly, but it is a starting place to revisit at a later time. + * + * In the current dataflow library, there are many missing nodes and edges, making this currently + * difficult or impossible to implement correctly. + */ +module TransientLambdaDataFlow { + import semmle.code.cpp.dataflow.new.DataFlow as DataFlow + import DataFlow::DataFlow as NewDataFlow + + final class FinalLocatable = Locatable; + + /** + * Create a custom path problem, which inherently performs a graph search to find paths from start + * nodes (in this case, lambda expressions) to end nodes (in this case, copies and moves of + * lambdas). + */ + module TransientLambdaConfig implements NewDataFlow::StateConfigSig { + class FlowState = TranslationUnit; + + predicate isSource(NewDataFlow::Node n, TranslationUnit state) { + n.asExpr() instanceof Source and state = n.asExpr().getFile() + } + + predicate isSink(NewDataFlow::Node n) { + n.asOperand().getDef().getAst() instanceof VariableDeclarationEntry + or + exists(n.asVariable()) and not exists(n.asParameter()) + or + exists(NewExpr alloc | + alloc.getAllocatedType() instanceof Closure and + alloc.getInitializer() = n.asExpr() + ) + or + // Detect casting to std::function, which results in a copy of the lambda. + exists(Conversion conv | conv.getExpr() = n.asExpr()) + or + // Detect all function calls, in case the definition is in a different translation unit. + // We cannot detect this with stateful dataflow, for performance reasons. + exists(FunctionCall fc | fc.getAnArgument() = n.asExpr()) + } + + predicate isSink(NewDataFlow::Node n, TranslationUnit state) { + // Ideally, we would be able to check here for calls to functions defined outside of the + // translation unit, but in the current stateful dataflow library, this will result in a + // cartesian product of all nodes with all translation units. This limitation doesn't exist + // in the alternate `TransientLambda` module which uses `CustomPathStateProblem`. + // + // Since this predicate holds for none(), it may seem that we don't need to use stateful flow. + // However, stateful flow is still a good idea so that we can add isBarrier() to prevent flow + // out of the translation unit. That should be possible to do without introducing a + // cartesian product. + // + // To work around the cartesian product, this predicate holds for none() and `isSink(n)` + // should hold for all function calls. After flow has found lambda/function call pairs, we + // can filter out those pairs where the function is defined in a different translation unit. + // + // This isn't quite implemented yet. + none() + } + + predicate isBarrierOut(NewDataFlow::Node n, TranslationUnit state) { + // TODO: Implement a barrier to prevent flow out of the translation unit. + none() + } + + predicate isAdditionalFlowStep( + NewDataFlow::Node a, TranslationUnit s1, NewDataFlow::Node b, TranslationUnit s2 + ) { + // Add additional flow steps to handle: + // + // auto x = []() { ... }; + // + // Which isn't represented in the dataflow graph otherwise. + pragma[only_bind_out](s2) = s1 and + ( + pragma[only_bind_out](a.asExpr()) = + b.asOperand() + .getDef() + .getAst() + .(VariableDeclarationEntry) + .getVariable() + .getInitializer() + .getExpr() + or + a.asExpr().(Conversion).getExpr() = b.asExpr() + ) + } + } + + import NewDataFlow::GlobalWithState as TransientFlow + + module PathProblem { + import TransientFlow::PathGraph + } + + predicate isStored(Source lambda, Element store) { + exists(NewDataFlow::Node sink | + TransientFlow::flow(NewDataFlow::exprNode(lambda), sink) and + store = sink.asOperand().getDef().getAst() and + not exists(FunctionCall fc | + fc.getAnArgument() = sink.asExpr() and + exists(FunctionDeclarationEntry funcDef | + funcDef = fc.getTarget().getDefinition() and + funcDef.getFile() = lambda.getFile().(TranslationUnit).getATransitivelyIncludedFile() + ) + ) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/Locations.qll b/cpp/common/src/codingstandards/cpp/Locations.qll index 800f44d18a..8b49738326 100644 --- a/cpp/common/src/codingstandards/cpp/Locations.qll +++ b/cpp/common/src/codingstandards/cpp/Locations.qll @@ -28,3 +28,14 @@ bindingset[filepath, lineNumber] int getLastColumnNumber(string filepath, int lineNumber) { result = max(Location l | l.hasLocationInfo(filepath, _, _, lineNumber, _) | l.getEndColumn()) } + +bindingset[a, b] +predicate shareEnding(Locatable a, Locatable b) { + exists(Location la, Location lb | + la = a.getLocation() and + lb = b.getLocation() and + la.getFile() = lb.getFile() and + la.getEndLine() = lb.getEndLine() and + la.getEndColumn() = lb.getEndColumn() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/Scope.qll b/cpp/common/src/codingstandards/cpp/Scope.qll index 5438c17133..994564a827 100644 --- a/cpp/common/src/codingstandards/cpp/Scope.qll +++ b/cpp/common/src/codingstandards/cpp/Scope.qll @@ -307,6 +307,15 @@ predicate inSameTranslationUnitLate(File f1, File f2) { ) } +bindingset[f, tu] +pragma[inline_late] +predicate functionDefinedInTranslationUnit(Function f, TranslationUnit tu) { + exists(FunctionDeclarationEntry fde | fde = f.getDefinition() | + fde = f.getDefinition() and + fde.getFile() = tu.getATransitivelyIncludedFile() + ) +} + /** A file that is a C/C++ source file */ class SourceFile extends File { SourceFile() { diff --git a/cpp/common/src/codingstandards/cpp/alertreporting/CustomPathStateProblem.qll b/cpp/common/src/codingstandards/cpp/alertreporting/CustomPathStateProblem.qll new file mode 100644 index 0000000000..74d12f1aeb --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/alertreporting/CustomPathStateProblem.qll @@ -0,0 +1,199 @@ +/** + * A module for creating custom path problem results in CodeQL from a stateful graph search. + * + * This module will soon be moved to a shared library outside of this project. + */ + +import cpp +import codingstandards.cpp.graph.GraphPathStateSearch as Search + +/** + * To create a custom stateful path problem, simply define the `Node` you want to search (which + * must be `Locatable`) and the `State` class for your path search state. Then, implement the + * `edge` relation, and `start` and `end` predicates to indicate the types of things that should + * be considered problems when connected in the graph. + * + * Optionally, you can also implement the `edgeInfo` and `nodeLabel` predicates to provide + * additional information about the edges and nodes in the graph. + * + * Lastly, import `CustomPathStateProblem` to get the `problem` predicate, which holds for + * pairs of connected locations that will be traceable in the path problem results. + * + * See the `CallGraphPathStateProblemConfig` module for an example of how to use this module. + */ +signature module CustomPathStateProblemConfigSig { + /** + * A class that connects nodes in the graph to search locations. + * + * This class should be as small as possible, to avoid unnecessary search space. + */ + class Node extends Element; + + /** + * A class that represents the state of the path search. + * + * This is initialized in `start()` and checked in `end()`. It also may be forwarded and/or + * transformed in the `edge()` predicate. + */ + bindingset[this] + class State; + + /** + * The directional edges of the graph, from `a` to `b`, and how the state progresses from `s1` + * to `s2` at this edge. + * + * The design of this predicate will have a large impact on the performance of the search. + * However, the underlying search algorithm is efficient, so this should be fast in many cases + * even if this is a very large relation. + */ + bindingset[s1] + bindingset[s2] + predicate edge(Node a, State s1, Node b, State s2); + + /** + * Optional predicate to set additional information on the edges of the graph. + * + * By setting `key` to "provenance", the `val` string will be displayed in the path problem + * results, with one line per word in `val`. + */ + bindingset[a, b] + default predicate edgeInfo(Node a, Node b, string key, string val) { key = "" and val = "" } + + /** + * Optional predicate to set a label on the nodes of the graph. + * + * This does not appear to be used by vscode when displaying path problem results, but it is + * still part of the path problem API. + */ + bindingset[n] + default predicate nodeLabel(Node n, string value) { value = n.toString() } + + /** + * Where the graph search should start with a given initial state. + * + * If this node is connected to a node `x` that holds for `end(x)`, then `problem(n, x)` will hold + * and edges between them will be added to the path problem results. + */ + predicate start(Node n, State s); + + /** + * Where the graph search should end (an end node and an end state). + * + * If this node is connected to a node `x` that holds for `start(x)`, then `problem(x, n)` will hold + * and edges between them will be added to the path problem results. + */ + bindingset[s] + predicate end(Node n, State s); + + /** + * Whether the graph search should continue past the end nodes. + */ + default predicate searchPastEnd() { any() } +} + +/** + * A module for creating custom path problem results in CodeQL, using an efficient forward-reverse + * search pattern under the hood with state tracked along the edges. + * + * Implement `CustomPathStateProblemConfigSig` to define the nodes and edges of your graph, as well as + * start and end predicates to indicate the types of things that should be considered problems + * when connected in the graph. + * + * Then import this module, and select nodes for which `problem(a, b)` holds, and they will be + * traceable in the path problem results. + * + * Example usage: + * ```ql + * module MacroPathProblemConfig implements CustomPathProblemConfigSig { + * class Node extends Locatable { + * Node() { this instanceof Macro or this instanceof MacroInvocation } + * } + * + * class State = int; // Set a max search depth + * + * predicate start(Node n, State depth) { + * // Start at root macro invocations + * n instanceof MacroInvocation and not exists(n.(MacroInvocation).getParentInvocation()) and + * // Set the initial state to a depth of 0 + * depth = 0 + * } + * + * // Find calls to macros we don't like, at any depth + * predicate end(Node n, State depth) { n instanceof Macro and isBad(n) and depth = any() } + * + * predicate edge(Node a, State s1, Node b, State s2) { + * // Limit the search depth to 10 + * s1 < 10 and + * // Increment the state which represents the search depth + * s2 = s1 + 1 and + * ( + * // The root macro invocation is connected to its definition + * b = a.(MacroInvocation).getMacro() + * or + * exists(MacroInvocation inner, MacroInvocation next | + * // Connect inner macros to the macros that invoke them + * inner.getParentInvocation() = next() and + * a = inner.getMacro() and b = next.getMacro() + * ) + * ) + * } + * } + * + * // Import query predicates that make path-problem work correctly + * import CustomPathStateProblem + * + * from MacroInvocation start, Macro end + * where problem(start, end) // find macro invocations that are connected to bad macros + * select start, start, end, "Macro invocation eventually calls a macro we don't like: $@", end, end.getName() + * ``` + * + * There is also a predicate `problem(a, s1, b, s2)` for reporting problems with their stateful + * search results. + */ +module CustomPathStateProblem { + private module ForwardReverseConfig implements Search::GraphPathStateSearchSig { + predicate edge = Config::edge/4; + + predicate start = Config::start/2; + + predicate end = Config::end/2; + + class State = Config::State; + + predicate searchPastEnd = Config::searchPastEnd/0; + } + + private import Search::GraphPathStateSearch as SearchResults + + /** The magical `edges` query predicate that powers `@kind path-problem` along with `nodes`. */ + query predicate edges(Locatable a, Locatable b, string key, string val) { + SearchResults::pathEdge(a, b) and + Config::edgeInfo(a, b, key, val) + } + + /** The magical `nodes` query predicate that powers `@kind path-problem` along with `edges`. */ + query predicate nodes(Config::Node n, string key, string value) { + n instanceof SearchResults::ReverseNode and + // It seems like "semmle.label" is the only valid key. + key = "semmle.label" and + Config::nodeLabel(n, value) + } + + /** + * A predicate that holds for locations that are connected in the graph. + * + * These pairs should all be problems reported by the query, otherwise the search space is larger + * than necessary. + */ + predicate problem(Config::Node a, Config::Node b) { SearchResults::hasConnection(a, b) } + + /** + * A predicate that holds for locations that are connected in the graph. + * + * These pairs should all be problems reported by the query, otherwise the search space is larger + * than necessary. + */ + predicate problem(Config::Node a, Config::State s1, Config::Node b, Config::State s2) { + SearchResults::hasConnection(a, s1, b, s2) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Expressions2.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Expressions2.qll new file mode 100644 index 0000000000..c4e5e3922a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Expressions2.qll @@ -0,0 +1,95 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Expressions2Query = + TLongLongLiteralWithSingleLSuffixQuery() or + TMissingPrecedenceClarifyingParenthesisQuery() or + TMissingSizeofOperatorParenthesisQuery() or + TNonTransientLambdaImplicitlyCapturesThisQuery() or + TImplicitCapturesDisallowedInNonTransientLambdaQuery() + +predicate isExpressions2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `longLongLiteralWithSingleLSuffix` query + Expressions2Package::longLongLiteralWithSingleLSuffixQuery() and + queryId = + // `@id` for the `longLongLiteralWithSingleLSuffix` query + "cpp/misra/long-long-literal-with-single-l-suffix" and + ruleId = "RULE-5-13-6" and + category = "required" + or + query = + // `Query` instance for the `missingPrecedenceClarifyingParenthesis` query + Expressions2Package::missingPrecedenceClarifyingParenthesisQuery() and + queryId = + // `@id` for the `missingPrecedenceClarifyingParenthesis` query + "cpp/misra/missing-precedence-clarifying-parenthesis" and + ruleId = "RULE-8-0-1" and + category = "advisory" + or + query = + // `Query` instance for the `missingSizeofOperatorParenthesis` query + Expressions2Package::missingSizeofOperatorParenthesisQuery() and + queryId = + // `@id` for the `missingSizeofOperatorParenthesis` query + "cpp/misra/missing-sizeof-operator-parenthesis" and + ruleId = "RULE-8-0-1" and + category = "advisory" + or + query = + // `Query` instance for the `nonTransientLambdaImplicitlyCapturesThis` query + Expressions2Package::nonTransientLambdaImplicitlyCapturesThisQuery() and + queryId = + // `@id` for the `nonTransientLambdaImplicitlyCapturesThis` query + "cpp/misra/non-transient-lambda-implicitly-captures-this" and + ruleId = "RULE-8-1-1" and + category = "required" + or + query = + // `Query` instance for the `implicitCapturesDisallowedInNonTransientLambda` query + Expressions2Package::implicitCapturesDisallowedInNonTransientLambdaQuery() and + queryId = + // `@id` for the `implicitCapturesDisallowedInNonTransientLambda` query + "cpp/misra/implicit-captures-disallowed-in-non-transient-lambda" and + ruleId = "RULE-8-1-2" and + category = "advisory" +} + +module Expressions2Package { + Query longLongLiteralWithSingleLSuffixQuery() { + //autogenerate `Query` type + result = + // `Query` type for `longLongLiteralWithSingleLSuffix` query + TQueryCPP(TExpressions2PackageQuery(TLongLongLiteralWithSingleLSuffixQuery())) + } + + Query missingPrecedenceClarifyingParenthesisQuery() { + //autogenerate `Query` type + result = + // `Query` type for `missingPrecedenceClarifyingParenthesis` query + TQueryCPP(TExpressions2PackageQuery(TMissingPrecedenceClarifyingParenthesisQuery())) + } + + Query missingSizeofOperatorParenthesisQuery() { + //autogenerate `Query` type + result = + // `Query` type for `missingSizeofOperatorParenthesis` query + TQueryCPP(TExpressions2PackageQuery(TMissingSizeofOperatorParenthesisQuery())) + } + + Query nonTransientLambdaImplicitlyCapturesThisQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonTransientLambdaImplicitlyCapturesThis` query + TQueryCPP(TExpressions2PackageQuery(TNonTransientLambdaImplicitlyCapturesThisQuery())) + } + + Query implicitCapturesDisallowedInNonTransientLambdaQuery() { + //autogenerate `Query` type + result = + // `Query` type for `implicitCapturesDisallowedInNonTransientLambda` query + TQueryCPP(TExpressions2PackageQuery(TImplicitCapturesDisallowedInNonTransientLambdaQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index abd6aeff96..2dfc73521e 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -18,6 +18,7 @@ import ExceptionSafety import Exceptions1 import Exceptions2 import Expressions +import Expressions2 import FloatingPoint import Freed import Functions @@ -73,6 +74,7 @@ newtype TCPPQuery = TExceptions1PackageQuery(Exceptions1Query q) or TExceptions2PackageQuery(Exceptions2Query q) or TExpressionsPackageQuery(ExpressionsQuery q) or + TExpressions2PackageQuery(Expressions2Query q) or TFloatingPointPackageQuery(FloatingPointQuery q) or TFreedPackageQuery(FreedQuery q) or TFunctionsPackageQuery(FunctionsQuery q) or @@ -128,6 +130,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isExceptions1QueryMetadata(query, queryId, ruleId, category) or isExceptions2QueryMetadata(query, queryId, ruleId, category) or isExpressionsQueryMetadata(query, queryId, ruleId, category) or + isExpressions2QueryMetadata(query, queryId, ruleId, category) or isFloatingPointQueryMetadata(query, queryId, ruleId, category) or isFreedQueryMetadata(query, queryId, ruleId, category) or isFunctionsQueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/common/src/codingstandards/cpp/graph/GraphPathStateSearch.qll b/cpp/common/src/codingstandards/cpp/graph/GraphPathStateSearch.qll new file mode 100644 index 0000000000..02436571c2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/graph/GraphPathStateSearch.qll @@ -0,0 +1,292 @@ +/** + * This module will soon be moved to a shared library outside of this project. + * + * Like `GraphPathSearch`, this file defines a module for efficiently finding paths in a directional + * graph using a performant pattern called forward-reverse pruning. + * + * Additionally, this module is designed to track state through the paths it is looking for. For + * instance, we could use this graph to find recursive functions, which requires knowing how an end + * node was reached from a start node (the state). + * + * Like `GraphPathSearch`, this module uses forward-reverse pruning, wihch is a pattern that is + * useful for efficiently finding connections between nodes in a directional graph. In a first pass, + * it finds nodes reachable from the starting point. In the second pass, it finds the subset of + * those nodes that can be reached from the end point. Together, these create a path from start + * points to end points. + * + * As with the other performance patterns in qtil, this module may be useful as is, or it may not + * fit your needs exactly. CodeQL evaluation and performance is very complex. In that case, consider + * this pattern as an example to create your own solution that fits your needs. + */ +signature class FiniteType; + +/** + * Implement this signature to define a graph, and a search for paths within that graph tracking + * some state, using the `GraphPathStateSearch` module. + * + * ```ql + * module MyConfig implements GraphPathStateSearchSig { + * class State extends ... { ... }; + * predicate start(Node n1) { ... } + * predicate edge(Node n1, Node n2) { ... } + * predicate end(Node n1) { ... } + * } + * ``` + * + * To flow without state, use `GraphPathSearchSig` instead. + */ +signature module GraphPathStateSearchSig { + /** + * The state to be tracked through the paths found by this module. + * + * For example, if searching for recursive functions, this class might be defined as: + * + * ```ql + * class State = Function; + * ``` + * + * The `edges` predicate defined in this signature module decides how to forward this state, so + * the state may change as the path is traversed. + */ + bindingset[this] + class State; + + /** + * The nodes that begin the search of the graph, and the starting state for those nodes. + * + * For instance, if searching for recursive functions, this predicate might hold for a Function + * and its state may be the Function itself. + * + * Ultimately, only paths from a start node to an end node will be found by this module. + * + * In most cases, this will ideally be a smaller set of nodes than the end nodes. However, if the + * graph branches in one direction more than the other, a larger set which branches less may be + * preferable. + * + * The design of this predicate has a great effect in how well this performance pattern will + * ultimately perform. + */ + predicate start(Node n1, State s1); + + /** + * A directional edge from `n1` to `n2`, and the state that is forwarded from `n1` to `n2`. + * + * This module will search for paths from `start` to `end` by looking following the direction of + * these edges. + * + * As an example state transformation, a maximum search depth could be tracked at each edge and + * the new state would be the old state with the depth incremented by one. Alternatively, if + * searching for recursive functions, the state could be the starting function, and this edge + * relation would forward that function unchanged. + * + * The design of this predicate has a great effect in how well this performance pattern will + * ultimately perform. + */ + bindingset[s1] + bindingset[s2] + predicate edge(Node n1, State s1, Node n2, State s2); + + /** + * The end nodes of the search, if reached with the given state. + * + * For instance, if searching for recursive functions, this predicate would likely hold when a + * function node is reached with the state being same function declaration (indicating flow from + * the start function to itself). + * + * Ultimately, only paths from a start node to an end node will be found by this module. + * + * The design of this predicate has a great effect in how well this performance pattern will + * ultimately perform. + */ + bindingset[s1] + predicate end(Node n1, State s1); + + /** + * Whether the search should continue past the end nodes. + */ + default predicate searchPastEnd() { any() } +} + +/** + * A module that implements an efficient search for a path that satisfies specified stateful + * constraints within a custom directional graph from a set of start nodes to a set of end nodes. + * + * For example, this module can be used to detect loops in the graph (perhaps to find recursive + * functions) by setting the "state" to be the start node, forwarding that state unchanged on each + * edge, and considering a node to be an end node if it is reached with itself as the state. + * Alternatively, the state could be used to track a maximum search depth, with a start state of + * zero that is incremented at each edge, and where the edge relation does not hold beyond a certain + * depth. + * + * To show discovered paths to users, see the module `CustomPathStateProblem` which uses this module + * as * its underlying search implementation. + * + * This module uses a pattern called "forward reverse pruning" for efficiency. This pattern is + * useful for reducing the search space when looking for paths in a directional graph. In a first + * pass, it finds nodes reachable from the starting point. In the second pass, it finds the subset + * of those nodes that can be reached from the end point. Together, these create a path from start + * points to end points. + * + * To use this module, provide an implementation of the `GraphPathSearchSig` signature as follows: + * + * ```ql + * module Config implements GraphPathSearchSig { + * class State extends Something { ... }; + * predicate start(Person p, State s) { p.checkSomething() and s = p.getSomeStartValue() } + * predicate edge(Person p1, State s1, Person p2, State s2) { p2 = p1.getAParent() and s2 = s1.next() } + * predicate end(Person p, State s) { p.checkSomethingElse() and s.isValidEndState() } + * } + * ``` + * + * The design of these predicate has a great effect in how well this performance pattern will + * ultimately perform. + * + * The resulting predicate `hasPath` should be a much more efficient search of connected start nodes + * to end nodes than a naive search (which in CodeQL could easily be evaluated as either a full + * graph search, or a search over the cross product of all nodes). + * + * ```ql + * from Person p1, State s1, Person p2, State s2 + * // Fast graph path detection thanks to forward-reverse pruning. + * where GraphPathStateSearch::hasPath(p1, s1, p2, p2) + * select p1, s1, p2, p2 + * ``` + * + * The resulting module also exposes two predicates: + * - `ForwardNode`: All nodes reachable from the start nodes, with member predicate `getState()`. + * - `ReverseNode`: All forward nodes that reach end nodes, with member predicate `getState()`. + * + * These classes may be useful in addition to the `hasPath` predicate. + * + * To track state as well as flow, use `GraphPathStateSearch` instead. + */ +module GraphPathStateSearch Config> { + final private class FinalNode = Node; + + /** + * The set of all nodes reachable from the start nodes (inclusive). + * + * Includes the member predicate `getState()` which returns the state associated with this node at + * this point in the search. + */ + class ForwardNode extends FinalNode { + Config::State state; + + ForwardNode() { forwardNode(this, state) } + + /** + * Get the state associated with this forward node at this point in the search. + */ + Config::State getState() { result = state } + + string toString() { result = "ForwardNode" } + } + + /** + * The performant predicate for looking forward one step at a time in the graph. + * + * In `GraphPathSearch`, this is fast because it is essentially a unary predicate. The same is + * true here when the correct joins occur, such that (n, s) effectively act as a single value. + * + * For this reason, we use `pragma[only_bind_into]` to ensure the correct join order. + */ + private predicate forwardNode(Node n, Config::State s) { + Config::start(pragma[only_bind_into](n), pragma[only_bind_into](s)) + or + exists(Node n0, Config::State s0 | + forwardNode(pragma[only_bind_into](n0), pragma[only_bind_into](s0)) and + Config::edge(n0, s0, pragma[only_bind_into](n), pragma[only_bind_into](s)) and + (Config::end(n0, s0) implies Config::searchPastEnd()) + ) + } + + /** + * The set of all forward nodes that reach end nodes (inclusive). + * + * Includes the member predicate `getState()` which returns the state associated with this node at + * this point in the search. + * + * These nodes are the nodes that exist along the path from start nodes to end nodes. + * + * Note: this is fast to compute because it is essentially a unary predicate. + */ + class ReverseNode extends ForwardNode { + ReverseNode() { + // 'state' field and getState() predicate are inherited from ForwardNode + reverseNode(this, state) + } + + override string toString() { result = "ReverseNode" } + } + + private predicate reverseNode(Node n, Config::State s) { + forwardNode(pragma[only_bind_into](n), pragma[only_bind_into](s)) and + Config::end(n, s) + or + exists(Node n0, Config::State s0 | + reverseNode(pragma[only_bind_into](n0), pragma[only_bind_into](s0)) and + Config::edge(n, s, n0, s0) + ) + } + + /** + * A start node, end node pair that are connected in the graph. + */ + predicate hasConnection(ReverseNode n1, ReverseNode n2) { hasConnection(n1, _, n2, _) } + + /** + * A start node, end node pair that are connected in the graph, and the states associated with + * those nodes. + */ + predicate hasConnection(ReverseNode n1, Config::State s1, ReverseNode n2, Config::State s2) { + Config::start(n1, s1) and + Config::end(n2, s2) and + ( + hasPath(n1, s1, n2, s2) + or + n1 = n2 and s1 = s2 + ) + } + + /** + * All relevant edges in the graph which participate in a connection from a start to an end node. + */ + predicate pathEdge(ReverseNode n1, ReverseNode n2) { pathEdge(n1, _, n2, _) } + + /** + * All relevant edges in the graph, plus state, which participate in a connection from a start to + * an end node. + */ + predicate pathEdge(ReverseNode n1, Config::State s1, ReverseNode n2, Config::State s2) { + Config::edge(n1, s1, n2, s2) and + reverseNode(pragma[only_bind_into](n2), pragma[only_bind_into](s2)) + } + + /** + * A performant path search within a custom directed graph from a set of start nodes to a set of + * end nodes. + * + * This predicate is the main entry point for the forward-reverse pruning pattern. The design of + * the config predicates has a great effect in how well this performance pattern will ultimately + * perform. + * + * Example: + * ```ql + * from Person p1, Person p2 + * where GraphPathSearch::hasPath(p1, p2) + * select p1, p2 + * ``` + * + * Note: this is fast to compute because limits the search space to nodes found by the fast unary + * searches done to find `ForwardNode` and `ReverseNode`. + */ + predicate hasPath(ReverseNode n1, Config::State s1, ReverseNode n2, Config::State s2) { + Config::start(n1, s1) and + Config::edge(n1, s1, n2, s2) + or + exists(ReverseNode nMid, Config::State sMid | + hasPath(n1, s1, nMid, sMid) and + Config::edge(pragma[only_bind_out](nMid), pragma[only_bind_out](sMid), n2, s2) + ) + } +} diff --git a/cpp/common/test/includes/standard-library/functional.h b/cpp/common/test/includes/standard-library/functional.h index 78d9c47745..0226366626 100644 --- a/cpp/common/test/includes/standard-library/functional.h +++ b/cpp/common/test/includes/standard-library/functional.h @@ -1,6 +1,8 @@ #ifndef _GHLIBCPP_FUNCTIONAL #define _GHLIBCPP_FUNCTIONAL +#include + namespace std { typedef unsigned long size_t; @@ -87,7 +89,9 @@ template class function; template class function { public: function(); - template function(F &&); + template function(F&& f) { + auto fptr = new F(std::forward(f)); + } template function &operator=(F &&); }; } // namespace std diff --git a/cpp/common/test/includes/standard-library/utility.h b/cpp/common/test/includes/standard-library/utility.h index b9bf47b8ce..13ae9effb3 100644 --- a/cpp/common/test/includes/standard-library/utility.h +++ b/cpp/common/test/includes/standard-library/utility.h @@ -1,8 +1,13 @@ #include "type_traits.h" namespace std { -template constexpr T &&forward(remove_reference_t &t) noexcept; -template constexpr T &&forward(remove_reference_t &&t) noexcept; +template constexpr T &&forward(remove_reference_t &t) noexcept { + return static_cast(t); +} +template constexpr T &&forward(remove_reference_t &&t) noexcept { + return static_cast(t); +} + template constexpr remove_reference_t &&move(T &&t) noexcept; template typename add_rvalue_reference::type declval() noexcept; diff --git a/cpp/misra/src/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.ql b/cpp/misra/src/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.ql new file mode 100644 index 0000000000..223dd887a0 --- /dev/null +++ b/cpp/misra/src/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.ql @@ -0,0 +1,28 @@ +/** + * @id cpp/misra/long-long-literal-with-single-l-suffix + * @name RULE-5-13-6: An integer-literal of type long long shall not use a single L or l in any suffix + * @description Declaring a long long integer literal with a single L or l in the suffix is + * misleading and can cause confusion. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-13-6 + * scope/single-translation-unit + * readability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.Cpp14Literal + +from Cpp14Literal::IntegerLiteral literal, Cpp14Literal::IntegerLiteralSuffix suffix +where + not isExcluded(literal, Expressions2Package::longLongLiteralWithSingleLSuffixQuery()) and + suffix = literal.getSuffix() and + suffix.getLCount() = 1 and + literal.getType() instanceof LongLongType +select literal, + "Integer literal declared with suffix '" + suffix + + "' indicative of a long type, but has actual type '" + literal.getType().toString() + "'." diff --git a/cpp/misra/src/rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.ql b/cpp/misra/src/rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.ql new file mode 100644 index 0000000000..303329eb72 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.ql @@ -0,0 +1,87 @@ +/** + * @id cpp/misra/missing-precedence-clarifying-parenthesis + * @name RULE-8-0-1: Parentheses should be used to make the meaning of an expression appropriately explicit + * @description Usage of parentheses improve program clarity when using multiple operators of + * different precedences. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-0-1 + * scope/single-translation-unit + * readability + * correctness + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.AlertReporting + +class MisraPrecedenceExpr instanceof Expr { + int precedence; + string operator; + + MisraPrecedenceExpr() { + not super.isCompilerGenerated() and + ( + operator = [this.(BinaryOperation).getOperator(), this.(Assignment).getOperator()] + or + this instanceof ConditionalExpr and operator = "?" + or + this instanceof ThrowExpr and operator = "throw" + ) and + ( + operator = ["*", "/", "%"] and precedence = 13 + or + operator = ["+", "-"] and precedence = 12 + or + operator = ["<<", ">>"] and precedence = 11 + or + operator = ["<", "<=", ">", ">="] and precedence = 10 + or + operator = ["==", "!="] and precedence = 9 + or + operator = "&" and precedence = 8 + or + operator = "^" and precedence = 7 + or + operator = "|" and precedence = 6 + or + operator = "&&" and precedence = 5 + or + operator = "||" and precedence = 4 + or + operator = ["=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>="] and + precedence = 2 + or + operator = "?" and precedence = 3 + or + operator = "throw" and precedence = 1 + ) + } + + int getMisraPrecedence() { result = precedence } + + string getOperator() { result = operator } + + MisraPrecedenceExpr getAnExplicitlyConvertedChild() { + result = super.getAChild().getExplicitlyConverted() + } + + Element getElement() { result = MacroUnwrapper::unwrapElement(this) } + + string toString() { result = super.toString() } +} + +from MisraPrecedenceExpr parent, MisraPrecedenceExpr child +where + not isExcluded(child, Expressions2Package::missingPrecedenceClarifyingParenthesisQuery()) and + not parent.(Expr).isFromTemplateInstantiation(_) and + parent.getAnExplicitlyConvertedChild() = child and + parent.getMisraPrecedence() > 2 and + parent.getMisraPrecedence() < child.getMisraPrecedence() +select child.getElement(), + "Expression with operator '" + parent.getOperator() + + "' contains an unparenthesized child expression with higher precedence operator '" + + child.getOperator() + "'." diff --git a/cpp/misra/src/rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.ql b/cpp/misra/src/rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.ql new file mode 100644 index 0000000000..e412c91095 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.ql @@ -0,0 +1,27 @@ +/** + * @id cpp/misra/missing-sizeof-operator-parenthesis + * @name RULE-8-0-1: Parentheses should be used to make the meaning of an expression appropriately explicit + * @description Usage of parentheses improve program clarity when using the sizeof() operator. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-0-1 + * scope/single-translation-unit + * readability + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.Locations as Locations + +from SizeofExprOperator sizeof +where + not isExcluded(sizeof, Expressions2Package::missingSizeofOperatorParenthesisQuery()) and + // Cannot use offset magic in macro expansions. + not sizeof.isInMacroExpansion() and + // In `sizeof(x)`, `x` ends before the `sizeof` expr, and in `sizeof x`, `x` and the `sizeof` expr + // end at the same line & column. + Locations::shareEnding(sizeof, sizeof.getExprOperand()) +select sizeof, "Sizeof operator used without parenthesis." diff --git a/cpp/misra/src/rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.ql b/cpp/misra/src/rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.ql new file mode 100644 index 0000000000..504c93f546 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.ql @@ -0,0 +1,32 @@ +/** + * @id cpp/misra/non-transient-lambda-implicitly-captures-this + * @name RULE-8-1-1: A non-transient lambda shall not implicitly capture this + * @description Using implicit capture for a lambda declared in a class will capture the `this` + * pointer and not the instance members, which can be suprising and result in undefined + * behavior past the object's lifetime. + * @kind path-problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-1-1 + * scope/single-translation-unit + * correctness + * readability + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.Lambda +import TransientLambda +import PathProblem + +from ImplicitThisCapturingLambdaExpr lambda, Element store, Class containingClass, string reason +where + not isExcluded(lambda, Expressions2Package::nonTransientLambdaImplicitlyCapturesThisQuery()) and + isStored(lambda, store, reason) and + containingClass = lambda.getEnclosingDeclaration().getEnclosingElement*() +select lambda, lambda, store, + "Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is " + + reason + ".", containingClass, containingClass.getName() diff --git a/cpp/misra/src/rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.ql b/cpp/misra/src/rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.ql new file mode 100644 index 0000000000..da98dd0897 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.ql @@ -0,0 +1,30 @@ +/** + * @id cpp/misra/implicit-captures-disallowed-in-non-transient-lambda + * @name RULE-8-1-2: Variables should be captured explicitly in a non-transient lambda + * @description Explicit captures in lambdas which are "stored" clarifies dependencies that must be + * managed for memory management and safety. + * @kind path-problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-1-2 + * scope/single-translation-unit + * readability + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.Lambda +import TransientLambda +import PathProblem + +from ImplicitCaptureLambdaExpr lambda, Element store, LambdaCapture capture, string reason +where + not isExcluded(lambda, Expressions2Package::implicitCapturesDisallowedInNonTransientLambdaQuery()) and + isStored(lambda, store, reason) and + capture = lambda.getImplicitCapture() +select lambda, lambda, store, + "Lambda implicitly captures $@ but is not considered a transient lambda because it is " + reason + + ", resulting in obfuscated lifetimes.", capture, capture.getField().getName() diff --git a/cpp/misra/test/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.expected b/cpp/misra/test/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.expected new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/misra/test/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.expected.clang b/cpp/misra/test/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.expected.clang new file mode 100644 index 0000000000..ee6c1b8c16 --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.expected.clang @@ -0,0 +1,33 @@ +| test.cpp:16:3:16:13 | 2147483648 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:17:3:17:13 | 2147483648 | Integer literal declared with suffix 'l' indicative of a long type, but has actual type 'long long'. | +| test.cpp:26:3:26:14 | 4294967296 | Integer literal declared with suffix 'UL' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:28:3:28:14 | 4294967296 | Integer literal declared with suffix 'Ul' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:30:3:30:14 | 4294967296 | Integer literal declared with suffix 'uL' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:32:3:32:14 | 4294967296 | Integer literal declared with suffix 'ul' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:36:3:36:22 | 9223372036854775807 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:37:3:37:22 | 9223372036854775807 | Integer literal declared with suffix 'l' indicative of a long type, but has actual type 'long long'. | +| test.cpp:40:3:40:24 | 18446744073709551615 | Integer literal declared with suffix 'UL' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:41:3:41:24 | 18446744073709551615 | Integer literal declared with suffix 'Ul' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:93:3:93:14 | 4294967296 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:95:3:95:14 | 4294967296 | Integer literal declared with suffix 'l' indicative of a long type, but has actual type 'long long'. | +| test.cpp:99:3:99:21 | 9223372036854775807 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:100:3:100:21 | 9223372036854775808 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:101:3:101:21 | 18446744073709551615 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:131:3:131:15 | 4294967296 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:133:3:133:15 | 4294967296 | Integer literal declared with suffix 'l' indicative of a long type, but has actual type 'long long'. | +| test.cpp:137:3:137:25 | 9223372036854775807 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:138:3:138:26 | 9223372036854775808 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:170:3:170:38 | 4294967296 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:172:3:172:38 | 4294967296 | Integer literal declared with suffix 'l' indicative of a long type, but has actual type 'long long'. | +| test.cpp:176:3:176:69 | 9223372036854775807 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:179:3:179:69 | 9223372036854775808 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:221:18:221:29 | 2147483648 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:222:27:222:39 | 4294967296 | Integer literal declared with suffix 'UL' indicative of a long type, but has actual type 'unsigned long long'. | +| test.cpp:223:18:223:30 | 4294967296 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:224:18:224:31 | 4294967296 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:225:18:225:38 | 9223372036854775807 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:231:17:231:31 | 2147483648 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:233:17:234:32 | 9223372036854775807 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:240:12:240:23 | 2147483648 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:243:12:243:24 | 4294967296 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | +| test.cpp:244:12:244:32 | 9223372036854775807 | Integer literal declared with suffix 'L' indicative of a long type, but has actual type 'long long'. | diff --git a/cpp/misra/test/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.qlref b/cpp/misra/test/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.qlref new file mode 100644 index 0000000000..4ae212415d --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.qlref @@ -0,0 +1 @@ +rules/RULE-5-13-6/LongLongLiteralWithSingleLSuffix.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-5-13-6/options.clang b/cpp/misra/test/rules/RULE-5-13-6/options.clang new file mode 100644 index 0000000000..6ea0848132 --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-13-6/options.clang @@ -0,0 +1 @@ +-m32 \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-5-13-6/test.cpp b/cpp/misra/test/rules/RULE-5-13-6/test.cpp new file mode 100644 index 0000000000..c0bc84770e --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-13-6/test.cpp @@ -0,0 +1,246 @@ +// This test does nothing useful on a standard LP64 system, because 'long' and +// 'long long' are the same size. Unfortunately, adding '-m32' to the +// '--semmle-extractor-options' does nothing as it gets overridden by the +// 'codeql test' command. +// +// However, we can use our compatibility testing framework to compile this with +// '-m32' and test the case where 'long' is 32 bits and 'long long' is 64 bits. +// The important expectations are defined there. + +void f1() { + // Boundary conditions for signed long (32-bit) + 2147483647L; // COMPLIANT - max value for 32-bit long + 2147483647l; // COMPLIANT - max value for 32-bit long + + // Long long literals with single l or L suffix (NON_COMPLIANT) + 2147483648L; // NON_COMPLIANT - single L suffix, too large for 32-bit long + 2147483648l; // NON_COMPLIANT - single l suffix, too large for 32-bit long + + // Boundary conditions for unsigned long (32-bit) + 4294967295UL; // COMPLIANT - max value for 32-bit unsigned long + 4294967295Ul; // COMPLIANT - max value for 32-bit unsigned long + 4294967295uL; // COMPLIANT - max value for 32-bit unsigned long + 4294967295ul; // COMPLIANT - max value for 32-bit unsigned long + + // Unsigned long long literals with single l or L suffix + 4294967296UL; // NON_COMPLIANT - single L suffix, too large for 32-bit + // unsigned long + 4294967296Ul; // NON_COMPLIANT - single l suffix, too large for 32-bit + // unsigned long + 4294967296uL; // NON_COMPLIANT - single L suffix, too large for 32-bit + // unsigned long + 4294967296ul; // NON_COMPLIANT - single l suffix, too large for 32-bit + // unsigned long + + // High-range long long literals with single L suffix + 9223372036854775807L; // NON_COMPLIANT - near LONG_LONG_MAX, single L + 9223372036854775807l; // NON_COMPLIANT - near LONG_LONG_MAX, single l + + // High-range unsigned long long literals with single L suffix + 18446744073709551615UL; // NON_COMPLIANT - ULONG_LONG_MAX, single L + 18446744073709551615Ul; // NON_COMPLIANT - ULONG_LONG_MAX, single l + + // Long long literals with double l or L suffix (COMPLIANT) + 2147483648LL; // COMPLIANT + 2147483648ll; // COMPLIANT + 4294967296ULL; // COMPLIANT + 4294967296Ull; // COMPLIANT + 4294967296uLL; // COMPLIANT + 4294967296ull; // COMPLIANT + + // High-range compliant literals + 9223372036854775807LL; // COMPLIANT - LONG_LONG_MAX + 9223372036854775808ULL; // COMPLIANT - beyond LONG_LONG_MAX + 18446744073709551615ULL; // COMPLIANT - ULONG_LONG_MAX + + // Long literals with double L (COMPLIANT - overkill but allowed) + 123LL; // COMPLIANT - overkill but allowed + 123ll; // COMPLIANT - overkill but allowed + 123ULL; // COMPLIANT - overkill but allowed + 2147483647LL; // COMPLIANT - overkill for long value + 4294967295ULL; // COMPLIANT - overkill for unsigned long value + + // Small literals with single L (COMPLIANT - these are actually of type long) + 123L; // COMPLIANT - type is long, not long long + 123l; // COMPLIANT - type is long, not long long + 123UL; // COMPLIANT - type is unsigned long, not unsigned long long + 123Ul; // COMPLIANT - type is unsigned long, not unsigned long long + + // Unsuffixed literals of all sizes (COMPLIANT) + 123; // COMPLIANT - unsuffixed + 2147483647; // COMPLIANT - unsuffixed (max 32-bit signed) + 2147483648; // COMPLIANT - unsuffixed + + // Literals suffixed with 'u' only (COMPLIANT) + 123u; // COMPLIANT - 'u' suffix only + 123U; // COMPLIANT - 'U' suffix only + 4294967295U; // COMPLIANT - 'U' suffix only (max 32-bit unsigned) + 4294967296U; // COMPLIANT - 'U' suffix only +} + +void f2() { + // Hex literals - boundary conditions + 0x7FFFFFFFL; // COMPLIANT - max value for 32-bit signed long + 0x7FFFFFFFl; // COMPLIANT - max value for 32-bit signed long + + // Hex literals - boundary for unsigned long + 0x80000000L; // COMPLIANT - fits in 32-bit unsigned long + 0x80000000l; // COMPLIANT - fits in 32-bit unsigned long + 0xFFFFFFFFL; // COMPLIANT - max value for 32-bit unsigned long + 0xFFFFFFFFl; // COMPLIANT - max value for 32-bit unsigned long + + // Hex literals - long long with single L (NON_COMPLIANT) + 0x100000000L; // NON_COMPLIANT - single L suffix, too large for 32-bit + // unsigned long + 0x100000000l; // NON_COMPLIANT - single l suffix, too large for 32-bit + // unsigned long + + // High-range hex literals + 0x7FFFFFFFFFFFFFFFL; // NON_COMPLIANT - LONG_LONG_MAX with single L + 0x8000000000000000L; // NON_COMPLIANT - beyond LONG_LONG_MAX with single L + 0xFFFFFFFFFFFFFFFFL; // NON_COMPLIANT - beyond LONG_LONG_MAX with single L + // (unsigned) + + // Hex literals - with double L (COMPLIANT) + 0x80000000LL; // COMPLIANT + 0x80000000ll; // COMPLIANT + 0x100000000LL; // COMPLIANT + 0x100000000ll; // COMPLIANT + 0xFFFFFFFFLL; // COMPLIANT - overkill for 32-bit unsigned long + 0xFFFFFFFFll; // COMPLIANT - overkill for 32-bit unsigned long + 0x7FFFFFFFFFFFFFFFLL; // COMPLIANT - LONG_LONG_MAX + 0xFFFFFFFFFFFFFFFFULL; // COMPLIANT - ULONG_LONG_MAX + + // Small hex literals with single L (COMPLIANT - these are actually long) + 0x123L; // COMPLIANT - type is long + 0xABCUL; // COMPLIANT - type is unsigned long +} + +void f3() { + // Octal literals - boundary conditions + 017777777777L; // COMPLIANT - max value for 32-bit long + 017777777777l; // COMPLIANT - max value for 32-bit long + + // Octal literals - boundary for unsigned long + 020000000000L; // COMPLIANT - fits in 32-bit unsigned long + 020000000000l; // COMPLIANT - fits in 32-bit unsigned long + 037777777777L; // COMPLIANT - max value for 32-bit unsigned long + 037777777777l; // COMPLIANT - max value for 32-bit unsigned long + + // Octal literals - long long with single L (NON_COMPLIANT) + 040000000000L; // NON_COMPLIANT - single L suffix, too large for 32-bit + // unsigned long + 040000000000l; // NON_COMPLIANT - single l suffix, too large for 32-bit + // unsigned long + + // High-range octal literals + 0777777777777777777777L; // NON_COMPLIANT - LONG_LONG_MAX with single L + 01000000000000000000000L; // NON_COMPLIANT - beyond LONG_LONG_MAX with single + // L + + // Octal literals - with double L (COMPLIANT) + 020000000000LL; // COMPLIANT + 020000000000ll; // COMPLIANT + 040000000000LL; // COMPLIANT + 040000000000ll; // COMPLIANT + 0777777777777777777777LL; // COMPLIANT - LONG_LONG_MAX + 01777777777777777777777ULL; // COMPLIANT - ULONG_LONG_MAX + + // Small octal literals with single L (COMPLIANT - these are actually long) + 0123L; // COMPLIANT - type is long + 0123UL; // COMPLIANT - type is unsigned long +} + +void f4() { + // Binary literals - boundary conditions + 0b01111111111111111111111111111111L; // COMPLIANT - max value for 32-bit long + 0b01111111111111111111111111111111l; // COMPLIANT - max value for 32-bit long + + // Binary literals - boundary for unsigned long + 0b10000000000000000000000000000000L; // COMPLIANT - fits in 32-bit unsigned + // long + 0b10000000000000000000000000000000l; // COMPLIANT - fits in 32-bit unsigned + // long + 0b11111111111111111111111111111111L; // COMPLIANT - max value for 32-bit + // unsigned long + 0b11111111111111111111111111111111l; // COMPLIANT - max value for 32-bit + // unsigned long + + // Binary literals - long long with single L (NON_COMPLIANT) + 0b100000000000000000000000000000000L; // NON_COMPLIANT - single L suffix, + // beyond unsigned long + 0b100000000000000000000000000000000l; // NON_COMPLIANT - single l suffix, + // beyond unsigned long + + // High-range binary literals + 0b0111111111111111111111111111111111111111111111111111111111111111L; // NON_COMPLIANT + // - + // LONG_LONG_MAX + 0b1000000000000000000000000000000000000000000000000000000000000000L; // NON_COMPLIANT + + // Binary literals - with double L (COMPLIANT) + 0b10000000000000000000000000000000LL; // COMPLIANT + 0b10000000000000000000000000000000ll; // COMPLIANT + 0b100000000000000000000000000000000LL; // COMPLIANT + 0b100000000000000000000000000000000ll; // COMPLIANT + 0b0111111111111111111111111111111111111111111111111111111111111111LL; // COMPLIANT + // - + // LONG_LONG_MAX + 0b1111111111111111111111111111111111111111111111111111111111111111ULL; // COMPLIANT + // - + // ULONG_LONG_MAX + + // Small binary literals with single L (COMPLIANT - these are actually long) + 0b1010L; // COMPLIANT - type is long + 0b1010UL; // COMPLIANT - type is unsigned long +} + +void f5() { + // Testing variable declarations + // These are COMPLIANT because the type of the literal is not long long + long long x1 = 123L; // COMPLIANT - 123L is of type long + long long x2 = 0xABCL; // COMPLIANT - 0xABCL is of type long + long long x3 = 0123L; // COMPLIANT - 0123L is of type long + long long x4 = 0b1010L; // COMPLIANT - 0b1010L is of type long + + // Boundary testing in variable assignments + long long x5 = 0x7FFFFFFFL; // COMPLIANT - 0x7FFFFFFFL is of type long + long long x6 = + 0x80000000L; // COMPLIANT - 0x80000000L is of type unsigned long + long long x7 = + 0xFFFFFFFFL; // COMPLIANT - 0xFFFFFFFFL is of type unsigned long + + // These literals are of long long type but properly use LL suffix + long long x8 = 2147483648LL; // COMPLIANT - uses LL + long long x9 = 0x100000000LL; // COMPLIANT - uses LL + long long x10 = 040000000000LL; // COMPLIANT - uses LL + long long x11 = 9223372036854775807LL; // COMPLIANT - LONG_LONG_MAX with LL + + // These are NON_COMPLIANT because the literal is of type long long but uses + // single L + long long x12 = 2147483648L; // NON_COMPLIANT - should use LL + unsigned long long x13 = 4294967296UL; // NON_COMPLIANT - should use ULL + long long x14 = 0x100000000L; // NON_COMPLIANT - should use LL + long long x15 = 040000000000L; // NON_COMPLIANT - should use LL + long long x16 = 9223372036854775807L; // NON_COMPLIANT - should use LL +} + +void f6() { + // With digit separators + long long y1 = 2'147'483'647L; // COMPLIANT - fits in long + long long y2 = 2'147'483'648L; // NON_COMPLIANT - requires long long + long long y3 = 2'147'483'648LL; // COMPLIANT - correct suffix + long long y4 = + 9'223'372'036'854'775'807L; // NON_COMPLIANT - LONG_LONG_MAX with single L + long long y5 = + 9'223'372'036'854'775'807LL; // COMPLIANT - LONG_LONG_MAX with LL + + // Test auto type deduction + auto a1 = 123L; // COMPLIANT - deduces to long + auto a2 = 2147483648L; // NON_COMPLIANT - single L for long long value + auto a3 = 2147483648LL; // COMPLIANT - correct suffix + auto a4 = 0x80000000L; // COMPLIANT - deduces to unsigned long + auto a5 = 0x100000000L; // NON_COMPLIANT - deduces to long long with single L + auto a6 = 9223372036854775807L; // NON_COMPLIANT - LONG_LONG_MAX with single L + auto a7 = 9223372036854775807LL; // COMPLIANT - LONG_LONG_MAX with LL +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.expected b/cpp/misra/test/rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.expected new file mode 100644 index 0000000000..63ab95e6b4 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.expected @@ -0,0 +1,34 @@ +| test.cpp:13:13:13:19 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:14:8:14:14 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:14:18:14:24 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:15:14:15:20 | ... + ... | Expression with operator '<<' contains an unparenthesized child expression with higher precedence operator '+'. | +| test.cpp:16:8:16:14 | ... & ... | Expression with operator '\|' contains an unparenthesized child expression with higher precedence operator '&'. | +| test.cpp:17:13:17:19 | ... & ... | Expression with operator '\|' contains an unparenthesized child expression with higher precedence operator '&'. | +| test.cpp:18:8:18:15 | ... && ... | Expression with operator '\|\|' contains an unparenthesized child expression with higher precedence operator '&&'. | +| test.cpp:21:8:21:14 | ... + ... | Expression with operator '<<' contains an unparenthesized child expression with higher precedence operator '+'. | +| test.cpp:24:8:24:14 | ... < ... | Expression with operator '&&' contains an unparenthesized child expression with higher precedence operator '<'. | +| test.cpp:24:19:24:25 | ... > ... | Expression with operator '&&' contains an unparenthesized child expression with higher precedence operator '>'. | +| test.cpp:27:13:27:19 | ... + ... | Expression with operator '&' contains an unparenthesized child expression with higher precedence operator '+'. | +| test.cpp:28:13:28:19 | ... + ... | Expression with operator '^' contains an unparenthesized child expression with higher precedence operator '+'. | +| test.cpp:31:13:31:19 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:56:8:56:19 | ... + ... | Expression with operator '<<' contains an unparenthesized child expression with higher precedence operator '+'. | +| test.cpp:56:13:56:19 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:59:8:59:14 | ... > ... | Expression with operator '&&' contains an unparenthesized child expression with higher precedence operator '>'. | +| test.cpp:59:8:59:25 | ... && ... | Expression with operator '\|\|' contains an unparenthesized child expression with higher precedence operator '&&'. | +| test.cpp:59:19:59:25 | ... < ... | Expression with operator '&&' contains an unparenthesized child expression with higher precedence operator '<'. | +| test.cpp:59:30:59:37 | ... == ... | Expression with operator '\|\|' contains an unparenthesized child expression with higher precedence operator '=='. | +| test.cpp:74:17:74:28 | ... + ... | Expression with operator '?' contains an unparenthesized child expression with higher precedence operator '+'. | +| test.cpp:74:22:74:28 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:80:13:80:19 | ... + ... | Expression with operator '?' contains an unparenthesized child expression with higher precedence operator '+'. | +| test.cpp:80:23:80:29 | ... * ... | Expression with operator '?' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:104:17:104:27 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:110:16:110:22 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:112:17:112:23 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:132:14:132:20 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:134:14:134:20 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:145:16:145:22 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:153:20:153:26 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:193:16:193:22 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:203:17:203:22 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:208:1:208:38 | #define UNSAFE_MACRO(x,y,z) x + y *z | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | +| test.cpp:225:14:225:18 | ... * ... | Expression with operator '+' contains an unparenthesized child expression with higher precedence operator '*'. | diff --git a/cpp/misra/test/rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.qlref b/cpp/misra/test/rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.qlref new file mode 100644 index 0000000000..428b69934c --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.qlref @@ -0,0 +1 @@ +rules/RULE-8-0-1/MissingPrecedenceClarifyingParenthesis.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.expected b/cpp/misra/test/rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.expected new file mode 100644 index 0000000000..56f4f3b1e9 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.expected @@ -0,0 +1,3 @@ +| test.cpp:246:3:246:11 | sizeof() | Sizeof operator used without parenthesis. | +| test.cpp:253:3:253:11 | sizeof() | Sizeof operator used without parenthesis. | +| test.cpp:256:3:257:6 | sizeof() | Sizeof operator used without parenthesis. | diff --git a/cpp/misra/test/rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.qlref b/cpp/misra/test/rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.qlref new file mode 100644 index 0000000000..816b1e634e --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.qlref @@ -0,0 +1 @@ +rules/RULE-8-0-1/MissingSizeofOperatorParenthesis.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-0-1/test.cpp b/cpp/misra/test/rules/RULE-8-0-1/test.cpp new file mode 100644 index 0000000000..b729d3674f --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-0-1/test.cpp @@ -0,0 +1,263 @@ +// Test file for MISRA Rule 8-0-1: Parentheses should be used to make the +// meaning of an expression appropriately explicit + +void f1() { + int l1 = 1, l2 = 2, l3 = 3, l4 = 4; + bool l5 = true, l6 = false; + + int l7; + bool l8; + + // NON_COMPLIANT: Missing parentheses in expressions with operators of + // different precedence + l7 = l1 + l2 * l3; // NON_COMPLIANT + l7 = l1 * l2 + l3 * l4; // NON_COMPLIANT + l7 = l1 << l2 + l3; // NON_COMPLIANT + l7 = l1 & l2 | l3; // NON_COMPLIANT + l7 = l1 | l2 & l3; // NON_COMPLIANT + l8 = l5 && l6 || l5; // NON_COMPLIANT + + // NON_COMPLIANT: Assignment with complex right-hand side + l7 = l1 + l2 << l3; // NON_COMPLIANT + + // NON_COMPLIANT: Conditional expressions without parentheses + l8 = l1 < l2 && l3 > l4; // NON_COMPLIANT + + // NON_COMPLIANT: Bit operations mixed with arithmetic + l7 = l1 & l2 + l3; // NON_COMPLIANT + l7 = l1 ^ l2 + l3; // NON_COMPLIANT + + // NON_COMPLIANT: Nested operations of different precedence + l7 = l1 + l2 * l3 + l4; // NON_COMPLIANT + + // COMPLIANT: Same operators, no precedence confusion + l7 = l1 + l2 + l3; // COMPLIANT: all same precedence + l7 = l1 * l2 * l3; // COMPLIANT: all same precedence + + // COMPLIANT: Different operators but with explicit parentheses + l7 = l1 + (l2 * l3); // COMPLIANT: parentheses clarify precedence + l7 = (l1 * l2) + (l3 * l4); // COMPLIANT: parentheses clarify precedence + l7 = l1 << (l2 + l3); // COMPLIANT: parentheses clarify precedence + l7 = (l1 & l2) | l3; // COMPLIANT: parentheses clarify precedence + l7 = l1 | (l2 & l3); // COMPLIANT: parentheses clarify precedence + l8 = (l5 && l6) || l5; // COMPLIANT: parentheses clarify precedence + + l7 = (l1 + l2) << l3; // COMPLIANT: parentheses clarify precedence + l8 = (l1 < l2) && (l3 > l4); // COMPLIANT: parentheses clarify precedence + + // COMPLIANT: Bit operations with parentheses + l7 = l1 & (l2 + l3); // COMPLIANT: parentheses clarify precedence + l7 = l1 ^ (l2 + l3); // COMPLIANT: parentheses clarify precedence + + // COMPLIANT: Nested operations with parentheses + l7 = l1 + (l2 * l3) + l4; // COMPLIANT: parentheses clarify precedence + + // NON_COMPLIANT: Multiple levels of precedence without clarification + l7 = l1 + l2 * l3 << l4; // NON_COMPLIANT + + // NON_COMPLIANT: Mixed logical and comparison operators + l8 = l1 > l2 && l3 < l4 || l1 == l2; // NON_COMPLIANT + + // COMPLIANT: Multiple levels with proper parentheses + l7 = l1 + ((l2 * l3) << l4); // COMPLIANT: fully parenthesized + + // COMPLIANT: Mixed logical and comparison with parentheses + l8 = ((l1 > l2) && (l3 < l4)) || (l1 == l2); // COMPLIANT: fully parenthesized +} + +// Test conditional expressions +void f2() { + int l1 = 1, l2 = 2, l3 = 3; + bool l4 = true; + + // NON_COMPLIANT: Conditional expression without clarifying parentheses + int l5 = l4 ? l1 + l2 * l3 : l1; // NON_COMPLIANT + + // COMPLIANT: Conditional expression with clarifying parentheses + l5 = l4 ? (l2 * l3) : l1; // COMPLIANT: multiplication is parenthesized + + // NON_COMPLIANT: Nested conditional without clarifying parentheses + l5 = l4 ? l1 + l2 : l3 * l1; // NON_COMPLIANT + + // COMPLIANT: Nested conditional with clarifying parentheses + l5 = l4 ? (l1 + l2) : (l3 * l1); // COMPLIANT +} + +// Test assignment expressions +void f3() { + int l1 = 1, l2 = 2, l3 = 3, l4 = 4; + + // COMPLIANT: Assignment is excluded and compliant + l1 += l2 * l3; // COMPLIANT + + // COMPLIANT: Chained assignment without clarifying parentheses + l1 = l2 = l3; +} + +int f4(int l1) { return l1 * 2; } + +// Test expressions with function calls +void f5() { + int l1 = 1, l2 = 2, l3 = 3; + + // NON_COMPLIANT: Function call in complex expression without parentheses + int l4 = l1 + f4(l2) * l3; // NON_COMPLIANT + + // COMPLIANT: Function call in complex expression with parentheses + l4 = l1 + (f4(l2) * l3); // COMPLIANT: multiplication is parenthesized + + // NON_COMPLIANT: Complex argument without parentheses + l4 = f4(l1 + l2 * l3); // NON_COMPLIANT + + l4 = f4((l1 + l2 * l3)); // NON_COMPLIANT: inner expression + l4 = f4(l1 + (l2 * l3)); // COMPLIANT: multiplication is parenthesized +} + +// Test unary operators (excluded, always compliant) +void f6() { + int l1 = 1, l2 = 2, l3 = 3; + int *l4 = &l1; + + // COMPLIANT: Unary plus and minus (excluded from rule) + int l5 = -l1 + l2; // COMPLIANT: unary minus is excluded + l5 = +l1 * l2; // COMPLIANT: unary plus is excluded + l5 = -l1 * +l2; // COMPLIANT: unary operators are excluded + + // COMPLIANT: Unary pointer operations (excluded from rule) + int l6 = *l4 + l2; // COMPLIANT: pointer dereference is excluded + l4 = &l4[0] + l2; // COMPLIANT: address-of is excluded + l6 = *l4 * +l4[0]; // COMPLIANT: unary operators are excluded + + // Combinations with binary operators + l6 = -l1 + l2 * l3; // NON_COMPLIANT: binary operators still need parentheses + l6 = -l1 + (l2 * l3); // COMPLIANT: binary operation is parenthesized + l6 = *l4 + l2 * l3; // NON_COMPLIANT: binary operators still need parentheses + l6 = *l4 + (l2 * l3); // COMPLIANT: binary operation is parenthesized +} + +// Test throw and comma operator (excluded and compliant) +void f7() { + int l1 = 1, l2 = 2, l3 = 3; + + // COMPLIANT: Throw expression (excluded from rule) + try { + throw l1 + l2; // COMPLIANT: throw is excluded + throw l1 + l2 * l3; // NON_COMPLIANT: expression inside throw + } catch (int) { + } + + // COMPLIANT: Comma operator (excluded from rule) + int l4 = (l1 + l2, l3); // COMPLIANT: comma operator is excluded + + // But binary operators in these contexts still need parentheses + l4 = (l1++, l2 + l3 * l1); // NON_COMPLIANT: expression inside comma operator + l4 = (l1++, l2 + (l3 * l1)); // COMPLIANT: multiplication is parenthesized +} + +// Test other excluded operators and expressions +void f8() { + struct S { + int l1; + int l2; + int arr[5]; + }; + + S l1 = {1, 2, {1, 2, 3, 4, 5}}; + S *l2 = &l1; + int l3 = 1, l4 = 2; + + // COMPLIANT: Member access, subscript, inc/dec (excluded from rule) + int l5 = l1.l1 + l3; // COMPLIANT: member access is excluded + l5 = l2->l2 * l4; // COMPLIANT: member access is excluded + l5 = l1.arr[0] + l4; // COMPLIANT: subscript is excluded + l5 = l3++ + l4; // COMPLIANT: increment is excluded + l5 = --l4 * l3; // COMPLIANT: decrement is excluded + + // COMPLIANT: Structure literals (excluded from rule) + S l6 = {l3 + l4, l3 * l4}; // COMPLIANT: initializer is excluded + + // COMPLIANT: C-style casts (excluded from rule) + l5 = (int)3.14 + l3; // COMPLIANT: cast is excluded + + // COMPLIANT: Literals (excluded from rule) + l5 = 42 + l3; // COMPLIANT: literals are excluded + + // COMPLIANT: sizeof (excluded from rule) + l5 = sizeof(int) * l3; // COMPLIANT: sizeof is excluded + + // COMPLIANT: new/delete (excluded from rule) + int *l7 = new int(l3 + l4); // COMPLIANT: new is excluded + delete l7; // COMPLIANT: delete is excluded + + // Binary operators still need parentheses + l5 = l1.l1 + l3 * l4; // NON_COMPLIANT: multiplication has higher precedence + l5 = l1.l1 + (l3 * l4); // COMPLIANT: multiplication is parenthesized +} + +// Test lambdas (excluded and compliant) +void f9() { + int l1 = 1, l2 = 2; + + // COMPLIANT: Lambda expressions (excluded from rule) + auto l4 = [&]() { + return l1 + l2 * 2; // NON_COMPLIANT: lambda body still checked + }(); // COMPLIANT: lambda definition is always compliant +} + +// Test macros +#define UNSAFE_MACRO(x, y, z) x + y *z // NON_COMPLIANT macro definition +#define SAFE_MACRO(x, y, z) x + (y * z) // COMPLIANT macro definition + +void f10() { + int l1 = 1, l2 = 2, l3 = 3; + + // Macro usage + int l4 = UNSAFE_MACRO(l1, l2, l3); // NON_COMPLIANT + l4 = SAFE_MACRO(l1, l2, l3); // COMPLIANT + + // Additional macro usage + l4 = UNSAFE_MACRO(l1, l2, l3) + l1; // NON_COMPLIANT + l4 = SAFE_MACRO(l1, l2, l3) + l1; // COMPLIANT +} + +// Test templates +template T unsafe_template(T x, T y, T z) { + return x + y * z; // NON_COMPLIANT: template function without parentheses +} + +template T safe_template(T x, T y, T z) { + return x + (y * z); // COMPLIANT: template function with parentheses +} + +void f11() { + int l1 = 1, l2 = 2, l3 = 3; + + // Template usage + int l4 = unsafe_template(l1, l2, l3); // NON_COMPLIANT + l4 = safe_template(l1, l2, l3); // COMPLIANT + + // Additional template usage + l4 = unsafe_template(l1, l2, l3) + l1; // NON_COMPLIANT + l4 = safe_template(l1, l2, l3) + l1; // COMPLIANT +} + +void f12() { + int l1; + sizeof l1; // NON_COMPLIANT: sizeof without parentheses + sizeof(l1); // COMPLIANT: sizeof with parentheses + sizeof(int); // COMPLIANT: sizeof with type always has parentheses + // sizeof int; -- not valid C++ + + // Since we're using locations to check this, add stress checks: + // clang-format off + sizeof l1 ; // NON_COMPLIANT + sizeof ( l1 ); // COMPLIANT + sizeof ( int) ; // COMPLIANT + sizeof // NON_COMPLIANT + l1 + ; + sizeof ( // COMPLIANT + l1 + ); + // clang-format on +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.expected b/cpp/misra/test/rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.expected new file mode 100644 index 0000000000..d75edf2335 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.expected @@ -0,0 +1,135 @@ +edges +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | test.cpp:164:14:164:31 | call to forward | | | +| test.cpp:8:59:8:59 | f | test.cpp:10:12:10:12 | f | | | +| test.cpp:8:59:8:59 | f | test.cpp:10:12:10:12 | f | | | +| test.cpp:8:59:8:59 | f | test.cpp:10:12:10:12 | f | | | +| test.cpp:10:12:10:12 | f | test.cpp:10:8:10:8 | l | | | +| test.cpp:10:12:10:12 | f | test.cpp:10:8:10:8 | l | | | +| test.cpp:10:12:10:12 | f | test.cpp:10:8:10:8 | l | | | +| test.cpp:37:14:37:34 | [...](...){...} | test.cpp:37:10:37:11 | l3 | | | +| test.cpp:38:14:38:34 | [...](...){...} | test.cpp:38:10:38:11 | l4 | | | +| test.cpp:41:14:41:48 | [...](...){...} | test.cpp:41:10:41:11 | l7 | | | +| test.cpp:55:28:55:47 | [...](...){...} | test.cpp:8:59:8:59 | f | | | +| test.cpp:56:28:56:47 | [...](...){...} | test.cpp:8:59:8:59 | f | | | +| test.cpp:59:28:59:61 | [...](...){...} | test.cpp:8:59:8:59 | f | | | +| test.cpp:90:39:90:58 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | | | +| test.cpp:91:39:91:58 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | | | +| test.cpp:97:28:97:47 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | | | +| test.cpp:98:28:98:47 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | | | +| test.cpp:135:16:135:38 | ... , ... | test.cpp:135:10:135:11 | l2 | | | +| test.cpp:135:19:135:38 | [...](...){...} | test.cpp:135:16:135:38 | ... , ... | | | +| test.cpp:138:15:138:18 | ... ? ... : ... | test.cpp:138:10:138:11 | l4 | | | +| test.cpp:138:22:138:41 | [...](...){...} | test.cpp:138:15:138:18 | ... ? ... : ... | | | +| test.cpp:141:15:141:18 | ... ? ... : ... | test.cpp:141:10:141:11 | l6 | | | +| test.cpp:141:38:141:57 | [...](...){...} | test.cpp:141:15:141:18 | ... ? ... : ... | | | +| test.cpp:153:39:153:58 | [...](...){...} | test.cpp:145:31:145:63 | function_outside_translation_unit | | | +| test.cpp:154:39:154:58 | [...](...){...} | test.cpp:145:31:145:63 | function_outside_translation_unit | | | +| test.cpp:163:47:163:47 | f | test.cpp:164:33:164:33 | f | | | +| test.cpp:164:14:164:31 | call to forward | test.cpp:164:5:164:35 | new | | | +| test.cpp:164:33:164:33 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| test.cpp:176:15:176:34 | [...](...){...} | test.cpp:163:47:163:47 | f | | | +nodes +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | semmle.label | new | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | semmle.label | new | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | semmle.label | new | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | semmle.label | new | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | semmle.label | call to forward | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | semmle.label | call to forward | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | semmle.label | call to forward | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | semmle.label | call to forward | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| test.cpp:8:59:8:59 | f | semmle.label | f | +| test.cpp:8:59:8:59 | f | semmle.label | f | +| test.cpp:8:59:8:59 | f | semmle.label | f | +| test.cpp:10:8:10:8 | l | semmle.label | l | +| test.cpp:10:8:10:8 | l | semmle.label | l | +| test.cpp:10:8:10:8 | l | semmle.label | l | +| test.cpp:10:12:10:12 | f | semmle.label | f | +| test.cpp:10:12:10:12 | f | semmle.label | f | +| test.cpp:10:12:10:12 | f | semmle.label | f | +| test.cpp:37:10:37:11 | l3 | semmle.label | l3 | +| test.cpp:37:14:37:34 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:38:10:38:11 | l4 | semmle.label | l4 | +| test.cpp:38:14:38:34 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:41:10:41:11 | l7 | semmle.label | l7 | +| test.cpp:41:14:41:48 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:55:28:55:47 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:56:28:56:47 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:59:28:59:61 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:90:39:90:58 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:91:39:91:58 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:97:28:97:47 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:98:28:98:47 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:135:10:135:11 | l2 | semmle.label | l2 | +| test.cpp:135:16:135:38 | ... , ... | semmle.label | ... , ... | +| test.cpp:135:19:135:38 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:138:10:138:11 | l4 | semmle.label | l4 | +| test.cpp:138:15:138:18 | ... ? ... : ... | semmle.label | ... ? ... : ... | +| test.cpp:138:22:138:41 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:141:10:141:11 | l6 | semmle.label | l6 | +| test.cpp:141:15:141:18 | ... ? ... : ... | semmle.label | ... ? ... : ... | +| test.cpp:141:38:141:57 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:145:31:145:63 | function_outside_translation_unit | semmle.label | function_outside_translation_unit | +| test.cpp:145:31:145:63 | function_outside_translation_unit | semmle.label | function_outside_translation_unit | +| test.cpp:153:39:153:58 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:154:39:154:58 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:163:47:163:47 | f | semmle.label | f | +| test.cpp:164:5:164:35 | new | semmle.label | new | +| test.cpp:164:14:164:31 | call to forward | semmle.label | call to forward | +| test.cpp:164:33:164:33 | f | semmle.label | f | +| test.cpp:176:15:176:34 | [...](...){...} | semmle.label | [...](...){...} | +#select +| test.cpp:37:14:37:34 | [...](...){...} | test.cpp:37:14:37:34 | [...](...){...} | test.cpp:37:10:37:11 | l3 | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:38:14:38:34 | [...](...){...} | test.cpp:38:14:38:34 | [...](...){...} | test.cpp:38:10:38:11 | l4 | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:41:14:41:48 | [...](...){...} | test.cpp:41:14:41:48 | [...](...){...} | test.cpp:41:10:41:11 | l7 | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:55:28:55:47 | [...](...){...} | test.cpp:55:28:55:47 | [...](...){...} | test.cpp:10:8:10:8 | l | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:56:28:56:47 | [...](...){...} | test.cpp:56:28:56:47 | [...](...){...} | test.cpp:10:8:10:8 | l | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:59:28:59:61 | [...](...){...} | test.cpp:59:28:59:61 | [...](...){...} | test.cpp:10:8:10:8 | l | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:90:39:90:58 | [...](...){...} | test.cpp:90:39:90:58 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:91:39:91:58 | [...](...){...} | test.cpp:91:39:91:58 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:97:28:97:47 | [...](...){...} | test.cpp:97:28:97:47 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:98:28:98:47 | [...](...){...} | test.cpp:98:28:98:47 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:23:7:23:8 | C1 | C1 | +| test.cpp:135:19:135:38 | [...](...){...} | test.cpp:135:19:135:38 | [...](...){...} | test.cpp:135:10:135:11 | l2 | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:131:7:131:8 | C2 | C2 | +| test.cpp:138:22:138:41 | [...](...){...} | test.cpp:138:22:138:41 | [...](...){...} | test.cpp:138:10:138:11 | l4 | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:131:7:131:8 | C2 | C2 | +| test.cpp:141:38:141:57 | [...](...){...} | test.cpp:141:38:141:57 | [...](...){...} | test.cpp:141:10:141:11 | l6 | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:131:7:131:8 | C2 | C2 | +| test.cpp:153:39:153:58 | [...](...){...} | test.cpp:153:39:153:58 | [...](...){...} | test.cpp:145:31:145:63 | function_outside_translation_unit | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is passed to a different translation unit. | test.cpp:147:7:147:8 | C3 | C3 | +| test.cpp:154:39:154:58 | [...](...){...} | test.cpp:154:39:154:58 | [...](...){...} | test.cpp:145:31:145:63 | function_outside_translation_unit | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is passed to a different translation unit. | test.cpp:147:7:147:8 | C3 | C3 | +| test.cpp:176:15:176:34 | [...](...){...} | test.cpp:176:15:176:34 | [...](...){...} | test.cpp:164:5:164:35 | new | Lambda implicitly captures `this` pointer of $@, making its lifetime dependent on the lifetime of the class object, and the lambda is not considered transient because it is copied or moved. | test.cpp:168:7:168:8 | C4 | C4 | diff --git a/cpp/misra/test/rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.qlref b/cpp/misra/test/rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.qlref new file mode 100644 index 0000000000..9acf6c615d --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.qlref @@ -0,0 +1 @@ +rules/RULE-8-1-1/NonTransientLambdaImplicitlyCapturesThis.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-1-1/test.cpp b/cpp/misra/test/rules/RULE-8-1-1/test.cpp new file mode 100644 index 0000000000..123ce6ede3 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-1-1/test.cpp @@ -0,0 +1,178 @@ +#include + +template void function_transient(Func f) { + // transient, does not store. + f(); +} + +template void function_not_transient(Func f) { + // Non transient, stores the lambda. + auto l = f; // NON_COMPLIANT +} + +template void calls_function_transient(Func f) { + // Calls a function that takes a transient lambda. + function_transient(f); +} + +template void calls_function_not_transient(Func f) { + // Calls a function that takes a non transient lambda. + function_not_transient(f); // NON_COMPLIANT +} + +class C1 { + int f1; + + void m1() { + // Not transient (always compliant) + [=]() { return f1; }(); // COMPLIANT + [&]() { return f1; }(); // COMPLIANT + [this]() { return f1; }(); // COMPLIANT + [self = *this]() { return self.f1; }(); // COMPLIANT + [&, self = *this]() { return f1; }(); // COMPLIANT + + // Transient by immediate move (must not implicitly capture this) + auto l1 = []() { return 1; }; // COMPLIANT + auto l2 = [=]() { return 1; }; // COMPLIANT + auto l3 = [=]() { return f1; }; // NON_COMPLIANT + auto l4 = [&]() { return f1; }; // NON_COMPLIANT + auto l5 = [this]() { return f1; }; // COMPLIANT + auto l6 = [self = *this]() { return self.f1; }; // COMPLIANT + auto l7 = [&, self = *this]() { return f1; }; // NON_COMPLIANT + + // Compliant, transient lambdas. + function_transient([]() { return 1; }); // COMPLIANT + function_transient([=]() { return 1; }); // COMPLIANT + function_transient([=]() { return f1; }); // COMPLIANT + function_transient([&]() { return f1; }); // COMPLIANT + function_transient([this]() { return f1; }); // COMPLIANT + function_transient([self = *this]() { return self.f1; }); // COMPLIANT + function_transient([&, self = *this]() { return f1; }); // COMPLIANT + + // Non-compliant with implicit this capture, non-transient lambdas. + function_not_transient([]() { return 1; }); // COMPLIANT + function_not_transient([=]() { return 1; }); // COMPLIANT + function_not_transient([=]() { return f1; }); // NON_COMPLIANT + function_not_transient([&]() { return f1; }); // NON_COMPLIANT + function_not_transient([this]() { return f1; }); // COMPLIANT + function_not_transient([self = *this]() { return self.f1; }); // COMPLIANT + function_not_transient([&, self = *this]() { return f1; }); // NON_COMPLIANT + + // Not valid cpp, as lambda expressions aren't rvalues: + // f(&([](int i) { return i; })); + // Not valid cpp, as each lambda expression has a unique implicit type: + // decltype([](int i) { return i; }) &f = [](int i) { return i; }; + + // No implicit capture of this, always compliant. + []() { return 1; }(); // COMPLIANT + [=]() { return 1; }(); // COMPLIANT + [&]() { return 1; }(); // COMPLIANT + [this]() { return 1; }(); // COMPLIANT + [self = *this]() { return 1; }(); // COMPLIANT + function_not_transient([]() { return 1; }()); // COMPLIANT + function_not_transient([=]() { return 1; }()); // COMPLIANT + function_not_transient([&]() { return 1; }()); // COMPLIANT + function_not_transient([this]() { return 1; }()); // COMPLIANT + function_not_transient([self = *this]() { return 1; }()); // COMPLIANT + + // Dead lambdas should be considered transient: + []() { return 1; }; // COMPLIANT + [=]() { return 1; }; // COMPLIANT + [=]() { return f1; }; // COMPLIANT + [&]() { return f1; }; // COMPLIANT + [this]() { return f1; }; // COMPLIANT + [self = *this]() { return self.f1; }; // COMPLIANT + [&, self = *this]() { return f1; }; // COMPLIANT + + // Casting to std::function is a copy, so it is non-transient: + static_cast>([]() { return 1; }); // COMPLIANT + static_cast>([]() { return 1; }); // COMPLIANT + static_cast>([=]() { return f1; }); // NON_COMPLIANT + static_cast>([&]() { return f1; }); // NON_COMPLIANT + static_cast>([this]() { return f1; }); // COMPLIANT + static_cast>( + [self = *this]() { return self.f1; }); // COMPLIANT + (std::function)([]() { return 1; }); // COMPLIANT + (std::function)([=]() { return 1; }); // COMPLIANT + (std::function)([=]() { return f1; }); // NON_COMPLIANT + (std::function)([&]() { return f1; }); // NON_COMPLIANT + (std::function)([this]() { return f1; }); // COMPLIANT + (std::function)([self = *this]() { return self.f1; }); // COMPLIANT + static_cast([]() { return 1; }); // COMPLIANT + + // Not valid cpp: + // reinterpret_cast>([]() { return 1; }); + // dynamic_cast>([]() { return 1; }); + // static_cast([=]() { return 1; }); + // static_cast([&]() { return 1; }); + // static_cast([this]() { return 1; }); + // static_cast([self = *this]() { return 1; }); + } + + static int f2; + static void m2() { + // No 'this' pointer to capture, since its a static method. + auto l1 = []() { return 1; }; // COMPLIANT + auto l2 = [=]() { return 1; }; // COMPLIANT + auto l3 = [&]() { return 1; }; // COMPLIANT + auto l4 = []() { return f2; }; // COMPLIANT + auto l5 = [=]() { return f2; }; // COMPLIANT + auto l6 = [&]() { return f2; }; // COMPLIANT + } +}; + +void f1() { + // No 'this' pointer to capture, since its not a class method. + auto l1 = []() { return 1; }; // COMPLIANT + auto l2 = [=]() { return 1; }; // COMPLIANT + auto l3 = [&]() { return 1; }; // COMPLIANT +} + +class C2 { + int f1; + void m2() { + auto l1 = (1, [&]() { return 1; }); // COMPLIANT + auto l2 = (1, [&]() { return f1; }); // NON_COMPLIANT + auto l3 = true ? [&]() { return 1; } // COMPLIANT + : throw "error"; + auto l4 = true ? [&]() { return f1; } // NON_COMPLIANT + : throw "error"; + auto l5 = true ? throw "error" : [&]() { return 1; }; // COMPLIANT + auto l6 = true ? throw "error" : [&]() { return f1; }; // NON_COMPLIANT + } +}; + +template void function_outside_translation_unit(Func f); + +class C3 { + int f1, f2; + + void m3() { + // Passing the lambda to a function outside the translation unit is + // considered non-transient, as the lambda may be stored and used later. + function_outside_translation_unit([=]() { return f1; }); // NON_COMPLIANT + function_outside_translation_unit([&]() { return f1; }); // NON_COMPLIANT + function_outside_translation_unit([this]() { return f1; }); // COMPLIANT + function_outside_translation_unit( + [self = *this]() { return self.f1; }); // COMPLIANT + } +}; + +class CopyAssignFunctorClass { +public: + template void operator=(Func &&f) { + new Func(std::forward(f)); + } +}; + +class C4 { + int f1; + + void m4() { + CopyAssignFunctorClass functor; + // Not transient, but no implicit this: + functor = [&]() { return 1; }; // COMPLIANT + // Non transient (copies assigned value), implicit this + functor = [&]() { return f1; }; // NON_COMPLIANT + } +}; \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.expected b/cpp/misra/test/rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.expected new file mode 100644 index 0000000000..4911b18da3 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.expected @@ -0,0 +1,200 @@ +edges +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | test.cpp:153:52:153:69 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | test.cpp:153:52:153:69 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | test.cpp:153:52:153:69 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | test.cpp:153:52:153:69 | call to forward | | | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | test.cpp:158:46:158:63 | call to forward | | | +| test.cpp:8:59:8:59 | f | test.cpp:10:12:10:12 | f | | | +| test.cpp:8:59:8:59 | f | test.cpp:10:12:10:12 | f | | | +| test.cpp:8:59:8:59 | f | test.cpp:10:12:10:12 | f | | | +| test.cpp:10:12:10:12 | f | test.cpp:10:8:10:8 | l | | | +| test.cpp:10:12:10:12 | f | test.cpp:10:8:10:8 | l | | | +| test.cpp:10:12:10:12 | f | test.cpp:10:8:10:8 | l | | | +| test.cpp:36:13:36:33 | [...](...){...} | test.cpp:36:8:36:10 | la3 | | | +| test.cpp:37:13:37:33 | [...](...){...} | test.cpp:37:8:37:10 | la4 | | | +| test.cpp:40:13:40:42 | [...](...){...} | test.cpp:40:8:40:10 | la7 | | | +| test.cpp:54:26:54:45 | [...](...){...} | test.cpp:8:59:8:59 | f | | | +| test.cpp:55:26:55:45 | [...](...){...} | test.cpp:8:59:8:59 | f | | | +| test.cpp:58:26:58:54 | [...](...){...} | test.cpp:8:59:8:59 | f | | | +| test.cpp:86:37:86:56 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | | | +| test.cpp:87:37:87:56 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | | | +| test.cpp:92:26:92:45 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | | | +| test.cpp:93:26:93:45 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | | | +| test.cpp:119:13:119:33 | [...](...){...} | test.cpp:119:8:119:10 | la7 | | | +| test.cpp:120:13:120:33 | [...](...){...} | test.cpp:120:8:120:10 | la8 | | | +| test.cpp:127:15:127:36 | ... , ... | test.cpp:127:8:127:10 | lb2 | | | +| test.cpp:127:18:127:36 | [...](...){...} | test.cpp:127:15:127:36 | ... , ... | | | +| test.cpp:130:14:130:17 | ... ? ... : ... | test.cpp:130:8:130:10 | lb4 | | | +| test.cpp:130:21:130:39 | [...](...){...} | test.cpp:130:14:130:17 | ... ? ... : ... | | | +| test.cpp:133:14:133:17 | ... ? ... : ... | test.cpp:133:8:133:10 | lb6 | | | +| test.cpp:133:37:133:55 | [...](...){...} | test.cpp:133:14:133:17 | ... ? ... : ... | | | +| test.cpp:143:37:143:56 | [...](...){...} | test.cpp:136:31:136:63 | function_outside_translation_unit | | | +| test.cpp:144:37:144:56 | [...](...){...} | test.cpp:136:31:136:63 | function_outside_translation_unit | | | +| test.cpp:148:7:148:35 | [...](...){...} | test.cpp:136:31:136:63 | function_outside_translation_unit | | | +| test.cpp:153:38:153:38 | f | test.cpp:153:71:153:71 | f | | | +| test.cpp:153:38:153:38 | f | test.cpp:153:71:153:71 | f | | | +| test.cpp:153:38:153:38 | f | test.cpp:153:71:153:71 | f | | | +| test.cpp:153:38:153:38 | f | test.cpp:153:71:153:71 | f | | | +| test.cpp:153:52:153:69 | call to forward | test.cpp:153:43:153:73 | new | | | +| test.cpp:153:52:153:69 | call to forward | test.cpp:153:43:153:73 | new | | | +| test.cpp:153:52:153:69 | call to forward | test.cpp:153:43:153:73 | new | | | +| test.cpp:153:52:153:69 | call to forward | test.cpp:153:43:153:73 | new | | | +| test.cpp:153:71:153:71 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| test.cpp:153:71:153:71 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| test.cpp:153:71:153:71 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| test.cpp:153:71:153:71 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| test.cpp:158:38:158:38 | f | test.cpp:158:65:158:65 | f | | | +| test.cpp:158:46:158:63 | call to forward | test.cpp:153:38:153:38 | f | | | +| test.cpp:158:65:158:65 | f | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | | | +| test.cpp:171:9:171:28 | [...](...){...} | test.cpp:153:38:153:38 | f | | | +| test.cpp:172:9:172:28 | [...](...){...} | test.cpp:153:38:153:38 | f | | | +| test.cpp:175:9:175:37 | [...](...){...} | test.cpp:153:38:153:38 | f | | | +| test.cpp:178:10:178:29 | [...](...){...} | test.cpp:158:38:158:38 | f | | | +| test.cpp:181:10:181:29 | [...](...){...} | test.cpp:163:28:163:29 | C3 | | | +nodes +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:92:35:92:35 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | semmle.label | new | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | semmle.label | new | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | semmle.label | new | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | semmle.label | new | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | semmle.label | call to forward | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | semmle.label | call to forward | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | semmle.label | call to forward | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:23:93:37 | call to forward | semmle.label | call to forward | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:39:93:39 | f | semmle.label | f | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:4:65:4:65 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/utility.h:5:29:5:29 | t | semmle.label | t | +| test.cpp:8:59:8:59 | f | semmle.label | f | +| test.cpp:8:59:8:59 | f | semmle.label | f | +| test.cpp:8:59:8:59 | f | semmle.label | f | +| test.cpp:10:8:10:8 | l | semmle.label | l | +| test.cpp:10:8:10:8 | l | semmle.label | l | +| test.cpp:10:8:10:8 | l | semmle.label | l | +| test.cpp:10:12:10:12 | f | semmle.label | f | +| test.cpp:10:12:10:12 | f | semmle.label | f | +| test.cpp:10:12:10:12 | f | semmle.label | f | +| test.cpp:36:8:36:10 | la3 | semmle.label | la3 | +| test.cpp:36:13:36:33 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:37:8:37:10 | la4 | semmle.label | la4 | +| test.cpp:37:13:37:33 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:40:8:40:10 | la7 | semmle.label | la7 | +| test.cpp:40:13:40:42 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:54:26:54:45 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:55:26:55:45 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:58:26:58:54 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:86:37:86:56 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:87:37:87:56 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:92:26:92:45 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:93:26:93:45 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:119:8:119:10 | la7 | semmle.label | la7 | +| test.cpp:119:13:119:33 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:120:8:120:10 | la8 | semmle.label | la8 | +| test.cpp:120:13:120:33 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:127:8:127:10 | lb2 | semmle.label | lb2 | +| test.cpp:127:15:127:36 | ... , ... | semmle.label | ... , ... | +| test.cpp:127:18:127:36 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:130:8:130:10 | lb4 | semmle.label | lb4 | +| test.cpp:130:14:130:17 | ... ? ... : ... | semmle.label | ... ? ... : ... | +| test.cpp:130:21:130:39 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:133:8:133:10 | lb6 | semmle.label | lb6 | +| test.cpp:133:14:133:17 | ... ? ... : ... | semmle.label | ... ? ... : ... | +| test.cpp:133:37:133:55 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:136:31:136:63 | function_outside_translation_unit | semmle.label | function_outside_translation_unit | +| test.cpp:136:31:136:63 | function_outside_translation_unit | semmle.label | function_outside_translation_unit | +| test.cpp:136:31:136:63 | function_outside_translation_unit | semmle.label | function_outside_translation_unit | +| test.cpp:143:37:143:56 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:144:37:144:56 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:148:7:148:35 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:153:38:153:38 | f | semmle.label | f | +| test.cpp:153:38:153:38 | f | semmle.label | f | +| test.cpp:153:38:153:38 | f | semmle.label | f | +| test.cpp:153:38:153:38 | f | semmle.label | f | +| test.cpp:153:43:153:73 | new | semmle.label | new | +| test.cpp:153:43:153:73 | new | semmle.label | new | +| test.cpp:153:43:153:73 | new | semmle.label | new | +| test.cpp:153:43:153:73 | new | semmle.label | new | +| test.cpp:153:52:153:69 | call to forward | semmle.label | call to forward | +| test.cpp:153:52:153:69 | call to forward | semmle.label | call to forward | +| test.cpp:153:52:153:69 | call to forward | semmle.label | call to forward | +| test.cpp:153:52:153:69 | call to forward | semmle.label | call to forward | +| test.cpp:153:71:153:71 | f | semmle.label | f | +| test.cpp:153:71:153:71 | f | semmle.label | f | +| test.cpp:153:71:153:71 | f | semmle.label | f | +| test.cpp:153:71:153:71 | f | semmle.label | f | +| test.cpp:158:38:158:38 | f | semmle.label | f | +| test.cpp:158:46:158:63 | call to forward | semmle.label | call to forward | +| test.cpp:158:65:158:65 | f | semmle.label | f | +| test.cpp:163:28:163:29 | C3 | semmle.label | C3 | +| test.cpp:171:9:171:28 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:172:9:172:28 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:175:9:175:37 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:178:10:178:29 | [...](...){...} | semmle.label | [...](...){...} | +| test.cpp:181:10:181:29 | [...](...){...} | semmle.label | [...](...){...} | +#select +| test.cpp:36:13:36:33 | [...](...){...} | test.cpp:36:13:36:33 | [...](...){...} | test.cpp:36:8:36:10 | la3 | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:36:29:36:29 | l1 | l1 | +| test.cpp:37:13:37:33 | [...](...){...} | test.cpp:37:13:37:33 | [...](...){...} | test.cpp:37:8:37:10 | la4 | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:37:29:37:29 | l1 | l1 | +| test.cpp:40:13:40:42 | [...](...){...} | test.cpp:40:13:40:42 | [...](...){...} | test.cpp:40:8:40:10 | la7 | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:40:38:40:38 | l2 | l2 | +| test.cpp:54:26:54:45 | [...](...){...} | test.cpp:54:26:54:45 | [...](...){...} | test.cpp:10:8:10:8 | l | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:54:41:54:41 | l1 | l1 | +| test.cpp:55:26:55:45 | [...](...){...} | test.cpp:55:26:55:45 | [...](...){...} | test.cpp:10:8:10:8 | l | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:55:41:55:41 | l1 | l1 | +| test.cpp:58:26:58:54 | [...](...){...} | test.cpp:58:26:58:54 | [...](...){...} | test.cpp:10:8:10:8 | l | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:58:50:58:50 | l2 | l2 | +| test.cpp:86:37:86:56 | [...](...){...} | test.cpp:86:37:86:56 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:86:52:86:52 | l1 | l1 | +| test.cpp:87:37:87:56 | [...](...){...} | test.cpp:87:37:87:56 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:87:52:87:52 | l1 | l1 | +| test.cpp:92:26:92:45 | [...](...){...} | test.cpp:92:26:92:45 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:92:41:92:41 | l1 | l1 | +| test.cpp:93:26:93:45 | [...](...){...} | test.cpp:93:26:93:45 | [...](...){...} | file:///home/runner/work/codeql-coding-standards/codeql-coding-standards/cpp/common/test/includes/standard-library/functional.h:93:17:93:41 | new | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:93:41:93:41 | l1 | l1 | +| test.cpp:119:13:119:33 | [...](...){...} | test.cpp:119:13:119:33 | [...](...){...} | test.cpp:119:8:119:10 | la7 | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:119:29:119:29 | l1 | l1 | +| test.cpp:120:13:120:33 | [...](...){...} | test.cpp:120:13:120:33 | [...](...){...} | test.cpp:120:8:120:10 | la8 | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:120:29:120:29 | l1 | l1 | +| test.cpp:127:18:127:36 | [...](...){...} | test.cpp:127:18:127:36 | [...](...){...} | test.cpp:127:8:127:10 | lb2 | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:127:33:127:33 | x | x | +| test.cpp:130:21:130:39 | [...](...){...} | test.cpp:130:21:130:39 | [...](...){...} | test.cpp:130:8:130:10 | lb4 | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:130:36:130:36 | x | x | +| test.cpp:133:37:133:55 | [...](...){...} | test.cpp:133:37:133:55 | [...](...){...} | test.cpp:133:8:133:10 | lb6 | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:133:52:133:52 | x | x | +| test.cpp:143:37:143:56 | [...](...){...} | test.cpp:143:37:143:56 | [...](...){...} | test.cpp:136:31:136:63 | function_outside_translation_unit | Lambda implicitly captures $@ but is not considered a transient lambda because it is passed to a different translation unit, resulting in obfuscated lifetimes. | test.cpp:143:52:143:52 | l1 | l1 | +| test.cpp:144:37:144:56 | [...](...){...} | test.cpp:144:37:144:56 | [...](...){...} | test.cpp:136:31:136:63 | function_outside_translation_unit | Lambda implicitly captures $@ but is not considered a transient lambda because it is passed to a different translation unit, resulting in obfuscated lifetimes. | test.cpp:144:52:144:52 | l1 | l1 | +| test.cpp:148:7:148:35 | [...](...){...} | test.cpp:148:7:148:35 | [...](...){...} | test.cpp:136:31:136:63 | function_outside_translation_unit | Lambda implicitly captures $@ but is not considered a transient lambda because it is passed to a different translation unit, resulting in obfuscated lifetimes. | test.cpp:148:31:148:31 | l2 | l2 | +| test.cpp:171:9:171:28 | [...](...){...} | test.cpp:171:9:171:28 | [...](...){...} | test.cpp:153:43:153:73 | new | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:171:24:171:24 | l1 | l1 | +| test.cpp:172:9:172:28 | [...](...){...} | test.cpp:172:9:172:28 | [...](...){...} | test.cpp:153:43:153:73 | new | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:172:24:172:24 | l1 | l1 | +| test.cpp:175:9:175:37 | [...](...){...} | test.cpp:175:9:175:37 | [...](...){...} | test.cpp:153:43:153:73 | new | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:175:33:175:33 | l2 | l2 | +| test.cpp:178:10:178:29 | [...](...){...} | test.cpp:178:10:178:29 | [...](...){...} | test.cpp:153:43:153:73 | new | Lambda implicitly captures $@ but is not considered a transient lambda because it is copied or moved, resulting in obfuscated lifetimes. | test.cpp:178:25:178:25 | l1 | l1 | +| test.cpp:181:10:181:29 | [...](...){...} | test.cpp:181:10:181:29 | [...](...){...} | test.cpp:163:28:163:29 | C3 | Lambda implicitly captures $@ but is not considered a transient lambda because it is passed to a different translation unit, resulting in obfuscated lifetimes. | test.cpp:181:25:181:25 | l1 | l1 | diff --git a/cpp/misra/test/rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.qlref b/cpp/misra/test/rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.qlref new file mode 100644 index 0000000000..fd762d7d2c --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.qlref @@ -0,0 +1 @@ +rules/RULE-8-1-2/ImplicitCapturesDisallowedInNonTransientLambda.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-1-2/test.cpp b/cpp/misra/test/rules/RULE-8-1-2/test.cpp new file mode 100644 index 0000000000..f362877b43 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-1-2/test.cpp @@ -0,0 +1,182 @@ +#include + +template void function_transient(Func f) { + // transient, does not store. + f(); +} + +template void function_not_transient(Func f) { + // Non transient, stores the lambda. + auto l = f; // NON_COMPLIANT +} + +template void calls_function_transient(Func f) { + // Calls a function that takes a transient lambda. + function_transient(f); +} + +template void calls_function_not_transient(Func f) { + // Calls a function that takes a non transient lambda. + function_not_transient(f); // NON_COMPLIANT +} + +void m1() { + int l1, l2, l3; + + // Not transient (always compliant) + [=]() { return l1; }(); // COMPLIANT + [&]() { return l1; }(); // COMPLIANT + [l1]() { return l1; }(); // COMPLIANT + [&, l1]() { return l1; }(); // COMPLIANT + [&, l1]() { return l1 + l2; }(); // NON_COMPLIANT + + // Transient by immediate move (must not implicitly capture local variables) + auto la1 = []() { return 1; }; // COMPLIANT + auto la2 = [=]() { return 1; }; // COMPLIANT + auto la3 = [=]() { return l1; }; // NON_COMPLIANT + auto la4 = [&]() { return l1; }; // NON_COMPLIANT + auto la5 = [l1]() { return l1; }; // COMPLIANT + auto la6 = [&, l1]() { return l1; }; // COMPLIANT + auto la7 = [&, l1]() { return l1 + l2; }; // NON_COMPLIANT + + // Compliant, transient lambdas. + function_transient([]() { return 1; }); // COMPLIANT + function_transient([=]() { return 1; }); // COMPLIANT + function_transient([=]() { return l1; }); // COMPLIANT + function_transient([&]() { return l1; }); // COMPLIANT + function_transient([l1]() { return l1; }); // COMPLIANT + function_transient([&, l1]() { return l1; }); // COMPLIANT + function_transient([&, l1]() { return l1 + l2; }); // COMPLIANT + + // Non-compliant with implicit local variable capture, non-transient lambdas. + function_not_transient([]() { return 1; }); // COMPLIANT + function_not_transient([=]() { return 1; }); // COMPLIANT + function_not_transient([=]() { return l1; }); // NON_COMPLIANT + function_not_transient([&]() { return l1; }); // NON_COMPLIANT + function_not_transient([l1]() { return l1; }); // COMPLIANT + function_not_transient([&, l1]() { return l1; }); // COMPLIANT + function_not_transient([&, l1]() { return l1 + l2; }); // NON_COMPLIANT + + // Not valid cpp, as lambda expressions aren't rvalues: + // f(&([](int i) { return i; })); + // Not valid cpp, as each lambda expression has a unique implicit type: + // decltype([](int i) { return i; }) &f = [](int i) { return i; }; + + // No implicit capture of local variables, always compliant. + []() { return 1; }(); // COMPLIANT + [=]() { return 1; }(); // COMPLIANT + [&]() { return 1; }(); // COMPLIANT + [l1]() { return l1; }(); // COMPLIANT + function_not_transient([]() { return 1; }()); // COMPLIANT + function_not_transient([=]() { return 1; }()); // COMPLIANT + function_not_transient([&]() { return 1; }()); // COMPLIANT + function_not_transient([l1]() { return l1; }()); // COMPLIANT + + // Dead lambdas should be considered transient: + []() { return 1; }; // COMPLIANT + [=]() { return 1; }; // COMPLIANT + [=]() { return l1; }; // COMPLIANT + [&]() { return l1; }; // COMPLIANT + [l1]() { return l1; }; // COMPLIANT + [&, l1]() { return l1; }; // COMPLIANT + + // Casting to std::function is a copy, so it is non-transient: + static_cast>([]() { return 1; }); // COMPLIANT + static_cast>([=]() { return 1; }); // COMPLIANT + static_cast>([=]() { return l1; }); // NON_COMPLIANT + static_cast>([&]() { return l1; }); // NON_COMPLIANT + static_cast>([l1]() { return l1; }); // COMPLIANT + static_cast>([&, l1]() { return l1; }); // COMPLIANT + (std::function)([]() { return 1; }); // COMPLIANT + (std::function)([=]() { return 1; }); // COMPLIANT + (std::function)([=]() { return l1; }); // NON_COMPLIANT + (std::function)([&]() { return l1; }); // NON_COMPLIANT + (std::function)([l1]() { return l1; }); // COMPLIANT + (std::function)([&, l1]() { return l1; }); // COMPLIANT + static_cast([]() { return 1; }); // COMPLIANT + + // Not valid cpp: + // reinterpret_cast>([]() { return 1; }); + // dynamic_cast>([]() { return 1; }); + // static_cast([=]() { return 1; }); + // static_cast([&]() { return 1; }); +} + +static int g1; + +void f1() { + int l1, l2; + + // No local variables captured, always compliant + auto la1 = []() { return 1; }; // COMPLIANT + auto la2 = [=]() { return 1; }; // COMPLIANT + auto la3 = [&]() { return 1; }; // COMPLIANT + auto la4 = []() { return g1; }; // COMPLIANT - global variable + auto la5 = [=]() { return g1; }; // COMPLIANT - global variable + auto la6 = [&]() { return g1; }; // COMPLIANT - global variable + + // Implicit capture of local variables + auto la7 = [=]() { return l1; }; // NON_COMPLIANT + auto la8 = [&]() { return l1; }; // NON_COMPLIANT +} + +void f2() { + int x, y; + + auto lb1 = (1, [&]() { return 1; }); // COMPLIANT + auto lb2 = (1, [&]() { return x; }); // NON_COMPLIANT + auto lb3 = true ? [&]() { return 1; } // COMPLIANT + : throw "error"; + auto lb4 = true ? [&]() { return x; } // NON_COMPLIANT + : throw "error"; + auto lb5 = true ? throw "error" : [&]() { return 1; }; // COMPLIANT + auto lb6 = true ? throw "error" : [&]() { return x; }; // NON_COMPLIANT +} + +template void function_outside_translation_unit(Func f); + +void f3() { + int l1, l2; + + // Passing the lambda to a function outside the translation unit is considered + // non-transient, as the lambda may be stored and used later. + function_outside_translation_unit([=]() { return l1; }); // NON_COMPLIANT + function_outside_translation_unit([&]() { return l1; }); // NON_COMPLIANT + function_outside_translation_unit([l1]() { return l1; }); // COMPLIANT + function_outside_translation_unit([&, l1]() { return l1; }); // COMPLIANT + function_outside_translation_unit( + [&, l1]() { return l1 + l2; }); // NON_COMPLIANT +} + +class C1 { +public: + template C1(Func &&f) { new Func(std::forward(f)); } +}; + +class C2 : public C1 { +public: + template C2(Func &&f) : C1(std::forward(f)) {} +}; + +class C3 { +public: + template C3(Func &&f); +}; + +void f4() { + int l1, l2; + // Using a lambda in a constructor, which is transient. + C1 c1([]() { return 1; }); // COMPLIANT + C1 c2([=]() { return 1; }); // COMPLIANT + C1 c3([=]() { return l1; }); // NON_COMPLIANT + C1 c4([&]() { return l1; }); // NON_COMPLIANT + C1 c5([l1]() { return l1; }); // COMPLIANT + C1 c6([&, l1]() { return l1; }); // COMPLIANT + C1 c7([&, l1]() { return l1 + l2; }); // NON_COMPLIANT + C2 c8([]() { return 1; }); // COMPLIANT + C2 c9([=]() { return 1; }); // COMPLIANT + C2 c10([=]() { return l1; }); // NON_COMPLIANT + C3 c11([]() { return 1; }); // COMPLIANT + C3 c12([=]() { return 1; }); // COMPLIANT + C3 c13([=]() { return l1; }); // NON_COMPLIANT +} diff --git a/rule_packages/cpp/Expressions2.json b/rule_packages/cpp/Expressions2.json new file mode 100644 index 0000000000..6c345ca119 --- /dev/null +++ b/rule_packages/cpp/Expressions2.json @@ -0,0 +1,104 @@ +{ + "MISRA-C++-2023": { + "RULE-5-13-6": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Declaring a long long integer literal with a single L or l in the suffix is misleading and can cause confusion.", + "kind": "problem", + "name": "An integer-literal of type long long shall not use a single L or l in any suffix", + "precision": "very-high", + "severity": "error", + "short_name": "LongLongLiteralWithSingleLSuffix", + "tags": [ + "scope/single-translation-unit", + "readability" + ] + } + ], + "title": "An integer-literal of type long long shall not use a single L or l in any suffix" + }, + "RULE-8-0-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Usage of parentheses improve program clarity when using multiple operators of different precedences.", + "kind": "problem", + "name": "Parentheses should be used to make the meaning of an expression appropriately explicit", + "precision": "very-high", + "severity": "error", + "short_name": "MissingPrecedenceClarifyingParenthesis", + "tags": [ + "scope/single-translation-unit", + "readability", + "correctness" + ] + }, + { + "description": "Usage of parentheses improve program clarity when using the sizeof() operator.", + "kind": "problem", + "name": "Parentheses should be used to make the meaning of an expression appropriately explicit", + "precision": "very-high", + "severity": "error", + "short_name": "MissingSizeofOperatorParenthesis", + "tags": [ + "scope/single-translation-unit", + "readability" + ] + } + ], + "title": "Parentheses should be used to make the meaning of an expression appropriately explicit" + }, + "RULE-8-1-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Using implicit capture for a lambda declared in a class will capture the `this` pointer and not the instance members, which can be suprising and result in undefined behavior past the object's lifetime.", + "kind": "path-problem", + "name": "A non-transient lambda shall not implicitly capture this", + "precision": "very-high", + "severity": "error", + "short_name": "NonTransientLambdaImplicitlyCapturesThis", + "tags": [ + "scope/single-translation-unit", + "correctness", + "readability", + "maintainability" + ] + } + ], + "title": "A non-transient lambda shall not implicitly capture this" + }, + "RULE-8-1-2": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Explicit captures in lambdas which are \"stored\" clarifies dependencies that must be managed for memory management and safety.", + "kind": "path-problem", + "name": "Variables should be captured explicitly in a non-transient lambda", + "precision": "very-high", + "severity": "error", + "short_name": "ImplicitCapturesDisallowedInNonTransientLambda", + "tags": [ + "scope/single-translation-unit", + "readability", + "maintainability" + ] + } + ], + "title": "Variables should be captured explicitly in a non-transient lambda" + } + } +} \ No newline at end of file