Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programming exercises: Enable static code analysis for C++ with LocalCI #10453

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from

Conversation

magaupp
Copy link
Contributor

@magaupp magaupp commented Mar 7, 2025

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the server coding and design guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).
  • I documented the Java code using JavaDoc style.

Changes affecting Programming Exercises

  • High priority: I tested all changes and their related features with all corresponding user types on a test server configured with the integrated lifecycle setup (LocalVC and LocalCI).

Motivation and Context

This PR enables static code analysis for C++ using Clang-Tidy.

Clang-Tidy can be configured using the .clang-tidy file in the test repository.

Description

Clang-Tidy's output is converted to SARIF using a fork of sarif-rs. The fork moves the clang-tidy check name from the message to the SARIF ruleId.

The Docker image is updated to install clang-tidy and our fork of the clang-tidy-sarif tool.
ls1intum/artemis-cpp-docker@0115918

The SARIF parser is updated to use the provided RuleCategorizer with a dummy ReportingDescriptor if the SARIF file does not contain any.

The Python test script is updated to configure CMake with CMAKE_EXPORT_COMPILE_COMMANDS=ON. This creates a compile-commands.json file which is used by clang-tidy.

Steps for Testing

  1. Navigate to the create programming exercise view
  2. Select C++ as the language
  3. Select advanced mode via the button on the bottom left
  4. Enable static code analysis in the Language section
  5. Fill out the required fields
  6. Generate the exercise via the button on the bottom right
  7. Verify that the Template Result has no code issues
  8. Verify that the Solution Result has no code issues
  9. Open the editor for any of the repositories via the Edit in Editor button on the top
  10. Add this code to a function:
  std::vector<int> a = {1, 2, 3};
  std::vector<int> b = std::move(a);
  [[maybe_unused]] int c = a[0];
  1. Submit this change
  2. Verify that the clang-analyzer-cplusplus.Move code issue is in the feedback
  3. Verify that the issue is highlighted in the correct location
    grafik

Testserver States

You can manage test servers using Helios. Check environment statuses in the environment list. To deploy to a test server, go to the CI/CD page, find your PR or branch, and trigger the deployment.

Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Test Coverage

Summary by CodeRabbit

  • New Features

    • Enhanced static analysis support for C++ with integrated Clang-Tidy checks, streamlined automation scripts for building, testing, and code analysis, and improved configuration flexibility.
  • Documentation

    • Updated feature support tables to clearly indicate static analysis capabilities for Python and C++.
  • Tests

    • Added new tests to validate the parsing and reporting of static analysis results for improved accuracy.

@github-actions github-actions bot added tests server Pull requests that update Java code. (Added Automatically!) documentation config-change Pull requests that change the config in a way that they require a deployment via Ansible. template core Pull requests that affect the corresponding module programming Pull requests that affect the corresponding module labels Mar 7, 2025
@magaupp magaupp force-pushed the feature/programming-exercises/cplusplus-sca branch from fe61bf5 to 45234d2 Compare March 11, 2025 09:13
@helios-aet helios-aet bot temporarily deployed to artemis-test2.artemis.cit.tum.de March 11, 2025 09:53 Inactive
@helios-aet helios-aet bot temporarily deployed to artemis-test2.artemis.cit.tum.de March 11, 2025 12:28 Inactive
@magaupp magaupp marked this pull request as ready for review March 11, 2025 12:53
@magaupp magaupp requested a review from a team as a code owner March 11, 2025 12:53
Copy link

coderabbitai bot commented Mar 11, 2025

Walkthrough

This pull request introduces a new constructor in the ReportingDescriptor record and simplifies the categorization logic in SarifParser. It extends static code analysis support for C++ by adding the CLANG_TIDY tool to the configuration and enum, updating parser policies, and adjusting language feature flags. Additionally, the documentation has been revised to reflect these changes, and new scripts and test files (both Bash and Python) have been added to set up the build environment, run tests, perform static analysis with Clang-Tidy, and validate reported issues.

Changes

