diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preprocessor.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preprocessor.qll new file mode 100644 index 0000000000..569a20a226 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preprocessor.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype PreprocessorQuery = + TUndefOfMacroNotDefinedInFileQuery() or + TInvalidTokenInDefinedOperatorQuery() or + TDefinedOperatorExpandedInIfDirectiveQuery() or + TNoValidIfdefGuardInHeaderQuery() + +predicate isPreprocessorQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `undefOfMacroNotDefinedInFile` query + PreprocessorPackage::undefOfMacroNotDefinedInFileQuery() and + queryId = + // `@id` for the `undefOfMacroNotDefinedInFile` query + "cpp/misra/undef-of-macro-not-defined-in-file" and + ruleId = "RULE-19-0-4" and + category = "advisory" + or + query = + // `Query` instance for the `invalidTokenInDefinedOperator` query + PreprocessorPackage::invalidTokenInDefinedOperatorQuery() and + queryId = + // `@id` for the `invalidTokenInDefinedOperator` query + "cpp/misra/invalid-token-in-defined-operator" and + ruleId = "RULE-19-1-1" and + category = "required" + or + query = + // `Query` instance for the `definedOperatorExpandedInIfDirective` query + PreprocessorPackage::definedOperatorExpandedInIfDirectiveQuery() and + queryId = + // `@id` for the `definedOperatorExpandedInIfDirective` query + "cpp/misra/defined-operator-expanded-in-if-directive" and + ruleId = "RULE-19-1-1" and + category = "required" + or + query = + // `Query` instance for the `noValidIfdefGuardInHeader` query + PreprocessorPackage::noValidIfdefGuardInHeaderQuery() and + queryId = + // `@id` for the `noValidIfdefGuardInHeader` query + "cpp/misra/no-valid-ifdef-guard-in-header" and + ruleId = "RULE-19-2-1" and + category = "required" +} + +module PreprocessorPackage { + Query undefOfMacroNotDefinedInFileQuery() { + //autogenerate `Query` type + result = + // `Query` type for `undefOfMacroNotDefinedInFile` query + TQueryCPP(TPreprocessorPackageQuery(TUndefOfMacroNotDefinedInFileQuery())) + } + + Query invalidTokenInDefinedOperatorQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidTokenInDefinedOperator` query + TQueryCPP(TPreprocessorPackageQuery(TInvalidTokenInDefinedOperatorQuery())) + } + + Query definedOperatorExpandedInIfDirectiveQuery() { + //autogenerate `Query` type + result = + // `Query` type for `definedOperatorExpandedInIfDirective` query + TQueryCPP(TPreprocessorPackageQuery(TDefinedOperatorExpandedInIfDirectiveQuery())) + } + + Query noValidIfdefGuardInHeaderQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noValidIfdefGuardInHeader` query + TQueryCPP(TPreprocessorPackageQuery(TNoValidIfdefGuardInHeaderQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 4a6cbe936b..e68d714243 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -40,6 +40,7 @@ import Operators import OrderOfEvaluation import OutOfBounds import Pointers +import Preprocessor import Representation import Scope import SideEffects1 @@ -94,6 +95,7 @@ newtype TCPPQuery = TOrderOfEvaluationPackageQuery(OrderOfEvaluationQuery q) or TOutOfBoundsPackageQuery(OutOfBoundsQuery q) or TPointersPackageQuery(PointersQuery q) or + TPreprocessorPackageQuery(PreprocessorQuery q) or TRepresentationPackageQuery(RepresentationQuery q) or TScopePackageQuery(ScopeQuery q) or TSideEffects1PackageQuery(SideEffects1Query q) or @@ -148,6 +150,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isOrderOfEvaluationQueryMetadata(query, queryId, ruleId, category) or isOutOfBoundsQueryMetadata(query, queryId, ruleId, category) or isPointersQueryMetadata(query, queryId, ruleId, category) or + isPreprocessorQueryMetadata(query, queryId, ruleId, category) or isRepresentationQueryMetadata(query, queryId, ruleId, category) or isScopeQueryMetadata(query, queryId, ruleId, category) or isSideEffects1QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/common/src/codingstandards/cpp/util/CondensedList.qll b/cpp/common/src/codingstandards/cpp/util/CondensedList.qll new file mode 100644 index 0000000000..bda0177bff --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/util/CondensedList.qll @@ -0,0 +1,105 @@ +private import codeql.util.DenseRank + +/** + * Describes how to construct a condensed list from sparse but orderable data, and how that data + * should be connected, with one such list per specified division. + */ +signature module CondensedListSig { + /** + * The division specifies which items are connected into lists, with one list per division. + * + * For instance, if connecting variables defined in a file, the division will be the file. + */ + class Division; + + /** + * The class of the items to be condensed into lists. + * + * For instance, when connecting variables defined in a file, the items are the variables. + */ + class Item { + string toString(); + } + + /** + * The index specifies the order of the items in the condensed list, and may be sparse (have + * gaps). + * + * For instance, if connecting variables defined in a file, the index will be the line number of + * the variable in the file. + * + * The sparse index (which may have gaps) is used to determine the ordering of the items in the + * condensed list. Once the condensed list is created, the items in the list will automatically be + * assigned a dense index (which has no gaps). + * + * There must be no duplicate indices for the same division for correctness. + */ + int getSparseIndex(Division d, Item l); +} + +/** + * A module to take orderable data (which may not be continuous) and condense it into one or more + * dense lists, with one such list per specified division. + * + * To instantiate this module, you need to provide a `CondensedListSig` module that + * specifies the spare index and division of the items to be connected. + * + * For instance, to create a condensed list of variables defined in every file, you can + * create a `CondensedListSig` module that specifies the file as the division and + * the line number as the sparse index. + * + * ```ql + * module ConfigFileListConfig { + * class Division = File; + * class Item = Variable; + * int getSparseIndex(File file, Variable var) { + * file = var.getLocation().getFile() and + * var.getLocation().getStartLine() + * } + * } + * + * import Condense + * + * from Condense::ListEntry l + * select l, l.getItem(), l.getDenseIndex(), l.getNext(), l.getPrev(), + * ``` + */ +module Condense { + newtype TList = + THead(Config::Item l, Config::Division t) { denseRank(t, l) = 1 } or + TCons(ListEntry prev, Config::Item l) { + prev.getDenseIndex() = denseRank(prev.getDivision(), l) - 1 + } + + private module DenseRankConfig implements DenseRankInputSig2 { + class Ranked = Config::Item; + + class C = Config::Division; + + predicate getRank = Config::getSparseIndex/2; + } + + private import DenseRank2 + + class ListEntry extends TList { + Config::Division getDivision() { + this = THead(_, result) + or + exists(ListEntry prev | this = TCons(prev, _) and result = prev.getDivision()) + } + + string toString() { result = getItem().toString() + " [index " + getDenseIndex() + "]" } + + Config::Item getItem() { + this = THead(result, _) + or + this = TCons(_, result) + } + + int getDenseIndex() { result = denseRank(getDivision(), getItem()) } + + ListEntry getPrev() { this = TCons(result, _) } + + ListEntry getNext() { result.getPrev() = this } + } +} diff --git a/cpp/common/src/codingstandards/cpp/util/Pair.qll b/cpp/common/src/codingstandards/cpp/util/Pair.qll new file mode 100644 index 0000000000..a0c879ab93 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/util/Pair.qll @@ -0,0 +1,21 @@ +bindingset[this] +signature class ItemSig { + bindingset[this] + string toString(); +} + +module Pair { + signature predicate pred(A a, B b); + + module Where { + private newtype TAll = TSome(A a, B b) { ctor(a, b) } + + class Pair extends TAll { + A getFirst() { this = TSome(result, _) } + + B getSecond() { this = TSome(_, result) } + + string toString() { result = getFirst().toString() + ", " + getSecond().toString() } + } + } +} diff --git a/cpp/misra/src/rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.ql b/cpp/misra/src/rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.ql new file mode 100644 index 0000000000..a8121f2da5 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.ql @@ -0,0 +1,66 @@ +/** + * @id cpp/misra/undef-of-macro-not-defined-in-file + * @name RULE-19-0-4: #undef should only be used for macros defined previously in the same file + * @description Using #undef to undefine a macro that is not defined in the same file can lead to + * confusion. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-19-0-4 + * scope/single-translation-unit + * readability + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.util.CondensedList +import codingstandards.cpp.util.Pair + +class DefOrUndef extends PreprocessorDirective { + string name; + + DefOrUndef() { + name = this.(PreprocessorUndef).getName() or + name = this.(Macro).getName() + } + + string getName() { result = name } +} + +predicate relevantNameAndFile(string name, File file) { + exists(DefOrUndef m | + m.getName() = name and + m.getFile() = file + ) +} + +class StringFilePair = Pair::Where::Pair; + +module DefUndefListConfig implements CondensedListSig { + class Division = StringFilePair; + + class Item = DefOrUndef; + + int getSparseIndex(StringFilePair division, DefOrUndef directive) { + directive.getName() = division.getFirst() and + directive.getFile() = division.getSecond() and + result = directive.getLocation().getStartLine() + } +} + +class ListEntry = Condense::ListEntry; + +from PreprocessorUndef undef, ListEntry defUndefListEntry +where + not isExcluded(undef, PreprocessorPackage::undefOfMacroNotDefinedInFileQuery()) and + // There exists a def or undef for a given name and file, and it is an #undef + undef = defUndefListEntry.getItem() and + // Exclude cases where the previous def or undef with the same name in the same file is a #define + not exists(ListEntry prev | + prev = defUndefListEntry.getPrev() and + prev.getItem() instanceof Macro + ) +select undef, "Undef of name '" + undef.getName() + "' not defined in the same file." diff --git a/cpp/misra/src/rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.ql b/cpp/misra/src/rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.ql new file mode 100644 index 0000000000..cb79a5bbf6 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.ql @@ -0,0 +1,27 @@ +/** + * @id cpp/misra/defined-operator-expanded-in-if-directive + * @name RULE-19-1-1: The defined preprocessor operator shall be used appropriately + * @description Macro expansions that produce the token 'defined' inside of an if directive result + * in undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-1-1 + * scope/single-translation-unit + * correctness + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from PreprocessorIf ifDirective, MacroInvocation mi +where + not isExcluded(ifDirective, PreprocessorPackage::definedOperatorExpandedInIfDirectiveQuery()) and + ifDirective.getLocation().subsumes(mi.getLocation()) and + mi.getMacro().getBody().regexpMatch(".*defined.*") +select ifDirective, + "If directive contains macro expansion including the token 'defined' from macro $@, which results in undefined behavior.", + mi.getMacro(), mi.getMacroName() diff --git a/cpp/misra/src/rules/RULE-19-1-1/InvalidTokenInDefinedOperator.ql b/cpp/misra/src/rules/RULE-19-1-1/InvalidTokenInDefinedOperator.ql new file mode 100644 index 0000000000..c405dcf542 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-1-1/InvalidTokenInDefinedOperator.ql @@ -0,0 +1,42 @@ +/** + * @id cpp/misra/invalid-token-in-defined-operator + * @name RULE-19-1-1: The defined preprocessor operator shall be used appropriately + * @description Using the defined operator without an immediately following optionally parenthesized + * identifier results in undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-1-1 + * scope/single-translation-unit + * correctness + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +string idRegex() { result = "[a-zA-Z_]([a-zA-Z_0-9]*)" } + +bindingset[body] +predicate hasInvalidDefinedOperator(string body) { + body.regexpMatch(".*\\bdefined" + + // Contains text "defined" at a word break + // Negative zero width lookahead: + "(?!(" + + // (group) optional whitespace followed by a valid identifier + "(\\s*" + idRegex() + ")" + + // or + "|" + + // (group) optional whitespace followed by parenthesis and valid identifier + "(\\s*\\(\\s*" + idRegex() + "\\s*\\))" + + // End negative zero width lookahead, match remaining text + ")).*") +} + +from PreprocessorIf ifDirective +where + not isExcluded(ifDirective, PreprocessorPackage::invalidTokenInDefinedOperatorQuery()) and + hasInvalidDefinedOperator(ifDirective.getHead()) +select ifDirective, "Invalid use of defined operator in if directive." diff --git a/cpp/misra/src/rules/RULE-19-2-1/NoValidIfdefGuardInHeader.ql b/cpp/misra/src/rules/RULE-19-2-1/NoValidIfdefGuardInHeader.ql new file mode 100644 index 0000000000..902a356f7a --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-2-1/NoValidIfdefGuardInHeader.ql @@ -0,0 +1,44 @@ +/** + * @id cpp/misra/no-valid-ifdef-guard-in-header + * @name RULE-19-2-1: Precautions shall be taken in order to prevent the contents of a header file being included more + * @description Precautions shall be taken in order to prevent the contents of a header file being + * included more than once. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-2-1 + * scope/single-translation-unit + * maintainability + * correctness + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import semmle.code.cpp.headers.MultipleInclusion + +predicate isOutside(CorrectIncludeGuard includeGuard, Location location) { + location.getFile() = includeGuard.getFile() and + ( + location.isBefore(includeGuard.getIfndef().getLocation()) + or + includeGuard.getEndif().getLocation().isBefore(location) + ) +} + +from File included +where + not isExcluded(included, PreprocessorPackage::noValidIfdefGuardInHeaderQuery()) and + included = any(Compilation c).getAFileCompiled().getAnIncludedFile+() and + not exists(CorrectIncludeGuard includeGuard | + includeGuard.getFile() = included and + // Stricter: define is before all other contents + not included + .getATopLevelDeclaration() + .getLocation() + .isBefore(includeGuard.getDefine().getLocation()) and + // Stricter: do not allow includes outside of the inclusion guard + not exists(Include include | isOutside(includeGuard, include.getLocation())) + ) +select included, "File does not have a well formatted include guard." diff --git a/cpp/misra/test/rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.expected b/cpp/misra/test/rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.expected new file mode 100644 index 0000000000..af0c0993eb --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.expected @@ -0,0 +1,3 @@ +| test.cpp:3:1:3:9 | #undef M1 | Undef of name 'M1' not defined in the same file. | +| test.cpp:6:1:6:9 | #undef M1 | Undef of name 'M1' not defined in the same file. | +| test.cpp:7:1:7:9 | #undef M2 | Undef of name 'M2' not defined in the same file. | diff --git a/cpp/misra/test/rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.qlref b/cpp/misra/test/rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.qlref new file mode 100644 index 0000000000..094c73c7f2 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.qlref @@ -0,0 +1 @@ +rules/RULE-19-0-4/UndefOfMacroNotDefinedInFile.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-0-4/test.cpp b/cpp/misra/test/rules/RULE-19-0-4/test.cpp new file mode 100644 index 0000000000..665d5b970a --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-0-4/test.cpp @@ -0,0 +1,7 @@ +#define M1 +#undef M1 // COMPLIANT +#undef M1 // NON-COMPLIANT +#define M1 +#undef M1 // COMPLIANT +#undef M1 // NON-COMPLIANT +#undef M2 // NON-COMPLIANT \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.expected b/cpp/misra/test/rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.expected new file mode 100644 index 0000000000..dbbbc5502f --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.expected @@ -0,0 +1,3 @@ +| test.cpp:39:1:39:6 | #if M1 | If directive contains macro expansion including the token 'defined' from macro $@, which results in undefined behavior. | test.cpp:34:1:34:18 | #define M1 defined | M1 | +| test.cpp:41:1:41:6 | #if M2 | If directive contains macro expansion including the token 'defined' from macro $@, which results in undefined behavior. | test.cpp:35:1:35:30 | #define M2 1 + 2 + defined + 3 | M2 | +| test.cpp:43:1:43:6 | #if M3 | If directive contains macro expansion including the token 'defined' from macro $@, which results in undefined behavior. | test.cpp:35:1:35:30 | #define M2 1 + 2 + defined + 3 | M2 | diff --git a/cpp/misra/test/rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.qlref b/cpp/misra/test/rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.qlref new file mode 100644 index 0000000000..733221320c --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.qlref @@ -0,0 +1 @@ +rules/RULE-19-1-1/DefinedOperatorExpandedInIfDirective.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-1-1/InvalidTokenInDefinedOperator.expected b/cpp/misra/test/rules/RULE-19-1-1/InvalidTokenInDefinedOperator.expected new file mode 100644 index 0000000000..7ccfd6fe82 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-1-1/InvalidTokenInDefinedOperator.expected @@ -0,0 +1,3 @@ +| test.cpp:11:1:11:11 | #if defined | Invalid use of defined operator in if directive. | +| test.cpp:13:1:13:26 | #if defined(M1) && defined | Invalid use of defined operator in if directive. | +| test.cpp:15:1:15:26 | #if defined && defined(M1) | Invalid use of defined operator in if directive. | diff --git a/cpp/misra/test/rules/RULE-19-1-1/InvalidTokenInDefinedOperator.qlref b/cpp/misra/test/rules/RULE-19-1-1/InvalidTokenInDefinedOperator.qlref new file mode 100644 index 0000000000..4a0f690bfd --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-1-1/InvalidTokenInDefinedOperator.qlref @@ -0,0 +1 @@ +rules/RULE-19-1-1/InvalidTokenInDefinedOperator.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-1-1/test.cpp b/cpp/misra/test/rules/RULE-19-1-1/test.cpp new file mode 100644 index 0000000000..29a5843c12 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-1-1/test.cpp @@ -0,0 +1,48 @@ +#if defined M1 // COMPLIANT +#endif +#if defined(M1) // COMPLIANT +#endif +#if defined(M1) // COMPLIANT +#endif +#if defined M1 && defined M2 // COMPLIANT +#endif +#if defined(M1) && defined(M2) // COMPLIANT +#endif +#if defined // NON-COMPLIANT +#endif +#if defined(M1) && defined // NON-COMPLIANT +#endif +#if defined && defined(M1) // NON-COMPLIANT +#endif +// Compliant, there are no keywords in the context of the preprocessor, only +// identifiers. Therefore, 'new' is a valid identifier. +#if defined new // COMPLIANT +#endif +#if defined(new) // COMPLIANT +#endif + +// These cases don't compile in default tests, but may on other compilers +// #if defined 1 // NON-COMPLIANT +// #endif +// #if defined ( 1 ) // NON-COMPLIANT +// #endif +// #if defined + // NON-COMPLIANT +// #endif +// #if defined ( + ) // NON-COMPLIANT +// #endif + +#define M1 defined +#define M2 1 + 2 + defined + 3 +#define M3 M2 +#define M4 1 + 2 + 3 +#define M5 M4 +#if M1 // NON-COMPLIANT +#endif +#if M2 // NON-COMPLIANT +#endif +#if M3 // NON-COMPLIANT +#endif +#if M4 // COMPLIANT +#endif +#if M5 // COMPLIANT +#endif diff --git a/cpp/misra/test/rules/RULE-19-2-1/NoValidIfdefGuardInHeader.expected b/cpp/misra/test/rules/RULE-19-2-1/NoValidIfdefGuardInHeader.expected new file mode 100644 index 0000000000..73ce7d7a52 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/NoValidIfdefGuardInHeader.expected @@ -0,0 +1,9 @@ +| invalid1.h:0:0:0:0 | invalid1.h | File does not have a well formatted include guard. | +| invalid2.h:0:0:0:0 | invalid2.h | File does not have a well formatted include guard. | +| invalid3.h:0:0:0:0 | invalid3.h | File does not have a well formatted include guard. | +| invalid4.h:0:0:0:0 | invalid4.h | File does not have a well formatted include guard. | +| invalid5_file2.h:0:0:0:0 | invalid5_file2.h | File does not have a well formatted include guard. | +| invalid6_b.h:0:0:0:0 | invalid6_b.h | File does not have a well formatted include guard. | +| invalid7.h:0:0:0:0 | invalid7.h | File does not have a well formatted include guard. | +| invalid8.h:0:0:0:0 | invalid8.h | File does not have a well formatted include guard. | +| invalid9.h:0:0:0:0 | invalid9.h | File does not have a well formatted include guard. | diff --git a/cpp/misra/test/rules/RULE-19-2-1/NoValidIfdefGuardInHeader.qlref b/cpp/misra/test/rules/RULE-19-2-1/NoValidIfdefGuardInHeader.qlref new file mode 100644 index 0000000000..2857bfe31f --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/NoValidIfdefGuardInHeader.qlref @@ -0,0 +1 @@ +rules/RULE-19-2-1/NoValidIfdefGuardInHeader.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid1.h b/cpp/misra/test/rules/RULE-19-2-1/invalid1.h new file mode 100644 index 0000000000..ed9b55508c --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid1.h @@ -0,0 +1,6 @@ +#ifndef MISSPELLED +#define MISPELED + +void invalid1_f1(); + +#endif \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid2.h b/cpp/misra/test/rules/RULE-19-2-1/invalid2.h new file mode 100644 index 0000000000..c2ebad359f --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid2.h @@ -0,0 +1,7 @@ +#ifdef INVALID2_H +#define INVALID2_H + +void invalid2_f1(); +// invalid: uses ifdef, not ifndef + +#endif \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid3.h b/cpp/misra/test/rules/RULE-19-2-1/invalid3.h new file mode 100644 index 0000000000..cfac97ead4 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid3.h @@ -0,0 +1,6 @@ +#ifndef INVALID3_H +#define INVALID3_H + +#endif + +void invalid3_f1(); // NON-COMPLIANT -- outside of guard condition \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid4.h b/cpp/misra/test/rules/RULE-19-2-1/invalid4.h new file mode 100644 index 0000000000..002e4308bd --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid4.h @@ -0,0 +1,14 @@ +#ifndef INVALID4_H1 +#define INVALID4_H1 + +void invalid4_f1(); + +#endif + +#ifndef INVALID4_H2 +#define INVALID4_H2 + +// NON-COMPLIANT -- There should not be two inclusion guards in one file. +void invalid4_f1(); + +#endif \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid5.h b/cpp/misra/test/rules/RULE-19-2-1/invalid5.h new file mode 100644 index 0000000000..79d7aaf838 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid5.h @@ -0,0 +1,6 @@ +#ifndef INVALID5_H +#define INVALID5_H + +#include "invalid5_file2.h" + +#endif \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid5_file2.h b/cpp/misra/test/rules/RULE-19-2-1/invalid5_file2.h new file mode 100644 index 0000000000..ef548fb872 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid5_file2.h @@ -0,0 +1,2 @@ +void invalid5_f1(); // NON-COMPLIANT -- guarded from invalid5.h but not in + // current file \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid6_a.h b/cpp/misra/test/rules/RULE-19-2-1/invalid6_a.h new file mode 100644 index 0000000000..97aba7e3e6 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid6_a.h @@ -0,0 +1,7 @@ +// NON-COMPLIANT -- same inclusion guard identifier as invalid6_b.h +#ifndef INVALID6_H +#define INVALID6_H + +void invalid6_f1(); + +#endif \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid6_b.h b/cpp/misra/test/rules/RULE-19-2-1/invalid6_b.h new file mode 100644 index 0000000000..43bf772174 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid6_b.h @@ -0,0 +1,7 @@ +// NON-COMPLIANT -- same inclusion guard identifier as invalid6_a.h +#ifndef INVALID6_H +#define INVALID6_H + +void invalid6_f2(); + +#endif \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid7.h b/cpp/misra/test/rules/RULE-19-2-1/invalid7.h new file mode 100644 index 0000000000..e45f13886e --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid7.h @@ -0,0 +1,2 @@ +// Empty file +// NON-COMPLIANT -- no inclusion guard \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid8.h b/cpp/misra/test/rules/RULE-19-2-1/invalid8.h new file mode 100644 index 0000000000..2dc25f09cd --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid8.h @@ -0,0 +1,8 @@ +#include "valid1.h" // NON-COMPLIANT -- contents not in inclusion guard + +#ifndef INVALID8_H +#define INVALID8_H + +void invalid8_f1(); + +#endif diff --git a/cpp/misra/test/rules/RULE-19-2-1/invalid9.h b/cpp/misra/test/rules/RULE-19-2-1/invalid9.h new file mode 100644 index 0000000000..50d89c22b5 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/invalid9.h @@ -0,0 +1,7 @@ +#ifndef INVALID9_H + +void invalid9_f1(); + +// NON-COMPLIANT -- define is not at top of inclusion guard +#define INVALID9_H +#endif diff --git a/cpp/misra/test/rules/RULE-19-2-1/test.cpp b/cpp/misra/test/rules/RULE-19-2-1/test.cpp new file mode 100644 index 0000000000..d80bc89498 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/test.cpp @@ -0,0 +1,14 @@ +#include "invalid1.h" +#include "invalid2.h" +#include "invalid3.h" +#include "invalid4.h" +#include "invalid5.h" +#include "invalid6_a.h" +#include "invalid6_b.h" +#include "invalid7.h" +#include "invalid8.h" +#include "invalid9.h" +#include "valid1.h" +#include "valid2.h" + +void f() {} // COMPLIANT -- not in header, no inclusion guard necessary \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/valid1.h b/cpp/misra/test/rules/RULE-19-2-1/valid1.h new file mode 100644 index 0000000000..e924cac32f --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/valid1.h @@ -0,0 +1,8 @@ +// COMPLIANT + +#ifndef VALID1_H +#define VALID1_H + +void valid1_f1(); + +#endif \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-1/valid2.h b/cpp/misra/test/rules/RULE-19-2-1/valid2.h new file mode 100644 index 0000000000..653ec0c249 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-1/valid2.h @@ -0,0 +1,8 @@ +// COMPLIANT + +#if !defined(VALID2_H) +#define VALID2_H + +void valid2_f1(); + +#endif \ No newline at end of file diff --git a/rule_packages/cpp/Preprocessor.json b/rule_packages/cpp/Preprocessor.json new file mode 100644 index 0000000000..b379af2ff7 --- /dev/null +++ b/rule_packages/cpp/Preprocessor.json @@ -0,0 +1,83 @@ +{ + "MISRA-C++-2023": { + "RULE-19-0-4": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Using #undef to undefine a macro that is not defined in the same file can lead to confusion.", + "kind": "problem", + "name": "#undef should only be used for macros defined previously in the same file", + "precision": "very-high", + "severity": "warning", + "short_name": "UndefOfMacroNotDefinedInFile", + "tags": [ + "scope/single-translation-unit", + "readability", + "maintainability" + ] + } + ], + "title": "#undef should only be used for macros defined previously in the same file" + }, + "RULE-19-1-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Using the defined operator without an immediately following optionally parenthesized identifier results in undefined behavior.", + "kind": "problem", + "name": "The defined preprocessor operator shall be used appropriately", + "precision": "very-high", + "severity": "error", + "short_name": "InvalidTokenInDefinedOperator", + "tags": [ + "scope/single-translation-unit", + "correctness", + "maintainability" + ] + }, + { + "description": "Macro expansions that produce the token 'defined' inside of an if directive result in undefined behavior.", + "kind": "problem", + "name": "The defined preprocessor operator shall be used appropriately", + "precision": "very-high", + "severity": "error", + "short_name": "DefinedOperatorExpandedInIfDirective", + "tags": [ + "scope/single-translation-unit", + "correctness", + "maintainability" + ] + } + ], + "title": "The defined preprocessor operator shall be used appropriately" + }, + "RULE-19-2-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Precautions shall be taken in order to prevent the contents of a header file being included more than once.", + "kind": "problem", + "name": "Precautions shall be taken in order to prevent the contents of a header file being included more", + "precision": "very-high", + "severity": "error", + "short_name": "NoValidIfdefGuardInHeader", + "tags": [ + "scope/single-translation-unit", + "maintainability", + "correctness" + ] + } + ], + "title": "Precautions shall be taken in order to prevent the contents of a header file being included more than once" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 3f7961b630..e87b99b810 100644 --- a/rules.csv +++ b/rules.csv @@ -963,14 +963,14 @@ cpp,MISRA-C++-2023,RULE-19-1-1,Yes,Required,Decidable,Single Translation Unit,Th cpp,MISRA-C++-2023,RULE-19-1-2,No,Required,Decidable,Single Translation Unit,"All #else, #elif and #endif preprocessor directives shall reside in the same file as the #if, #ifdef or #ifndef directive to which they are related",M16-1-2,,, cpp,MISRA-C++-2023,RULE-19-1-3,Yes,Required,Decidable,Single Translation Unit,All identifiers used in the controlling expression of #if or #elif preprocessing directives shall be defined prior to evaluation,M16-0-7,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-19-2-1,Yes,Required,Decidable,Single Translation Unit,Precautions shall be taken in order to prevent the contents of a header file being included more than once,M16-2-3,Preprocessor,Easy, -cpp,MISRA-C++-2023,RULE-19-2-2,Yes,Required,Decidable,Single Translation Unit,"The #include directive shall be followed by either a or ""filename"" sequence",,Preprocessor,Easy, +cpp,MISRA-C++-2023,RULE-19-2-2,Yes,Required,Decidable,Single Translation Unit,"The #include directive shall be followed by either a or ""filename"" sequence",,Preprocessor2,Easy, cpp,MISRA-C++-2023,RULE-19-2-3,Yes,Required,Decidable,Single Translation Unit,"The ' or "" or \ characters and the /* or // character sequences shall not occur in a header file name",A16-2-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-19-3-1,Yes,Advisory,Decidable,Single Translation Unit,The # and ## preprocessor operators should not be used,M16-3-2,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-19-3-2,Yes,Required,Decidable,Single Translation Unit,A macro parameter immediately following a # operator shall not be immediately followed by a ## operator,RULE-20-11,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-19-3-3,Yes,Required,Decidable,Single Translation Unit,The argument to a mixed-use macro parameter shall not be subject to further expansion,RULE-20-12,ImportMisra23,Import, -cpp,MISRA-C++-2023,RULE-19-3-4,Yes,Required,Decidable,Single Translation Unit,Parentheses shall be used to ensure macro arguments are expanded appropriately,M16-0-6,Preprocessor,Medium, +cpp,MISRA-C++-2023,RULE-19-3-4,Yes,Required,Decidable,Single Translation Unit,Parentheses shall be used to ensure macro arguments are expanded appropriately,M16-0-6,Preprocessor2,Medium, cpp,MISRA-C++-2023,RULE-19-3-5,Yes,Required,Decidable,Single Translation Unit,Tokens that look like a preprocessing directive shall not occur within a macro argument,RULE-20-6,ImportMisra23,Import, -cpp,MISRA-C++-2023,RULE-19-6-1,Yes,Advisory,Decidable,Single Translation Unit,The #pragma directive and the _Pragma operator should not be used,A16-7-1,Preprocessor,Easy, +cpp,MISRA-C++-2023,RULE-19-6-1,Yes,Advisory,Decidable,Single Translation Unit,The #pragma directive and the _Pragma operator should not be used,A16-7-1,Preprocessor2,Easy, cpp,MISRA-C++-2023,RULE-21-2-1,Yes,Required,Decidable,Single Translation Unit,"The library functions atof, atoi, atol and atoll from shall not be used",RULE-21-7,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-21-2-2,Yes,Required,Decidable,Single Translation Unit,"The string handling functions from , , and shall not be used",M18-0-5,BannedAPIs,Easy, cpp,MISRA-C++-2023,RULE-21-2-3,Yes,Required,Decidable,Single Translation Unit,The library function system from shall not be used,M18-0-3,BannedAPIs,Easy,