├── .codeclimate.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc.base.json ├── .eslintrc.yml ├── .github ├── CODEOWNERS ├── release.yml └── workflows │ ├── automerge.yaml │ └── cicd.yml ├── .gitignore ├── .mdlrc ├── .mdlrc.style ├── .reuse └── dep5 ├── .vscode ├── cSpell.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── C-Cpp-FlyLint_icon.png ├── C-Cpp-FlyLint_icon.svg ├── CHANGELOG.md ├── LICENSE ├── LICENSES └── MIT.txt ├── README.md ├── client ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── extension.ts │ ├── flylintLanguageClient.ts │ └── stateUtils.ts ├── tsconfig.json └── webpack.config.js ├── code-climate.sh ├── package-lock.json ├── package.json ├── server ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .vscode │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── customInstallServerIntoExtension.js ├── package-lock.json ├── package.json ├── src │ ├── linters │ │ ├── clang.ts │ │ ├── cppcheck.ts │ │ ├── flawfinder.ts │ │ ├── flexelint.ts │ │ ├── linter.ts │ │ ├── lizard.ts │ │ └── pclintplus.ts │ ├── server.ts │ ├── settings.ts │ └── utils.ts ├── tsconfig.json └── webpack.config.js ├── shared.webpack.config.js ├── specs ├── .eslintrc.json ├── debug-console │ ├── colorize.ts │ └── logger.ts ├── index.ts ├── mock-config.ts ├── mock-fs.ts ├── runSpecs.ts ├── setup.ts ├── suite │ ├── fixtures │ │ ├── c_cpp_properties.json │ │ └── c_cpp_properties │ │ │ ├── .vscode │ │ │ ├── c_cpp_properties.json │ │ │ ├── settings.json │ │ │ └── tasks.json │ │ │ ├── c_cpp_properties.c │ │ │ └── inc │ │ │ ├── a │ │ │ ├── aa │ │ │ │ └── .gitkeep │ │ │ ├── bb │ │ │ │ └── .gitkeep │ │ │ └── cc │ │ │ │ └── .gitkeep │ │ │ ├── b │ │ │ ├── aa │ │ │ │ └── .gitkeep │ │ │ ├── bb │ │ │ │ └── .gitkeep │ │ │ └── cc │ │ │ │ └── .gitkeep │ │ │ ├── c │ │ │ ├── aa │ │ │ │ └── .gitkeep │ │ │ ├── bb │ │ │ │ └── .gitkeep │ │ │ └── cc │ │ │ │ └── .gitkeep │ │ │ └── d │ │ │ ├── aa │ │ │ └── .gitkeep │ │ │ ├── bb │ │ │ └── .gitkeep │ │ │ └── cc │ │ │ └── .gitkeep │ ├── linter-clang.spec.ts │ ├── linter-cppcheck.spec.ts │ ├── linter-flawfinder.spec.ts │ ├── linter-flexelint.spec.ts │ ├── linter-lizard.spec.ts │ ├── linter-pclintplus.spec.ts │ ├── linter.spec.ts │ ├── setup-vfs.spec.ts │ ├── unit-c_cpp_properties.spec.ts │ └── unit-path.spec.ts └── tsconfig.json ├── tsconfig.base.json ├── tsconfig.json └── typings.d.ts /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | 4 | plugins: 5 | duplication: 6 | enabled: true 7 | config: 8 | languages: 9 | - ruby 10 | - javascript 11 | - python 12 | - php 13 | eslint: 14 | enabled: true 15 | fixme: 16 | enabled: true 17 | markdownlint: 18 | enabled: true 19 | radon: 20 | enabled: true 21 | 22 | checks: 23 | argument-count: 24 | config: 25 | threshold: 4 26 | complex-logic: 27 | config: 28 | threshold: 4 29 | file-lines: 30 | config: 31 | threshold: 500 32 | method-complexity: 33 | config: 34 | threshold: 15 35 | method-count: 36 | config: 37 | threshold: 20 38 | method-lines: 39 | config: 40 | threshold: 50 41 | 42 | exclude_patterns: 43 | - "**/node_modules/**/*" 44 | - "**/out/**/*" 45 | - "**/dist/**/*" 46 | - "**/.vscode/**/*" 47 | - "**/.vscode-test/**/*" 48 | - "client/server/**/*" 49 | - "**/.git/**/*" 50 | - "**/webpack.config.js" 51 | - "**/tsconfig.json" 52 | - "**/customInstallServerIntoExtension.js" 53 | - "**/tsconfig.base.json" 54 | - "**/shared.webpack.config.js" 55 | 56 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # See: http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [package.json] 14 | indent_size = 2 15 | 16 | [tsconfig*] 17 | indent_style = tab 18 | 19 | [*webpack*] 20 | indent_style = tab 21 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | -------------------------------------------------------------------------------- /.eslintrc.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | }, 7 | "plugins": [ 8 | "@typescript-eslint" 9 | ], 10 | "env": { 11 | "node": true 12 | }, 13 | "rules": { 14 | "semi": "off", 15 | "@typescript-eslint/semi": "error", 16 | "no-extra-semi": "warn", 17 | "curly": "warn", 18 | "quotes": ["error", "single", { "allowTemplateLiterals": true } ], 19 | "eqeqeq": "error" 20 | } 21 | } -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parserOptions: 3 | sourceType: module 4 | ecmaFeatures: 5 | jsx: true 6 | 7 | env: 8 | amd: true 9 | browser: true 10 | es6: true 11 | jquery: true 12 | node: true 13 | 14 | # http://eslint.org/docs/rules/ 15 | rules: 16 | # Possible Errors 17 | no-await-in-loop: off 18 | no-cond-assign: error 19 | no-console: off 20 | no-constant-condition: error 21 | no-control-regex: error 22 | no-debugger: error 23 | no-dupe-args: error 24 | no-dupe-keys: error 25 | no-duplicate-case: error 26 | no-empty-character-class: error 27 | no-empty: error 28 | no-ex-assign: error 29 | no-extra-boolean-cast: error 30 | no-extra-parens: off 31 | no-extra-semi: error 32 | no-func-assign: error 33 | no-inner-declarations: 34 | - error 35 | - functions 36 | no-invalid-regexp: error 37 | no-irregular-whitespace: error 38 | no-negated-in-lhs: error 39 | no-obj-calls: error 40 | no-prototype-builtins: off 41 | no-regex-spaces: error 42 | no-sparse-arrays: error 43 | no-template-curly-in-string: off 44 | no-unexpected-multiline: error 45 | no-unreachable: error 46 | no-unsafe-finally: off 47 | no-unsafe-negation: off 48 | use-isnan: error 49 | valid-jsdoc: off 50 | valid-typeof: error 51 | 52 | # Best Practices 53 | accessor-pairs: error 54 | array-callback-return: off 55 | block-scoped-var: off 56 | class-methods-use-this: off 57 | complexity: 58 | - error 59 | - 6 60 | consistent-return: off 61 | curly: off 62 | default-case: off 63 | dot-location: off 64 | dot-notation: off 65 | eqeqeq: error 66 | guard-for-in: error 67 | no-alert: error 68 | no-caller: error 69 | no-case-declarations: error 70 | no-div-regex: error 71 | no-else-return: off 72 | no-empty-function: off 73 | no-empty-pattern: error 74 | no-eq-null: error 75 | no-eval: error 76 | no-extend-native: error 77 | no-extra-bind: error 78 | no-extra-label: off 79 | no-fallthrough: error 80 | no-floating-decimal: off 81 | no-global-assign: off 82 | no-implicit-coercion: off 83 | no-implied-eval: error 84 | no-invalid-this: off 85 | no-iterator: error 86 | no-labels: 87 | - error 88 | - allowLoop: true 89 | allowSwitch: true 90 | no-lone-blocks: error 91 | no-loop-func: error 92 | no-magic-number: off 93 | no-multi-spaces: off 94 | no-multi-str: off 95 | no-native-reassign: error 96 | no-new-func: error 97 | no-new-wrappers: error 98 | no-new: error 99 | no-octal-escape: error 100 | no-octal: error 101 | no-param-reassign: off 102 | no-proto: error 103 | no-redeclare: error 104 | no-restricted-properties: off 105 | no-return-assign: error 106 | no-return-await: off 107 | no-script-url: error 108 | no-self-assign: off 109 | no-self-compare: error 110 | no-sequences: off 111 | no-throw-literal: off 112 | no-unmodified-loop-condition: off 113 | no-unused-expressions: error 114 | no-unused-labels: off 115 | no-useless-call: error 116 | no-useless-concat: error 117 | no-useless-escape: off 118 | no-useless-return: off 119 | no-void: error 120 | no-warning-comments: off 121 | no-with: error 122 | prefer-promise-reject-errors: off 123 | radix: error 124 | require-await: off 125 | vars-on-top: off 126 | wrap-iife: error 127 | yoda: off 128 | 129 | # Strict 130 | strict: off 131 | 132 | # Variables 133 | init-declarations: off 134 | no-catch-shadow: error 135 | no-delete-var: error 136 | no-label-var: error 137 | no-restricted-globals: off 138 | no-shadow-restricted-names: error 139 | no-shadow: off 140 | no-undef-init: error 141 | no-undef: off 142 | no-undefined: off 143 | no-unused-vars: off 144 | no-use-before-define: off 145 | 146 | # Node.js and CommonJS 147 | callback-return: error 148 | global-require: error 149 | handle-callback-err: error 150 | no-mixed-requires: off 151 | no-new-require: off 152 | no-path-concat: error 153 | no-process-env: off 154 | no-process-exit: error 155 | no-restricted-modules: off 156 | no-sync: off 157 | 158 | # Stylistic Issues 159 | array-bracket-spacing: off 160 | block-spacing: off 161 | brace-style: off 162 | camelcase: off 163 | capitalized-comments: off 164 | comma-dangle: 165 | - error 166 | - never 167 | comma-spacing: off 168 | comma-style: off 169 | computed-property-spacing: off 170 | consistent-this: off 171 | eol-last: off 172 | func-call-spacing: off 173 | func-name-matching: off 174 | func-names: off 175 | func-style: off 176 | id-length: off 177 | id-match: off 178 | indent: off 179 | jsx-quotes: off 180 | key-spacing: off 181 | keyword-spacing: off 182 | line-comment-position: off 183 | linebreak-style: off 184 | lines-around-comment: off 185 | lines-around-directive: off 186 | max-depth: off 187 | max-len: off 188 | max-nested-callbacks: off 189 | max-params: off 190 | max-statements-per-line: off 191 | max-statements: 192 | - error 193 | - 30 194 | multiline-ternary: off 195 | new-cap: off 196 | new-parens: off 197 | newline-after-var: off 198 | newline-before-return: off 199 | newline-per-chained-call: off 200 | no-array-constructor: off 201 | no-bitwise: off 202 | no-continue: off 203 | no-inline-comments: off 204 | no-lonely-if: off 205 | no-mixed-operators: off 206 | no-mixed-spaces-and-tabs: off 207 | no-multi-assign: off 208 | no-multiple-empty-lines: off 209 | no-negated-condition: off 210 | no-nested-ternary: off 211 | no-new-object: off 212 | no-plusplus: off 213 | no-restricted-syntax: off 214 | no-spaced-func: off 215 | no-tabs: off 216 | no-ternary: off 217 | no-trailing-spaces: off 218 | no-underscore-dangle: off 219 | no-unneeded-ternary: off 220 | object-curly-newline: off 221 | object-curly-spacing: off 222 | object-property-newline: off 223 | one-var-declaration-per-line: off 224 | one-var: off 225 | operator-assignment: off 226 | operator-linebreak: off 227 | padded-blocks: off 228 | quote-props: off 229 | quotes: off 230 | require-jsdoc: off 231 | semi-spacing: off 232 | semi: off 233 | sort-keys: off 234 | sort-vars: off 235 | space-before-blocks: off 236 | space-before-function-paren: off 237 | space-in-parens: off 238 | space-infix-ops: off 239 | space-unary-ops: off 240 | spaced-comment: off 241 | template-tag-spacing: off 242 | unicode-bom: off 243 | wrap-regex: off 244 | 245 | # ECMAScript 6 246 | arrow-body-style: off 247 | arrow-parens: off 248 | arrow-spacing: off 249 | constructor-super: off 250 | generator-star-spacing: off 251 | no-class-assign: off 252 | no-confusing-arrow: off 253 | no-const-assign: off 254 | no-dupe-class-members: off 255 | no-duplicate-imports: off 256 | no-new-symbol: off 257 | no-restricted-imports: off 258 | no-this-before-super: off 259 | no-useless-computed-key: off 260 | no-useless-constructor: off 261 | no-useless-rename: off 262 | no-var: off 263 | object-shorthand: off 264 | prefer-arrow-callback: off 265 | prefer-const: off 266 | prefer-destructuring: off 267 | prefer-numeric-literals: off 268 | prefer-rest-params: off 269 | prefer-reflect: off 270 | prefer-spread: off 271 | prefer-template: off 272 | require-yield: off 273 | rest-spread-spacing: off 274 | sort-imports: off 275 | symbol-description: off 276 | template-curly-spacing: off 277 | yield-star-spacing: off 278 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @jbenden 2 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-for-release 5 | authors: 6 | - octocat 7 | categories: 8 | - title: Breaking Changes 🛠 9 | labels: 10 | - Semver-Major 11 | - breaking-change 12 | - title: Exciting New Features 🎉 13 | labels: 14 | - Semver-Minor 15 | - enhancement 16 | - title: Other Changes 17 | labels: 18 | - "*" 19 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yaml: -------------------------------------------------------------------------------- 1 | name: Automate Dependency Updates 2 | 3 | on: 4 | pull_request_target: 5 | paths: 6 | - package.json 7 | - package-lock.json 8 | 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | 13 | jobs: 14 | checks: 15 | name: Check 4 Changes 16 | runs-on: ubuntu-latest 17 | if: ${{ github.actor == 'dependabot[bot]' }} 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: actions/setup-node@v3 21 | - name: REUSE Compliance Check 22 | uses: fsfe/reuse-action@v1 23 | - run: npm ci 24 | - run: git diff --exit-code 25 | 26 | automerge: 27 | name: Auto Merge Pull Request 28 | runs-on: ubuntu-latest 29 | needs: checks 30 | if: ${{ github.actor == 'dependabot[bot]' }} 31 | steps: 32 | - name: automerge 33 | uses: pascalgn/automerge-action@v0.15.3 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | MERGE_LABELS: dependencies 37 | MERGE_METHOD: rebase 38 | -------------------------------------------------------------------------------- /.github/workflows/cicd.yml: -------------------------------------------------------------------------------- 1 | name: Build, Analyze, Test and Deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | release: 11 | types: 12 | - published 13 | schedule: 14 | - cron: "30 5 * * 1" 15 | 16 | env: 17 | CC_TEST_REPORTER_ID: 841d8434027b80ebe04ecbf7823df79314cbda58af076e88bdc7c53693c7640b 18 | 19 | jobs: 20 | legal: 21 | name: REUSE Specification 22 | runs-on: ubuntu-latest 23 | timeout-minutes: 10 24 | steps: 25 | - name: REUSE Compliance Check 26 | uses: fsfe/reuse-action@v1 27 | 28 | build-lint-test: 29 | name: Node ${{ matrix.node }} on ${{ matrix.os }} 30 | runs-on: ${{ matrix.os }} 31 | needs: legal 32 | timeout-minutes: 10 33 | strategy: 34 | fail-fast: true 35 | matrix: 36 | os: 37 | - ubuntu-latest 38 | - macos-latest 39 | # - windows-latest 40 | node: 41 | - 16 42 | - 18 43 | outputs: 44 | vsixPath: ${{ steps.packageExtension.outputs.vsixPath }} 45 | steps: 46 | - name: Setup GIT for Windows 47 | run: | 48 | git config --global core.autocrlf false 49 | git config --global core.eol lf 50 | if: runner.os == 'Windows' 51 | - name: Ensure xvfb package exists for Linux 52 | run: sudo apt install -yqq xvfb 53 | if: runner.os == 'Linux' 54 | - name: Checkout Source 55 | uses: actions/checkout@v3 56 | - name: Install Node ${{ matrix.node }} 57 | uses: actions/setup-node@v3 58 | with: 59 | node-version: ${{ matrix.node }} 60 | - name: Install Dependencies 61 | run: npm ci 62 | - name: Run Linter 63 | run: npm run lint 64 | - name: Build Code 65 | run: npm run compile 66 | - name: Run Unit Tests on Windows or macOS 67 | run: npm run test 68 | if: runner.os != 'Linux' 69 | - name: Run Unit Tests on Linux 70 | run: xvfb-run "--server-args=-screen 0 1024x768x24" -a npm test 71 | if: runner.os == 'Linux' 72 | - name: Update Coverage Report to Code Climate 73 | run: | 74 | if test `uname -s` = "Linux"; then 75 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 76 | else 77 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-darwin-amd64 > ./cc-test-reporter 78 | fi 79 | chmod +x ./cc-test-reporter 80 | ./cc-test-reporter format-coverage -t lcov -o coverage/codeclimate.${{ matrix.os }}-node${{ matrix.node }}.json coverage/lcov.info 81 | cp -vp coverage/codeclimate.${{ matrix.os }}-node${{ matrix.node }}.json . 82 | - name: Upload Coverage as Artifact 83 | uses: actions/upload-artifact@v2 84 | with: 85 | name: coverage-${{ matrix.os }}-node${{ matrix.node }} 86 | path: codeclimate.${{ matrix.os }}-node${{ matrix.node }}.json 87 | - name: Upload Coverage Report to Coveralls 88 | uses: coverallsapp/github-action@v1.2.5 89 | with: 90 | github-token: ${{ secrets.GITHUB_TOKEN }} 91 | parallel: true 92 | flag-name: Test node ${{ matrix.node }} on ${{ matrix.os }} 93 | - name: Package Extension 94 | id: packageExtension 95 | uses: HaaLeo/publish-vscode-extension@v1 96 | with: 97 | pat: stub 98 | dryRun: true 99 | - name: Upload Extension Package as Artifact 100 | uses: actions/upload-artifact@v2 101 | with: 102 | name: ${{ matrix.os }} 103 | path: ${{ steps.packageExtension.outputs.vsixPath }} 104 | 105 | coverage: 106 | name: Code Coverage 107 | runs-on: ubuntu-latest 108 | timeout-minutes: 10 109 | needs: build-lint-test 110 | steps: 111 | - name: Let Coveralls know that all tests have finished 112 | uses: coverallsapp/github-action@v1.2.5 113 | with: 114 | github-token: ${{ secrets.GITHUB_TOKEN }} 115 | parallel-finished: true 116 | - name: Download Build Artifact 117 | uses: actions/download-artifact@v2 118 | - name: Update the Coverage Report on Code Climate 119 | run: | 120 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 121 | chmod +x ./cc-test-reporter 122 | ./cc-test-reporter sum-coverage coverage*/codeclimate.*.json -p 4 123 | ./cc-test-reporter upload-coverage 124 | 125 | publish: 126 | name: Publish 127 | needs: 128 | - build-lint-test 129 | - coverage 130 | if: github.event_name == 'release' 131 | runs-on: ubuntu-latest 132 | timeout-minutes: 10 133 | steps: 134 | - name: Checkout Source 135 | uses: actions/checkout@v3 136 | - name: REUSE Compliance Check 137 | uses: fsfe/reuse-action@v1 138 | - name: Install Node v16 139 | uses: actions/setup-node@v1 140 | with: 141 | node-version: 16 142 | - name: Install Dependencies 143 | run: npm ci 144 | - name: Download Build Artifact 145 | uses: actions/download-artifact@v2 146 | with: 147 | name: ubuntu-latest 148 | - name: GitHub Release 149 | uses: softprops/action-gh-release@v1 150 | with: 151 | generate_release_notes: true 152 | discussion_category_name: Announcements 153 | fail_on_unmatched_files: true 154 | files: ${{ needs.build-lint-test.outputs.vsixPath }} 155 | - name: Publish to Visual Studio Marketplace 156 | uses: HaaLeo/publish-vscode-extension@v1 157 | with: 158 | pat: ${{ secrets.VSCE_TOKEN }} 159 | extensionFile: ${{ needs.build-lint-test.outputs.vsixPath }} 160 | registryUrl: https://marketplace.visualstudio.com 161 | - name: Publish to Open VSX Registry 162 | uses: HaaLeo/publish-vscode-extension@v1 163 | with: 164 | pat: ${{ secrets.OPEN_VSX_TOKEN }} 165 | extensionFile: ${{ needs.build-lint-test.outputs.vsixPath }} 166 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /scratchfile.* 2 | /*.log 3 | node_modules 4 | /dist 5 | **/out/* 6 | /*.patch 7 | /cppcheck-* 8 | *.vsix 9 | *.pdf 10 | /.vscode-test 11 | /coverage 12 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | style "./.mdlrc.style" 2 | -------------------------------------------------------------------------------- /.mdlrc.style: -------------------------------------------------------------------------------- 1 | all 2 | exclude_rule 'MD024' 3 | rule 'MD046', :style => :indented 4 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: C/C++ Flylint 3 | Upstream-Contact: Joseph Benden 4 | Source: https://github.com/jbenden/vscode-flylint.git 5 | 6 | Files: C-Cpp-FlyLint_icon.svg 7 | C-Cpp-FlyLint_icon.png 8 | package.json package-lock.json */package.json */package-lock.json 9 | */.eslintignore 10 | */.gitignore 11 | */tsconfig.json tsconfig.json tsconfig.base.json 12 | CHANGELOG.md 13 | .gitignore 14 | .mdlrc 15 | .codeclimate.yml 16 | .editorconfig 17 | .eslintignore 18 | .eslintrc.yml 19 | .github/workflows/* 20 | .vscode/* 21 | .eslintrc.base.json 22 | .vscodeignore 23 | README.md 24 | */.eslintrc.json 25 | */.vscode/*.json 26 | */README.md 27 | specs/suite/fixtures/c_cpp_properties.json 28 | */webpack* shared.webpack.config.js 29 | server/src/linters/* 30 | .github/CODEOWNERS 31 | .github/release.yml 32 | Copyright: 2017-2022 The VSCode C/C++ Flylint Authors 33 | License: MIT 34 | -------------------------------------------------------------------------------- /.vscode/cSpell.json: -------------------------------------------------------------------------------- 1 | // cSpell Settings 2 | { 3 | // Version of the setting file. Always 0.1 4 | "version": "0.1", 5 | // language - current active spelling language 6 | "language": "en", 7 | // words - list of words to be always considered correct 8 | "words": [ 9 | "Flexelint", 10 | "flylint" 11 | ], 12 | // flagWords - list of words to be always considered incorrect 13 | // This is useful for offensive words and common spelling errors. 14 | // For example "hte" should be "the" 15 | "flagWords": [ 16 | "hte" 17 | ] 18 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "type": "pwa-extensionHost", 7 | "request": "launch", 8 | "name": "Launch Client", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceFolder}" ], 11 | "sourceMaps": true, 12 | "outFiles": ["${workspaceFolder}/client/out/**/*.js"], 13 | "preLaunchTask": "npm: compile" 14 | }, 15 | { 16 | "type": "node", 17 | "request": "attach", 18 | "name": "Attach to Server", 19 | "address": "localhost", 20 | "protocol": "inspector", 21 | "port": 6011, 22 | "timeout": 60000, 23 | "sourceMaps": true, 24 | "outFiles": ["${workspaceFolder}/server/out/**/*.js"] 25 | }, 26 | { 27 | "name": "Launch Mocha Tests", 28 | "type": "node", 29 | "request": "launch", 30 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/mocha", 31 | "args": [ 32 | "--no-timeouts", 33 | "--colors", 34 | "${workspaceFolder}/server/out/linters/tests/*.test.js" 35 | ], 36 | "env": { 37 | "TRAVIS": "true" 38 | }, 39 | "internalConsoleOptions": "openOnSessionStart", 40 | "stopOnEntry": false, 41 | "sourceMaps": true, 42 | "outFiles": [ "${workspaceFolder}/server/out/**/*.js" ], 43 | "preLaunchTask": "npm: compile" 44 | } 45 | ], 46 | "compounds": [ 47 | { 48 | "name": "Client + Server", 49 | "configurations": ["Launch Client", "Attach to Server"] 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false 5 | }, 6 | "search.exclude": { 7 | "out": true, 8 | "server": true 9 | }, 10 | "files.trimTrailingWhitespace": true, 11 | "editor.insertSpaces": true, 12 | "editor.tabSize": 4, 13 | "typescript.tsdk": "./node_modules/typescript/lib", 14 | "typescript.tsc.autoDetect": "off", 15 | "typescript.tsserver.trace": "off", 16 | "typescript.tsserver.log": "off", 17 | "tslint.enable": false, 18 | "eslint.enable": true, 19 | "editor.codeActionsOnSave": { 20 | "source.fixAll.eslint": true 21 | }, 22 | "editor.codeActionsOnSaveTimeout": 2000, 23 | "eslint.workingDirectories": [ 24 | "./client", 25 | "./server" 26 | ], 27 | "eslint.trace.server": "off", 28 | "eslint.debug": false, 29 | "eslint.lintTask.enable": false, 30 | "eslint.format.enable": true, 31 | "markdownlint.config": { 32 | "MD024": false 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "isBackground": true, 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "presentation": { 15 | "reveal": "never", 16 | "panel": "dedicated" 17 | }, 18 | "problemMatcher": [ 19 | "$tsc-watch" 20 | ] 21 | }, 22 | { 23 | "type": "npm", 24 | "script": "compile", 25 | "isBackground": false, 26 | "group": "build", 27 | "presentation": { 28 | "reveal": "never", 29 | "panel": "dedicated" 30 | }, 31 | "problemMatcher": [ 32 | "$tsc" 33 | ] 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .github/** 2 | .gitignore 3 | .log/** 4 | .vscode/** 5 | .vscode-test/** 6 | yarn.lock 7 | **/*.test.js 8 | **/test_*.js 9 | **/*.map 10 | src/** 11 | tsconfig.json 12 | tsconfig.base.json 13 | *.vsix 14 | server/** 15 | !server/out/**/*.js 16 | client/** 17 | !client/out/**/*.js 18 | webpack.config.js 19 | shared.webpack.config.js 20 | **/*.sh 21 | .codeclimate.yml 22 | .eslintignore 23 | .mdlrc 24 | node_modules/** 25 | *.svg 26 | *.pdf 27 | cppcheck-* 28 | *.patch 29 | -------------------------------------------------------------------------------- /C-Cpp-FlyLint_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/C-Cpp-FlyLint_icon.png -------------------------------------------------------------------------------- /C-Cpp-FlyLint_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | C/C++ FlyLintimage/svg+xmlC/C++ FlyLintvscodeIcon for the vscode-c-cpp-flylint extension for VS Code.fly 96 | 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Joseph Benden 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