File(s) Change Summary
src/main/java/de/tum/.../scaparser/format/sarif/ReportingDescriptor.java
src/main/java/de/tum/.../scaparser/strategy/sarif/SarifParser.java
Added a new constructor to ReportingDescriptor and simplified rule categorization in SarifParser by consistently creating a ReportingDescriptor from the ruleId when needed.
src/main/java/de/tum/.../core/config/StaticCodeAnalysisConfigurer.java
src/main/java/de/tum/.../programming/domain/StaticCodeAnalysisTool.java
src/main/java/de/tum/.../scaparser/strategy/ParserPolicy.java
src/main/java/de/tum/.../LocalCIProgrammingLanguageFeatureService.java
Updated static code analysis configuration for C++ by adding the CLANG_TIDY tool: new enum constant, default mappings, parser policy case, and an enabled feature flag in language features.
docs/user/.../programming-exercise-features.inc Updated the support table to indicate that Local CI supports Static Code Analysis for Python and C++ (while Jenkins remains unsupported).
src/main/resources/templates/aeolus/c_plus_plus/default_static.sh Introduced a new Bash script that sets up the build environment, runs tests, performs static analysis using Clang-Tidy, and orchestrates post-execution actions.
src/main/resources/templates/c_plus_plus/staticCodeAnalysis/test/.clang-tidy
src/main/resources/templates/c_plus_plus/staticCodeAnalysis/test/Tests.py
Added a Clang-Tidy configuration file and a Python test runner for executing and validating C++ static code analysis.
src/test/java/de/tum/.../StaticCodeAnalysisParserUnitTest.java
src/test/java/de/tum/.../ProgrammingExerciseFactory.java
Added a test case for verifying Clang-Tidy parser functionality and integrated a new case to correctly categorize issues from the CLANG_TIDY tool.
src/test/resources/test-data/static-code-analysis/expected/clang-tidy.json
src/test/resources/test-data/static-code-analysis/reports/clang-tidy.sarif
Added expected JSON results and a SARIF report file generated by Clang-Tidy, detailing a warning on a null pointer dereference.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant M as Main (default_static.sh)
    participant S as setup_the_build_environment
    participant T as build_and_run_all_tests
    participant A as static_code_analysis
    participant F as final_aeolus_post_action

    U->>M: Execute script
    M->>S: Set up build environment
    S-->>M: Environment ready
    M->>T: Run tests
    T-->>M: Test results
    M->>A: Run Clang-Tidy analysis
    A-->>M: Analysis report generated
    M->>F: Execute final post actions
    F-->>M: Post actions complete
Loading
sequenceDiagram
    participant C as Caller
    participant PP as ParserPolicy
    participant SP as SarifParser
    participant CC as SingleCategoryCategorizer

    C->>PP: configure(fileName)
    alt file indicates CLANG_TIDY
        PP->>SP: Create new SarifParser(CLANG_TIDY, CC)
        SP-->>PP: Parser instance with Clang-Tidy support
    else other cases
        PP-->>C: Return appropriate parser
    end
Loading

Suggested labels

ready to merge

Suggested reviewers

  • BBesrour
  • SimonEntholzer
  • krusche
  • HanyangXu0508
  • coolchock
  • ole-ve
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/main/resources/templates/c_plus_plus/staticCodeAnalysis/test/Tests.py (2)

1-34: Test script follows good practices but lacks error handling

The script is well-structured and correctly imports necessary components, configures the build environment, and runs tests. However, there's no error handling in case of failures during configuration or test execution.

Consider adding basic error handling to gracefully handle failures:

 # Run the actual tests:
-tester.run()
+try:
+    tester.run()
+except Exception as e:
+    print(f"Error running tests: {e}")
+    import sys
+    sys.exit(1)

 # Export the results into the JUnit XML format:
-tester.exportResult("./test-reports/tests-results.xml")
+try:
+    tester.exportResult("./test-reports/tests-results.xml")
+except Exception as e:
+    print(f"Error exporting test results: {e}")
+    import sys
+    sys.exit(1)

13-13: Consider making the build directory configurable

The build directory is hardcoded as "./build", which limits flexibility.

Consider making it configurable through command-line arguments:

+import argparse
+
 def main() -> None:
+    parser = argparse.ArgumentParser(description='Run C++ tests with static analysis')
+    parser.add_argument('--build-dir', default='./build', help='Build directory')
+    args = parser.parse_args()
+
     # Create a new instance of the tester:
     tester: Tester = Tester()
 
-    buildDir = "./build"
+    buildDir = args.build_dir
src/main/resources/templates/aeolus/c_plus_plus/default_static.sh (1)

1-79: Script follows good practices but could benefit from input validation

The script is well-structured with clear function separations and proper error handling. Consider adding validation for essential environment variables and commands to fail gracefully with informative error messages.

