fn process_preprocessor_line()

in src/compiler/c.rs [802:925]


fn process_preprocessor_line(
    input_file: &Path,
    cwd: &Path,
    included_files: &mut HashMap<PathBuf, String>,
    config: PreprocessorCacheModeConfig,
    time_of_compilation: std::time::SystemTime,
    bytes: &mut [u8],
    mut start: usize,
    mut hash_start: usize,
    digest: &mut Digest,
    total_len: usize,
    normalized_include_paths: &mut HashMap<Vec<u8>, Option<Vec<u8>>>,
    fs_impl: &impl PreprocessorFSAbstraction,
) -> Result<PreprocessedLineAction> {
    let mut slice = &bytes[start..];
    // Workarounds for preprocessor linemarker bugs in GCC version 6.
    if slice.get(2) == Some(&b'3') {
        if slice.starts_with(HASH_31_COMMAND_LINE_NEWLINE) {
            // Bogus extra line with #31, after the regular #1:
            // Ignore the whole line, and continue parsing.
            digest.update(&bytes[hash_start..start]);
            while start < hash_start && slice[0] != b'\n' {
                start += 1;
            }
            start += 1;
            hash_start = start;
            return Ok(ControlFlow::Break((start, hash_start, true)));
        } else if slice.starts_with(HASH_32_COMMAND_LINE_2_NEWLINE) {
            // Bogus wrong line with #32, instead of regular #1:
            // Replace the line number with the usual one.
            digest.update(&bytes[hash_start..start]);
            start += 1;
            bytes[start..=start + 2].copy_from_slice(b"# 1");
            hash_start = start;
            slice = &bytes[start..];
        }
    }
    while start < total_len && slice[0] != b'"' && slice[0] != b'\n' {
        start += 1;
        if start < total_len {
            slice = &bytes[start..];
        }
    }
    slice = &bytes[start..];
    if start < total_len && slice[0] == b'\n' {
        // a newline before the quotation mark -> no match
        return Ok(ControlFlow::Break((start, hash_start, true)));
    }
    start += 1;
    if start >= total_len {
        bail!("Failed to parse included file path");
    }
    // `start` points to the beginning of an include file path
    digest.update(&bytes[hash_start..start]);
    hash_start = start;
    slice = &bytes[start..];
    while start < total_len && slice[0] != b'"' {
        start += 1;
        if start < total_len {
            slice = &bytes[start..];
        }
    }
    if start == hash_start {
        // Skip empty file name.
        return Ok(ControlFlow::Break((start, hash_start, true)));
    }
    // Look for preprocessor flags, after the "filename".
    let mut system = false;
    let mut pointer = start + 1;
    while pointer < total_len && bytes[pointer] != b'\n' {
        if bytes[pointer] == b'3' {
            // System header.
            system = true;
        }
        pointer += 1;
    }

    // `hash_start` and `start` span the include file path.
    let include_path = &bytes[hash_start..start];
    // We need to normalize the path now since it's part of the
    // hash and since we need to deduplicate the include files.
    // We cache the results since they are often quite a bit repeated.
    let include_path: &[u8] = if let Some(opt) = normalized_include_paths.get(include_path) {
        match opt {
            Some(normalized) => normalized,
            None => include_path,
        }
    } else {
        let path_buf = decode_path(include_path)?;
        let normalized = normalize_path(&path_buf);
        if normalized == path_buf {
            // `None` is a marker that the normalization is the same
            normalized_include_paths.insert(include_path.to_owned(), None);
            include_path
        } else {
            let mut encoded = Vec::with_capacity(include_path.len());
            encode_path(&mut encoded, &normalized)?;
            normalized_include_paths.insert(include_path.to_owned(), Some(encoded));
            // No entry API on hashmaps, so we need to query again
            normalized_include_paths
                .get(include_path)
                .unwrap()
                .as_ref()
                .unwrap()
        }
    };

    if !remember_include_file(
        include_path,
        input_file,
        cwd,
        included_files,
        digest,
        system,
        config,
        time_of_compilation,
        fs_impl,
    )? {
        return Ok(ControlFlow::Break((start, hash_start, false)));
    };
    // Everything of interest between hash_start and start has been hashed now.
    hash_start = start;
    Ok(ControlFlow::Continue((start, hash_start)))
}