skip to Main Content

I have two C files, main.c and library.c, which contain the following:

// main.c
#include <stdio.h>

int MY_NUMBER = 6;

#include "library.c"

int main() {
    change_the_number();
    printf("The number: %dn", MY_NUMBER);
}
// library.c
void change_the_number() {
    MY_NUMBER = 5;
}

After running gcc main.c -o main, and then running main, I get the output The number: 5, which is exactly what I would expect.

VSCode underlines MY_NUMBER in library.c, complaining that "identifier MY_NUMBER is undefined". This is also expected behaviour, as far as I’m concerned – looking solely at library.c, there’s nothing to indicate what MY_NUMBER is or where it comes from.

How can I configure Microsoft’s C/C++ extension to recognise MY_NUMBER?

What I’ve tried so far:

  • Adding ${workspaceFolder} and ${workspaceFolder}/** to C-Cpp > Default: Include Path.
  • Changing library.c to library.h (I didn’t really think this would do anything and I was right.)

One constraint is that I am unable to change the code – I must setup the IDE to allow for this declaration/usage arrangement, because I’m an intern and this pattern is repeated hundreds, if not thousands of times across the codebase I’m working on. I would ask my coworkers for help with this, but none of them use VSCode with C/C++ extension – every single one uses something different.

Ultimately, if the MS C/C++ extension doesn’t support this kind of thing, that’s fine, I’ll just use something else, but I thought it was interesting that this completely valid C program would flag up as problematic.

2

Answers


  1. Chosen as BEST ANSWER

    In the unlikely event that anyone stumbles upon this, I thought I'd share a realisation I had some time after reading @BoP's answer - what I want the linter to do becomes increasingly difficult as a codebase grows in size, to the point of intractability.

    If you start trying to include .c files in the way I've shown in my question, things get a little complicated, because you can have a constant - in our case, MY_NUMBER - declared in the original file, and used in the included file. This is valid C, as shown above.

    However - if you take just the included file (library.c in my question), and look at it in isolation from the rest of the code, you have no idea which code could include that .c file. It could be any source code on your computer. A linter obviously can't scan your entire computer, so the problem is intractable, and there is no linter that efficiently does what I want.

    Moral of the story - use C properly.


  2. This is a weird pattern. I can’t see the context, so from this minimal example, I’d have imagined to instead declare the variable extern in a library header, define it in a source file in the library, and include the header where it is defined, and wherever the variable is used inside or outside the library.

    But let’s play along and imagine there’s a good reason I can’t see without more context. Technically, an IntelliSense / static code analysis tool could piece this together given both .c files and their locations in the filesystem. But it would be a tradeoff. Without additional knowledge about how programs are built from these files (and whether they are used just together or in other programs), it would be a correctness/accuracy tradeoff. If the file is intended to be included in another source outside of the files the analysis tool knows about, what if those files don’t define the variable the included file expects to exist? What should the analysis tool do? I don’t know if there’s a globally uncontroversial answer to that question.

    Now, let’s get our heads out of the theory clouds and back to the ground with the cpptools extension for VS Code. There is at least one way to address this given your specific context, I think. See the official docs on getting IntelliSense to work. You can define a compile_commands.json file where for the files that get included, you pass a compiler flag to define a macro with the same name as the expected variable, and define it to some value with the same type as the expected variable’s type. But… at least in my experience, usually compile_commands.json is machine-generated by a build tool and not written by hand. Since you said this is a common pattern in your codebase, you could investigate the feasability of generating a compile_commands.json file for this. I don’t know if you need to fill in correct entries for all your files though if you go this route.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search