All files / TRANSPILE/1-Analyze CommentExtractor.ts

100% Statements 27/27
100% Branches 12/12
100% Functions 5/5
100% Lines 25/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124                                                463x     463x             462x   462x 477x 477x     462x             2x                 477x     477x 477x 5x                           477x 477x 478x 478x     2x   478x 1x                   1x     1x                   477x   468x 468x 4x                      
import ECommentType from "../../types/ECommentType";
import CommentScanner from "../../PARSE/2-Parse/CommentScanner";
import IComment from "../../types/IComment";
import ICommentError from "./types/ICommentError";
 
/**
 * Validates comments against MISRA C:2012 Rules 3.1 and 3.2 (ADR-043).
 *
 * #1322: the extraction half is `CommentScanner`, in the parser. This is the
 * rejection half, and a rejection is 2.1's to author. The split is what lets
 * codegen read comments -- which it must, to re-attach them -- without
 * importing an analyzer.
 *
 * #1445: it takes the comments rather than the token stream they came off.
 * Scanning was 2.1 re-deriving a fact about the PARSE, which is what the
 * lifetime axis forbids -- so 1.2 now carries them on `IParsedFile` and this
 * class names no parse type at all. `CommentScanner` remains imported for
 * `markerLength`, a pure function of the comment kind; the confinement rule is
 * direct rather than reachable, because its claim is about what a pass's
 * source NAMES.
 */
class CommentExtractor {
  private readonly comments: readonly IComment[];
 
  private errors: ICommentError[] = [];
 
  constructor(comments: readonly IComment[]) {
    this.comments = comments;
  }
 
  /**
   * Validate all comments against MISRA C:2012 Rules 3.1 and 3.2
   */
  validate(): ICommentError[] {
    this.errors = [];
 
    for (const comment of this.comments) {
      this.validateMisra31(comment);
      this.validateMisra32(comment);
    }
 
    return this.errors;
  }
 
  /**
   * Get validation errors
   */
  getErrors(): ICommentError[] {
    return this.errors;
  }
 
  /**
   * MISRA C:2012 Rule 3.1: No nested comment markers
   * The character sequences /* and // shall not appear within a comment.
   * Exception: :// (URI pattern) is allowed per Amendment 4
   */
  private validateMisra31(comment: IComment): void {
    const content = comment.content;
 
    // Check for nested /* (not part of a URI)
    const nestedBlockStart = content.indexOf("/*");
    if (nestedBlockStart !== -1) {
      this.errors.push({
        rule: "3.1",
        message:
          "Nested comment marker '/*' found inside comment (MISRA C:2012 Rule 3.1)",
        line: comment.line,
        column:
          comment.column +
          CommentScanner.markerLength(comment.type) +
          nestedBlockStart,
      });
    }
 
    // Check for nested // (not part of a URI like ://)
    // Find all // occurrences and check if preceded by :
    let searchStart = 0;
    while (true) {
      const slashSlash = content.indexOf("//", searchStart);
      if (slashSlash === -1) break;
 
      // Check if this is part of a URI (preceded by :)
      const isUri = slashSlash > 0 && content[slashSlash - 1] === ":";
 
      if (!isUri) {
        this.errors.push({
          rule: "3.1",
          message:
            "Nested comment marker '//' found inside comment (MISRA C:2012 Rule 3.1)",
          line: comment.line,
          column:
            comment.column +
            CommentScanner.markerLength(comment.type) +
            slashSlash,
        });
        break; // Only report first occurrence
      }
 
      searchStart = slashSlash + 2;
    }
  }
 
  /**
   * MISRA C:2012 Rule 3.2: No line-splice in line comments
   * Line comments ending with \ cause undefined behavior
   */
  private validateMisra32(comment: IComment): void {
    // Only applies to line comments (// and ///)
    if (comment.type === ECommentType.Block) return;
 
    const content = comment.content;
    if (content.endsWith("\\")) {
      this.errors.push({
        rule: "3.2",
        message: String.raw`Line comment ends with '\' which causes line-splice (MISRA C:2012 Rule 3.2)`,
        line: comment.line,
        column: comment.column,
      });
    }
  }
}
 
export default CommentExtractor;