docs: @SafeVarargs
    public static AstScanner<Grammar> create(CxxLanguage language, CxxConfiguration conf,
      SquidAstVisitor<Grammar>... visitors) {
      final SquidAstVisitorContextImpl<Grammar> context
        = new SquidAstVisitorContextImpl<>(new SourceProject("Cxx Project"));
      final Parser<Grammar> parser = CxxParser.create(context, conf, language);
  
      AstScanner.Builder<Grammar> builder = AstScanner.<Grammar>builder(context).setBaseParser(parser);
  
      /* Metrics */
      builder.withMetrics(CxxMetric.values());
  
      /* Files */
      builder.setFilesMetric(CxxMetric.FILES);
  
      /* Comments */
      builder.setCommentAnalyser(
        new CommentAnalyser() {
        @Override
        public boolean isBlank(String line) {
          for (int i = 0; i < line.length(); i++) {
            if (Character.isLetterOrDigit(line.charAt(i))) {
              return false;
            }
          }
          return true;
        }
  
        @Override
        public String getContents(String comment) {
          final int HEADER_LEN = 2;
          return "/*".equals(comment.substring(0, HEADER_LEN))
            ? comment.substring(HEADER_LEN, comment.length() - HEADER_LEN)
            : comment.substring(HEADER_LEN);
        }
      });
  
      /* Functions */
      builder.withSquidAstVisitor(new SourceCodeBuilderVisitor<>((SourceCode parentSourceCode, AstNode astNode) -> {
        StringBuilder sb = new StringBuilder(512);
        for (Token token : astNode.getFirstDescendant(CxxGrammarImpl.declaratorId).getTokens()) {
          sb.append(token.getValue());
        }
        String functionName = sb.toString();
        sb.setLength(0);
        // todo: check if working with nested-namespace-definition
        AstNode namespace = astNode.getFirstAncestor(CxxGrammarImpl.namedNamespaceDefinition);
        while (namespace != null) {
          if (sb.length() > 0) {
            sb.insert(0, "::");
          }
          sb.insert(0, namespace.getFirstDescendant(GenericTokenType.IDENTIFIER).getTokenValue());
          // todo: check if working with nested-namespace-definition
          namespace = namespace.getFirstAncestor(CxxGrammarImpl.namedNamespaceDefinition);
        }
        String namespaceName = sb.length() > 0 ? sb.toString() + "::" : "";
        SourceFunction function = new SourceFunction(intersectingConcatenate(namespaceName, functionName)
          + ":" + astNode.getToken().getLine());
        function.setStartAtLine(astNode.getTokenLine());
        return function;
      }, CxxGrammarImpl.functionDefinition));
  
      builder.withSquidAstVisitor(CounterVisitor.<Grammar>builder()
        .setMetricDef(CxxMetric.FUNCTIONS)
        .subscribeTo(CxxGrammarImpl.functionDefinition)
        .build());
  
      /* Classes */
      builder.withSquidAstVisitor(new SourceCodeBuilderVisitor<>((SourceCode parentSourceCode, AstNode astNode) -> {
        AstNode classNameAst = astNode.getFirstDescendant(CxxGrammarImpl.className);
        String className = classNameAst == null ? "" : classNameAst.getFirstChild().getTokenValue();
        SourceClass cls = new SourceClass(className + ":" + astNode.getToken().getLine(), className);
        cls.setStartAtLine(astNode.getTokenLine());
        return cls;
      }, CxxGrammarImpl.classSpecifier));
  
      builder.withSquidAstVisitor(CounterVisitor.<Grammar>builder()
        .setMetricDef(CxxMetric.CLASSES)
        .subscribeTo(CxxGrammarImpl.classSpecifier)
        .build());
  
      /* Metrics */
      builder.withSquidAstVisitor(new LinesVisitor<>(CxxMetric.LINES));
      builder.withSquidAstVisitor(new CxxLinesOfCodeVisitor<>(CxxMetric.LINES_OF_CODE));
      builder.withSquidAstVisitor(new CxxLinesOfCodeInFunctionBodyVisitor<>());
      builder.withSquidAstVisitor(new CxxPublicApiVisitor<>(language));
  
      builder.withSquidAstVisitor(CommentsVisitor.<Grammar>builder().withCommentMetric(CxxMetric.COMMENT_LINES)
        .withNoSonar(true)
        .withIgnoreHeaderComment(conf.getIgnoreHeaderComments())
        .build());
  
      /* Statements */
      builder.withSquidAstVisitor(CounterVisitor.<Grammar>builder()
        .setMetricDef(CxxMetric.STATEMENTS)
        .subscribeTo(CxxGrammarImpl.statement)
        .build());
  
      builder.withSquidAstVisitor(new CxxCyclomaticComplexityVisitor<Grammar>(ComplexityVisitor.<Grammar>builder()
        .setMetricDef(CxxMetric.COMPLEXITY)
        .subscribeTo(CxxComplexityConstants.getCyclomaticComplexityTypes())
        .build()));
  
      builder.withSquidAstVisitor(new CxxCognitiveComplexityVisitor<>());
  
      builder.withSquidAstVisitor(new CxxFunctionComplexityVisitor<>(language));
      builder.withSquidAstVisitor(new CxxFunctionSizeVisitor<>(language));
  
      // to emit a 'new file' event to the internals of the plugin
      builder.withSquidAstVisitor(new CxxFileVisitor<>());
  
      // log syntax errors
      builder.withSquidAstVisitor(new CxxParseErrorLoggerVisitor<>());
  
      /* External visitors (typically Check ones) */
      for (SquidAstVisitor<Grammar> visitor : visitors) {
        if (visitor instanceof CxxCharsetAwareVisitor) {
          ((CxxCharsetAwareVisitor) visitor).setCharset(conf.getCharset());
        }
        builder.withSquidAstVisitor(visitor);
      }
  
      return builder.build();
    }