@specs-feup/clava-misra 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CxxSources/lib.cpp +3 -0
  2. package/CxxSources/lib.h +8 -0
  3. package/CxxSources/main.cpp +40 -0
  4. package/README.md +32 -0
  5. package/TODO.md +1 -0
  6. package/consumer_order.txt +2 -0
  7. package/enum_integer_type.txt +0 -0
  8. package/is_temporary.txt +0 -0
  9. package/jest.config.js +25 -0
  10. package/omp.txt +0 -0
  11. package/package.json +23 -0
  12. package/src/foo.ts +6 -0
  13. package/src/main.ts +36 -0
  14. package/src/misra/MISRAAnalyser.ts +43 -0
  15. package/src/misra/MISRAPass.ts +85 -0
  16. package/src/misra/MISRAPassResult.ts +20 -0
  17. package/src/misra/MISRAReporter.ts +57 -0
  18. package/src/misra/passes/S10_EssentialTypePass.ts +395 -0
  19. package/src/misra/passes/S12_ExpressionPass.ts +86 -0
  20. package/src/misra/passes/S13_SideEffectPass.ts +121 -0
  21. package/src/misra/passes/S15_ControlFlowPass.ts +108 -0
  22. package/src/misra/passes/S16_SwitchStatementPass.ts +168 -0
  23. package/src/misra/passes/S17_FunctionPass.ts +43 -0
  24. package/src/misra/passes/S18_PointersArraysPass.ts +126 -0
  25. package/src/misra/passes/S19_OverlappingStoragePass.ts +27 -0
  26. package/src/misra/passes/S21_StandardLibPass.ts +92 -0
  27. package/src/misra/passes/S3_CommentPass.ts +40 -0
  28. package/src/misra/passes/S5_IdentifierPass.ts +66 -0
  29. package/src/misra/passes/S6_TypePass.ts +32 -0
  30. package/src/misra/passes/S7_LiteralsConstantsPass.ts +80 -0
  31. package/src/misra/passes/S8_DeclDefPass.ts +138 -0
  32. package/src/misra/sections/Section10_EssentialTypeModel.ts +377 -0
  33. package/src/misra/sections/Section11_PointerTypeConversions.ts +103 -0
  34. package/src/misra/sections/Section12_Expressions.ts +80 -0
  35. package/src/misra/sections/Section13_SideEffects.ts +100 -0
  36. package/src/misra/sections/Section14_ControlStmtExprs.ts +27 -0
  37. package/src/misra/sections/Section15_ControlFlow.ts +114 -0
  38. package/src/misra/sections/Section16_SwitchStatements.ts +154 -0
  39. package/src/misra/sections/Section17_Functions.ts +33 -0
  40. package/src/misra/sections/Section18_PointersAndArrays.ts +108 -0
  41. package/src/misra/sections/Section19_OverlappingStorage.ts +18 -0
  42. package/src/misra/sections/Section20_PreprocessingDirectives.ts +22 -0
  43. package/src/misra/sections/Section21_StandardLibraries.ts +65 -0
  44. package/src/misra/sections/Section2_UnusedCode.ts +47 -0
  45. package/src/misra/sections/Section3_Comments.ts +23 -0
  46. package/src/misra/sections/Section5_Identifiers.ts +149 -0
  47. package/src/misra/sections/Section6_Types.ts +26 -0
  48. package/src/misra/sections/Section7_LiteralsConstants.ts +76 -0
  49. package/src/misra/sections/Section8_DeclarationsDefinitions.ts +133 -0
  50. package/src/misra/tests/S10_EssentialTypes.test.ts +253 -0
  51. package/src/misra/tests/S12_Expressions.test.ts +43 -0
  52. package/src/misra/tests/S13_SideEffects.test.ts +77 -0
  53. package/src/misra/tests/S15_ControlFlow.test.ts +144 -0
  54. package/src/misra/tests/S16_SwitchStatements.test.ts +164 -0
  55. package/src/misra/tests/S17_Functions.test.ts +46 -0
  56. package/src/misra/tests/S18_PointersArrays.test.ts +167 -0
  57. package/src/misra/tests/S19_OverlappingStorage.test.ts +38 -0
  58. package/src/misra/tests/S3_Comments.test.ts +36 -0
  59. package/src/misra/tests/S6_Types.test.ts +36 -0
  60. package/src/misra/tests/S7_LiteralsConstants.test.ts +48 -0
  61. package/src/misra/tests/utils.ts +47 -0
  62. package/tsconfig.jest.json +5 -0
  63. package/tsconfig.json +18 -0
  64. package/typedoc.config.js +6 -0
  65. package/types_with_templates.txt +0 -0
