MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 3 / 0 / 3
Functions: 100.0% 3 / 0 / 3
Branches: -% 0 / 0 / 0

data/SieveScript.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QByteArray>
4 #include <QList>
5 #include <QString>
6 #include <QStringList>
7
8 // Lossless Sieve (RFC 5228) syntax tree (Sprint 95 S95.1.6, SIEVE-PARSE).
9 //
10 // The parser works on the raw UTF-8 bytes of a script and records a byte
11 // span for every command, test, argument and string. The tree never owns a
12 // rewritten copy of the text: serialize() concatenates the original bytes,
13 // so serialize(parse(x)) == x for every input, valid or not (gate G1).
14 // Writers (SieveRuleCodec) patch the script by replacing whole spans and
15 // copy every untouched byte.
16 //
17 // Invariants:
18 // - Spans are half-open byte ranges [begin, end) into Script::source.
19 // - Identifiers and tags are stored lower case (Sieve is case-insensitive
20 // for them); string values keep their case.
21 // - A script that fails to parse keeps its source and every top-level
22 // command that ended before the error; `error` names line and column.
23 // - Limits (size, nesting depth, command count) turn hostile input into a
24 // parse error instead of a hang or deep recursion (SR-11).
25 namespace Sieve {
26
27 struct Span {
28 int begin = 0;
29 int end = 0;
30 72872 int length() const { return end - begin; }
31 bool isEmpty() const { return end <= begin; }
32 };
33
34 struct StringValue {
35 QString value; // decoded: escapes removed, dot-unstuffed
36 Span span; // raw bytes incl. quotes or "text:" ... "."
37 bool multiLine = false; // written as "text:" multi-line string
38 };
39
40 struct Argument {
41 enum class Kind { StringList, Number, Tag };
42 Kind kind = Kind::Tag;
43 Span span;
44 QList<StringValue> strings; // StringList only
45 bool bracketed = false; // StringList written as [ ... ]
46 quint64 number = 0; // Number only, quantifier applied
47 QString text; // Tag: lower case incl. ':'; Number: raw text
48
49 QStringList values() const;
50 };
51
52 struct Test {
53 QString name; // lower case
54 Span span;
55 int line = 0;
56 QList<Argument> arguments;
57 QList<Test> tests; // operands of allof/anyof/not
58 bool testList = false; // operands written as "( ... )"
59 };
60
61 struct Command {
62 QString name; // lower case
63 Span span; // identifier .. ";" or "}"
64 int line = 0;
65 QList<Argument> arguments;
66 QList<Test> tests;
67 bool testList = false;
68 bool hasBlock = false;
69 Span blockSpan; // "{" .. "}"
70 QList<Command> block;
71 };
72
73 struct ParseError {
74 QString message; // English, technical; UI maps it to plain words
75 int offset = -1;
76 int line = 0; // 1-based
77 int column = 0; // 1-based, in characters
78 1905204 bool isNull() const { return offset < 0; }
79 };
80
81 struct Limits {
82 int maxBytes = 1024 * 1024;
83 int maxDepth = 64;
84 int maxCommands = 10000;
85 };
86
87 struct Script {
88 QByteArray source;
89 QList<Command> commands; // top level, in source order
90 ParseError error;
91 7277 bool isValid() const { return error.isNull(); }
92 };
93
94 Script parse(const QByteArray &source, const Limits &limits = {});
95
96 // Concatenates the gaps and spans of the top-level commands and the tail;
97 // byte-identical to the parsed source.
98 QByteArray serialize(const Script &script);
99
100 // 1-based line and character column of a byte offset.
101 int lineOfOffset(const QByteArray &source, int offset);
102 int columnOfOffset(const QByteArray &source, int offset);
103
104 // Writes `value` as a quoted string ("..." with \" and \\ escaped), or as a
105 // multi-line "text:" string when it ends with a line break. `lineEnd` is the
106 // line terminator written for every line break of the value.
107 // Invariant (S106-091): parsing the result yields `value` again, with each
108 // line break written as `lineEnd`; a value with an inner line break but no
109 // trailing one stays a quoted string (a "text:" string always ends with
110 // one). S106-092: no Sieve string can hold NUL or a lone CR (RFC 5228
111 // §8.1) — a lone CR is written as a line break and NUL is dropped.
112 QByteArray stringLiteral(const QString &value, const QByteArray &lineEnd = "\n");
113 // A string list: a single string as is, several as [ "a", "b" ].
114 QByteArray stringListLiteral(const QStringList &values, const QByteArray &lineEnd = "\n");
115 // True when stringLiteral() writes `value` unchanged: no NUL and no CR
116 // outside a CRLF pair.
117 bool representableString(const QString &value);
118
119 // Parses one test expression (e.g. the commented-out test of Roundcube's
120 // disabled rule form "if false # <test>"). Returns false on any error or
121 // trailing content.
122 bool parseTestExpression(const QByteArray &text, Test *test);
123
124 } // namespace Sieve
125