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)))
}