diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 0000000..4e878cc --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,23 @@ +version = 1 + +test_patterns = [ + "**/tests/**", + "**/*tests.rs" +] + +exclude_patterns = [ + "tests/**", + "**/tests/**", +] + +[[analyzers]] +name = "rust" + + [analyzers.meta] + msrv = "stable" + +[[analyzers]] +name = "secrets" + +[[transformers]] +name = "rustfmt" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..b139747 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @raghav-deepsource @srijan-deepsource @swarnim-deepsource @prajwal-deepsource diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..202de28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/cppcheck_result.json +/cppcheck_error.xml \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bf67b62 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,207 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cppcheck-deepsource" +version = "0.2.0" +dependencies = [ + "atty", + "env_struct", + "log", + "quick-xml", + "serde", + "serde_json", + "walkdir", +] + +[[package]] +name = "env_struct" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "262a034c48a8051d032efcd1d546cb63a2c437d2be2731725df610daa13efe27" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "libc" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0e91231 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "cppcheck-deepsource" +version = "0.2.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# serialize & deserialize +serde = { version = "1.0.144", features = ["derive"] } +# json support +serde_json = "1.0.85" +log = { version = "0.4.17" } +atty = "0.2.14" +walkdir = "2.3.2" +quick-xml = { version = "0.28.0", features = ["serialize"] } + +env_struct = "0.1.3" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..63bb28b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,65 @@ +# ----------------------------------------------------------- +# Base Image with LLVM +# ----------------------------------------------------------- +FROM ubuntu:22.04 as ubuntu_llvm +ENV DEBIAN_FRONTEND=noninteractive + +# update the system and install any dependencies +RUN apt-get update \ + && apt-get upgrade -y libksba-dev \ + && apt-get install -y git cmake build-essential byacc libpcre3 libpcre3-dev grep lsb-release wget software-properties-common gnupg libcurl4-openssl-dev unzip lcov --no-install-recommends # skipcq: DOK-DL3018 + +# Get LLVM +ARG LLVM_VER=15 +RUN wget --no-verbose https://apt.llvm.org/llvm.sh +RUN chmod +x ./llvm.sh \ + && ./llvm.sh ${LLVM_VER} \ + && apt-get -y install libclang-${LLVM_VER}-dev libclang-cpp${LLVM_VER}-dev --no-install-recommends \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Add environment variables for build +ENV PATH="$PATH:/usr/lib/llvm-${LLVM_VER}/bin" +ENV LLVM_INSTALL_DIR "/usr/lib/llvm-${LLVM_VER}" +ENV SENTRY_INSTALL_DIR="/usr/lib/sentry-sdk" + +# Get Sentry +ARG SENTRY_TAG=0.6.3 +RUN mkdir /sentry-sdk \ + && cd /sentry-sdk \ + && wget --no-verbose "https://github.com/getsentry/sentry-native/releases/download/${SENTRY_TAG}/sentry-native.zip" \ + && unzip sentry-native.zip \ + && cmake -B ./build \ + && cmake --build ./build --parallel \ + && cmake --install ./build --prefix "${SENTRY_INSTALL_DIR}" + +# Install spdlog +RUN git clone --depth=1 --branch v1.11.0 https://github.com/gabime/spdlog.git \ + && cd spdlog \ + && cmake -B build \ + && cmake --build build --parallel \ + && cd build && make install + +# Install cppcheck +RUN git clone --depth=1 --branch 2.10.3 https://github.com/danmar/cppcheck.git \ + && cd cppcheck \ + && cmake -B build -DHAVE_RULES=ON -DUSE_MATCHCOMPILER=ON -DCMAKE_BUILD_TYPE=RELEASE \ + && cmake --build build --parallel 4 \ + && cd build && make install + +# ----------------------------------------------------------- +# End +# ----------------------------------------------------------- + +FROM rust:slim-bookworm AS rs_builder + +RUN mkdir -p /code +ADD . /code +WORKDIR /code + +RUN cargo b --release + +FROM ubuntu_llvm + +RUN mkdir -p /toolbox +COPY --from=rs_builder /code/target/release/cppcheck-deepsource /toolbox/ diff --git a/analysis_config.json b/analysis_config.json new file mode 100644 index 0000000..bc6686c --- /dev/null +++ b/analysis_config.json @@ -0,0 +1 @@ +{ "files": [ "test.c" ] } diff --git a/cloudbuild_depl.yaml b/cloudbuild_depl.yaml new file mode 100644 index 0000000..6b0faaf --- /dev/null +++ b/cloudbuild_depl.yaml @@ -0,0 +1,13 @@ +timeout: 30m0s + +steps: + - name: 'gcr.io/kaniko-project/executor:v1.0.0' + args: + - --destination=us.gcr.io/deepsource-production/cppcheck-deepsource:$TAG_NAME + - --destination=us.gcr.io/deepsource-production/cppcheck-deepsource:latest + - --dockerfile=Dockerfile + - --cache=false + - --snapshotMode=redo + +options: + machineType: 'E2_HIGHCPU_8' diff --git a/cloudbuild_depl_dev.yaml b/cloudbuild_depl_dev.yaml new file mode 100644 index 0000000..d6ccb7d --- /dev/null +++ b/cloudbuild_depl_dev.yaml @@ -0,0 +1,13 @@ +timeout: 30m0s + +steps: +- name: 'gcr.io/kaniko-project/executor:v1.0.0' + args: + - --destination=us.gcr.io/deepsource-dev/cppcheck-deepsource:dev + - --dockerfile=Dockerfile + - --cache=true + - --cache-ttl=24h + - --snapshotMode=redo + +options: + machineType: 'E2_HIGHCPU_8' diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..ce0b219 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,38 @@ +use std::path::PathBuf; + +use serde::Deserialize; + +#[derive(Default, Deserialize, Debug)] +pub struct AnalyzerConfig { + files: Vec, + #[serde(default)] + pub analyzer_meta: AnalyzerMeta, +} + +impl AnalyzerConfig { + pub fn cxx_files(self) -> Vec { + self.files + .into_iter() + .filter(|f| !f.is_symlink()) + .filter(|f| f.is_file()) + .filter(|f| { + f.extension() + .map(|x| x.eq("cpp") | x.eq("c")) + .unwrap_or_default() + }) + // ignore files > ~25MB in size + .filter(|f| { + !f.metadata() + .map(|x| x.len() > 25_000_000) + .unwrap_or_default() + }) + .collect() + } +} + +#[derive(Deserialize, Default, Debug)] +pub struct AnalyzerMeta { + pub name: String, + pub enabled: bool, + // todo(swarnim): add misra_compliance: bool +} diff --git a/src/cppcheck.rs b/src/cppcheck.rs new file mode 100644 index 0000000..4571baf --- /dev/null +++ b/src/cppcheck.rs @@ -0,0 +1,550 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Location { + #[serde(rename = "@file")] + pub file: String, + #[serde(rename = "@line")] + pub line: u32, + #[serde(rename = "@column")] + pub column: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Error { + #[serde(rename = "@id")] + pub id: String, + #[serde(rename = "@severity")] + pub severity: String, + #[serde(rename = "@msg")] + pub msg: String, + #[serde(rename = "@verbose")] + pub verbose: String, + #[serde(rename = "@file0")] + pub file0: Option, + #[serde(rename = "@cwe")] + pub cwe: Option, + pub location: Option>, + pub symbol: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Errors { + #[serde(default)] + pub error: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Results { + pub errors: Errors, +} + +#[test] +fn t_xml() { + let src = r##" + + + + + + + + + + + + + + + + + + + + + + + + + + + + "##; + let _value: Results = quick_xml::de::from_str(src).unwrap(); +} + +pub(crate) fn mapping(s: &str) -> Option { + Some( + match s { + // compiler only issue + "misra-c2012-1.1" => "CXX-W3001", + "misra-c2012-1.2" => "CXX-W3002", + // implmented in CppCheck + "misra-c2012-1.3" => "CXX-W3003", + "misra-c2012-1.4" => "CXX-W3004", + "misra-c2012-2.1" => "CXX-W3005", + "misra-c2012-2.2" => "CXX-W3006", + "misra-c2012-2.3" => "CXX-W3007", + "misra-c2012-2.4" => "CXX-W3008", + "misra-c2012-2.5" => "CXX-W3009", + "misra-c2012-2.6" => "CXX-W3010", + "misra-c2012-2.7" => "CXX-W3011", + "misra-c2012-3.1" => "CXX-W3012", + "misra-c2012-3.2" => "CXX-W3013", + "misra-c2012-4.1" => "CXX-W3014", + "misra-c2012-4.2" => "CXX-W3015", + "misra-c2012-5.1" => "CXX-W3016", + "misra-c2012-5.2" => "CXX-W3017", + "misra-c2012-5.3" => "CXX-W3018", + "misra-c2012-5.4" => "CXX-W3019", + "misra-c2012-5.5" => "CXX-W3020", + "misra-c2012-5.6" => "CXX-W3021", + "misra-c2012-5.7" => "CXX-W3022", + "misra-c2012-5.8" => "CXX-W3023", + "misra-c2012-5.9" => "CXX-W3024", + "misra-c2012-6.1" => "CXX-W3025", + "misra-c2012-6.2" => "CXX-W3026", + "misra-c2012-7.1" => "CXX-W3027", + "misra-c2012-7.2" => "CXX-W3028", + "misra-c2012-7.3" => "CXX-W3029", + "misra-c2012-7.4" => "CXX-W3030", + "misra-c2012-8.1" => "CXX-W3031", + "misra-c2012-8.2" => "CXX-W3032", + "misra-c2012-8.3" => "CXX-W3033", + "misra-c2012-8.4" => "CXX-W3034", + "misra-c2012-8.5" => "CXX-W3035", + "misra-c2012-8.6" => "CXX-W3036", + "misra-c2012-8.7" => "CXX-W3037", + "misra-c2012-8.8" => "CXX-W3038", + "misra-c2012-8.9" => "CXX-W3039", + "misra-c2012-8.10" => "CXX-W3040", + "misra-c2012-8.11" => "CXX-W3041", + "misra-c2012-8.12" => "CXX-W3042", + "misra-c2012-8.13" => "CXX-W3043", + "misra-c2012-8.14" => "CXX-W3044", + "misra-c2012-9.1" => "CXX-W3045", + "misra-c2012-9.2" => "CXX-W3046", + "misra-c2012-9.3" => "CXX-W3047", + "misra-c2012-9.4" => "CXX-W3048", + "misra-c2012-9.5" => "CXX-W3049", + "misra-c2012-10.1" => "CXX-W3050", + "misra-c2012-10.2" => "CXX-W3051", + "misra-c2012-10.3" => "CXX-W3052", + "misra-c2012-10.4" => "CXX-W3053", + "misra-c2012-10.5" => "CXX-W3054", + "misra-c2012-10.6" => "CXX-W3055", + "misra-c2012-10.7" => "CXX-W3056", + "misra-c2012-10.8" => "CXX-W3057", + "misra-c2012-11.1" => "CXX-W3058", + "misra-c2012-11.2" => "CXX-W3059", + "misra-c2012-11.3" => "CXX-W3060", + "misra-c2012-11.4" => "CXX-W3061", + "misra-c2012-11.5" => "CXX-W3062", + "misra-c2012-11.6" => "CXX-W3063", + "misra-c2012-11.7" => "CXX-W3064", + "misra-c2012-11.8" => "CXX-W3065", + "misra-c2012-11.9" => "CXX-W3066", + "misra-c2012-12.1" => "CXX-W3067", + "misra-c2012-12.2" => "CXX-W3068", + "misra-c2012-12.3" => "CXX-W3069", + "misra-c2012-12.4" => "CXX-W3070", + "misra-c2012-13.1" => "CXX-W3071", + "misra-c2012-13.2" => "CXX-W3072", + "misra-c2012-13.3" => "CXX-W3073", + "misra-c2012-13.4" => "CXX-W3074", + "misra-c2012-13.5" => "CXX-W3075", + "misra-c2012-13.6" => "CXX-W3076", + "misra-c2012-14.1" => "CXX-W3077", + "misra-c2012-14.2" => "CXX-W3078", + "misra-c2012-14.3" => "CXX-W3079", + "misra-c2012-14.4" => "CXX-W3080", + "misra-c2012-15.1" => "CXX-W3081", + "misra-c2012-15.2" => "CXX-W3082", + "misra-c2012-15.3" => "CXX-W3083", + "misra-c2012-15.4" => "CXX-W3084", + "misra-c2012-15.5" => "CXX-W3085", + "misra-c2012-15.6" => "CXX-W3086", + "misra-c2012-15.7" => "CXX-W3087", + "misra-c2012-16.1" => "CXX-W3088", + "misra-c2012-16.2" => "CXX-W3089", + "misra-c2012-16.3" => "CXX-W3090", + "misra-c2012-16.4" => "CXX-W3091", + "misra-c2012-16.5" => "CXX-W3092", + "misra-c2012-16.6" => "CXX-W3093", + "misra-c2012-16.7" => "CXX-W3094", + "misra-c2012-17.1" => "CXX-W3095", + "misra-c2012-17.2" => "CXX-W3096", + // compiler only issue + "misra-c2012-17.3" => "CXX-W3097", + // ------------------- + "misra-c2012-17.4" => "CXX-W3098", + "misra-c2012-17.5" => "CXX-W3099", + "misra-c2012-17.6" => "CXX-W3100", + "misra-c2012-17.7" => "CXX-W3101", + "misra-c2012-17.8" => "CXX-W3102", + "misra-c2012-18.1" => "CXX-W3103", + "misra-c2012-18.2" => "CXX-W3104", + "misra-c2012-18.3" => "CXX-W3105", + "misra-c2012-18.4" => "CXX-W3106", + "misra-c2012-18.5" => "CXX-W3107", + "misra-c2012-18.6" => "CXX-W3108", + "misra-c2012-18.7" => "CXX-W3109", + "misra-c2012-18.8" => "CXX-W3110", + "misra-c2012-19.1" => "CXX-W3111", + "misra-c2012-19.2" => "CXX-W3112", + "misra-c2012-20.1" => "CXX-W3113", + "misra-c2012-20.2" => "CXX-W3114", + "misra-c2012-20.3" => "CXX-W3115", + "misra-c2012-20.4" => "CXX-W3116", + "misra-c2012-20.5" => "CXX-W3117", + "misra-c2012-20.6" => "CXX-W3118", + "misra-c2012-20.7" => "CXX-W3119", + "misra-c2012-20.8" => "CXX-W3120", + "misra-c2012-20.9" => "CXX-W3121", + "misra-c2012-20.10" => "CXX-W3122", + "misra-c2012-20.11" => "CXX-W3123", + "misra-c2012-20.12" => "CXX-W3124", + "misra-c2012-20.13" => "CXX-W3125", + "misra-c2012-20.14" => "CXX-W3126", + "misra-c2012-21.1" => "CXX-W3127", + "misra-c2012-21.2" => "CXX-W3128", + "misra-c2012-21.3" => "CXX-W3129", + "misra-c2012-21.4" => "CXX-W3130", + "misra-c2012-21.5" => "CXX-W3131", + "misra-c2012-21.6" => "CXX-W3132", + "misra-c2012-21.7" => "CXX-W3133", + "misra-c2012-21.8" => "CXX-W3134", + "misra-c2012-21.9" => "CXX-W3135", + "misra-c2012-21.10" => "CXX-W3136", + "misra-c2012-21.11" => "CXX-W3137", + "misra-c2012-21.12" => "CXX-W3138", + "misra-c2012-21.13" => "CXX-W3139", + "misra-c2012-21.14" => "CXX-W3140", + "misra-c2012-21.15" => "CXX-W3141", + "misra-c2012-21.16" => "CXX-W3142", + "misra-c2012-21.17" => "CXX-W3143", + "misra-c2012-21.18" => "CXX-W3144", + "misra-c2012-21.19" => "CXX-W3145", + "misra-c2012-21.20" => "CXX-W3146", + "misra-c2012-21.21" => "CXX-W3147", + "misra-c2012-22.1" => "CXX-W3148", + "misra-c2012-22.2" => "CXX-W3149", + "misra-c2012-22.3" => "CXX-W3150", + "misra-c2012-22.4" => "CXX-W3151", + "misra-c2012-22.5" => "CXX-W3152", + "misra-c2012-22.6" => "CXX-W3153", + "misra-c2012-22.7" => "CXX-W3154", + "misra-c2012-22.8" => "CXX-W3155", + "misra-c2012-22.9" => "CXX-W3156", + "misra-c2012-22.10" => "CXX-W3157", + // all other cpp check issues + "purgedConfiguration" => "CXX-W3500", + "toomanyconfigs" => "CXX-W3501", + "AssignmentAddressToInteger" => "CXX-W3502", + "AssignmentIntegerToAddress" => "CXX-W3503", + "CastIntegerToAddressAtReturn" => "CXX-W3504", + "CastAddressToIntegerAtReturn" => "CXX-W3505", + "assertWithSideEffect" => "CXX-W3506", + "assignmentInAssert" => "CXX-W3507", + "autoVariables" => "CXX-W3508", + "returnReference" => "CXX-W3509", + "danglingReference" => "CXX-W3510", + "returnTempReference" => "CXX-W3511", + "danglingTempReference" => "CXX-W3512", + "autovarInvalidDeallocation" => "CXX-W3513", + "uselessAssignmentArg" => "CXX-W3514", + "uselessAssignmentPtrArg" => "CXX-W3515", + "returnDanglingLifetime" => "CXX-W3516", + "invalidLifetime" => "CXX-W3517", + "danglingLifetime" => "CXX-W3518", + "danglingTemporaryLifetime" => "CXX-W3519", + "assignBoolToPointer" => "CXX-W3520", + "assignBoolToFloat" => "CXX-W3521", + "comparisonOfFuncReturningBoolError" => "CXX-W3522", + "comparisonOfTwoFuncsReturningBoolError" => "CXX-W3523", + "comparisonOfBoolWithBoolError" => "CXX-W3524", + "incrementboolean" => "CXX-W3525", + "bitwiseOnBoolean" => "CXX-W3526", + "compareBoolExpressionWithInt" => "CXX-W3527", + "pointerArithBool" => "CXX-W3528", + "comparisonOfBoolWithInvalidComparator" => "CXX-W3529", + "returnNonBoolInBooleanFunction" => "CXX-W3530", + "boostForeachError" => "CXX-W3531", + "arrayIndexOutOfBounds" => "CXX-W3532", + "arrayIndexOutOfBoundsCond" => "CXX-W3533", + "pointerOutOfBounds" => "CXX-W3534", + "negativeIndex" => "CXX-W3535", + "arrayIndexThenCheck" => "CXX-W3536", + "bufferAccessOutOfBounds" => "CXX-W3537", + "objectIndex" => "CXX-W3538", + "argumentSize" => "CXX-W3539", + "negativeMemoryAllocationSize" => "CXX-W3540", + "negativeArraySize" => "CXX-W3541", + "invalidFunctionArg" => "CXX-W3542", + "invalidFunctionArgBool" => "CXX-W3543", + "invalidFunctionArgStr" => "CXX-W3544", + "ignoredReturnValue" => "CXX-W3545", + "wrongmathcall" => "CXX-W3546", + "unpreciseMathCall" => "CXX-W3547", + "memsetZeroBytes" => "CXX-W3548", + "memsetFloat" => "CXX-W3549", + "memsetValueOutOfRange" => "CXX-W3550", + "missingReturn" => "CXX-W3551", + "returnStdMoveLocal" => "CXX-W3552", + "useStandardLibrary" => "CXX-W3553", + "noConstructor" => "CXX-W3554", + "noExplicitConstructor" => "CXX-W3555", + "copyCtorPointerCopying" => "CXX-W3556", + "noCopyConstructor" => "CXX-W3557", + "noOperatorEq" => "CXX-W3558", + "noDestructor" => "CXX-W3559", + "uninitMemberVar" => "CXX-W3560", + "uninitMemberVarPrivate" => "CXX-W3561", + "uninitDerivedMemberVar" => "CXX-W3562", + "uninitDerivedMemberVarPrivate" => "CXX-W3563", + "missingMemberCopy" => "CXX-W3564", + "operatorEqVarError" => "CXX-W3565", + "unusedPrivateFunction" => "CXX-W3566", + "memsetClass" => "CXX-W3567", + "memsetClassReference" => "CXX-W3568", + "memsetClassFloat" => "CXX-W3569", + "mallocOnClassWarning" => "CXX-W3570", + "mallocOnClassError" => "CXX-W3571", + "virtualDestructor" => "CXX-W3572", + "thisSubtraction" => "CXX-W3573", + "operatorEqRetRefThis" => "CXX-W3574", + "operatorEqMissingReturnStatement" => "CXX-W3575", + "operatorEqShouldBeLeftUnimplemented" => "CXX-W3576", + "operatorEqToSelf" => "CXX-W3577", + "functionConst" => "CXX-W3578", + "functionStatic" => "CXX-W3579", + "initializerList" => "CXX-W3580", + "useInitializationList" => "CXX-W3581", + "selfInitialization" => "CXX-W3582", + "duplInheritedMember" => "CXX-W3583", + "copyCtorAndEqOperator" => "CXX-W3584", + "pureVirtualCall" => "CXX-W3585", + "virtualCallInConstructor" => "CXX-W3586", + "missingOverride" => "CXX-W3587", + "thisUseAfterFree" => "CXX-W3588", + "unsafeClassRefMember" => "CXX-W3589", + "assignIfError" => "CXX-W3590", + "badBitmaskCheck" => "CXX-W3591", + "comparisonError" => "CXX-W3592", + "duplicateCondition" => "CXX-W3593", + "multiCondition" => "CXX-W3594", + "mismatchingBitAnd" => "CXX-W3595", + "oppositeInnerCondition" => "CXX-W3596", + "identicalInnerCondition" => "CXX-W3597", + "identicalConditionAfterEarlyExit" => "CXX-W3598", + "incorrectLogicOperator" => "CXX-W3599", + "redundantCondition" => "CXX-W3600", + "moduloAlwaysTrueFalse" => "CXX-W3601", + "clarifyCondition" => "CXX-W3602", + "knownConditionTrueFalse" => "CXX-W3603", + "invalidTestForOverflow" => "CXX-W3604", + "pointerAdditionResultNotNull" => "CXX-W3605", + "duplicateConditionalAssign" => "CXX-W3606", + "assignmentInCondition" => "CXX-W3607", + "compareValueOutOfTypeRangeError" => "CXX-W3608", + "exceptThrowInDestructor" => "CXX-W3609", + "exceptDeallocThrow" => "CXX-W3610", + "exceptRethrowCopy" => "CXX-W3611", + "catchExceptionByValue" => "CXX-W3612", + "throwInNoexceptFunction" => "CXX-W3613", + "unhandledExceptionSpecification" => "CXX-W3614", + "rethrowNoCurrentException" => "CXX-W3615", + "coutCerrMisusage" => "CXX-W3616", + "fflushOnInputStream" => "CXX-W3617", + "IOWithoutPositioning" => "CXX-W3618", + "readWriteOnlyFile" => "CXX-W3619", + "writeReadOnlyFile" => "CXX-W3620", + "useClosedFile" => "CXX-W3621", + "seekOnAppendedFile" => "CXX-W3622", + "incompatibleFileOpen" => "CXX-W3623", + "invalidscanf" => "CXX-W3624", + "wrongPrintfScanfArgNum" => "CXX-W3625", + "invalidScanfArgType_s" => "CXX-W3626", + "invalidScanfArgType_int" => "CXX-W3627", + "invalidScanfArgType_float" => "CXX-W3628", + "invalidPrintfArgType_s" => "CXX-W3629", + "invalidPrintfArgType_n" => "CXX-W3630", + "invalidPrintfArgType_p" => "CXX-W3631", + "invalidPrintfArgType_uint" => "CXX-W3632", + "invalidPrintfArgType_sint" => "CXX-W3633", + "invalidPrintfArgType_float" => "CXX-W3634", + "invalidLengthModifierError" => "CXX-W3635", + "invalidScanfFormatWidth" => "CXX-W3636", + "invalidScanfFormatWidth_smaller" => "CXX-W3637", + "wrongPrintfScanfParameterPositionError" => "CXX-W3638", + "deallocret" => "CXX-W3639", + "doubleFree" => "CXX-W3640", + "leakNoVarFunctionCall" => "CXX-W3641", + "leakReturnValNotUsed" => "CXX-W3642", + "leakUnsafeArgAlloc" => "CXX-W3643", + "publicAllocationError" => "CXX-W3644", + "unsafeClassCanLeak" => "CXX-W3645", + "memleak" => "CXX-W3646", + "resourceLeak" => "CXX-W3647", + "deallocuse" => "CXX-W3648", + "mismatchAllocDealloc" => "CXX-W3649", + "memleakOnRealloc" => "CXX-W3650", + "nullPointer" => "CXX-W3651", + "nullPointerDefaultArg" => "CXX-W3652", + "nullPointerRedundantCheck" => "CXX-W3653", + "nullPointerArithmetic" => "CXX-W3654", + "nullPointerArithmeticRedundantCheck" => "CXX-W3655", + "zerodiv" => "CXX-W3656", + "zerodivcond" => "CXX-W3657", + "unusedScopedObject" => "CXX-W3658", + "invalidPointerCast" => "CXX-W3659", + "shiftNegativeLHS" => "CXX-W3660", + "shiftNegative" => "CXX-W3661", + "raceAfterInterlockedDecrement" => "CXX-W3662", + "invalidFree" => "CXX-W3663", + "overlappingWriteUnion" => "CXX-W3664", + "overlappingWriteFunction" => "CXX-W3665", + "redundantCopyLocalConst" => "CXX-W3666", + "redundantCopy" => "CXX-W3667", + "comparisonFunctionIsAlwaysTrueOrFalse" => "CXX-W3668", + "checkCastIntToCharAndBack" => "CXX-W3669", + "cstyleCast" => "CXX-W3670", + "passedByValue" => "CXX-W3671", + "constParameter" => "CXX-W3672", + "constVariable" => "CXX-W3673", + "constParameterCallback" => "CXX-W3674", + "constStatement" => "CXX-W3675", + "signedCharArrayIndex" => "CXX-W3676", + "unknownSignCharArrayIndex" => "CXX-W3677", + "charBitOp" => "CXX-W3678", + "variableScope" => "CXX-W3679", + "redundantAssignInSwitch" => "CXX-W3680", + "suspiciousCase" => "CXX-W3681", + "selfAssignment" => "CXX-W3682", + "clarifyCalculation" => "CXX-W3683", + "clarifyStatement" => "CXX-W3684", + "duplicateBranch" => "CXX-W3685", + "duplicateAssignExpression" => "CXX-W3686", + "oppositeExpression" => "CXX-W3687", + "duplicateExpression" => "CXX-W3688", + "duplicateValueTernary" => "CXX-W3689", + "duplicateExpressionTernary" => "CXX-W3690", + "duplicateBreak" => "CXX-W3691", + "unreachableCode" => "CXX-W3692", + "unsignedLessThanZero" => "CXX-W3693", + "unsignedPositive" => "CXX-W3694", + "pointerLessThanZero" => "CXX-W3695", + "pointerPositive" => "CXX-W3696", + "suspiciousSemicolon" => "CXX-W3697", + "incompleteArrayFill" => "CXX-W3698", + "varFuncNullUB" => "CXX-W3699", + "nanInArithmeticExpression" => "CXX-W3700", + "commaSeparatedReturn" => "CXX-W3701", + "redundantPointerOp" => "CXX-W3702", + "unusedLabel" => "CXX-W3703", + "unusedLabelConfiguration" => "CXX-W3704", + "unusedLabelSwitch" => "CXX-W3705", + "unusedLabelSwitchConfiguration" => "CXX-W3706", + "unknownEvaluationOrder" => "CXX-W3707", + "accessMoved" => "CXX-W3708", + "accessForwarded" => "CXX-W3709", + "funcArgNamesDifferent" => "CXX-W3710", + "redundantBitwiseOperationInSwitch" => "CXX-W3711", + "shadowVariable" => "CXX-W3712", + "shadowFunction" => "CXX-W3713", + "shadowArgument" => "CXX-W3714", + "knownArgument" => "CXX-W3715", + "knownArgumentHiddenVariableExpression" => "CXX-W3716", + "comparePointers" => "CXX-W3717", + "redundantAssignment" => "CXX-W3718", + "redundantInitialization" => "CXX-W3719", + "funcArgOrderDifferent" => "CXX-W3720", + "moduloofone" => "CXX-W3721", + "containerOutOfBounds" => "CXX-W3722", + "invalidIterator1" => "CXX-W3723", + "iterators1" => "CXX-W3724", + "iterators2" => "CXX-W3725", + "iterators3" => "CXX-W3726", + "invalidContainerLoop" => "CXX-W3727", + "invalidContainer" => "CXX-W3728", + "mismatchingContainerIterator" => "CXX-W3729", + "mismatchingContainers" => "CXX-W3730", + "mismatchingContainerExpression" => "CXX-W3731", + "sameIteratorExpression" => "CXX-W3732", + "eraseDereference" => "CXX-W3733", + "stlOutOfBounds" => "CXX-W3734", + "negativeContainerIndex" => "CXX-W3735", + "stlBoundaries" => "CXX-W3736", + "stlIfFind" => "CXX-W3737", + "stlIfStrFind" => "CXX-W3738", + "stlFindInsert" => "CXX-W3739", + "stlcstr" => "CXX-W3740", + "stlcstrReturn" => "CXX-W3741", + "stlcstrParam" => "CXX-W3742", + "stlcstrthrow" => "CXX-W3743", + "stlSize" => "CXX-W3744", + "StlMissingComparison" => "CXX-W3745", + "redundantIfRemove" => "CXX-W3746", + "uselessCallsCompare" => "CXX-W3747", + "uselessCallsSwap" => "CXX-W3748", + "uselessCallsSubstr" => "CXX-W3749", + "uselessCallsEmpty" => "CXX-W3750", + "uselessCallsRemove" => "CXX-W3751", + "derefInvalidIterator" => "CXX-W3752", + "useStlAlgorithm" => "CXX-W3753", + "knownEmptyContainer" => "CXX-W3754", + "globalLockGuard" => "CXX-W3755", + "localMutex" => "CXX-W3756", + "sizeofwithsilentarraypointer" => "CXX-W3757", + "pointerSize" => "CXX-W3758", + "sizeofDivisionMemfunc" => "CXX-W3759", + "sizeofwithnumericparameter" => "CXX-W3760", + "sizeofsizeof" => "CXX-W3761", + "sizeofCalculation" => "CXX-W3762", + "sizeofFunctionCall" => "CXX-W3763", + "multiplySizeof" => "CXX-W3764", + "divideSizeof" => "CXX-W3765", + "sizeofVoid" => "CXX-W3766", + "sizeofDereferencedVoidPointer" => "CXX-W3767", + "arithOperationsOnVoidPointer" => "CXX-W3768", + "stringLiteralWrite" => "CXX-W3769", + "sprintfOverlappingData" => "CXX-W3770", + "strPlusChar" => "CXX-W3771", + "incorrectStringCompare" => "CXX-W3772", + "literalWithCharPtrCompare" => "CXX-W3773", + "charLiteralWithCharPtrCompare" => "CXX-W3774", + "incorrectStringBooleanError" => "CXX-W3775", + "incorrectCharBooleanError" => "CXX-W3776", + "staticStringCompare" => "CXX-W3777", + "stringCompare" => "CXX-W3778", + "overlappingStrcmp" => "CXX-W3779", + "shiftTooManyBits" => "CXX-W3780", + "shiftTooManyBitsSigned" => "CXX-W3781", + "integerOverflow" => "CXX-W3782", + "signConversion" => "CXX-W3783", + "truncLongCastAssignment" => "CXX-W3784", + "truncLongCastReturn" => "CXX-W3785", + "floatConversionOverflow" => "CXX-W3786", + "uninitdata" => "CXX-W3787", + "uninitStructMember" => "CXX-W3788", + "unusedFunction" => "CXX-W3789", + "unusedVariable" => "CXX-W3790", + "unusedAllocatedMemory" => "CXX-W3791", + "unreadVariable" => "CXX-W3792", + "unassignedVariable" => "CXX-W3793", + "unusedStructMember" => "CXX-W3794", + "postfixOperator" => "CXX-W3795", + "va_start_wrongParameter" => "CXX-W3796", + "va_start_referencePassed" => "CXX-W3797", + "va_end_missing" => "CXX-W3798", + "va_list_usedBeforeStarted" => "CXX-W3799", + "va_start_subsequentCalls" => "CXX-W3800", + "missingInclude" => "CXX-W3801", + "missingIncludeSystem" => "CXX-W3802", + "ConfigurationNotChecked" => "CXX-W3803", + "preprocessorErrorDirective" => "CXX-W3804", + _ => return None, + } + .to_string(), + ) +} diff --git a/src/fmtlogger.rs b/src/fmtlogger.rs new file mode 100644 index 0000000..ac12871 --- /dev/null +++ b/src/fmtlogger.rs @@ -0,0 +1,76 @@ +use log::{Level, LevelFilter, Log, Metadata, Record}; + +const CLICOLOR_FORCE: &str = "CLICOLOR_FORCE"; + +/// Implements [`Log`] and a set of simple builder methods for configuration. +struct Logger { + /// Global logging level when using this type + level: LevelFilter, +} + +impl Default for Logger { + fn default() -> Self { + Logger { + level: LevelFilter::Trace, + } + } +} + +impl Log for Logger { + fn enabled(&self, metadata: &Metadata) -> bool { + &metadata.level().to_level_filter() <= &self.level + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let target = if record.target().is_empty() { + record.module_path().unwrap_or_default() + } else { + record.target() + }; + + const BLUE: &str = "\x1B[34m"; + const RED: &str = "\x1B[31m"; + const YELLOW: &str = "\x1B[33m"; + const WHITE: &str = "\x1B[37m"; + const RESET: &str = "\x1B[0m"; + + let color = match record.level() { + Level::Error => RED, + Level::Warn => YELLOW, + Level::Info => WHITE, + Level::Debug => BLUE, + Level::Trace => "", + }; + + let supports_color = + atty::is(atty::Stream::Stderr) || std::env::var(CLICOLOR_FORCE).is_ok(); + let mut log = if let Some((file, line)) = record.file().zip(record.line()) { + format!( + "{file}:{line} [{}][{target}]: {}", + record.level(), + record.args(), + ) + } else { + format!("[{}][{target}]: {}", record.level(), record.args()) + }; + if supports_color { + log = format!("{}{}{}", color, log, RESET); + } + eprintln!("{}", log); + } + } + + fn flush(&self) {} +} + +pub fn default() { + const LOGGER: Logger = Logger { + level: LevelFilter::Trace, + }; + log::set_max_level(LOGGER.level); + if let Err(err) = log::set_logger(&LOGGER) { + // used const to allow for static lifetime + eprintln!("attaching logger failed! shouldn't be possible: {:?}", err); + } +} diff --git a/src/issue.rs b/src/issue.rs new file mode 100644 index 0000000..9f11ef2 --- /dev/null +++ b/src/issue.rs @@ -0,0 +1,8 @@ +use crate::result::Mark; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Occurrence { + pub file: String, + pub begin: Mark, + pub end: Mark, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..fb5d177 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,153 @@ +mod config; +mod cppcheck; +mod fmtlogger; +mod issue; +mod result; + +use std::{ + collections::HashSet, + error::Error, + path::{Path, PathBuf}, + process::{self, Command, Stdio}, +}; + +use crate::config::AnalyzerConfig; + +use env_struct::env_struct; +env_struct! { + #[derive(Debug)] + pub struct Env { + pub toolbox_path = "/toolbox".into(), + pub code_path = "/code".into(), + } +} + +env_struct! { + #[derive(Debug)] + pub struct CppcheckEnv { + pub cppcheck_cache_path, + } +} + +fn run_cppcheck<'a>(executable: &str, code_path: impl AsRef, output_path: impl AsRef) { + let start = std::time::Instant::now(); + // ensure the command is run in `sh` + let mut command_with_sh = Command::new("sh"); + // only enable caching if cache_path is set + let cppcheck_build_dir = if let Ok(cppcheck_env) = CppcheckEnv::try_load_from_env() { + format!("--cppcheck-build-dir={}", cppcheck_env.cppcheck_cache_path) + } else { + "".to_string() + }; + let code_dir = code_path.as_ref(); + let output_file = output_path.as_ref().display(); + // build the command to run + command_with_sh + .arg("-c") + .arg(format!("{executable} {code_dir} -l 6 --addon=misra --xml --output-file={output_file} {cppcheck_build_dir}")) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()); + log::debug!("Running cppcheck START :: {:?}", start.elapsed()); + log::debug!("Shell command: {command_with_sh:?}"); + let output = command_with_sh.output(); + log::debug!("Ran cppcheck END :: {:?}", start.elapsed()); + log::trace!("{:#?}", output); +} + +fn main() { + // setup logging + fmtlogger::default(); + + // all errors are propagated to sentry with backtrace + if let Err(err) = _main() { + log::error!("error raised: {err}"); + // early exit with status 1 + process::exit(1); + } +} + +fn _main() -> Result<(), Box> { + let env = Env::load_from_env(); + let toolbox_directory = PathBuf::from(env.toolbox_path); + let cppcheck_executable = "cppcheck"; + let cppcheck_output_path = toolbox_directory.join("cppcheck_error.xml"); + let analysis_config_path = toolbox_directory.join("analysis_config.json"); + let files_set: HashSet = HashSet::from_iter( + std::fs::read_to_string(&analysis_config_path) + .ok() + .and_then(|s| serde_json::from_str::(&s).ok()) + .map_or_else( + || { + log::error!( + "Failed to load analysis config, at `{}`, using empty file list.", + analysis_config_path.display() + ); + Vec::default() + }, + AnalyzerConfig::cxx_files, + ), + ); + + run_cppcheck(cppcheck_executable, env.code_path, &cppcheck_output_path); + + log::debug!("{:#?}", files_set); + let mut issue_occurrences = vec![]; + if let Some(cppcheck_results) = std::fs::read_to_string(cppcheck_output_path) + .ok() + .map(|src| quick_xml::de::from_str::(&src).unwrap()) + { + // log::debug!("{:?}", cppcheck_results); + for error in cppcheck_results.errors.error { + if let Some(issue_code) = cppcheck::mapping(&error.id) { + let Some(location) = error.location.as_ref().and_then(|l| l.get(0)) else { + continue; + }; + if !files_set.contains(&PathBuf::from(&location.file)) { + continue; + } + let issue_text = if error.msg.starts_with("misra") { + format!( + "{} {}", + error.id, + error.symbol.unwrap_or_else(|| "".to_string()) + ) + } else { + error.msg + }; + issue_occurrences.push(result::Issue { + issue_text, + issue_code, + location: result::Location { + path: location.file.clone(), + position: result::Position { + begin: result::Mark { + line: location.line, + column: location.column, + }, + end: result::Mark { + line: location.line, + column: location.column, + }, + }, + }, + }); + } + } + } + + let json_output = serde_json::to_string(&issue_occurrences); + log::debug!( + "{}", + json_output.as_ref().map(String::as_str).unwrap_or("{}") + ); + + let src = json_output.unwrap_or_else(|err| { + log::error!("{err}"); + "{}".to_string() + }); + + let result_json = toolbox_directory.join("cppcheck_result.json"); + std::fs::write(result_json, src)?; + + Ok(()) +} diff --git a/src/result.rs b/src/result.rs new file mode 100644 index 0000000..3217978 --- /dev/null +++ b/src/result.rs @@ -0,0 +1,27 @@ +#![allow(dead_code)] +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +pub struct Mark { + pub line: u32, + pub column: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Position { + pub begin: Mark, + pub end: Mark, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Location { + pub path: String, + pub position: Position, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Issue { + pub issue_text: String, + pub issue_code: String, + pub location: Location, +}