C/C++ Advanced Lint for VS Code

4 | 5 |
6 | 7 | [![Code Climate](https://codeclimate.com/github/jbenden/vscode-c-cpp-flylint/badges/gpa.png)](https://codeclimate.com/github/jbenden/vscode-c-cpp-flylint) 8 | 9 |
10 | 11 | A [Visual Studio Code](https://code.visualstudio.com/) extension 12 | supporting a number of static code analyzers for C and C++ code. 13 | 14 |
15 | 16 | ## Features 17 | 18 | * On-the-fly linting within the code editor, upon file save or after 19 | file edits. 20 | * Automatically finds available static analysis tools. 21 | * Easily supports additional static analyzers with minimum 22 | development effort. 23 | 24 | ## Supported Static Analyzers 25 | 26 | * [Clang](https://clang.llvm.org/) 27 | * [CppCheck](http://cppcheck.sourceforge.net/) 28 | * [FlawFinder](https://dwheeler.com/flawfinder/) 29 | * [PC-lint Plus](https://gimpel.com/) 30 | * [Flexelint](http://www.gimpel.com/html/flex.htm) or 31 | [PC-lint](http://www.gimpel.com/html/pcl.htm) 32 | * [lizard](https://github.com/terryyin/lizard) 33 | 34 | ## Requirements 35 | 36 | At least one of the above static code analyzers must be installed 37 | on your machine(s). 38 | 39 | The extension should support any versions of the listed static code 40 | analyzers; and will attempt to locate them within your `PATH` 41 | environment variable. 42 | 43 | If a tool is not automatically found, the appropriate 44 | `c-cpp-flylint.*.executable` configuration must be specified manually. 45 | 46 |
47 | Debian & Ubuntu 48 |
49 | 50 | Clang is available via `apt-get`: 51 | 52 | # sudo apt-get install clang 53 | 54 | CppCheck is available via `apt-get`: 55 | 56 | # sudo apt-get install cppcheck 57 | 58 | Flexelint is commercial software; however, it may be obtained from 59 | the URL mentioned elsewhere in this documentation. 60 | 61 | PC-lint and PC-lint Plus are commercial software; however, they may 62 | be obtained from the URL mentioned elsewhere in this documentation. 63 | 64 | FlawFinder is available via `pip`: 65 | 66 | # sudo pip install flawfinder 67 | 68 | lizard is available via `pip`: 69 | 70 | # sudo pip install lizard 71 | 72 |
73 | 74 |
75 | macOS 76 |
77 | 78 | For macOS users, Clang is already included when Xcode and its' CLI 79 | tools are installed. 80 | 81 | For macOS users, CppCheck can most easily be installed through 82 | [Homebrew](https://brew.sh/). 83 | 84 | # brew install cppcheck 85 | 86 | Flexelint is commercial software; however, it may be obtained from 87 | the URL mentioned elsewhere in this documentation. 88 | 89 | PC-lint and PC-lint Plus are commercial software; however, they may 90 | be obtained from the URL mentioned elsewhere in this documentation. 91 | 92 |
93 | 94 |
95 | Windows 96 |
97 | 98 | Windows users may download and install the static code analyzers 99 | from the listed URLs mentioned elsewhere in this documentation. 100 | 101 | If PC-lint has been installed, be certain to use the `Flexelint` 102 | configuration sections, specifying the full path and filename 103 | of PC-lint as the `c-cpp-flylint.flexelint.executable` 104 | configuration option. 105 | 106 |
107 | 108 | ## Usage 109 | 110 | Once all requirements are met, the extension may be installed through 111 | one of the available marketplaces: 112 | 113 | * [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=jbenden.c-cpp-flylint) 114 | * [Open-VSX](https://open-vsx.org/extension/jbenden/c-cpp-flylint) 115 | 116 | After the extension is installed, one must then decide on how to best 117 | implement the necessary configuration changes to best meet their project 118 | and/or environment needs. For instance, would `cppcheck` best be 119 | configured globally, for all projects; or configured for a whole workspace; 120 | or configured for a specific project. 121 | 122 | Once an above choice is made, open the appropriate configuration 123 | window. See VSCode documentation for help on accessing user, 124 | workspace, and project configuration windows. 125 | 126 | It is then recommended to narrow in to the extension's configuration; 127 | to view, and decide upon each and every setting. Start with enabling 128 | the linters desired and disabling those not, along with mapping any 129 | necessary build/compiler flags, as needed by most of the linters. 130 | 131 | > It is a huge help if the linters being configured are in working 132 | > order on the command-line, prior to an attempt at configuring 133 | > the extension within VSCode. 134 | 135 | ### Security 136 | 137 | This extension runs a few third-party command-line tools found from the 138 | locations determined by the `PATH` or `Path` environment variable, and 139 | the settings such as `"c-cpp-flylint.clang.executable"` or 140 | `"c-cpp-flylint.cppcheck.executable"`. Configuring them in workspace 141 | settings allows users to conveniently select a different set of tools 142 | based on project's need, but also allows attackers to run arbitrary 143 | binaries on your machine if they successfully convince you to open a 144 | random repository. In order to reduce the security risk, this extension 145 | reads the settings from user settings, by default. If the repository can 146 | be trusted and workspace settings must be used, you can mark the 147 | workspace as a trusted workspace using the 148 | `"C/C++ Flylint: Toggle Workspace Trust Flag"` command. 149 | 150 | ### Configuration Settings 151 | 152 | Due to the large quantity of configuration options -- in tandem with the 153 | ever growing number of supported static code analyzers -- all 154 | configuration options are not documented here. 155 | 156 | However, every configuration option is well documented within 157 | **File** -> **Preferences** -> **Settings** [alternatively, one of the 158 | keybindings: Command+, or Ctrl+,]. 159 | 160 | ## Development Setup 161 | 162 | * run `npm install` inside the project root 163 | 164 | ### Developing the Server 165 | 166 | * open VS Code rooted inside the project root. 167 | * run `cd server && npm run test && cd ..` to execute the unit-tests for all linters. 168 | * run `npm run compile` or `npm run watch` to build the server 169 | and it will compile it into the `client/out` folder. 170 | * to debug press F5 which attaches a debugger to the server. 171 | 172 | ### Developing the Extension/Client 173 | 174 | * open VS Code rooted inside the project root. 175 | * run F5 to build and debug the whole (client with the 176 | server) extension. 177 | 178 | ## Project details 179 | 180 | Both the source code and issue tracker are hosted at 181 | [GitHub](https://github.com/jbenden/vscode-c-cpp-flylint/). 182 | 183 | For support purposes, please visit the above URL and select 184 | from the Issue and/or Pull Request areas. 185 | 186 | ## License 187 | 188 | Copyright (C) 2017-2022 The VSCode C/C++ Flylint Authors. 189 | 190 | Licensed under the [MIT License](https://opensource.org/licenses/MIT). We check 191 | with support for the [REUSE](https://reuse.software/spec/) specification! 192 | -------------------------------------------------------------------------------- /client/.eslintignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules -------------------------------------------------------------------------------- /client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "../.eslintrc.base.json", 4 | "rules": { 5 | } 6 | } -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | ## 2 | # IMPORTANT: Do not use this file to ignore IDE-specific project configuration files. Ignore them locally instead. 3 | # (see http://stackoverflow.com/questions/1753070/git-ignore-files-only-locally) 4 | ## 5 | 6 | /.vscode-test/ 7 | /node_modules/ 8 | /out/ 9 | /server/ 10 | yarn.lock 11 | *.vsix 12 | -------------------------------------------------------------------------------- /client/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"], 11 | "stopOnEntry": false, 12 | "sourceMaps": true, 13 | "outFiles": [ 14 | "${workspaceRoot}/out" 15 | ], 16 | "preLaunchTask": "yarn" 17 | }, 18 | { 19 | "name": "Launch Tests", 20 | "type": "extensionHost", 21 | "request": "launch", 22 | "runtimeExecutable": "${execPath}", 23 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/server/linters/tests" ], 24 | "stopOnEntry": false, 25 | "sourceMaps": true, 26 | "outFiles": [ "${workspaceRoot}/out" ], 27 | "preLaunchTask": "yarn" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /client/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${fileBasename}: the current opened file's basename 5 | // ${fileDirname}: the current opened file's dirname 6 | // ${fileExtname}: the current opened file's extension 7 | // ${cwd}: the current working directory of the spawned process 8 | 9 | // A task runner that calls a custom npm script that compiles the extension. 10 | { 11 | "version": "0.1.0", 12 | 13 | // we want to run npm 14 | "command": "yarn", 15 | 16 | // the command is a shell script 17 | "isShellCommand": true, 18 | 19 | // show the output window only if unrecognized errors occur. 20 | "showOutput": "silent", 21 | 22 | // we run the custom script "compile" as defined in package.json 23 | "args": ["run", "compile"], 24 | 25 | // The tsc compiler is started in watching mode 26 | "isBackground": false, 27 | 28 | // use the standard tsc in watch mode problem matcher to find compile problems in the output. 29 | "problemMatcher": "$tsc" 30 | } 31 | -------------------------------------------------------------------------------- /client/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Joseph Benden 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

C/C++ Advanced Lint for VS Code

4 | 5 |
6 | 7 | [![Version](https://vsmarketplacebadge.apphb.com/version-short/jbenden.c-cpp-flylint.svg)](https://marketplace.visualstudio.com/items?itemName=jbenden.c-cpp-flylint) 8 | [![Installs](https://vsmarketplacebadge.apphb.com/installs-short/jbenden.c-cpp-flylint.svg)](https://marketplace.visualstudio.com/items?itemName=jbenden.c-cpp-flylint) 9 | [![Rating](https://vsmarketplacebadge.apphb.com/rating-short/jbenden.c-cpp-flylint.svg)](https://marketplace.visualstudio.com/items?itemName=jbenden.c-cpp-flylint) 10 | [![Build Status](https://github.com/jbenden/vscode-c-cpp-flylint/workflows/CI/badge.svg?branch=main)](https://github.com/jbenden/vscode-c-cpp-flylint/actions) 11 | [![Code Climate](https://codeclimate.com/github/jbenden/vscode-c-cpp-flylint/badges/gpa.svg)](https://codeclimate.com/github/jbenden/vscode-c-cpp-flylint) 12 | 13 |
14 | 15 | A [Visual Studio Code](https://code.visualstudio.com/) extension 16 | supporting a number of static code analyzers for C and C++ code. 17 | 18 |
19 | 20 | ## Features 21 | 22 | * On-the-fly linting within the code editor, upon file save or after 23 | file edits. 24 | * Automatically finds available static analysis tools. 25 | * Easily supports additional static analyzers with minimum 26 | development effort. 27 | 28 | ## Supported Static Analyzers 29 | 30 | * [Clang](https://clang.llvm.org/) 31 | * [CppCheck](http://cppcheck.sourceforge.net/) 32 | * [FlawFinder](https://dwheeler.com/flawfinder/) 33 | * [PC-lint Plus](https://gimpel.com/) 34 | * [Flexelint](http://www.gimpel.com/html/flex.htm) or 35 | [PC-lint](http://www.gimpel.com/html/pcl.htm) 36 | * [lizard](https://github.com/terryyin/lizard) 37 | 38 | ## Requirements 39 | 40 | At least one of the above static code analyzers must be installed 41 | on your machine(s). 42 | 43 | The extension should support any versions of the listed static code 44 | analyzers; and will attempt to locate them within your `PATH` 45 | environment variable. 46 | 47 | If a tool is not automatically found, the appropriate 48 | `c-cpp-flylint.*.executable` configuration must be specified manually. 49 | 50 |
51 | Debian & Ubuntu 52 |
53 | 54 | Clang is available via `apt-get`: 55 | 56 | # sudo apt-get install clang 57 | 58 | CppCheck is available via `apt-get`: 59 | 60 | # sudo apt-get install cppcheck 61 | 62 | Flexelint is commercial software; however, it may be obtained from 63 | the URL mentioned elsewhere in this documentation. 64 | 65 | PC-lint and PC-lint Plus are commercial software; however, they may 66 | be obtained from the URL mentioned elsewhere in this documentation. 67 | 68 | FlawFinder is available via `pip`: 69 | 70 | # sudo pip install flawfinder 71 | 72 | lizard is available via `pip`: 73 | 74 | # sudo pip install lizard 75 | 76 |
77 | 78 |
79 | macOS 80 |
81 | 82 | For macOS users, Clang is already included when Xcode and its' CLI 83 | tools are installed. 84 | 85 | For macOS users, CppCheck can most easily be installed through 86 | [Homebrew](https://brew.sh/). 87 | 88 | # brew install cppcheck 89 | 90 | Flexelint is commercial software; however, it may be obtained from 91 | the URL mentioned elsewhere in this documentation. 92 | 93 | PC-lint and PC-lint Plus are commercial software; however, they may 94 | be obtained from the URL mentioned elsewhere in this documentation. 95 | 96 |
97 | 98 |
99 | Windows 100 |
101 | 102 | Windows users may download and install the static code analyzers 103 | from the listed URLs mentioned elsewhere in this documentation. 104 | 105 | If PC-lint has been installed, be certain to use the `Flexelint` 106 | configuration sections, specifying the full path and filename 107 | of PC-lint as the `c-cpp-flylint.flexelint.executable` 108 | configuration option. 109 | 110 |
111 | 112 | ## Usage 113 | 114 | Once all requirements are met, the extension may be installed through 115 | one of the available marketplaces: 116 | 117 | * [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=jbenden.c-cpp-flylint) 118 | * [Open-VSX](https://open-vsx.org/extension/jbenden/c-cpp-flylint) 119 | 120 | After the extension is installed, one must then decide on how to best 121 | implement the necessary configuration changes to best meet their project 122 | and/or environment needs. For instance, would `cppcheck` best be 123 | configured globally, for all projects; or configured for a whole workspace; 124 | or configured for a specific project. 125 | 126 | Once an above choice is made, open the appropriate configuration 127 | window. See VSCode documentation for help on accessing user, 128 | workspace, and project configuration windows. 129 | 130 | It is then recommended to narrow in to the extension's configuration; 131 | to view, and decide upon each and every setting. Start with enabling 132 | the linters desired and disabling those not, along with mapping any 133 | necessary build/compiler flags, as needed by most of the linters. 134 | 135 | > It is a huge help if the linters being configured are in working 136 | > order on the command-line, prior to an attempt at configuring 137 | > the extension within VSCode. 138 | 139 | ### Security 140 | 141 | This extension runs a few third-party command-line tools found from the 142 | locations determined by the `PATH` or `Path` environment variable, and 143 | the settings such as `"c-cpp-flylint.clang.executable"` or 144 | `"c-cpp-flylint.cppcheck.executable"`. Configuring them in workspace 145 | settings allows users to conveniently select a different set of tools 146 | based on project's need, but also allows attackers to run arbitrary 147 | binaries on your machine if they successfully convince you to open a 148 | random repository. In order to reduce the security risk, this extension 149 | reads the settings from user settings, by default. If the repository can 150 | be trusted and workspace settings must be used, you can mark the 151 | workspace as a trusted workspace using the 152 | `"C/C++ Flylint: Toggle Workspace Trust Flag"` command. 153 | 154 | ### Configuration Settings 155 | 156 | Due to the large quantity of configuration options -- in tandem with the 157 | ever growing number of supported static code analyzers -- all 158 | configuration options are not documented here. 159 | 160 | However, every configuration option is well documented within 161 | **File** -> **Preferences** -> **Settings** [alternatively, one of the 162 | keybindings: Command+, or Ctrl+,]. 163 | 164 | ## Development Setup 165 | 166 | * run `npm install` inside the project root 167 | 168 | ### Developing the Server 169 | 170 | * open VS Code rooted inside the project root. 171 | * run `cd server && npm run test && cd ..` to execute the unit-tests for all linters. 172 | * run `npm run compile` or `npm run watch` to build the server 173 | and it will compile it into the `client/out` folder. 174 | * to debug press F5 which attaches a debugger to the server. 175 | 176 | ### Developing the Extension/Client 177 | 178 | * open VS Code rooted inside the project root. 179 | * run F5 to build and debug the whole (client with the 180 | server) extension. 181 | 182 | ## Project details 183 | 184 | Both the source code and issue tracker are hosted at 185 | [GitHub](https://github.com/jbenden/vscode-c-cpp-flylint/). 186 | 187 | For support purposes, please visit the above URL and select 188 | from the Issue and/or Pull Request areas. 189 | 190 | ## License 191 | 192 | Copyright (C) 2017-2021 [Joseph Benden](mailto:joe@benden.us). 193 | 194 | Licensed under the [MIT License](https://opensource.org/licenses/MIT). 195 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "c-cpp-flylint", 3 | "private": "true", 4 | "displayName": "C/C++ Advanced Lint", 5 | "description": "An advanced, modern, static analysis extension for C/C++ that supports a number of back-end analyzer programs.", 6 | "author": { 7 | "name": "Joseph Benden", 8 | "email": "joe@benden.us", 9 | "url": "http://benden.us/" 10 | }, 11 | "publisher": "jbenden", 12 | "version": "1.15.0", 13 | "license": "MIT", 14 | "icon": "C-Cpp-FlyLint_icon.png", 15 | "galleryBanner": { 16 | "color": "#303f9f", 17 | "theme": "dark" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/jbenden/vscode-c-cpp-flylint.git" 22 | }, 23 | "bugs": { 24 | "url": "https://github.com/jbenden/vscode-c-cpp-flylint/issues", 25 | "email": "joe@benden.us" 26 | }, 27 | "homepage": "https://github.com/jbenden/vscode-c-cpp-flylint/blob/main/README.md", 28 | "engines": { 29 | "vscode": "^1.52.0" 30 | }, 31 | "devDependencies": { 32 | "@types/mock-fs": "^4.13.0", 33 | "@types/node": "^13.13.40", 34 | "@types/vscode": "^1.52.0", 35 | "mock-fs": "^4.13.0", 36 | "source-map-support": "^0.5.16", 37 | "ts-loader": "^9.4.1", 38 | "typescript": "^4.1.2", 39 | "vscode-test": "^1.5.0", 40 | "webpack": "^5.76.0", 41 | "webpack-cli": "^4.2.0" 42 | }, 43 | "dependencies": { 44 | "cross-spawn": "^7.0.3", 45 | "fast-glob": "^3.2.5", 46 | "lodash": "^4.17.21", 47 | "slash": "^3.0.0", 48 | "tmp": "^0.2.1", 49 | "unixify": "^1.0.0", 50 | "var-expansion": "^0.1.0", 51 | "vscode-languageclient": "^7.0.0", 52 | "vscode-uri": "^3.0.2", 53 | "which": "^2.0.2" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /client/src/extension.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { TextDocument, commands, env, ExtensionContext, window, workspace, tasks, TaskEndEvent, TaskGroup, Uri, WorkspaceConfiguration } from 'vscode'; 6 | import { 7 | LanguageClientOptions, 8 | ServerOptions, 9 | SettingMonitor, 10 | TransportKind, 11 | } from 'vscode-languageclient/node'; 12 | import * as vscode from 'vscode'; 13 | import { FlylintLanguageClient } from './flylintLanguageClient'; 14 | import * as path from 'path'; 15 | import { getFromWorkspaceState, resetWorkspaceState, setWorkspaceState, updateWorkspaceState } from './stateUtils'; 16 | import { isBoolean } from 'lodash'; 17 | 18 | const FLYLINT_ID: string = 'c-cpp-flylint'; 19 | 20 | const WORKSPACE_IS_TRUSTED_KEY = 'WORKSPACE_IS_TRUSTED_KEY'; 21 | const SECURITY_SENSITIVE_CONFIG: string[] = [ 22 | 'clang.executable', 23 | 'cppcheck.executable', 24 | 'flexelint.executable', 25 | 'flawfinder.executable', 26 | 'lizard.executable', 27 | 'pclintplus.executable', 28 | 'queryUrlBase' 29 | ]; 30 | let IS_TRUSTED: boolean = false; 31 | 32 | export async function maybeWorkspaceIsTrusted(ctx: ExtensionContext) { 33 | if (workspace.hasOwnProperty('isTrusted') && workspace.hasOwnProperty('isTrusted') !== null) { 34 | const workspaceIsTrusted = (workspace as any)['isTrusted']; 35 | console.log(`Workspace has property "isTrusted". It has the value of "${workspaceIsTrusted}".`); 36 | if (isBoolean(workspaceIsTrusted) && workspaceIsTrusted) { 37 | IS_TRUSTED = true; 38 | console.log(`Workspace was marked trusted, by user of VSCode.`); 39 | } else { 40 | IS_TRUSTED = false; 41 | console.log(`Workspace is not trusted!`); 42 | } 43 | return; 44 | } 45 | 46 | const isTrusted = getFromWorkspaceState(WORKSPACE_IS_TRUSTED_KEY, false); 47 | if (isTrusted !== IS_TRUSTED) { 48 | IS_TRUSTED = true; 49 | } 50 | 51 | ctx.subscriptions.push(commands.registerCommand('c-cpp-flylint.workspace.isTrusted.toggle', async () => { 52 | await toggleWorkspaceIsTrusted(); 53 | commands.executeCommand('c-cpp-flylint.analyzeWorkspace'); 54 | })); 55 | ctx.subscriptions.push(commands.registerCommand('c-cpp-flylint.workspace.resetState', resetWorkspaceState)); 56 | 57 | if (isTrusted) { 58 | return; 59 | } 60 | 61 | const ignored = ignoredWorkspaceConfig(workspace.getConfiguration(FLYLINT_ID), SECURITY_SENSITIVE_CONFIG); 62 | if (ignored.length === 0) { 63 | return; 64 | } 65 | const ignoredSettings = ignored.map((x) => `"${FLYLINT_ID}.${x}"`).join(','); 66 | const val = await window.showWarningMessage( 67 | `Some workspace/folder-level settings (${ignoredSettings}) from the untrusted workspace are disabled ` + 68 | 'by default. If this workspace is trusted, explicitly enable the workspace/folder-level settings ' + 69 | 'by running the "C/C++ Flylint: Toggle Workspace Trust Flag" command.', 70 | 'OK', 71 | 'Trust This Workspace', 72 | 'More Info' 73 | ); 74 | switch (val) { 75 | case 'Trust This Workspace': 76 | await toggleWorkspaceIsTrusted(); 77 | break; 78 | case 'More Info': 79 | env.openExternal(Uri.parse('https://github.com/jbenden/vscode-c-cpp-flylint/blob/main/README.md#security')); 80 | break; 81 | default: 82 | break; 83 | } 84 | } 85 | 86 | function ignoredWorkspaceConfig(cfg: WorkspaceConfiguration, keys: string[]) { 87 | return keys.filter((key) => { 88 | const inspect = cfg.inspect(key); 89 | if (inspect === undefined) { 90 | return false; 91 | } 92 | return inspect.workspaceValue !== undefined || inspect.workspaceFolderValue !== undefined; 93 | }); 94 | } 95 | 96 | async function toggleWorkspaceIsTrusted() { 97 | IS_TRUSTED = !IS_TRUSTED; 98 | await updateWorkspaceState(WORKSPACE_IS_TRUSTED_KEY, IS_TRUSTED); 99 | } 100 | 101 | export async function activate(context: ExtensionContext) { 102 | setWorkspaceState(context.workspaceState); 103 | 104 | await maybeWorkspaceIsTrusted(context); 105 | 106 | // The server is implemented in Node. 107 | const serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js')); 108 | 109 | // The debug options for the server. 110 | const debugOptions = { 111 | execArgv: ['--nolazy', '--inspect=6011'] 112 | }; 113 | 114 | // If the extension is launched in debug mode the debug server options are used, otherwise the run options are used. 115 | const serverOptions: ServerOptions = { 116 | run: { 117 | module: serverModule, 118 | transport: TransportKind.ipc 119 | }, 120 | debug: { 121 | module: serverModule, 122 | transport: TransportKind.ipc, 123 | options: debugOptions 124 | } 125 | }; 126 | 127 | // Create the language client and start it. 128 | startLSClient(serverOptions, context); 129 | } 130 | 131 | function startLSClient(serverOptions: ServerOptions, context: ExtensionContext) { 132 | // Options to control the language client. 133 | const clientOptions: LanguageClientOptions = { 134 | // Register the server for C/C++ documents. 135 | documentSelector: [{ scheme: 'file', language: 'c' }, { scheme: 'file', language: 'cpp' }], 136 | synchronize: { 137 | // Synchronize the setting section "c-cpp-flylint" to the server. 138 | configurationSection: FLYLINT_ID, 139 | fileEvents: workspace.createFileSystemWatcher('**/.vscode/c_cpp_properties.json') 140 | } 141 | }; 142 | 143 | let settings = vscode.workspace.getConfiguration(FLYLINT_ID); 144 | let queryUrlBase = settings.get('queryUrlBase'); 145 | let webQueryMatchSet = settings.get>('webQueryMatchSet'); 146 | 147 | if (queryUrlBase === undefined) { 148 | queryUrlBase = 'https://www.google.com/search?q='; 149 | } 150 | if (webQueryMatchSet === undefined) { 151 | webQueryMatchSet = []; 152 | } 153 | 154 | const client = new FlylintLanguageClient(FLYLINT_ID, 'C/C++ Flylint', serverOptions, clientOptions, 155 | queryUrlBase, webQueryMatchSet); 156 | 157 | client.onReady() 158 | .then(() => { 159 | 160 | // ---------------------------------------------------------------- 161 | 162 | context.subscriptions.push(commands.registerCommand('c-cpp-flylint.getLocalConfig', async (d: TextDocument) => { 163 | return client.sendRequest('getLocalConfig', d); 164 | })); 165 | 166 | // ---------------------------------------------------------------- 167 | 168 | // Here we must watch for all extension dependencies to start and be ready. 169 | var untilReadyRetries = 40; // 40x250 = 10 seconds maximum 170 | const untilReady = async () => { 171 | try { 172 | await commands.executeCommand('cpptools.activeConfigName'); 173 | client.sendNotification('begin', { document: window.activeTextEditor!.document }); 174 | } 175 | catch (err) { 176 | if (--untilReadyRetries > 0) { 177 | setTimeout(untilReady, 250); // repeat 178 | } else { 179 | client.outputChannel.appendLine(`Failed to access "ms-vstools.cpptools"` + 180 | `extension's active workspace` + 181 | `configuration.`); 182 | client.sendNotification('begin'); 183 | } 184 | } 185 | }; 186 | setTimeout(untilReady, 250); // primer 187 | 188 | // ---------------------------------------------------------------- 189 | 190 | client.onRequest('activeTextDocument', () => { 191 | return window.activeTextEditor!.document; 192 | }); 193 | 194 | // ---------------------------------------------------------------- 195 | 196 | client.onRequest('c-cpp-flylint.cpptools.activeConfigName', async () => { 197 | return commands.executeCommand('cpptools.activeConfigName'); 198 | }); 199 | 200 | // ---------------------------------------------------------------- 201 | 202 | client.onRequest('isTrusted', () => { 203 | return IS_TRUSTED; 204 | }); 205 | 206 | // ---------------------------------------------------------------- 207 | 208 | tasks.onDidEndTask((e: TaskEndEvent) => { 209 | if (e.execution.task.group && e.execution.task.group === TaskGroup.Build) { 210 | // send a build notification event 211 | let params = { 212 | taskName: e.execution.task.name, 213 | taskSource: e.execution.task.source, 214 | isBackground: e.execution.task.isBackground, 215 | }; 216 | client.sendNotification('onBuild', params); 217 | } 218 | }); 219 | }); 220 | 221 | context.subscriptions.push(new SettingMonitor(client, `${FLYLINT_ID}.enable`).start()); 222 | } 223 | -------------------------------------------------------------------------------- /client/src/flylintLanguageClient.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node'; 6 | import * as ls from 'vscode-languageserver'; 7 | import * as code from 'vscode'; 8 | 9 | export class FlylintLanguageClient extends LanguageClient { 10 | constructor( 11 | id: string, 12 | name: string, 13 | serverOptions: ServerOptions, 14 | clientOptions: LanguageClientOptions, 15 | queryUrlStr: string, 16 | matchSet: string[], 17 | forceDebug?: boolean 18 | ) { 19 | super(id, name, serverOptions, clientOptions, forceDebug); 20 | let originalAsDiagnostic = this.protocol2CodeConverter.asDiagnostic; 21 | this.protocol2CodeConverter.asDiagnostic = ((diagnostic: ls.Diagnostic): code.Diagnostic => { 22 | let result = originalAsDiagnostic(diagnostic); 23 | if ((result.code !== undefined) && (typeof result.code === 'string') && (queryUrlStr.length > 0)) { 24 | let codeStr = String(result.code); 25 | if (matchSet.some(v => codeStr.includes(v))) { 26 | result.code = { 27 | value: result.code, 28 | target: this.protocol2CodeConverter.asUri(`${queryUrlStr}${result.source}%20${result.code}`) 29 | }; 30 | } 31 | } 32 | return result; 33 | }); 34 | this.protocol2CodeConverter.asDiagnostics = ((diagnostics: ls.Diagnostic[]): code.Diagnostic[] => { 35 | return diagnostics.map(this.protocol2CodeConverter.asDiagnostic); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/src/stateUtils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /* 3 | * SPDX-FileCopyrightText: The Microsoft Corporation 4 | * 5 | * SPDX-License-Identifier: MIT 6 | */ 7 | 8 | import vscode = require('vscode'); 9 | 10 | let workspaceState: vscode.Memento; 11 | 12 | export function getFromWorkspaceState(key: string, defaultValue?: any) { 13 | if (!workspaceState) { 14 | return defaultValue; 15 | } 16 | return workspaceState.get(key, defaultValue); 17 | } 18 | 19 | export function updateWorkspaceState(key: string, value: any) { 20 | if (!workspaceState) { 21 | return Promise.resolve(); 22 | } 23 | return workspaceState.update(key, value); 24 | } 25 | 26 | export function setWorkspaceState(state: vscode.Memento) { 27 | workspaceState = state; 28 | } 29 | 30 | export function getWorkspaceState(): vscode.Memento { 31 | return workspaceState; 32 | } 33 | 34 | export function resetWorkspaceState() { 35 | return resetStateQuickPick(workspaceState, updateWorkspaceState); 36 | } 37 | 38 | export function getMementoKeys(state: vscode.Memento): string[] { 39 | if (!state) { 40 | return []; 41 | } 42 | // tslint:disable-next-line: no-empty 43 | if ((state as any)._value) { 44 | const keys = Object.keys((state as any)._value); 45 | // Filter out keys with undefined values, so they are not shown 46 | // in the quick pick menu. 47 | return keys.filter((key) => state.get(key) !== undefined); 48 | } 49 | return []; 50 | } 51 | 52 | async function resetStateQuickPick(state: vscode.Memento, updateFn: (key: string, value: any) => {}) { 53 | const items = await vscode.window.showQuickPick(getMementoKeys(state), { 54 | canPickMany: true, 55 | placeHolder: 'Select the keys to reset.' 56 | }); 57 | resetItemsState(items!, updateFn); 58 | } 59 | 60 | export function resetItemsState(items: string[], updateFn: (key: string, value: any) => {}) { 61 | if (!items) { 62 | return; 63 | } 64 | items.forEach((item) => updateFn(item, undefined)); 65 | } 66 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "target": "es2017", 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "lib": [ "es2017" ], 9 | "sourceMap": true 10 | }, 11 | "include": [ 12 | "src", 13 | "../typings.d.ts" 14 | ], 15 | "exclude": [ 16 | "node_modules" 17 | ] 18 | } -------------------------------------------------------------------------------- /client/webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 'use strict'; 3 | 4 | const withDefaults = require('../shared.webpack.config'); 5 | const path = require('path'); 6 | 7 | module.exports = withDefaults({ 8 | context: path.join(__dirname), 9 | entry: { 10 | extension: './src/extension.ts', 11 | }, 12 | output: { 13 | filename: 'extension.js', 14 | path: path.join(__dirname, 'out'), 15 | devtoolModuleFilenameTemplate: "../[resource-path]", 16 | hashFunction: 'xxhash64' 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /code-climate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 3 | # 4 | # SPDX-License-Identifier: MIT 5 | 6 | env CONTAINER_TIMEOUT_SECONDS=1800 docker run \ 7 | --interactive --tty --rm \ 8 | --env CODECLIMATE_CODE="$PWD" \ 9 | --volume "$PWD":/code \ 10 | --volume /var/run/docker.sock:/var/run/docker.sock \ 11 | --volume /tmp/cc:/tmp/cc \ 12 | codeclimate/codeclimate "$@" 13 | -------------------------------------------------------------------------------- /server/.eslintignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules -------------------------------------------------------------------------------- /server/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "../.eslintrc.base.json", 4 | "rules": { 5 | "no-console": "error" 6 | } 7 | } -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | ## 2 | # IMPORTANT: Do not use this file to ignore IDE-specific project configuration files. Ignore them locally instead. 3 | # (see http://stackoverflow.com/questions/1753070/git-ignore-files-only-locally) 4 | ## 5 | 6 | /node_modules/ 7 | yarn.lock 8 | .yarnclean 9 | -------------------------------------------------------------------------------- /server/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | // List of configurations. Add new configurations or edit existing ones. 4 | "configurations": [ 5 | { 6 | "name": "Attach", 7 | "type": "node", 8 | "request": "attach", 9 | "port": 6004, 10 | "sourceMaps": true, 11 | "outFiles": [ 12 | "${workspaceRoot}/../c-cpp-flylint/server" 13 | ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /server/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "./node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /server/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "yarn", 4 | "isShellCommand": true, 5 | "showOutput": "silent", 6 | "args": ["run", "watch"], 7 | "isBackground": true, 8 | "problemMatcher": "$tsc-watch" 9 | } 10 | -------------------------------------------------------------------------------- /server/customInstallServerIntoExtension.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // SPDX-FileCopyrightText: The Microsoft Corporation 4 | // 5 | // SPDX-License-Identifier: MIT 6 | 7 | /* eslint-disable @typescript-eslint/no-var-requires */ 8 | 9 | // This file is based on the "installServerIntoExtension" that ships with the 10 | // vscode-languagserver node package. We needed to modify it because the original 11 | // version does not copy the package-lock.json file, and it uses npm update 12 | // rather than npm install. 13 | 14 | const path = require('path'); 15 | const fs = require('fs'); 16 | const cp = require('child_process'); 17 | 18 | let extensionDirectory = process.argv[2]; 19 | if (!extensionDirectory) { 20 | console.error('No extension directory provided.'); 21 | process.exit(1); 22 | } 23 | extensionDirectory = path.resolve(extensionDirectory); 24 | if (!fs.existsSync(extensionDirectory)) { 25 | console.error('Extension directory ' + extensionDirectory + " doesn't exist on disk."); 26 | process.exit(1); 27 | } 28 | 29 | let packageFile = process.argv[3]; 30 | if (!packageFile) { 31 | console.error('No package.json file provided.'); 32 | process.exit(1); 33 | } 34 | packageFile = path.resolve(packageFile); 35 | if (!fs.existsSync(packageFile)) { 36 | console.error('Package file ' + packageFile + " doesn't exist on disk."); 37 | process.exit(1); 38 | } 39 | let tsconfigFile = process.argv[4]; 40 | if (!tsconfigFile) { 41 | console.error('No tsconfig.json file provided'); 42 | process.exit(1); 43 | } 44 | tsconfigFile = path.resolve(tsconfigFile); 45 | if (!fs.existsSync(tsconfigFile)) { 46 | console.error('tsconfig file ' + tsconfigFile + " doesn't exist on disk."); 47 | process.exit(1); 48 | } 49 | 50 | const extensionServerDirectory = path.join(extensionDirectory, 'server'); 51 | 52 | const json = require(tsconfigFile); 53 | const compilerOptions = json.compilerOptions; 54 | if (compilerOptions) { 55 | const outDir = compilerOptions.outDir; 56 | if (!outDir || path.join(path.dirname(tsconfigFile), outDir) !== extensionServerDirectory) { 57 | console.error( 58 | 'outDir in ' + 59 | process.argv[4] + 60 | ' must point to ' + 61 | extensionServerDirectory + 62 | ' but it points to ' + 63 | path.join(path.dirname(tsconfigFile), outDir) 64 | ); 65 | console.error( 66 | 'Please change outDir in ' + 67 | process.argv[4] + 68 | ' to ' + 69 | path.relative(path.dirname(tsconfigFile), extensionServerDirectory).replace(/\\/g, '/') 70 | ); 71 | process.exit(1); 72 | } 73 | } 74 | 75 | if (!fs.existsSync(extensionServerDirectory)) { 76 | fs.mkdirSync(extensionServerDirectory); 77 | } 78 | 79 | const dest = path.join(extensionServerDirectory, 'package.json'); 80 | console.log("Copying package.json to extension's server location..."); 81 | fs.writeFileSync(dest, fs.readFileSync(packageFile, 'utf8'), 0, 'utf8'); 82 | 83 | let packageLockFile = process.argv[5]; 84 | if (fs.existsSync(packageLockFile)) { 85 | const packageLockFileDest = path.join(extensionServerDirectory, 'package-lock.json'); 86 | packageLockFile = path.resolve(packageLockFile); 87 | console.log("Copying package-lock.json to extension's server location..."); 88 | fs.writeFileSync(packageLockFileDest, fs.readFileSync(packageLockFile, 'utf8'), 0, 'utf8'); 89 | } 90 | 91 | console.log("Installing server npm modules into extension's server location..."); 92 | process.chdir(extensionServerDirectory); 93 | cp.execSync('npm install --production', 'utf8'); 94 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "c-cpp-flylint-server", 3 | "description": "C/C++ Advanced Lint: Server", 4 | "version": "1.15.0", 5 | "author": { 6 | "name": "Joseph Benden", 7 | "email": "joe@benden.us", 8 | "url": "http://benden.us/" 9 | }, 10 | "license": "MIT", 11 | "private": true, 12 | "engines": { 13 | "node": "*" 14 | }, 15 | "dependencies": { 16 | "cross-spawn": "^7.0.3", 17 | "fast-glob": "^3.2.5", 18 | "lodash": "^4.17.21", 19 | "slash": "^3.0.0", 20 | "tmp": "^0.2.1", 21 | "unixify": "^1.0.0", 22 | "var-expansion": "^0.1.0", 23 | "vscode-languageserver": "^7.0.0", 24 | "vscode-languageserver-textdocument": "^1.0.1", 25 | "vscode-uri": "^3.0.2", 26 | "which": "^2.0.2" 27 | }, 28 | "devDependencies": { 29 | "@types/cross-spawn": "^6.0.2", 30 | "@types/mock-fs": "^4.13.0", 31 | "@types/node": "^13.13.40", 32 | "@types/vscode": "^1.52.0", 33 | "mock-fs": "^4.13.0", 34 | "typescript": "^4.1.2", 35 | "webpack": "^5.76.0", 36 | "webpack-cli": "^4.2.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/src/linters/clang.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import * as path from 'path'; 6 | import { ClangSeverityMaps, Settings, VS_DiagnosticSeverity } from '../settings'; 7 | import { Linter, Lint } from './linter'; 8 | import { InternalDiagnostic } from '../server'; 9 | import { path as sysPath } from '../utils'; 10 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 11 | 12 | export class Clang extends Linter { 13 | private actualFileName: string = ''; 14 | private tmpFileName: string = ''; 15 | 16 | constructor(settings: Settings, workspaceRoot: string) { 17 | super('Clang', settings, workspaceRoot, false); 18 | this.cascadeCommonSettings('clang'); 19 | 20 | this.executable = settings.clang.executable; 21 | this.configFile = settings.clang.configFile; 22 | this.active = this.enabled = settings.clang.enable; 23 | } 24 | 25 | /* istanbul ignore next */ 26 | public lintOn(): Lint[] { 27 | return [Lint.ON_SAVE, Lint.ON_TYPE, Lint.ON_BUILD]; 28 | } 29 | 30 | protected buildCommandLine(fileName: string, tmpFileName: string): string[] { 31 | let includePathParams = this.getIncludePathParams(); 32 | let languageParam = this.getLanguageParam(); 33 | let iquoteParams: string[]; 34 | 35 | if (this.settings.run === 'onType') { 36 | iquoteParams = this.expandedArgsFor( 37 | '-iquote', 38 | false, 39 | [path.dirname(fileName)].concat(this.includePaths), 40 | null 41 | ); 42 | } else { 43 | iquoteParams = []; 44 | } 45 | 46 | let pedanticParams = this.getPedanticParams(); 47 | let msExtensions = this.settings.clang.msExtensions ? 48 | ['-fms-extensions'] : []; 49 | let noExceptions = this.settings.clang.noExceptions ? 50 | ['-fno-exceptions'] : []; 51 | let noRtti = this.settings.clang.noRtti ? 52 | ['-fno-rtti'] : []; 53 | let blocks = this.settings.clang.blocks ? 54 | ['-fblocks'] : []; 55 | let includeArgParams = this.expandedArgsFor( 56 | '-include', 57 | false, 58 | this.settings.clang.includes, 59 | null); 60 | let warningsParams = this.expandedArgsFor( 61 | '-W', 62 | true, 63 | this.settings.clang.warnings, 64 | null); 65 | let standardParams = this.expandedArgsFor( 66 | '--std=', 67 | true, 68 | this.standard, 69 | ['c11', 'c++11']); 70 | let standardLibParams = this.expandedArgsFor( 71 | '--stdlib=', 72 | true, 73 | this.settings.clang.standardLibs, 74 | null); 75 | let defineParams = this.expandedArgsFor( 76 | '-D', 77 | true, 78 | this.defines, 79 | null); 80 | let undefineParams = this.expandedArgsFor( 81 | '-U', 82 | true, 83 | this.undefines, 84 | null); 85 | 86 | let args = [ 87 | this.executable, 88 | '-fsyntax-only', 89 | '-fno-color-diagnostics', 90 | '-fno-caret-diagnostics', 91 | '-fno-diagnostics-show-option', 92 | '-fdiagnostics-show-category=name', 93 | '-ferror-limit=200' 94 | ] 95 | .concat(iquoteParams) 96 | .concat(standardParams) 97 | .concat(pedanticParams) 98 | .concat(standardLibParams) 99 | .concat(msExtensions) 100 | .concat(noExceptions) 101 | .concat(noRtti) 102 | .concat(blocks) 103 | .concat(includeArgParams) 104 | .concat(warningsParams) 105 | .concat(defineParams) 106 | .concat(undefineParams) 107 | .concat(includePathParams) 108 | .concat(languageParam) 109 | .concat((this.settings.clang.extraArgs || []).map(a => this.expandVariables(a).result || a)); 110 | 111 | if (this.settings.run === 'onType') { 112 | args.push(sysPath(tmpFileName)); 113 | } else { 114 | args.push(sysPath(fileName)); 115 | } 116 | 117 | this.actualFileName = fileName; 118 | this.tmpFileName = tmpFileName; 119 | 120 | return args; 121 | } 122 | 123 | protected parseLine(line: string): InternalDiagnostic | null { 124 | let regex = /^(.+?):([0-9]+):([0-9]+):\s(fatal|error|warning|note)(?: error)?:\s(.*)$/; 125 | let regexArray: RegExpExecArray | null; 126 | 127 | if (line === '') { 128 | // skip this line 129 | return null; 130 | } 131 | 132 | let excludeRegex = /^(WX.*|_WX.*|__WX.*|Q_.*|warning: .* incompatible with .*|warning: .* input unused|warning: include location .* is unsafe for cross-compilation.*)$/; 133 | if (excludeRegex.exec(line) !== null) { 134 | // skip this line 135 | return null; 136 | } 137 | 138 | let inFileArray: RegExpExecArray | null; 139 | let inFileRegex = /^In file included from (.+?):([0-9]+):$/; 140 | 141 | if ((inFileArray = inFileRegex.exec(line)) !== null) { 142 | return { 143 | fileName: (inFileArray[1] === this.tmpFileName ? this.actualFileName : inFileArray[1]), 144 | line: parseInt(inFileArray[2]) - 1, 145 | column: 0, 146 | severity: DiagnosticSeverity.Warning, 147 | code: 0, 148 | message: 'Issues in file included from here', 149 | source: this.name 150 | }; 151 | } 152 | 153 | if ((regexArray = regex.exec(line)) !== null) { 154 | return { 155 | fileName: (regexArray[1] === this.tmpFileName ? this.actualFileName : regexArray[1]), 156 | line: parseInt(regexArray[2]) - 1, 157 | column: parseInt(regexArray[3]) - 1, 158 | severity: this.getSeverityCode(regexArray[4]), 159 | code: 0, 160 | message: regexArray[5], 161 | source: this.name, 162 | }; 163 | } else { 164 | return { 165 | parseError: 'Line could not be parsed: ' + line, 166 | fileName: '', 167 | line: 0, 168 | column: 0, 169 | severity: DiagnosticSeverity.Error, 170 | code: 0, 171 | message: '', 172 | source: this.name 173 | }; 174 | } 175 | } 176 | 177 | private getSeverityCode(severity: string): DiagnosticSeverity { 178 | let output = this.settings.clang.severityLevels[severity as keyof ClangSeverityMaps]; 179 | return VS_DiagnosticSeverity.from(output); 180 | } 181 | 182 | private getPedanticParams(): string[] { 183 | let params: string[] = []; 184 | 185 | if (this.settings.clang.pedantic) { 186 | params.push(`-pedantic`); 187 | } 188 | 189 | if (this.settings.clang.pedanticErrors) { 190 | params.push(`-pedantic-errors`); 191 | } 192 | 193 | return params; 194 | } 195 | 196 | private getLanguageParam(): string[] { 197 | let language = this.language; 198 | let params: string[] = []; 199 | 200 | if (this.isValidLanguage(language)) { 201 | params.push(`-x`); 202 | params.push(`${language}`); 203 | } 204 | 205 | return params; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /server/src/linters/cppcheck.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import * as _ from 'lodash'; 6 | import { CppCheckSeverityMaps, Settings, VS_DiagnosticSeverity } from '../settings'; 7 | import { Linter } from './linter'; 8 | import { InternalDiagnostic } from '../server'; 9 | import { path as sysPath } from '../utils'; 10 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 11 | 12 | export class CppCheck extends Linter { 13 | constructor(settings: Settings, workspaceRoot: string) { 14 | super('CppCheck', settings, workspaceRoot, false); 15 | this.cascadeCommonSettings('cppcheck'); 16 | 17 | this.executable = settings.cppcheck.executable; 18 | this.configFile = settings.cppcheck.configFile; 19 | this.active = this.enabled = settings.cppcheck.enable; 20 | } 21 | 22 | protected buildCommandLine(fileName: string, _tmpFileName: string): string[] { 23 | let enableParams = this.settings.cppcheck.unusedFunctions 24 | ? ['--enable=warning,style,performance,portability,information,unusedFunction'] 25 | : ['--enable=warning,style,performance,portability,information']; 26 | let addonParams = this.getAddonParams(); 27 | let includeParams = this.getIncludePathParams(); 28 | let suppressionParams = this.getSuppressionParams().map(a => this.expandVariables(a).result || a); 29 | let languageParam = this.getLanguageParam(); 30 | let platformParams = this.getPlatformParams(); 31 | let standardParams = this.expandedArgsFor( 32 | '--std=', 33 | true, 34 | this.standard, 35 | ['c11', 'c++11']); 36 | let defineParams = this.expandedArgsFor( 37 | '-D', 38 | true, 39 | this.defines, 40 | null); 41 | let undefineParams = this.expandedArgsFor( 42 | '-U', 43 | true, 44 | this.undefines, 45 | null); 46 | 47 | let args = [this.executable] 48 | .concat(['--inline-suppr']) 49 | .concat(enableParams) 50 | .concat((addonParams || []).map(a => this.expandVariables(a).result || a)) 51 | .concat(includeParams) 52 | .concat(standardParams) 53 | .concat(defineParams) 54 | .concat(undefineParams) 55 | .concat(suppressionParams) 56 | .concat(languageParam) 57 | .concat([platformParams]) 58 | .concat([`--template="{file} {line} {severity} {id}: {message}"`]) 59 | .concat((this.settings.cppcheck.extraArgs || []).map(a => this.expandVariables(a).result || a)); 60 | 61 | /* istanbul ignore if */ 62 | if (this.settings.cppcheck.verbose) { 63 | args.push('--verbose'); 64 | } 65 | 66 | /* istanbul ignore if */ 67 | if (this.settings.cppcheck.force) { 68 | args.push('--force'); 69 | } 70 | 71 | /* istanbul ignore if */ 72 | if (this.settings.cppcheck.inconclusive) { 73 | args.push('--inconclusive'); 74 | } 75 | 76 | args.push(sysPath(fileName)); 77 | 78 | return args; 79 | } 80 | 81 | protected parseLine(line: string): InternalDiagnostic | null { 82 | let regex = /^(.+?)\s\s([0-9]+)\s([0-9]+\s)?\s(style|information|portability|performance|warning|error)\s(.+?):\s(.*)$/; 83 | let regexArray: RegExpExecArray | null; 84 | 85 | let excludeRegex = /^((Checking |Active checkers:|Defines:|Undefines:|Includes:|Platform:|.*information.*missingInclude.*).*|cppcheck: .*. Disabling .* check.|)$/; 86 | 87 | if (excludeRegex.exec(line) !== null) { 88 | // skip this line 89 | return null; 90 | } 91 | 92 | if ((regexArray = regex.exec(line)) !== null) { 93 | return { 94 | fileName: regexArray[1], 95 | line: parseInt(regexArray[2]) - 1, 96 | column: parseInt(regexArray[3]) || 0, 97 | severity: this.getSeverityCode(regexArray[4]), 98 | code: regexArray[5], 99 | message: regexArray[6], 100 | source: this.name, 101 | }; 102 | } else { 103 | return { 104 | parseError: 'Line could not be parsed: ' + line, 105 | fileName: '', 106 | line: 0, 107 | column: 0, 108 | severity: DiagnosticSeverity.Error, 109 | code: 0, 110 | message: '', 111 | source: this.name 112 | }; 113 | } 114 | } 115 | 116 | private getSeverityCode(severity: string): DiagnosticSeverity { 117 | let output = this.settings.cppcheck.severityLevels[severity as keyof CppCheckSeverityMaps]; 118 | return VS_DiagnosticSeverity.from(output); 119 | } 120 | 121 | private getPlatformParams(): string { 122 | let platform = this.settings.cppcheck.platform; 123 | 124 | if (platform) { 125 | let substituted_platform = this.expandVariables(platform).result || platform; 126 | return `--platform=${substituted_platform}`; 127 | } 128 | 129 | return '--platform=native'; 130 | } 131 | 132 | private getSuppressionParams(): string[] { 133 | let suppressions = this.settings.cppcheck.suppressions; 134 | let params: string[] = []; 135 | 136 | if (suppressions) { 137 | _.each(suppressions, (element: string) => { 138 | params.push(`--suppress=${element}`); 139 | }); 140 | } 141 | 142 | return params; 143 | } 144 | 145 | private getLanguageParam(): string[] { 146 | let language = this.language; 147 | let params: string[] = []; 148 | 149 | if (this.isValidLanguage(language)) { 150 | params.push(`--language=${language}`); 151 | } 152 | 153 | return params; 154 | } 155 | 156 | private getAddonParams(): string[] { 157 | let addons = this.settings.cppcheck.addons; 158 | let params: string[] = []; 159 | 160 | if (addons) { 161 | _.each(addons, (element: string) => { 162 | params.push(`--addon=${element}`); 163 | }); 164 | } 165 | 166 | return params; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /server/src/linters/flawfinder.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { FlawFinderSeverityMaps, Settings, VS_DiagnosticSeverity } from '../settings'; 6 | import { Linter } from './linter'; 7 | import { InternalDiagnostic } from '../server'; 8 | import { path as sysPath } from '../utils'; 9 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 10 | 11 | export class FlawFinder extends Linter { 12 | constructor(settings: Settings, workspaceRoot: string) { 13 | super('FlawFinder', settings, workspaceRoot, false); 14 | this.cascadeCommonSettings('flawfinder'); 15 | 16 | this.setExecutable(settings.flawfinder.executable); 17 | this.active = this.enabled = settings.flawfinder.enable; 18 | } 19 | 20 | protected buildCommandLine(fileName: string, _tmpFileName: string): string[] { 21 | let args = [this.executable] 22 | .concat(['--columns']) 23 | .concat(['--dataonly']) 24 | .concat(['--singleline']) 25 | .concat([]); 26 | 27 | args.push(sysPath(fileName)); 28 | 29 | return args; 30 | } 31 | 32 | protected parseLine(line: string): InternalDiagnostic | null { 33 | let regex = /^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:?\s\s\[([0-5])] ([^:]+):(.+)$/; 34 | let regexArray: RegExpExecArray | null; 35 | 36 | let excludeRegex = /^((Examining ).*|)$/; 37 | 38 | if (excludeRegex.exec(line) !== null) { 39 | // skip this line 40 | return null; 41 | } 42 | 43 | if ((regexArray = regex.exec(line)) !== null) { 44 | return { 45 | fileName: regexArray[1], 46 | line: parseInt(regexArray[2]) - 1, 47 | /* istanbul ignore next */ 48 | column: parseInt(regexArray[3]) - 1 || 0, 49 | severity: this.getSeverityCode(regexArray[4]), 50 | code: regexArray[5], 51 | message: regexArray[6], 52 | source: 'FlawFinder', 53 | }; 54 | } else { 55 | return { 56 | parseError: 'Line could not be parsed: ' + line, 57 | fileName: '', 58 | line: 0, 59 | column: 0, 60 | severity: DiagnosticSeverity.Error, 61 | code: 0, 62 | message: '', 63 | source: 'FlawFinder' 64 | }; 65 | } 66 | } 67 | 68 | private getSeverityCode(severity: string): DiagnosticSeverity { 69 | let output = this.settings.flawfinder.severityLevels[severity as keyof FlawFinderSeverityMaps]; 70 | return VS_DiagnosticSeverity.from(output); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /server/src/linters/flexelint.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import * as path from 'path'; 6 | import * as _ from 'lodash'; 7 | import { FlexelintSeverityMaps, Settings, VS_DiagnosticSeverity } from '../settings'; 8 | import { headerExts, Linter } from './linter'; 9 | import { InternalDiagnostic } from '../server'; 10 | import { path as sysPath } from '../utils'; 11 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 12 | 13 | export class Flexelint extends Linter { 14 | constructor(settings: Settings, workspaceRoot: string) { 15 | super('Flexelint', settings, workspaceRoot, true); 16 | this.cascadeCommonSettings('flexelint'); 17 | 18 | this.executable = settings.flexelint.executable; 19 | this.configFile = settings.flexelint.configFile; 20 | this.active = this.enabled = settings.flexelint.enable; 21 | } 22 | 23 | protected buildCommandLine(fileName: string, _tmpFileName: string): string[] { 24 | var args = [ 25 | this.executable, 26 | '-v', 27 | '-b', 28 | '-format=%f %l %c %t %n: %m', 29 | this.configFile, 30 | '-hsFr_1', 31 | '-width(4096,0)', 32 | '-zero(400)', 33 | ]; 34 | 35 | if (headerExts.indexOf(path.extname(fileName)) !== -1) { 36 | var hArgs = this.settings.flexelint.headerArgs; 37 | 38 | if (_.isString(hArgs)) { 39 | args.push(hArgs); 40 | } else { 41 | hArgs.forEach(element => { 42 | args.push(element); 43 | }); 44 | } 45 | } 46 | 47 | args.push(sysPath(fileName)); 48 | 49 | return args; 50 | } 51 | 52 | protected transformParse(currentParsed: InternalDiagnostic | null, parsed: InternalDiagnostic | null) { 53 | if (parsed) { 54 | if ((parsed['code'] === '830' && parsed['message'] !== 'Location cited in prior message') || (parsed['code'] === '831' && parsed['message'] !== 'Reference cited in prior message')) { 55 | currentParsed!['line'] = parsed['line']; 56 | currentParsed!['column'] = parsed['column']; 57 | parsed = null; 58 | } else if (parsed['code'] === '830' || parsed['code'] === '831') { 59 | parsed = null; 60 | } 61 | } 62 | 63 | return { currentParsed: currentParsed, parsed: parsed }; 64 | } 65 | 66 | protected parseLine(line: string): InternalDiagnostic | null { 67 | let regex = /^(.+?)\s\s([0-9]+)\s([0-9]+\s)?\s(Info|Warning|Error|Note)\s([0-9]+):\s(.*)$/; 68 | let regexArray: RegExpExecArray | null; 69 | 70 | let excludeRegex = /^((During Specific Walk:|\s\sFile\s).*|)$/; 71 | 72 | if (excludeRegex.exec(line) !== null) { 73 | // skip this line 74 | return null; 75 | } 76 | 77 | if ((regexArray = regex.exec(line)) !== null) { 78 | return { 79 | fileName: regexArray[1], 80 | line: parseInt(regexArray[2]) - 1, 81 | column: 0, 82 | severity: this.getSeverityCode(regexArray[4]), 83 | code: regexArray[5], 84 | message: regexArray[6], 85 | source: this.name, 86 | }; 87 | } else { 88 | return { 89 | parseError: 'Line could not be parsed: ' + line, 90 | fileName: '', 91 | line: 0, 92 | column: 0, 93 | severity: DiagnosticSeverity.Error, 94 | code: 0, 95 | message: '', 96 | source: this.name 97 | }; 98 | } 99 | } 100 | 101 | private getSeverityCode(severity: string): DiagnosticSeverity { 102 | let output = this.settings.flexelint.severityLevels[severity as keyof FlexelintSeverityMaps]; 103 | return VS_DiagnosticSeverity.from(output); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /server/src/linters/linter.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | /* eslint-disable no-console */ 6 | import * as which from 'which'; 7 | import * as fs from 'fs'; 8 | import * as path from 'path'; 9 | import * as _ from 'lodash'; 10 | import { InternalDiagnostic } from '../server'; 11 | import { Settings } from '../settings'; 12 | import { path as sysPath } from '../utils'; 13 | import * as cross_spawn from 'cross-spawn'; 14 | import * as child_process from 'child_process'; 15 | 16 | const substituteVariables = require('var-expansion').substituteVariables; // no types available 17 | const slash = require('slash'); // no types available 18 | 19 | export interface IExpansionResult { 20 | error?: any; 21 | result?: string; 22 | } 23 | 24 | export const headerExts = ['.h', '.H', '.hh', '.hpp', '.h++', '.hxx']; 25 | 26 | export enum Lint { 27 | ON_SAVE = 1, 28 | ON_TYPE = 2, 29 | ON_BUILD = 3, 30 | } 31 | 32 | /* istanbul ignore next */ 33 | export function toLint(s: string): Lint { 34 | switch (s) { 35 | case 'onSave': return Lint.ON_SAVE; 36 | case 'onType': return Lint.ON_TYPE; 37 | case 'onBuild': return Lint.ON_BUILD; 38 | default: 39 | throw Error('Unknown onLint value of ' + s); 40 | } 41 | } 42 | 43 | /* istanbul ignore next */ 44 | export function fromLint(lint: Lint): string { 45 | switch (lint) { 46 | case Lint.ON_SAVE: return 'ON_SAVE'; 47 | case Lint.ON_TYPE: return 'ON_TYPE'; 48 | case Lint.ON_BUILD: return 'ON_BUILD'; 49 | default: 50 | throw Error('Unknown enum Lint value of ' + lint); 51 | } 52 | } 53 | 54 | /* istanbul ignore next */ 55 | export class PathEnv { 56 | private paths: Array = []; 57 | 58 | constructor() { 59 | if (process.env.PATH) { this.paths = process.env.PATH.split(path.delimiter); } 60 | } 61 | 62 | append(p: string | Array) { 63 | // assert(p.includes(path.delimiter) !== true); 64 | 65 | this.paths = this.deduplicate(this.paths.concat(...p)); 66 | } 67 | 68 | prepend(p: string | Array) { 69 | // assert(p.includes(path.delimiter) !== true); 70 | 71 | if (typeof p === 'string') { 72 | p = [p]; 73 | } 74 | 75 | this.paths = this.deduplicate(p.concat(...this.paths)); 76 | } 77 | 78 | protected deduplicate(array: ReadonlyArray) { 79 | return Array.from(new Set(array)); 80 | } 81 | 82 | toString() { 83 | return this.paths.join(path.delimiter); 84 | } 85 | } 86 | 87 | export class Linter { 88 | protected name: string; 89 | protected settings: Settings; 90 | protected workspaceRoot: string; 91 | protected enabled: boolean; 92 | protected active: boolean; 93 | protected executable: string = ''; 94 | protected configFile: string = ''; 95 | protected requireConfig: boolean; 96 | protected language: string; 97 | protected standard: string[]; 98 | protected defines: string[]; 99 | protected undefines: string[]; 100 | protected includePaths: string[]; 101 | 102 | protected constructor(name: string, settings: Settings, workspaceRoot: string, requireConfig: boolean) { 103 | this.name = name; 104 | this.settings = settings; 105 | this.workspaceRoot = workspaceRoot; 106 | this.requireConfig = requireConfig; 107 | this.enabled = true; 108 | this.active = true; 109 | this.language = settings.language; 110 | this.standard = settings.standard; 111 | this.defines = settings.defines; 112 | this.undefines = settings.undefines; 113 | this.includePaths = settings.includePaths; 114 | } 115 | 116 | /* istanbul ignore next */ 117 | protected cascadeCommonSettings(key: string) { 118 | let checkKey = (item: string): boolean => { 119 | return this.settings[key as keyof Settings].hasOwnProperty(item) && 120 | this.settings[key as keyof Settings].hasOwnProperty(item) !== null && 121 | (this.settings[key as keyof Settings] as any)[item] !== null; 122 | }; 123 | 124 | let maybe = (orig: string[] | string, maybeKey: string) => { 125 | if (checkKey(maybeKey)) { 126 | if (_.isArray(orig) || _.isString(orig)) { 127 | return (this.settings[key as keyof Settings] as any)[maybeKey]; 128 | } 129 | } 130 | 131 | return orig; 132 | }; 133 | 134 | this.language = maybe(this.language, 'language'); 135 | this.standard = maybe(this.standard, 'standard'); 136 | this.defines = maybe(this.defines, 'defines'); 137 | this.undefines = maybe(this.undefines, 'undefines'); 138 | this.includePaths = maybe(this.includePaths, 'includePaths'); 139 | } 140 | 141 | /* istanbul ignore next */ 142 | protected setExecutable(fileName: string) { 143 | this.executable = fileName; 144 | } 145 | 146 | /* istanbul ignore next */ 147 | protected setConfigFile(fileName: string) { 148 | this.configFile = fileName; 149 | } 150 | 151 | /* istanbul ignore next */ 152 | public Name(): string { 153 | return this.name; 154 | } 155 | 156 | /* istanbul ignore next */ 157 | public isEnabled(): boolean { 158 | return this.enabled; 159 | } 160 | 161 | /* istanbul ignore next */ 162 | public isActive(): boolean { 163 | return this.active; 164 | } 165 | 166 | /* istanbul ignore next */ 167 | public enable() { 168 | this.enabled = true; 169 | } 170 | 171 | /* istanbul ignore next */ 172 | public disable() { 173 | this.enabled = false; 174 | } 175 | 176 | /* istanbul ignore next */ 177 | public async initialize() { 178 | await this.maybeEnable().catch(() => { 179 | // empty 180 | }); 181 | return this; 182 | } 183 | 184 | /* istanbul ignore next */ 185 | private async maybeEnable() { 186 | if (!this.isEnabled()) { 187 | return Promise.resolve(''); 188 | } 189 | 190 | return this.maybeExecutablePresent() 191 | .then((val) => { 192 | this.executable = val; 193 | 194 | return this.maybeConfigFilePresent(); 195 | }); 196 | } 197 | 198 | /* istanbul ignore next */ 199 | private maybeExecutablePresent(): Promise { 200 | return new Promise((resolve, reject) => { 201 | let paths = new PathEnv(); 202 | 203 | paths.prepend(path.resolve(__dirname, '../../..')); 204 | 205 | which(this.executable, { path: paths.toString() }, (err: any, result: any) => { 206 | if (err) { 207 | this.disable(); 208 | 209 | if (this.settings.debug) { 210 | console.log(`The executable was not found for ${this.name}; looked for ${this.executable}`); 211 | } 212 | 213 | reject(Error(`The executable was not found for ${this.name}, disabling linter`)); 214 | } 215 | else { 216 | resolve(result); 217 | } 218 | }); 219 | }); 220 | } 221 | 222 | /* istanbul ignore next */ 223 | private async maybeConfigFilePresent(): Promise { 224 | if (!this.requireConfig) { 225 | return Promise.resolve(''); 226 | } 227 | 228 | return this.locateFile(this.workspaceRoot, this.configFile) 229 | .then((val) => { 230 | this.configFile = val; 231 | 232 | this.enable(); 233 | 234 | return val; 235 | }) 236 | .catch(() => { 237 | this.disable(); 238 | 239 | console.log(`The configuration file was not found for ${this.name}; looked for ${this.configFile}`); 240 | 241 | throw Error(`could not locate configuration file for ${this.name}, disabling linter`); 242 | }); 243 | } 244 | 245 | /* istanbul ignore next */ 246 | protected locateFile(directory: string, fileName: string): Promise { 247 | return new Promise((resolve, reject) => { 248 | let parent = directory; 249 | 250 | do { 251 | directory = parent; 252 | 253 | const location: string = (() => { 254 | if (path.isAbsolute(fileName)) { 255 | return fileName; 256 | } else { 257 | return path.join(directory, fileName); 258 | } 259 | })(); 260 | 261 | try { 262 | fs.accessSync(location, fs.constants.R_OK); 263 | resolve(location); 264 | } catch (e) { 265 | // do nothing 266 | } 267 | 268 | parent = path.dirname(directory); 269 | } while (parent !== directory); 270 | 271 | reject('could not locate file within project workspace'); 272 | }); 273 | } 274 | 275 | /* istanbul ignore next */ 276 | protected expandVariables(str: string): IExpansionResult { 277 | process.env.workspaceRoot = this.workspaceRoot; 278 | process.env.workspaceFolder = this.workspaceRoot; 279 | let { value, error } = substituteVariables(str, { env: process.env }); 280 | 281 | if (error) { 282 | return { error: error }; 283 | } else if (value === '') { 284 | return { error: `Expanding '${str}' resulted in an empty string.` }; 285 | } else { 286 | return { result: slash(value) }; 287 | } 288 | } 289 | 290 | /* istanbul ignore next */ 291 | protected buildCommandLine(fileName: string, tmpFileName: string): string[] { 292 | return [this.executable, fileName, tmpFileName]; 293 | } 294 | 295 | protected runLinter(params: string[], workspaceDir: string): child_process.SpawnSyncReturns { 296 | let cmd = params.shift() || this.executable; 297 | 298 | /* istanbul ignore if */ 299 | if (this.settings.debug) { 300 | console.log('executing: ', cmd, params.join(' ')); 301 | } 302 | 303 | return cross_spawn.sync(cmd, params, { 'cwd': workspaceDir, encoding: 'utf8' }); 304 | } 305 | 306 | public lint(fileName: string, directory: null | string, tmpFileName: string): InternalDiagnostic[] { 307 | if (!this.enabled) { return []; } 308 | 309 | let result = this.runLinter(this.buildCommandLine(fileName, tmpFileName), directory || this.workspaceRoot); 310 | let stdout = result.stdout !== null ? result.stdout.replace(/\r/g, '').split('\n') : []; 311 | let stderr = result.stderr !== null ? result.stderr.replace(/\r/g, '').split('\n') : []; 312 | 313 | /* istanbul ignore if */ 314 | if (this.settings.debug) { 315 | console.log(stdout); 316 | console.log(stderr); 317 | } 318 | 319 | /* istanbul ignore if */ 320 | if (result.status !== 0) { 321 | console.log(`${this.name} exited with status code ${result.status}`); 322 | } 323 | 324 | return this.parseLines(stdout.concat(stderr)); 325 | } 326 | 327 | /* istanbul ignore next */ 328 | protected isQuote(ch: string): boolean { 329 | return ch === '\'' || ch === '\"'; 330 | } 331 | 332 | protected parseLines(lines: string[]): InternalDiagnostic[] { 333 | let results: InternalDiagnostic[] = []; 334 | let currentParsed: InternalDiagnostic | null = null; 335 | 336 | lines.forEach(line => { 337 | if (this.isQuote(line.charAt(0))) { 338 | line = line.substring(1); 339 | 340 | if (this.isQuote(line.charAt(line.length - 1))) { 341 | line = line.substring(0, line.length - 1); 342 | } 343 | } 344 | 345 | let parsed = this.parseLine(line); 346 | if (parsed) { 347 | // check for parse error 348 | if (parsed.parseError) { 349 | if (this.settings.ignoreParseErrors) { 350 | console.log(parsed.parseError); 351 | return; 352 | } else { 353 | throw Error(parsed.parseError); 354 | } 355 | } 356 | 357 | ({ currentParsed, parsed } = this.transformParse(currentParsed, parsed)); 358 | 359 | if (currentParsed !== null && !currentParsed.parseError) { 360 | // output an entry 361 | results.push(currentParsed); 362 | } 363 | 364 | currentParsed = parsed; 365 | } 366 | }); 367 | 368 | if (currentParsed !== null) { 369 | // output an entry 370 | results.push(currentParsed); 371 | } 372 | 373 | return results; 374 | } 375 | 376 | /* istanbul ignore next */ 377 | protected transformParse(currentParsed: InternalDiagnostic | null, parsed: InternalDiagnostic | null) { 378 | return { currentParsed: currentParsed, parsed: parsed }; 379 | } 380 | 381 | /* istanbul ignore next */ 382 | protected parseLine(_line: string): InternalDiagnostic | null { 383 | return null; 384 | } 385 | 386 | /* istanbul ignore next */ 387 | protected isValidLanguage(language: string): boolean { 388 | const allowLanguages = ['c', 'c++']; 389 | return _.includes(allowLanguages, language); 390 | } 391 | 392 | /* istanbul ignore next */ 393 | protected getIncludePathParams(): string[] { 394 | let paths = this.includePaths; 395 | let params: string[] = []; 396 | 397 | if (paths) { 398 | _.each(paths, (element: string) => { 399 | let value = this.expandVariables(element); 400 | if (value.error) { 401 | console.log(`Error expanding include path '${element}': ${value.error.message}`); 402 | } else { 403 | params.push(`-I`); 404 | params.push(`${sysPath(value.result!)}`); 405 | } 406 | }); 407 | } 408 | 409 | return params; 410 | } 411 | 412 | /* istanbul ignore next */ 413 | protected expandedArgsFor(key: string, joined: boolean, values: string[] | null, defaults: string[] | null): string[] { 414 | let params: string[] = []; 415 | let elaborateArguments = (element: string) => { 416 | if (element === '') { 417 | return; 418 | } 419 | let value = this.expandVariables(element); 420 | if (value.error) { 421 | console.log(`Error expanding '${element}': ${value.error.message}`); 422 | } else { 423 | if (joined) { 424 | params.push(`${key}${value.result}`); 425 | } else { 426 | params.push(key); 427 | params.push(`${value.result}`); 428 | } 429 | } 430 | }; 431 | 432 | _.each(values ? values : defaults, elaborateArguments); 433 | 434 | return params; 435 | } 436 | } 437 | -------------------------------------------------------------------------------- /server/src/linters/lizard.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { Settings } from '../settings'; 6 | import { Linter } from './linter'; 7 | import { InternalDiagnostic } from '../server'; 8 | import { path as sysPath } from '../utils'; 9 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 10 | 11 | export class Lizard extends Linter { 12 | constructor(settings: Settings, workspaceRoot: string) { 13 | super('Lizard', settings, workspaceRoot, false); 14 | this.cascadeCommonSettings('lizard'); 15 | 16 | this.setExecutable(settings.lizard.executable); 17 | this.active = this.enabled = settings.lizard.enable; 18 | } 19 | 20 | protected buildCommandLine(fileName: string, _tmpFileName: string): string[] { 21 | let args = [this.executable] 22 | .concat(['--warnings_only']) 23 | .concat((this.settings.lizard.extraArgs || []).map(a => this.expandVariables(a).result || a)); 24 | 25 | args.push(sysPath(fileName)); 26 | 27 | return args; 28 | } 29 | 30 | protected parseLine(line: string): InternalDiagnostic | null { 31 | let regex = /^([a-zA-Z]?:?[^:]+):(\d+)?:? warning: (.+)$/; 32 | let regexArray: RegExpExecArray | null; 33 | let excludeRegex = /^$/; 34 | 35 | if (excludeRegex.exec(line) !== null) { 36 | // skip this line 37 | return null; 38 | } 39 | 40 | if ((regexArray = regex.exec(line)) !== null) { 41 | return { 42 | fileName: regexArray[1], 43 | line: parseInt(regexArray[2]) - 1, 44 | column: 0, 45 | severity: DiagnosticSeverity.Warning, 46 | code: 'Cyclomatic complexity', 47 | message: regexArray[3], 48 | source: this.name, 49 | }; 50 | } else { 51 | return { 52 | parseError: 'Line could not be parsed: ' + line, 53 | fileName: '', 54 | line: 0, 55 | column: 0, 56 | severity: DiagnosticSeverity.Error, 57 | code: 0, 58 | message: '', 59 | source: this.name 60 | }; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /server/src/linters/pclintplus.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import * as path from 'path'; 6 | import * as _ from 'lodash'; 7 | import { PclintPlusSeverityMaps, Settings, VS_DiagnosticSeverity } from '../settings'; 8 | import { headerExts, Linter } from './linter'; 9 | import { InternalDiagnostic } from '../server'; 10 | import { path as sysPath } from '../utils'; 11 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 12 | 13 | type ParseData = { 14 | fileName: string, 15 | line: string, 16 | column: string, 17 | }; 18 | 19 | export class PclintPlus extends Linter { 20 | private lastParse: ParseData = {}; 21 | 22 | constructor(settings: Settings, workspaceRoot: string) { 23 | super('PclintPlus', settings, workspaceRoot, true); 24 | this.cascadeCommonSettings('pclintplus'); 25 | 26 | this.setExecutable(settings.pclintplus.executable); 27 | this.setConfigFile(settings.pclintplus.configFile); 28 | this.active = this.enabled = settings.pclintplus.enable; 29 | } 30 | 31 | protected buildCommandLine(fileName: string, _tmpFileName: string): string[] { 32 | let args = [ 33 | this.executable, 34 | this.configFile, 35 | '-v', // turn off verbosity 36 | '-b', // suppress banner output 37 | '-format=%f %l %c %t %n: %m', // format of diagnostic 38 | '-h1', // height of diagnostics message 39 | '-width(0,0)', // width of a line, and continuation indent 40 | '-zero(400)', // exit zero if no warnings at level >= 400 41 | ]; 42 | 43 | if (headerExts.indexOf(path.extname(fileName)) !== -1) { 44 | let hArgs = this.settings.pclintplus.headerArgs; 45 | 46 | if (typeof hArgs === 'string') { 47 | args.push(hArgs); 48 | } else { 49 | hArgs.forEach(element => { 50 | args.push(element); 51 | }); 52 | } 53 | } 54 | 55 | args.push(sysPath(fileName)); 56 | 57 | return args; 58 | } 59 | 60 | protected parseLine(line: string): InternalDiagnostic | null { 61 | let regex = /^(([^ ]+)?\s\s([0-9]+)\s([0-9]+\s)?\s([iI]nfo|[wW]arning|[eE]rror|[nN]ote|[sS]upplemental)\s([0-9]+):\s(.*)|(.+?):([0-9]+):([0-9]+:)?\s([iI]nfo|[wW]arning|[eE]rror|[nN]ote|[sS]upplemental)\s([0-9]+):\s(.*))$/; 62 | let regexArray: RegExpExecArray | null; 63 | 64 | let excludeRegex = /^(\s+file '.*'|PC-lint.*|licensed.*|LICENSED.*|.*evaluation license.*|[^ \t]+|)$/; 65 | if (excludeRegex.exec(line) !== null) { 66 | // skip this line 67 | return null; 68 | } 69 | 70 | if ((regexArray = regex.exec(line)) !== null) { 71 | if (_.every([regexArray[3], regexArray[4], regexArray[5]], el => el !== undefined)) { 72 | if (_.isUndefined(regexArray[2])) { 73 | regexArray[2] = this.lastParse.fileName; 74 | regexArray[3] = this.lastParse.line; 75 | regexArray[4] = this.lastParse.column; 76 | } else { 77 | this.lastParse.fileName = regexArray[2]; 78 | this.lastParse.line = regexArray[3]; 79 | this.lastParse.column = regexArray[4]; 80 | } 81 | return { 82 | fileName: regexArray[2], 83 | line: parseInt(regexArray[3]) - 1, 84 | column: 0, 85 | severity: this.getSeverityCode(regexArray[5].toLowerCase()), 86 | code: regexArray[6], 87 | message: regexArray[7], 88 | source: this.name, 89 | }; 90 | } else { 91 | if (_.isUndefined(regexArray[8])) { 92 | regexArray[8] = this.lastParse.fileName; 93 | regexArray[9] = this.lastParse.line; 94 | regexArray[10] = this.lastParse.column; 95 | } else { 96 | this.lastParse.fileName = regexArray[8]; 97 | this.lastParse.line = regexArray[9]; 98 | this.lastParse.column = regexArray[10]; 99 | } 100 | return { 101 | fileName: regexArray[8], 102 | line: parseInt(regexArray[9]) - 1, 103 | column: 0, 104 | severity: this.getSeverityCode(regexArray[11].toLowerCase()), 105 | code: regexArray[12], 106 | message: regexArray[13], 107 | source: this.name, 108 | }; 109 | } 110 | } else { 111 | return { 112 | parseError: 'Line could not be parsed: ' + line, 113 | fileName: '', 114 | line: 0, 115 | column: 0, 116 | severity: DiagnosticSeverity.Error, 117 | code: 0, 118 | message: '', 119 | source: this.name 120 | }; 121 | } 122 | } 123 | 124 | protected transformParse(currentParsed: InternalDiagnostic | null, parsed: InternalDiagnostic | null) { 125 | if (parsed) { 126 | // Skip over successful completion messages... 127 | if (parsed['code'] === '900') { 128 | parsed = null; 129 | } 130 | } 131 | 132 | return { currentParsed: currentParsed, parsed: parsed }; 133 | } 134 | 135 | private getSeverityCode(severity: string): DiagnosticSeverity { 136 | let output = this.settings.pclintplus.severityLevels[severity as keyof PclintPlusSeverityMaps]; 137 | return VS_DiagnosticSeverity.from(output); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /server/src/settings.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import * as _ from 'lodash'; 6 | import * as os from 'os'; 7 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 8 | 9 | export interface IConfiguration { 10 | name: string; 11 | includePath: string[]; 12 | defines: string[]; 13 | } 14 | 15 | export interface IConfigurations { 16 | configurations: IConfiguration[]; 17 | } 18 | 19 | /* istanbul ignore next */ 20 | export function propertiesPlatform() { 21 | switch (os.platform()) { 22 | case 'darwin': return 'Mac'; 23 | case 'linux': return 'Linux'; 24 | case 'win32': return 'Win32'; 25 | default: 26 | throw RangeError(`Unsupported operating system; no entry for ${os.platform()}`); 27 | } 28 | } 29 | 30 | export type SeverityLevel = DiagnosticSeverity | string; 31 | 32 | export namespace VS_DiagnosticSeverity { 33 | /* istanbul ignore next */ 34 | export function from(value: SeverityLevel): DiagnosticSeverity { 35 | if (_.isNumber(value)) { 36 | return value as DiagnosticSeverity; 37 | } 38 | 39 | if (!_.isString(value)) { 40 | throw TypeError(`The diagnostic code ${value} was neither a number nor string!`); 41 | } 42 | 43 | switch (value) { 44 | case 'Error': return DiagnosticSeverity.Error; 45 | case 'Warning': return DiagnosticSeverity.Warning; 46 | case 'Information': return DiagnosticSeverity.Information; 47 | case 'Hint': return DiagnosticSeverity.Hint; 48 | default: 49 | throw RangeError(`The diagnostic code ${value} has no mapping to DiagnosticSeverty.`); 50 | } 51 | } 52 | } 53 | 54 | export interface CppCheckSeverityMaps { 55 | error: SeverityLevel; 56 | warning: SeverityLevel; 57 | style: SeverityLevel; 58 | performance: SeverityLevel; 59 | portability: SeverityLevel; 60 | information: SeverityLevel; 61 | } 62 | 63 | export interface FlexelintSeverityMaps { 64 | Error: SeverityLevel; 65 | Warning: SeverityLevel; 66 | Info: SeverityLevel; 67 | Note: SeverityLevel; 68 | } 69 | 70 | export interface ClangSeverityMaps { 71 | fatal: SeverityLevel; 72 | error: SeverityLevel; 73 | warning: SeverityLevel; 74 | note: SeverityLevel; 75 | } 76 | 77 | export interface PclintPlusSeverityMaps { 78 | error: SeverityLevel; 79 | warning: SeverityLevel; 80 | info: SeverityLevel; 81 | note: SeverityLevel; 82 | supplemental: SeverityLevel; 83 | } 84 | 85 | export interface FlawFinderSeverityMaps { 86 | '0': SeverityLevel; 87 | '1': SeverityLevel; 88 | '2': SeverityLevel; 89 | '3': SeverityLevel; 90 | '4': SeverityLevel; 91 | '5': SeverityLevel; 92 | } 93 | 94 | export interface Settings { 95 | enable: boolean; 96 | debug: boolean; 97 | run: 'onSave' | 'onType' | 'onBuild'; 98 | ignoreParseErrors: boolean; 99 | 100 | excludeFromWorkspacePaths: string[]; 101 | 102 | // common options, which may be overridden per syntax analyzer 103 | standard: string[]; 104 | includePaths: string[]; 105 | defines: string[]; 106 | undefines: string[]; 107 | language: 'c' | 'c++'; 108 | 109 | flexelint: { 110 | enable: boolean; 111 | executable: string; 112 | configFile: string; 113 | headerArgs: string | string[]; 114 | severityLevels: FlexelintSeverityMaps; 115 | } 116 | pclintplus: { 117 | enable: boolean; 118 | executable: string; 119 | configFile: string; 120 | headerArgs: string | string[]; 121 | severityLevels: PclintPlusSeverityMaps; 122 | } 123 | cppcheck: { 124 | enable: boolean; 125 | executable: string; 126 | configFile: string; 127 | unusedFunctions: boolean; 128 | verbose: boolean; 129 | force: boolean; 130 | inconclusive: boolean; 131 | platform: 'avr8' | 'unix32' | 'unix64' | 'win32A' | 'win32W' | 'win64' | 'native'; 132 | standard: string[] | null; 133 | includePaths: string[] | null; 134 | defines: string[] | null; 135 | undefines: string[] | null; 136 | suppressions: string[]; 137 | addons: string[]; 138 | language: 'c' | 'c++' | null; 139 | severityLevels: CppCheckSeverityMaps; 140 | extraArgs: string[] | null; 141 | } 142 | clang: { 143 | enable: boolean; 144 | executable: string; 145 | configFile: string; 146 | severityLevels: ClangSeverityMaps; 147 | 148 | // common options, which may be overridden per syntax analyzer 149 | standard: string[]; 150 | includePaths: string[]; 151 | defines: string[]; 152 | undefines: string[]; 153 | language: 'c' | 'c++'; 154 | 155 | // special options 156 | extraArgs: string[] | null; 157 | warnings: string[] | null; 158 | pedantic: boolean; 159 | pedanticErrors: boolean; 160 | msExtensions: boolean; 161 | noExceptions: boolean; 162 | noRtti: boolean; 163 | blocks: boolean; 164 | includes: string[] | null; 165 | standardLibs: string[] | null; 166 | } 167 | flawfinder: { 168 | enable: boolean; 169 | executable: string; 170 | severityLevels: FlawFinderSeverityMaps; 171 | } 172 | lizard: { 173 | enable: boolean; 174 | executable: string; 175 | extraArgs: string[] | null; 176 | } 177 | } 178 | 179 | // Settings as defined in VS Code. 180 | export interface GlobalSettings { 181 | 'c-cpp-flylint': Settings; 182 | } 183 | -------------------------------------------------------------------------------- /server/src/utils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | // 5 | // istanbul ignore file 6 | 7 | import * as unixify from 'unixify'; 8 | 9 | export namespace RobustPromises { 10 | type thenableExecutor = () => Thenable; 11 | 12 | export async function retry(retries: number, delay: number, timeout: number, executor: thenableExecutor): Promise { 13 | return new Promise((resolve, reject) => { 14 | const until = async () => { 15 | const failed = async () => { 16 | // eslint-disable-next-line no-console 17 | // console.log(`Try timed out. Retrying...`); 18 | if (--retries > 0) { 19 | setTimeout(until, delay); 20 | } else { 21 | reject(); 22 | } 23 | }; 24 | 25 | let t = setTimeout(failed, timeout); 26 | try { 27 | // eslint-disable-next-line no-console 28 | // console.log(`Try attempts are at ${retries}.`); 29 | const result = await executor(); 30 | clearTimeout(t); 31 | // eslint-disable-next-line no-console 32 | // console.log(`Try succeeded!`); 33 | resolve(result); 34 | } catch (err) { 35 | clearTimeout(t); 36 | // eslint-disable-next-line no-console 37 | console.log(`Try caught an error. ${err}\nRetrying...`); 38 | if (--retries > 0) { 39 | setTimeout(until, delay); 40 | } else { 41 | reject(); 42 | } 43 | } 44 | }; 45 | setTimeout(until, delay); // primer 46 | }); 47 | } 48 | } 49 | 50 | export function sleep(ms: number): Promise { 51 | return new Promise(resolve => { 52 | setTimeout(resolve, ms); 53 | }); 54 | } 55 | 56 | // Reference: https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats 57 | export function path(path: string): string { 58 | let lc = path.slice(0, 1).toLowerCase(); 59 | if (lc >= 'a' && lc <= 'z' && path.slice(1, 2) === ':') { 60 | if (path.slice(2, 3) === '\\' || path.slice(2, 3) === '/') { 61 | var segs = path.split(/[/\\]+/); 62 | if (segs[segs.length - 1] === '') { 63 | segs.pop(); 64 | } 65 | return segs.join('/'); 66 | } else { 67 | // Windows: Make a relative MSYS-compatible path 68 | path = path.slice(2); 69 | } 70 | } else if (path.slice(0, 2) === '\\\\') { 71 | // Windows: Ensure UNC paths keep initial slashes 72 | return '/' + unixify(path); 73 | } 74 | return unixify(path); 75 | } 76 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "target": "es2017", 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "lib": [ "es2017" ], 9 | "sourceMap": true 10 | }, 11 | "include": [ 12 | "src", 13 | "../typings.d.ts" 14 | ], 15 | "exclude": [ 16 | "node_modules" 17 | ] 18 | } -------------------------------------------------------------------------------- /server/webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 'use strict'; 3 | 4 | const withDefaults = require('../shared.webpack.config'); 5 | const path = require('path'); 6 | 7 | module.exports = withDefaults({ 8 | context: path.join(__dirname), 9 | entry: { 10 | extension: './src/server.ts', 11 | }, 12 | output: { 13 | filename: 'server.js', 14 | path: path.join(__dirname, 'out'), 15 | devtoolModuleFilenameTemplate: "../[resource-path]", 16 | hashFunction: 'xxhash64' 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /shared.webpack.config.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | //@ts-check 7 | /** @typedef {import('webpack').Configuration} WebpackConfig **/ 8 | 9 | 'use strict'; 10 | 11 | const path = require('path'); 12 | const merge = require('merge-options'); 13 | 14 | module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { 15 | 16 | /** @type WebpackConfig */ 17 | let defaultConfig = { 18 | mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 19 | target: 'node', // extensions run in a node context 20 | node: { 21 | __dirname: false // leave the __dirname-behaviour intact 22 | }, 23 | resolve: { 24 | mainFields: ['module', 'main'], 25 | extensions: ['.ts', '.js'] // support ts-files and js-files 26 | }, 27 | module: { 28 | rules: [{ 29 | test: /\.ts$/, 30 | exclude: /node_modules/, 31 | use: [{ 32 | // configure TypeScript loader: 33 | // * enable sources maps for end-to-end source maps 34 | loader: 'ts-loader', 35 | options: { 36 | compilerOptions: { 37 | "sourceMap": true 38 | } 39 | } 40 | }] 41 | }] 42 | }, 43 | externals: { 44 | 'vscode': 'commonjs vscode' // ignored because it doesn't exist 45 | }, 46 | output: { 47 | // all output goes into `dist`. 48 | // packaging depends on that and this must always be like it 49 | filename: '[name].js', 50 | path: path.join(extConfig.context, 'out'), 51 | libraryTarget: "commonjs", 52 | hashFunction: 'xxhash64' 53 | }, 54 | // yes, really source maps 55 | devtool: 'source-map' 56 | }; 57 | 58 | return merge(defaultConfig, extConfig); 59 | }; 60 | -------------------------------------------------------------------------------- /specs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "../.eslintrc.base.json", 4 | "rules": { 5 | "no-console": "error" 6 | } 7 | } -------------------------------------------------------------------------------- /specs/debug-console/colorize.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: The Microsoft Corporation 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | function colorize(colorCode: string) { 8 | return (text: string): string => `\u001B[${colorCode}m${text}\u001B[0m`; 9 | } 10 | 11 | export const bold = colorize('1'); 12 | export const black = colorize('30'); 13 | export const red = colorize('31'); 14 | export const green = colorize('32'); 15 | export const yellow = colorize('33'); 16 | export const purple = colorize('35'); 17 | export const redBg = colorize('41'); 18 | export const greenBg = colorize('42'); 19 | export const darkGray = colorize('90'); 20 | export const white = colorize('97'); 21 | -------------------------------------------------------------------------------- /specs/debug-console/logger.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: The Microsoft Corporation 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | import { bold, black, green, purple, red, yellow, darkGray, greenBg, redBg } from './colorize'; 8 | 9 | export var didFailure: boolean = false; 10 | 11 | export function logger(text: string): string | boolean { 12 | text = text.replace(/\n$/, ''); 13 | 14 | let message = formatTestHeader(text); 15 | 16 | if (!message) { 17 | message = formatTestDescription(text); 18 | } 19 | 20 | if (!message) { 21 | message = formatTestError(text); 22 | } 23 | 24 | if (!message) { 25 | message = formatSnapshotMessage(text); 26 | } 27 | 28 | if (!message) { 29 | message = formatTestSummary(text); 30 | } 31 | 32 | if (process.env.NODE_ENV === 'unknown') { 33 | return message || text; 34 | } 35 | 36 | /* eslint-disable no-console */ 37 | console.log(message || text); 38 | return true; 39 | } 40 | 41 | function formatTestHeader(text: string): string { 42 | const filepath = text.replace(/^(PASS|FAIL)/, '').trim(); 43 | const [testFilename, ...testPathParts] = filepath.split('/').reverse(); 44 | const testPath = testPathParts.reverse().join('/'); 45 | 46 | if (text.startsWith('PASS')) { 47 | return `${bold(greenBg(black(' PASS ')))} ${darkGray(`${testPath}/`)}${bold(testFilename)}`; 48 | } 49 | 50 | if (text.startsWith('FAIL')) { 51 | didFailure = true; 52 | return `${bold(redBg(black(' FAIL ')))} ${darkGray(`${testPath}/`)}${bold(testFilename)}`; 53 | } 54 | 55 | return ''; 56 | } 57 | 58 | function formatTestDescription(text: string): string { 59 | if (text.includes('✓')) { 60 | return ` ${green('✓')} ${darkGray(text.replace(/✓/, '').trim())}`; 61 | } 62 | 63 | if (text.includes('✕')) { 64 | return ` ${red('✕')} ${darkGray(text.replace(/✕/, '').trim())}`; 65 | } 66 | 67 | if (text.includes('○')) { 68 | return ` ${yellow('○')} ${darkGray(text.replace(/○/, '').trim())}`; 69 | } 70 | 71 | if (text.includes('✎')) { 72 | return ` ${purple('✎')} ${darkGray(text.replace(/✎/, '').trim())}`; 73 | } 74 | 75 | return ''; 76 | } 77 | 78 | function formatTestError(text: string): string { 79 | return text.includes('●') ? red(text) : ''; 80 | } 81 | 82 | function formatSnapshotMessage(text: string): string { 83 | if (text.endsWith('updated.') || text.endsWith('written.') || text.endsWith('removed.')) { 84 | return bold(green(text)); 85 | } 86 | 87 | if (text.endsWith('obsolete.')) { 88 | return bold(yellow(text)); 89 | } 90 | 91 | if (text.endsWith('failed.')) { 92 | return bold(red(text)); 93 | } 94 | 95 | if (text === 'Snapshot Summary') { 96 | return bold(text); 97 | } 98 | 99 | if (text.includes('written from')) { 100 | return formatSnapshotSummary(text, 'written', green); 101 | } 102 | 103 | if (text.includes('updated from')) { 104 | return formatSnapshotSummary(text, 'updated', green); 105 | } 106 | 107 | if (text.includes('removed from')) { 108 | // Use custom messaging for removed snapshot files 109 | if (text.includes('file')) { 110 | const [numSnapshots, numTestSuites] = /(\d)+/.exec(text)!; 111 | return ` ${bold( 112 | green(`› ${numSnapshots} snapshot ${Number(numSnapshots) > 1 ? 'files' : 'file'} removed`) 113 | )} from ${numTestSuites} ${Number(numTestSuites) > 1 ? 'test suites' : 'test suite'}.`; 114 | } 115 | 116 | return formatSnapshotSummary(text, 'removed', green); 117 | } 118 | 119 | if (text.includes('obsolete from')) { 120 | return `${formatSnapshotSummary(text, 'obsolete', yellow)} ${darkGray( 121 | 'To remove them all, re-run jest with `JEST_RUNNER_UPDATE_SNAPSHOTS=true`.' 122 | )}`; 123 | } 124 | 125 | if (text.includes('↳')) { 126 | const filepath = text.replace(/↳/, '').trim(); 127 | const [testFilename, ...testPathParts] = filepath.split('/').reverse(); 128 | const testPath = testPathParts.reverse().join('/'); 129 | return ` ↳ ${darkGray(`${testPath}/`)}${bold(testFilename)}`; 130 | } 131 | 132 | if (text.includes('failed from')) { 133 | return `${formatSnapshotSummary(text, 'failed', red)} ${darkGray( 134 | 'Inspect your code changes or re-run jest with `JEST_RUNNER_UPDATE_SNAPSHOTS=true` to update them.' 135 | )}`; 136 | } 137 | 138 | return ''; 139 | } 140 | 141 | function formatSnapshotSummary( 142 | text: string, 143 | status: 'written' | 'updated' | 'failed' | 'removed' | 'obsolete', 144 | colorFunc: (text: string) => string 145 | ): string { 146 | const [numSnapshots, numTestSuites] = /(\d)+/.exec(text)!; 147 | return ` ${bold( 148 | colorFunc(`› ${numSnapshots} ${Number(numSnapshots) > 1 ? 'snapshots' : 'snapshot'} ${status}`) 149 | )} from ${numTestSuites} ${Number(numTestSuites) > 1 ? 'test suites' : 'test suite'}.`; 150 | } 151 | 152 | function formatTestSummary(text: string): string { 153 | if (!text.includes('\n')) { 154 | return ''; 155 | } 156 | 157 | const summary = []; 158 | 159 | for (let line of text.split('\n')) { 160 | if (line.includes('Ran all test suites.')) { 161 | summary.push(darkGray(line)); 162 | continue; 163 | } 164 | 165 | if (line.includes('Test Suites:')) { 166 | line = line.replace('Test Suites:', bold('Test Suites:')); 167 | } 168 | 169 | if (line.includes('Tests:')) { 170 | line = line.replace('Tests:', bold('Tests:')); 171 | } 172 | 173 | if (line.includes('Snapshots:')) { 174 | line = line.replace('Snapshots:', bold('Snapshots:')); 175 | } 176 | 177 | if (line.includes('Time:')) { 178 | line = line.replace('Time:', bold('Time:')); 179 | } 180 | 181 | if (line.includes('passed')) { 182 | line = line.replace(/(?\d+) passed/, bold(green('$ passed'))); 183 | } 184 | 185 | if (line.includes('updated')) { 186 | line = line.replace(/(?\d+) updated/, bold(green('$ updated'))); 187 | } 188 | 189 | if (line.includes('written')) { 190 | line = line.replace(/(?\d+) written/, bold(green('$ written'))); 191 | } 192 | 193 | if (line.includes('removed')) { 194 | // Use custom messaging for removed snapshot files 195 | line = line.replace(/(?\d+) (?file|files) removed/, bold(green('$ $ removed'))); 196 | line = line.replace(/(?\d+) removed/, bold(green('$ removed'))); 197 | } 198 | 199 | if (line.includes('todo')) { 200 | line = line.replace(/(?\d+) todo/, bold(purple('$ todo'))); 201 | } 202 | 203 | if (line.includes('skipped')) { 204 | line = line.replace(/(?\d+) skipped/, bold(yellow('$ skipped'))); 205 | } 206 | 207 | if (line.includes('obsolete')) { 208 | line = line.replace(/(?\d+) obsolete/, bold(yellow('$ obsolete'))); 209 | } 210 | 211 | if (line.includes('failed')) { 212 | line = line.replace(/(?\d+) failed/, bold(red('$ failed'))); 213 | } 214 | 215 | summary.push(line); 216 | } 217 | 218 | return summary.join('\n'); 219 | } 220 | -------------------------------------------------------------------------------- /specs/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: The Microsoft Corporation 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | import * as path from 'path'; 8 | import { run as runJest } from 'jest-cli'; 9 | import { didFailure, logger } from './debug-console/logger'; 10 | 11 | const rootDir = path.resolve(process.cwd(), '.'); 12 | 13 | export async function run(): Promise { 14 | //process.stdout.write = (text: string) => !!logger(text); 15 | process.stderr.write = (text: string) => !!logger(text); 16 | 17 | let args: string[] = []; 18 | if (process.env.JEST_ARGS) { 19 | args = JSON.parse(process.env.JEST_ARGS); 20 | } 21 | 22 | args.push( 23 | '--runInBand', 24 | '--useStderr', 25 | '--env=vscode', 26 | '--colors', 27 | '--watchman=false', 28 | `--roots=${rootDir}`, 29 | `--setupFilesAfterEnv=${path.resolve(__dirname, './setup.js')}`, 30 | ); 31 | 32 | try { 33 | await runJest(args, rootDir); 34 | } catch (e) { 35 | /* eslint-disable no-console */ 36 | console.error(e); 37 | process.exit(1); 38 | } 39 | 40 | if (didFailure) { process.exit(1); } 41 | } 42 | -------------------------------------------------------------------------------- /specs/mock-config.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | // 5 | // istanbul ignore file 6 | 7 | import { GlobalSettings } from '../server/src/settings'; 8 | 9 | export const isWindows = 10 | process.platform === 'win32' || 11 | process.env.OSTYPE === 'cygwin' || 12 | process.env.OSTYPE === 'msys'; 13 | 14 | export const defaultConfig: GlobalSettings = { 15 | 'c-cpp-flylint': { 16 | enable: true, 17 | debug: false, 18 | run: 'onSave', 19 | 20 | standard: ['c99'], 21 | includePaths: [], 22 | defines: [], 23 | undefines: [], 24 | language: 'c', 25 | ignoreParseErrors: false, 26 | excludeFromWorkspacePaths: [], 27 | 28 | flexelint: { 29 | enable: true, 30 | executable: (isWindows ? 'flexelint.exe' : 'flexelint'), 31 | configFile: 'tsconfig.json', 32 | headerArgs: [ 33 | '-e750', 34 | '-e751', 35 | '-e752', 36 | '-e753', 37 | '-e754', 38 | '-e1526', 39 | '-e1714' 40 | ], 41 | severityLevels: { 42 | Error: 'Error', 43 | Warning: 'Warning', 44 | Info: 'Information', 45 | Note: 'Hint' 46 | } 47 | }, 48 | 49 | pclintplus: { 50 | enable: true, 51 | executable: (isWindows ? 'pclp.exe' : 'pclp'), 52 | configFile: 'tsconfig.json', 53 | headerArgs: [ 54 | '-e750', 55 | '-e751', 56 | '-e752', 57 | '-e753', 58 | '-e754', 59 | '-e1526', 60 | '-e1714' 61 | ], 62 | severityLevels: { 63 | error: 'Error', 64 | warning: 'Warning', 65 | info: 'Information', 66 | note: 'Hint', 67 | supplemental: 'Hint' 68 | } 69 | }, 70 | 71 | cppcheck: { 72 | enable: true, 73 | executable: (isWindows ? 'cppcheck.exe' : 'cppcheck'), 74 | configFile: '.clang_complete', 75 | unusedFunctions: false, 76 | verbose: false, 77 | force: false, 78 | inconclusive: false, 79 | platform: 'native', 80 | suppressions: [], 81 | severityLevels: { 82 | error: 'Error', 83 | warning: 'Warning', 84 | style: 'Information', 85 | performance: 'Warning', 86 | portability: 'Warning', 87 | information: 'Information' 88 | }, 89 | standard: ['c99'], 90 | includePaths: [], 91 | defines: [], 92 | undefines: [], 93 | language: 'c', 94 | addons: [], 95 | extraArgs: null, 96 | }, 97 | 98 | clang: { 99 | enable: true, 100 | executable: (isWindows ? 'clang.exe' : 'clang'), 101 | configFile: '.clang_complete', 102 | severityLevels: { 103 | error: 'Error', 104 | fatal: 'Error', 105 | warning: 'Warning', 106 | note: 'Information' 107 | }, 108 | standard: ['c99'], 109 | includePaths: [], 110 | defines: [], 111 | undefines: [], 112 | language: 'c', 113 | extraArgs: null, 114 | warnings: ['all', 'extra', 'everything'], 115 | pedantic: false, 116 | pedanticErrors: false, 117 | msExtensions: false, 118 | noExceptions: true, 119 | noRtti: true, 120 | blocks: true, 121 | includes: null, 122 | standardLibs: null 123 | }, 124 | 125 | flawfinder: { 126 | enable: true, 127 | executable: 'flawfinder', 128 | severityLevels: { 129 | 5: 'Error', 130 | 4: 'Warning', 131 | 3: 'Information', 132 | 2: 'Information', 133 | 1: 'Information', 134 | 0: 'Information' 135 | } 136 | }, 137 | 138 | lizard: { 139 | enable: true, 140 | executable: 'lizard', 141 | extraArgs: null, 142 | } 143 | } 144 | }; 145 | -------------------------------------------------------------------------------- /specs/mock-fs.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | // 5 | // istanbul ignore file 6 | 7 | import * as mock from 'mock-fs'; 8 | import { afterEach, beforeEach } from '@jest/globals'; 9 | 10 | export function injectMockFileSystem() { 11 | beforeEach(() => { 12 | mock({ 13 | '.clang_complete': 'text content', 14 | 15 | 'tsconfig.json': 'text content', 16 | 17 | // fake EXE for Windows users 18 | 'flexelint.exe': mock.file({ 19 | content: 'I MISS DOS...', 20 | mode: 0o755 21 | }), 22 | 23 | // fake binary for non-Windows users 24 | 'flexelint': mock.file({ 25 | content: '#!/usr/bin/env bash\n\nexit 0\n', 26 | mode: 0o755 27 | }), 28 | 29 | // fake EXE for Windows users 30 | 'pclp.exe': mock.file({ 31 | content: 'I MISS DOS...', 32 | mode: 0o755 33 | }), 34 | 35 | // fake binary for non-Windows users 36 | 'pclp': mock.file({ 37 | content: '#!/usr/bin/env bash\n\nexit 0\n', 38 | mode: 0o755 39 | }), 40 | 41 | // fake EXE for Windows users 42 | 'cppcheck.exe': mock.file({ 43 | content: 'I MISS DOS...', 44 | mode: 0o755 45 | }), 46 | 47 | // fake binary for non-Windows users 48 | 'cppcheck': mock.file({ 49 | content: '#!/usr/bin/env bash\n\nexit 0\n', 50 | mode: 0o755 51 | }), 52 | 53 | // fake EXE for Windows users 54 | 'clang.exe': mock.file({ 55 | content: 'I MISS DOS...', 56 | mode: 0o755 57 | }), 58 | 59 | // fake binary for non-Windows users 60 | 'clang': mock.file({ 61 | content: '#!/usr/bin/env bash\n\nexit 0\n', 62 | mode: 0o755 63 | }), 64 | 65 | // fake binary for non-Windows users 66 | 'flawfinder': mock.file({ 67 | content: '#!/usr/bin/env bash\n\nexit 0\n', 68 | mode: 0o755 69 | }), 70 | 71 | // fake binary for non-Windows users 72 | 'lizard': mock.file({ 73 | content: '#!/usr/bin/env bash\n\nexit 0\n', 74 | mode: 0o755 75 | }), 76 | 77 | '.gitignore': mock.file({ 78 | content: 'git ignore content', 79 | mode: 0o644 80 | }), 81 | }); 82 | }); 83 | 84 | afterEach(() => { 85 | mock.restore(); 86 | }); 87 | } 88 | -------------------------------------------------------------------------------- /specs/runSpecs.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { downloadAndUnzipVSCode, resolveCliPathFromVSCodeExecutablePath, runTests } from '@vscode/test-electron'; 6 | import * as cp from 'child_process'; 7 | import * as path from 'path'; 8 | 9 | async function main() { 10 | try { 11 | // The folder containing the Extension Manifest package.json 12 | // Passed to `--extensionDevelopmentPath` 13 | const extensionDevelopmentPath = path.resolve(__dirname, '../../..'); 14 | 15 | // The path to the extension test runner script 16 | // Passed to --extensionTestsPath 17 | const extensionTestsPath = path.resolve(__dirname, './index'); 18 | 19 | const vscodeExecutablePath = await downloadAndUnzipVSCode('1.62.2'); 20 | const cliPath = resolveCliPathFromVSCodeExecutablePath(vscodeExecutablePath); 21 | 22 | // Install the C/C++ base tools 23 | cp.spawnSync(cliPath, ['--install-extension', 'ms-vscode.cpptools@1.7.1'], { 24 | encoding: 'utf-8', 25 | stdio: 'inherit' 26 | }); 27 | 28 | process.chdir(extensionDevelopmentPath); 29 | 30 | var launchArgs: string[] = [ 31 | '--disable-gpu', 32 | '--disable-updates', 33 | '--disable-workspace-trust', 34 | '--disable-crash-reporter', 35 | '--disable-telemetry', 36 | '--skip-welcome', 37 | '--skip-release-notes', 38 | '--no-sandbox', 39 | '--ms-enable-electron-run-as-node', 40 | '--disable-keytar', 41 | '--sync=off', 42 | '--disable-user-env', 43 | '--logExtensionHostCommunication' 44 | ]; 45 | 46 | // TODO(jbenden): `process.env.CODE_DEBUG` fails to work under Windows. 47 | if (process.env.CODE_DEBUG) { 48 | launchArgs = launchArgs.concat('--log=debug').concat('--verbose'); 49 | } else if (process.env.CODE_VERBOSE) { 50 | launchArgs = launchArgs.concat('--verbose'); 51 | } 52 | 53 | if (process.env.INSPECT_PORT) { 54 | launchArgs = launchArgs.concat(`--nolazy`, `--inspect='${process.env.INSPECT_PORT}'`); 55 | } 56 | 57 | // Run the extension test 58 | process.exit(await runTests({ 59 | // Use the specified `code` executable 60 | vscodeExecutablePath, 61 | extensionDevelopmentPath, 62 | extensionTestsPath, 63 | launchArgs 64 | })); 65 | } catch (err) { 66 | /* eslint-disable no-console */ 67 | console.error(err); 68 | /* eslint-disable no-console */ 69 | console.error('Failed to run tests'); 70 | process.exit(1); 71 | } 72 | } 73 | 74 | main(); 75 | -------------------------------------------------------------------------------- /specs/setup.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: The Microsoft Corporation 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /* istanbul ignore file */ 8 | 9 | process.env.NODE_ENV = 'test'; 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 12 | jest.mock('vscode', () => (global as any).vscode, { virtual: true }); 13 | -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "${workspaceRoot}", 7 | "/usr/include", 8 | "/usr/local/include" 9 | ], 10 | "defines": [], 11 | "browse": { 12 | "path": [ 13 | "/usr/include", 14 | "/usr/local/include", 15 | "${workspaceRoot}" 16 | ], 17 | "limitSymbolsToIncludedHeaders": true, 18 | "databaseFilename": "" 19 | }, 20 | "intelliSenseMode": "clang-x64", 21 | "macFrameworkPath": [ 22 | "/System/Library/Frameworks", 23 | "/Library/Frameworks" 24 | ] 25 | }, 26 | { 27 | "name": "Linux", 28 | "includePath": [ 29 | "${workspaceRoot}", 30 | "${workspaceRoot}/lib", 31 | "${workspaceRoot}/popt", 32 | "${workspaceRoot}/zlib", 33 | "/usr/lib/gcc/x86_64-linux-gnu/5/include", 34 | "/usr/local/include", 35 | "/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed", 36 | "/usr/include/x86_64-linux-gnu", 37 | "/usr/include" 38 | ], 39 | "defines": [ 40 | "HAVE_CONFIG_H" 41 | ], 42 | "browse": { 43 | "path": [ 44 | "/usr/include", 45 | "/usr/local/include", 46 | "${workspaceRoot}" 47 | ], 48 | "limitSymbolsToIncludedHeaders": true, 49 | "databaseFilename": "" 50 | }, 51 | "intelliSenseMode": "clang-x64" 52 | }, 53 | { 54 | "name": "Win32", 55 | "intelliSenseMode": "clang-x64", 56 | "includePath": [ 57 | "${workspaceRoot}", 58 | "C:/msys64/usr/lib/gcc/x86_64-pc-msys/6.3.0/include", 59 | "C:/msys64/usr/include", 60 | "C:/msys64/usr/include/sys", 61 | "C:/msys64/usr/include/w32api" 62 | ], 63 | "defines": [ 64 | "_DEBUG", 65 | "UNICODE", 66 | "_UNICODE", 67 | "_WIN32", 68 | "WINVER=0x0600", 69 | "_WIN32_WINNT=0x0600", 70 | "__MSVCRT_VERSION__=0x0800" 71 | ], 72 | "browse": { 73 | "path": [ 74 | "C:/msys64/usr/lib/gcc/x86_64-pc-msys/6.3.0/include", 75 | "C:/msys64/usr/include", 76 | "C:/msys64/usr/include/sys", 77 | "C:/msys64/usr/include/w32api", 78 | "${workspaceRoot}" 79 | ], 80 | "limitSymbolsToIncludedHeaders": true, 81 | "databaseFilename": "" 82 | } 83 | } 84 | ], 85 | "version": 3 86 | } -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "${workspaceRoot}/**/*", 7 | "/usr/include", 8 | "/usr/local/include" 9 | ], 10 | "defines": [], 11 | "browse": { 12 | "path": [ 13 | "/usr/include", 14 | "/usr/local/include", 15 | "${workspaceRoot}" 16 | ], 17 | "limitSymbolsToIncludedHeaders": true, 18 | "databaseFilename": "" 19 | }, 20 | "intelliSenseMode": "clang-x64", 21 | "macFrameworkPath": [ 22 | "/System/Library/Frameworks", 23 | "/Library/Frameworks" 24 | ], 25 | "compilerPath": "/usr/bin/clang", 26 | "cStandard": "c17", 27 | "cppStandard": "c++14" 28 | }, 29 | { 30 | "name": "Linux", 31 | "includePath": [ 32 | "${workspaceRoot}/**/*", 33 | "/usr/lib/gcc/x86_64-linux-gnu/5/include", 34 | "/usr/local/include", 35 | "/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed", 36 | "/usr/include/x86_64-linux-gnu", 37 | "/usr/include" 38 | ], 39 | "defines": [ 40 | "HAVE_CONFIG_H" 41 | ], 42 | "browse": { 43 | "path": [ 44 | "/usr/include", 45 | "/usr/local/include", 46 | "${workspaceRoot}" 47 | ], 48 | "limitSymbolsToIncludedHeaders": true, 49 | "databaseFilename": "" 50 | }, 51 | "intelliSenseMode": "clang-x64", 52 | "compilerPath": "/usr/bin/clang", 53 | "cStandard": "c17", 54 | "cppStandard": "c++14" 55 | }, 56 | { 57 | "name": "Win32", 58 | "intelliSenseMode": "clang-x64", 59 | "includePath": [ 60 | "${workspaceRoot}/**/*", 61 | "C:/msys64/usr/lib/gcc/x86_64-pc-msys/6.3.0/include", 62 | "C:/msys64/usr/include", 63 | "C:/msys64/usr/include/sys", 64 | "C:/msys64/usr/include/w32api" 65 | ], 66 | "defines": [ 67 | "_DEBUG", 68 | "UNICODE", 69 | "_UNICODE", 70 | "_WIN32", 71 | "WINVER=0x0600", 72 | "_WIN32_WINNT=0x0600", 73 | "__MSVCRT_VERSION__=0x0800" 74 | ], 75 | "browse": { 76 | "path": [ 77 | "C:/msys64/usr/lib/gcc/x86_64-pc-msys/6.3.0/include", 78 | "C:/msys64/usr/include", 79 | "C:/msys64/usr/include/sys", 80 | "C:/msys64/usr/include/w32api", 81 | "${workspaceRoot}" 82 | ], 83 | "limitSymbolsToIncludedHeaders": true, 84 | "databaseFilename": "" 85 | }, 86 | "compilerPath": "/usr/bin/clang", 87 | "cStandard": "c17", 88 | "cppStandard": "c++14" 89 | } 90 | ], 91 | "version": 4 92 | } -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "cSpell.enabled": false, 4 | "editor.formatOnSave": true, 5 | "c-cpp-flylint.clang.enable": true, 6 | "c-cpp-flylint.clang.executable": "clang", 7 | "c-cpp-flylint.cppcheck.enable": false, 8 | "c-cpp-flylint.cppcheck.force": true, 9 | // "c-cpp-flylint.cppcheck.extraArgs": ["-j", "6"], 10 | "c-cpp-flylint.debug": true, 11 | "c-cpp-flylint.flexelint.executable": "/home/jbenden/Source/FlexeLint_Workstation/flexelint8/src/flexelint", 12 | "c-cpp-flylint.flexelint.enable": false, 13 | "c-cpp-flylint.pclintplus.enable": true, 14 | "files.associations": { 15 | "*.rst": "restructuredtext", 16 | "rsync.h": "c", 17 | "deflate.h": "c", 18 | "fileapi.h": "c", 19 | "errhandlingapi.h": "c", 20 | "winbase.h": "c", 21 | "stat.h": "c", 22 | "winnt.h": "c" 23 | }, 24 | "files.exclude": { 25 | "**/.git": true, 26 | "**/.svn": true, 27 | "**/.hg": true, 28 | "**/CVS": true, 29 | "**/.DS_Store": true, 30 | "**/*.o": true, 31 | "**/*.exe": true, 32 | "**/{configure,Makefile}": true, 33 | "**/config.{guess,h,log,status,sub}": true, 34 | "**/*-tstamp": true, 35 | "**/{GPATH,GRTAGS,GTAGS}": true 36 | }, 37 | "c-cpp-flylint.run": "onSave", 38 | "c-cpp-flylint.pclintplus.enable": false, 39 | "c-cpp-flylint.pclintplus.executable": "/bin/echo1", 40 | "c-cpp-flylint.pclintplus.configFile": ".flexelint.lnt", 41 | "c-cpp-flylint.excludeFromWorkspacePaths": [ 42 | ".git", 43 | "${workspaceRoot}/include/hal" 44 | ] 45 | } -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "echo", 8 | "type": "shell", 9 | "command": "echo Hello", 10 | "group": "build", 11 | "problemMatcher": [ 12 | "$gcc" 13 | ] 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/c_cpp_properties.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include 6 | #include 7 | 8 | int main(int argc, char* argv[]) { 9 | /** 10 | * Generates an input and send it to stdout, 11 | * takes one argument for the length of input 12 | * 13 | * generate_input [len] 14 | * 15 | */ 16 | 17 | if (argc != 2) { 18 | printf("error: only 1 argument expected (%i given)\n", argc - 1); 19 | } 20 | 21 | int len = argv[1]; 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/a/aa/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/a/aa/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/a/bb/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/a/bb/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/a/cc/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/a/cc/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/b/aa/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/b/aa/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/b/bb/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/b/bb/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/b/cc/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/b/cc/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/c/aa/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/c/aa/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/c/bb/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/c/bb/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/c/cc/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/c/cc/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/d/aa/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/d/aa/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/d/bb/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/d/bb/.gitkeep -------------------------------------------------------------------------------- /specs/suite/fixtures/c_cpp_properties/inc/d/cc/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbenden/vscode-c-cpp-flylint/f4599c171154c2ac921e25cc546cdb1a9f67be73/specs/suite/fixtures/c_cpp_properties/inc/d/cc/.gitkeep -------------------------------------------------------------------------------- /specs/suite/linter-clang.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { cloneDeep } from 'lodash'; 6 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 7 | import { Clang } from '../../server/src/linters/clang'; 8 | import { Linter } from '../../server/src/linters/linter'; 9 | import { Settings } from '../../server/src/settings'; 10 | import { defaultConfig } from '../mock-config'; 11 | import { injectMockFileSystem } from '../mock-fs'; 12 | import { FLYLINT_ID } from '../../server/src/server'; 13 | 14 | describe('Clang parser', () => { 15 | injectMockFileSystem(); 16 | 17 | let config: Settings; 18 | let linter: Linter; 19 | 20 | beforeEach(() => { 21 | config = cloneDeep(defaultConfig[FLYLINT_ID]); 22 | linter = new Clang(config, process.cwd()); 23 | }); 24 | 25 | test('should build a proper DOS command-line for a C++ source file', () => { 26 | // this method call syntax permits protected/private method calling; due to JavaScript. 27 | const actual = linter['buildCommandLine']('C:\\Project\\main.cc', 'C:\\Project\\main.cc'); 28 | expect(actual).toHaveLength(17); 29 | expect(actual.slice(-1)).toStrictEqual(['C:/Project/main.cc']); 30 | }); 31 | 32 | test('should build a proper POSIX command-line for a C++ source file', () => { 33 | // this method call syntax permits protected/private method calling; due to JavaScript. 34 | const actual = linter['buildCommandLine']('/usr/src/main.cc', '/usr/src/main.cc'); 35 | expect(actual).toHaveLength(17); 36 | expect(actual.slice(-1)).toStrictEqual(['/usr/src/main.cc']); 37 | }); 38 | 39 | test('should build a proper command-line for a C++ header file', () => { 40 | const actual = linter['buildCommandLine']('main.h', 'main.h'); 41 | expect(actual).toHaveLength(17); 42 | }); 43 | 44 | test('should handle parsing an invalid line', () => { 45 | const actual = linter['parseLine']('should not parse!')!; 46 | expect(actual).toHaveProperty('parseError'); 47 | }); 48 | 49 | test('should correctly parse multiple lines', () => { 50 | const test = [ 51 | `rounding.c:35:51: error: use of undeclared identifier 'EXTRA_ROUNDING'`, 52 | `rounding.c:26:30: error: use of undeclared identifier 'EXTRA_ROUNDING'`, 53 | `rounding.c:31:52: note: expanded from macro 'EXPECTED_SIZE'`, 54 | `rounding.c:22:20: note: expanded from macro 'ARRAY_LEN'`, 55 | `rounding.c:22:20: note: expanded from macro 'ARRAY_LEN'`, 56 | `./rsync.h:673:42: warning: 'HAVE_HPUX_ACLS' is not defined, evaluates to 0`, 57 | `In file included from rounding.c:20:`, 58 | `In file included from ./rsync.h:977:`, 59 | `./byteorder.h:91:23: warning: cast from 'const char *' to 'unsigned char * drops const qualifier`, 60 | `In file included from rounding.c:20:`, 61 | `./rsync.h:1159:9: warning: macro name is a reserved identifier`, 62 | `./rsync.h:1163:9: warning: macro name is a reserved identifier`, 63 | `rounding.c:26:30: error: use of undeclared identifier 'EXTRA_ROUNDING'`, 64 | `rounding.c:22:20: note: expanded from macro 'ARRAY_LEN'`, 65 | `rounding.c:35:51: error: use of undeclared identifier 'EXTRA_ROUNDING'`, 66 | `rounding.c:31:52: note: expanded from macro 'EXPECTED_SIZE'`, 67 | `rounding.c:22:20: note: expanded from macro 'ARRAY_LEN'`, 68 | ]; 69 | const actual = linter['parseLines'](test); 70 | 71 | expect(actual).toHaveLength(17); 72 | 73 | let result = actual.pop()!; 74 | 75 | expect(result).toHaveProperty('fileName', 'rounding.c'); 76 | expect(result).toHaveProperty('line', 21); 77 | expect(result).toHaveProperty('column', 19); 78 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Information); 79 | expect(result['message']).toMatch(/^expanded from macro \'ARRAY_LEN\'/); 80 | }); 81 | 82 | test('should handle excluded lines during parse', () => { 83 | const test = [ 84 | `warning: include location '/usr/local/include' is unsafe for cross-compilation [-Wpoison-system-directories]`, 85 | `/Users/user/cpp-test/main.cpp:8:2: warning: C++98 requires newline at end of file [Lexical or Preprocessor Issue]` 86 | ]; 87 | const actual = linter['parseLines'](test); 88 | 89 | expect(actual).toHaveLength(1); 90 | 91 | let result = actual.pop()!; 92 | 93 | expect(result).toHaveProperty('fileName', '/Users/user/cpp-test/main.cpp'); 94 | expect(result).toHaveProperty('line', 7); 95 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Warning); 96 | expect(result['message']).toMatch(/^C\+\+98 requires newline at end of file/); 97 | }); 98 | }); 99 | -------------------------------------------------------------------------------- /specs/suite/linter-flawfinder.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { cloneDeep } from 'lodash'; 6 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 7 | import { FlawFinder } from '../../server/src/linters/flawfinder'; 8 | import { Linter } from '../../server/src/linters/linter'; 9 | import { Settings } from '../../server/src/settings'; 10 | import { defaultConfig } from '../mock-config'; 11 | import { injectMockFileSystem } from '../mock-fs'; 12 | import { FLYLINT_ID } from '../../server/src/server'; 13 | 14 | describe('FlawFinder parser', () => { 15 | injectMockFileSystem(); 16 | 17 | let config: Settings; 18 | let linter: Linter; 19 | 20 | beforeEach(() => { 21 | config = cloneDeep(defaultConfig[FLYLINT_ID]); 22 | linter = new FlawFinder(config, process.cwd()); 23 | }); 24 | 25 | test('should build a proper command-line for a C++ source file', () => { 26 | // this method call syntax permits protected/private method calling; due to JavaScript. 27 | const actual = linter['buildCommandLine']('main.cc', 'main.cc'); 28 | expect(actual).toHaveLength(5); 29 | }); 30 | 31 | test('should build a proper command-line for a C++ header file', () => { 32 | // this method call syntax permits protected/private method calling; due to JavaScript. 33 | const actual = linter['buildCommandLine']('main.h', 'main.h'); 34 | expect(actual).toHaveLength(5); 35 | }); 36 | 37 | test('should handle parsing an invalid line', () => { 38 | // this method call syntax permits protected/private method calling; due to JavaScript. 39 | const actual = linter['parseLine']('should not parse!')!; 40 | expect(actual).toHaveProperty('parseError'); 41 | }); 42 | 43 | test('should skip over excluded patterns', () => { 44 | const test = [ 45 | `Examining git/test/test.c`, 46 | ``, 47 | `git/test/test.c:7:5: [4] (buffer) sprintf:Does not check for buffer overflows (CWE-120). Use sprintf_s, snprintf, or vsnprintf.` 48 | ]; 49 | // this method call syntax permits protected/private method calling; due to JavaScript. 50 | const actual = linter['parseLines'](test); 51 | 52 | expect(actual).toHaveLength(1); 53 | 54 | let result = actual.pop()!; 55 | 56 | expect(result).toHaveProperty('fileName', 'git/test/test.c'); 57 | expect(result).toHaveProperty('line', 6); 58 | expect(result).toHaveProperty('column', 4); 59 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Warning); 60 | expect(result).toHaveProperty('code', '(buffer) sprintf'); 61 | expect(result['message']).toMatch(/^Does not check for buffer overflows/); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /specs/suite/linter-flexelint.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { cloneDeep } from 'lodash'; 6 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 7 | import { Flexelint } from '../../server/src/linters/flexelint'; 8 | import { Linter } from '../../server/src/linters/linter'; 9 | import { Settings } from '../../server/src/settings'; 10 | import { defaultConfig } from '../mock-config'; 11 | import { injectMockFileSystem } from '../mock-fs'; 12 | import { FLYLINT_ID } from '../../server/src/server'; 13 | 14 | describe('Flexelint parser', () => { 15 | injectMockFileSystem(); 16 | 17 | let config: Settings; 18 | let linter: Linter; 19 | 20 | beforeEach(() => { 21 | config = cloneDeep(defaultConfig[FLYLINT_ID]); 22 | linter = new Flexelint(config, process.cwd()); 23 | }); 24 | 25 | test('should build a proper command-line for a C++ source file', () => { 26 | // this method call syntax permits protected/private method calling; due to JavaScript. 27 | const actual = linter['buildCommandLine']('main.cc', 'main.cc'); 28 | expect(actual).toHaveLength(9); 29 | }); 30 | 31 | test('should build a proper command-line for a C++ header file', () => { 32 | // this method call syntax permits protected/private method calling; due to JavaScript. 33 | const actual = linter['buildCommandLine']('main.h', 'main.h'); 34 | expect(actual).toHaveLength(16); 35 | }); 36 | 37 | test('should handle parsing an invalid line', () => { 38 | // this method call syntax permits protected/private method calling; due to JavaScript. 39 | const actual = linter['parseLine']('should not parse!')!; 40 | expect(actual).toHaveProperty('parseError'); 41 | }); 42 | 43 | test('should skip over excluded patterns', () => { 44 | const test = [ 45 | `During Specific Walk:`, 46 | ` File flist.c line 2245: flist_new(0, !=0)`, 47 | `flist.c 2933 0 Warning 613: Possible use of null pointer 'flist' in left argument to operator '->' [Reference: file flist.c: line 2901]`, 48 | `flist.c 2901 0 Info 831: Reference cited in prior message`, 49 | ``, 50 | `During Specific Walk:`, 51 | ` File flist.c line 2245: flist_new(0, !=0)`, 52 | `flist.c 2933 0 Warning 613: Possible use of null pointer 'flist' in left argument to operator '->' [Reference: file flist.c: line 2901]`, 53 | `flist.c 2901 0 Info 831: Reference cited in prior message` 54 | ]; 55 | const actual = linter['parseLines'](test); 56 | 57 | expect(actual).toHaveLength(2); 58 | 59 | let result = actual.pop()!; 60 | 61 | expect(result).toHaveProperty('fileName', 'flist.c'); 62 | expect(result).toHaveProperty('line', 2932); 63 | expect(result).toHaveProperty('column', 0); 64 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Warning); 65 | expect(result).toHaveProperty('code', '613'); 66 | expect(result['message']).toMatch(/^Possible use of null pointer \'flist\'/); 67 | }); 68 | 69 | test('should parse a line with a missing column number', () => { 70 | const test = 'include/omniplayer/app.h 36 Warning 1526: Member function \'tp::op::OPApp::OnCmdLineParsed(wxCmdLineParser &)\' (line 36, file include/omniplayer/app.h) not defined'; 71 | const actual = linter['parseLine'](test)!; 72 | 73 | expect(actual).toHaveProperty('fileName', 'include/omniplayer/app.h'); 74 | expect(actual).toHaveProperty('line', 35); 75 | expect(actual).toHaveProperty('column', 0); 76 | expect(actual).toHaveProperty('severity', DiagnosticSeverity.Warning); 77 | expect(actual).toHaveProperty('code', '1526'); 78 | expect(actual['message']).toMatch(/^Member function/); 79 | }); 80 | 81 | test('should parse a line with complete detail', () => { 82 | const test = 'include/omniplayer/app.h 48 0 Info 1714: Member function \'tp::op::OPApp::IsVerbose(void) const\' (line 48, file /home/jbenden/repo/git/omniplayer.git/include/omniplayer/app.h) not referenced'; 83 | const actual = linter['parseLine'](test)!; 84 | 85 | expect(actual).toHaveProperty('fileName', 'include/omniplayer/app.h'); 86 | expect(actual).toHaveProperty('line', 47); 87 | expect(actual).toHaveProperty('column', 0); 88 | expect(actual).toHaveProperty('severity', DiagnosticSeverity.Information); 89 | expect(actual).toHaveProperty('code', '1714'); 90 | expect(actual['message']).toMatch(/^Member function/); 91 | }); 92 | 93 | test('should parse a multi-line message', () => { 94 | const test = [`C:\\msys64\\usr\\lib\\gcc\\x86_64-pc-msys\\6.3.0\\include\\c++\\stdlib.h 60 14 Error 1087: Previous declaration of 'div(int, int)' (line 91, file C:\\msys64\\usr\\include\\stdlib.h) is incompatible with 'div(int, int)' (line 91, file C:\\msys64\\usr\\include\\stdlib.h) which was introduced by the current using-declaration`, 95 | `C:\\msys64\\usr\\include\\stdlib.h 91 0 Info 830: Location cited in prior message`, 96 | `C:\\msys64\\usr\\include\\stdlib.h 91 0 Info 830: Location cited in prior message` 97 | ]; 98 | const actual = linter['parseLines'](test); 99 | 100 | expect(actual).toHaveLength(1); 101 | 102 | let result = actual.pop()!; 103 | 104 | expect(result).toHaveProperty('fileName', 'C:\\msys64\\usr\\lib\\gcc\\x86_64-pc-msys\\6.3.0\\include\\c++\\stdlib.h'); 105 | expect(result).toHaveProperty('line', 59); 106 | expect(result).toHaveProperty('column', 0); 107 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Error); 108 | expect(result).toHaveProperty('code', '1087'); 109 | expect(result['message']).toMatch(/^Previous declaration of \'div/); 110 | }); 111 | 112 | test('should parse the MS-DOS formatted filenames', () => { 113 | const test = 'c:\\Source\\rsync2\\rsync.h 314 10 Warning 537: Repeated include file \'C:\\msys64\\usr\\lib\\gcc\\x86_64-pc-msys\\6.3.0\\include\\stdint.h\''; 114 | const actual = linter['parseLine'](test)!; 115 | 116 | expect(actual).toHaveProperty('fileName', 'c:\\Source\\rsync2\\rsync.h'); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /specs/suite/linter-lizard.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { cloneDeep } from 'lodash'; 6 | import { DiagnosticSeverity } from 'vscode-languageserver/node'; 7 | import { Linter } from '../../server/src/linters/linter'; 8 | import { Lizard } from '../../server/src/linters/lizard'; 9 | import { Settings } from '../../server/src/settings'; 10 | import { defaultConfig } from '../mock-config'; 11 | import { injectMockFileSystem } from '../mock-fs'; 12 | import { FLYLINT_ID } from '../../server/src/server'; 13 | 14 | describe('Lizard parser', () => { 15 | injectMockFileSystem(); 16 | 17 | let config: Settings; 18 | let linter: Linter; 19 | 20 | beforeEach(() => { 21 | config = cloneDeep(defaultConfig[FLYLINT_ID]); 22 | linter = new Lizard(config, process.cwd()); 23 | }); 24 | 25 | test('should build a proper command-line for a C++ source file', () => { 26 | // this method call syntax permits protected/private method calling; due to JavaScript. 27 | const actual = linter['buildCommandLine']('main.cc', 'main.cc'); 28 | expect(actual).toHaveLength(3); 29 | }); 30 | 31 | test('should build a proper command-line for a C++ header file', () => { 32 | // this method call syntax permits protected/private method calling; due to JavaScript. 33 | const actual = linter['buildCommandLine']('main.h', 'main.h'); 34 | expect(actual).toHaveLength(3); 35 | }); 36 | 37 | test('should handle parsing an invalid line', () => { 38 | // this method call syntax permits protected/private method calling; due to JavaScript. 39 | const actual = linter['parseLine']('should not parse!')!; 40 | expect(actual).toHaveProperty('parseError'); 41 | }); 42 | 43 | test('should parse a multi-line message', () => { 44 | const test = [ 45 | '', 46 | 'agast_score.cpp:96: warning: cv::agast_cornerScore has 2066 NLOC, 688 CCN, 9184 token, 3 PARAM, 2072 length', 47 | 'agast_score.cpp:2171: warning: cv::agast_cornerScore has 1203 NLOC, 394 CCN, 5318 token, 3 PARAM, 1209 length', 48 | 'agast_score.cpp:3383: warning: cv::agast_cornerScore has 5625 NLOC, 1868 CCN, 24480 token, 3 PARAM, 5631 length', 49 | 'agast_score.cpp:9017: warning: cv::agast_cornerScore has 353 NLOC, 112 CCN, 1608 token, 3 PARAM, 359 length' 50 | ]; 51 | const actual = linter['parseLines'](test); 52 | 53 | expect(actual).toHaveLength(4); 54 | 55 | let result = actual.pop()!; 56 | 57 | expect(result).toHaveProperty('fileName', 'agast_score.cpp'); 58 | expect(result).toHaveProperty('line', 9016); 59 | expect(result).toHaveProperty('column', 0); 60 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Warning); 61 | expect(result).toHaveProperty('code', 'Cyclomatic complexity'); 62 | expect(result['message']).toMatch(/^cv::agast_cornerScore { 15 | injectMockFileSystem(); 16 | 17 | let config: Settings; 18 | let linter: Linter; 19 | 20 | beforeEach(() => { 21 | config = cloneDeep(defaultConfig[FLYLINT_ID]); 22 | linter = new PclintPlus(config, process.cwd()); 23 | }); 24 | 25 | test('should build a proper command-line for a C++ source file', () => { 26 | // this method call syntax permits protected/private method calling; due to JavaScript. 27 | const actual = linter['buildCommandLine']('main.cc', 'main.cc'); 28 | expect(actual).toHaveLength(9); 29 | }); 30 | 31 | test('should build a proper command-line for a C++ header file', () => { 32 | // this method call syntax permits protected/private method calling; due to JavaScript. 33 | const actual = linter['buildCommandLine']('main.h', 'main.h'); 34 | expect(actual).toHaveLength(16); 35 | }); 36 | 37 | test('should handle parsing an invalid line', () => { 38 | // this method call syntax permits protected/private method calling; due to JavaScript. 39 | const actual = linter['parseLine']('should not parse!')!; 40 | expect(actual).toHaveProperty('parseError'); 41 | }); 42 | 43 | test('should parse using first exclude pattern', () => { 44 | const test = [ 45 | 'C:\\pclp-1.3.5\\windows\\config\\co-xc16.lnt 164 0 error 307: cannot open indirect ', 46 | ' file \'me-project.lnt\'', 47 | 'me-project.lnt', 48 | '^', 49 | '' 50 | ]; 51 | // this method call syntax permits protected/private method calling; due to JavaScript. 52 | const actual = linter['parseLines'](test); 53 | 54 | expect(actual).toHaveLength(1); 55 | 56 | let result = actual.pop()!; 57 | 58 | expect(result).toHaveProperty('fileName', 'C:\\pclp-1.3.5\\windows\\config\\co-xc16.lnt'); 59 | expect(result).toHaveProperty('line', 163); 60 | expect(result).toHaveProperty('column', 0); 61 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Error); 62 | expect(result).toHaveProperty('code', '307'); 63 | expect(result['message']).toMatch(/^cannot open indirect/); 64 | }); 65 | 66 | test('should parse using second exclude pattern', () => { 67 | const test = [ 68 | 'C:\\pclp-1.3.5\\windows\\config\\co-xc16.lnt:164:0: error 307: cannot open indirect ', 69 | ' file \'me-project.lnt\'', 70 | 'me-project.lnt', 71 | '^', 72 | '' 73 | ]; 74 | // this method call syntax permits protected/private method calling; due to JavaScript. 75 | const actual = linter['parseLines'](test); 76 | 77 | expect(actual).toHaveLength(1); 78 | 79 | let result = actual.pop()!; 80 | 81 | expect(result).toHaveProperty('fileName', 'C:\\pclp-1.3.5\\windows\\config\\co-xc16.lnt'); 82 | expect(result).toHaveProperty('line', 163); 83 | expect(result).toHaveProperty('column', 0); 84 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Error); 85 | expect(result).toHaveProperty('code', '307'); 86 | expect(result['message']).toMatch(/^cannot open indirect/); 87 | }); 88 | 89 | test('should parse multiple lines of output', () => { 90 | const test = [ 91 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:78:4: warning 534: ignoring return value of function \'printf\'', 92 | 'c:\\program files (x86)\\microchip\\xc16\\v1.41\\bin\\bin\\../..\\include\\lega-c\\stdio.h:102:4: supplemental 891: declared here', 93 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:79:4: warning 534: ignoring return value of function \'printf\'', 94 | 'c:\\program files (x86)\\microchip\\xc16\\v1.41\\bin\\bin\\../..\\include\\lega-c\\stdio.h:102:4: supplemental 891: declared here', 95 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:81:4: warning 534: ignoring return value of function \'memset\'', 96 | 'c:\\program files (x86)\\microchip\\xc16\\v1.41\\bin\\bin\\../..\\include\\lega-c\\string.h:29:7: supplemental 891: declared here', 97 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:82:4: warning 534: ignoring return value of function \'memset\'', 98 | 'c:\\program files (x86)\\microchip\\xc16\\v1.41\\bin\\bin\\../..\\include\\lega-c\\string.h:29:7: supplemental 891: declared here', 99 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:71:8: warning 530: \'i\' is likely uninitialized', 100 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:56:8: supplemental 891: allocated here', 101 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:76:6: info 838: previous value assigned to \'i\' not used', 102 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:73:10: supplemental 891: previous assignment is here', 103 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:89:4: warning 438: last value assigned to \'i\' not used', 104 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:76:6: supplemental 891: previous assignment is here', 105 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:60:29: warning 641: implicit conversion of enum \'mainXC16_enum\' to integral type \'int\'', 106 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:68:25: info 713: implicit conversion (assignment) from \'unsigned int\' to \'int\'', 107 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:69:26: info 713: implicit conversion (assignment) from \'unsigned int\' to \'int\'', 108 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:81:21: info 732: loss of sign (call) (\'int\' to \'size_t\' (aka \'unsigned int\'))', 109 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:82:21: info 732: loss of sign (call) (\'int\' to \'size_t\' (aka \'unsigned int\'))', 110 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:84:11: info 716: infinite loop via while', 111 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:44:1: info 751: local typedef \'mainXC16_enum_t\' not referenced', 112 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:30:5: info 714: external symbol \'init_uart\' was defined but not referenced', 113 | 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c:30:5: info 765: external symbol \'init_uart\' could be made static', 114 | '' 115 | ]; 116 | // this method call syntax permits protected/private method calling; due to JavaScript. 117 | const actual = linter['parseLines'](test); 118 | 119 | expect(actual).toHaveLength(23); 120 | 121 | let result = actual.pop()!; 122 | 123 | expect(result).toHaveProperty('fileName', 'c:\\Users\\Username\\source\\repos\\Array\\mainXC16.c'); 124 | expect(result).toHaveProperty('line', 29); 125 | expect(result).toHaveProperty('column', 0); 126 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Information); 127 | expect(result).toHaveProperty('code', '765'); 128 | expect(result['message']).toMatch(/^external symbol \'init_uart\' could be made static/); 129 | }); 130 | 131 | test('should parse multi-line diagnostics', () => { 132 | const test = [ 133 | 'c:\\experiments\\VSCodeDemo\\src\\main.cpp 1 0 Warning 686: Option \'-e*\' is suspicious because of \'the likelihood of causing meaningless output\'; receiving a syntax error inside a library file most likely means something is wrong with your Lint configuration', 134 | 'c:\\experiments\\VSCodeDemo\\src\\main.cpp 7 7 Note 1960: Violates MISRA C++ 2008 Required Rule 16-0-3, use of \'#undef\' is discouraged: \'SOMETHING\' ', 135 | 'c:\\experiments\\VSCodeDemo\\src\\main.cpp 10 0 Note 1960: Violates MISRA C++ 2008 Required Rule 7-3-1, Global declaration of symbol \'avg\' ', 136 | ' 0 0 Note 1960: Violates MISRA C++ 2008 Required Rule 0-1-8, Void return type for function without external side-effects: avg(void)', 137 | 'c:\\experiments\\VSCodeDemo\\src\\main.cpp 7 7 Note 1960: Violates MISRA C++ 2008 Required Rule 16-0-3, use of \'#undef\' is discouraged: \'SOMETHING\' ', 138 | 'c:\\experiments\\VSCodeDemo\\src\\main.cpp 10 0 Note 1960: Violates MISRA C++ 2008 Required Rule 7-3-1, Global declaration of symbol \'avg\' ', 139 | ' 0 0 Note 974: Worst case function for stack usage: \'avg\' is finite, requires 12 bytes total stack in calling \'no function\'. See +stack for a full report.', 140 | ' 0 0 Note 900: Successful completion, 7 messages produced', 141 | '' 142 | ]; 143 | // this method call syntax permits protected/private method calling; due to JavaScript. 144 | const actual = linter['parseLines'](test); 145 | 146 | expect(actual).toHaveLength(7); 147 | 148 | actual.shift()!; 149 | actual.shift()!; 150 | 151 | let result = actual.shift()!; 152 | expect(result).toHaveProperty('fileName', 'c:\\experiments\\VSCodeDemo\\src\\main.cpp'); 153 | expect(result).toHaveProperty('line', 9); 154 | expect(result).toHaveProperty('column', 0); 155 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Hint); 156 | expect(result).toHaveProperty('code', '1960'); 157 | expect(result['message']).toMatch(/Required Rule 7-3-1/); 158 | 159 | result = actual.shift()!; 160 | expect(result).toHaveProperty('fileName', 'c:\\experiments\\VSCodeDemo\\src\\main.cpp'); 161 | expect(result).toHaveProperty('line', 9); 162 | expect(result).toHaveProperty('column', 0); 163 | expect(result).toHaveProperty('severity', DiagnosticSeverity.Hint); 164 | expect(result).toHaveProperty('code', '1960'); 165 | expect(result['message']).toMatch(/Required Rule 0-1-8/); 166 | }); 167 | }); 168 | -------------------------------------------------------------------------------- /specs/suite/linter.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { cloneDeep } from 'lodash'; 6 | import { basename } from 'path'; 7 | import { Clang } from '../../server/src/linters/clang'; 8 | import { CppCheck } from '../../server/src/linters/cppcheck'; 9 | import { FlawFinder } from '../../server/src/linters/flawfinder'; 10 | import { Flexelint } from '../../server/src/linters/flexelint'; 11 | import { Linter } from '../../server/src/linters/linter'; 12 | import { Lizard } from '../../server/src/linters/lizard'; 13 | import { PclintPlus } from '../../server/src/linters/pclintplus'; 14 | import { Settings } from '../../server/src/settings'; 15 | import { defaultConfig } from '../mock-config'; 16 | import { injectMockFileSystem } from '../mock-fs'; 17 | import { FLYLINT_ID } from '../../server/src/server'; 18 | 19 | describe('Analyser executables', () => { 20 | injectMockFileSystem(); 21 | 22 | let config: Settings; 23 | let linter: Linter; 24 | 25 | describe.each([ 26 | { 27 | formal_name: 'Clang', 28 | binary_name: 'clang', 29 | claz: (c: Settings, p: string) => { return new Clang(c, p); } 30 | }, { 31 | formal_name: 'CppCheck', 32 | binary_name: 'cppcheck', 33 | claz: (c: Settings, p: string) => { return new CppCheck(c, p); } 34 | }, { 35 | formal_name: 'FlawFinder', 36 | binary_name: 'flawfinder', 37 | claz: (c: Settings, p: string) => { return new FlawFinder(c, p); } 38 | }, { 39 | formal_name: 'Flexelint', 40 | binary_name: 'flexelint', 41 | claz: (c: Settings, p: string) => { return new Flexelint(c, p); } 42 | }, { 43 | formal_name: 'Lizard', 44 | binary_name: 'lizard', 45 | claz: (c: Settings, p: string) => { return new Lizard(c, p); } 46 | }, { 47 | formal_name: 'PclintPlus', 48 | binary_name: 'pclp', 49 | claz: (c: Settings, p: string) => { return new PclintPlus(c, p); } 50 | } 51 | ])('.analyser($formal_name, $binary_name)', ({ formal_name, binary_name, claz }) => { 52 | 53 | beforeEach(() => { 54 | config = cloneDeep(defaultConfig[FLYLINT_ID]); 55 | linter = claz(config, process.cwd()); 56 | }); 57 | 58 | test(`should find the actual ${formal_name} executable`, async () => { 59 | await linter['maybeEnable'](); 60 | 61 | // access private member variable via JavaScript property access. 62 | const exe = basename(linter['executable']); 63 | 64 | expect(linter.isActive()).toBeTruthy(); 65 | expect(exe).toBe(binary_name); 66 | }); 67 | 68 | test(`should NOT find a missing ${formal_name} executable`, async () => { 69 | // GIVEN 70 | linter['setExecutable']('non-existent'); 71 | 72 | // WHEN 73 | await linter['maybeEnable']() 74 | // THEN 75 | .then(() => { 76 | fail(new Error('Should not have gotten a result value')); 77 | }) 78 | .catch((e: Error) => { 79 | expect(e.message).toEqual(`The executable was not found for ${formal_name}, disabling linter`); 80 | }); 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /specs/suite/setup-vfs.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import * as fs from 'fs'; 6 | import { injectMockFileSystem } from '../mock-fs'; 7 | 8 | const SUBJECT: string = '.gitignore'; 9 | const EXPECTED: string = 'git ignore content'; 10 | 11 | describe('mock file-system', () => { 12 | injectMockFileSystem(); 13 | 14 | test('has specific file on disk', () => { 15 | const data = fs.statSync(SUBJECT); 16 | 17 | expect(data.isFile()).toBeTruthy(); 18 | }); 19 | 20 | test('has known content in specific file', () => { 21 | const data = fs.readFileSync(SUBJECT, 'utf8'); 22 | 23 | expect(data).toBe(EXPECTED); 24 | }); 25 | }); 26 | 27 | describe('real file-system', () => { 28 | test('has specific file on disk', () => { 29 | const data = fs.statSync(SUBJECT); 30 | 31 | expect(data.isFile()).toBeTruthy(); 32 | }); 33 | 34 | test('has unknown content in specific file', () => { 35 | const data = fs.readFileSync(SUBJECT, 'utf8'); 36 | 37 | expect(data).not.toBe(EXPECTED); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /specs/suite/unit-c_cpp_properties.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { readFileSync } from 'fs'; 6 | import { resolve } from 'path'; 7 | import * as vscode from 'vscode'; 8 | import { Settings, IConfigurations, propertiesPlatform } from '../../server/src/settings'; 9 | import { RobustPromises } from '../../server/src/utils'; 10 | 11 | // inspired by: https://stackoverflow.com/a/69396619 12 | const testIf = (condition:any, param1:any, ...args:Array) => 13 | condition ? test(param1, ...args) : test.skip(param1, ...args); 14 | 15 | const testIfLinux = (param1:any, ...args:Array) => 16 | testIf(process.platform === 'linux', param1, ...args); 17 | 18 | // ------------------------------ Critical ------------------------------- 19 | // One CANNOT test across IPC channels to the server, as the test is NOT the 20 | // same Node.js process! 21 | // ------------------------------ Critical ------------------------------- 22 | 23 | // ------------------------------ Critical ------------------------------- 24 | // VSCode's idea of settings are not ment for run-time calculated settings, 25 | // as if it were used in this manner, these values would be persisted to 26 | // the actual settings store. 27 | // ------------------------------ Critical ------------------------------- 28 | 29 | const currentDir = __dirname; 30 | 31 | jest.setTimeout(300000); 32 | 33 | describe('c_cpp_properties.json unit-tests', () => { 34 | test('should find the fixture file', () => { 35 | let propertiesData: IConfigurations = JSON.parse(readFileSync(resolve(currentDir, './fixtures/c_cpp_properties.json'), 'utf8')); 36 | 37 | const config = propertiesData.configurations.find(el => el.name === propertiesPlatform()); 38 | 39 | expect(config).toBeDefined(); 40 | expect(config).toHaveProperty('includePath'); 41 | }); 42 | 43 | describe('GIVEN an opened workspace', () => { 44 | const workspaceFolder = resolve(currentDir, './fixtures/c_cpp_properties'); 45 | const filePath = resolve(workspaceFolder, 'c_cpp_properties.c'); 46 | 47 | let document: vscode.TextDocument; 48 | 49 | beforeEach(async () => { 50 | await vscode.commands.executeCommand('vscode.openFolder', vscode.Uri.file(workspaceFolder)); 51 | 52 | document = await vscode.workspace.openTextDocument(vscode.Uri.file(filePath)); 53 | 54 | await vscode.window.showTextDocument(document); 55 | }); 56 | 57 | afterEach(async () => { 58 | await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); 59 | }); 60 | 61 | async function getDocumentSettings(document: vscode.TextDocument): Promise { 62 | return (await RobustPromises.retry(42, // # of attempts 63 | 1000, // delay between retries 64 | 1000, // timeout for a try 65 | () => vscode.commands.executeCommand('c-cpp-flylint.getLocalConfig', document))) as Settings; 66 | } 67 | 68 | test('it should handle non-existing includePaths setting', async () => { 69 | // WHEN 70 | let config: Settings = await getDocumentSettings(document); 71 | 72 | // THEN: simple checks against the set of includePaths 73 | expect(config).toBeDefined(); 74 | expect(config.includePaths.length).toBeGreaterThan(2); 75 | 76 | // and then: a known set of directories are in the set of all includePaths 77 | expect(config.includePaths).not.toEqual( 78 | expect.arrayContaining( 79 | [ 80 | expect.stringMatching(/\/usr\/lib\/gcc\/x86_64-linux-gnu\/5\/include/), 81 | expect.stringMatching(/\$\{workspaceRoot}/), 82 | expect.stringMatching(/^$/) 83 | ] 84 | ) 85 | ); 86 | }); 87 | 88 | testIfLinux('it should handle plain includePaths setting', async () => { 89 | // WHEN 90 | let config: Settings = await getDocumentSettings(document); 91 | 92 | // THEN: simple checks against the set of includePaths 93 | expect(config).toBeDefined(); 94 | expect(config.includePaths.length).toBeGreaterThan(2); 95 | 96 | // and then: a known set of directories are in the set of all includePaths 97 | expect(config.includePaths).toEqual( 98 | expect.arrayContaining( 99 | [ 100 | expect.stringMatching(/\/usr\/include$/), 101 | expect.stringMatching(/\/usr\/local\/include$/) 102 | ] 103 | ) 104 | ); 105 | }); 106 | 107 | test('it should handle glob expansion of includePaths setting', async () => { 108 | // WHEN 109 | let config: Settings = await getDocumentSettings(document); 110 | 111 | // THEN: simple checks against the set of includePaths 112 | expect(config).toBeDefined(); 113 | expect(config.includePaths.length).toBeGreaterThan(2); 114 | 115 | // and then: no glob sequences are in the set of all includePaths 116 | expect(config.includePaths).not.toEqual( 117 | expect.arrayContaining( 118 | [ 119 | expect.stringMatching(/\/\*\*/), 120 | ] 121 | ) 122 | ); 123 | 124 | // and then: a known set of directories are in the set of all includePaths 125 | expect(config.includePaths).toEqual( 126 | expect.arrayContaining( 127 | [ 128 | expect.stringMatching(/\/a\/aa$/), 129 | expect.stringMatching(/\/b\/aa$/), 130 | expect.stringMatching(/\/c\/aa$/), 131 | expect.stringMatching(/\/c\/bb$/) 132 | ] 133 | ) 134 | ); 135 | }); 136 | }); 137 | }); 138 | -------------------------------------------------------------------------------- /specs/suite/unit-path.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | import { URI } from 'vscode-uri'; 6 | import { path } from '../../server/src/utils'; 7 | 8 | describe('path', () => { 9 | describe('POSIX-based directory paths', () => { 10 | test('it should pass-through POSIX path', () => { 11 | expect(path('a/b')).toBe('a/b'); 12 | }); 13 | 14 | test('it should pass-through POSIX absolute path', () => { 15 | expect(path('/a/b')).toBe('/a/b'); 16 | }); 17 | 18 | test('it should handle POSIX absolute paths', () => { 19 | let uri = URI.file('/a/b/ab.c'); 20 | expect(uri.scheme).toBe('file'); 21 | expect(uri.path).toBe('/a/b/ab.c'); 22 | 23 | let uri2 = URI.file(path('/a/b/ab.c')); 24 | expect(uri2).toStrictEqual(uri); 25 | }); 26 | 27 | test('it should handle POSIX relative paths', () => { 28 | let uri = URI.file('a/b/ab.c'); 29 | expect(uri.scheme).toBe('file'); 30 | expect(uri.path).toBe('/a/b/ab.c'); 31 | 32 | let uri2 = URI.file(path('a/b/ab.c')); 33 | expect(uri2).toStrictEqual(uri); 34 | }); 35 | }); 36 | 37 | describe('DOS-based directory paths', () => { 38 | test('it should convert DOS path', () => { 39 | expect(path('\\a\\b.txt')).toBe('/a/b.txt'); 40 | }); 41 | 42 | test('it should convert DOS absolute path', () => { 43 | expect(path('c:\\a\\b.txt')).toBe('c:/a/b.txt'); 44 | }); 45 | 46 | test('it should convert DOS relative with drive letter path', () => { 47 | expect(path('C:a\\b.txt')).toBe('a/b.txt'); 48 | }); 49 | 50 | test('it should convert DOS absolute path with POSIX separators', () => { 51 | expect(path('C:/a/b.txt')).toBe('C:/a/b.txt'); 52 | }); 53 | 54 | test('it should convert DOS relative with drive letter path and POSIX separators', () => { 55 | expect(path('C:a/b.txt')).toBe('a/b.txt'); 56 | }); 57 | 58 | test('it should handle DOS absolute paths', () => { 59 | let uri = URI.file('C:\\a\\b\\ab.c'); 60 | expect(uri.scheme).toBe('file'); 61 | expect(uri.path).toBe('/C:\\a\\b\\ab.c'); 62 | 63 | let uri2 = URI.file(path('C:\\a\\b\\ab.c')); 64 | expect(uri2.scheme).toBe('file'); 65 | expect(uri2.path).toBe('/C:/a/b/ab.c'); 66 | expect(uri2.toString()).toBe('file:///c%3A/a/b/ab.c'); 67 | }); 68 | 69 | test('it should handle DOS relative paths', () => { 70 | let uri = URI.file('a\\b\\ab.c'); 71 | expect(uri.scheme).toBe('file'); 72 | expect(uri.path).toBe('/a\\b\\ab.c'); 73 | 74 | let uri2 = URI.file(path('a\\b\\ab.c')); 75 | expect(uri2.scheme).toBe('file'); 76 | expect(uri2.path).toBe('/a/b/ab.c'); 77 | }); 78 | 79 | test('it should handle DOS relative path with drive letter', () => { 80 | let uri = URI.file('C:a\\b\\ab.c'); 81 | expect(uri.scheme).toBe('file'); 82 | expect(uri.path).toBe('/C:a\\b\\ab.c'); 83 | expect(uri.toString()).toBe('file:///c%3Aa%5Cb%5Cab.c'); 84 | 85 | let uri2 = URI.file(path('C:a\\b\\ab.c')); 86 | expect(uri2.scheme).toBe('file'); 87 | expect(uri2.path).toBe('/a/b/ab.c'); 88 | expect(uri2.toString()).toBe('file:///a/b/ab.c'); 89 | }); 90 | }); 91 | 92 | describe('Windows UNC paths', () => { 93 | test('it should pass-through with converted directory separators', () => { 94 | // UNC DOS-style device paths 95 | expect(path('\\\\.\\a.txt')).toBe('//./a.txt'); 96 | expect(path('\\\\?\\a.txt')).toBe('//?/a.txt'); 97 | 98 | // UNC paths 99 | expect(path('\\\\127.0.0.1\\c$\\a.txt')).toBe('//127.0.0.1/c$/a.txt'); 100 | expect(path('\\\\.\\UNC\\LOCALHOST\\c$\\a.txt')).toBe('//./UNC/LOCALHOST/c$/a.txt'); 101 | }); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /specs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "target": "es2017", 7 | "outDir": "out", 8 | "rootDir": "..", 9 | "lib": [ "es2017" ], 10 | "sourceMap": true 11 | }, 12 | "include": [ 13 | ".", 14 | "../typings.d.ts" 15 | ], 16 | "exclude": [ 17 | "node_modules" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "noImplicitAny": true, 6 | "noImplicitReturns": true, 7 | "noUnusedLocals": true, 8 | "noUnusedParameters": true, 9 | "strict": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "target": "es6", 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "lib": [ "es6" ], 9 | "sourceMap": true 10 | }, 11 | "include": [ 12 | "src", 13 | "typings.d.ts" 14 | ], 15 | "exclude": [ 16 | "node_modules" 17 | ], 18 | "references": [ 19 | { "path": "./client" }, 20 | { "path": "./server" } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /typings.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2022 The VSCode C/C++ Flylint Authors 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | declare interface Thenable extends PromiseLike {} 6 | 7 | declare module "globule"; 8 | 9 | declare module 'which'; 10 | 11 | declare module 'unixify'; 12 | --------------------------------------------------------------------------------