#!/usr/bin/env bash
set -e
export AEOLUS_INITIAL_DIRECTORY=${PWD}

+ # Validate required tools are available
+ command -v clang-tidy >/dev/null 2>&1 || { echo "Error: clang-tidy is required but not installed."; exit 1; }
+ command -v clang-tidy-sarif >/dev/null 2>&1 || { echo "Error: clang-tidy-sarif converter is required but not installed."; exit 1; }
+
🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 54-54: studentParentWorkingDirectoryName is referenced but not assigned.

(SC2154)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06f2da4 and 186db8a.

⛔ Files ignored due to path filters (3)
  • src/main/resources/config/application.yml is excluded by !**/*.yml
  • src/main/resources/templates/aeolus/c_plus_plus/default_static.yaml is excluded by !**/*.yaml
  • src/main/resources/config/application.yml is excluded by !**/*.yml
📒 Files selected for processing (14)
  • src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/format/sarif/ReportingDescriptor.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/strategy/sarif/SarifParser.java (1 hunks)
  • docs/user/exercises/programming-exercise-features.inc (2 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/config/StaticCodeAnalysisConfigurer.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/programming/domain/StaticCodeAnalysisTool.java (2 hunks)
  • src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/strategy/ParserPolicy.java (1 hunks)
  • src/main/resources/templates/aeolus/c_plus_plus/default_static.sh (1 hunks)
  • src/main/resources/templates/c_plus_plus/staticCodeAnalysis/test/.clang-tidy (1 hunks)
  • src/main/resources/templates/c_plus_plus/staticCodeAnalysis/test/Tests.py (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisParserUnitTest.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java (1 hunks)
  • src/test/resources/test-data/static-code-analysis/expected/clang-tidy.json (1 hunks)
  • src/test/resources/test-data/static-code-analysis/reports/clang-tidy.sarif (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`src/test/java/**/*.java`: test_naming: descriptive; test_si...

src/test/java/**/*.java: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true

  • src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisParserUnitTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java
`src/main/java/**/*.java`: naming:CamelCase; principles:{sin...

src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

  • src/main/java/de/tum/cit/aet/artemis/core/config/StaticCodeAnalysisConfigurer.java
  • src/main/java/de/tum/cit/aet/artemis/programming/domain/StaticCodeAnalysisTool.java
  • src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/strategy/ParserPolicy.java
  • src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/strategy/sarif/SarifParser.java
  • src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/format/sarif/ReportingDescriptor.java
  • src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java
🧠 Learnings (2)
src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/strategy/sarif/SarifParser.java (1)
Learnt from: magaupp
PR: ls1intum/Artemis#10204
File: src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/strategy/sarif/RubocopCategorizer.java:13-15
Timestamp: 2025-01-28T17:50:58.545Z
Learning: In the Artemis codebase, null checks for ReportingDescriptor and rule.id() are handled during SARIF parsing before reaching the categorizer classes.
src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java (1)
Learnt from: magaupp
PR: ls1intum/Artemis#9261
File: src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java:43-55
Timestamp: 2024-11-12T12:51:40.391Z
Learning: For constructors with multiple boolean parameters, it's acceptable to keep them as is because parameter names are clear and IDEs provide inline hints, making the code readable without refactoring to the builder pattern.
🪛 Shellcheck (0.10.0)
src/main/resources/templates/aeolus/c_plus_plus/default_static.sh

[warning] 54-54: studentParentWorkingDirectoryName is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (15)
src/main/resources/templates/c_plus_plus/staticCodeAnalysis/test/.clang-tidy (1)

1-2: LGTM! Configuration enables appropriate Clang-Tidy checks

The configuration correctly enables all diagnostic and analyzer checks through wildcards, which is a good starting point for static code analysis in C++.

src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java (1)

356-356: LGTM! Proper integration of CLANG_TIDY in the switch statement

The addition of the CLANG_TIDY case with "Lint" as the category aligns well with the existing pattern used for other linting tools.

src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisParserUnitTest.java (1)

96-99: LGTM! Well-structured test method for Clang-Tidy parser

The test follows the established pattern in the class, has appropriate JUnit5 annotation, and correctly references the input SARIF file and expected JSON output.

src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/format/sarif/ReportingDescriptor.java (1)

35-42: LGTM! Well-documented convenience constructor

The new constructor provides a clean way to instantiate a ReportingDescriptor with just an ID, which is useful when other fields aren't needed. The JavaDoc clearly explains its purpose.

src/test/resources/test-data/static-code-analysis/expected/clang-tidy.json (1)

1-16: Correctly structured test fixture for Clang-Tidy output

The JSON structure looks well-formed and provides a good test fixture for validating the Clang-Tidy analyzer integration. It correctly includes the essential fields needed for static analysis reporting: tool identification, file path, line/column position, rule ID, category, and priority level.

docs/user/exercises/programming-exercise-features.inc (2)

70-70: Documentation updated to reflect Python static code analysis support

The feature table has been properly updated to indicate that static code analysis for Python is now supported in Local CI but not in Jenkins.


94-94: Documentation updated to reflect C++ static code analysis support

The feature table has been correctly updated to indicate that static code analysis for C++ is now supported in Local CI but not in Jenkins, which aligns with the PR objectives.

src/test/resources/test-data/static-code-analysis/reports/clang-tidy.sarif (1)

1-34: Well-formatted SARIF report for Clang-Tidy

The SARIF file structure is correct and follows the standard 2.1.0 format. It properly includes all required elements: the tool driver information, result level, location details, and rule ID. This format will enable proper interoperability with other static analysis tools and platforms.

src/main/resources/templates/c_plus_plus/staticCodeAnalysis/test/Tests.py (1)

17-22: CMake configuration correctly includes compile commands export

The -DCMAKE_EXPORT_COMPILE_COMMANDS=ON flag is correctly included, which is essential for Clang-Tidy to function properly as it needs the compilation database to understand how files are compiled.

src/main/java/de/tum/cit/aet/artemis/core/config/StaticCodeAnalysisConfigurer.java (1)

96-96: Appropriate addition of Clang-Tidy for C++ static code analysis.

The C++ programming language has been properly configured with the Clang-Tidy tool using the generic lint category. This aligns well with the PR objective of enabling static code analysis for C++ using Clang-Tidy.

src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/strategy/ParserPolicy.java (1)

39-39: Good implementation of Clang-Tidy parser.

The CLANG_TIDY case is implemented correctly in the switch statement, creating a SarifParser with the appropriate tool and categorizer. This follows the established pattern used for other single-category tools like ESLINT.

src/main/java/de/tum/cit/aet/artemis/programming/service/localci/scaparser/strategy/sarif/SarifParser.java (1)

129-129: Improved rule categorization logic.

The code has been simplified to use a consistent approach for rule categorization. Rather than using a separate fallback mechanism, the code now directly creates a new ReportingDescriptor from the rule ID when necessary. This simplification makes the code more maintainable while ensuring consistent behavior.

This implementation aligns with the learning from PR #10204 that null checks for ReportingDescriptor are handled during SARIF parsing.

src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java (1)

65-65: Enabled static code analysis for C++.

The second parameter in the ProgrammingLanguageFeature constructor has been changed from false to true, enabling static code analysis support for C++. This change is essential for the PR objective of implementing Clang-Tidy analysis for C++.

src/main/java/de/tum/cit/aet/artemis/programming/domain/StaticCodeAnalysisTool.java (2)

17-17: New static code analysis tool for C++ looks good!

The addition of CLANG_TIDY follows the existing pattern for tool declarations with the correct SARIF filename format.


39-39: C++ language mapping correctly associated with Clang-Tidy tool

This addition properly extends the static code analysis capabilities to support C++ programming exercises, aligning with the PR objective.

Copy link
Contributor

@SimonEntholzer SimonEntholzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code

@@ -67,7 +67,7 @@ Instructors can still use those templates to generate programming exercises and
+======================+======================+======================+=====================+==============+==========================================+==============================+========================+
| Java | yes | yes | yes | yes | Gradle, Maven, J: `DejaGnu`_ | no | L: yes, J: no |
+----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+
| Python | L: yes; J: no | no | yes | no | n/a | no | L: yes, J: no |
| Python | L: yes; J: no | L: yes; J: no | yes | no | n/a | no | L: yes, J: no |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this just not updated when it should have been, so you did it now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this was implemented a while ago in #9573

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
config-change Pull requests that change the config in a way that they require a deployment via Ansible. core Pull requests that affect the corresponding module documentation programming Pull requests that affect the corresponding module ready for review server Pull requests that update Java code. (Added Automatically!) template tests
Projects
Status: Ready For Review
Status: In review
Development

Successfully merging this pull request may close these issues.

2 participants