@@ -0,0 +1,108 @@
1
+ import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
2
+ import MISRAPass from "../MISRAPass.js";
3
+ import { PreprocessingReqs } from "../MISRAReporter.js";
4
+ import { Break, GotoStmt, Joinpoint, LabelStmt, Loop } from "@specs-feup/clava/api/Joinpoints.js";
5
+ import Query from "@specs-feup/lara/api/weaver/Query.js";
6
+
7
+ export default class S15_ControlFlowPass extends MISRAPass {
8
+ protected _preprocessingReqs: PreprocessingReqs[] = [];
9
+ private _labelMap: Map<string, Joinpoint> | undefined;
10
+
11
+ initRuleMapper(): void {
12
+ this._ruleMapper = new Map([
13
+ [1, this.r15_1_noGoto.bind(this)],
14
+ [2, this.r15_2_noBackJumps.bind(this)],
15
+ [3, this.r15_3_gotoBlockEnclosed.bind(this)],
16
+ [4, this.r15_4_loopSingleBreak.bind(this)]
17
+ ]);
18
+ }
19
+
20
+ matchJoinpoint($jp: LaraJoinPoint): boolean {
21
+ return $jp instanceof GotoStmt || $jp instanceof Loop;
22
+ }
23
+
24
+ private r15_1_noGoto($startNode: Joinpoint) {
25
+ if (!($startNode instanceof GotoStmt)) return;
26
+ this.logMISRAError("Goto statements should not be used");
27
+ }
28
+
29
+ private static isBeforeInCode(line1: number, col1: number, line2: number, col2: number): boolean {
30
+ if (line1 < line2) return true;
31
+ else return col1 < col2;
32
+ }
33
+
34
+ private r15_2_noBackJumps($startNode: Joinpoint) {
35
+ if (!($startNode instanceof GotoStmt)) return;
36
+ if (!S15_ControlFlowPass.isBeforeInCode($startNode.line, $startNode.column, $startNode.label.line, $startNode.label.column)) {
37
+ this.logMISRAError("Goto statements must not jump backwards in the code.");
38
+ }
39
+ }
40
+
41
+ private computeLabelMap() {
42
+ this._labelMap = new Map();
43
+ Query.search(LabelStmt).get().forEach(stmt => this._labelMap?.set(stmt.decl.astId, stmt), this);
44
+ }
45
+
46
+ private r15_3_gotoBlockEnclosed($startNode: Joinpoint) {
47
+ if (!($startNode instanceof GotoStmt)) return;
48
+ if (!this._labelMap) this.computeLabelMap();
49
+
50
+
51
+ let curr = $startNode.getAncestor("scope");
52
+ const ancestor = $startNode.getAncestor("function");
53
+ let error = true;
54
+ let temp;
55
+
56
+ do {
57
+ temp = curr;
58
+ if (curr.children.map(n => n.astId).includes(this._labelMap?.get($startNode.label.astId)?.astId as string)) {
59
+ error = false;
60
+ break;
61
+ }
62
+ curr = curr.getAncestor("scope");
63
+ } while (temp.parent.astId !== ancestor.astId);
64
+
65
+ if (error) {
66
+ this.logMISRAError("The label of a goto statement must be declared in a block or switch clause enclosing the goto.");
67
+ }
68
+ }
69
+
70
+ private static countBreakExits($startNode: Joinpoint): number {
71
+ let count = 0;
72
+ for (const goto of Query.searchFrom($startNode, Break)) {
73
+ const ancestor = goto.getAncestor("loop");
74
+ const switchAncestor = goto.getAncestor("switch");
75
+ if (ancestor.astId === $startNode.astId && !(switchAncestor && $startNode.contains(switchAncestor))) {
76
+ count++;
77
+ }
78
+ }
79
+
80
+ return count;
81
+ }
82
+
83
+ private static countGotoExits($startNode: Joinpoint, labels: Map<string, Joinpoint>) {
84
+ let count = 0;
85
+ for (const goto of Query.searchFrom($startNode, GotoStmt)) {
86
+ if (!($startNode.contains(labels.get(goto.label.astId) as Joinpoint))) {
87
+ count++;
88
+ }
89
+ }
90
+
91
+ return count;
92
+ }
93
+
94
+ private r15_4_loopSingleBreak($startNode: Joinpoint) {
95
+ if (!($startNode instanceof Loop)) return;
96
+ if (!this._labelMap) this.computeLabelMap();
97
+
98
+ const breakExits = S15_ControlFlowPass.countBreakExits($startNode);
99
+ const gotoExits = S15_ControlFlowPass.countGotoExits($startNode, this._labelMap as Map<string, Joinpoint>);
100
+
101
+ if (breakExits + gotoExits > 1) {
102
+ this.logMISRAError("Loops must only have one exit");
103
+ }
104
+ }
105
+
106
+ protected _name: string = "Control Flow";
107
+
108
+ }
@@ -0,0 +1,168 @@
1
+ import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
2
+ import MISRAPass from "../MISRAPass.js";
3
+ import { PreprocessingReqs } from "../MISRAReporter.js";
4
+ import { BinaryOp, Break, BuiltinType, Case, Expression, Joinpoint, Switch } from "@specs-feup/clava/api/Joinpoints.js";
5
+ import Fix from "@specs-feup/clava/api/clava/analysis/Fix.js";
6
+ import ClavaJoinPoints from "@specs-feup/clava/api/clava/ClavaJoinPoints.js";
7
+
8
+ export default class S16_SwitchStatementPass extends MISRAPass {
9
+ protected _preprocessingReqs: PreprocessingReqs[] = [];
10
+ private _wellFormedSwitch: boolean = false;
11
+
12
+ initRuleMapper(): void {
13
+ this._ruleMapper = new Map([
14
+ [1, this.r16_1_16_3_wellFormedSwitch.bind(this)],
15
+ [2, this.r16_2_topLevelSwitchLabels.bind(this)],
16
+ [4, this.r16_4_switchHasDefault.bind(this)],
17
+ [5, this.r16_5_defaultFirstOrLast.bind(this)],
18
+ [6, this.r16_6_noTwoClauses.bind(this)],
19
+ [7, this.r16_7_noEssentialBooleanInSwitch.bind(this)]
20
+ ]);
21
+ }
22
+
23
+ matchJoinpoint($jp: LaraJoinPoint): boolean {
24
+ return $jp instanceof Switch || $jp instanceof Case;
25
+ }
26
+
27
+ private r16_1_16_3_wellFormedSwitch($startNode: Joinpoint) {
28
+ if (!($startNode instanceof Switch)) return;
29
+ this._wellFormedSwitch = true;
30
+
31
+ let foundStmt = false;
32
+ let first = true;
33
+ for (const child of $startNode.children[1].children) {
34
+ if (child instanceof Break) {;
35
+ foundStmt = false;
36
+ }
37
+ else if (child instanceof Case) {
38
+ first = false;
39
+ }
40
+ else {
41
+ foundStmt = true;
42
+ }
43
+
44
+ if (foundStmt && child instanceof Case) {
45
+ this.logMISRAError(`A break is missing before ${child.code}`);
46
+ this._wellFormedSwitch = false;
47
+ }
48
+ }
49
+ if (!($startNode.children[1].lastChild instanceof Break)) {
50
+ this.logMISRAError("A break is missing at the end of the switch statement.");
51
+ this._wellFormedSwitch = false;
52
+ }
53
+ }
54
+
55
+ private r16_2_topLevelSwitchLabels($startNode: Joinpoint) {
56
+ if (!($startNode instanceof Case)) return;
57
+
58
+ if (!($startNode.currentRegion instanceof Switch)) {
59
+ this.logMISRAError("A switch label can only be used if its enclosing compound statement is the switch statement itself.");
60
+ }
61
+ }
62
+
63
+ private r16_4_switchHasDefault($startNode: Joinpoint) {
64
+ if (!($startNode instanceof Switch && !$startNode.hasDefaultCase)) return;
65
+
66
+ this.logMISRAError("Switch statement is missing a default case.");
67
+ }
68
+
69
+
70
+ private r16_5_defaultFirstOrLast($startNode: Joinpoint) {
71
+ if (!($startNode instanceof Switch)) return;
72
+
73
+ for (let i = 0; i < $startNode.cases.length; i++) {
74
+ if ($startNode.cases[i].isDefault && (i == 0 || i == $startNode.cases.length-1)) {
75
+ return;
76
+ }
77
+ else if ($startNode.cases[i].isDefault) {
78
+ this.logMISRAError("The default case of a switch statement must be the first or last label.");
79
+ return;
80
+ }
81
+ }
82
+ }
83
+
84
+ private r16_6_noTwoClauses($startNode: Joinpoint) {
85
+ if (!($startNode instanceof Switch)) return;
86
+ this.dependsOn(1, $startNode);
87
+ if (this._wellFormedSwitch === false) return;
88
+
89
+ let clauses = 0;
90
+ for (const child of $startNode.children[1].children) {
91
+ if (child instanceof Break) {
92
+ clauses++;
93
+ }
94
+ }
95
+
96
+ if (clauses <= 2) {
97
+ this.logMISRAError("Switch statements should have more than two clauses.", new Fix(
98
+ $startNode,
99
+ (switchStmt: Joinpoint) => {
100
+ const switchJp = switchStmt as Switch;
101
+ let firstClauseExpr: Expression | undefined = undefined;
102
+ let secondClauseExpr: Expression | undefined = undefined;
103
+ let firstClause: Joinpoint[] = [];
104
+ let secondClause: Joinpoint[] = [];
105
+ let currClauseExpr: Expression | undefined = undefined;
106
+ let currClause: Joinpoint[] = [];
107
+ let clauseHasDefault: boolean = false;
108
+ let filledFirstClause: boolean = false;
109
+ const newVar = ClavaJoinPoints.varDecl("switchToIf_" + switchJp.astId, switchJp.condition);
110
+ switchJp.insertBefore(newVar.stmt);
111
+ for (const child of switchStmt.children[1].children) {
112
+ if (child instanceof Case) {
113
+ let tempOp: BinaryOp;
114
+ if (child.isDefault) {
115
+ clauseHasDefault = true;
116
+ continue;
117
+ }
118
+ else if (child.values.length === 1) {
119
+ tempOp = ClavaJoinPoints.binaryOp("eq", newVar.varref(), child.values[0]);
120
+ }
121
+ else {
122
+ tempOp = ClavaJoinPoints.binaryOp("l_or", ClavaJoinPoints.binaryOp("ge", newVar.varref(), child.values[0]), ClavaJoinPoints.binaryOp("le", newVar.varref(), child.values[1]));
123
+ }
124
+ currClauseExpr = currClauseExpr ? ClavaJoinPoints.binaryOp("l_or", currClauseExpr, tempOp) : tempOp;
125
+ }
126
+ else if (child instanceof Break) {
127
+ if (clauseHasDefault) {
128
+ secondClause = currClause;
129
+ secondClauseExpr = undefined;
130
+ }
131
+ else if (filledFirstClause) {
132
+ secondClause = currClause;
133
+ secondClauseExpr = currClauseExpr;
134
+ }
135
+ else {
136
+ firstClause = currClause;
137
+ firstClauseExpr = currClauseExpr;
138
+ filledFirstClause = true;
139
+ }
140
+ clauseHasDefault = false;
141
+ currClause = [];
142
+ currClauseExpr = undefined;
143
+ }
144
+ else {
145
+ currClause.push(child);
146
+ }
147
+ }
148
+
149
+ const elseStmt = secondClauseExpr ? ClavaJoinPoints.ifStmt(secondClauseExpr, ClavaJoinPoints.scope(...secondClause)) : ClavaJoinPoints.scope(...secondClause);
150
+ switchJp.replaceWith(ClavaJoinPoints.ifStmt(firstClauseExpr ?? "", ClavaJoinPoints.scope(...firstClause), elseStmt));
151
+ }
152
+ ));
153
+ }
154
+ }
155
+
156
+ private r16_7_noEssentialBooleanInSwitch($startNode: Joinpoint) { //UNFINISHED, can have transformation
157
+ if (!($startNode instanceof Switch)) return;
158
+ this.dependsOn(1, $startNode);
159
+ if (this._wellFormedSwitch === false) return;
160
+
161
+ if ($startNode.condition.type.desugarAll instanceof BuiltinType && $startNode.condition.type.desugarAll.builtinKind === "Bool") {
162
+ this.logMISRAError("The controlling expression of a switch statement must not have essentially boolean type.");
163
+ }
164
+ }
165
+
166
+ protected _name: string = "Switch statements";
167
+
168
+ }
@@ -0,0 +1,43 @@
1
+ import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
2
+ import MISRAPass from "../MISRAPass.js";
3
+ import { PreprocessingReqs } from "../MISRAReporter.js";
4
+ import { BuiltinType, Call, ExprStmt, Include, Joinpoint } from "@specs-feup/clava/api/Joinpoints.js";
5
+ import Fix from "@specs-feup/clava/api/clava/analysis/Fix.js";
6
+ import ClavaJoinPoints from "@specs-feup/clava/api/clava/ClavaJoinPoints.js";
7
+
8
+ export default class S17_FunctionPass extends MISRAPass {
9
+ protected _preprocessingReqs: PreprocessingReqs[] = [];
10
+
11
+ initRuleMapper(): void {
12
+ this._ruleMapper = new Map([
13
+ [1, this.r17_1_noStdargUsage.bind(this)],
14
+ [7, this.r17_7_returnValuesAreUsed.bind(this)]
15
+ ]);
16
+ }
17
+ matchJoinpoint($jp: LaraJoinPoint): boolean {
18
+ return $jp instanceof Include || $jp instanceof Call;
19
+ }
20
+
21
+ private r17_1_noStdargUsage($startNode: Joinpoint) {
22
+ if (!($startNode instanceof Include)) return;;
23
+
24
+ if ($startNode.name === "stdarg.h" && $startNode.isAngled) {
25
+ this.logMISRAError("Use of <stdarg.h> is not allowed.");
26
+ }
27
+ }
28
+
29
+ private r17_7_returnValuesAreUsed($startNode: Joinpoint) {
30
+ if (!($startNode instanceof Call)) return;
31
+ if ($startNode.returnType instanceof BuiltinType && $startNode.returnType.isVoid) return;
32
+
33
+ if ($startNode.parent instanceof ExprStmt) {
34
+ this.logMISRAError(`Return value of ${$startNode.signature} must be used. It can be discarded with an explicit cast to void.`, new Fix($startNode, ($jp) => {
35
+ const newJp = ClavaJoinPoints.cStyleCast(ClavaJoinPoints.type("void"), $jp as Call);
36
+ $jp.replaceWith(newJp);
37
+ }));
38
+ }
39
+ }
40
+
41
+ protected _name: string = "Functions";
42
+
43
+ }
@@ -0,0 +1,126 @@
1
+ import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
2
+ import MISRAPass from "../MISRAPass.js";
3
+ import { PreprocessingReqs } from "../MISRAReporter.js";
4
+ import { BinaryOp, Field, FunctionJp, FunctionType, Joinpoint, Param, PointerType, QualType, Type, Vardecl } from "@specs-feup/clava/api/Joinpoints.js";
5
+
6
+ export default class S18_PointersArraysPass extends MISRAPass {
7
+ protected _preprocessingReqs: PreprocessingReqs[] = [];
8
+
9
+ initRuleMapper(): void {
10
+ this._ruleMapper = new Map([
11
+ [4, this.r18_4_noPointerArithmetic.bind(this)],
12
+ [5, this.r18_5_noExcessivePointerNesting.bind(this)],
13
+ [7, this.r18_7_noFlexibleArrayMembers.bind(this)],
14
+ [8, this.r18_8_noVariableLengthArrays.bind(this)]
15
+ ]);
16
+ }
17
+ matchJoinpoint($jp: LaraJoinPoint): boolean {
18
+ return $jp instanceof BinaryOp || $jp instanceof Vardecl || $jp instanceof FunctionJp || $jp instanceof Field;
19
+ }
20
+
21
+ private static isPointerType($type: Type): boolean {
22
+ const newT = $type.desugarAll
23
+ if (newT instanceof QualType) {
24
+ return this.isPointerType(newT.unqualifiedType);
25
+ }
26
+ else return newT.isPointer;
27
+ }
28
+
29
+ private r18_4_noPointerArithmetic($startNode: Joinpoint) {
30
+ if (!($startNode instanceof BinaryOp && /(\+=)|(-=)|(\+)|(-)/.test($startNode.operator))) return;
31
+
32
+ const leftT = $startNode.left.type;
33
+ const rightT = $startNode.right.type;
34
+
35
+ if ((S18_PointersArraysPass.isPointerType(leftT) && !S18_PointersArraysPass.isPointerType(rightT))
36
+ || (S18_PointersArraysPass.isPointerType(rightT) && !S18_PointersArraysPass.isPointerType(leftT))) {
37
+ this.logMISRAError("Pointer arithmetic is not allowed. The only exception is if two pointers to elements of the same array are subtracted.")
38
+ }
39
+ }
40
+
41
+ private static getDepth(type: Type) {
42
+ let depth = 0;
43
+ let curr = type.desugarAll;
44
+ while (curr.isPointer === true) {
45
+ depth++;
46
+
47
+ if(curr instanceof PointerType) {
48
+ curr = curr.pointee.desugarAll;
49
+ } else if(curr instanceof QualType) {
50
+ curr = (curr.unqualifiedType as PointerType).pointee.desugarAll;
51
+ } else {
52
+ throw new Error(`Not supported for type ${curr.joinPointType}.`);
53
+ }
54
+ }
55
+
56
+ return depth;
57
+ }
58
+
59
+ private static getUnderlyingType(type: Type) {
60
+ let curr = type.desugarAll;
61
+ while (curr.isPointer === true) {
62
+
63
+ if(curr instanceof PointerType) {
64
+ curr = curr.pointee.desugarAll;
65
+ } else if(curr instanceof QualType) {
66
+ curr = (curr.unqualifiedType as PointerType).pointee.desugarAll;
67
+ } else {
68
+ throw new Error(`Not supported for type ${curr.joinPointType}.`);
69
+ }
70
+ }
71
+
72
+ return curr;
73
+ }
74
+
75
+ private r18_5_noExcessivePointerNesting($startNode: Joinpoint) { //must apply to fields as well
76
+ if (($startNode instanceof Vardecl && !($startNode instanceof Param)) || $startNode instanceof Field) {
77
+ const depth = S18_PointersArraysPass.getDepth($startNode.type);
78
+ const underlyingType = S18_PointersArraysPass.getUnderlyingType($startNode.type);
79
+ if (underlyingType instanceof FunctionType) {
80
+ const retDepth = S18_PointersArraysPass.getDepth(underlyingType.returnType);
81
+ const paramDepths = underlyingType.paramTypes.map(type => S18_PointersArraysPass.getDepth(type)).filter(d => d > 2);
82
+ if (retDepth > 2) {
83
+ this.logMISRAError(`Return type of function pointer ${$startNode.code} has more than two levels of indirection.`);
84
+ }
85
+ if (paramDepths.length > 0) {
86
+ this.logMISRAError(`One or more parameters of function pointer ${$startNode.code} have more than two levels of indirection.`);
87
+ }
88
+ }
89
+ if (depth > 2) {
90
+ this.logMISRAError(`Type ${$startNode.type.code} has more than two levels of indirection.`)
91
+ }
92
+ }
93
+ else if ($startNode instanceof FunctionJp) {
94
+ const retDepth = S18_PointersArraysPass.getDepth($startNode.functionType.returnType);
95
+ const paramDepths = $startNode.functionType.paramTypes.map(type => S18_PointersArraysPass.getDepth(type)).filter(d => d > 2);
96
+ if (retDepth > 2) {
97
+ this.logMISRAError(`Return type of function ${$startNode.signature} has more than two levels of indirection.`);
98
+ }
99
+ if (paramDepths.length > 0) {
100
+ this.logMISRAError(`One or more parameters of function ${$startNode.signature} have more than two levels of indirection.`);
101
+ }
102
+ }
103
+ }
104
+
105
+ private r18_7_noFlexibleArrayMembers($startNode: Joinpoint) {
106
+ if (!($startNode instanceof Field)) return;
107
+ if (!$startNode.type.desugarAll.isArray || $startNode instanceof Param) return;
108
+
109
+ if ($startNode.type.desugarAll.arraySize === -1) {
110
+ this.logMISRAError(`Array ${$startNode.name} has variable or undefined size.`);
111
+ }
112
+ }
113
+
114
+ private r18_8_noVariableLengthArrays($startNode: Joinpoint) {
115
+ if (!($startNode instanceof Vardecl)) return;
116
+ if (!$startNode.type.desugarAll.isArray || $startNode.instanceOf("param")) return;
117
+
118
+ if ($startNode.type.desugarAll.arraySize === -1) {
119
+ this.logMISRAError(`Array ${$startNode.name} has variable or undefined size.`);
120
+ }
121
+ }
122
+
123
+
124
+ protected _name: string = "Pointers and arrays";
125
+
126
+ }
@@ -0,0 +1,27 @@
1
+ import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
2
+ import MISRAPass from "../MISRAPass.js";
3
+ import { PreprocessingReqs } from "../MISRAReporter.js";
4
+ import { Class, Joinpoint } from "@specs-feup/clava/api/Joinpoints.js";
5
+
6
+ export default class S19_OverlappingStoragePass extends MISRAPass {
7
+ protected _preprocessingReqs: PreprocessingReqs[] = [];
8
+
9
+ initRuleMapper(): void {
10
+ this._ruleMapper = new Map([
11
+ [2, this.r19_2_noUnion.bind(this)]
12
+ ]);
13
+ }
14
+
15
+ matchJoinpoint($jp: LaraJoinPoint): boolean {
16
+ return $jp instanceof Class;
17
+ }
18
+
19
+ private r19_2_noUnion($startNode: Joinpoint) {
20
+ if (!($startNode instanceof Class && $startNode.kind === "union")) return;
21
+
22
+ this.logMISRAError("The union keyword should not be used.");
23
+ }
24
+
25
+ protected _name: string = "Overlapping storage";
26
+
27
+ }
@@ -0,0 +1,92 @@
1
+ import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
2
+ import MISRAPass from "../MISRAPass.js";
3
+ import { PreprocessingReqs } from "../MISRAReporter.js";
4
+ import { Call, Include, Joinpoint } from "@specs-feup/clava/api/Joinpoints.js";
5
+
6
+ export default class S21_StandardLibPass extends MISRAPass {
7
+ protected _preprocessingReqs: PreprocessingReqs[] = [];
8
+
9
+ initRuleMapper(): void {
10
+ this._ruleMapper = new Map([
11
+ [3, this.r21_3_noDynamicAllocation.bind(this)],
12
+ [4, this.r21_4_noSetjmpUsage.bind(this)],
13
+ [5, this.r21_5_noSignalUsage.bind(this)],
14
+ [6, this.r21_6_noStandardIO.bind(this)],
15
+ [7, this.r21_7_noStringFunctions.bind(this)],
16
+ [8, this.r21_8_noSysFunctions.bind(this)],
17
+ [9, this.r21_9_noStdAlgos.bind(this)],
18
+ [10, this.r21_10_noDateUsage.bind(this)],
19
+ [11, this.r21_11_noTgmathUsage.bind(this)],
20
+ [12, this.r21_12_noFenvExceptions.bind(this)]
21
+ ]);
22
+ }
23
+
24
+ matchJoinpoint($jp: LaraJoinPoint): boolean {
25
+ return $jp instanceof Call || $jp instanceof Include;
26
+ }
27
+
28
+ private r21_3_noDynamicAllocation($startNode: Joinpoint) {
29
+ if (!($startNode instanceof Call && /(malloc|realloc|calloc|free)/.test($startNode.name))) return;
30
+
31
+ this.logMISRAError("Use of memory allocation functions provided by <stdlib.h> is not allowed.");
32
+ }
33
+
34
+ private r21_4_noSetjmpUsage($startNode: Joinpoint) {
35
+ if (!($startNode instanceof Include && $startNode.isAngled && $startNode.name === "setjmp.h")) return;
36
+
37
+ this.logMISRAError("Use of <setjmp.h> is not allowed.");
38
+ }
39
+
40
+ private r21_5_noSignalUsage($startNode: Joinpoint) {
41
+ if (!($startNode instanceof Include && $startNode.isAngled && $startNode.name === "signal.h")) return;
42
+
43
+ this.logMISRAError("Use of <signal.h> is not allowed.");
44
+ }
45
+
46
+ private r21_6_noStandardIO($startNode: Joinpoint) {
47
+ if (!($startNode instanceof Include && $startNode.isAngled && /(stdio.h|wchar.h)/.test($startNode.name))) return;
48
+
49
+ this.logMISRAError("Use of the standard library I/O functions provided by <stdio.h> and <wchar.h> is not allowed.");
50
+ }
51
+
52
+ private r21_7_noStringFunctions($startNode: Joinpoint) {
53
+ if (!($startNode instanceof Call && /(atoi|atof|atol|atoll)/.test($startNode.name))) return;
54
+
55
+ this.logMISRAError(`Use of function ${$startNode.signature} is not allowed.`);
56
+ }
57
+
58
+ private r21_8_noSysFunctions($startNode: Joinpoint) {
59
+ if (!($startNode instanceof Call && /(system|abort|exit|getenv)/.test($startNode.name))) return;
60
+
61
+ this.logMISRAError(`Use of function ${$startNode.signature} is not allowed.`);
62
+ }
63
+
64
+ private r21_9_noStdAlgos($startNode: Joinpoint) {
65
+ if (!($startNode instanceof Call && /(qsort|bsearch)/.test($startNode.name))) return;
66
+
67
+ this.logMISRAError(`Use of function ${$startNode.signature} is not allowed.`);
68
+ }
69
+
70
+ private r21_10_noDateUsage($startNode: Joinpoint) {
71
+ if ($startNode instanceof Include && $startNode.isAngled && $startNode.name === "time.h") {
72
+ this.logMISRAError("Use of <time.h> is not allowed.");
73
+ }
74
+ else if ($startNode instanceof Call && $startNode.name === "wcsftime") {
75
+ this.logMISRAError("Identifier 'wcsftime' shall not be used.");
76
+ }
77
+ }
78
+
79
+ private r21_11_noTgmathUsage($startNode: Joinpoint) {
80
+ if (!($startNode instanceof Include && $startNode.isAngled && $startNode.name === "tgmath.h")) return;
81
+
82
+ this.logMISRAError("Use of <tgmath.h> is not allowed.");
83
+ }
84
+
85
+ private r21_12_noFenvExceptions($startNode: Joinpoint) {
86
+ if (!($startNode instanceof Call && /(feclearexcept|fegetexceptflag|feraiseexcept|fesetexceptflag|fetestexcept)/.test($startNode.name))) return;
87
+
88
+ this.logMISRAError(`Use of function ${$startNode.signature} is not allowed.`);
89
+ }
90
+
91
+ protected _name: string = "Standard libraries";
92
+ }
@@ -0,0 +1,40 @@
1
+ import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
2
+ import MISRAPass from "../MISRAPass.js";
3
+ import { PreprocessingReqs } from "../MISRAReporter.js";
4
+ import { Comment, Joinpoint } from "@specs-feup/clava/api/Joinpoints.js";
5
+ import Fix from "@specs-feup/clava/api/clava/analysis/Fix.js";
6
+
7
+ export default class S3_CommentPass extends MISRAPass {
8
+ protected _preprocessingReqs: PreprocessingReqs[] = [];
9
+
10
+ initRuleMapper(): void {
11
+ this._ruleMapper = new Map([
12
+ [3, this.r3_1_fixComments.bind(this)]
13
+ ]);
14
+ }
15
+
16
+ matchJoinpoint($jp: LaraJoinPoint): boolean {
17
+ return true;
18
+ }
19
+
20
+ private static removeCommentSequences(str: string) {
21
+ return str.replace(/(\/\/|\/\*)/g, '');
22
+ }
23
+
24
+ private r3_1_fixComments($startNode: Joinpoint) { //inlines
25
+ if ($startNode instanceof Comment && /(\/\/|\/\*)/g.test($startNode.text)) {
26
+ this.logMISRAError(`Comment ${$startNode.text} contains invalid character sequences.`, new Fix(
27
+ $startNode,
28
+ $jp => ($jp as Comment).setText(S3_CommentPass.removeCommentSequences($startNode.text))
29
+ ));
30
+ }
31
+ $startNode.inlineComments.filter(comment => /(\/\/|\/\*)/g.test(comment.text)).forEach(comment => {
32
+ this.logMISRAError(`Comment ${comment.text} contains invalid character sequences.`, new Fix(
33
+ $startNode,
34
+ $jp => ($jp as Comment).setText(S3_CommentPass.removeCommentSequences(comment.text))
35
+ ));
36
+ });
37
+ }
38
+
39
+ protected _name: string = "Comments";
40
+ }
@@ -0,0 +1,66 @@
1
+ import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
2
+ import MISRAPass from "../MISRAPass.js";
3
+ import { PreprocessingReqs } from "../MISRAReporter.js";
4
+ import { Class, FileJp, FunctionJp, Joinpoint, NamedDecl, StorageClass, TypedefDecl, TypedefNameDecl, Vardecl } from "@specs-feup/clava/api/Joinpoints.js";
5
+ import Fix from "@specs-feup/clava/api/clava/analysis/Fix.js";
6
+
7
+ export default class S5_IdentifierPass extends MISRAPass {
8
+ protected _preprocessingReqs: PreprocessingReqs[] = [PreprocessingReqs.EXTERNAL_LINKAGE_DECLS];
9
+
10
+ initRuleMapper(): void {
11
+ throw new Error("Method not implemented.");
12
+ }
13
+
14
+ matchJoinpoint($jp: LaraJoinPoint): boolean {
15
+ throw new Error("Method not implemented.");
16
+ }
17
+
18
+ private static hasExternalLinkage($class: StorageClass) {
19
+ return $class !== StorageClass.STATIC && $class !== StorageClass.EXTERN;
20
+ }
21
+
22
+ private r5_1_externalIdentifierLength($startNode: Joinpoint) {
23
+ if (!(($startNode instanceof FunctionJp || $startNode instanceof Vardecl) && $startNode.parent instanceof FileJp)) return;
24
+ if (!S5_IdentifierPass.hasExternalLinkage($startNode.storageClass)) return;
25
+
26
+ if (!this._preprocessing?.externalLinkageDecls) {
27
+ throw new Error("Preprocessing has not been initialized properly.");
28
+ }
29
+
30
+ if (this._preprocessing.externalLinkageDecls.filter(jp => jp.astId !== $startNode.astId).map(jp => jp.name.substring(0, 31)).includes($startNode.name.substring(0, 31))) {
31
+ this.logMISRAError(`Identifier ${$startNode.name} is not distinct from other external identifiers.`, new Fix($startNode, $jp => {
32
+ ($jp as FunctionJp || Vardecl).name = "a_" + ($jp as FunctionJp || Vardecl).name;
33
+ }));
34
+ }
35
+ }
36
+
37
+ private r5_6_uniqueTypedefs($startNode: Joinpoint) {
38
+ if (!this._preprocessing?.typedefDecls) {
39
+ throw new Error("Preprocessing has not been initialized properly.");
40
+ }
41
+ if ($startNode instanceof NamedDecl && !($startNode instanceof TypedefNameDecl)) {
42
+ if ($startNode instanceof Class) {
43
+ const typedefChildren = $startNode.getDescendants("typedefDecl") as TypedefDecl[];
44
+ for (const child of typedefChildren) {
45
+ if ($startNode.name === child.name) return;
46
+ }
47
+ }
48
+ if (this._preprocessing.typedefDecls.some(jp => jp.name === $startNode.name)) {
49
+ this.logMISRAError(`${$startNode.name} is also the name of a typedef. Typedef identifiers must not be reused.`, new Fix($startNode, jp => {
50
+ const declJp = jp as NamedDecl;
51
+ declJp.name = declJp.name + "_" + declJp.astId;
52
+ }));
53
+ }
54
+ }
55
+ else if ($startNode instanceof TypedefNameDecl) {
56
+ if (this._preprocessing.typedefDecls.filter(jp => jp.astId !== $startNode.astId).some(jp => jp.name === $startNode.name)) {
57
+ this.logMISRAError("Typedef names must be unique across all translation units.", new Fix($startNode, jp => {
58
+ const typedefJp = jp as TypedefDecl;
59
+ typedefJp.name = typedefJp.name + "_" + typedefJp.astId;
60
+ }));
61
+ }
62
+ }
63
+ }
64
+
65
+ protected _name: string = "Identifiers";
66
+ }